ManaPlus
openglimagehelper.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 #ifdef USE_OPENGL
27 
28 #include "graphicsmanager.h"
29 
33 #ifdef __native_client__
35 #endif // __native_client__
38 
39 #include "render/opengl/mgl.h"
40 #include "render/opengl/mglcheck.h"
41 
42 #include "resources/dye/dye.h"
44 
45 #include "resources/image/image.h"
46 
47 #include "utils/checkutils.h"
48 #include "utils/sdlcheckutils.h"
49 
50 #include "debug.h"
51 
52 #ifndef SDL_BIG_ENDIAN
53 #error missing SDL_endian.h
54 #endif // SDL_BYTEORDER
55 
59 bool OpenGLImageHelper::mBlur = true;
61 
63 {
64  glDeleteTextures(static_cast<GLsizei>(texturesSize - mFreeTextureIndex),
66 }
67 
68 Image *OpenGLImageHelper::load(SDL_RWops *const rw, Dye const &dye)
69 {
70  SDL_Surface *const tmpImage = loadPng(rw);
71  if (tmpImage == nullptr)
72  {
73  reportAlways("Error, image load failed: %s", SDL_GetError())
74  return nullptr;
75  }
76 
77  SDL_Surface *const surf = convertTo32Bit(tmpImage);
78  MSDL_FreeSurface(tmpImage);
79  if (surf == nullptr)
80  return nullptr;
81 
82  uint32_t *pixels = static_cast<uint32_t *>(surf->pixels);
83  const int type = dye.getType();
84 
85  switch (type)
86  {
87  case 1:
88  {
89  const DyePalette *const pal = dye.getSPalete();
90  if (pal != nullptr)
91  DYEPALETTEP(pal, SOGLColor)(pixels, surf->w * surf->h);
92  break;
93  }
94  case 2:
95  {
96  const DyePalette *const pal = dye.getAPalete();
97  if (pal != nullptr)
98  DYEPALETTEP(pal, AOGLColor)(pixels, surf->w * surf->h);
99  break;
100  }
101  case 0:
102  default:
103  {
104  dye.normalOGLDye(pixels, surf->w * surf->h);
105  break;
106  }
107  }
108 
109  Image *const image = loadSurface(surf);
110  MSDL_FreeSurface(surf);
111  return image;
112 }
113 
114 Image *OpenGLImageHelper::loadSurface(SDL_Surface *const tmpImage)
115 {
116  return glLoad(tmpImage);
117 }
118 
119 Image *OpenGLImageHelper::createTextSurface(SDL_Surface *const tmpImage,
120  const int width, const int height,
121  const float alpha)
122 {
123  if (tmpImage == nullptr)
124  return nullptr;
125 
126  Image *const img = glLoad(tmpImage, width, height);
127  if (img != nullptr)
128  img->setAlpha(alpha);
129  return img;
130 }
131 
132 int OpenGLImageHelper::powerOfTwo(const int input)
133 {
134  int value = 1;
135  while (value < input && value < mTextureSize)
136  value <<= 1;
137  return value >= mTextureSize ? mTextureSize : value;
138 }
139 
140 SDL_Surface *OpenGLImageHelper::convertSurfaceNormalize(SDL_Surface *tmpImage,
141  int width, 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  reportAlways("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  reportAlways("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 *OpenGLImageHelper::convertSurface(SDL_Surface *tmpImage,
203  int width, int height)
204 {
205  if (tmpImage == nullptr)
206  return nullptr;
207 
208 #ifdef USE_SDL2
209  SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE);
210 #else // USE_SDL2
211 
212  // Make sure the alpha channel is not used, but copied to destination
213  SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
214 #endif // USE_SDL2
215 
216  // Determine 32-bit masks based on byte order
217  uint32_t rmask, gmask, bmask, amask;
218 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
219  rmask = 0xff000000U;
220  gmask = 0x00ff0000U;
221  bmask = 0x0000ff00U;
222  amask = 0x000000ffU;
223 #else // SDL_BYTEORDER == SDL_BIG_ENDIAN
224 
225  rmask = 0x000000ffU;
226  gmask = 0x0000ff00U;
227  bmask = 0x00ff0000U;
228  amask = 0xff000000U;
229 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN
230 
231  if (tmpImage->format->BitsPerPixel != 32
232  || rmask != tmpImage->format->Rmask
233  || gmask != tmpImage->format->Gmask
234  || amask != tmpImage->format->Amask)
235  {
236  SDL_Surface *oldImage = tmpImage;
237 #ifdef USE_SDL2
238  SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE);
239 #endif // USE_SDL2
240 
241  tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
242  32, rmask, gmask, bmask, amask);
243 
244  if (tmpImage == nullptr)
245  {
246  reportAlways("Error, image convert failed: out of memory")
247  return nullptr;
248  }
249  SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr);
250  }
251  return tmpImage;
252 }
253 
254 void OpenGLImageHelper::bindTexture(const GLuint texture)
255 {
256  switch (mUseOpenGL)
257  {
258 #ifdef ANDROID
260  case RENDER_SAFE_OPENGL:
262  case RENDER_GLES2_OPENGL:
263  break;
264  case RENDER_GLES_OPENGL:
266  break;
267 #elif defined(__native_client__) || defined(__SWITCH__)
270  case RENDER_GLES_OPENGL:
271  break;
272  case RENDER_SAFE_OPENGL:
273 #ifndef __SWITCH__
275 #endif
276  break;
277  case RENDER_GLES2_OPENGL:
279  break;
280 #else // ANDROID
281 
284  break;
287  break;
288  case RENDER_SAFE_OPENGL:
290  break;
291  case RENDER_GLES_OPENGL:
293  break;
294  case RENDER_GLES2_OPENGL:
296  break;
297 #endif // ANDROID
298 
299  case RENDER_SOFTWARE:
300  case RENDER_SDL2_DEFAULT:
301  case RENDER_NULL:
302  case RENDER_LAST:
303  default:
304  reportAlways("Unknown OpenGL backend: %d", mUseOpenGL)
305  break;
306  }
307 }
308 
309 Image *OpenGLImageHelper::glLoad(SDL_Surface *tmpImage,
310  int width, int height)
311 {
312  if (tmpImage == nullptr)
313  return nullptr;
314 
315  BLOCK_START("OpenGLImageHelper::glLoad")
316  // Flush current error flag.
318 
319  if (width == 0)
320  width = tmpImage->w;
321  if (height == 0)
322  height = tmpImage->h;
323 
324  SDL_Surface *oldImage = tmpImage;
325  tmpImage = convertSurfaceNormalize(tmpImage, width, height);
326  if (tmpImage == nullptr)
327  return nullptr;
328 
329  const int realWidth = tmpImage->w;
330  const int realHeight = tmpImage->h;
331 
332  const GLuint texture = getNewTexture();
333  bindTexture(texture);
334 
335  if (SDL_MUSTLOCK(tmpImage))
336  SDL_LockSurface(tmpImage);
337 
341  {
342 #ifndef __SWITCH__
343  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
344 #endif
345 #ifdef OPENGLERRORS
347 #endif // OPENGLERRORS
348  }
349 
350  if (!mUseTextureSampler)
351  {
352  if (mBlur)
353  {
354  mglTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
355 #ifdef OPENGLERRORS
357 #endif // OPENGLERRORS
358 
359  mglTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
360 #ifdef OPENGLERRORS
362 #endif // OPENGLERRORS
363  }
364  else
365  {
366  mglTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
367 #ifdef OPENGLERRORS
369 #endif // OPENGLERRORS
370 
371  mglTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
372 #ifdef OPENGLERRORS
374 #endif // OPENGLERRORS
375  }
376  }
377 #if !defined(ANDROID) && !defined(__native_client__)
378  mglTexParameteri(mTextureType, GL_TEXTURE_MAX_LEVEL, 0);
379 #endif // !defined(ANDROID) && !defined(__native_client__)
380 
382  tmpImage->w, tmpImage->h,
383  0, GL_RGBA, GL_UNSIGNED_BYTE, tmpImage->pixels);
384 #ifdef OPENGLERRORS
386 #endif // OPENGLERRORS
387 
388 #ifdef DEBUG_OPENGL
389 /*
390  disabled for now, because debugger can't show it
391  if (isGLNotNull(mglLabelObject))
392  {
393  const char *const text = "image text";
394  mglLabelObject(GL_TEXTURE, texture, strlen(text), text);
395  }
396 */
397 #endif // DEBUG_OPENGL
398 
399 /*
400  GLint compressed;
401  glGetTexLevelParameteriv(mTextureType, 0,
402  GL_TEXTURE_COMPRESSED_ARB, &compressed);
403  if (compressed)
404  logger->log("image compressed");
405  else
406  logger->log("image not compressed");
407 */
408 
409 #ifdef DEBUG_OPENGL_LEAKS
410  textures_count ++;
411 #endif // DEBUG_OPENGL_LEAKS
412 
413  if (SDL_MUSTLOCK(tmpImage))
414  SDL_UnlockSurface(tmpImage);
415 
416  if (oldImage != tmpImage)
417  MSDL_FreeSurface(tmpImage);
418 
420  if (error != 0U)
421  {
422  std::string errmsg = GraphicsManager::errorToString(error);
423  reportAlways("Error: Image GL import failed: %s (%u)",
424  errmsg.c_str(), error)
425 // return nullptr;
426  }
427 
428  BLOCK_END("OpenGLImageHelper::glLoad")
429  return new Image(texture, width, height, realWidth, realHeight);
430 }
431 
433 {
434  if (mBlur)
435  {
436  mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
437  mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
438  }
439  else
440  {
441  mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
442  mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
443  }
444 }
445 
447  int height) const
448 {
449 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
450  const uint32_t rmask = 0xff000000U;
451  const uint32_t gmask = 0x00ff0000U;
452  const uint32_t bmask = 0x0000ff00U;
453  const uint32_t amask = 0x000000ffU;
454 #else // SDL_BYTEORDER == SDL_BIG_ENDIAN
455 
456  const uint32_t rmask = 0x000000ffU;
457  const uint32_t gmask = 0x0000ff00U;
458  const uint32_t bmask = 0x00ff0000U;
459  const uint32_t amask = 0xff000000U;
460 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN
461 
462  width = powerOfTwo(width);
463  height = powerOfTwo(height);
464 
465  return MSDL_CreateRGBSurface(SDL_SWSURFACE,
466  width, height, 32, rmask, gmask, bmask, amask);
467 }
468 
470 {
471  GLuint texture = mTextures[mFreeTextureIndex];
474  {
475  mFreeTextureIndex = 0;
476  postInit();
477  }
478  return texture;
479 }
480 
482 {
484 }
485 
486 void OpenGLImageHelper::invalidate(const GLuint textureId)
487 {
488  if (isGLNotNull(mglInvalidateTexImage))
489  {
490  logger->log("invalidate: %u", textureId);
491  mglInvalidateTexImage(textureId, 0);
492  }
493 }
494 
496  const int x, const int y,
497  SDL_Surface *surface) const
498 {
499  if (surface == nullptr || image == nullptr)
500  return;
501 
502  SDL_Surface *const oldSurface = surface;
503  surface = convertSurface(surface, surface->w, surface->h);
504  if (surface == nullptr)
505  return;
506 
507  // +++ probably need combine
508  // mglTextureSubImage2D and mglTextureSubImage2DEXT
509  if (mglTextureSubImage2D != nullptr)
510  {
511  mglTextureSubImage2D(image->mGLImage,
512  0,
513  x, y,
514  surface->w, surface->h,
515  GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
516  }
517  else
518  {
519  mglTextureSubImage2DEXT(image->mGLImage,
520  mTextureType, 0,
521  x, y,
522  surface->w, surface->h,
523  GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
524  }
525 #ifdef OPENGLERRORS
527 #endif // OPENGLERRORS
528 
529  if (surface != oldSurface)
530  MSDL_FreeSurface(surface);
531 }
532 
533 #endif // USE_OPENGL
#define reportAlways(...)
Definition: checkutils.h:253
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 void logError()
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)
static void initTextureSampler(const GLint id)
static SDL_Surface * convertSurfaceNormalize(SDL_Surface *tmpImage, int width, int height)
static void invalidate(const GLuint textureId)
static SDL_Surface * convertSurface(SDL_Surface *tmpImage, int width, int height)
GLuint mTextures[texturesSize]
SDL_Surface * create32BitSurface(int width, int height) const
Image * glLoad(SDL_Surface *tmpImage, int width=0, int height=0)
Image * createTextSurface(SDL_Surface *const tmpImage, const int width, const int height, const float alpha)
static void bindTexture(const GLuint texture)
static int mInternalTextureType
static bool mUseTextureSampler
static int powerOfTwo(const int input)
Image * load(SDL_RWops *const rw, Dye const &dye)
void copySurfaceToImage(const Image *const image, const int x, const int y, SDL_Surface *surface) const
static const size_t texturesSize
#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 mglTexParameteri(...)
Definition: mgl.hpp:101
#define mglGenTextures(...)
Definition: mgl.hpp:105
#define mglTexImage2D(...)
Definition: mgl.hpp:103
#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