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-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 
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 
70 
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  mEnableAlphaFix(config.getBoolValue("enableAlphaFix")),
87  mDisableAdvBeingCaching(config.getBoolValue("disableAdvBeingCaching")),
88  mDisableBeingCaching(config.getBoolValue("disableBeingCaching"))
89 {
90  mAlpha = 1.0F;
91 }
92 
94 {
95  clear();
96  mImage = nullptr;
97  mAlphaImage = nullptr;
98 }
99 
101 {
102  bool ret = false;
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;
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 
134 {
135  bool ret = false;
136  if (A_UNLIKELY(mLastTime == 0))
137  mStartTime = time;
138  mLastTime = time;
140  {
141  if (*it != nullptr)
142  ret |= (*it)->update(time);
143  }
144  mNeedsRedraw |= ret;
145  return ret;
146 }
147 
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  {
166  graphics->drawImage(mAlphaImage,
167  posX + mOffsetX, posY + mOffsetY);
168  }
169  else
170  {
171  CompoundSprite::drawSprites(graphics, posX, posY);
172  }
173 }
174 
176  const int posX,
177  const int posY) const
178 {
180  {
181  if (*it != nullptr)
182  {
183  (*it)->setAlpha(mAlpha);
184  (*it)->draw(graphics, posX, posY);
185  }
186  }
187 }
188 
190  const int posX,
191  const int posY) const
192 {
194  {
195  if (*it != nullptr)
196  (*it)->draw(graphics, posX, posY);
197  }
198 }
199 
201 {
203  {
204  const Sprite *const base = *it;
205  if (base != nullptr)
206  return base->getWidth();
207  }
208 
209  return 0;
210 }
211 
213 {
215  {
216  const Sprite *const base = *it;
217  if (base != nullptr)
218  return base->getHeight();
219  }
220 
221  return 0;
222 }
223 
225 {
226  return mImage;
227 }
228 
230 {
231  bool ret = false;
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 
244 {
245  if ((mImage != nullptr) || (mAlphaImage != nullptr))
246  return 1;
247  return CAST_S32(mSprites.size());
248 }
249 
251 {
253  {
254  if (*it != nullptr)
255  return (*it)->getCurrentFrame();
256  }
257  return 0;
258 }
259 
260 unsigned int CompoundSprite::getFrameCount() const
261 {
263  {
264  if (*it != nullptr)
265  return (*it)->getFrameCount();
266  }
267  return 0;
268 }
269 
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 
298 {
299  // Skip if it won't change anything
300  if (!mSprites.empty())
301  {
303  mSprites.clear();
304  }
305  mNeedsRedraw = true;
307  imagesCache.clear();
309  mLastTime = 0;
310 }
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 
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 
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
403 #endif // USE_OPENGL
404  mSprites.size() > 3U)
405  {
407  {
408  if (*it != nullptr)
409  (*it)->setAlpha(alpha);
410  }
411  }
412  mAlpha = alpha;
413  }
414 }
415 
417 {
418 #ifndef USE_SDL2
419 #ifdef USE_OPENGL
421  return;
422 #endif // USE_OPENGL
423 
424  if (mEnableDelay)
425  {
427  return;
429  }
430  mNeedsRedraw = false;
431 
433  {
434  if (mSprites.size() <= 3U)
435  return;
436 
438  {
439  if (updateFromCache())
440  return;
441 
442  redraw();
443 
444  if (mImage != nullptr)
446  }
447  else
448  {
449  redraw();
450  }
451  }
452 #endif // USE_SDL2
453 }
454 
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 
525 {
526  delete mCacheItem;
527  mCacheItem = new CompoundItem;
530 // mCacheItem->alpha = mAlpha;
531 
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);
545  {
546  if (*it != nullptr)
547  {
548  if ((*it)->updateNumber(num))
549  res = true;
550  }
551  }
552  return res;
553 }
554 
556  data(),
557  image(nullptr),
558  alphaImage(nullptr)
559 {
560 }
561 
563 {
564  delete image;
565  delete alphaImage;
566 }
int getHeight() const
#define FOR_EACH(type, iter, array)
Definition: foreach.h:24
virtual RenderType useOpenGL() const
Definition: imagehelper.h:106
bool setSpriteDirection(const SpriteDirection::Type direction)
bool update(const int time)
ImageHelper * imageHelper
Definition: imagehelper.cpp:43
volatile int tick_time
Definition: timer.cpp:52
CompoundItem * mCacheItem
bool updateNumber(const unsigned num)
unsigned int getCurrentFrame() const
void setBlitMode(const BlitModeT mode)
const Image * getImage() const
Definition: sprite.h:32
void addSprite(Sprite *const sprite)
#define MSDL_FreeSurface(surface)
Definition: debug.h:53
Configuration config
void drawSimple(Graphics *const graphics, const int posX, const int posY) const
static const int BUFFER_HEIGHT
void initCurrentCacheItem() const
static const unsigned cache_clean_part
int getNumberOfLayers() const
#define delete2(var)
Definition: delete2.h:24
#define A_UNLIKELY(x)
Definition: likely.h:29
Map * getCurrentMap() const
Definition: game.h:106
Image * image
Definition: compounditem.h:42
virtual void setAlpha(const float alpha)
Definition: image.cpp:285
Image * alphaImage
Definition: compounditem.h:43
int get_elapsed_time1(const int startTime)
Definition: timer.cpp:104
ImagesCache imagesCache
#define CAST_S32
Definition: cast.h:29
void delete_all(Container &c)
Definition: dtor.h:55
void drawSprites(Graphics *const graphics, const int posX, const int posY) const
virtual int getHeight() const =0
std::vector< Sprite * >::iterator SpriteIterator
Definition: game.h:62
uint32_t data
int getWidth() const
std::vector< Sprite * >::const_iterator SpriteConstIterator
std::vector< Sprite * > mSprites
void removeSprite(const int layer)
static const unsigned cache_max_size
#define nullptr
Definition: localconsts.h:44
static bool mEnableDelay
bool updateFromCache() const
#define PRAGMA48(str)
Definition: localconsts.h:214
#define FUNC_BLOCK(name, id)
Definition: perfomance.h:80
Definition: map.h:68
virtual ~CompoundSprite()
virtual int getWidth() const =0
void setTarget(SDL_Surface *const target)
virtual void drawSpritesSDL(Graphics *const graphics, const int posX, const int posY) const
unsigned int getFrameCount() const
bool play(const std::string &action)
virtual Image * loadSurface(SDL_Surface *const)
Definition: imagehelper.h:72
static bool mEnableAlpha
Definition: imagehelper.h:116
virtual void drawImage(const Image *const image, int dstX, int dstY)=0
bool mDisableAdvBeingCaching
VectorPointers data
Definition: compounditem.h:41
int getTileWidth() const
Definition: map.h:174
void ensureSize(const size_t layerCount)
Definition: image.h:61
void updateImages() const
static const int mapTileSize
Definition: map.h:26
static Game * instance()
Definition: game.h:81
float mAlpha
Definition: sprite.h:129
void setAlpha(float alpha)
void setSprite(const size_t layer, Sprite *const sprite)
static const int BUFFER_WIDTH
void redraw() const
#define MSDL_CreateRGBSurface(flags, w, h, d, r, g, b, a)
Definition: debug.h:54