GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/image/image.cpp Lines: 52 160 32.5 %
Date: 2017-11-29 Branches: 13 96 13.5 %

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/image/image.h"
24
25
#include "logger.h"
26
27
#ifdef USE_OPENGL
28
#include "resources/openglimagehelper.h"
29
#endif  // USE_OPENGL
30
31
#include "resources/memorymanager.h"
32
#include "resources/sdlimagehelper.h"
33
34
#include "resources/image/subimage.h"
35
36
#include "resources/resourcemanager/resourcemanager.h"
37
38
#include "utils/cast.h"
39
#include "utils/sdlcheckutils.h"
40
41
PRAGMA48(GCC diagnostic push)
42
PRAGMA48(GCC diagnostic ignored "-Wshadow")
43
#ifdef USE_SDL2
44
#include <SDL2_rotozoom.h>
45
#else  // USE_SDL2
46
#include <SDL_rotozoom.h>
47
#endif  // USE_SDL2
48
PRAGMA48(GCC diagnostic pop)
49
50
#include "debug.h"
51
52
#ifdef UNITTESTS
53
286
Image::Image(const int width,
54
286
             const int height) :
55
    Resource(),
56
#ifdef USE_OPENGL
57
    mGLImage(0),
58
    mTexWidth(0),
59
    mTexHeight(0),
60
#endif  // USE_OPENGL
61
    mBounds(),
62
    mAlpha(1.0F),
63
    mSDLSurface(nullptr),
64
#ifdef USE_SDL2
65
    mTexture(nullptr),
66
#endif  // USE_SDL2
67
    mAlphaChannel(nullptr),
68
    mAlphaCache(),
69
    mLoaded(false),
70
    mHasAlphaChannel(false),
71
    mUseAlphaCache(false),
72
    mIsAlphaVisible(true),
73
858
    mIsAlphaCalculated(false)
74
{
75
    mBounds.x = 0;
76
    mBounds.y = 0;
77
286
    mBounds.w = width;
78
286
    mBounds.h = height;
79
286
}
80
#endif  // UNITTESTS
81
82
#ifdef USE_SDL2
83
Image::Image(SDL_Texture *restrict const image,
84
             const int width, const int height) :
85
    Resource(),
86
#ifdef USE_OPENGL
87
    mGLImage(0),
88
    mTexWidth(0),
89
    mTexHeight(0),
90
#endif  // USE_OPENGL
91
    mBounds(),
92
    mAlpha(1.0F),
93
    mSDLSurface(nullptr),
94
    mTexture(image),
95
    mAlphaChannel(nullptr),
96
    mAlphaCache(),
97
    mLoaded(false),
98
    mHasAlphaChannel(false),
99
    mUseAlphaCache(false),
100
    mIsAlphaVisible(true),
101
    mIsAlphaCalculated(false)
102
{
103
#ifdef DEBUG_IMAGES
104
    logger->log("created image: %p", this);
105
#endif  // DEBUG_IMAGES
106
107
    mBounds.x = 0;
108
    mBounds.y = 0;
109
110
    if (mTexture)
111
    {
112
        mBounds.w = CAST_U16(width);
113
        mBounds.h = CAST_U16(height);
114
115
        mLoaded = true;
116
    }
117
    else
118
    {
119
        mBounds.w = 0;
120
        mBounds.h = 0;
121
    }
122
}
123
#endif  // USE_SDL2
124
125
56440
Image::Image(SDL_Surface *restrict const image, const bool hasAlphaChannel0,
126
56440
             uint8_t *restrict const alphaChannel) :
127
    Resource(),
128
#ifdef USE_OPENGL
129
    mGLImage(0),
130
    mTexWidth(0),
131
    mTexHeight(0),
132
#endif  // USE_OPENGL
133
    mBounds(),
134
    mAlpha(1.0F),
135
    mSDLSurface(image),
136
#ifdef USE_SDL2
137
    mTexture(nullptr),
138
#endif  // USE_SDL2
139
    mAlphaChannel(alphaChannel),
140
    mAlphaCache(),
141
    mLoaded(false),
142
    mHasAlphaChannel(hasAlphaChannel0),
143
    mUseAlphaCache(SDLImageHelper::mEnableAlphaCache),
144
    mIsAlphaVisible(hasAlphaChannel0),
145
169320
    mIsAlphaCalculated(false)
