ManaPlus
compoundsprite.cpp
Go to the documentation of this file.
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 
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 
71 
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  mEnableAlphaFix(config.getBoolValue("enableAlphaFix")),
88  mDisableAdvBeingCaching(config.getBoolValue("disableAdvBeingCaching")),
89  mDisableBeingCaching(config.getBoolValue("disableBeingCaching"))
90 {
91  mAlpha = 1.0F;
92 }
93 
95 {
96  clear();
97  mImage = nullptr;
98  mAlphaImage = nullptr;
99 }
100 
102 {
103  bool ret = false;
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;
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;
141  {
142  if (*it != nullptr)
143  ret |= (*it)->update(time);
144  }
145  mNeedsRedraw |= ret;
146  return ret;
147 }
148 
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 
177  const int posX,
178  const int posY) const
179 {
181  {
182  if (*it != nullptr)
183  {
184  (*it)->setAlpha(mAlpha);
185  (*it)->draw(graphics, posX, posY);
186  }
187  }
188 }
189 
191  const int posX,
192  const int posY) const
193 {
195  {
196  if (*it != nullptr)
197  (*it)->draw(graphics, posX, posY);
198  }
199 }
200 
202 {
204  {
205  const Sprite *const base = *it;
206  if (base != nullptr)
207  return base->getWidth();
208  }
209 
210  return 0;
211 }
212 
214 {
216  {
217  const Sprite *const base = *it;
218  if (base != nullptr)
219  return base->getHeight();
220  }
221 
222  return 0;
223 }
224 
226 {
227  return mImage;
228 }
229 
231 {
232  bool ret = false;
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 
245 {
246  if ((mImage != nullptr) || (mAlphaImage != nullptr))
247  return 1;
248  return CAST_S32(mSprites.size());
249 }
250 
252 {
254  {
255  if (*it != nullptr)
256  return (*it)->getCurrentFrame();
257  }
258  return 0;
259 }
260 
261 unsigned int CompoundSprite::getFrameCount() const
262 {
264  {
265  if (*it != nullptr)
266  return (*it)->getFrameCount();
267  }
268  return 0;
269 }
270 
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 
299 {
300  // Skip if it won't change anything
301  if (!mSprites.empty())
302  {
304  mSprites.clear();
305  }
306  mNeedsRedraw = true;
308  imagesCache.clear();
310  mLastTime = 0;
311 }
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 
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 
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
404 #endif // USE_OPENGL
405  mSprites.size() > 3U)
406  {
408  {
409  if (*it != nullptr)
410  (*it)->setAlpha(alpha);
411  }
412  }
413  mAlpha = alpha;
414  }
415 }
416 
418 {
419 #ifndef USE_SDL2
420 #ifdef USE_OPENGL
422  return;
423 #endif // USE_OPENGL
424 
425  if (mEnableDelay)
426  {
428  return;
430  }
431  mNeedsRedraw = false;
432 
434  {
435  if (mSprites.size() <= 3U)
436  return;
437 
439  {
440  if (updateFromCache())
441  return;
442 
443  redraw();
444 
445  if (mImage != nullptr)
447  }
448  else
449  {
450  redraw();
451  }
452  }
453 #endif // USE_SDL2
454 }
455 
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 
526 {
527  delete mCacheItem;
528  mCacheItem = new CompoundItem;
531 // mCacheItem->alpha = mAlpha;
532 
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);
546  {
547  if (*it != nullptr)
548  {
549  if ((*it)->updateNumber(num))
550  res = true;
551  }
552  }
553  return res;
554 }
555 
557  data(),
558  image(nullptr),
559  alphaImage(nullptr)
560 {
561 }
562 
564 {
565  delete image;
566  delete alphaImage;
567 }
#define CAST_S32
Definition: cast.h:30
VectorPointers data
Definition: compounditem.h:42
Image * alphaImage
Definition: compounditem.h:44
Image * image
Definition: compounditem.h:43
void updateImages() const
void ensureSize(const size_t layerCount)
const Image * getImage() const
void redraw() const
int getHeight() const
std::vector< Sprite * >::iterator SpriteIterator
void drawSimple(Graphics *const graphics, const int posX, const int posY) const
bool setSpriteDirection(const SpriteDirection::Type direction)
std::vector< Sprite * > mSprites
virtual void drawSpritesSDL(Graphics *const graphics, const int posX, const int posY) const
int getNumberOfLayers() const
static bool mEnableDelay
bool update(const int time)
CompoundItem * mCacheItem
unsigned int getFrameCount() const
void setAlpha(float alpha)
bool play(const std::string &action)
void drawSprites(Graphics *const graphics, const int posX, const int posY) const
ImagesCache imagesCache
bool updateFromCache() const
void addSprite(Sprite *const sprite)
bool updateNumber(const unsigned num)
std::vector< Sprite * >::const_iterator SpriteConstIterator
unsigned int getCurrentFrame() const
void removeSprite(const int layer)
int getWidth() const
void initCurrentCacheItem() const
bool mDisableAdvBeingCaching
void setSprite(const size_t layer, Sprite *const sprite)
Definition: game.h:64
Map * getCurrentMap() const
Definition: game.h:107
static Game * instance()
Definition: game.h:82
virtual void drawImage(const Image *const image, int dstX, int dstY)=0
virtual RenderType useOpenGL() const
Definition: imagehelper.h:107
virtual Image * loadSurface(SDL_Surface *const)
Definition: imagehelper.h:73
static bool mEnableAlpha
Definition: imagehelper.h:117
Definition: map.h:75
int getTileWidth() const
Definition: map.h:178
Definition: sprite.h:34
float mAlpha
Definition: sprite.h:130
void setTarget(SDL_Surface *const target)
void setBlitMode(const BlitModeT mode)
static const int BUFFER_HEIGHT
static const unsigned cache_clean_part
static const unsigned cache_max_size
static const int BUFFER_WIDTH
Configuration config
static const int mapTileSize
Definition: map.h:27
#define MSDL_CreateRGBSurface(flags, w, h, d, r, g, b, a)
Definition: debug.h:55
#define MSDL_FreeSurface(surface)
Definition: debug.h:54
#define delete2(var)
Definition: delete2.h:25
void delete_all(Container &c)
Definition: dtor.h:56
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
ImageHelper * imageHelper
Definition: imagehelper.cpp:44
#define A_UNLIKELY(x)
Definition: likely.h:30
#define PRAGMA48(str)
Definition: localconsts.h:199
#define nullptr
Definition: localconsts.h:45
uint32_t data
volatile int tick_time
Definition: timer.cpp:53
@ BLIT_GFX
Definition: blitmode.h:32
#define FUNC_BLOCK(name, id)
Definition: perfomance.h:81
@ RENDER_SOFTWARE
Definition: rendertype.h:27
int get_elapsed_time1(const int startTime)
Definition: timer.cpp:105