GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/openglimagehelper.cpp Lines: 0 169 0.0 %
Date: 2017-11-29 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-2017  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 = 0xff000000;
166
    gmask = 0x00ff0000;
167
    bmask = 0x0000ff00;
168
    amask = 0x000000ff;
169
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
170
171
    rmask = 0x000000ff;
172
    gmask = 0x0000ff00;
173
    bmask = 0x00ff0000;
174
    amask = 0xff000000;
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 = 0xff000000;
219
    gmask = 0x00ff0000;
220
    bmask = 0x0000ff00;
221
    amask = 0x000000ff;
222
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
223
224
    rmask = 0x000000ff;
225
    gmask = 0x0000ff00;
226
    bmask = 0x00ff0000;
227
    amask = 0xff000000;
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__)
267
        case RENDER_NORMAL_OPENGL:
268
        case RENDER_MODERN_OPENGL:
269
        case RENDER_GLES_OPENGL:
270
            break;
271
        case RENDER_SAFE_OPENGL:
272
            SafeOpenGLGraphics::bindTexture(mTextureType, texture);
273
            break;
274
        case RENDER_GLES2_OPENGL:
275
            MobileOpenGL2Graphics::bindTexture(mTextureType, texture);
276
            break;
277
#else  // ANDROID
278
279
        case RENDER_NORMAL_OPENGL:
280
            NormalOpenGLGraphics::bindTexture(mTextureType, texture);
281
            break;
282
        case RENDER_MODERN_OPENGL:
283
            ModernOpenGLGraphics::bindTexture(mTextureType, texture);
284
            break;
285
        case RENDER_SAFE_OPENGL:
286
            SafeOpenGLGraphics::bindTexture(mTextureType, texture);
287
            break;
288
        case RENDER_GLES_OPENGL:
289
            MobileOpenGLGraphics::bindTexture(mTextureType, texture);
290
            break;
291
        case RENDER_GLES2_OPENGL:
292
            MobileOpenGL2Graphics::bindTexture(mTextureType, texture);
293
            break;
294
#endif  // ANDROID
295
296
        case RENDER_SOFTWARE:
297
        case RENDER_SDL2_DEFAULT:
298
        case RENDER_NULL:
299
        case RENDER_LAST:
300
        default:
301
            reportAlways("Unknown OpenGL backend: %d", mUseOpenGL);
302
            break;
303
    }
304
}
305
306
Image *OpenGLImageHelper::glLoad(SDL_Surface *tmpImage,
307
                                 int width, int height)
308
{
309
    if (tmpImage == nullptr)
310
        return nullptr;
311
312
    BLOCK_START("OpenGLImageHelper::glLoad")
313
    // Flush current error flag.
314
    graphicsManager.getLastError();
315
316
    if (width == 0)
317
        width = tmpImage->w;
318
    if (height == 0)
319
        height = tmpImage->h;
320
321
    SDL_Surface *oldImage = tmpImage;
322
    tmpImage = convertSurfaceNormalize(tmpImage, width, height);
323
    if (tmpImage == nullptr)
324
        return nullptr;
325
326
    const int realWidth = tmpImage->w;
327
    const int realHeight = tmpImage->h;
328
329
    const GLuint texture = getNewTexture();
330
    bindTexture(texture);
331
332
    if (SDL_MUSTLOCK(tmpImage))
333
        SDL_LockSurface(tmpImage);
334
335
    if (mUseOpenGL != RENDER_MODERN_OPENGL &&
336
        mUseOpenGL != RENDER_GLES_OPENGL &&
337
        mUseOpenGL != RENDER_GLES2_OPENGL)
338
    {
339
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
340
#ifdef OPENGLERRORS
341
        graphicsManager.logError();
342
#endif  // OPENGLERRORS
343
    }
344
345
    if (!mUseTextureSampler)
346
    {
347
        if (mBlur)
348
        {
349
            mglTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
350
#ifdef OPENGLERRORS
351
            graphicsManager.logError();
352
#endif  // OPENGLERRORS
353
354
            mglTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
355
#ifdef OPENGLERRORS
356
            graphicsManager.logError();
357
#endif  // OPENGLERRORS
358
        }
359
        else
360
        {
361
            mglTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
362
#ifdef OPENGLERRORS
363
            graphicsManager.logError();
364
#endif  // OPENGLERRORS
365
366
            mglTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
367
#ifdef OPENGLERRORS
368
            graphicsManager.logError();
369
#endif  // OPENGLERRORS
370
        }
371
    }
372
#if !defined(ANDROID) && !defined(__native_client__)
373
    mglTexParameteri(mTextureType, GL_TEXTURE_MAX_LEVEL, 0);
374
#endif  // !defined(ANDROID) && !defined(__native_client__)
375
376
    mglTexImage2D(mTextureType, 0, mInternalTextureType,
377
        tmpImage->w, tmpImage->h,
378
        0, GL_RGBA, GL_UNSIGNED_BYTE, tmpImage->pixels);
379
#ifdef OPENGLERRORS
380
    graphicsManager.logError();
381
#endif  // OPENGLERRORS
382
383
#ifdef DEBUG_OPENGL
384
/*
385
    disabled for now, because debugger can't show it
386
    if (isGLNotNull(mglLabelObject))
387
    {
388
        const char *const text = "image text";
389
        mglLabelObject(GL_TEXTURE, texture, strlen(text), text);
390
    }
391
*/
392
#endif  // DEBUG_OPENGL
393
394
/*
395
    GLint compressed;
396
    glGetTexLevelParameteriv(mTextureType, 0,
397
        GL_TEXTURE_COMPRESSED_ARB, &compressed);
398
    if (compressed)
399
        logger->log("image compressed");
400
    else
401
        logger->log("image not compressed");
402
*/
403
404
#ifdef DEBUG_OPENGL_LEAKS
405
    textures_count ++;
406
#endif  // DEBUG_OPENGL_LEAKS
407
408
    if (SDL_MUSTLOCK(tmpImage))
409
        SDL_UnlockSurface(tmpImage);
410
411
    if (oldImage != tmpImage)
412
        MSDL_FreeSurface(tmpImage);
413
414
    GLenum error = graphicsManager.getLastError();
415
    if (error != 0u)
416
    {
417
        std::string errmsg = GraphicsManager::errorToString(error);
418
        reportAlways("Error: Image GL import failed: %s (%u)",
419
            errmsg.c_str(), error);
420
//        return nullptr;
421
    }
422
423
    BLOCK_END("OpenGLImageHelper::glLoad")
424
    return new Image(texture, width, height, realWidth, realHeight);
425
}
426
427
void OpenGLImageHelper::initTextureSampler(const GLint id)
428
{
429
    if (mBlur)
430
    {
431
        mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
432
        mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
433
    }
434
    else
435
    {
436
        mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
437
        mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
438
    }
439
}
440
441
SDL_Surface *OpenGLImageHelper::create32BitSurface(int width,
442
                                                   int height) const
