GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/safeopenglimagehelper.cpp Lines: 0 165 0.0 %
Date: 2021-03-17 Branches: 0 127 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/safeopenglimagehelper.h"
25
26
#if defined(USE_OPENGL) && !defined(ANDROID) && !defined(__SWITCH__)
27
28
#include "graphicsmanager.h"
29
30
#include "render/mobileopengl2graphics.h"
31
#include "render/mobileopenglgraphics.h"
32
#include "render/modernopenglgraphics.h"
33
#include "render/normalopenglgraphics.h"
34
#include "render/safeopenglgraphics.h"
35
36
#include "render/opengl/mgl.h"
37
#include "render/opengl/mglcheck.h"
38
39
#include "resources/dye/dye.h"
40
#include "resources/dye/dyepalette.h"
41
42
#include "resources/image/image.h"
43
44
#include "utils/sdlcheckutils.h"
45
46
#include "debug.h"
47
48
#ifndef SDL_BIG_ENDIAN
49
#error missing SDL_endian.h
50
#endif  // SDL_BYTEORDER
51
52
int SafeOpenGLImageHelper::mTextureType = 0;
53
int SafeOpenGLImageHelper::mInternalTextureType = GL_RGBA8;
54
int SafeOpenGLImageHelper::mTextureSize = 0;
55
bool SafeOpenGLImageHelper::mBlur = true;
56
bool SafeOpenGLImageHelper::mUseTextureSampler = false;
57
58
SafeOpenGLImageHelper::~SafeOpenGLImageHelper()
59
{
60
    glDeleteTextures(static_cast<GLsizei>(texturesSize - mFreeTextureIndex),
61
        &mTextures[mFreeTextureIndex]);
62
}
63
64
Image *SafeOpenGLImageHelper::load(SDL_RWops *const rw,
65
                                   Dye const &dye)
66
{
67
    SDL_Surface *const tmpImage = loadPng(rw);
68
    if (tmpImage == nullptr)
69
    {
70
        logger->log("Error, image load failed: %s", SDL_GetError());
71
        return nullptr;
72
    }
73
74
    SDL_Surface *const surf = convertTo32Bit(tmpImage);
75
    MSDL_FreeSurface(tmpImage);
76
    if (surf == nullptr)
77
        return nullptr;
78
79
    uint32_t *pixels = static_cast<uint32_t *>(surf->pixels);
80
    const int type = dye.getType();
81
82
    switch (type)
83
    {
84
        case 1:
85
        {
86
            const DyePalette *const pal = dye.getSPalete();
87
            if (pal != nullptr)
88
                DYEPALETTEP(pal, SOGLColor)(pixels, surf->w * surf->h);
89
            break;
90
        }
91
        case 2:
92
        {
93
            const DyePalette *const pal = dye.getAPalete();
94
            if (pal != nullptr)
95
                DYEPALETTEP(pal, AOGLColor)(pixels, surf->w * surf->h);
96
            break;
97
        }
98
        case 0:
99
        default:
100
        {
101
            dye.normalOGLDye(pixels, surf->w * surf->h);
102
            break;
103
        }
104
    }
105
106
    Image *const image = loadSurface(surf);
107
    MSDL_FreeSurface(surf);
108
    return image;
109
}
110
111
Image *SafeOpenGLImageHelper::loadSurface(SDL_Surface *const tmpImage)
112
{
113
    return glLoad(tmpImage);
114
}
115
116
Image *SafeOpenGLImageHelper::createTextSurface(SDL_Surface *const tmpImage,
117
                                                const int width,
118
                                                const int height,
119
                                                const float alpha)
120
{
121
    if (tmpImage == nullptr)
122
        return nullptr;
123
124
    Image *const img = glLoad(tmpImage, width, height);
125
    if (img != nullptr)
126
        img->setAlpha(alpha);
127
    return img;
128
}
129
130
int SafeOpenGLImageHelper::powerOfTwo(const int input)
131
{
132
    int value = 1;
133
    while (value < input && value < mTextureSize)
134
        value <<= 1;
135
    return value >= mTextureSize ? mTextureSize : value;
136
}
137
138
SDL_Surface *SafeOpenGLImageHelper::convertSurfaceNormalize(SDL_Surface
139
                                                            *tmpImage,
140
                                                            int width,
141
                                                            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
        logger->log("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
            logger->log("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 *SafeOpenGLImageHelper::convertSurface(SDL_Surface *tmpImage,
203
                                                   int width,
204
                                                   int height)
205
{
206
    if (tmpImage == nullptr)
207
        return nullptr;
208
209
#ifdef USE_SDL2
210
    SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE);
211
#else  // USE_SDL2
212
213
    // Make sure the alpha channel is not used, but copied to destination
214
    SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
215
#endif  // USE_SDL2
216
217
    // Determine 32-bit masks based on byte order
218
    uint32_t rmask, gmask, bmask, amask;
219
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
220
    rmask = 0xff000000U;
221
    gmask = 0x00ff0000U;
222
    bmask = 0x0000ff00U;
223
    amask = 0x000000ffU;
224
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
225
226
    rmask = 0x000000ffU;
227
    gmask = 0x0000ff00U;
228
    bmask = 0x00ff0000U;
229
    amask = 0xff000000U;
230
#endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
231
232
    if (tmpImage->format->BitsPerPixel != 32
233
        || rmask != tmpImage->format->Rmask
234
        || gmask != tmpImage->format->Gmask
235
        || amask != tmpImage->format->Amask)
236
    {
237
        SDL_Surface *oldImage = tmpImage;
238
#ifdef USE_SDL2
239
        SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE);
240
#endif  // USE_SDL2
241
242
        tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
243
            32, rmask, gmask, bmask, amask);
244
245
        if (tmpImage == nullptr)
246
        {
247
            logger->log("Error, image convert failed: out of memory");
248
            return nullptr;
249
        }
250
        SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr);
