GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/being/compoundsprite.cpp Lines: 19 151 12.6 %
Date: 2021-03-17 Branches: 8 116 6.9 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2010  The Mana Developers
4
 *  Copyright (C) 2011-2019  The ManaPlus Developers
5
 *  Copyright (C) 2019-2021  Andrei Karas
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 "being/compoundsprite.h"
24
25
#include "configuration.h"
26
27
#include "sdlshared.h"
28
29
#include "being/compounditem.h"
30
31
#include "render/surfacegraphics.h"
32
33
#if defined(USE_OPENGL) || !defined(USE_SDL2)
34
#include "resources/imagehelper.h"
35
#endif  // USE_OPENGL
36
#include "resources/image/image.h"
37
38
#include "utils/delete2.h"
39
#include "utils/dtor.h"
40
#include "utils/foreach.h"
41
#include "utils/likely.h"
42
#include "utils/sdlcheckutils.h"
43
44
#ifndef USE_SDL2
45
#include "game.h"
46
47
#include "const/resources/map/map.h"
48
49
#include "resources/map/map.h"
50
51
#include "utils/timer.h"
52
53
PRAGMA48(GCC diagnostic push)
54
PRAGMA48(GCC diagnostic ignored "-Wshadow")
55
#ifndef SDL_BIG_ENDIAN
56
#include <SDL_endian.h>
57
#endif  // SDL_BYTEORDER
58
PRAGMA48(GCC diagnostic pop)
59
#endif  // USE_SDL2
60
61
#include "debug.h"
62
63
#ifndef USE_SDL2
64
static const int BUFFER_WIDTH = 100;
65
static const int BUFFER_HEIGHT = 100;
66
static const unsigned cache_max_size = 10;
67
static const unsigned cache_clean_part = 3;
68
#endif  // USE_SDL2
69
70
bool CompoundSprite::mEnableDelay = true;
71
72
103
CompoundSprite::CompoundSprite() :
73
    Sprite(),
74
    mSprites(),
75
    imagesCache(),
76
    mCacheItem(nullptr),
77
    mImage(nullptr),
78
    mAlphaImage(nullptr),
79
    mOffsetX(0),
80
    mOffsetY(0),
81
    mStartTime(0),
82
    mLastTime(0),
83
#ifndef USE_SDL2
84
    mNextRedrawTime(0),
85
#endif  // USE_SDL2
86
    mNeedsRedraw(false),
87

309
    mEnableAlphaFix(config.getBoolValue("enableAlphaFix")),
88

309
    mDisableAdvBeingCaching(config.getBoolValue("disableAdvBeingCaching")),
89

927
    mDisableBeingCaching(config.getBoolValue("disableBeingCaching"))
90
{
91
103
    mAlpha = 1.0F;
92
103
}
93
94
412
CompoundSprite::~CompoundSprite()
95
{
96
103
    clear();
97
103
    mImage = nullptr;
98
103
    mAlphaImage = nullptr;
99
103
}
100
101
bool CompoundSprite::reset()
102
{
103
    bool ret = false;
104
    FOR_EACH (SpriteIterator, it, mSprites)
105
    {
106
        if (*it != nullptr)
107
            ret |= (*it)->reset();
108
    }
109
    if (ret)
110
        mLastTime = 0;
111
    mNeedsRedraw |= ret;
112
    return ret;
113
}
114
115
bool CompoundSprite::play(const std::string &action)
116
{
117
    bool ret = false;
118
    bool ret2 = true;
119
    FOR_EACH (SpriteIterator, it, mSprites)
120
    {
121
        if (*it != nullptr)
122
        {
123
            const bool tmpVal = (*it)->play(action);
124
            ret |= tmpVal;
125
            ret2 &= tmpVal;
126
        }
127
    }
128
    mNeedsRedraw |= ret;
129
    if (ret2)
130
        mLastTime = 0;
131
    return ret;
132
}
133
134
bool CompoundSprite::update(const int time)
135
{
136
    bool ret = false;
137
    if (A_UNLIKELY(mLastTime == 0))
138
        mStartTime = time;
139
    mLastTime = time;
140
    FOR_EACH (SpriteIterator, it, mSprites)
141
    {
142
        if (*it != nullptr)
143
            ret |= (*it)->update(time);
144
    }
145
    mNeedsRedraw |= ret;
146
    return ret;
147
}
148
149
void CompoundSprite::drawSimple(Graphics *const graphics,
150
                                const int posX,
151
                                const int posY) const
