diff options
Diffstat (limited to 'src/resources/image/image.cpp')
-rw-r--r-- | src/resources/image/image.cpp | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/src/resources/image/image.cpp b/src/resources/image/image.cpp new file mode 100644 index 000000000..683129e7a --- /dev/null +++ b/src/resources/image/image.cpp @@ -0,0 +1,499 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2016 The ManaPlus Developers + * + * This file is part of The ManaPlus 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/image.h" + +#include "logger.h" + +#ifdef USE_OPENGL +#include "resources/openglimagehelper.h" +#endif // USE_OPENGL + +#include "resources/memorymanager.h" +#include "resources/sdlimagehelper.h" +#include "resources/subimage.h" + +#include "resources/resourcemanager/resourcemanager.h" + +#include "utils/sdlcheckutils.h" + +#ifdef USE_SDL2 +#include <SDL2_rotozoom.h> +#else // USE_SDL2 +#include <SDL_rotozoom.h> +#endif // USE_SDL2 + +#include "debug.h" + +#ifdef USE_SDL2 +Image::Image(SDL_Texture *restrict const image, + const int width, const int height) : + Resource(), +#ifdef USE_OPENGL + mGLImage(0), + mTexWidth(0), + mTexHeight(0), +#endif + mBounds(), + mAlpha(1.0F), + mSDLSurface(nullptr), + mTexture(image), + mAlphaChannel(nullptr), + mAlphaCache(), + mLoaded(false), + mHasAlphaChannel(false), + mUseAlphaCache(false), + mIsAlphaVisible(true), + mIsAlphaCalculated(false) +{ +#ifdef DEBUG_IMAGES + logger->log("created image: %p", this); +#endif + + mBounds.x = 0; + mBounds.y = 0; + + if (mTexture) + { + mBounds.w = CAST_U16(width); + mBounds.h = CAST_U16(height); + + mLoaded = true; + } + else + { + logger->log("Image::Image(SDL_Texture *const image):" + " Couldn't load invalid Surface!"); + mBounds.w = 0; + mBounds.h = 0; + } +} +#endif + +Image::Image(SDL_Surface *restrict const image, const bool hasAlphaChannel0, + uint8_t *restrict const alphaChannel) : + Resource(), +#ifdef USE_OPENGL + mGLImage(0), + mTexWidth(0), + mTexHeight(0), +#endif + mBounds(), + mAlpha(1.0F), + mSDLSurface(image), +#ifdef USE_SDL2 + mTexture(nullptr), +#endif + mAlphaChannel(alphaChannel), + mAlphaCache(), + mLoaded(false), + mHasAlphaChannel(hasAlphaChannel0), + mUseAlphaCache(SDLImageHelper::mEnableAlphaCache), + mIsAlphaVisible(hasAlphaChannel0), + mIsAlphaCalculated(false) +{ +#ifdef DEBUG_IMAGES + logger->log("created image: %p", static_cast<void*>(this)); +#endif + + mBounds.x = 0; + mBounds.y = 0; + + if (mSDLSurface) + { + mBounds.w = CAST_U16(mSDLSurface->w); + mBounds.h = CAST_U16(mSDLSurface->h); + + mLoaded = true; + } + else + { + logger->log( + "Image::Image(SDL_Surface*): Couldn't load invalid Surface!"); + mBounds.w = 0; + mBounds.h = 0; + } +} + +#ifdef USE_OPENGL +Image::Image(const GLuint glimage, const int width, const int height, + const int texWidth, const int texHeight) : + Resource(), + mGLImage(glimage), + mTexWidth(texWidth), + mTexHeight(texHeight), + mBounds(), + mAlpha(1.0F), + mSDLSurface(nullptr), +#ifdef USE_SDL2 + mTexture(nullptr), +#endif + mAlphaChannel(nullptr), + mAlphaCache(), + mLoaded(false), + mHasAlphaChannel(true), + mUseAlphaCache(false), + mIsAlphaVisible(true), + mIsAlphaCalculated(false) +{ +#ifdef DEBUG_IMAGES + logger->log("created image: %p", static_cast<void*>(this)); +#endif + + mBounds.x = 0; + mBounds.y = 0; + mBounds.w = CAST_U16(width); + mBounds.h = CAST_U16(height); + + if (mGLImage) + { + mLoaded = true; + } + else + { + logger->log("Image::Image(GLuint*, ...):" + " Couldn't load invalid Surface!"); + } +} +#endif + +Image::~Image() +{ +#ifdef DEBUG_IMAGES + logger->log("delete image: %p", static_cast<void*>(this)); + logger->log(" %s, %s", mIdPath.c_str(), mSource.c_str()); +#endif + unload(); +} + +void Image::SDLCleanCache() +{ + for (std::map<float, SDL_Surface*>::iterator + i = mAlphaCache.begin(), i_end = mAlphaCache.end(); + i != i_end; ++i) + { + if (mSDLSurface != i->second) + resourceManager->scheduleDelete(i->second); + i->second = nullptr; + } + mAlphaCache.clear(); +} + +void Image::unload() +{ + mLoaded = false; + + if (mSDLSurface) + { + SDLCleanCache(); + // Free the image surface. + MSDL_FreeSurface(mSDLSurface); + mSDLSurface = nullptr; + + delete [] mAlphaChannel; + mAlphaChannel = nullptr; + } +#ifdef USE_SDL2 + if (mTexture) + { + SDL_DestroyTexture(mTexture); + mTexture = nullptr; + } +#endif + +#ifdef USE_OPENGL + if (mGLImage) + { + glDeleteTextures(1, &mGLImage); + mGLImage = 0; +#ifdef DEBUG_OPENGL_LEAKS + if (textures_count > 0) + textures_count --; +#endif + } +#endif +} + +bool Image::hasAlphaChannel() const +{ + if (mLoaded) + return mHasAlphaChannel; + +#ifdef USE_OPENGL + if (OpenGLImageHelper::mUseOpenGL != RENDER_SOFTWARE) + return true; +#endif + + return false; +} + +SDL_Surface *Image::getByAlpha(const float alpha) +{ + const std::map<float, SDL_Surface*>::const_iterator + it = mAlphaCache.find(alpha); + if (it != mAlphaCache.end()) + return (*it).second; + return nullptr; +} + +void Image::setAlpha(const float alpha) +{ + if (mAlpha == alpha || !ImageHelper::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*>::const_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) + { + if (mSDLSurface == surface) + logger->log("bug"); + mAlphaCache.erase(alpha); + mSDLSurface = surface; + mAlpha = alpha; + return; + } + else + { + mSDLSurface = SDLImageHelper::SDLDuplicateSurface(mSDLSurface); + if (!mSDLSurface) + return; + } + } + + mAlpha = alpha; + + if (!mHasAlphaChannel) + { +#ifdef USE_SDL2 + SDL_SetSurfaceAlphaMod(mSDLSurface, + CAST_U8(255 * mAlpha)); +#else + // Set the alpha value this image is drawn at + SDL_SetAlpha(mSDLSurface, SDL_SRCALPHA, + CAST_U8(255 * mAlpha)); +#endif + } + else + { + if (SDL_MUSTLOCK(mSDLSurface)) + SDL_LockSurface(mSDLSurface); + + const int bx = mBounds.x; + const int by = mBounds.y; + const int bw = mBounds.w; + const int bh = mBounds.h; + const int bxw = bx + bw; + const int sw = mSDLSurface->w; + const int maxHeight = std::min(by + bh, mSDLSurface->h); + const int maxWidth = std::min(bxw, sw); + const int i1 = by * sw + bx; + const SDL_PixelFormat * const fmt = mSDLSurface->format; + const uint32_t amask = fmt->Amask; + const uint32_t invMask = ~fmt->Amask; + const uint8_t aloss = fmt->Aloss; + const uint8_t ashift = fmt->Ashift; + + if (!bx && bxw == sw) + { + const int i2 = (maxHeight - 1) * sw + maxWidth - 1; + for (int i = i1; i <= i2; i++) + { + const uint8_t sourceAlpha = mAlphaChannel[i]; + if (sourceAlpha > 0) + { + const uint8_t a = CAST_U8( + static_cast<float>(sourceAlpha) * mAlpha); + + uint32_t c = (static_cast<uint32_t*>( + mSDLSurface->pixels))[i]; + c &= invMask; + c |= ((a >> aloss) << ashift & amask); + (static_cast<uint32_t*>(mSDLSurface->pixels))[i] = c; + } + } + } + else + { + for (int y = by; y < maxHeight; y ++) + { + const int idx = y * sw; + const int x1 = idx + bx; + const int x2 = idx + maxWidth; + for (int i = x1; i < x2; i ++) + { + const uint8_t sourceAlpha = mAlphaChannel[i]; + if (sourceAlpha > 0) + { + const uint8_t a = CAST_U8( + static_cast<float>(sourceAlpha) * mAlpha); + + uint32_t c = (static_cast<uint32_t*>( + mSDLSurface->pixels))[i]; + c &= invMask; + c |= ((a >> aloss) << ashift & amask); + (static_cast<uint32_t*>( + mSDLSurface->pixels))[i] = c; + } + } + } + } + + if (SDL_MUSTLOCK(mSDLSurface)) + SDL_UnlockSurface(mSDLSurface); + } + } +#ifdef USE_SDL2 + else if (mTexture) + { + mAlpha = alpha; + SDL_SetTextureAlphaMod(mTexture, + CAST_U8(255 * mAlpha)); + } +#endif + else + { + mAlpha = alpha; + } +} + +Image* Image::SDLgetScaledImage(const int width, const int height) const +{ + // No scaling on incorrect new values. + if (width == 0 || height == 0) + return nullptr; + + // No scaling when there is ... no different given size ... + if (width == mBounds.w && height == mBounds.h) + return nullptr; + + Image* scaledImage = nullptr; + + if (mSDLSurface) + { + SDL_Surface *const scaledSurface = zoomSurface(mSDLSurface, + static_cast<double>(width) / mBounds.w, + static_cast<double>(height) / mBounds.h, + 1); + + // The load function takes care of the SDL<->OpenGL implementation + // and about freeing the given SDL_surface*. + if (scaledSurface) + { + scaledImage = imageHelper->loadSurface(scaledSurface); + MSDL_FreeSurface(scaledSurface); + } + } + return scaledImage; +} + +Image *Image::getSubImage(const int x, const int y, + const int width, const int height) +{ + // Create a new clipped sub-image +#ifdef USE_OPENGL + const RenderType mode = OpenGLImageHelper::mUseOpenGL; + if (mode == RENDER_NORMAL_OPENGL || + mode == RENDER_SAFE_OPENGL || + mode == RENDER_GLES_OPENGL || + mode == RENDER_GLES2_OPENGL || + mode == RENDER_MODERN_OPENGL) + { + return new SubImage(this, + mGLImage, + x, y, + width, height, + mTexWidth, mTexHeight); + } +#endif + +#ifdef USE_SDL2 +#ifndef USE_OPENGL + const RenderType mode = ImageHelper::mUseOpenGL; +#endif + if (mode == RENDER_SOFTWARE) + return new SubImage(this, mSDLSurface, x, y, width, height); + else + return new SubImage(this, mTexture, x, y, width, height); +#else + return new SubImage(this, mSDLSurface, x, y, width, height); +#endif +} + +void Image::SDLTerminateAlphaCache() +{ + SDLCleanCache(); + mUseAlphaCache = false; +} + +int Image::calcMemoryLocal() const +{ + // +++ this calculation can be wrong for SDL2 + int sz = static_cast<int>(sizeof(Image) + + sizeof(std::map<float, SDL_Surface*>)) + + Resource::calcMemoryLocal(); + if (mSDLSurface) + { + sz += CAST_S32(mAlphaCache.size()) * + memoryManager.getSurfaceSize(mSDLSurface); + } + return sz; +} + +#ifdef USE_OPENGL +void Image::decRef() +{ + if (mGLImage && getRefCount() <= 1) + OpenGLImageHelper::invalidate(mGLImage); + Resource::decRef(); +} +#endif |