GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/openglimagehelper.cpp Lines: 0 161 0.0 %
Date: 2021-03-17 Branches: 0 147 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2019  The ManaPlus Developers
6
 *  Copyright (C) 2019-2021  Andrei Karas
7
 *
8
 *  This file is part of The ManaPlus Client.
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License as published by
12
 *  the Free Software Foundation; either version 2 of the License, or
13
 *  any later version.
14
 *
15
 *  This program is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU General Public License
21
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
#include "resources/openglimagehelper.h"
25
26
#ifdef USE_OPENGL
27
28
#include "graphicsmanager.h"
29
30
#include "render/mobileopengl2graphics.h"
31
#include "render/mobileopenglgraphics.h"
32
#include "render/modernopenglgraphics.h"
33
#ifdef __native_client__
34
#include "render/opengl/naclglfunctions.h"
35
#endif  // __native_client__
36
#include "render/normalopenglgraphics.h"
37
#include "render/safeopenglgraphics.h"
38
39
#include "render/opengl/mgl.h"
40
#include "render/opengl/mglcheck.h"
41
42
#include "resources/dye/dye.h"
43
#include "resources/dye/dyepalette.h"
44
45
#include "resources/image/image.h"
46
47
#include "utils/checkutils.h"
48
#include "utils/sdlcheckutils.h"
49
50
#include "debug.h"
51
52
#ifndef SDL_BIG_ENDIAN
53
#error missing SDL_endian.h
54
#endif  // SDL_BYTEORDER
55
56
int OpenGLImageHelper::mTextureType = 0;
57
int OpenGLImageHelper::mInternalTextureType = GL_RGBA8;
58
int OpenGLImageHelper::mTextureSize = 0;
59
bool OpenGLImageHelper::mBlur = true;
60
bool OpenGLImageHelper::mUseTextureSampler = false;
61
62
OpenGLImageHelper::~OpenGLImageHelper()
63
{
64
    glDeleteTextures(static_cast<GLsizei>(texturesSize - mFreeTextureIndex),
65
        &mTextures[mFreeTextureIndex]);
66
}
67
68
Image *OpenGLImageHelper::load(SDL_RWops *const rw, Dye const &dye)
69
{
70
    SDL_Surface *const tmpImage = loadPng(rw);
71
    if (tmpImage == nullptr)
72
    {
73
        reportAlways("Error, image load failed: %s", SDL_GetError())
74
        return nullptr;
75
    }
76
77
    SDL_Surface *const surf = convertTo32Bit(tmpImage);
78
    MSDL_FreeSurface(tmpImage);
79
    if (surf == nullptr)
80
        return nullptr;
81
82
    uint32_t *pixels = static_cast<uint32_t *>(surf->pixels);
83
    const int type = dye.getType();
84
85
    switch (type)
86
    {
87
        case 1:
88
        {
89
            const DyePalette *const pal = dye.getSPalete();
90
            if (pal != nullptr)
91
                DYEPALETTEP(pal, SOGLColor)(pixels, surf->w * surf->h);
92
            break;
93
        }
94
        case 2:
95
        {
96
            const DyePalette *const pal = dye.getAPalete();
97
            if (pal != nullptr)
98
                DYEPALETTEP(pal, AOGLColor)(pixels, surf->w * surf->h);
99
            break;
100
        }
101
        case 0:
102
        default:
103
        {
104
            dye.normalOGLDye(pixels, surf->w * surf->h);
105
            break;
106
        }
107
    }
108
109
    Image *const image = loadSurface(surf);
110
    MSDL_FreeSurface(surf);
111
    return image;
112
}
113
114
Image *OpenGLImageHelper::loadSurface(SDL_Surface *const tmpImage)
115
{
116
    return glLoad(tmpImage);
117
}
118
119
Image *OpenGLImageHelper::createTextSurface(SDL_Surface *const tmpImage,
120
                                            const int width, const int height,
121
                                            const float alpha)
122
{
123
    if (tmpImage == nullptr)
124
        return nullptr;
125
126
    Image *const img = glLoad(tmpImage, width, height);
127
    if (img != nullptr)
128
        img->setAlpha(alpha);
129
    return img;
130
}
131
132
int OpenGLImageHelper::powerOfTwo(const int input)
133
{
134
    int value = 1;
135
    while (value < input && value < mTextureSize)
136
        value <<= 1;
137
    return value >= mTextureSize ? mTextureSize : value;
138
}
139
140
SDL_Surface *OpenGLImageHelper::convertSurfaceNormalize(SDL_Surface *tmpImage,
141
                                                        int width, int height)