152
{
153
    FUNC_BLOCK("CompoundSprite::draw", 1)
154
    if (mNeedsRedraw)
155
        updateImages();
156
157
    if (mSprites.empty())  // Nothing to draw
158
        return;
159
160
    if (mAlpha == 1.0F && (mImage != nullptr))
161
    {
162
        graphics->drawImage(mImage, posX + mOffsetX, posY + mOffsetY);
163
    }
164
    else if ((mAlpha != 0.0F) && (mAlphaImage != nullptr))
165
    {
166
        mAlphaImage->setAlpha(mAlpha);
167
        graphics->drawImage(mAlphaImage,
168
            posX + mOffsetX, posY + mOffsetY);
169
    }
170
    else
171
    {
172
        CompoundSprite::drawSprites(graphics, posX, posY);
173
    }
174
}
175
176
void CompoundSprite::drawSprites(Graphics *const graphics,
177
                                 const int posX,
178
                                 const int posY) const
179
{
180
    FOR_EACH (SpriteConstIterator, it, mSprites)
181
    {
182
        if (*it != nullptr)
183
        {
184
            (*it)->setAlpha(mAlpha);
185
            (*it)->draw(graphics, posX, posY);
186
        }
187
    }
188
}
189
190
void CompoundSprite::drawSpritesSDL(Graphics *const graphics,
191
                                    const int posX,
192
                                    const int posY) const
