diff options
Diffstat (limited to 'src/resources/image.cpp')
-rw-r--r-- | src/resources/image.cpp | 431 |
1 files changed, 262 insertions, 169 deletions
diff --git a/src/resources/image.cpp b/src/resources/image.cpp index 9af3059a..d2a1c82e 100644 --- a/src/resources/image.cpp +++ b/src/resources/image.cpp @@ -34,31 +34,56 @@ int Image::mTextureType = 0; int Image::mTextureSize = 0; #endif -Image::Image(SDL_Surface *image): +Image::Image(SDL_Surface *image, bool hasAlphaChannel, Uint8 *alphaChannel): + mAlpha(1.0f), + mHasAlphaChannel(hasAlphaChannel), + mSDLSurface(image), + mAlphaChannel(alphaChannel) +{ #ifdef USE_OPENGL - mGLImage(0), + mGLImage = 0; #endif - mImage(image), - mAlpha(1.0f) -{ + mBounds.x = 0; mBounds.y = 0; - mBounds.w = mImage->w; - mBounds.h = mImage->h; + + mLoaded = false; + + if (mSDLSurface) + { + mBounds.w = mSDLSurface->w; + mBounds.h = 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), mGLImage(glimage), mTexWidth(texWidth), - mTexHeight(texHeight), - mImage(0), - mAlpha(1.0) + mTexHeight(texHeight) { mBounds.x = 0; mBounds.y = 0; mBounds.w = width; mBounds.h = height; + + if (mGLImage) + mLoaded = true; + else + { + logger->log( + "Image::Image(GLuint*, ...): Couldn't load invalid Surface!"); + mLoaded = false; + } } #endif @@ -132,153 +157,23 @@ Image *Image::load(SDL_Surface *tmpImage) { #ifdef USE_OPENGL if (mUseOpenGL) - { - // 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); - glBindTexture(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; - } - logger->log("Error: Image GL import failed: %s", errmsg.c_str()); - return NULL; - } - - return new Image(texture, width, height, realWidth, realHeight); - } + return _GLload(tmpImage); #endif - - bool hasAlpha = false; - - if (tmpImage->format->BitsPerPixel == 32) - { - // 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; - break; - } - } - } - - SDL_Surface *image; - - // Convert the surface to the current display format - if (hasAlpha) - image = SDL_DisplayFormatAlpha(tmpImage); - else - image = SDL_DisplayFormat(tmpImage); - - if (!image) - { - logger->log("Error: Image convert failed."); - return NULL; - } - - return new Image(image); + return _SDLload(tmpImage); } void Image::unload() { mLoaded = false; - if (mImage) + if (mSDLSurface) { // Free the image surface. - SDL_FreeSurface(mImage); - mImage = NULL; + SDL_FreeSurface(mSDLSurface); + mSDLSurface = NULL; + + delete[] mAlphaChannel; + mAlphaChannel = NULL; } #ifdef USE_OPENGL @@ -299,34 +194,79 @@ bool Image::isAnOpenGLOne() const #endif } -void Image::setAlpha(float a) +bool Image::hasAlphaChannel() { - if (mAlpha == a) + if (mLoaded) + return mHasAlphaChannel; + +#ifdef USE_OPENGL + if (mUseOpenGL) + return true; +#endif + + return false; +} + +void Image::setAlpha(float alpha) +{ + if (mAlpha == alpha) return; - mAlpha = a; + if (alpha < 0.0f || alpha > 1.0f) + return; - if (mImage) + mAlpha = alpha; + + if (mSDLSurface) { - // Set the alpha value this image is drawn at - SDL_SetAlpha(mImage, SDL_SRCALPHA, (int) (255 * mAlpha)); + if (!hasAlphaChannel()) + { + // Set the alpha value this image is drawn at + SDL_SetAlpha(mSDLSurface, SDL_SRCALPHA, (int) (255 * mAlpha)); + } + else + { + if (SDL_MUSTLOCK(mSDLSurface)) + SDL_LockSurface(mSDLSurface); + + for (int i = 0; i < mSDLSurface->w * mSDLSurface->h; ++i) + { + Uint8 r, g, b, a; + SDL_GetRGBA( + ((Uint32*) mSDLSurface->pixels)[i], + mSDLSurface->format, + &r, &g, &b, &a); + + a = (Uint8) (mAlphaChannel[i] * mAlpha); + + // Here is the pixel we want to set + ((Uint32 *)(mSDLSurface->pixels))[i] = + SDL_MapRGBA(mSDLSurface->format, r, g, b, a); + } + + if (SDL_MUSTLOCK(mSDLSurface)) + SDL_UnlockSurface(mSDLSurface); + } } } -Image* Image::merge(Image *image, int x, int y) +Image* Image::SDLmerge(Image *image, int x, int y) { - SDL_Surface* surface = new SDL_Surface(*(image->mImage)); + if (!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 = mImage->format; + 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(mImage); + 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++) @@ -340,7 +280,7 @@ Image* Image::merge(Image *image, int x, int y) // Retrieving a pixel to merge surface_pix = ((Uint32*) surface->pixels)[surface_offset]; - cur_pix = ((Uint32*) mImage->pixels)[current_offset]; + cur_pix = ((Uint32*) mSDLSurface->pixels)[current_offset]; // Retreiving each channel of the pixel using pixel format r = (Uint8)(((surface_pix & surface_fmt->Rmask) >> @@ -381,18 +321,13 @@ Image* Image::merge(Image *image, int x, int y) } } SDL_UnlockSurface(surface); - SDL_UnlockSurface(mImage); + SDL_UnlockSurface(mSDLSurface); Image *newImage = new Image(surface); return newImage; } -float Image::getAlpha() const -{ - return mAlpha; -} - Image* Image::SDLgetScaledImage(int width, int height) { // No scaling on incorrect new values. @@ -406,9 +341,9 @@ Image* Image::SDLgetScaledImage(int width, int height) Image* scaledImage = NULL; SDL_Surface* scaledSurface = NULL; - if (mImage) + if (mSDLSurface) { - scaledSurface = _SDLzoomSurface(mImage, + scaledSurface = _SDLzoomSurface(mSDLSurface, (double) width / getWidth(), (double) height / getHeight(), 1); @@ -421,7 +356,160 @@ Image* Image::SDLgetScaledImage(int width, int height) return scaledImage; } +Image *Image::_SDLload(SDL_Surface *tmpImage) +{ + if (!tmpImage) + return NULL; + + bool hasAlpha = false; + + // The alpha channel to be filled with alpha values + Uint8 *alphaChannel = new Uint8[tmpImage->w * tmpImage->h]; + + if (tmpImage->format->BitsPerPixel == 32) + { + // 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 = NULL; + } + + if (!image) + { + logger->log("Error: Image convert failed."); + delete[] alphaChannel; + return NULL; + } + + return new Image(image, hasAlpha, alphaChannel); +} + #ifdef USE_OPENGL +Image *Image::_GLload(SDL_Surface *tmpImage) +{ + // 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); + glBindTexture(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; + } + logger->log("Error: Image GL import failed: %s", errmsg.c_str()); + return NULL; + } + + return new Image(texture, width, height, realWidth, realHeight); +} + void Image::setLoadAsOpenGL(bool useOpenGL) { Image::mUseOpenGL = useOpenGL; @@ -455,7 +543,7 @@ Image *Image::getSubImage(int x, int y, int width, int height) mTexWidth, mTexHeight); #endif - return new SubImage(this, mImage, x, y, width, height); + return new SubImage(this, mSDLSurface, x, y, width, height); } //============================================================================ @@ -469,6 +557,9 @@ SubImage::SubImage(Image *parent, SDL_Surface *image, { mParent->incRef(); + mHasAlphaChannel = mParent->hasAlphaChannel(); + mAlphaChannel = mParent->SDLgetAlphaChannel(); + // Set up the rectangle. mBounds.x = x; mBounds.y = y; @@ -496,7 +587,9 @@ SubImage::SubImage(Image *parent, GLuint image, SubImage::~SubImage() { // Avoid destruction of the image - mImage = 0; + mSDLSurface = 0; + // Avoid possible destruction of its alpha channel + mAlphaChannel = 0; #ifdef USE_OPENGL mGLImage = 0; #endif |