443
{
444
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
445
    const int rmask = 0xff000000;
446
    const int gmask = 0x00ff0000;
447
    const int bmask = 0x0000ff00;
448
    const int amask = 0x000000ff;
449
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
450
451
    const int rmask = 0x000000ff;
452
    const int gmask = 0x0000ff00;
453
    const int bmask = 0x00ff0000;
454
    const int amask = 0xff000000;
455
#endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
456
457
    width = powerOfTwo(width);
458
    height = powerOfTwo(height);
459
460
    return MSDL_CreateRGBSurface(SDL_SWSURFACE,
461
        width, height, 32, rmask, gmask, bmask, amask);
462
}
463
464
GLuint OpenGLImageHelper::getNewTexture()
465
{
466
    GLuint texture = mTextures[mFreeTextureIndex];
467
    mFreeTextureIndex ++;
468
    if (mFreeTextureIndex == texturesSize)
469
    {
470
        mFreeTextureIndex = 0;
471
        postInit();
472
    }
473
    return texture;
474
}
475
476
void OpenGLImageHelper::postInit()
477
{
478
    mglGenTextures(texturesSize, &mTextures[mFreeTextureIndex]);
479
}
480
481
void OpenGLImageHelper::invalidate(const GLuint textureId)
482
{
483
    if (isGLNotNull(mglInvalidateTexImage))
484
    {
485
        logger->log("invalidate: %u", textureId);
486
        mglInvalidateTexImage(textureId, 0);
487
    }
488
}
489
490
void OpenGLImageHelper::copySurfaceToImage(const Image *const image,
491
                                           const int x, const int y,
492
                                           SDL_Surface *surface) const
493
{
494
    if (surface == nullptr || image == nullptr)
495
        return;
496
497
    SDL_Surface *const oldSurface = surface;
498
    surface = convertSurface(surface, surface->w, surface->h);
499
    if (surface == nullptr)
500
        return;
501
502
    // +++ probably need combine
503
    // mglTextureSubImage2D and mglTextureSubImage2DEXT
504
    if (mglTextureSubImage2D != nullptr)
505
    {
506
        mglTextureSubImage2D(image->mGLImage,
507
            0,
508
            x, y,
509
            surface->w, surface->h,
510
            GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
511
    }
512
    else
513
    {
514
        mglTextureSubImage2DEXT(image->mGLImage,
515
            mTextureType, 0,
516
            x, y,
517
            surface->w, surface->h,
518
            GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
519
    }
520
#ifdef OPENGLERRORS
521
    graphicsManager.logError();
522
#endif  // OPENGLERRORS
523
524
    if (surface != oldSurface)
525
        MSDL_FreeSurface(surface);
526
}
527
528
#endif  // USE_OPENGL