GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/being/compoundsprite.cpp Lines: 19 151 12.6 %
Date: 2017-11-29 Branches: 8 114 7.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2010  The Mana Developers
4
 *  Copyright (C) 2011-2017  The ManaPlus Developers
5
 *
6
 *  This file is part of The ManaPlus Client.
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
#include "being/compoundsprite.h"
23
24
#include "configuration.h"
25
26
#include "sdlshared.h"
27
28
#include "being/compounditem.h"
29
30
#include "render/surfacegraphics.h"
31
32
#if defined(USE_OPENGL) || !defined(USE_SDL2)
33
#include "resources/imagehelper.h"
34
#endif  // USE_OPENGL
35
#include "resources/image/image.h"
36
37
#include "utils/delete2.h"
38
#include "utils/dtor.h"
39
#include "utils/foreach.h"
40
#include "utils/likely.h"
41
#include "utils/sdlcheckutils.h"
42
43
#ifndef USE_SDL2
44
#include "game.h"
45
46
#include "const/resources/map/map.h"
47
48
#include "resources/map/map.h"
49
50
#include "utils/timer.h"
51
52
PRAGMA48(GCC diagnostic push)
53
PRAGMA48(GCC diagnostic ignored "-Wshadow")
54
#ifndef SDL_BIG_ENDIAN
55
#include <SDL_endian.h>
56
#endif  // SDL_BYTEORDER
57
PRAGMA48(GCC diagnostic pop)
58
#endif  // USE_SDL2
59
60
#include "debug.h"
61
62
#ifndef USE_SDL2
63
static const int BUFFER_WIDTH = 100;
64
static const int BUFFER_HEIGHT = 100;
65
static const unsigned cache_max_size = 10;
66
static const unsigned cache_clean_part = 3;
67
#endif  // USE_SDL2
68
69
bool CompoundSprite::mEnableDelay = true;
70
71
204
CompoundSprite::CompoundSprite() :
72
    Sprite(),
73
    mSprites(),
74
    imagesCache(),
75
    mCacheItem(nullptr),
76
    mImage(nullptr),
77
    mAlphaImage(nullptr),
78
    mOffsetX(0),
79
    mOffsetY(0),
80
    mStartTime(0),
81
    mLastTime(0),
82
#ifndef USE_SDL2
83
    mNextRedrawTime(0),
84
#endif  // USE_SDL2
85
    mNeedsRedraw(false),
86

816
    mEnableAlphaFix(config.getBoolValue("enableAlphaFix")),
87

816
    mDisableAdvBeingCaching(config.getBoolValue("disableAdvBeingCaching")),
88

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