142
{
143
    if (tmpImage == nullptr)
144
        return nullptr;
145
146
    int realWidth = powerOfTwo(width);
147
    int realHeight = powerOfTwo(height);
148
149
    if (realWidth < width || realHeight < height)
150
    {
151
        reportAlways("Warning: image too large, cropping to %dx%d texture!",
152
            tmpImage->w, tmpImage->h)
153
    }
154
155
#ifdef USE_SDL2
156
    SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE);
157
#else  // USE_SDL2
158
159
    // Make sure the alpha channel is not used, but copied to destination
160
    SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
161
#endif  // USE_SDL2
162
163
    // Determine 32-bit masks based on byte order
164
    uint32_t rmask, gmask, bmask, amask;
165
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
166
    rmask = 0xff000000U;
167
    gmask = 0x00ff0000U;
168
    bmask = 0x0000ff00U;
169
    amask = 0x000000ffU;
170
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
171
172
    rmask = 0x000000ffU;
173
    gmask = 0x0000ff00U;
174
    bmask = 0x00ff0000U;
175
    amask = 0xff000000U;
176
#endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
177
178
    if (tmpImage->format->BitsPerPixel != 32
179
        || realWidth != width || realHeight != height
180
        || rmask != tmpImage->format->Rmask
181
        || gmask != tmpImage->format->Gmask
182
        || amask != tmpImage->format->Amask)
183
    {
184
        SDL_Surface *oldImage = tmpImage;
185
#ifdef USE_SDL2
186
        SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE);
187
#endif  // USE_SDL2
188
189
        tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight,
190
            32, rmask, gmask, bmask, amask);
191
192
        if (tmpImage == nullptr)
193
        {
194
            reportAlways("Error, image convert failed: out of memory")
195
            return nullptr;
196
        }
197
        SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr);
198
    }
199
    return tmpImage;
200
}
201
202
SDL_Surface *OpenGLImageHelper::convertSurface(SDL_Surface *tmpImage,
203
                                               int width, int height)
204
{
205
    if (tmpImage == nullptr)
206
        return nullptr;
207
208
#ifdef USE_SDL2
209
    SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE);
210
#else  // USE_SDL2
211
212
    // Make sure the alpha channel is not used, but copied to destination
213
    SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
214
#endif  // USE_SDL2
215
216
    // Determine 32-bit masks based on byte order
217
    uint32_t rmask, gmask, bmask, amask;
218
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
219
    rmask = 0xff000000U;
220
    gmask = 0x00ff0000U;
221
    bmask = 0x0000ff00U;
222
    amask = 0x000000ffU;
223
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
224
225
    rmask = 0x000000ffU;
226
    gmask = 0x0000ff00U;
227
    bmask = 0x00ff0000U;
228
    amask = 0xff000000U;
229
#endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
230
231
    if (tmpImage->format->BitsPerPixel != 32
232
        || rmask != tmpImage->format->Rmask
233
        || gmask != tmpImage->format->Gmask
234
        || amask != tmpImage->format->Amask)
235
    {
236
        SDL_Surface *oldImage = tmpImage;
237
#ifdef USE_SDL2
238
        SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE);
239
#endif  // USE_SDL2
240
241
        tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
242
            32, rmask, gmask, bmask, amask);
243
244
        if (tmpImage == nullptr)
245
        {
246
            reportAlways("Error, image convert failed: out of memory")
247
            return nullptr;
248
        }
249
        SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr);
250
    }
251
    return tmpImage;