251
    }
252
    return tmpImage;
253
}
254
255
void SafeOpenGLImageHelper::bindTexture(const GLuint texture)
256
{
257
    switch (mUseOpenGL)
258
    {
259
#ifdef __native_client__
260
        case RENDER_NORMAL_OPENGL:
261
        case RENDER_MODERN_OPENGL:
262
        case RENDER_GLES_OPENGL:
263
            break;
264
        case RENDER_SAFE_OPENGL:
265
            SafeOpenGLGraphics::bindTexture(mTextureType, texture);
266
            break;
267
        case RENDER_GLES2_OPENGL:
268
            MobileOpenGL2Graphics::bindTexture(mTextureType, texture);
269
            break;
270
#elif defined(ANDROID)
271
        case RENDER_NORMAL_OPENGL:
272
        case RENDER_MODERN_OPENGL:
273
        case RENDER_SAFE_OPENGL:
274
        case RENDER_GLES2_OPENGL:
275
            break;
276
        case RENDER_GLES_OPENGL:
277
            MobileOpenGLGraphics::bindTexture(mTextureType, texture);
278
            break;
279
#else  // __native_client__
280
        case RENDER_NORMAL_OPENGL:
281
            NormalOpenGLGraphics::bindTexture(mTextureType, texture);
282
            break;
283
        case RENDER_MODERN_OPENGL:
284
            ModernOpenGLGraphics::bindTexture(mTextureType, texture);
285
            break;
286
        case RENDER_SAFE_OPENGL:
287
            SafeOpenGLGraphics::bindTexture(mTextureType, texture);
288
            break;
289
        case RENDER_GLES_OPENGL:
290
            MobileOpenGLGraphics::bindTexture(mTextureType, texture);
291
            break;
292
        case RENDER_GLES2_OPENGL:
293
            MobileOpenGL2Graphics::bindTexture(mTextureType, texture);
294
            break;
295
#endif  // __native_client__
296
        case RENDER_SOFTWARE:
297
        case RENDER_SDL2_DEFAULT:
298
        case RENDER_NULL:
299
        case RENDER_LAST:
300
        default:
301
            logger->log("Unknown OpenGL backend: %d", mUseOpenGL);
302
            break;
303
    }
304
}
305
306
Image *SafeOpenGLImageHelper::glLoad(SDL_Surface *tmpImage,
307
                                     int width,
308
                                     int height)