146
{
147
#ifdef DEBUG_IMAGES
148
    logger->log("created image: %p", static_cast<void*>(this));
149
#endif  // DEBUG_IMAGES
150
151
    mBounds.x = 0;
152
    mBounds.y = 0;
153
154
56440
    if (mSDLSurface != nullptr)
155
    {
156
56440
        mBounds.w = CAST_U16(mSDLSurface->w);
157
56440
        mBounds.h = CAST_U16(mSDLSurface->h);
158
159
56440
        mLoaded = true;
160
    }
161
    else
162
    {
163
        mBounds.w = 0;
164
        mBounds.h = 0;
165
    }
166
56440
}
167
168
#ifdef USE_OPENGL
169
Image::Image(const GLuint glimage, const int width, const int height,
170
             const int texWidth, const int texHeight) :
171
    Resource(),
172
    mGLImage(glimage),
173
    mTexWidth(texWidth),
174
    mTexHeight(texHeight),
175
    mBounds(),
176
    mAlpha(1.0F),
177
    mSDLSurface(nullptr),
178
#ifdef USE_SDL2
179
    mTexture(nullptr),
180
#endif  // USE_SDL2
181
    mAlphaChannel(nullptr),
182
    mAlphaCache(),
183
    mLoaded(false),
184
    mHasAlphaChannel(true),
185
    mUseAlphaCache(false),
186
    mIsAlphaVisible(true),
187
    mIsAlphaCalculated(false)
