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-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 
24 
25 #ifdef USE_OPENGL
26 
27 #include "graphicsmanager.h"
28 
32 #ifdef __native_client__
34 #endif // __native_client__
37 
38 #include "render/opengl/mgl.h"
39 #include "render/opengl/mglcheck.h"
40 
41 #include "resources/dye/dye.h"
43 
44 #include "resources/image/image.h"
45 
46 #include "utils/checkutils.h"
47 #include "utils/sdlcheckutils.h"
48 
49 #include "debug.h"
50 
51 #ifndef SDL_BIG_ENDIAN
52 #error missing SDL_endian.h
53 #endif // SDL_BYTEORDER
54 
58 bool OpenGLImageHelper::mBlur = true;
60 
62 {
63  glDeleteTextures(static_cast<GLsizei>(texturesSize - mFreeTextureIndex),
65 }
66 
67 Image *OpenGLImageHelper::load(SDL_RWops *const rw, Dye const &dye)
68 {
69  SDL_Surface *const tmpImage = loadPng(rw);
70  if (tmpImage == nullptr)
71  {
72  reportAlways("Error, image load failed: %s", SDL_GetError());
73  return nullptr;
74  }
75 
76  SDL_Surface *const surf = convertTo32Bit(tmpImage);
77  MSDL_FreeSurface(tmpImage);
78  if (surf == nullptr)
79  return nullptr;
80 
81  uint32_t *pixels = static_cast<uint32_t *>(surf->pixels);
82  const int type = dye.getType();
83 
84  switch (type)
85  {
86  case 1:
87  {
88  const DyePalette *const pal = dye.getSPalete();
89  if (pal != nullptr)
90  DYEPALETTEP(pal, SOGLColor)(pixels, surf->w * surf->h);
91  break;
92  }
93  case 2:
94  {
95  const DyePalette *const pal = dye.getAPalete();
96  if (pal != nullptr)
97  DYEPALETTEP(pal, AOGLColor)(pixels, surf->w * surf->h);
98  break;
99  }
100  case 0:
101  default:
102  {
103  dye.normalOGLDye(pixels, surf->w * surf->h);
104  break;
105  }
106  }
107 
108  Image *const image = loadSurface(surf);
109  MSDL_FreeSurface(surf);
110  return image;
111 }
112 
113 Image *OpenGLImageHelper::loadSurface(SDL_Surface *const tmpImage)
114 {
115  return glLoad(tmpImage);
116 }
117 
118 Image *OpenGLImageHelper::createTextSurface(SDL_Surface *const tmpImage,
119  const int width, const int height,
120  const float alpha)
121 {
122  if (tmpImage == nullptr)
123  return nullptr;
124 
125  Image *const img = glLoad(tmpImage, width, height);
126  if (img != nullptr)
127  img->setAlpha(alpha);
128  return img;
129 }
130 
131 int OpenGLImageHelper::powerOfTwo(const int input)
132 {
133  int value = 1;
134  while (value < input && value < mTextureSize)
135  value <<= 1;
136  return value >= mTextureSize ? mTextureSize : value;
137 }
138 
139 SDL_Surface *OpenGLImageHelper::convertSurfaceNormalize(SDL_Surface *tmpImage,
140  int width, int height)
141 {
142  if (tmpImage == nullptr)
143  return nullptr;
144 
145  int realWidth = powerOfTwo(width);
146  int realHeight = powerOfTwo(height);
147 
148  if (realWidth < width || realHeight < height)
149  {
150  reportAlways("Warning: image too large, cropping to %dx%d texture!",
151  tmpImage->w, tmpImage->h);
152  }
153 
154 #ifdef USE_SDL2
155  SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE);
156 #else // USE_SDL2
157 
158  // Make sure the alpha channel is not used, but copied to destination
159  SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
160 #endif // USE_SDL2
161 
162  // Determine 32-bit masks based on byte order
163  uint32_t rmask, gmask, bmask, amask;
164 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
165  rmask = 0xff000000;
166  gmask = 0x00ff0000;
167  bmask = 0x0000ff00;
168  amask = 0x000000ff;
169 #else // SDL_BYTEORDER == SDL_BIG_ENDIAN
170 
171  rmask = 0x000000ff;
172  gmask = 0x0000ff00;
173  bmask = 0x00ff0000;
174  amask = 0xff000000;
175 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN
176 
177  if (tmpImage->format->BitsPerPixel != 32
178  || realWidth != width || realHeight != height
179  || rmask != tmpImage->format->Rmask
180  || gmask != tmpImage->format->Gmask
181  || amask != tmpImage->format->Amask)
182  {
183  SDL_Surface *oldImage = tmpImage;
184 #ifdef USE_SDL2
185  SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE);
186 #endif // USE_SDL2
187 
188  tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight,
189  32, rmask, gmask, bmask, amask);
190 
191  if (tmpImage == nullptr)
192  {
193  reportAlways("Error, image convert failed: out of memory");
194  return nullptr;
195  }
196  SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr);
197  }
198  return tmpImage;
199 }
200 
201 SDL_Surface *OpenGLImageHelper::convertSurface(SDL_Surface *tmpImage,
202  int width, int height)
203 {
204  if (tmpImage == nullptr)
205  return nullptr;
206 
207 #ifdef USE_SDL2
208  SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE);
209 #else // USE_SDL2
210 
211  // Make sure the alpha channel is not used, but copied to destination
212  SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
213 #endif // USE_SDL2
214 
215  // Determine 32-bit masks based on byte order
216  uint32_t rmask, gmask, bmask, amask;
217 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
218  rmask = 0xff000000;
219  gmask = 0x00ff0000;
220  bmask = 0x0000ff00;
221  amask = 0x000000ff;
222 #else // SDL_BYTEORDER == SDL_BIG_ENDIAN
223 
224  rmask = 0x000000ff;
225  gmask = 0x0000ff00;
226  bmask = 0x00ff0000;
227  amask = 0xff000000;
228 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN
229 
230  if (tmpImage->format->BitsPerPixel != 32
231  || rmask != tmpImage->format->Rmask
232  || gmask != tmpImage->format->Gmask
233  || amask != tmpImage->format->Amask)
234  {
235  SDL_Surface *oldImage = tmpImage;
236 #ifdef USE_SDL2
237  SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE);
238 #endif // USE_SDL2
239 
240  tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
241  32, rmask, gmask, bmask, amask);
242 
243  if (tmpImage == nullptr)
244  {
245  reportAlways("Error, image convert failed: out of memory");
246  return nullptr;
247  }
248  SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr);
249  }
250  return tmpImage;
251 }
252 
253 void OpenGLImageHelper::bindTexture(const GLuint texture)
254 {
255  switch (mUseOpenGL)
256  {
257 #ifdef ANDROID
259  case RENDER_SAFE_OPENGL:
261  case RENDER_GLES2_OPENGL:
262  break;
263  case RENDER_GLES_OPENGL:
265  break;
266 #elif defined(__native_client__)
269  case RENDER_GLES_OPENGL:
270  break;
271  case RENDER_SAFE_OPENGL:
273  break;
274  case RENDER_GLES2_OPENGL:
276  break;
277 #else // ANDROID
278 
281  break;
284  break;
285  case RENDER_SAFE_OPENGL:
287  break;
288  case RENDER_GLES_OPENGL:
290  break;
291  case RENDER_GLES2_OPENGL:
293  break;
294 #endif // ANDROID
295 
296  case RENDER_SOFTWARE:
297  case RENDER_SDL2_DEFAULT:
298  case RENDER_NULL:
299  case RENDER_LAST:
300  default:
301  reportAlways("Unknown OpenGL backend: %d", mUseOpenGL);
302  break;
303  }
304 }
305 
306 Image *OpenGLImageHelper::glLoad(SDL_Surface *tmpImage,
307  int width, int height)
308 {
309  if (tmpImage == nullptr)
310  return nullptr;
311 
312  BLOCK_START("OpenGLImageHelper::glLoad")
313  // Flush current error flag.
315 
316  if (width == 0)
317  width = tmpImage->w;
318  if (height == 0)
319  height = tmpImage->h;
320 
321  SDL_Surface *oldImage = tmpImage;
322  tmpImage = convertSurfaceNormalize(tmpImage, width, height);
323  if (tmpImage == nullptr)
324  return nullptr;
325 
326  const int realWidth = tmpImage->w;
327  const int realHeight = tmpImage->h;
328 
329  const GLuint texture = getNewTexture();
330  bindTexture(texture);
331 
332  if (SDL_MUSTLOCK(tmpImage))
333  SDL_LockSurface(tmpImage);
334 
338  {
339  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
340 #ifdef OPENGLERRORS
342 #endif // OPENGLERRORS
343  }
344 
345  if (!mUseTextureSampler)
346  {
347  if (mBlur)
348  {
349  mglTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
350 #ifdef OPENGLERRORS
352 #endif // OPENGLERRORS
353 
354  mglTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
355 #ifdef OPENGLERRORS
357 #endif // OPENGLERRORS
358  }
359  else
360  {
361  mglTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
362 #ifdef OPENGLERRORS
364 #endif // OPENGLERRORS
365 
366  mglTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
367 #ifdef OPENGLERRORS
369 #endif // OPENGLERRORS
370  }
371  }
372 #if !defined(ANDROID) && !defined(__native_client__)
373  mglTexParameteri(mTextureType, GL_TEXTURE_MAX_LEVEL, 0);
374 #endif // !defined(ANDROID) && !defined(__native_client__)
375 
377  tmpImage->w, tmpImage->h,
378  0, GL_RGBA, GL_UNSIGNED_BYTE, tmpImage->pixels);
379 #ifdef OPENGLERRORS
381 #endif // OPENGLERRORS
382 
383 #ifdef DEBUG_OPENGL
384 /*
385  disabled for now, because debugger can't show it
386  if (isGLNotNull(mglLabelObject))
387  {
388  const char *const text = "image text";
389  mglLabelObject(GL_TEXTURE, texture, strlen(text), text);
390  }
391 */
392 #endif // DEBUG_OPENGL
393 
394 /*
395  GLint compressed;
396  glGetTexLevelParameteriv(mTextureType, 0,
397  GL_TEXTURE_COMPRESSED_ARB, &compressed);
398  if (compressed)
399  logger->log("image compressed");
400  else
401  logger->log("image not compressed");
402 */
403 
404 #ifdef DEBUG_OPENGL_LEAKS
405  textures_count ++;
406 #endif // DEBUG_OPENGL_LEAKS
407 
408  if (SDL_MUSTLOCK(tmpImage))
409  SDL_UnlockSurface(tmpImage);
410 
411  if (oldImage != tmpImage)
412  MSDL_FreeSurface(tmpImage);
413 
415  if (error != 0u)
416  {
417  std::string errmsg = GraphicsManager::errorToString(error);
418  reportAlways("Error: Image GL import failed: %s (%u)",
419  errmsg.c_str(), error);
420 // return nullptr;
421  }
422 
423  BLOCK_END("OpenGLImageHelper::glLoad")
424  return new Image(texture, width, height, realWidth, realHeight);
425 }
426 
428 {
429  if (mBlur)
430  {
431  mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
432  mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
433  }
434  else
435  {
436  mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
437  mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
438  }
439 }
440 
442  int height) const
443 {
444 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
445  const int rmask = 0xff000000;
446  const int gmask = 0x00ff0000;
447  const int bmask = 0x0000ff00;
448  const int amask = 0x000000ff;
449 #else // SDL_BYTEORDER == SDL_BIG_ENDIAN
450 
451  const int rmask = 0x000000ff;
452  const int gmask = 0x0000ff00;
453  const int bmask = 0x00ff0000;
454  const int amask = 0xff000000;
455 #endif // SDL_BYTEORDER == SDL_BIG_ENDIAN
456 
457  width = powerOfTwo(width);
458  height = powerOfTwo(height);
459 
460  return MSDL_CreateRGBSurface(SDL_SWSURFACE,
461  width, height, 32, rmask, gmask, bmask, amask);
462 }
463 
465 {
466  GLuint texture = mTextures[mFreeTextureIndex];
469  {
470  mFreeTextureIndex = 0;
471  postInit();
472  }
473  return texture;
474 }
475 
477 {
479 }
480 
481 void OpenGLImageHelper::invalidate(const GLuint textureId)
482 {
483  if (isGLNotNull(mglInvalidateTexImage))
484  {
485  logger->log("invalidate: %u", textureId);
486  mglInvalidateTexImage(textureId, 0);
487  }
488 }
489 
491  const int x, const int y,
492  SDL_Surface *surface) const
493 {
494  if (surface == nullptr || image == nullptr)
495  return;
496 
497  SDL_Surface *const oldSurface = surface;
498  surface = convertSurface(surface, surface->w, surface->h);
499  if (surface == nullptr)
500  return;
501 
502  // +++ probably need combine
503  // mglTextureSubImage2D and mglTextureSubImage2DEXT
504  if (mglTextureSubImage2D != nullptr)
505  {
506  mglTextureSubImage2D(image->mGLImage,
507  0,
508  x, y,
509  surface->w, surface->h,
510  GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
511  }
512  else
513  {
514  mglTextureSubImage2DEXT(image->mGLImage,
515  mTextureType, 0,
516  x, y,
517  surface->w, surface->h,
518  GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
519  }
520 #ifdef OPENGLERRORS
522 #endif // OPENGLERRORS
523 
524  if (surface != oldSurface)
525  MSDL_FreeSurface(surface);
526 }
527 
528 #endif // USE_OPENGL
Image * createTextSurface(SDL_Surface *const tmpImage, const int width, const int height, const float alpha)
static SDL_Surface * loadPng(SDL_RWops *const rw)
#define isGLNotNull(func)
Definition: mglcheck.h:27
int textures_count
Definition: client.cpp:125
Image * load(SDL_RWops *const rw, Dye const &dye)
#define MSDL_FreeSurface(surface)
Definition: debug.h:53
#define BLOCK_START(name)
Definition: perfomance.h:78
int getType() const
Definition: dye.cpp:147
Image * glLoad(SDL_Surface *tmpImage, int width=0, int height=0)
static void bindTexture(const GLenum target, const GLuint texture)
#define BLOCK_END(name)
Definition: perfomance.h:79
static SDL_Surface * convertTo32Bit(SDL_Surface *const tmpImage)
const DyePalette * getAPalete() const
Definition: dye.h:72
static void bindTexture(const GLenum target, const GLuint texture)
static RenderType mUseOpenGL
Definition: imagehelper.h:117
#define mglTexParameteri(...)
Definition: mgl.hpp:100
Definition: dye.h:39
Logger * logger
Definition: logger.cpp:95
SDL_Surface * create32BitSurface(int width, int height) const
#define DYEPALETTEP(palette, color)
Definition: dyepalette.h:39
virtual void setAlpha(const float alpha)
Definition: image.cpp:285
static void invalidate(const GLuint textureId)
static void bindTexture(const GLenum target, const GLuint texture)
static const size_t texturesSize
#define mglTexImage2D(...)
Definition: mgl.hpp:102
static bool mUseTextureSampler
static void bindTexture(const GLuint texture)
static SDL_Surface * convertSurfaceNormalize(SDL_Surface *tmpImage, int width, int height)
void normalOGLDye(uint32_t *pixels, const int bufSize) const
Definition: dye.cpp:285
const DyePalette * getSPalete() const
Definition: dye.h:66
GLuint mGLImage
Definition: image.h:182
static int mInternalTextureType
static void bindTexture(const GLenum target, const GLuint texture)
static void logError()
void copySurfaceToImage(const Image *const image, const int x, const int y, SDL_Surface *surface) const
static int powerOfTwo(const int input)
static void bindTexture(const GLenum target, const GLuint texture)
Definition: image.h:61
bool error(InputEvent &event) __attribute__((noreturn))
Definition: actions.cpp:81
#define mglGenTextures(...)
Definition: mgl.hpp:104
GLuint mTextures[texturesSize]
static GLenum getLastError()
void log(const char *const log_text,...)
Definition: logger.cpp:243
static std::string errorToString(const GLenum error)
static SDL_Surface * convertSurface(SDL_Surface *tmpImage, int width, int height)
#define reportAlways(...)
Definition: checkutils.h:252
GraphicsManager graphicsManager
static void initTextureSampler(const GLint id)
#define MSDL_CreateRGBSurface(flags, w, h, d, r, g, b, a)
Definition: debug.h:54
Image * loadSurface(SDL_Surface *const tmpImage)