diff options
author | Andrei Karas <akaras@inbox.ru> | 2015-12-15 18:57:03 +0300 |
---|---|---|
committer | Andrei Karas <akaras@inbox.ru> | 2015-12-15 18:58:08 +0300 |
commit | 1ffeeb5d960ecf45ff285b8045265bb38de86b18 (patch) | |
tree | f3e0a40a39cf0b00376d0201eee53e13e7592c5c /src/resources | |
parent | 559bd8c3275b05cf061813094a880223f343cff5 (diff) | |
download | manaplus-1ffeeb5d960ecf45ff285b8045265bb38de86b18.tar.gz manaplus-1ffeeb5d960ecf45ff285b8045265bb38de86b18.tar.bz2 manaplus-1ffeeb5d960ecf45ff285b8045265bb38de86b18.tar.xz manaplus-1ffeeb5d960ecf45ff285b8045265bb38de86b18.zip |
Add safeopenglimagehelper for using in safe opengl mode only.
Diffstat (limited to 'src/resources')
-rw-r--r-- | src/resources/image.cpp | 9 | ||||
-rw-r--r-- | src/resources/image.h | 7 | ||||
-rw-r--r-- | src/resources/safeopenglimagehelper.cpp | 475 | ||||
-rw-r--r-- | src/resources/safeopenglimagehelper.h | 159 |
4 files changed, 645 insertions, 5 deletions
diff --git a/src/resources/image.cpp b/src/resources/image.cpp index 8b952779c..c196b217c 100644 --- a/src/resources/image.cpp +++ b/src/resources/image.cpp @@ -28,7 +28,10 @@ #ifdef USE_OPENGL #include "resources/openglimagehelper.h" -#endif +#ifndef ANDROID +#include "resources/safeopenglimagehelper.h" +#endif // ANDROID +#endif // USE_OPENGL #include "resources/sdlimagehelper.h" #include "resources/subimage.h" @@ -36,9 +39,9 @@ #ifdef USE_SDL2 #include <SDL2_rotozoom.h> -#else +#else // USE_SDL2 #include <SDL_rotozoom.h> -#endif +#endif // USE_SDL2 #include "debug.h" diff --git a/src/resources/image.h b/src/resources/image.h index 804df4c2e..0740864b7 100644 --- a/src/resources/image.h +++ b/src/resources/image.h @@ -55,7 +55,6 @@ class Image notfinal : public Resource friend class CompoundSprite; friend class Graphics; friend class ImageHelper; - friend class OpenGLImageHelper; friend class SDLGraphics; friend class SDLImageHelper; friend class SurfaceGraphics; @@ -73,7 +72,11 @@ class Image notfinal : public Resource friend class NormalOpenGLGraphics; friend class NullOpenGLGraphics; friend class SafeOpenGLGraphics; -#endif + friend class OpenGLImageHelper; +#ifndef ANDROID + friend class SafeOpenGLImageHelper; +#endif // ANDROID +#endif // USE_OPENGL public: A_DELETE_COPY(Image) diff --git a/src/resources/safeopenglimagehelper.cpp b/src/resources/safeopenglimagehelper.cpp new file mode 100644 index 000000000..1271b7e1b --- /dev/null +++ b/src/resources/safeopenglimagehelper.cpp @@ -0,0 +1,475 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 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/safeopenglimagehelper.h" + +#ifdef USE_OPENGL + +#include "graphicsmanager.h" +#include "logger.h" + +#include "render/mgl.h" +#include "render/mglcheck.h" +#include "render/mobileopengl2graphics.h" +#include "render/mobileopenglgraphics.h" +#include "render/modernopenglgraphics.h" +#include "render/normalopenglgraphics.h" +#include "render/safeopenglgraphics.h" + +#include "resources/dye.h" +#include "resources/dyepalette.h" +#include "resources/image.h" + +#include "utils/sdlcheckutils.h" + +#include <SDL_image.h> + +#include "debug.h" + +int SafeOpenGLImageHelper::mTextureType = 0; +int SafeOpenGLImageHelper::mInternalTextureType = GL_RGBA8; +int SafeOpenGLImageHelper::mTextureSize = 0; +bool SafeOpenGLImageHelper::mBlur = true; +bool SafeOpenGLImageHelper::mUseTextureSampler = false; + +SafeOpenGLImageHelper::~SafeOpenGLImageHelper() +{ + glDeleteTextures(static_cast<GLsizei>(texturesSize - mFreeTextureIndex), + &mTextures[mFreeTextureIndex]); +} + +Image *SafeOpenGLImageHelper::load(SDL_RWops *const rw, + Dye const &dye) +{ + SDL_Surface *const tmpImage = loadPng(rw); + if (!tmpImage) + { + logger->log("Error, image load failed: %s", IMG_GetError()); + return nullptr; + } + + SDL_Surface *const surf = convertTo32Bit(tmpImage); + MSDL_FreeSurface(tmpImage); + if (!surf) + return nullptr; + + uint32_t *pixels = static_cast<uint32_t *>(surf->pixels); + const int type = dye.getType(); + + switch (type) + { + case 1: + { + const DyePalette *const pal = dye.getSPalete(); + if (pal) + pal->replaceSOGLColor(pixels, surf->w * surf->h); + break; + } + case 2: + { + const DyePalette *const pal = dye.getAPalete(); + if (pal) + pal->replaceAOGLColor(pixels, surf->w * surf->h); + break; + } + case 0: + default: + { + dye.normalOGLDye(pixels, surf->w * surf->h); + break; + } + } + + Image *const image = load(surf); + MSDL_FreeSurface(surf); + return image; +} + +Image *SafeOpenGLImageHelper::load(SDL_Surface *const tmpImage) +{ + return glLoad(tmpImage); +} + +Image *SafeOpenGLImageHelper::createTextSurface(SDL_Surface *const tmpImage, + const int width, + const int height, + const float alpha) +{ + if (!tmpImage) + return nullptr; + + Image *const img = glLoad(tmpImage, width, height); + if (img) + img->setAlpha(alpha); + return img; +} + +int SafeOpenGLImageHelper::powerOfTwo(const 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; +} + +SDL_Surface *SafeOpenGLImageHelper::convertSurfaceNormalize(SDL_Surface + *tmpImage, + int width, + int height) +{ + if (!tmpImage) + return nullptr; + + 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); + } + +#ifdef USE_SDL2 + SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE); +#else + // Make sure the alpha channel is not used, but copied to destination + SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE); +#endif + + // Determine 32-bit masks based on byte order + uint32_t 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 + + if (tmpImage->format->BitsPerPixel != 32 + || realWidth != width || realHeight != height + || rmask != tmpImage->format->Rmask + || gmask != tmpImage->format->Gmask + || amask != tmpImage->format->Amask) + { + SDL_Surface *oldImage = tmpImage; +#ifdef USE_SDL2 + SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE); +#endif + tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, realWidth, realHeight, + 32, rmask, gmask, bmask, amask); + + if (!tmpImage) + { + logger->log("Error, image convert failed: out of memory"); + return nullptr; + } + SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr); + } + return tmpImage; +} + +SDL_Surface *SafeOpenGLImageHelper::convertSurface(SDL_Surface *tmpImage, + int width, + int height) +{ + if (!tmpImage) + return nullptr; + +#ifdef USE_SDL2 + SDL_SetSurfaceAlphaMod(tmpImage, SDL_ALPHA_OPAQUE); +#else + // Make sure the alpha channel is not used, but copied to destination + SDL_SetAlpha(tmpImage, 0, SDL_ALPHA_OPAQUE); +#endif + + // Determine 32-bit masks based on byte order + uint32_t 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 + + if (tmpImage->format->BitsPerPixel != 32 + || rmask != tmpImage->format->Rmask + || gmask != tmpImage->format->Gmask + || amask != tmpImage->format->Amask) + { + SDL_Surface *oldImage = tmpImage; +#ifdef USE_SDL2 + SDL_SetSurfaceBlendMode(oldImage, SDL_BLENDMODE_NONE); +#endif + tmpImage = MSDL_CreateRGBSurface(SDL_SWSURFACE, width, height, + 32, rmask, gmask, bmask, amask); + + if (!tmpImage) + { + logger->log("Error, image convert failed: out of memory"); + return nullptr; + } + SDL_BlitSurface(oldImage, nullptr, tmpImage, nullptr); + } + return tmpImage; +} + +void SafeOpenGLImageHelper::bindTexture(const GLuint texture) +{ + switch (mUseOpenGL) + { +#ifdef __native_client__ + case RENDER_NORMAL_OPENGL: + case RENDER_MODERN_OPENGL: + case RENDER_GLES_OPENGL: + break; + case RENDER_SAFE_OPENGL: + SafeOpenGLGraphics::bindTexture(mTextureType, texture); + break; + case RENDER_GLES2_OPENGL: + MobileOpenGL2Graphics::bindTexture(mTextureType, texture); + break; +#else // __native_client__ + case RENDER_NORMAL_OPENGL: + NormalOpenGLGraphics::bindTexture(mTextureType, texture); + break; + case RENDER_MODERN_OPENGL: + ModernOpenGLGraphics::bindTexture(mTextureType, texture); + break; + case RENDER_SAFE_OPENGL: + SafeOpenGLGraphics::bindTexture(mTextureType, texture); + break; + case RENDER_GLES_OPENGL: + MobileOpenGLGraphics::bindTexture(mTextureType, texture); + break; + case RENDER_GLES2_OPENGL: + MobileOpenGL2Graphics::bindTexture(mTextureType, texture); + break; +#endif // __native_client__ + case RENDER_SOFTWARE: + case RENDER_SDL2_DEFAULT: + case RENDER_NULL: + case RENDER_LAST: + default: + logger->log("Unknown OpenGL backend: %d", mUseOpenGL); + break; + } +} + +Image *SafeOpenGLImageHelper::glLoad(SDL_Surface *tmpImage, + int width, + int height) +{ + if (!tmpImage) + return nullptr; + + BLOCK_START("SafeOpenGLImageHelper::glLoad") + // Flush current error flag. + graphicsManager.getLastError(); + + if (!width) + width = tmpImage->w; + if (!height) + height = tmpImage->h; + + SDL_Surface *oldImage = tmpImage; + tmpImage = convertSurfaceNormalize(tmpImage, width, height); + if (!tmpImage) + return nullptr; + + const int realWidth = tmpImage->w; + const int realHeight = tmpImage->h; + + const GLuint texture = getNewTexture(); + bindTexture(texture); + + if (SDL_MUSTLOCK(tmpImage)) + SDL_LockSurface(tmpImage); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (!mUseTextureSampler) + { + if (mBlur) + { + glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(mTextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(mTextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + } +#ifndef ANDROID + glTexParameteri(mTextureType, GL_TEXTURE_MAX_LEVEL, 0); +#endif + + glTexImage2D(mTextureType, 0, mInternalTextureType, + tmpImage->w, tmpImage->h, + 0, GL_RGBA, GL_UNSIGNED_BYTE, tmpImage->pixels); + +#ifdef DEBUG_OPENGL +// disabled for now, because debugger can't show it +// if (isGLNotNull(mglLabelObject)) +// { +// const char *const text = "image text"; +// mglLabelObject(GL_TEXTURE, texture, strlen(text), text); +// } +#endif + +/* + GLint compressed; + glGetTexLevelParameteriv(mTextureType, 0, + GL_TEXTURE_COMPRESSED_ARB, &compressed); + if (compressed) + logger->log("image compressed"); + else + logger->log("image not compressed"); +*/ + +#ifdef DEBUG_OPENGL_LEAKS + textures_count ++; +#endif + + if (SDL_MUSTLOCK(tmpImage)) + SDL_UnlockSurface(tmpImage); + + if (oldImage != tmpImage) + MSDL_FreeSurface(tmpImage); + + GLenum error = graphicsManager.getLastError(); + if (error) + { + std::string errmsg = GraphicsManager::errorToString(error); + logger->log("Error: Image GL import failed: %s (%u)", + errmsg.c_str(), error); +// return nullptr; + } + + BLOCK_END("SafeOpenGLImageHelper::glLoad") + return new Image(texture, width, height, realWidth, realHeight); +} + +void SafeOpenGLImageHelper::initTextureSampler(const GLint id) +{ + if (mBlur) + { + mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + mglSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + mglSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } +} + +SDL_Surface *SafeOpenGLImageHelper::create32BitSurface(int width, + int height) const +{ +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int rmask = 0xff000000; + const int gmask = 0x00ff0000; + const int bmask = 0x0000ff00; + const int amask = 0x000000ff; +#else + const int rmask = 0x000000ff; + const int gmask = 0x0000ff00; + const int bmask = 0x00ff0000; + const int amask = 0xff000000; +#endif + + width = powerOfTwo(width); + height = powerOfTwo(height); + + return MSDL_CreateRGBSurface(SDL_SWSURFACE, + width, height, 32, rmask, gmask, bmask, amask); +} + +GLuint SafeOpenGLImageHelper::getNewTexture() +{ + GLuint texture = mTextures[mFreeTextureIndex]; + mFreeTextureIndex ++; + if (mFreeTextureIndex == texturesSize) + { + mFreeTextureIndex = 0; + postInit(); + } + return texture; +} + +void SafeOpenGLImageHelper::postInit() +{ + glGenTextures(texturesSize, &mTextures[mFreeTextureIndex]); +} + +void SafeOpenGLImageHelper::invalidate(const GLuint textureId) +{ + if (isGLNotNull(mglInvalidateTexImage)) + { + logger->log("invalidate: %u", textureId); + mglInvalidateTexImage(textureId, 0); + } +} + +void SafeOpenGLImageHelper::copySurfaceToImage(const Image *const image, + const int x, + const int y, + SDL_Surface *surface) const +{ + if (!surface || !image) + return; + + SDL_Surface *const oldSurface = surface; + surface = convertSurface(surface, surface->w, surface->h); + if (!surface) + return; + + mglTextureSubImage2D(image->mGLImage, + mTextureType, 0, + x, y, + surface->w, surface->h, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels); + + if (surface != oldSurface) + MSDL_FreeSurface(surface); +} + +#endif diff --git a/src/resources/safeopenglimagehelper.h b/src/resources/safeopenglimagehelper.h new file mode 100644 index 000000000..14de03913 --- /dev/null +++ b/src/resources/safeopenglimagehelper.h @@ -0,0 +1,159 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2015 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/>. + */ + +#ifndef RESOURCES_SAFEOPENGLIMAGEHELPER_H +#define RESOURCES_SAFEOPENGLIMAGEHELPER_H + +#include "localconsts.h" +#include "main.h" + +#ifdef USE_OPENGL + +#ifndef GL_TEXTURE_RECTANGLE_ARB +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif + +#include "resources/imagehelper.h" + +#ifdef ANDROID +#include <GLES/gl.h> +#define GL_RGBA8 GL_RGBA8_OES +#else +#ifndef USE_SDL2 +#define GL_GLEXT_PROTOTYPES 1 +#endif +#include <SDL_opengl.h> +#endif + +class Dye; +class Image; + +/** + * Defines a class for loading and storing images. + */ +class SafeOpenGLImageHelper final : public ImageHelper +{ + friend class CompoundSprite; + friend class Graphics; + friend class Image; + + public: + SafeOpenGLImageHelper() : + mFreeTextureIndex(0) + { + } + + A_DELETE_COPY(SafeOpenGLImageHelper) + + ~SafeOpenGLImageHelper(); + + /** + * Loads an image from an SDL_RWops structure and recolors it. + * + * @param rw The SDL_RWops to load the image from. + * @param dye The dye used to recolor the image. + * + * @return <code>NULL</code> if an error occurred, a valid pointer + * otherwise. + */ + Image *load(SDL_RWops *const rw, + Dye const &dye) override final A_WARN_UNUSED; + + /** + * Loads an image from an SDL surface. + */ + Image *load(SDL_Surface *const tmpImage) + override final A_WARN_UNUSED; + + Image *createTextSurface(SDL_Surface *const tmpImage, + const int width, const int height, + const float alpha) + override final A_WARN_UNUSED; + + // OpenGL only public functions + + static int getTextureType() A_WARN_UNUSED + { return mTextureType; } + + static int getInternalTextureType() A_WARN_UNUSED + { return mInternalTextureType; } + + static void setInternalTextureType(const int n) + { mInternalTextureType = n; } + + static void setBlur(const bool n) + { mBlur = n; } + + static int mTextureType; + + static int mInternalTextureType; + + static int getTextureSize() A_WARN_UNUSED + { return mTextureSize; } + + static void initTextureSampler(const GLint id); + + static void setUseTextureSampler(const bool b) + { mUseTextureSampler = b; } + + static void invalidate(const GLuint textureId); + + static void bindTexture(const GLuint texture); + + SDL_Surface *create32BitSurface(int width, + int height) const override final; + + void postInit() override final; + + void copySurfaceToImage(const Image *const image, + const int x, const int y, + SDL_Surface *surface) const override final; + + protected: + /** + * Returns the first power of two equal or bigger than the input. + */ + static int powerOfTwo(const int input) A_WARN_UNUSED; + + static SDL_Surface *convertSurfaceNormalize(SDL_Surface *tmpImage, + int width, int height); + + static SDL_Surface *convertSurface(SDL_Surface *tmpImage, + int width, int height); + + Image *glLoad(SDL_Surface *tmpImage, + int width = 0, int height = 0) A_WARN_UNUSED; + + GLuint getNewTexture(); + + static const size_t texturesSize = 10; + size_t mFreeTextureIndex; + GLuint mTextures[texturesSize]; + + static int mTextureSize; + static bool mBlur; + static bool mUseTextureSampler; +}; + +#endif +#endif // RESOURCES_SAFEOPENGLIMAGEHELPER_H |