252
}
253
254
void OpenGLImageHelper::bindTexture(const GLuint texture)
255
{
256
    switch (mUseOpenGL)
257
    {
258
#ifdef ANDROID
259
        case RENDER_NORMAL_OPENGL:
260
        case RENDER_SAFE_OPENGL:
261
        case RENDER_MODERN_OPENGL:
262
        case RENDER_GLES2_OPENGL:
263
            break;
264
        case RENDER_GLES_OPENGL:
265
            MobileOpenGLGraphics::bindTexture(mTextureType, texture);
266
            break;
267
#elif defined(__native_client__) || defined(__SWITCH__)
268
        case RENDER_NORMAL_OPENGL:
269
        case RENDER_MODERN_OPENGL:
270
        case RENDER_GLES_OPENGL:
271
            break;
272
        case RENDER_SAFE_OPENGL:
273
#ifndef __SWITCH__
274
            SafeOpenGLGraphics::bindTexture(mTextureType, texture);
275
#endif
276
            break;
277
        case RENDER_GLES2_OPENGL:
278
            MobileOpenGL2Graphics::bindTexture(mTextureType, texture);
279
            break;
280
#else  // ANDROID
281
282
        case RENDER_NORMAL_OPENGL:
283
            NormalOpenGLGraphics::bindTexture(mTextureType, texture);
284
            break;
285
        case RENDER_MODERN_OPENGL:
286
            ModernOpenGLGraphics::bindTexture(mTextureType, texture);
287
            break;
288
        case RENDER_SAFE_OPENGL:
289
            SafeOpenGLGraphics::bindTexture(mTextureType, texture);
290
            break;
291
        case RENDER_GLES_OPENGL:
292
            MobileOpenGLGraphics::bindTexture(mTextureType, texture);
293
            break;
294
        case RENDER_GLES2_OPENGL:
295
            MobileOpenGL2Graphics::bindTexture(mTextureType, texture);
296
            break;
297
#endif  // ANDROID
298
299
        case RENDER_SOFTWARE:
300
        case RENDER_SDL2_DEFAULT:
301
        case RENDER_NULL:
302
        case RENDER_LAST:
303
        default:
304
            reportAlways("Unknown OpenGL backend: %d", mUseOpenGL)
305
            break;
306
    }
307
}
308
309
Image *OpenGLImageHelper::glLoad(SDL_Surface *tmpImage,
310
                                 int width, int height)
311
{
312
    if (tmpImage == nullptr)
313
        return nullptr;
314
315
    BLOCK_START("OpenGLImageHelper::glLoad")
316
    // Flush current error flag.
317
    graphicsManager.getLastError();
318
319
    if (width == 0)
320
        width = tmpImage->w;
321
    if (height == 0)
322
        height = tmpImage->h;
323
324
    SDL_Surface *oldImage = tmpImage;
325
    tmpImage = convertSurfaceNormalize(tmpImage, width, height);
326
    if (tmpImage == nullptr)
327
        return nullptr;
328
329
    const int realWidth = tmpImage->w;
330
    const int realHeight = tmpImage->h;
331
332
    const GLuint texture = getNewTexture();
333
    bindTexture(texture);
334
335
    if (SDL_MUSTLOCK(tmpImage))
336
        SDL_LockSurface(tmpImage);
337
338
    if (mUseOpenGL != RENDER_MODERN_OPENGL &&
339
        mUseOpenGL != RENDER_GLES_OPENGL &&
340
        mUseOpenGL != RENDER_GLES2_OPENGL)
341
    {
342
#ifndef __SWITCH__
343
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
344
#endif
345
#ifdef OPENGLERRORS
346
        graphicsManager.logError();
347
#endif  // OPENGLERRORS
348
    }
349
350
    if (!mUseTextureSampler)
351
    {
352
        if (mBlur)
353
        {
354
            mglTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
355
#ifdef OPENGLERRORS
356
            graphicsManager.logError();
357
#endif  // OPENGLERRORS
358
359
            mglTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
360
#ifdef OPENGLERRORS
361
            graphicsManager.logError();
362
#endif  // OPENGLERRORS
363
        }
364
        else
365
        {
366
            mglTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
367
#ifdef OPENGLERRORS
368
            graphicsManager.logError();
369
#endif  // OPENGLERRORS
370
371
            mglTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
372
#ifdef OPENGLERRORS
373
            graphicsManager.logError();
374
#endif  // OPENGLERRORS
375
        }
376
    }
377
#if !defined(ANDROID) && !defined(__native_client__)
378
    mglTexParameteri(mTextureType, GL_TEXTURE_MAX_LEVEL, 0);
379
#endif  // !defined(ANDROID) && !defined(__native_client__)
380
381
    mglTexImage2D(mTextureType, 0, mInternalTextureType,
382
        tmpImage->w, tmpImage->h,
383
        0, GL_RGBA, GL_UNSIGNED_BYTE, tmpImage->pixels);
384
#ifdef OPENGLERRORS
385
    graphicsManager.logError();
386
#endif  // OPENGLERRORS
387
388
#ifdef DEBUG_OPENGL
389
/*
390
    disabled for now, because debugger can't show it
391
    if (isGLNotNull(mglLabelObject))
392
    {
393
        const char *const text = "image text";
394
        mglLabelObject(GL_TEXTURE, texture, strlen(text), text);
395
    }
396
*/
397
#endif  // DEBUG_OPENGL
398
399
/*
400
    GLint compressed;
401
    glGetTexLevelParameteriv(mTextureType, 0,
402
        GL_TEXTURE_COMPRESSED_ARB, &compressed);
403
    if (compressed)
404
        logger->log("image compressed");
405
    else
406
        logger->log("image not compressed");
407
*/
408
409
#ifdef DEBUG_OPENGL_LEAKS
410
    textures_count ++;
411
#endif  // DEBUG_OPENGL_LEAKS
412
413
    if (SDL_MUSTLOCK(tmpImage))
414
        SDL_UnlockSurface(tmpImage);
415
416
    if (oldImage != tmpImage)
417
        MSDL_FreeSurface(tmpImage);
418
419
    GLenum error = graphicsManager.getLastError();
420
    if (error != 0U)
421
    {
422
        std::string errmsg = GraphicsManager::errorToString(error);
423
        reportAlways("Error: Image GL import failed: %s (%u)",
424
            errmsg.c_str(), error)
425
//        return nullptr;
426
    }
427
428
    BLOCK_END("OpenGLImageHelper::glLoad")
429
    return new Image(texture, width, height, realWidth, realHeight);
430
}
431
432
void OpenGLImageHelper::initTextureSampler(const GLint id)
433
{
434
    if (mBlur)
435
    {
436
        mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
437
        mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
438
    }
439
    else
440
    {
441
        mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
442
        mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
443
    }
444
}
445
446
SDL_Surface *OpenGLImageHelper::create32BitSurface(int width,
447
                                                   int height) const
