00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "resources/image.h"
00023
00024 #include "resources/dye.h"
00025
00026 #include "log.h"
00027
00028 #include <SDL_image.h>
00029
00030 #ifdef USE_OPENGL
00031 bool Image::mUseOpenGL = false;
00032 int Image::mTextureType = 0;
00033 int Image::mTextureSize = 0;
00034 #endif
00035
00036 Image::Image(SDL_Surface *image):
00037 #ifdef USE_OPENGL
00038 mGLImage(0),
00039 #endif
00040 mImage(image),
00041 mAlpha(1.0f)
00042 {
00043 mBounds.x = 0;
00044 mBounds.y = 0;
00045 mBounds.w = mImage->w;
00046 mBounds.h = mImage->h;
00047 }
00048
00049 #ifdef USE_OPENGL
00050 Image::Image(GLuint glimage, int width, int height, int texWidth, int texHeight):
00051 mGLImage(glimage),
00052 mTexWidth(texWidth),
00053 mTexHeight(texHeight),
00054 mImage(0),
00055 mAlpha(1.0)
00056 {
00057 mBounds.x = 0;
00058 mBounds.y = 0;
00059 mBounds.w = width;
00060 mBounds.h = height;
00061 }
00062 #endif
00063
00064 Image::~Image()
00065 {
00066 unload();
00067 }
00068
00069 Resource *Image::load(void *buffer, unsigned bufferSize)
00070 {
00071
00072 SDL_RWops *rw = SDL_RWFromMem(buffer, bufferSize);
00073 SDL_Surface *tmpImage = IMG_Load_RW(rw, 1);
00074
00075 if (!tmpImage)
00076 {
00077 logger->log("Error, image load failed: %s", IMG_GetError());
00078 return NULL;
00079 }
00080
00081 Image *image = load(tmpImage);
00082
00083 SDL_FreeSurface(tmpImage);
00084 return image;
00085 }
00086
00087 Resource *Image::load(void *buffer, unsigned bufferSize, Dye const &dye)
00088 {
00089 SDL_RWops *rw = SDL_RWFromMem(buffer, bufferSize);
00090 SDL_Surface *tmpImage = IMG_Load_RW(rw, 1);
00091
00092 if (!tmpImage)
00093 {
00094 logger->log("Error, image load failed: %s", IMG_GetError());
00095 return NULL;
00096 }
00097
00098 SDL_PixelFormat rgba;
00099 rgba.palette = NULL;
00100 rgba.BitsPerPixel = 32;
00101 rgba.BytesPerPixel = 4;
00102 rgba.Rmask = 0xFF000000; rgba.Rloss = 0; rgba.Rshift = 24;
00103 rgba.Gmask = 0x00FF0000; rgba.Gloss = 0; rgba.Gshift = 16;
00104 rgba.Bmask = 0x0000FF00; rgba.Bloss = 0; rgba.Bshift = 8;
00105 rgba.Amask = 0x000000FF; rgba.Aloss = 0; rgba.Ashift = 0;
00106 rgba.colorkey = 0;
00107 rgba.alpha = 255;
00108
00109 SDL_Surface *surf = SDL_ConvertSurface(tmpImage, &rgba, SDL_SWSURFACE);
00110 SDL_FreeSurface(tmpImage);
00111
00112 Uint32 *pixels = static_cast< Uint32 * >(surf->pixels);
00113 for (Uint32 *p_end = pixels + surf->w * surf->h; pixels != p_end; ++pixels)
00114 {
00115 int alpha = *pixels & 255;
00116 if (!alpha) continue;
00117 int v[3];
00118 v[0] = (*pixels >> 24) & 255;
00119 v[1] = (*pixels >> 16) & 255;
00120 v[2] = (*pixels >> 8 ) & 255;
00121 dye.update(v);
00122 *pixels = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | alpha;
00123 }
00124
00125 Image *image = load(surf);
00126 SDL_FreeSurface(surf);
00127 return image;
00128 }
00129
00130 Image *Image::load(SDL_Surface *tmpImage)
00131 {
00132 #ifdef USE_OPENGL
00133 if (mUseOpenGL)
00134 {
00135
00136 glGetError();
00137
00138 int width = tmpImage->w;
00139 int height = tmpImage->h;
00140 int realWidth = powerOfTwo(width);
00141 int realHeight = powerOfTwo(height);
00142
00143 if (realWidth < width || realHeight < height)
00144 {
00145 logger->log("Warning: image too large, cropping to %dx%d texture!",
00146 tmpImage->w, tmpImage->h);
00147 }
00148
00149
00150 SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
00151
00152
00153 Uint32 rmask, gmask, bmask, amask;
00154 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00155 rmask = 0xff000000;
00156 gmask = 0x00ff0000;
00157 bmask = 0x0000ff00;
00158 amask = 0x000000ff;
00159 #else
00160 rmask = 0x000000ff;
00161 gmask = 0x0000ff00;
00162 bmask = 0x00ff0000;
00163 amask = 0xff000000;
00164 #endif
00165
00166 SDL_Surface *oldImage = tmpImage;
00167 tmpImage = SDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight,
00168 32, rmask, gmask, bmask, amask);
00169
00170 if (!tmpImage)
00171 {
00172 logger->log("Error, image convert failed: out of memory");
00173 return NULL;
00174 }
00175
00176 SDL_BlitSurface(oldImage, NULL, tmpImage, NULL);
00177
00178 GLuint texture;
00179 glGenTextures(1, &texture);
00180 glBindTexture(mTextureType, texture);
00181
00182 if (SDL_MUSTLOCK(tmpImage))
00183 SDL_LockSurface(tmpImage);
00184
00185 glTexImage2D(
00186 mTextureType, 0, 4,
00187 tmpImage->w, tmpImage->h,
00188 0, GL_RGBA, GL_UNSIGNED_BYTE,
00189 tmpImage->pixels);
00190
00191 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
00192 glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00193 glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00194
00195 if (SDL_MUSTLOCK(tmpImage)) {
00196 SDL_UnlockSurface(tmpImage);
00197 }
00198
00199 SDL_FreeSurface(tmpImage);
00200
00201 GLenum error = glGetError();
00202 if (error)
00203 {
00204 std::string errmsg = "Unknown error";
00205 switch (error)
00206 {
00207 case GL_INVALID_ENUM:
00208 errmsg = "GL_INVALID_ENUM";
00209 break;
00210 case GL_INVALID_VALUE:
00211 errmsg = "GL_INVALID_VALUE";
00212 break;
00213 case GL_INVALID_OPERATION:
00214 errmsg = "GL_INVALID_OPERATION";
00215 break;
00216 case GL_STACK_OVERFLOW:
00217 errmsg = "GL_STACK_OVERFLOW";
00218 break;
00219 case GL_STACK_UNDERFLOW:
00220 errmsg = "GL_STACK_UNDERFLOW";
00221 break;
00222 case GL_OUT_OF_MEMORY:
00223 errmsg = "GL_OUT_OF_MEMORY";
00224 break;
00225 }
00226 logger->log("Error: Image GL import failed: %s", errmsg.c_str());
00227 return NULL;
00228 }
00229
00230 return new Image(texture, width, height, realWidth, realHeight);
00231 }
00232 #endif
00233
00234 bool hasAlpha = false;
00235
00236 if (tmpImage->format->BitsPerPixel == 32)
00237 {
00238
00239 for (int i = 0; i < tmpImage->w * tmpImage->h; ++i)
00240 {
00241 Uint8 r, g, b, a;
00242 SDL_GetRGBA(
00243 ((Uint32*) tmpImage->pixels)[i],
00244 tmpImage->format,
00245 &r, &g, &b, &a);
00246
00247 if (a != 255)
00248 {
00249 hasAlpha = true;
00250 break;
00251 }
00252 }
00253 }
00254
00255 SDL_Surface *image;
00256
00257
00258 if (hasAlpha)
00259 image = SDL_DisplayFormatAlpha(tmpImage);
00260 else
00261 image = SDL_DisplayFormat(tmpImage);
00262
00263 if (!image)
00264 {
00265 logger->log("Error: Image convert failed.");
00266 return NULL;
00267 }
00268
00269 return new Image(image);
00270 }
00271
00272 void Image::unload()
00273 {
00274 mLoaded = false;
00275
00276 if (mImage)
00277 {
00278
00279 SDL_FreeSurface(mImage);
00280 mImage = NULL;
00281 }
00282
00283 #ifdef USE_OPENGL
00284 if (mGLImage)
00285 {
00286 glDeleteTextures(1, &mGLImage);
00287 mGLImage = 0;
00288 }
00289 #endif
00290 }
00291
00292 Image *Image::getSubImage(int x, int y, int width, int height)
00293 {
00294
00295 #ifdef USE_OPENGL
00296 if (mUseOpenGL)
00297 return new SubImage(this, mGLImage, x, y, width, height,
00298 mTexWidth, mTexHeight);
00299 #endif
00300
00301 return new SubImage(this, mImage, x, y, width, height);
00302 }
00303
00304 void Image::setAlpha(float a)
00305 {
00306 if (mAlpha == a)
00307 return;
00308
00309 mAlpha = a;
00310
00311 if (mImage)
00312 {
00313
00314 SDL_SetAlpha(mImage, SDL_SRCALPHA, (int) (255 * mAlpha));
00315 }
00316 }
00317
00318 Image* Image::merge(Image *image, int x, int y)
00319 {
00320 SDL_Surface* surface = new SDL_Surface(*(image->mImage));
00321
00322 Uint32 surface_pix, cur_pix;
00323 Uint8 r, g, b, a, p_r, p_g, p_b, p_a;
00324 double f_a, f_ca, f_pa;
00325 SDL_PixelFormat *current_fmt = mImage->format;
00326 SDL_PixelFormat *surface_fmt = surface->format;
00327 int current_offset, surface_offset;
00328 int offset_x, offset_y;
00329
00330 SDL_LockSurface(surface);
00331 SDL_LockSurface(mImage);
00332
00333 for (offset_x = (x > 0 ? 0 : -x); offset_x < image->getWidth() &&
00334 x + offset_x < getWidth(); offset_x++)
00335 {
00336 for (offset_y = (y > 0 ? 0 : -y); offset_y < image->getHeight()
00337 && y + offset_y < getHeight(); offset_y++)
00338 {
00339
00340 current_offset = (y + offset_y) * getWidth() + x + offset_x;
00341 surface_offset = offset_y * surface->w + offset_x;
00342
00343
00344 surface_pix = ((Uint32*) surface->pixels)[surface_offset];
00345 cur_pix = ((Uint32*) mImage->pixels)[current_offset];
00346
00347
00348 r = (Uint8)(((surface_pix & surface_fmt->Rmask) >>
00349 surface_fmt->Rshift) << surface_fmt->Rloss);
00350 g = (Uint8)(((surface_pix & surface_fmt->Gmask) >>
00351 surface_fmt->Gshift) << surface_fmt->Gloss);
00352 b = (Uint8)(((surface_pix & surface_fmt->Bmask) >>
00353 surface_fmt->Bshift) << surface_fmt->Bloss);
00354 a = (Uint8)(((surface_pix & surface_fmt->Amask) >>
00355 surface_fmt->Ashift) << surface_fmt->Aloss);
00356
00357
00358 p_a = (Uint8)(((cur_pix & current_fmt->Amask) >>
00359 current_fmt->Ashift) << current_fmt->Aloss);
00360
00361
00362 if (a == SDL_ALPHA_OPAQUE || (p_a == 0 && a > 0))
00363 ((Uint32 *)(surface->pixels))[current_offset] =
00364 SDL_MapRGBA(current_fmt, r, g, b, a);
00365 else if (a > 0)
00366 {
00367 f_a = (double) a / 255.0;
00368 f_ca = 1.0 - f_a;
00369 f_pa = (double) p_a / 255.0;
00370 p_r = (Uint8)(((cur_pix & current_fmt->Rmask) >>
00371 current_fmt->Rshift) << current_fmt->Rloss);
00372 p_g = (Uint8)(((cur_pix & current_fmt->Gmask) >>
00373 current_fmt->Gshift) << current_fmt->Gloss);
00374 p_b = (Uint8)(((cur_pix & current_fmt->Bmask) >>
00375 current_fmt->Bshift) << current_fmt->Bloss);
00376 r = (Uint8)((double) p_r * f_ca * f_pa + (double)r * f_a);
00377 g = (Uint8)((double) p_g * f_ca * f_pa + (double)g * f_a);
00378 b = (Uint8)((double) p_b * f_ca * f_pa + (double)b * f_a);
00379 a = (a > p_a ? a : p_a);
00380 ((Uint32 *)(surface->pixels))[current_offset] =
00381 SDL_MapRGBA(current_fmt, r, g, b, a);
00382 }
00383 }
00384 }
00385 SDL_UnlockSurface(surface);
00386 SDL_UnlockSurface(mImage);
00387
00388 Image *newImage = new Image(surface);
00389
00390 return newImage;
00391 }
00392
00393 float Image::getAlpha() const
00394 {
00395 return mAlpha;
00396 }
00397
00398 #ifdef USE_OPENGL
00399 void Image::setLoadAsOpenGL(bool useOpenGL)
00400 {
00401 Image::mUseOpenGL = useOpenGL;
00402 }
00403
00404 int Image::powerOfTwo(int input)
00405 {
00406 int value;
00407 if (mTextureType == GL_TEXTURE_2D)
00408 {
00409 value = 1;
00410 while (value < input && value < mTextureSize)
00411 {
00412 value <<= 1;
00413 }
00414 }
00415 else
00416 {
00417 value = input;
00418 }
00419 return value >= mTextureSize ? mTextureSize : value;
00420 }
00421 #endif
00422
00423
00424
00425
00426
00427 SubImage::SubImage(Image *parent, SDL_Surface *image,
00428 int x, int y, int width, int height):
00429 Image(image),
00430 mParent(parent)
00431 {
00432 mParent->incRef();
00433
00434
00435 mBounds.x = x;
00436 mBounds.y = y;
00437 mBounds.w = width;
00438 mBounds.h = height;
00439 }
00440
00441 #ifdef USE_OPENGL
00442 SubImage::SubImage(Image *parent, GLuint image,
00443 int x, int y, int width, int height,
00444 int texWidth, int texHeight):
00445 Image(image, width, height, texWidth, texHeight),
00446 mParent(parent)
00447 {
00448 mParent->incRef();
00449
00450
00451 mBounds.x = x;
00452 mBounds.y = y;
00453 mBounds.w = width;
00454 mBounds.h = height;
00455 }
00456 #endif
00457
00458 SubImage::~SubImage()
00459 {
00460
00461 mImage = 0;
00462 #ifdef USE_OPENGL
00463 mGLImage = 0;
00464 #endif
00465 mParent->decRef();
00466 }
00467
00468 Image *SubImage::getSubImage(int x, int y, int w, int h)
00469 {
00470 return mParent->getSubImage(mBounds.x + x, mBounds.y + y, w, h);
00471 }
00472