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