ManaPlus
safeopenglimagehelper.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-2019 The ManaPlus Developers
6  * Copyright (C) 2019-2021 Andrei Karas
7  *
8  * This file is part of The ManaPlus Client.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
25 
26 #if defined(USE_OPENGL) && !defined(ANDROID) && !defined(__SWITCH__)
27 
28 #include "graphicsmanager.h"
29 
35 
36 #include "render/opengl/mgl.h"
37 #include "render/opengl/mglcheck.h"
38 
39 #include "resources/dye/dye.h"
41 
42 #include "resources/image/image.h"
43 
44 #include "utils/sdlcheckutils.h"
45 
46 #include "debug.h"
47 
48 #ifndef SDL_BIG_ENDIAN
49 #error missing SDL_endian.h
50 #endif // SDL_BYTEORDER
51 
57 
59 {
60  glDeleteTextures(static_cast<GLsizei>(texturesSize - mFreeTextureIndex),
62 }
63 
64 Image *SafeOpenGLImageHelper::load(SDL_RWops *const rw,
65  Dye const &dye)
66 {
67  SDL_Surface *const tmpImage = loadPng(rw);
68  if (tmpImage == nullptr)
69  {
70  logger->log("Error, image load failed: %s", SDL_GetError());
71  return nullptr;
72  }
73 
74  SDL_Surface *const surf = convertTo32Bit(tmpImage);
75  MSDL_FreeSurface(tmpImage);
76  if (surf == nullptr)
77  return nullptr;
78 
79  uint32_t *pixels = static_cast<uint32_t *>(surf->pixels);
80  const int type = dye.getType();
81 
82  switch (type)
83  {
84  case 1:
85  {
86  const DyePalette *const pal = dye.getSPalete();
87  if (pal != nullptr)
88  DYEPALETTEP(pal, SOGLColor)(pixels, surf->w * surf->h);
89  break;
90  }
91  case 2:
92  {
93  const DyePalette *const pal = dye.getAPalete();
94  if (pal != nullptr)
95  DYEPALETTEP(pal, AOGLColor)(pixels, surf->w * surf->h);
96  break;
97  }
98  case 0:
99  default:
100  {
101  dye.normalOGLDye(pixels, surf->w * surf->h);
102  break;
103  }
104  }
105 
106  Image *const image = loadSurface(surf);
107  MSDL_FreeSurface(surf);
108  return image;
109 }
110 
111 Image *SafeOpenGLImageHelper::loadSurface(SDL_Surface *const tmpImage)
112 {
113  return glLoad(tmpImage);
114 }
115 
116 Image *SafeOpenGLImageHelper::createTextSurface(SDL_Surface *const tmpImage,
117  const int width,
118  const int height,
119  const float alpha)
120 {
121  if (tmpImage == nullptr)
122  return nullptr;
123 
124  Image *const img = glLoad(tmpImage, width, height);
125  if (img != nullptr)
126  img->setAlpha(alpha);
127  return img;
128 }
129 
131 {
132  int value = 1;
133  while (value < input && value < mTextureSize)
134  value <<= 1;
135  return value >= mTextureSize ? mTextureSize : value;
136 }
137 
139  *tmpImage,
140  int width,
141  int height)
142 {
143  if (tmpImage == nullptr)
144  return nullptr;
145 
146  int realWidth = powerOfTwo(width);
147  int realHeight = powerOfTwo(height);
148 
149  if (realWidth < width || realHeight < height)
150  {
151  logger->log("Warning: image too large, cropping to %dx%d texture!",
152  tmpImage->w, tmpImage->h);
153  }
154 
155 #ifdef USE_SDL2
156  SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE);
157 #else // USE_SDL2
158 
159  // Make sure the alpha channel is not used, but copied to destination
160  SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
161 #endif // USE_SDL2
162 
163  // Determine 32-bit masks based on byte order
164  uint32_t rmask, gmask, bmask, amask;
165 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
166  rmask = 0xff000000U;
167  gmask = 0x00ff0000U;
168  bmask = 0x0000ff00U;
169  amask = 0x000000ffU;
170 #else // SDL_BYTEORDER == SDL_BIG_ENDIAN
171 
172  rmask = 0x000000ffU;
173  gmask = 0x0000ff00U;
174  bmask = 0x00ff0000U;
175  amask = 0xff000000U;
176 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN
177 
178  if (tmpImage->format->BitsPerPixel != 32
179  || realWidth != width || realHeight != height
180  || rmask != tmpImage->format->Rmask
181  || gmask != tmpImage->format->Gmask
182  || amask != tmpImage->format->Amask)
183  {
184  SDL_Surface *oldImage = tmpImage;
185 #ifdef USE_SDL2
186  SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE);
187 #endif // USE_SDL2
188 
189  tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight,
190  32, rmask, gmask, bmask, amask);
191 
192  if (tmpImage == nullptr)
193  {
194  logger->log("Error, image convert failed: out of memory");
195  return nullptr;
196  }
197  SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr);
198  }
199  return tmpImage;
200 }
201 
202 SDL_Surface *SafeOpenGLImageHelper::convertSurface(SDL_Surface *tmpImage,
203  int width,
204  int height)
205 {
206  if (tmpImage == nullptr)
207  return nullptr;
208 
209 #ifdef USE_SDL2
210  SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE);
211 #else // USE_SDL2
212 
213  // Make sure the alpha channel is not used, but copied to destination
214  SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
215 #endif // USE_SDL2
216 
217  // Determine 32-bit masks based on byte order
218  uint32_t rmask, gmask, bmask, amask;
219 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
220  rmask = 0xff000000U;
221  gmask = 0x00ff0000U;
222  bmask = 0x0000ff00U;
223  amask = 0x000000ffU;
224 #else // SDL_BYTEORDER == SDL_BIG_ENDIAN
225 
226  rmask = 0x000000ffU;
227  gmask = 0x0000ff00U;
228  bmask = 0x00ff0000U;
229  amask = 0xff000000U;
230 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN
231 
232  if (tmpImage->format->BitsPerPixel != 32
233  || rmask != tmpImage->format->Rmask
234  || gmask != tmpImage->format->Gmask
235  || amask != tmpImage->format->Amask)
236  {
237  SDL_Surface *oldImage = tmpImage;
238 #ifdef USE_SDL2
239  SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE);
240 #endif // USE_SDL2
241 
242  tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
243  32, rmask, gmask, bmask, amask);
244 
245  if (tmpImage == nullptr)
246  {
247  logger->log("Error, image convert failed: out of memory");
248  return nullptr;
249  }
250  SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr);
251  }
252  return tmpImage;
253 }
254 
255 void SafeOpenGLImageHelper::bindTexture(const GLuint texture)
256 {
257  switch (mUseOpenGL)
258  {
259 #ifdef __native_client__
262  case RENDER_GLES_OPENGL:
263  break;
264  case RENDER_SAFE_OPENGL:
266  break;
267  case RENDER_GLES2_OPENGL:
269  break;
270 #elif defined(ANDROID)
273  case RENDER_SAFE_OPENGL:
274  case RENDER_GLES2_OPENGL:
275  break;
276  case RENDER_GLES_OPENGL:
278  break;
279 #else // __native_client__
282  break;
285  break;
286  case RENDER_SAFE_OPENGL:
288  break;
289  case RENDER_GLES_OPENGL:
291  break;
292  case RENDER_GLES2_OPENGL:
294  break;
295 #endif // __native_client__
296  case RENDER_SOFTWARE:
297  case RENDER_SDL2_DEFAULT:
298  case RENDER_NULL:
299  case RENDER_LAST:
300  default:
301  logger->log("Unknown OpenGL backend: %d", mUseOpenGL);
302  break;
303  }
304 }
305 
306 Image *SafeOpenGLImageHelper::glLoad(SDL_Surface *tmpImage,
307  int width,
308  int height)
309 {
310  if (tmpImage == nullptr)
311  return nullptr;
312 
313  BLOCK_START("SafeOpenGLImageHelper::glLoad")
314  // Flush current error flag.
316 
317  if (width == 0)
318  width = tmpImage->w;
319  if (height == 0)
320  height = tmpImage->h;
321 
322  SDL_Surface *oldImage = tmpImage;
323  tmpImage = convertSurfaceNormalize(tmpImage, width, height);
324  if (tmpImage == nullptr)
325  return nullptr;
326 
327  const int realWidth = tmpImage->w;
328  const int realHeight = tmpImage->h;
329 
330  const GLuint texture = getNewTexture();
331  bindTexture(texture);
332 
333  if (SDL_MUSTLOCK(tmpImage))
334  SDL_LockSurface(tmpImage);
335 
336  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
337 
338  if (!mUseTextureSampler)
339  {
340  if (mBlur)
341  {
342  glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
343  glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
344  }
345  else
346  {
347  glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
348  glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
349  }
350  }
351 #ifndef ANDROID
352  glTexParameteri(mTextureType, GL_TEXTURE_MAX_LEVEL, 0);
353 #endif // ANDROID
354 
355  glTexImage2D(mTextureType, 0, mInternalTextureType,
356  tmpImage->w, tmpImage->h,
357  0, GL_RGBA, GL_UNSIGNED_BYTE, tmpImage->pixels);
358 
359 #ifdef DEBUG_OPENGL
360 /*
361  disabled for now, because debugger can't show it
362  if (isGLNotNull(mglLabelObject))
363  {
364  const char *const text = "image text";
365  mglLabelObject(GL_TEXTURE, texture, strlen(text), text);
366  }
367 */
368 #endif // DEBUG_OPENGL
369 
370 /*
371  GLint compressed;
372  glGetTexLevelParameteriv(mTextureType, 0,
373  GL_TEXTURE_COMPRESSED_ARB, &compressed);
374  if (compressed)
375  logger->log("image compressed");
376  else
377  logger->log("image not compressed");
378 */
379 
380 #ifdef DEBUG_OPENGL_LEAKS
381  textures_count ++;
382 #endif // DEBUG_OPENGL_LEAKS
383 
384  if (SDL_MUSTLOCK(tmpImage))
385  SDL_UnlockSurface(tmpImage);
386 
387  if (oldImage != tmpImage)
388  MSDL_FreeSurface(tmpImage);
389 
391  if (error != 0U)
392  {
393  std::string errmsg = GraphicsManager::errorToString(error);
394  logger->log("Error: Image GL import failed: %s (%u)",
395  errmsg.c_str(), error);
396 // return nullptr;
397  }
398 
399  BLOCK_END("SafeOpenGLImageHelper::glLoad")
400  return new Image(texture, width, height, realWidth, realHeight);
401 }
402 
404 {
405  if (mBlur)
406  {
407  mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
408  mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
409  }
410  else
411  {
412  mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
413  mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
414  }
415 }
416 
418  int height) const
419 {
420 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
421  const uint32_t rmask = 0xff000000U;
422  const uint32_t gmask = 0x00ff0000U;
423  const uint32_t bmask = 0x0000ff00U;
424  const uint32_t amask = 0x000000ffU;
425 #else // SDL_BYTEORDER == SDL_BIG_ENDIAN
426 
427  const uint32_t rmask = 0x000000ffU;
428  const uint32_t gmask = 0x0000ff00U;
429  const uint32_t bmask = 0x00ff0000U;
430  const uint32_t amask = 0xff000000U;
431 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN
432 
433  width = powerOfTwo(width);
434  height = powerOfTwo(height);
435 
436  return MSDL_CreateRGBSurface(SDL_SWSURFACE,
437  width, height, 32, rmask, gmask, bmask, amask);
438 }
439 
441 {
442  GLuint texture = mTextures[mFreeTextureIndex];
445  {
446  mFreeTextureIndex = 0;
447  postInit();
448  }
449  return texture;
450 }
451 
453 {
454  glGenTextures(texturesSize, &mTextures[mFreeTextureIndex]);
455 }
456 
457 void SafeOpenGLImageHelper::invalidate(const GLuint textureId)
458 {
459  if (isGLNotNull(mglInvalidateTexImage))
460  {
461  logger->log("invalidate: %u", textureId);
462  mglInvalidateTexImage(textureId, 0);
463  }
464 }
465 
467  const int x,
468  const int y,
469  SDL_Surface *surface) const
470 {
471  if (surface == nullptr || image == nullptr)
472  return;
473 
474  SDL_Surface *const oldSurface = surface;
475  surface = convertSurface(surface, surface->w, surface->h);
476  if (surface == nullptr)
477  return;
478 
479  // +++ probably need combine
480  // mglTextureSubImage2D and mglTextureSubImage2DEXT
481  if (mglTextureSubImage2D != nullptr)
482  {
483  mglTextureSubImage2D(image->mGLImage,
484  0,
485  x, y,
486  surface->w, surface->h,
487  GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
488  }
489  else
490  {
491  mglTextureSubImage2DEXT(image->mGLImage,
492  mTextureType, 0,
493  x, y,
494  surface->w, surface->h,
495  GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
496  }
497 
498  if (surface != oldSurface)
499  MSDL_FreeSurface(surface);
500 }
501 
502 #endif // defined(USE_OPENGL) && !defined(ANDROID)
Definition: dye.h:41
const DyePalette * getSPalete() const
Definition: dye.h:67
const DyePalette * getAPalete() const
Definition: dye.h:73
void normalOGLDye(uint32_t *pixels, const int bufSize) const
Definition: dye.cpp:224
int getType() const
Definition: dye.cpp:149
static GLenum getLastError()
static std::string errorToString(const GLenum error)
static RenderType mUseOpenGL
Definition: imagehelper.h:118
static SDL_Surface * convertTo32Bit(SDL_Surface *const tmpImage)
static SDL_Surface * loadPng(SDL_RWops *const rw)
void log(const char *const log_text,...)
Definition: logger.cpp:269
Image * loadSurface(SDL_Surface *const tmpImage)
Image * glLoad(SDL_Surface *tmpImage, int width=0, int height=0)
static const size_t texturesSize
static void bindTexture(const GLuint texture)
static void invalidate(const GLuint textureId)
Image * load(SDL_RWops *const rw, Dye const &dye)
Image * createTextSurface(SDL_Surface *const tmpImage, const int width, const int height, const float alpha)
GLuint mTextures[texturesSize]
void copySurfaceToImage(const Image *const image, const int x, const int y, SDL_Surface *surface) const
static SDL_Surface * convertSurfaceNormalize(SDL_Surface *tmpImage, int width, int height)
static int powerOfTwo(const int input)
SDL_Surface * create32BitSurface(int width, int height) const
static SDL_Surface * convertSurface(SDL_Surface *tmpImage, int width, int height)
static void initTextureSampler(const GLint id)
#define MSDL_CreateRGBSurface(flags, w, h, d, r, g, b, a)
Definition: debug.h:55
#define MSDL_FreeSurface(surface)
Definition: debug.h:54
int textures_count
Definition: client.cpp:138
#define DYEPALETTEP(palette, color)
Definition: dyepalette.h:40
GraphicsManager graphicsManager
Logger * logger
Definition: logger.cpp:89
#define isGLNotNull(func)
Definition: mglcheck.h:28
bool error(InputEvent &event) __attribute__((noreturn))
Definition: actions.cpp:82
static void bindTexture(const GLenum target, const GLuint texture)
#define BLOCK_END(name)
Definition: perfomance.h:80
#define BLOCK_START(name)
Definition: perfomance.h:79
@ RENDER_SAFE_OPENGL
Definition: rendertype.h:29
@ RENDER_GLES2_OPENGL
Definition: rendertype.h:33
@ RENDER_GLES_OPENGL
Definition: rendertype.h:30
@ RENDER_MODERN_OPENGL
Definition: rendertype.h:32
@ RENDER_LAST
Definition: rendertype.h:35
@ RENDER_NORMAL_OPENGL
Definition: rendertype.h:28
@ RENDER_SDL2_DEFAULT
Definition: rendertype.h:31
@ RENDER_NULL
Definition: rendertype.h:34
@ RENDER_SOFTWARE
Definition: rendertype.h:27