ManaPlus
image.cpp
Go to the documentation of this file.
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-2017 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
29 #endif // USE_OPENGL
30 
33 
35 
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 Image::Image(const int width,
54  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  mIsAlphaCalculated(false)
74 {
75  mBounds.x = 0;
76  mBounds.y = 0;
77  mBounds.w = width;
78  mBounds.h = height;
79 }
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", this);
105 #endif // DEBUG_IMAGES
106 
107  mBounds.x = 0;
108  mBounds.y = 0;
109 
110  if (mTexture)
111  {
112  mBounds.w = CAST_U16(width);
113  mBounds.h = CAST_U16(height);
114 
115  mLoaded = true;
116  }
117  else
118  {
119  mBounds.w = 0;
120  mBounds.h = 0;
121  }
122 }
123 #endif // USE_SDL2
124 
125 Image::Image(SDL_Surface *restrict const image, const bool hasAlphaChannel0,
126  uint8_t *restrict const alphaChannel) :
127  Resource(),
128 #ifdef USE_OPENGL
129  mGLImage(0),
130  mTexWidth(0),
131  mTexHeight(0),
132 #endif // USE_OPENGL
133  mBounds(),
134  mAlpha(1.0F),
135  mSDLSurface(image),
136 #ifdef USE_SDL2
137  mTexture(nullptr),
138 #endif // USE_SDL2
139  mAlphaChannel(alphaChannel),
140  mAlphaCache(),
141  mLoaded(false),
142  mHasAlphaChannel(hasAlphaChannel0),
143  mUseAlphaCache(SDLImageHelper::mEnableAlphaCache),
144  mIsAlphaVisible(hasAlphaChannel0),
145  mIsAlphaCalculated(false)
146 {
147 #ifdef DEBUG_IMAGES
148  logger->log("created image: %p", static_cast<void*>(this));
149 #endif // DEBUG_IMAGES
150 
151  mBounds.x = 0;
152  mBounds.y = 0;
153 
154  if (mSDLSurface != nullptr)
155  {
156  mBounds.w = CAST_U16(mSDLSurface->w);
157  mBounds.h = CAST_U16(mSDLSurface->h);
158 
159  mLoaded = true;
160  }
161  else
162  {
163  mBounds.w = 0;
164  mBounds.h = 0;
165  }
166 }
167 
168 #ifdef USE_OPENGL
169 Image::Image(const GLuint glimage, const int width, const int height,
170  const int texWidth, const int texHeight) :
171  Resource(),
172  mGLImage(glimage),
173  mTexWidth(texWidth),
174  mTexHeight(texHeight),
175  mBounds(),
176  mAlpha(1.0F),
178 #ifdef USE_SDL2
179  mTexture(nullptr),
180 #endif // USE_SDL2
182  mAlphaCache(),
183  mLoaded(false),
184  mHasAlphaChannel(true),
185  mUseAlphaCache(false),
186  mIsAlphaVisible(true),
187  mIsAlphaCalculated(false)
188 {
189 #ifdef DEBUG_IMAGES
190  logger->log("created image: %p", static_cast<void*>(this));
191 #endif // DEBUG_IMAGES
192 
193  mBounds.x = 0;
194  mBounds.y = 0;
195  mBounds.w = CAST_U16(width);
196  mBounds.h = CAST_U16(height);
197 
198  if (mGLImage != 0u)
199  {
200  mLoaded = true;
201  }
202 }
203 #endif // USE_OPENGL
204 
206 {
207 #ifdef DEBUG_IMAGES
208  logger->log("delete image: %p", static_cast<void*>(this));
209  logger->log(" %s, %s", mIdPath.c_str(), mSource.c_str());
210 #endif // DEBUG_IMAGES
211 
212  unload();
213 }
214 
216 {
217  for (std::map<float, SDL_Surface*>::iterator
218  i = mAlphaCache.begin(), i_end = mAlphaCache.end();
219  i != i_end; ++i)
220  {
221  if (mSDLSurface != i->second)
223  i->second = nullptr;
224  }
225  mAlphaCache.clear();
226 }
227 
229 {
230  mLoaded = false;
231 
232  if (mSDLSurface != nullptr)
233  {
234  SDLCleanCache();
235  // Free the image surface.
237  mSDLSurface = nullptr;
238 
239  delete [] mAlphaChannel;
240  mAlphaChannel = nullptr;
241  }
242 #ifdef USE_SDL2
243  if (mTexture)
244  {
245  SDL_DestroyTexture(mTexture);
246  mTexture = nullptr;
247  }
248 #endif // USE_SDL2
249 
250 #ifdef USE_OPENGL
251  if (mGLImage != 0u)
252  {
253  glDeleteTextures(1, &mGLImage);
254  mGLImage = 0;
255 #ifdef DEBUG_OPENGL_LEAKS
256  if (textures_count > 0)
257  textures_count --;
258 #endif // DEBUG_OPENGL_LEAKS
259  }
260 #endif // USE_OPENGL
261 }
262 
264 {
265  if (mLoaded)
266  return mHasAlphaChannel;
267 
268 #ifdef USE_OPENGL
270  return true;
271 #endif // USE_OPENGL
272 
273  return false;
274 }
275 
276 SDL_Surface *Image::getByAlpha(const float alpha)
277 {
278  const std::map<float, SDL_Surface*>::const_iterator
279  it = mAlphaCache.find(alpha);
280  if (it != mAlphaCache.end())
281  return (*it).second;
282  return nullptr;
283 }
284 
285 void Image::setAlpha(const float alpha)
286 {
287  if (mAlpha == alpha || !ImageHelper::mEnableAlpha)
288  return;
289 
290  if (alpha < 0.0F || alpha > 1.0F)
291  return;
292 
293  if (mSDLSurface != nullptr)
294  {
295  if (mUseAlphaCache)
296  {
297  SDL_Surface *surface = getByAlpha(mAlpha);
298  if (surface == nullptr)
299  {
300  if (mAlphaCache.size() > 100)
301  {
302 #ifdef DEBUG_ALPHA_CACHE
303  logger->log("cleanCache");
304  for (std::map<float, SDL_Surface*>::const_iterator
305  i = mAlphaCache.begin(), i_end = mAlphaCache.end();
306  i != i_end; ++i)
307  {
308  logger->log("alpha: " + toString(i->first));
309  }
310 #endif // DEBUG_ALPHA_CACHE
311 
312  SDLCleanCache();
313  }
314  surface = mSDLSurface;
315  if (surface != nullptr)
316  mAlphaCache[mAlpha] = surface;
317  }
318  else
319  {
320  logger->log("cache bug");
321  }
322 
323  surface = getByAlpha(alpha);
324  if (surface != nullptr)
325  {
326  if (mSDLSurface == surface)
327  logger->log("bug");
328  mAlphaCache.erase(alpha);
329  mSDLSurface = surface;
330  mAlpha = alpha;
331  return;
332  }
334  if (mSDLSurface == nullptr)
335  return;
336  }
337 
338  mAlpha = alpha;
339 
340  if (!mHasAlphaChannel)
341  {
342 #ifdef USE_SDL2
343  SDL_SetSurfaceAlphaMod(mSDLSurface,
344  CAST_U8(255 * mAlpha));
345 #else // USE_SDL2
346 
347  // Set the alpha value this image is drawn at
348  SDL_SetAlpha(mSDLSurface, SDL_SRCALPHA,
349  CAST_U8(255 * mAlpha));
350 #endif // USE_SDL2
351  }
352  else
353  {
354  if (SDL_MUSTLOCK(mSDLSurface))
355  SDL_LockSurface(mSDLSurface);
356 
357  const int bx = mBounds.x;
358  const int by = mBounds.y;
359  const int bw = mBounds.w;
360  const int bh = mBounds.h;
361  const int bxw = bx + bw;
362  const int sw = mSDLSurface->w;
363  const int maxHeight = std::min(by + bh, mSDLSurface->h);
364  const int maxWidth = std::min(bxw, sw);
365  const int i1 = by * sw + bx;
366  const SDL_PixelFormat * const fmt = mSDLSurface->format;
367  const uint32_t amask = fmt->Amask;
368  const uint32_t invMask = ~fmt->Amask;
369  const uint8_t aloss = fmt->Aloss;
370  const uint8_t ashift = fmt->Ashift;
371 
372  if ((bx == 0) && bxw == sw)
373  {
374  const int i2 = (maxHeight - 1) * sw + maxWidth - 1;
375  for (int i = i1; i <= i2; i++)
376  {
377  const uint8_t sourceAlpha = mAlphaChannel[i];
378  if (sourceAlpha > 0)
379  {
380  const uint8_t a = CAST_U8(
381  static_cast<float>(sourceAlpha) * mAlpha);
382 
383  uint32_t c = (static_cast<uint32_t*>(
384  mSDLSurface->pixels))[i];
385  c &= invMask;
386  c |= ((a >> aloss) << ashift & amask);
387  (static_cast<uint32_t*>(mSDLSurface->pixels))[i] = c;
388  }
389  }
390  }
391  else
392  {
393  for (int y = by; y < maxHeight; y ++)
394  {
395  const int idx = y * sw;
396  const int x1 = idx + bx;
397  const int x2 = idx + maxWidth;
398  for (int i = x1; i < x2; i ++)
399  {
400  const uint8_t sourceAlpha = mAlphaChannel[i];
401  if (sourceAlpha > 0)
402  {
403  const uint8_t a = CAST_U8(
404  static_cast<float>(sourceAlpha) * mAlpha);
405 
406  uint32_t c = (static_cast<uint32_t*>(
407  mSDLSurface->pixels))[i];
408  c &= invMask;
409  c |= ((a >> aloss) << ashift & amask);
410  (static_cast<uint32_t*>(
411  mSDLSurface->pixels))[i] = c;
412  }
413  }
414  }
415  }
416 
417  if (SDL_MUSTLOCK(mSDLSurface))
418  SDL_UnlockSurface(mSDLSurface);
419  }
420  }
421 #ifdef USE_SDL2
422  else if (mTexture)
423  {
424  mAlpha = alpha;
425  SDL_SetTextureAlphaMod(mTexture,
426  CAST_U8(255 * mAlpha));
427  }
428 #endif // USE_SDL2
429  else
430  {
431  mAlpha = alpha;
432  }
433 }
434 
435 Image* Image::SDLgetScaledImage(const int width, const int height) const
436 {
437  // No scaling on incorrect new values.
438  if (width == 0 || height == 0)
439  return nullptr;
440 
441  // No scaling when there is ... no different given size ...
442  if (width == mBounds.w && height == mBounds.h)
443  return nullptr;
444 
445  Image* scaledImage = nullptr;
446 
447  if (mSDLSurface != nullptr)
448  {
449  SDL_Surface *const scaledSurface = zoomSurface(mSDLSurface,
450  static_cast<double>(width) / mBounds.w,
451  static_cast<double>(height) / mBounds.h,
452  1);
453 
454  // The load function takes care of the SDL<->OpenGL implementation
455  // and about freeing the given SDL_surface*.
456  if (scaledSurface != nullptr)
457  {
458  scaledImage = imageHelper->loadSurface(scaledSurface);
459  MSDL_FreeSurface(scaledSurface);
460  }
461  }
462  return scaledImage;
463 }
464 
465 Image *Image::getSubImage(const int x, const int y,
466  const int width, const int height)
467 {
468  // Create a new clipped sub-image
469 #ifdef USE_OPENGL
471  if (mode == RENDER_NORMAL_OPENGL ||
472  mode == RENDER_SAFE_OPENGL ||
473  mode == RENDER_GLES_OPENGL ||
474  mode == RENDER_GLES2_OPENGL ||
475  mode == RENDER_MODERN_OPENGL)
476  {
477  return new SubImage(this,
478  mGLImage,
479  x, y,
480  width, height,
482  }
483 #endif // USE_OPENGL
484 
485 #ifdef USE_SDL2
486 #ifndef USE_OPENGL
487  const RenderType mode = ImageHelper::mUseOpenGL;
488 #endif // USE_OPENGL
489 
490  if (mode == RENDER_SOFTWARE)
491  return new SubImage(this, mSDLSurface, x, y, width, height);
492  else
493  return new SubImage(this, mTexture, x, y, width, height);
494 #else // USE_SDL2
495 
496  return new SubImage(this, mSDLSurface, x, y, width, height);
497 #endif // USE_SDL2
498 }
499 
501 {
502  SDLCleanCache();
503  mUseAlphaCache = false;
504 }
505 
507 {
508  // +++ this calculation can be wrong for SDL2
509  int sz = static_cast<int>(sizeof(Image) +
510  sizeof(std::map<float, SDL_Surface*>)) +
512  if (mSDLSurface != nullptr)
513  {
514  sz += CAST_S32(mAlphaCache.size()) *
516  }
517  return sz;
518 }
519 
520 #ifdef USE_OPENGL
522 {
523  if ((mGLImage != 0u) && mRefCount <= 1)
526 }
527 #endif // USE_OPENGL
std::string mSource
Definition: resource.h:84
bool hasAlphaChannel() const
Definition: image.cpp:263
void SDLCleanCache()
Definition: image.cpp:215
ImageHelper * imageHelper
Definition: imagehelper.cpp:43
#define CAST_U8
Definition: cast.h:26
virtual ~Image()
Definition: image.cpp:205
int calcMemoryLocal() const
Definition: image.cpp:506
virtual void decRef()
Definition: resource.cpp:49
void SDLTerminateAlphaCache()
Definition: image.cpp:500
int textures_count
Definition: client.cpp:125
int calcMemoryLocal() const
Definition: resource.cpp:75
unsigned int mRefCount
Definition: resource.h:86
bool mIsAlphaCalculated
Definition: image.h:244
#define MSDL_FreeSurface(surface)
Definition: debug.h:53
MemoryManager memoryManager
bool mLoaded
Definition: image.h:240
static RenderType mUseOpenGL
Definition: imagehelper.h:117
Logger * logger
Definition: logger.cpp:95
virtual void setAlpha(const float alpha)
Definition: image.cpp:285
#define CAST_U16
Definition: cast.h:28
static void invalidate(const GLuint textureId)
SDL_Surface * mSDLSurface
Definition: image.h:230
#define CAST_S32
Definition: cast.h:29
bool mIsAlphaVisible
Definition: image.h:243
virtual Image * getSubImage(const int x, const int y, const int width, const int height)
Definition: image.cpp:465
int mTexHeight
Definition: image.h:184
uint8_t * mAlphaChannel
Definition: image.h:236
bool mUseAlphaCache
Definition: image.h:242
#define nullptr
Definition: localconsts.h:44
void unload()
Definition: image.cpp:228
float mAlpha
Definition: image.h:212
bool mHasAlphaChannel
Definition: image.h:241
#define PRAGMA48(str)
Definition: localconsts.h:214
Image(SDL_Surface *const image, const bool hasAlphaChannel, uint8_t *const alphaChannel=0)
Definition: image.cpp:125
Image * SDLgetScaledImage(const int width, const int height) const
Definition: image.cpp:435
GLuint mGLImage
Definition: image.h:182
SDL_Surface * getByAlpha(const float alpha)
Definition: image.cpp:276
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:1774
virtual Image * loadSurface(SDL_Surface *const)
Definition: imagehelper.h:72
static bool mEnableAlpha
Definition: imagehelper.h:116
int getSurfaceSize(const SDL_Surface *const surface)
static SDL_Surface * SDLDuplicateSurface(SDL_Surface *const tmpImage)
Definition: image.h:61
#define restrict
Definition: localconsts.h:176
SDL_Rect mBounds
Definition: image.h:210
void log(const char *const log_text,...)
Definition: logger.cpp:243
std::string mIdPath
Definition: resource.h:83
int mTexWidth
Definition: image.h:183
void decRef()
Definition: image.cpp:521
void scheduleDelete(SDL_Surface *const surface)
std::map< float, SDL_Surface * > mAlphaCache
Definition: image.h:238
RenderType
Definition: rendertype.h:24
SDL_Surface * zoomSurface(SDL_Surface *src, double zoomx, double zoomy, int smooth)
Zoom a surface by independent horizontal and vertical factors with optional smoothing.