448
{
449
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
450
    const uint32_t rmask = 0xff000000U;
451
    const uint32_t gmask = 0x00ff0000U;
452
    const uint32_t bmask = 0x0000ff00U;
453
    const uint32_t amask = 0x000000ffU;
454
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
455
456
    const uint32_t rmask = 0x000000ffU;
457
    const uint32_t gmask = 0x0000ff00U;
458
    const uint32_t bmask = 0x00ff0000U;
459
    const uint32_t amask = 0xff000000U;
460
#endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
461
462
    width = powerOfTwo(width);
463
    height = powerOfTwo(height);
464
465
    return MSDL_CreateRGBSurface(SDL_SWSURFACE,
466
        width, height, 32, rmask, gmask, bmask, amask);
467
}
468
469
GLuint OpenGLImageHelper::getNewTexture()
470
{
471
    GLuint texture = mTextures[mFreeTextureIndex];
472
    mFreeTextureIndex ++;
473
    if (mFreeTextureIndex == texturesSize)
474
    {
475
        mFreeTextureIndex = 0;
476
        postInit();
477
    }
478
    return texture;
479
}
480
481
void OpenGLImageHelper::postInit()
482
{
483
    mglGenTextures(texturesSize, &mTextures[mFreeTextureIndex]);
484
}
485
486
void OpenGLImageHelper::invalidate(const GLuint textureId)
487
{
488
    if (isGLNotNull(mglInvalidateTexImage))
489
    {
490
        logger->log("invalidate: %u", textureId);
491
        mglInvalidateTexImage(textureId, 0);
492
    }
493
}
494
495
void OpenGLImageHelper::copySurfaceToImage(const Image *const image,
496
                                           const int x, const int y,
497
                                           SDL_Surface *surface) const
498
{
499
    if (surface == nullptr || image == nullptr)
500
        return;
501
502
    SDL_Surface *const oldSurface = surface;
503
    surface = convertSurface(surface, surface->w, surface->h);
504
    if (surface == nullptr)
505
        return;
506
507
    // +++ probably need combine
508
    // mglTextureSubImage2D and mglTextureSubImage2DEXT
509
    if (mglTextureSubImage2D != nullptr)
510
    {
511
        mglTextureSubImage2D(image->mGLImage,
512
            0,
513
            x, y,
514
            surface->w, surface->h,
515
            GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
516
    }
517
    else
518
    {
519
        mglTextureSubImage2DEXT(image->mGLImage,
520
            mTextureType, 0,
521
            x, y,
522
            surface->w, surface->h,
523
            GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
524
    }
525
#ifdef OPENGLERRORS
526
    graphicsManager.logError();
527
#endif  // OPENGLERRORS
528
529
    if (surface != oldSurface)
530
        MSDL_FreeSurface(surface);
531
}
532
533
#endif  // USE_OPENGL