193
{
194
    FOR_EACH (SpriteConstIterator, it, mSprites)
195
    {
196
        if (*it != nullptr)
197
            (*it)->draw(graphics, posX, posY);
198
    }
199
}
200
201
int CompoundSprite::getWidth() const
202
{
203
    FOR_EACH (SpriteConstIterator, it, mSprites)
204
    {
205
        const Sprite *const base = *it;
206
        if (base != nullptr)
207
            return base->getWidth();
208
    }
209
210
    return 0;
211
}
212
213
int CompoundSprite::getHeight() const
214
{
215
    FOR_EACH (SpriteConstIterator, it, mSprites)
216
    {
217
        const Sprite *const base = *it;
218
        if (base != nullptr)
219
            return base->getHeight();
220
    }
221
222
    return 0;
223
}
224
225
const Image *CompoundSprite::getImage() const
226
{
227
    return mImage;
228
}
229
230
bool CompoundSprite::setSpriteDirection(const SpriteDirection::Type direction)
231
{
232
    bool ret = false;
233
    FOR_EACH (SpriteIterator, it, mSprites)
234
    {
235
        if (*it != nullptr)
236
            ret |= (*it)->setSpriteDirection(direction);
237
    }
238
    if (ret)
239
        mLastTime = 0;
240
    mNeedsRedraw |= ret;
241
    return ret;
242
}
243
244
int CompoundSprite::getNumberOfLayers() const
245
{
246
    if ((mImage != nullptr) || (mAlphaImage != nullptr))
247
        return 1;
248
    return CAST_S32(mSprites.size());
249
}
250
251
unsigned int CompoundSprite::getCurrentFrame() const
252
{
253
    FOR_EACH (SpriteConstIterator, it, mSprites)
254
    {
255
        if (*it != nullptr)
256
            return (*it)->getCurrentFrame();
257
    }
258
    return 0;
259
}
260
261
unsigned int CompoundSprite::getFrameCount() const
262
{
263
    FOR_EACH (SpriteConstIterator, it, mSprites)
264
    {
265
        if (*it != nullptr)
266
            return (*it)->getFrameCount();
267
    }
268
    return 0;
269
}
270
271
void CompoundSprite::addSprite(Sprite *const sprite)
272
{
273
    mSprites.push_back(sprite);
274
    mNeedsRedraw = true;
275
}
276
277
void CompoundSprite::setSprite(const size_t layer, Sprite *const sprite)
278
{
279
    // Skip if it won't change anything
280
    if (mSprites[layer] == sprite)
281
        return;
282
283
    delete mSprites[layer];
284
    mSprites[layer] = sprite;
285
    mNeedsRedraw = true;
286
}
287
288
void CompoundSprite::removeSprite(const int layer)
289
{
290
    // Skip if it won't change anything
291
    if (mSprites[layer] == nullptr)
292
        return;
293
294
    delete2(mSprites[layer])
295
    mNeedsRedraw = true;
296
}
297
298
103
void CompoundSprite::clear()
299
{
300
    // Skip if it won't change anything
301
206
    if (!mSprites.empty())
302
    {
303
        delete_all(mSprites);
304
        mSprites.clear();
305
    }
306
103
    mNeedsRedraw = true;
307
206
    delete_all(imagesCache);
308
206
    imagesCache.clear();
309
103
    delete2(mCacheItem)
310
103
    mLastTime = 0;
311
103
}
312
313
void CompoundSprite::ensureSize(const size_t layerCount)
314
{
315
    // Skip if it won't change anything
316
    if (mSprites.size() >= layerCount)
317
        return;
318
319
//    resize(layerCount, nullptr);
320
    mSprites.resize(layerCount);
321
}
322
323
void CompoundSprite::redraw() const
324
{
325
#ifndef USE_SDL2
326
327
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
328
    const uint32_t rmask = 0xff000000U;
329
    const uint32_t gmask = 0x00ff0000U;
330
    const uint32_t bmask = 0x0000ff00U;
331
    const uint32_t amask = 0x000000ffU;
332
#else  // SDL_BYTEORDER == SDL_BIG_ENDIAN
333
    const uint32_t rmask = 0x000000ffU;
334
    const uint32_t gmask = 0x0000ff00U;
335
    const uint32_t bmask = 0x00ff0000U;
336
    const uint32_t amask = 0xff000000U;
337
#endif  // SDL_BYTEORDER == SDL_BIG_ENDIAN
338
339
    SDL_Surface *const surface = MSDL_CreateRGBSurface(SDL_HWSURFACE,
340
        BUFFER_WIDTH, BUFFER_HEIGHT, 32, rmask, gmask, bmask, amask);
341
342
    if (surface == nullptr)
343
        return;
344
345
    SurfaceGraphics *graphics = new SurfaceGraphics;
346
    graphics->setBlitMode(BlitMode::BLIT_GFX);
347
    graphics->setTarget(surface);
348
    graphics->beginDraw();
349
350
    int tileX = mapTileSize / 2;
351
    int tileY = mapTileSize;
352
353
    const Game *const game = Game::instance();
354
    if (game != nullptr)
355
    {
356
        const Map *const map = game->getCurrentMap();
357
        if (map != nullptr)
358
        {
359
            tileX = map->getTileWidth() / 2;
360
            tileY = map->getTileWidth();
361
        }
362
    }
363
364
    const int posX = BUFFER_WIDTH / 2 - tileX;
365
    const int posY = BUFFER_HEIGHT - tileY;
366
367
    mOffsetX = tileX - BUFFER_WIDTH / 2;
368
    mOffsetY = tileY - BUFFER_HEIGHT;
369
370
    drawSpritesSDL(graphics, posX, posY);
371
372
    delete2(graphics)
373
374
    SDL_SetAlpha(surface, 0, SDL_ALPHA_OPAQUE);
375
376
    delete mAlphaImage;
377
378
    if (ImageHelper::mEnableAlpha)
379
    {
380
        SDL_Surface *const surfaceA = MSDL_CreateRGBSurface(SDL_HWSURFACE,
381
            BUFFER_WIDTH, BUFFER_HEIGHT, 32, rmask, gmask, bmask, amask);
382
        SDL_BlitSurface(surface, nullptr, surfaceA, nullptr);
383
        mAlphaImage = imageHelper->loadSurface(surfaceA);
384
        MSDL_FreeSurface(surfaceA);
385
    }
386
    else
387
    {
388
        mAlphaImage = nullptr;
389
    }
390
391
    delete mImage;
392
    mImage = imageHelper->loadSurface(surface);
393
    MSDL_FreeSurface(surface);
394
#endif  // USE_SDL2
395
}
396
397
void CompoundSprite::setAlpha(float alpha)
398
{
399
    if (alpha != mAlpha)
400
    {
401
        if (mEnableAlphaFix &&
402
#ifdef USE_OPENGL
403
            imageHelper->useOpenGL() == RENDER_SOFTWARE &&
404
#endif  // USE_OPENGL
405
            mSprites.size() > 3U)
406
        {
407
            FOR_EACH (SpriteConstIterator, it, mSprites)
408
            {
409
                if (*it != nullptr)
410
                    (*it)->setAlpha(alpha);
411
            }
412
        }
413
        mAlpha = alpha;
414
    }
415
}
416
417
void CompoundSprite::updateImages() const
418
{
419
#ifndef USE_SDL2
420
#ifdef USE_OPENGL
421
    if (imageHelper->useOpenGL() != RENDER_SOFTWARE)
422
        return;
423
#endif  // USE_OPENGL
424
425
    if (mEnableDelay)
426
    {
427
        if (get_elapsed_time1(mNextRedrawTime) < 10)
428
            return;
429
        mNextRedrawTime = tick_time;
430
    }
431
    mNeedsRedraw = false;
432
433
    if (!mDisableBeingCaching)
434
    {
435
        if (mSprites.size() <= 3U)
436
            return;
437
438
        if (!mDisableAdvBeingCaching)
439
        {
440
            if (updateFromCache())
441
                return;
442
443
            redraw();
444
445
            if (mImage != nullptr)
446
                initCurrentCacheItem();
447
        }
448
        else
449
        {
450
            redraw();
451
        }
452
    }
453
#endif  // USE_SDL2
454
}
455
456
bool CompoundSprite::updateFromCache() const
457
{
458
#ifndef USE_SDL2
459
//    static int hits = 0;
460
//    static int miss = 0;
461
462
    if ((mCacheItem != nullptr) && (mCacheItem->image != nullptr))
463
    {
464
        imagesCache.push_front(mCacheItem);
465
        mCacheItem = nullptr;
466
        if (imagesCache.size() > cache_max_size)
467
        {
468
            for (unsigned f = 0; f < cache_clean_part; f ++)
469
            {
470
                CompoundItem *item = imagesCache.back();
471
                imagesCache.pop_back();
472
                delete item;
473
            }
474
        }
475
    }
476
477
//    logger->log("cache size: %d, hit %d, miss %d",
478
//        (int)imagesCache.size(), hits, miss);
479
480
    const size_t sz = mSprites.size();
481
    FOR_EACH (ImagesCache::iterator, it, imagesCache)
482
    {
483
        CompoundItem *const ic = *it;
484
        if ((ic != nullptr) && ic->data.size() == sz)
485
        {
486
            bool fail(false);
487
            VectorPointers::const_iterator it2 = ic->data.begin();
488
            const VectorPointers::const_iterator it2_end = ic->data.end();
489
490
            for (SpriteConstIterator it1 = mSprites.begin(),
491
                 it1_end = mSprites.end();
492
                 it1 != it1_end && it2 != it2_end;
493
                 ++ it1, ++ it2)
494
            {
495
                const void *ptr1 = nullptr;
496
                const void *ptr2 = nullptr;
497
                if (*it1 != nullptr)
498
                    ptr1 = (*it1)->getHash();
499
                if (*it2 != nullptr)
500
                    ptr2 = *it2;
501
                if (ptr1 != ptr2)
502
                {
503
                    fail = true;
504
                    break;
505
                }
506
            }
507
            if (!fail)
508
            {
509
//                hits ++;
510
                mImage = (*it)->image;
511
                mAlphaImage = (*it)->alphaImage;
512
                imagesCache.erase(it);
513
                mCacheItem = ic;
514
                return true;
515
            }
516
        }
517
    }
518
    mImage = nullptr;
519
    mAlphaImage = nullptr;
520
//    miss++;
521
#endif  // USE_SDL2
522
    return false;
523
}
524
525
void CompoundSprite::initCurrentCacheItem() const
526
{
527
    delete mCacheItem;
528
    mCacheItem = new CompoundItem;
529
    mCacheItem->image = mImage;
530
    mCacheItem->alphaImage = mAlphaImage;
531
//    mCacheItem->alpha = mAlpha;
532
533
    FOR_EACH (SpriteConstIterator, it, mSprites)
534
    {
535
        if (*it != nullptr)
536
            mCacheItem->data.push_back((*it)->getHash());
537
        else
538
            mCacheItem->data.push_back(nullptr);
539
    }
540
}
541
542
bool CompoundSprite::updateNumber(const unsigned num)
543
{
544
    bool res(false);
545
    FOR_EACH (SpriteConstIterator, it, mSprites)
546
    {
547
        if (*it != nullptr)
548
        {
549
            if ((*it)->updateNumber(num))
550
                res = true;
551
        }
552
    }
553
    return res;
554
}
555
556
CompoundItem::CompoundItem() :
557
    data(),
558
    image(nullptr),
559
    alphaImage(nullptr)
560
{
561
}
562
563
CompoundItem::~CompoundItem()
564
{
565
    delete image;
566
    delete alphaImage;
567
}