GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/image/image.cpp Lines: 52 156 33.3 %
Date: 2018-09-20 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-2018  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
143
Image::Image(const int width,
54
143
             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
429
    mIsAlphaCalculated(false)
74
{
75
    mBounds.x = 0;
76
    mBounds.y = 0;
77
143
    mBounds.w = width;
78
143
    mBounds.h = height;
79
143
}
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",
105
        static_cast<void*>(this));
106
#endif  // DEBUG_IMAGES
107
108
    mBounds.x = 0;
109
    mBounds.y = 0;
110
111
    if (mTexture)
112
    {
113
        mBounds.w = CAST_U16(width);
114
        mBounds.h = CAST_U16(height);
115
116
        mLoaded = true;
117
    }
118
    else
119
    {
120
        mBounds.w = 0;
121
        mBounds.h = 0;
122
    }
123
}
124
#endif  // USE_SDL2
125
126
30866
Image::Image(SDL_Surface *restrict const image, const bool hasAlphaChannel0,
127
30866
             uint8_t *restrict const alphaChannel) :
128
    Resource(),
129
#ifdef USE_OPENGL
130
    mGLImage(0),
131
    mTexWidth(0),
132
    mTexHeight(0),
133
#endif  // USE_OPENGL
134
    mBounds(),
135
    mAlpha(1.0F),
136
    mSDLSurface(image),
137
#ifdef USE_SDL2
138
    mTexture(nullptr),
139
#endif  // USE_SDL2
140
    mAlphaChannel(alphaChannel),
141
    mAlphaCache(),
142
    mLoaded(false),
143
    mHasAlphaChannel(hasAlphaChannel0),
144
    mUseAlphaCache(SDLImageHelper::mEnableAlphaCache),
145
    mIsAlphaVisible(hasAlphaChannel0),
146
92598
    mIsAlphaCalculated(false)
147
{
148
#ifdef DEBUG_IMAGES
149
    logger->log("created image: %p", static_cast<void*>(this));
150
#endif  // DEBUG_IMAGES
151
152
    mBounds.x = 0;
153
    mBounds.y = 0;
154
155
30866
    if (mSDLSurface != nullptr)
156
    {
157
30866
        mBounds.w = CAST_U16(mSDLSurface->w);
158
30866
        mBounds.h = CAST_U16(mSDLSurface->h);
159
160
30866
        mLoaded = true;
161
    }
162
    else
163
    {
164
        mBounds.w = 0;
165
        mBounds.h = 0;
166
    }
167
30866
}
168
169
#ifdef USE_OPENGL
170
Image::Image(const GLuint glimage, const int width, const int height,
171
             const int texWidth, const int texHeight) :
172
    Resource(),
173
    mGLImage(glimage),
174
    mTexWidth(texWidth),
175
    mTexHeight(texHeight),
176
    mBounds(),
177
    mAlpha(1.0F),
178
    mSDLSurface(nullptr),
179
#ifdef USE_SDL2
180
    mTexture(nullptr),
181
#endif  // USE_SDL2
182
    mAlphaChannel(nullptr),
183
    mAlphaCache(),
184
    mLoaded(false),
185
    mHasAlphaChannel(true),
186
    mUseAlphaCache(false),
187
    mIsAlphaVisible(true),
188
    mIsAlphaCalculated(false)
189
{
190
#ifdef DEBUG_IMAGES
191
    logger->log("created image: %p", static_cast<void*>(this));
192
#endif  // DEBUG_IMAGES
193
194
    mBounds.x = 0;
195
    mBounds.y = 0;
196
    mBounds.w = CAST_U16(width);
197
    mBounds.h = CAST_U16(height);
198
199
    if (mGLImage != 0u)
200
    {
201
        mLoaded = true;
202
    }
203
}
204
#endif  // USE_OPENGL
205
206
95030
Image::~Image()
207
{
208
#ifdef DEBUG_IMAGES
209
    logger->log("delete image: %p", static_cast<void*>(this));
210
    logger->log("  %s, %s", mIdPath.c_str(), mSource.c_str());
211
#endif  // DEBUG_IMAGES
212
213
31009
    unload();
214
33012
}
215
216
30866
void Image::SDLCleanCache()
217
{
218
30866
    for (std::map<float, SDL_Surface*>::iterator
219
92598
         i = mAlphaCache.begin(), i_end = mAlphaCache.end();
220
         i != i_end; ++i)
221
    {
222
        if (mSDLSurface != i->second)
223
            ResourceManager::scheduleDelete(i->second);
224
        i->second = nullptr;
225
    }
226
61732
    mAlphaCache.clear();
227
30866
}
228
229
31009
void Image::unload()
230
{
231
31009
    mLoaded = false;
232
233
31009
    if (mSDLSurface != nullptr)
234
    {
235
1860
        SDLCleanCache();
236
        // Free the image surface.
237
1860
        MSDL_FreeSurface(mSDLSurface);
238
1860
        mSDLSurface = nullptr;
239
240
1860
        delete [] mAlphaChannel;
241
1860
        mAlphaChannel = nullptr;
242
    }
243
#ifdef USE_SDL2
244
    if (mTexture)
245
    {
246
        SDL_DestroyTexture(mTexture);
247
        mTexture = nullptr;
248
    }
249
#endif  // USE_SDL2
250
251
#ifdef USE_OPENGL
252
31009
    if (mGLImage != 0u)
253
    {
254
        glDeleteTextures(1, &mGLImage);
255
        mGLImage = 0;
256
#ifdef DEBUG_OPENGL_LEAKS
257
        if (textures_count > 0)
258
            textures_count --;
259
#endif  // DEBUG_OPENGL_LEAKS
260
    }
261
#endif  // USE_OPENGL
262
31009
}
263
264
29006
bool Image::hasAlphaChannel() const
265
{
266
29006
    if (mLoaded)
267
29006
        return mHasAlphaChannel;
268
269
#ifdef USE_OPENGL
270
    if (OpenGLImageHelper::mUseOpenGL != RENDER_SOFTWARE)
271
        return true;
272
#endif  // USE_OPENGL
273
274
    return false;
275
}
276
277
SDL_Surface *Image::getByAlpha(const float alpha)
278
{
279
    const std::map<float, SDL_Surface*>::const_iterator
280
        it = mAlphaCache.find(alpha);
281
    if (it != mAlphaCache.end())
282
        return (*it).second;
283
    return nullptr;
284
}
285
286
5084
void Image::setAlpha(const float alpha)
287
{
288

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

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