Mana
Loading...
Searching...
No Matches
image.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana 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 "resources/image.h"
23
24#include "resources/dye.h"
26
27#ifdef USE_OPENGL
28#include "openglgraphics.h"
29#endif
30
31#include "log.h"
32
33#include <SDL_image.h>
34
35#include <algorithm>
36
37#ifdef USE_OPENGL
38bool Image::mUseOpenGL = false;
39bool Image::mPowerOfTwoTextures = true;
40int Image::mTextureType = 0;
41int Image::mTextureSize = 0;
42#endif
43
44// The low CPU mode is disabled per default
46
47SDL_Renderer *Image::mRenderer;
48
49Image::Image(SDL_Texture *texture, int width, int height):
50 mTexture(texture)
51{
52 mBounds.x = 0;
53 mBounds.y = 0;
54 mBounds.w = width;
55 mBounds.h = height;
56
57 if (!texture)
58 {
59 Log::info("Image::Image(SDL_Texture*, ...): Couldn't load invalid Surface!");
60 }
61}
62
63#ifdef USE_OPENGL
64Image::Image(GLuint glimage, int width, int height, int texWidth, int texHeight):
65 mGLImage(glimage),
66 mTexWidth(texWidth),
67 mTexHeight(texHeight)
68{
69 mBounds.x = 0;
70 mBounds.y = 0;
71 mBounds.w = width;
72 mBounds.h = height;
73
74 if (glimage == 0)
75 {
76 Log::info("Image::Image(GLuint, ...): Couldn't load invalid Surface!");
77 }
78}
79#endif
80
82{
83 if (mTexture)
84 {
85 SDL_DestroyTexture(mTexture);
86 mTexture = nullptr;
87 }
88
89#ifdef USE_OPENGL
90 if (mGLImage)
91 {
92 glDeleteTextures(1, &mGLImage);
93 mGLImage = 0;
94 }
95#endif
96}
97
98Resource *Image::load(SDL_RWops *rw)
99{
100 SDL_Surface *tmpImage = IMG_Load_RW(rw, 1);
101
102 if (!tmpImage)
103 {
104 Log::info("Error, image load failed: %s", IMG_GetError());
105 return nullptr;
106 }
107
108 Image *image = load(tmpImage);
109
110 SDL_FreeSurface(tmpImage);
111 return image;
112}
113
114Resource *Image::load(SDL_RWops *rw, const Dye &dye)
115{
116 SDL_Surface *surf = IMG_Load_RW(rw, 1);
117
118 if (!surf)
119 {
120 Log::info("Error, image load failed: %s", IMG_GetError());
121 return nullptr;
122 }
123
124 if (surf->format->format != SDL_PIXELFORMAT_RGBA32)
125 {
126 Log::warn("Image format is %s, not SDL_PIXELFORMAT_RGBA32. Converting...",
127 SDL_GetPixelFormatName(surf->format->format));
128
129 SDL_Surface *convertedSurf = SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_RGBA32, 0);
130 SDL_FreeSurface(surf);
131 if (!convertedSurf)
132 {
133 Log::info("Error, image convert failed: %s", SDL_GetError());
134 return nullptr;
135 }
136 surf = convertedSurf;
137 }
138
139 auto *pixels = static_cast<SDL_Color *>(surf->pixels);
140 for (SDL_Color *p_end = pixels + surf->w * surf->h; pixels != p_end; ++pixels)
141 {
142 if (!pixels->a)
143 continue;
144
145 int v[3] = { pixels->r, pixels->g, pixels->b };
146 dye.update(v);
147
148 pixels->r = v[0];
149 pixels->g = v[1];
150 pixels->b = v[2];
151 }
152
153 Image *image = load(surf);
154 SDL_FreeSurface(surf);
155 return image;
156}
157
158Image *Image::load(SDL_Surface *tmpImage)
159{
160#ifdef USE_OPENGL
161 if (mUseOpenGL)
162 return _GLload(tmpImage);
163#endif
164 return _SDLload(tmpImage);
165}
166
168{
169#ifdef USE_OPENGL
170 return mUseOpenGL;
171#else
172 return false;
173#endif
174}
175
176void Image::setAlpha(float alpha)
177{
179 return;
180
181 mAlpha = std::clamp(alpha, 0.0f, 1.0f);
182}
183
184Image *Image::_SDLload(SDL_Surface *image)
185{
186 if (!image || !mRenderer)
187 return nullptr;
188
189 SDL_Texture *texture = SDL_CreateTextureFromSurface(mRenderer, image);
190 return new Image(texture, image->w, image->h);
191}
192
193void Image::setRenderer(SDL_Renderer *renderer)
194{
195 mRenderer = renderer;
196}
197
198#ifdef USE_OPENGL
199Image *Image::_GLload(SDL_Surface *image)
200{
201 // Flush current error flag.
202 glGetError();
203
204 int width = image->w;
205 int height = image->h;
206 int realWidth = powerOfTwo(width);
207 int realHeight = powerOfTwo(height);
208
209 if (realWidth < width || realHeight < height)
210 {
211 Log::warn("Image too large, cropping to %dx%d texture!",
212 realWidth, realHeight);
213 }
214
215 // Determine 32-bit masks based on byte order
216 Uint32 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
223 rmask = 0x000000ff;
224 gmask = 0x0000ff00;
225 bmask = 0x00ff0000;
226 amask = 0xff000000;
227#endif
228
229 bool needsConversion = !(realWidth == width &&
230 realHeight == height &&
231 image->format->BytesPerPixel == 4 &&
232 image->format->Rmask == rmask &&
233 image->format->Gmask == gmask &&
234 image->format->Bmask == bmask &&
235 image->format->Amask == amask);
236
237 if (needsConversion)
238 {
239 SDL_Surface *oldImage = image;
240 image = SDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight,
241 32, rmask, gmask, bmask, amask);
242
243 if (!image)
244 {
245 Log::info("Error, image convert failed: out of memory");
246 return nullptr;
247 }
248
249 // Make sure the alpha channel is not used, but copied to destination
250 SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE);
251 SDL_BlitSurface(oldImage, nullptr, image, nullptr);
252 }
253
254 GLuint texture;
255 glGenTextures(1, &texture);
256 OpenGLGraphics::bindTexture(mTextureType, texture);
257
258 if (SDL_MUSTLOCK(image))
259 SDL_LockSurface(image);
260
261 glTexImage2D(mTextureType, 0, GL_RGBA8,
262 image->w, image->h,
263 0, GL_RGBA, GL_UNSIGNED_BYTE,
264 image->pixels);
265
266 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
267 glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
268 glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
269
270 if (SDL_MUSTLOCK(image))
271 SDL_UnlockSurface(image);
272
273 if (needsConversion)
274 SDL_FreeSurface(image);
275
276 if (GLenum error = glGetError())
277 {
278 const char *errmsg = "Unknown error";
279 switch (error)
280 {
281 case GL_INVALID_ENUM:
282 errmsg = "GL_INVALID_ENUM";
283 break;
284 case GL_INVALID_VALUE:
285 errmsg = "GL_INVALID_VALUE";
286 break;
287 case GL_INVALID_OPERATION:
288 errmsg = "GL_INVALID_OPERATION";
289 break;
290 case GL_STACK_OVERFLOW:
291 errmsg = "GL_STACK_OVERFLOW";
292 break;
293 case GL_STACK_UNDERFLOW:
294 errmsg = "GL_STACK_UNDERFLOW";
295 break;
296 case GL_OUT_OF_MEMORY:
297 errmsg = "GL_OUT_OF_MEMORY";
298 break;
299 }
300 Log::error("Image GL import failed: %s", errmsg);
301 return nullptr;
302 }
303
304 return new Image(texture, width, height, realWidth, realHeight);
305}
306
307void Image::setLoadAsOpenGL(bool useOpenGL)
308{
309 Image::mUseOpenGL = useOpenGL;
310}
311
312int Image::powerOfTwo(int input)
313{
314 int value;
315 if (mPowerOfTwoTextures)
316 {
317 value = 1;
318 while (value < input && value < mTextureSize)
319 {
320 value <<= 1;
321 }
322 }
323 else
324 {
325 value = input;
326 }
327 return value >= mTextureSize ? mTextureSize : value;
328}
329#endif
330
331Image *Image::getSubImage(int x, int y, int width, int height)
332{
333 // Create a new clipped sub-image
334#ifdef USE_OPENGL
335 if (mUseOpenGL)
336 return new SubImage(this, mGLImage,
337 mBounds.x + x,
338 mBounds.y + y,
339 width, height,
340 mTexWidth, mTexHeight);
341#endif
342
343 return new SubImage(this, mTexture,
344 mBounds.x + x,
345 mBounds.y + y,
346 width, height);
347}
348
349//============================================================================
350// SubImage Class
351//============================================================================
352
353SubImage::SubImage(Image *parent, SDL_Texture *texture,
354 int x, int y, int width, int height):
355 Image(texture, width, height),
356 mParent(parent)
357{
358 // Set up the rectangle.
359 mBounds.x = x;
360 mBounds.y = y;
361 mBounds.w = width;
362 mBounds.h = height;
363}
364
365#ifdef USE_OPENGL
366SubImage::SubImage(Image *parent, GLuint image,
367 int x, int y, int width, int height,
368 int texWidth, int texHeight):
369 Image(image, width, height, texWidth, texHeight),
370 mParent(parent)
371{
372 // Set up the rectangle.
373 mBounds.x = x;
374 mBounds.y = y;
375 mBounds.w = width;
376 mBounds.h = height;
377}
378#endif
379
381{
382 // Avoid destruction of the texture
383 mTexture = nullptr;
384#ifdef USE_OPENGL
385 mGLImage = 0;
386#endif
387}
Class for dispatching pixel-recoloring amongst several palettes.
Definition dye.h:66
void update(int color[3]) const
Modifies a pixel color.
Definition dye.cpp:230
Defines a class for loading and storing images.
Definition image.h:45
static Image * _SDLload(SDL_Surface *tmpImage)
SDL_Surface to SDL_Texture Image loader.
Definition image.cpp:184
SDL_Texture * mTexture
Definition image.h:166
static Resource * load(SDL_RWops *rw)
Loads an image from an SDL_RWops structure.
Definition image.cpp:98
SDL_Rect mBounds
Definition image.h:153
~Image() override
Definition image.cpp:81
static bool useOpenGL()
Tells if the system is using OpenGL or SDL.
Definition image.cpp:167
Image * getSubImage(int x, int y, int width, int height)
Creates a new image with the desired clipping rectangle.
Definition image.cpp:331
static bool mDisableTransparency
Stores whether the transparency is disabled.
Definition image.h:169
Image(SDL_Texture *texture, int width, int height)
SDL Constructor.
Definition image.cpp:49
void setAlpha(float alpha)
Sets the alpha value of this image.
Definition image.cpp:176
static void setRenderer(SDL_Renderer *renderer)
Definition image.cpp:193
float mAlpha
Definition image.h:154
static SDL_Renderer * mRenderer
Definition image.h:171
A generic reference counted resource object.
Definition resource.h:31
A clipped version of a larger image.
Definition image.h:204
~SubImage() override
Definition image.cpp:380
SubImage(Image *parent, SDL_Texture *texture, int x, int y, int width, int height)
Definition image.cpp:353
void warn(const char *log_text,...) LOG_PRINTF_ATTR
void info(const char *log_text,...) LOG_PRINTF_ATTR
void error(const char *log_text,...) LOG_PRINTF_ATTR