188
{
189
#ifdef DEBUG_IMAGES
190
    logger->log("created image: %p", static_cast<void*>(this));
191
#endif  // DEBUG_IMAGES
192
193
    mBounds.x = 0;
194
    mBounds.y = 0;
195
    mBounds.w = CAST_U16(width);
196
    mBounds.h = CAST_U16(height);
197
198
    if (mGLImage != 0u)
199
    {
200
        mLoaded = true;
201
    }
202
}
203
#endif  // USE_OPENGL
204
205
173894
Image::~Image()
206
{
207
#ifdef DEBUG_IMAGES
208
    logger->log("delete image: %p", static_cast<void*>(this));
209
    logger->log("  %s, %s", mIdPath.c_str(), mSource.c_str());
210
#endif  // DEBUG_IMAGES
211
212
56726
    unload();
213
60442
}
214
215
56440
void Image::SDLCleanCache()
216
{
217
    for (std::map<float, SDL_Surface*>::iterator
218
169320
         i = mAlphaCache.begin(), i_end = mAlphaCache.end();
219
56440
         i != i_end; ++i)
220
    {
221
        if (mSDLSurface != i->second)
222
            ResourceManager::scheduleDelete(i->second);
223
        i->second = nullptr;
224
    }
225
112880
    mAlphaCache.clear();
226
56440
}
227
228
56726
void Image::unload()
229
{
230
56726
    mLoaded = false;
231
232
56726
    if (mSDLSurface != nullptr)
233
    {
234
3430
        SDLCleanCache();
235
        // Free the image surface.
236
3430
        MSDL_FreeSurface(mSDLSurface);
237
3430
        mSDLSurface = nullptr;
238
239
3430
        delete [] mAlphaChannel;
240
3430
        mAlphaChannel = nullptr;
241
    }
242
#ifdef USE_SDL2
243
    if (mTexture)
244
    {
245
        SDL_DestroyTexture(mTexture);
246
        mTexture = nullptr;
247
    }
248
#endif  // USE_SDL2
249
250
#ifdef USE_OPENGL
251
56726
    if (mGLImage != 0u)
252
    {
253
        glDeleteTextures(1, &mGLImage);
254
        mGLImage = 0;
255
#ifdef DEBUG_OPENGL_LEAKS
256
        if (textures_count > 0)
257
            textures_count --;
258
#endif  // DEBUG_OPENGL_LEAKS
259
    }
260
#endif  // USE_OPENGL
261
56726
}
262
263
53010
bool Image::hasAlphaChannel() const
264
{
265
53010
    if (mLoaded)
266
53010
        return mHasAlphaChannel;
267
268
#ifdef USE_OPENGL
269
    if (OpenGLImageHelper::mUseOpenGL != RENDER_SOFTWARE)
270
        return true;
271
#endif  // USE_OPENGL
272
273
    return false;
274
}
275
276
SDL_Surface *Image::getByAlpha(const float alpha)
277
{
278
    const std::map<float, SDL_Surface*>::const_iterator
279
        it = mAlphaCache.find(alpha);
280
    if (it != mAlphaCache.end())
281
        return (*it).second;
282
    return nullptr;
283
}
284
285
9954
void Image::setAlpha(const float alpha)
286
{
287

9954
    if (mAlpha == alpha || !ImageHelper::mEnableAlpha)
288
        return;
289
290
    if (alpha < 0.0F || alpha > 1.0F)
291
        return;
292
293
    if (mSDLSurface != nullptr)
294
    {
295
        if (mUseAlphaCache)
296
        {
297
            SDL_Surface *surface = getByAlpha(mAlpha);
298
            if (surface == nullptr)
299
            {
300
                if (mAlphaCache.size() > 100)
301
                {
302
#ifdef DEBUG_ALPHA_CACHE
303
                    logger->log("cleanCache");
304
                    for (std::map<float, SDL_Surface*>::const_iterator
305
                         i = mAlphaCache.begin(), i_end = mAlphaCache.end();
306
                         i != i_end; ++i)
307
                    {
308
                        logger->log("alpha: " + toString(i->first));
309
                    }
310
#endif  // DEBUG_ALPHA_CACHE
311
312
                    SDLCleanCache();
313
                }
314
                surface = mSDLSurface;
315
                if (surface != nullptr)
316
                    mAlphaCache[mAlpha] = surface;
317
            }
318
            else
319
            {
320
                logger->log("cache bug");
321
            }
322
323
            surface = getByAlpha(alpha);
324
            if (surface != nullptr)
325
            {
326
                if (mSDLSurface == surface)
327
                    logger->log("bug");
328
                mAlphaCache.erase(alpha);
329
                mSDLSurface = surface;
330
                mAlpha = alpha;
331
                return;
332
            }
333
            mSDLSurface = SDLImageHelper::SDLDuplicateSurface(mSDLSurface);
334
            if (mSDLSurface == nullptr)
335
                return;
336
        }
337
338
        mAlpha = alpha;
339
340
        if (!mHasAlphaChannel)
341
        {
342
#ifdef USE_SDL2
343
            SDL_SetSurfaceAlphaMod(mSDLSurface,
344
                CAST_U8(255 * mAlpha));
345
#else  // USE_SDL2
346
347
            // Set the alpha value this image is drawn at
348
            SDL_SetAlpha(mSDLSurface, SDL_SRCALPHA,
349
                CAST_U8(255 * mAlpha));
350
#endif  // USE_SDL2
351
        }
352
        else
353
        {
354
            if (SDL_MUSTLOCK(mSDLSurface))
355
                SDL_LockSurface(mSDLSurface);
356
357
            const int bx = mBounds.x;
358
            const int by = mBounds.y;
359
            const int bw = mBounds.w;
360
            const int bh = mBounds.h;
361
            const int bxw = bx + bw;
362
            const int sw = mSDLSurface->w;
363
            const int maxHeight = std::min(by + bh, mSDLSurface->h);
364
            const int maxWidth = std::min(bxw, sw);
365
            const int i1 = by * sw + bx;
366
            const SDL_PixelFormat * const fmt = mSDLSurface->format;
367
            const uint32_t amask = fmt->Amask;
368
            const uint32_t invMask = ~fmt->Amask;
369
            const uint8_t aloss = fmt->Aloss;
370
            const uint8_t ashift = fmt->Ashift;
371
372
            if ((bx == 0) && bxw == sw)
373
            {
374
                const int i2 = (maxHeight - 1) * sw + maxWidth - 1;
375
                for (int i = i1; i <= i2; i++)
376
                {
377
                    const uint8_t sourceAlpha = mAlphaChannel[i];
378
                    if (sourceAlpha > 0)
379
                    {
380
                        const uint8_t a = CAST_U8(
381
                            static_cast<float>(sourceAlpha) * mAlpha);
382
383
                        uint32_t c = (static_cast<uint32_t*>(
384
                            mSDLSurface->pixels))[i];
385
                        c &= invMask;
386
                        c |= ((a >> aloss) << ashift & amask);
387
                        (static_cast<uint32_t*>(mSDLSurface->pixels))[i] = c;
388
                    }
389
                }
390
            }
391
            else
392
            {
393
                for (int y = by; y < maxHeight; y ++)
394
                {
395
                    const int idx = y * sw;
396
                    const int x1 = idx + bx;
397
                    const int x2 = idx + maxWidth;
398
                    for (int i = x1; i < x2; i ++)
399
                    {
400
                        const uint8_t sourceAlpha = mAlphaChannel[i];
401
                        if (sourceAlpha > 0)
402
                        {
403
                            const uint8_t a = CAST_U8(
404
                                static_cast<float>(sourceAlpha) * mAlpha);
405
406
                            uint32_t c = (static_cast<uint32_t*>(
407
                                mSDLSurface->pixels))[i];
408
                            c &= invMask;
409
                            c |= ((a >> aloss) << ashift & amask);
410
                            (static_cast<uint32_t*>(
411
                                mSDLSurface->pixels))[i] = c;
412
                        }
413
                    }
414
                }
415
            }
416
417
            if (SDL_MUSTLOCK(mSDLSurface))
418
                SDL_UnlockSurface(mSDLSurface);
419
        }
420
    }
421
#ifdef USE_SDL2
422
    else if (mTexture)
423
    {
424
        mAlpha = alpha;
425
        SDL_SetTextureAlphaMod(mTexture,
426
            CAST_U8(255 * mAlpha));
427
    }
428
#endif  // USE_SDL2
429
    else
430
    {
431
        mAlpha = alpha;
432
    }
433
}
434
435
Image* Image::SDLgetScaledImage(const int width, const int height) const
436
{
437
    // No scaling on incorrect new values.
438
    if (width == 0 || height == 0)
439
        return nullptr;
440
441
    // No scaling when there is ... no different given size ...
442
    if (width == mBounds.w && height == mBounds.h)
443
        return nullptr;
444
445
    Image* scaledImage = nullptr;
446
447
    if (mSDLSurface != nullptr)
448
    {
449
        SDL_Surface *const scaledSurface = zoomSurface(mSDLSurface,
450
                    static_cast<double>(width) / mBounds.w,
451
                    static_cast<double>(height) / mBounds.h,
452
                    1);
453
454
        // The load function takes care of the SDL<->OpenGL implementation
455
        // and about freeing the given SDL_surface*.
456
        if (scaledSurface != nullptr)
457
        {
458
            scaledImage = imageHelper->loadSurface(scaledSurface);
459
            MSDL_FreeSurface(scaledSurface);
460
        }
461
    }
462
    return scaledImage;
463
}
464
465
53010
Image *Image::getSubImage(const int x, const int y,
466
                          const int width, const int height)
