summaryrefslogtreecommitdiff
path: root/src/resources/image.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/resources/image.cpp')
-rw-r--r--src/resources/image.cpp797
1 files changed, 797 insertions, 0 deletions
diff --git a/src/resources/image.cpp b/src/resources/image.cpp
new file mode 100644
index 000000000..78cec909b
--- /dev/null
+++ b/src/resources/image.cpp
@@ -0,0 +1,797 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "resources/image.h"
+
+#include "resources/dye.h"
+#include "resources/resourcemanager.h"
+
+#ifdef USE_OPENGL
+#include "openglgraphics.h"
+#include "opengl1graphics.h"
+#endif
+
+#include "log.h"
+#include "main.h"
+
+#include "utils/stringutils.h"
+
+#include <SDL_image.h>
+#include <SDL_rotozoom.h>
+
+#ifdef USE_OPENGL
+int Image::mUseOpenGL = 0;
+int Image::mTextureType = 0;
+int Image::mTextureSize = 0;
+#endif
+bool Image::mEnableAlphaCache = false;
+bool Image::mEnableAlpha = true;
+
+Image::Image(SDL_Surface *image, bool hasAlphaChannel, Uint8 *alphaChannel):
+ mAlpha(1.0f),
+ mHasAlphaChannel(hasAlphaChannel),
+ mSDLSurface(image),
+ mAlphaChannel(alphaChannel)
+{
+#ifdef USE_OPENGL
+ mGLImage = 0;
+#endif
+
+ mUseAlphaCache = Image::mEnableAlphaCache;
+
+ mBounds.x = 0;
+ mBounds.y = 0;
+
+ mLoaded = false;
+
+ if (mSDLSurface)
+ {
+ mBounds.w = static_cast<Uint16>(mSDLSurface->w);
+ mBounds.h = static_cast<Uint16>(mSDLSurface->h);
+
+ mLoaded = true;
+ }
+ else
+ {
+ logger->log(
+ "Image::Image(SDL_Surface*): Couldn't load invalid Surface!");
+ }
+}
+
+#ifdef USE_OPENGL
+Image::Image(GLuint glimage, int width, int height,
+ int texWidth, int texHeight):
+ mAlpha(1.0f),
+ mHasAlphaChannel(true),
+ mSDLSurface(0),
+ mAlphaChannel(0),
+ mUseAlphaCache(false),
+ mGLImage(glimage),
+ mTexWidth(texWidth),
+ mTexHeight(texHeight)
+{
+ mBounds.x = 0;
+ mBounds.y = 0;
+ mBounds.w = static_cast<Uint16>(width);
+ mBounds.h = static_cast<Uint16>(height);
+
+ if (mGLImage)
+ {
+ mLoaded = true;
+ }
+ else
+ {
+ logger->log(
+ "Image::Image(GLuint*, ...): Couldn't load invalid Surface!");
+ mLoaded = false;
+ }
+}
+#endif
+
+Image::~Image()
+{
+ unload();
+}
+
+Resource *Image::load(void *buffer, unsigned bufferSize)
+{
+ // Load the raw file data from the buffer in an RWops structure
+ SDL_RWops *rw = SDL_RWFromMem(buffer, bufferSize);
+ SDL_Surface *tmpImage = IMG_Load_RW(rw, 1);
+
+ if (!tmpImage)
+ {
+ logger->log("Error, image load failed: %s", IMG_GetError());
+ return NULL;
+ }
+
+ Image *image = load(tmpImage);
+
+ SDL_FreeSurface(tmpImage);
+ return image;
+}
+
+Resource *Image::load(void *buffer, unsigned bufferSize, Dye const &dye)
+{
+ SDL_RWops *rw = SDL_RWFromMem(buffer, bufferSize);
+ SDL_Surface *tmpImage = IMG_Load_RW(rw, 1);
+
+ if (!tmpImage)
+ {
+ logger->log("Error, image load failed: %s", IMG_GetError());
+ return NULL;
+ }
+
+ SDL_PixelFormat rgba;
+ rgba.palette = NULL;
+ rgba.BitsPerPixel = 32;
+ rgba.BytesPerPixel = 4;
+ rgba.Rmask = 0xFF000000; rgba.Rloss = 0; rgba.Rshift = 24;
+ rgba.Gmask = 0x00FF0000; rgba.Gloss = 0; rgba.Gshift = 16;
+ rgba.Bmask = 0x0000FF00; rgba.Bloss = 0; rgba.Bshift = 8;
+ rgba.Amask = 0x000000FF; rgba.Aloss = 0; rgba.Ashift = 0;
+ rgba.colorkey = 0;
+ rgba.alpha = 255;
+
+ SDL_Surface *surf = SDL_ConvertSurface(tmpImage, &rgba, SDL_SWSURFACE);
+ SDL_FreeSurface(tmpImage);
+
+ Uint32 *pixels = static_cast< Uint32 * >(surf->pixels);
+ for (Uint32 *p_end = pixels + surf->w * surf->h; pixels != p_end; ++pixels)
+ {
+ int alpha = *pixels & 255;
+ if (!alpha) continue;
+ int v[3];
+ v[0] = (*pixels >> 24) & 255;
+ v[1] = (*pixels >> 16) & 255;
+ v[2] = (*pixels >> 8 ) & 255;
+ dye.update(v);
+ *pixels = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | alpha;
+ }
+
+ Image *image = load(surf);
+ SDL_FreeSurface(surf);
+ return image;
+}
+
+Image *Image::load(SDL_Surface *tmpImage)
+{
+#ifdef USE_OPENGL
+ if (mUseOpenGL)
+ return _GLload(tmpImage);
+#endif
+ return _SDLload(tmpImage);
+}
+
+void Image::SDLCleanCache()
+{
+ ResourceManager *resman = ResourceManager::getInstance();
+
+ for (std::map<float, SDL_Surface*>::iterator
+ i = mAlphaCache.begin(), i_end = mAlphaCache.end();
+ i != i_end; ++i)
+ {
+ if (mSDLSurface != i->second)
+ resman->scheduleDelete(i->second);
+ i->second = 0;
+ }
+ mAlphaCache.clear();
+}
+
+void Image::unload()
+{
+ mLoaded = false;
+
+ if (mSDLSurface)
+ {
+ SDLCleanCache();
+ // Free the image surface.
+ SDL_FreeSurface(mSDLSurface);
+ mSDLSurface = NULL;
+
+ delete[] mAlphaChannel;
+ mAlphaChannel = NULL;
+ }
+
+#ifdef USE_OPENGL
+ if (mGLImage)
+ {
+ glDeleteTextures(1, &mGLImage);
+ mGLImage = 0;
+ }
+#endif
+}
+
+int Image::useOpenGL() const
+{
+#ifdef USE_OPENGL
+ return mUseOpenGL;
+#else
+ return 0;
+#endif
+}
+
+bool Image::hasAlphaChannel()
+{
+ if (mLoaded)
+ return mHasAlphaChannel;
+
+#ifdef USE_OPENGL
+ if (mUseOpenGL)
+ return true;
+#endif
+
+ return false;
+}
+
+SDL_Surface *Image::getByAlpha(float alpha)
+{
+ std::map<float, SDL_Surface*>::iterator it = mAlphaCache.find(alpha);
+ if (it != mAlphaCache.end())
+ return (*it).second;
+ return 0;
+}
+
+void Image::setAlpha(float alpha)
+{
+ if (mAlpha == alpha || !mEnableAlpha)
+ return;
+
+ if (alpha < 0.0f || alpha > 1.0f)
+ return;
+
+ if (mSDLSurface)
+ {
+ if (mUseAlphaCache)
+ {
+ SDL_Surface *surface = getByAlpha(mAlpha);
+ if (!surface)
+ {
+ if (mAlphaCache.size() > 100)
+ {
+#ifdef DEBUG_ALPHA_CACHE
+ logger->log("cleanCache");
+ for (std::map<float, SDL_Surface*>::iterator
+ i = mAlphaCache.begin(), i_end = mAlphaCache.end();
+ i != i_end; ++i)
+ {
+ logger->log("alpha: " + toString(i->first));
+ }
+#endif
+ SDLCleanCache();
+ }
+ surface = mSDLSurface;
+ if (surface)
+ mAlphaCache[mAlpha] = surface;
+ }
+ else
+ {
+ logger->log("cache bug");
+ }
+
+ surface = getByAlpha(alpha);
+ if (surface)
+ {
+// logger->log("hit");
+ if (mSDLSurface == surface)
+ logger->log("bug");
+// else
+// SDL_FreeSurface(mSDLSurface);
+ mAlphaCache.erase(alpha);
+ mSDLSurface = surface;
+ mAlpha = alpha;
+ return;
+ }
+ else
+ {
+ mSDLSurface = Image::SDLDuplicateSurface(mSDLSurface);
+ }
+ // logger->log("miss");
+ }
+
+ mAlpha = alpha;
+
+ if (!hasAlphaChannel())
+ {
+ // Set the alpha value this image is drawn at
+ SDL_SetAlpha(mSDLSurface, SDL_SRCALPHA,
+ static_cast<unsigned char>(255 * mAlpha));
+ }
+ else
+ {
+ if (SDL_MUSTLOCK(mSDLSurface))
+ SDL_LockSurface(mSDLSurface);
+
+ // Precompute as much as possible
+ int maxHeight = std::min((mBounds.y + mBounds.h), mSDLSurface->h);
+ int maxWidth = std::min((mBounds.x + mBounds.w), mSDLSurface->w);
+ int i = 0;
+
+ for (int y = mBounds.y; y < maxHeight; y++)
+ {
+ for (int x = mBounds.x; x < maxWidth; x++)
+ {
+ i = y * mSDLSurface->w + x;
+ // Only change the pixel if it was visible at load time...
+ Uint8 sourceAlpha = mAlphaChannel[i];
+ if (sourceAlpha > 0)
+ {
+ Uint8 r, g, b, a;
+ SDL_GetRGBA((static_cast<Uint32*>
+ (mSDLSurface->pixels))[i],
+ mSDLSurface->format,
+ &r, &g, &b, &a);
+
+ a = (Uint8) (static_cast<float>(sourceAlpha) * mAlpha);
+
+ // Here is the pixel we want to set
+ (static_cast<Uint32 *>(mSDLSurface->pixels))[i] =
+ SDL_MapRGBA(mSDLSurface->format, r, g, b, a);
+ }
+ }
+ }
+
+ if (SDL_MUSTLOCK(mSDLSurface))
+ SDL_UnlockSurface(mSDLSurface);
+ }
+ }
+ else
+ {
+ mAlpha = alpha;
+ }
+}
+
+Image* Image::SDLmerge(Image *image, int x, int y)
+{
+ if (!mSDLSurface || !image || !image->mSDLSurface)
+ return NULL;
+
+ SDL_Surface* surface = new SDL_Surface(*(image->mSDLSurface));
+
+ Uint32 surface_pix, cur_pix;
+ Uint8 r, g, b, a, p_r, p_g, p_b, p_a;
+ double f_a, f_ca, f_pa;
+ SDL_PixelFormat *current_fmt = mSDLSurface->format;
+ SDL_PixelFormat *surface_fmt = surface->format;
+ int current_offset, surface_offset;
+ int offset_x, offset_y;
+
+ SDL_LockSurface(surface);
+ SDL_LockSurface(mSDLSurface);
+ // for each pixel lines of a source image
+ for (offset_x = (x > 0 ? 0 : -x); offset_x < image->getWidth() &&
+ x + offset_x < getWidth(); offset_x++)
+ {
+ for (offset_y = (y > 0 ? 0 : -y); offset_y < image->getHeight()
+ && y + offset_y < getHeight(); offset_y++)
+ {
+ // Computing offset on both images
+ current_offset = (y + offset_y) * getWidth() + x + offset_x;
+ surface_offset = offset_y * surface->w + offset_x;
+
+ // Retrieving a pixel to merge
+ surface_pix = ((Uint32*) surface->pixels)[surface_offset];
+ cur_pix = ((Uint32*) mSDLSurface->pixels)[current_offset];
+
+ // Retreiving each channel of the pixel using pixel format
+ r = (Uint8)(((surface_pix & surface_fmt->Rmask) >>
+ surface_fmt->Rshift) << surface_fmt->Rloss);
+ g = (Uint8)(((surface_pix & surface_fmt->Gmask) >>
+ surface_fmt->Gshift) << surface_fmt->Gloss);
+ b = (Uint8)(((surface_pix & surface_fmt->Bmask) >>
+ surface_fmt->Bshift) << surface_fmt->Bloss);
+ a = (Uint8)(((surface_pix & surface_fmt->Amask) >>
+ surface_fmt->Ashift) << surface_fmt->Aloss);
+
+ // Retreiving previous alpha value
+ p_a = (Uint8)(((cur_pix & current_fmt->Amask) >>
+ current_fmt->Ashift) << current_fmt->Aloss);
+
+ // new pixel with no alpha or nothing on previous pixel
+ if (a == SDL_ALPHA_OPAQUE || (p_a == 0 && a > 0))
+ ((Uint32 *)(surface->pixels))[current_offset] =
+ SDL_MapRGBA(current_fmt, r, g, b, a);
+ else if (a > 0)
+ { // alpha is lower => merge color with previous value
+ f_a = static_cast<double>(a) / 255.0;
+ f_ca = 1.0 - f_a;
+ f_pa = static_cast<double>(p_a) / 255.0;
+ p_r = (Uint8)(((cur_pix & current_fmt->Rmask) >>
+ current_fmt->Rshift) << current_fmt->Rloss);
+ p_g = (Uint8)(((cur_pix & current_fmt->Gmask) >>
+ current_fmt->Gshift) << current_fmt->Gloss);
+ p_b = (Uint8)(((cur_pix & current_fmt->Bmask) >>
+ current_fmt->Bshift) << current_fmt->Bloss);
+ r = (Uint8)((double) p_r * f_ca * f_pa + (double)r * f_a);
+ g = (Uint8)((double) p_g * f_ca * f_pa + (double)g * f_a);
+ b = (Uint8)((double) p_b * f_ca * f_pa + (double)b * f_a);
+ a = (a > p_a ? a : p_a);
+ ((Uint32 *)(surface->pixels))[current_offset] =
+ SDL_MapRGBA(current_fmt, r, g, b, a);
+ }
+ }
+ }
+ SDL_UnlockSurface(surface);
+ SDL_UnlockSurface(mSDLSurface);
+
+ Image *newImage = new Image(surface);
+
+ return newImage;
+}
+
+Image* Image::SDLgetScaledImage(int width, int height)
+{
+ // No scaling on incorrect new values.
+ if (width == 0 || height == 0)
+ return NULL;
+
+ // No scaling when there is ... no different given size ...
+ if (width == getWidth() && height == getHeight())
+ return NULL;
+
+ Image* scaledImage = NULL;
+ SDL_Surface* scaledSurface = NULL;
+
+ if (mSDLSurface)
+ {
+ scaledSurface = zoomSurface(mSDLSurface,
+ (double) width / getWidth(),
+ (double) height / getHeight(),
+ 1);
+
+ // The load function takes care of the SDL<->OpenGL implementation
+ // and about freeing the given SDL_surface*.
+ if (scaledSurface)
+ scaledImage = load(scaledSurface);
+ }
+ return scaledImage;
+}
+
+SDL_Surface* Image::convertTo32Bit(SDL_Surface* tmpImage)
+{
+ if (!tmpImage)
+ return NULL;
+ SDL_PixelFormat RGBAFormat;
+ RGBAFormat.palette = 0;
+ RGBAFormat.colorkey = 0;
+ RGBAFormat.alpha = 0;
+ RGBAFormat.BitsPerPixel = 32;
+ RGBAFormat.BytesPerPixel = 4;
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ RGBAFormat.Rmask = 0xFF000000;
+ RGBAFormat.Rshift = 0;
+ RGBAFormat.Rloss = 0;
+ RGBAFormat.Gmask = 0x00FF0000;
+ RGBAFormat.Gshift = 8;
+ RGBAFormat.Gloss = 0;
+ RGBAFormat.Bmask = 0x0000FF00;
+ RGBAFormat.Bshift = 16;
+ RGBAFormat.Bloss = 0;
+ RGBAFormat.Amask = 0x000000FF;
+ RGBAFormat.Ashift = 24;
+ RGBAFormat.Aloss = 0;
+#else
+ RGBAFormat.Rmask = 0x000000FF;
+ RGBAFormat.Rshift = 24;
+ RGBAFormat.Rloss = 0;
+ RGBAFormat.Gmask = 0x0000FF00;
+ RGBAFormat.Gshift = 16;
+ RGBAFormat.Gloss = 0;
+ RGBAFormat.Bmask = 0x00FF0000;
+ RGBAFormat.Bshift = 8;
+ RGBAFormat.Bloss = 0;
+ RGBAFormat.Amask = 0xFF000000;
+ RGBAFormat.Ashift = 0;
+ RGBAFormat.Aloss = 0;
+#endif
+ return SDL_ConvertSurface(tmpImage, &RGBAFormat, SDL_SWSURFACE);
+}
+
+SDL_Surface* Image::SDLDuplicateSurface(SDL_Surface* tmpImage)
+{
+ if (!tmpImage || !tmpImage->format)
+ return NULL;
+
+ return SDL_ConvertSurface(tmpImage, tmpImage->format, SDL_SWSURFACE);
+}
+
+Image *Image::_SDLload(SDL_Surface *tmpImage)
+{
+ if (!tmpImage)
+ return NULL;
+
+ bool hasAlpha = false;
+ bool converted = false;
+
+ // The alpha channel to be filled with alpha values
+ Uint8 *alphaChannel = new Uint8[tmpImage->w * tmpImage->h];
+
+ if (tmpImage->format->BitsPerPixel != 32)
+ {
+ tmpImage = convertTo32Bit(tmpImage);
+
+ if (!tmpImage)
+ {
+ delete[] alphaChannel;
+ return NULL;
+ }
+ converted = true;
+ }
+
+ // Figure out whether the image uses its alpha layer
+ for (int i = 0; i < tmpImage->w * tmpImage->h; ++i)
+ {
+ Uint8 r, g, b, a;
+ SDL_GetRGBA(((Uint32*) tmpImage->pixels)[i],
+ tmpImage->format, &r, &g, &b, &a);
+
+ if (a != 255)
+ hasAlpha = true;
+
+ alphaChannel[i] = a;
+ }
+
+ SDL_Surface *image;
+
+ // Convert the surface to the current display format
+ if (hasAlpha)
+ {
+ image = SDL_DisplayFormatAlpha(tmpImage);
+ }
+ else
+ {
+ image = SDL_DisplayFormat(tmpImage);
+
+ // We also delete the alpha channel since
+ // it's not used.
+ delete[] alphaChannel;
+ alphaChannel = 0;
+ }
+
+ if (!image)
+ {
+ logger->log1("Error: Image convert failed.");
+ delete[] alphaChannel;
+ return 0;
+ }
+
+ if (converted)
+ SDL_FreeSurface(tmpImage);
+
+ return new Image(image, hasAlpha, alphaChannel);
+}
+
+#ifdef USE_OPENGL
+Image *Image::_GLload(SDL_Surface *tmpImage)
+{
+ if (!tmpImage)
+ return NULL;
+
+ // Flush current error flag.
+ glGetError();
+
+ int width = tmpImage->w;
+ int height = tmpImage->h;
+ int realWidth = powerOfTwo(width);
+ int realHeight = powerOfTwo(height);
+
+ if (realWidth < width || realHeight < height)
+ {
+ logger->log("Warning: image too large, cropping to %dx%d texture!",
+ tmpImage->w, tmpImage->h);
+ }
+
+ // Make sure the alpha channel is not used, but copied to destination
+ SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE);
+
+ // Determine 32-bit masks based on byte order
+ Uint32 rmask, gmask, bmask, amask;
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ rmask = 0xff000000;
+ gmask = 0x00ff0000;
+ bmask = 0x0000ff00;
+ amask = 0x000000ff;
+#else
+ rmask = 0x000000ff;
+ gmask = 0x0000ff00;
+ bmask = 0x00ff0000;
+ amask = 0xff000000;
+#endif
+
+ SDL_Surface *oldImage = tmpImage;
+ tmpImage = SDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight,
+ 32, rmask, gmask, bmask, amask);
+
+ if (!tmpImage)
+ {
+ logger->log("Error, image convert failed: out of memory");
+ return NULL;
+ }
+
+ SDL_BlitSurface(oldImage, NULL, tmpImage, NULL);
+
+ GLuint texture;
+ glGenTextures(1, &texture);
+ if (mUseOpenGL == 1)
+ OpenGLGraphics::bindTexture(mTextureType, texture);
+ else if (mUseOpenGL == 2)
+// glBindTexture(mTextureType, texture);
+ OpenGL1Graphics::bindTexture(mTextureType, texture);
+
+ if (SDL_MUSTLOCK(tmpImage))
+ SDL_LockSurface(tmpImage);
+
+ glTexImage2D(mTextureType, 0, 4, tmpImage->w, tmpImage->h,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, tmpImage->pixels);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ if (SDL_MUSTLOCK(tmpImage))
+ SDL_UnlockSurface(tmpImage);
+
+ SDL_FreeSurface(tmpImage);
+
+ GLenum error = glGetError();
+ if (error)
+ {
+ std::string errmsg = "Unknown error";
+ switch (error)
+ {
+ case GL_INVALID_ENUM:
+ errmsg = "GL_INVALID_ENUM";
+ break;
+ case GL_INVALID_VALUE:
+ errmsg = "GL_INVALID_VALUE";
+ break;
+ case GL_INVALID_OPERATION:
+ errmsg = "GL_INVALID_OPERATION";
+ break;
+ case GL_STACK_OVERFLOW:
+ errmsg = "GL_STACK_OVERFLOW";
+ break;
+ case GL_STACK_UNDERFLOW:
+ errmsg = "GL_STACK_UNDERFLOW";
+ break;
+ case GL_OUT_OF_MEMORY:
+ errmsg = "GL_OUT_OF_MEMORY";
+ break;
+ default:
+ break;
+ }
+ logger->log("Error: Image GL import failed: %s", errmsg.c_str());
+ return NULL;
+ }
+
+ return new Image(texture, width, height, realWidth, realHeight);
+}
+
+void Image::setLoadAsOpenGL(int useOpenGL)
+{
+ Image::mUseOpenGL = useOpenGL;
+}
+
+int Image::powerOfTwo(int input)
+{
+ int value;
+ if (mTextureType == GL_TEXTURE_2D)
+ {
+ value = 1;
+ while (value < input && value < mTextureSize)
+ value <<= 1;
+ }
+ else
+ {
+ value = input;
+ }
+ return value >= mTextureSize ? mTextureSize : value;
+}
+#endif
+
+Image *Image::getSubImage(int x, int y, int width, int height)
+{
+ // Create a new clipped sub-image
+#ifdef USE_OPENGL
+ if (mUseOpenGL)
+ {
+ return new SubImage(this, mGLImage, x, y, width, height,
+ mTexWidth, mTexHeight);
+ }
+#endif
+
+ return new SubImage(this, mSDLSurface, x, y, width, height);
+}
+
+void Image::SDLTerminateAlphaCache()
+{
+ SDLCleanCache();
+ mUseAlphaCache = false;
+}
+
+//============================================================================
+// SubImage Class
+//============================================================================
+
+SubImage::SubImage(Image *parent, SDL_Surface *image,
+ int x, int y, int width, int height):
+ Image(image),
+ mParent(parent)
+{
+ if (mParent)
+ {
+ mParent->incRef();
+ mParent->SDLTerminateAlphaCache();
+ mHasAlphaChannel = mParent->hasAlphaChannel();
+ mAlphaChannel = mParent->SDLgetAlphaChannel();
+ }
+ else
+ {
+ mHasAlphaChannel = false;
+ mAlphaChannel = 0;
+ }
+
+ // Set up the rectangle.
+ mBounds.x = static_cast<short>(x);
+ mBounds.y = static_cast<short>(y);
+ mBounds.w = static_cast<Uint16>(width);
+ mBounds.h = static_cast<Uint16>(height);
+ mUseAlphaCache = false;
+}
+
+#ifdef USE_OPENGL
+SubImage::SubImage(Image *parent, GLuint image,
+ int x, int y, int width, int height,
+ int texWidth, int texHeight):
+ Image(image, width, height, texWidth, texHeight),
+ mParent(parent)
+{
+ if (mParent)
+ mParent->incRef();
+
+ // Set up the rectangle.
+ mBounds.x = static_cast<short>(x);
+ mBounds.y = static_cast<short>(y);
+ mBounds.w = static_cast<Uint16>(width);
+ mBounds.h = static_cast<Uint16>(height);
+}
+#endif
+
+SubImage::~SubImage()
+{
+ // Avoid destruction of the image
+ mSDLSurface = 0;
+ // Avoid possible destruction of its alpha channel
+ mAlphaChannel = 0;
+#ifdef USE_OPENGL
+ mGLImage = 0;
+#endif
+ if (mParent)
+ mParent->decRef();
+}
+
+Image *SubImage::getSubImage(int x, int y, int w, int h)
+{
+ if (mParent)
+ return mParent->getSubImage(mBounds.x + x, mBounds.y + y, w, h);
+ else
+ return NULL;
+}