gleimage.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2011 by the FIFE team                              *
00003  *   http://www.fifengine.net                                              *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 #include <cassert>
00024 #include <iostream>
00025 // 3rd party library includes
00026 
00027 // FIFE includes
00028 // These includes are split up in two parts, separated by one empty line
00029 // First block: files included from the FIFE root src directory
00030 // Second block: files included from the same folder
00031 #include "util/structures/rect.h"
00032 #include "video/imagemanager.h"
00033 #include "video/sdl/sdlimage.h"
00034 #include "video/opengle/renderbackendopengle.h"
00035 
00036 #include "gleimage.h"
00037 
00038 namespace FIFE {
00039     GLeImage::GLeImage(IResourceLoader* loader):
00040         Image(loader),
00041         m_compressed(false),
00042         m_texId(0) {
00043 
00044         resetGlimage();
00045     }
00046 
00047     GLeImage::GLeImage(const std::string& name, IResourceLoader* loader):
00048         Image(name, loader),
00049         m_compressed(false),
00050         m_texId(0) {
00051 
00052         resetGlimage();
00053     }
00054 
00055     GLeImage::GLeImage(SDL_Surface* surface):
00056         Image(surface),
00057         m_compressed(false),
00058         m_texId(0) {
00059 
00060         resetGlimage();
00061     }
00062 
00063     GLeImage::GLeImage(const std::string& name, SDL_Surface* surface):
00064         Image(name, surface),
00065         m_compressed(false),
00066         m_texId(0) {
00067 
00068         resetGlimage();
00069     }
00070 
00071     GLeImage::GLeImage(const uint8_t* data, uint32_t width, uint32_t height):
00072         Image(data, width, height),
00073         m_compressed(false),
00074         m_texId(0) {
00075 
00076         assert(m_surface);
00077         resetGlimage();
00078     }
00079 
00080     GLeImage::GLeImage(const std::string& name, const uint8_t* data, uint32_t width, uint32_t height):
00081         Image(name, data, width, height),
00082         m_compressed(false),
00083         m_texId(0) {
00084 
00085         assert(m_surface);
00086         resetGlimage();
00087     }
00088 
00089     GLeImage::~GLeImage() {
00090         cleanup();
00091     }
00092 
00093     void GLeImage::invalidate() {
00094         resetGlimage();
00095     }
00096 
00097     void GLeImage::setSurface(SDL_Surface* surface) {
00098         reset(surface);
00099         resetGlimage();
00100     }
00101 
00102     void GLeImage::resetGlimage() {
00103         cleanup();
00104 
00105         m_chunk_size_w = 0;
00106         m_chunk_size_h = 0;
00107 
00108         m_colorkey = RenderBackend::instance()->getColorKey();
00109     }
00110 
00111     void GLeImage::cleanup() {
00112         if (m_texId) {
00113             if (!m_shared) {
00114                 glDeleteTextures(1, &m_texId);
00115             }
00116             m_texId = 0;
00117             m_compressed = false;
00118         }
00119 
00120         m_tex_coords[0] = m_tex_coords[1] = 
00121             m_tex_coords[2] = m_tex_coords[3] = 0.0f;
00122     }
00123 
00124     bool GLeImage::renderCheck(const Rect& rect, uint8_t alpha) {
00125         // completely transparent so dont bother rendering
00126         if (0 == alpha) {
00127             return false;
00128         }
00129         RenderBackend* rb = RenderBackend::instance();
00130         SDL_Surface* target = rb->getRenderTargetSurface();
00131         assert(target != m_surface); // can't draw on the source surface
00132 
00133         // not on the screen.  dont render
00134         if (rect.right() < 0 || rect.x > static_cast<int32_t>(target->w) || 
00135             rect.bottom() < 0 || rect.y > static_cast<int32_t>(target->h)) {
00136             return false;
00137         }
00138 
00139         if (!m_texId) {
00140             generateGLTexture();
00141         } else if (m_shared) {
00142             validateShared();
00143         }
00144         return true;
00145     }
00146 
00147     void GLeImage::render(const Rect& rect, uint8_t alpha, uint8_t const* rgb) {
00148         if(renderCheck(rect, alpha)) {
00149             // rgb only for instances (not available for grid renderer f.e.)
00150             RenderBackend::instance()->addImageToArray(m_texId, rect, m_tex_coords, alpha, NULL);
00151         }
00152     }
00153 
00154     void GLeImage::renderZ(const Rect& rect, float vertexZ, uint8_t alpha, bool forceNewBatch, uint8_t const* rgb) {
00155         if(renderCheck(rect, alpha)) {
00156             static_cast<RenderBackendOpenGLe*>(RenderBackend::instance())->addImageToArrayZ(
00157                 m_texId, rect, vertexZ, m_tex_coords, alpha, forceNewBatch, rgb);
00158         }
00159     }
00160 
00161     void GLeImage::generateGLTexture() {
00162         if(m_shared) {
00163             // First make sure we loaded big image to opengl
00164             validateShared();
00165             return;
00166         }
00167 
00168         const uint32_t width = m_surface->w;
00169         const uint32_t height = m_surface->h;
00170 
00171         // With OpenGL 2.0 or GL_ARB_texture_non_power_of_two we don't really need to care
00172         // about non power of 2 textures 
00173         if(GLEE_ARB_texture_non_power_of_two && RenderBackend::instance()->isNPOTEnabled()) {
00174             m_chunk_size_w = width;
00175             m_chunk_size_h = height;
00176         }
00177         else {
00178             //calculate the nearest larger power of 2
00179             m_chunk_size_w = nextPow2(width);
00180             m_chunk_size_h = nextPow2(height);
00181         }
00182 
00183         // used to calculate the fill ratio for given chunk
00184         m_tex_coords[0] = m_tex_coords[1] = 0.0f;
00185         m_tex_coords[2] = static_cast<float>(m_surface->w%m_chunk_size_w) / static_cast<float>(m_chunk_size_w);
00186         m_tex_coords[3] = static_cast<float>(m_surface->h%m_chunk_size_h) / static_cast<float>(m_chunk_size_h);
00187 
00188         if (m_tex_coords[2] == 0.0f){
00189             m_tex_coords[2] = 1.0f;
00190         }
00191 
00192         if (m_tex_coords[3] == 0.0f){
00193             m_tex_coords[3] = 1.0f;
00194         }
00195 
00196         uint8_t* data = static_cast<uint8_t*>(m_surface->pixels);
00197         int32_t pitch = m_surface->pitch;
00198 
00199         assert(!m_texId);
00200 
00201         // get texture id from opengl
00202         glGenTextures(1, &m_texId);
00203         // set focus on that texture
00204         static_cast<RenderBackendOpenGLe*>(RenderBackend::instance())->bindTexture(m_texId);
00205         // set filters for texture
00206         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00207         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00208 
00209         GLint internalFormat = GL_RGBA8;
00210         if(GLEE_ARB_texture_compression && RenderBackend::instance()->isImageCompressingEnabled()) {
00211             internalFormat = GL_COMPRESSED_RGBA;
00212             m_compressed = true;
00213         } else {
00214             m_compressed = false;
00215         }
00216 
00217         SDL_Surface* target = RenderBackend::instance()->getRenderTargetSurface();
00218         int32_t bpp_target = target->format->BitsPerPixel;
00219         int32_t bpp_source = m_surface->format->BitsPerPixel;
00220         // create 16 bit texture, RGBA_4444
00221         if (bpp_target == 16 && bpp_source == 32) {
00222             uint16_t* oglbuffer = new uint16_t[m_chunk_size_w * m_chunk_size_h];
00223             memset(oglbuffer, 0x00, m_chunk_size_w*m_chunk_size_h*sizeof(uint16_t));
00224 
00225             for (uint32_t y = 0;  y < height; ++y) {
00226                 for (uint32_t x = 0; x < width; ++x) {
00227                     uint32_t pos = (y * pitch) + (x * 4);
00228 
00229                     uint8_t r = data[pos + 0];
00230                     uint8_t g = data[pos + 1];
00231                     uint8_t b = data[pos + 2];
00232                     uint8_t a = data[pos + 3];
00233 
00234                     if (RenderBackend::instance()->isColorKeyEnabled()) {
00235                         // only set alpha to zero if colorkey feature is enabled
00236                         if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
00237                             a = 0;
00238                         }
00239                     }
00240 
00241                     oglbuffer[(y*m_chunk_size_w) + x] = ((r >> 4) << 12) |
00242                                                         ((g >> 4) << 8) |
00243                                                         ((b >> 4) << 4) |
00244                                                         ((a >> 4) << 0);
00245                 }
00246             }
00247             // in case of compression we let OpenGL handle it
00248             if (!m_compressed) {
00249                 internalFormat = GL_RGBA4;
00250             }
00251 
00252             // transfer data from sdl buffer
00253             glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
00254                 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, oglbuffer);
00255 
00256             delete[] oglbuffer;
00257             return;
00258         }
00259 
00260         if(GLEE_ARB_texture_non_power_of_two && RenderBackend::instance()->isNPOTEnabled()) {
00261             if(RenderBackend::instance()->isColorKeyEnabled()) {
00262                 uint8_t* oglbuffer = new uint8_t[width * height * 4];
00263                 memcpy(oglbuffer, data, width * height * 4 * sizeof(uint8_t));
00264 
00265                 for (uint32_t y = 0;  y < height; ++y) {
00266                     for (uint32_t x = 0; x < width * 4; x += 4) {
00267                         uint32_t gid = x + y * width;
00268 
00269                         uint8_t r = oglbuffer[gid + 0];
00270                         uint8_t g = oglbuffer[gid + 1];
00271                         uint8_t b = oglbuffer[gid + 2];
00272                         uint8_t a = oglbuffer[gid + 3];
00273 
00274                         // set alpha to zero
00275                         if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
00276                             oglbuffer[gid + 3] = 0;
00277                         }
00278                     }
00279                 }
00280 
00281                 // transfer data from sdl buffer
00282                 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
00283                     0, GL_RGBA, GL_UNSIGNED_BYTE, oglbuffer);
00284 
00285                 delete [] oglbuffer;
00286             } else {
00287 
00288                 // transfer data directly from sdl buffer
00289                 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
00290                     0, GL_RGBA, GL_UNSIGNED_BYTE, data);
00291             }
00292         // Non power of 2 textures are not supported, we need to pad the size of texture to nearest power of 2
00293         } else {
00294             uint32_t* oglbuffer = new uint32_t[m_chunk_size_w * m_chunk_size_h];
00295             memset(oglbuffer, 0x00, m_chunk_size_w*m_chunk_size_h*sizeof(uint32_t));
00296 
00297             for (uint32_t y = 0;  y < height; ++y) {
00298                 for (uint32_t x = 0; x < width; ++x) {
00299                     uint32_t pos = (y * pitch) + (x * 4);
00300 
00301                     uint8_t a = data[pos + 3];
00302                     uint8_t b = data[pos + 2];
00303                     uint8_t g = data[pos + 1];
00304                     uint8_t r = data[pos + 0];
00305 
00306                     if (RenderBackend::instance()->isColorKeyEnabled()) {
00307                         // only set alpha to zero if colorkey feature is enabled
00308                         if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
00309                             a = 0;
00310                         }
00311                     }
00312 
00313                     oglbuffer[(y*m_chunk_size_w) + x] = r | (g << 8) | (b << 16) | (a<<24);
00314                 }
00315             }
00316 
00317             // transfer data from sdl buffer
00318             glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
00319                 0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLvoid*>(oglbuffer));
00320 
00321             delete[] oglbuffer;
00322         }
00323     }
00324 
00325     void GLeImage::generateGLSharedTexture(const GLeImage* shared, const Rect& region) {
00326         uint32_t width = shared->getWidth();
00327         uint32_t height = shared->getHeight();
00328 
00329         if(!GLEE_ARB_texture_non_power_of_two || !RenderBackend::instance()->isNPOTEnabled()) {
00330             width = nextPow2(width);
00331             height = nextPow2(height);
00332         }
00333 
00334         m_tex_coords[0] = static_cast<GLfloat>(region.x) / static_cast<GLfloat>(width);
00335         m_tex_coords[1] = static_cast<GLfloat>(region.y) / static_cast<GLfloat>(height);
00336         m_tex_coords[2] = static_cast<GLfloat>(region.x + region.w) / static_cast<GLfloat>(width);
00337         m_tex_coords[3] = static_cast<GLfloat>(region.y + region.h) / static_cast<GLfloat>(height);
00338     }
00339 
00340     void GLeImage::useSharedImage(const ImagePtr& shared, const Rect& region) {
00341         GLeImage* img = static_cast<GLeImage*>(shared.get());
00342 
00343         m_shared_img = img;
00344         m_texId = img->m_texId;
00345         m_shared = true;
00346         m_subimagerect = region;
00347         m_atlas_img = shared;
00348         m_surface = m_shared_img->m_surface;
00349         m_compressed = m_shared_img->m_compressed;
00350         m_atlas_name = m_shared_img->getName();
00351 
00352         if(m_texId) {
00353             generateGLSharedTexture(img, region);
00354         }
00355 
00356         setState(IResource::RES_LOADED);
00357     }
00358 
00359     void GLeImage::forceLoadInternal() {
00360         if (m_texId == 0) {
00361             generateGLTexture();
00362         } else if (m_shared) {
00363             validateShared();
00364         }
00365     }
00366 
00367     void GLeImage::validateShared() {
00368         // if image is valid we can return
00369         if (m_shared_img->m_texId && m_shared_img->m_texId == m_texId) {
00370             return;
00371         }
00372 
00373         if (m_shared_img->getState() == IResource::RES_NOT_LOADED) {
00374             m_shared_img->load();
00375             m_shared_img->generateGLTexture();
00376         }
00377 
00378         m_texId = m_shared_img->m_texId;
00379         m_surface = m_shared_img->m_surface;
00380         m_compressed = m_shared_img->m_compressed;
00381         generateGLSharedTexture(m_shared_img, m_subimagerect);
00382     }
00383 
00384     void GLeImage::copySubimage(uint32_t xoffset, uint32_t yoffset, const ImagePtr& img) {
00385         Image::copySubimage(xoffset, yoffset, img);
00386 
00387         if(m_texId) {
00388             static_cast<RenderBackendOpenGLe*>(RenderBackend::instance())->bindTexture(m_texId);
00389             glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, img->getWidth(), img->getHeight(),
00390                 GL_RGBA, GL_UNSIGNED_BYTE, img->getSurface()->pixels);
00391         }
00392     }
00393 
00394     void GLeImage::load() {
00395         if (m_shared) {
00396             // check atlas image
00397             // if it does not exist, it is generated.
00398             if (!ImageManager::instance()->exists(m_atlas_name)) {
00399                 ImagePtr newAtlas = ImageManager::instance()->create(m_atlas_name);
00400                 GLeImage* img = static_cast<GLeImage*>(newAtlas.get());
00401                 m_atlas_img = newAtlas;
00402                 m_shared_img = img;
00403             }
00404             
00405             // check if texture ids and surfaces are identical
00406             if (m_shared_img->m_surface != m_surface || m_texId != m_shared_img->m_texId) {
00407                 m_texId = m_shared_img->m_texId;
00408                 m_surface = m_shared_img->m_surface;
00409                 m_compressed = m_shared_img->m_compressed;
00410                 if (m_texId) {
00411                     generateGLSharedTexture(m_shared_img, m_subimagerect);
00412                 }
00413             }
00414             m_state = IResource::RES_LOADED;
00415         } else {
00416             Image::load();
00417         }
00418     }
00419 
00420     void GLeImage::free() {
00421         setSurface(NULL);
00422         m_state = IResource::RES_NOT_LOADED;
00423     }
00424 
00425     GLuint GLeImage::getTexId() const {
00426         return m_texId;
00427     }
00428 
00429     const GLfloat* GLeImage::getTexCoords() const {
00430         return m_tex_coords;
00431     }
00432 }