467
{
468
    // Create a new clipped sub-image
469
#ifdef USE_OPENGL
470
53010
    const RenderType mode = OpenGLImageHelper::mUseOpenGL;
471
53010
    if (mode == RENDER_NORMAL_OPENGL ||
472
53010
        mode == RENDER_SAFE_OPENGL ||
473
53010
        mode == RENDER_GLES_OPENGL ||
474
106020
        mode == RENDER_GLES2_OPENGL ||
475
        mode == RENDER_MODERN_OPENGL)
476
    {
477
        return new SubImage(this,
478
            mGLImage,
479
            x, y,
480
            width, height,
481
            mTexWidth, mTexHeight);
482
    }
483
#endif  // USE_OPENGL
484
485
#ifdef USE_SDL2
486
#ifndef USE_OPENGL
487
    const RenderType mode = ImageHelper::mUseOpenGL;
488
#endif  // USE_OPENGL
489
490
    if (mode == RENDER_SOFTWARE)
491
        return new SubImage(this, mSDLSurface, x, y, width, height);
492
    else
493
        return new SubImage(this, mTexture, x, y, width, height);
494
#else  // USE_SDL2
495
496
53010
    return new SubImage(this, mSDLSurface, x, y, width, height);
497
#endif  // USE_SDL2
498
}
499
500
53010
void Image::SDLTerminateAlphaCache()
501
{
502
53010
    SDLCleanCache();
503
53010
    mUseAlphaCache = false;
504
53010
}
505
506
int Image::calcMemoryLocal() const
507
{
508
    // +++ this calculation can be wrong for SDL2
509
    int sz = static_cast<int>(sizeof(Image) +
510
        sizeof(std::map<float, SDL_Surface*>)) +
511
        Resource::calcMemoryLocal();
512
    if (mSDLSurface != nullptr)
513
    {
514
        sz += CAST_S32(mAlphaCache.size()) *
515
            memoryManager.getSurfaceSize(mSDLSurface);
516
    }
517
    return sz;
518
}
519
520
#ifdef USE_OPENGL
521
77498
void Image::decRef()
522
{
523

77498
    if ((mGLImage != 0u) && mRefCount <= 1)
524
        OpenGLImageHelper::invalidate(mGLImage);
525
77498
    Resource::decRef();
526
77498
}
527
#endif  // USE_OPENGL