309
{
310
    if (tmpImage == nullptr)
311
        return nullptr;
312
313
    BLOCK_START("SafeOpenGLImageHelper::glLoad")
314
    // Flush current error flag.
315
    graphicsManager.getLastError();
316
317
    if (width == 0)
318
        width = tmpImage->w;
319
    if (height == 0)
320
        height = tmpImage->h;
321
322
    SDL_Surface *oldImage = tmpImage;
323
    tmpImage = convertSurfaceNormalize(tmpImage, width, height);
324
    if (tmpImage == nullptr)
325
        return nullptr;
326
327
    const int realWidth = tmpImage->w;
328
    const int realHeight = tmpImage->h;
329
330
    const GLuint texture = getNewTexture();
331
    bindTexture(texture);
332
333
    if (SDL_MUSTLOCK(tmpImage))
334
        SDL_LockSurface(tmpImage);
335
336
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
337
338
    if (!mUseTextureSampler)
339
    {
340
        if (mBlur)
341
        {
342
            glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
343
            glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
344
        }
345
        else
346
        {
347
            glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
348
            glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
349
        }
350
    }
351
#ifndef ANDROID
352
    glTexParameteri(mTextureType, GL_TEXTURE_MAX_LEVEL, 0);
353
#endif  // ANDROID
354
355
    glTexImage2D(mTextureType, 0, mInternalTextureType,
356
        tmpImage->w, tmpImage->h,
357
        0, GL_RGBA, GL_UNSIGNED_BYTE, tmpImage->pixels);
358
359
#ifdef DEBUG_OPENGL
360
/*
361
    disabled for now, because debugger can't show it
362
    if (isGLNotNull(mglLabelObject))
363
    {
364
        const char *const text = "image text";
365
        mglLabelObject(GL_TEXTURE, texture, strlen(text), text);
366
    }
367
*/
368
#endif  // DEBUG_OPENGL
369
370
/*
371
    GLint compressed;
372
    glGetTexLevelParameteriv(mTextureType, 0,
373
        GL_TEXTURE_COMPRESSED_ARB, &compressed);
374
    if (compressed)
375
        logger->log("image compressed");
376
    else
377
        logger->log("image not compressed");
378
*/
379
380
#ifdef DEBUG_OPENGL_LEAKS
381
    textures_count ++;
382
#endif  // DEBUG_OPENGL_LEAKS
383
384
    if (SDL_MUSTLOCK(tmpImage))
385
        SDL_UnlockSurface(tmpImage);
386
387
    if (oldImage != tmpImage)
388
        MSDL_FreeSurface(tmpImage);
389
390
    GLenum error = graphicsManager.getLastError();
391
    if (error != 0U)
392
    {
393
        std::string errmsg = GraphicsManager::errorToString(error);
394
        logger->log("Error: Image GL import failed: %s (%u)",
395
            errmsg.c_str(), error);
396
//        return nullptr;
397
    }
398
399
    BLOCK_END("SafeOpenGLImageHelper::glLoad")
400
    return new Image(texture, width, height, realWidth, realHeight);
401
}
402
403
void SafeOpenGLImageHelper::initTextureSampler(const GLint id)
404
{
405
    if (mBlur)
406
    {
407
        mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
408
        mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
409
    }
410
    else
411
    {
412
        mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
413
        mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
414
    }
415
}
416
417
SDL_Surface *SafeOpenGLImageHelper::create32BitSurface(int width,
418
                                                       int height) const
419
{
420
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
421
    const uint32_t rmask = 0xff000000U;
422
    const uint32_t gmask = 0x00ff0000U;
423
    const uint32_t bmask = 0x0000ff00U;
424
    const uint32_t amask = 0x000000ffU;
425
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
426
427
    const uint32_t rmask = 0x000000ffU;
428
    const uint32_t gmask = 0x0000ff00U;
429
    const uint32_t bmask = 0x00ff0000U;
430
    const uint32_t amask = 0xff000000U;
431
#endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
432
433
    width = powerOfTwo(width);
434
    height = powerOfTwo(height);
435
436
    return MSDL_CreateRGBSurface(SDL_SWSURFACE,
437
        width, height, 32, rmask, gmask, bmask, amask);
438
}
439
440
GLuint SafeOpenGLImageHelper::getNewTexture()
441
{
442
    GLuint texture = mTextures[mFreeTextureIndex];
443
    mFreeTextureIndex ++;
444
    if (mFreeTextureIndex == texturesSize)
445
    {
446
        mFreeTextureIndex = 0;
447
        postInit();
448
    }
449
    return texture;
450
}
451
452
void SafeOpenGLImageHelper::postInit()
453
{
454
    glGenTextures(texturesSize, &mTextures[mFreeTextureIndex]);
455
}
456
457
void SafeOpenGLImageHelper::invalidate(const GLuint textureId)
458
{
459
    if (isGLNotNull(mglInvalidateTexImage))
460
    {
461
        logger->log("invalidate: %u", textureId);
462
        mglInvalidateTexImage(textureId, 0);
463
    }
464
}
465
466
void SafeOpenGLImageHelper::copySurfaceToImage(const Image *const image,
467
                                               const int x,
468
                                               const int y,
469
                                               SDL_Surface *surface) const
470
{
471
    if (surface == nullptr || image == nullptr)
472
        return;
473
474
    SDL_Surface *const oldSurface = surface;
475
    surface = convertSurface(surface, surface->w, surface->h);
476
    if (surface == nullptr)
477
        return;
478
479
    // +++ probably need combine
480
    // mglTextureSubImage2D and mglTextureSubImage2DEXT
481
    if (mglTextureSubImage2D != nullptr)
482
    {
483
        mglTextureSubImage2D(image->mGLImage,
484
            0,
485
            x, y,
486
            surface->w, surface->h,
487
            GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
488
    }
489
    else
490
    {
491
        mglTextureSubImage2DEXT(image->mGLImage,
492
            mTextureType, 0,
493
            x, y,
494
            surface->w, surface->h,
495
            GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
496
    }
497
498
    if (surface != oldSurface)
499
        MSDL_FreeSurface(surface);
500
}
501
502
#endif  // defined(USE_OPENGL) && !defined(ANDROID)