glimage.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/renderbackend.h"
00035 #include "video/opengl/renderbackendopengl.h"
00036 
00037 #include "glimage.h"
00038 
00039 namespace FIFE {
00040     GLImage::GLImage(IResourceLoader* loader):
00041         Image(loader),
00042         m_compressed(false),
00043         m_texId(0) {
00044 
00045         resetGlimage();
00046     }
00047 
00048     GLImage::GLImage(const std::string& name, IResourceLoader* loader):
00049         Image(name, loader),
00050         m_compressed(false),
00051         m_texId(0) {
00052 
00053         resetGlimage();
00054     }
00055 
00056     GLImage::GLImage(SDL_Surface* surface):
00057         Image(surface),
00058         m_compressed(false),
00059         m_texId(0) {
00060 
00061         resetGlimage();
00062     }
00063 
00064     GLImage::GLImage(const std::string& name, SDL_Surface* surface):
00065         Image(name, surface),
00066         m_compressed(false),
00067         m_texId(0) {
00068 
00069         resetGlimage();
00070     }
00071 
00072     GLImage::GLImage(const uint8_t* data, uint32_t width, uint32_t height):
00073         Image(data, width, height),
00074         m_compressed(false),
00075         m_texId(0) {
00076 
00077         assert(m_surface);
00078         resetGlimage();
00079     }
00080 
00081     GLImage::GLImage(const std::string& name, const uint8_t* data, uint32_t width, uint32_t height):
00082         Image(name, data, width, height),
00083         m_compressed(false),
00084         m_texId(0) {
00085 
00086         assert(m_surface);
00087         resetGlimage();
00088     }
00089 
00090     GLImage::~GLImage() {
00091         cleanup();
00092     }
00093 
00094     void GLImage::invalidate() {
00095         resetGlimage();
00096     }
00097 
00098     void GLImage::setSurface(SDL_Surface* surface) {
00099         reset(surface);
00100         resetGlimage();
00101     }
00102 
00103     void GLImage::resetGlimage() {
00104         cleanup();
00105 
00106         m_chunk_size_w = 0;
00107         m_chunk_size_h = 0;
00108 
00109         m_colorkey = RenderBackend::instance()->getColorKey();
00110     }
00111 
00112     void GLImage::cleanup() {
00113         if (m_texId) {
00114             if(!m_shared) {
00115                 glDeleteTextures(1, &m_texId);
00116             }
00117             m_texId = 0;
00118             m_compressed = false;
00119         }
00120 
00121         m_tex_coords[0] = m_tex_coords[1] = 
00122             m_tex_coords[2] = m_tex_coords[3] = 0.0f;
00123     }
00124 
00125     void GLImage::render(const Rect& rect, uint8_t alpha, uint8_t const* rgb) {
00126         // completely transparent so dont bother rendering
00127         if (0 == alpha) {
00128             return;
00129         }
00130         RenderBackend* rb = RenderBackend::instance();
00131         SDL_Surface* target = rb->getRenderTargetSurface();
00132         assert(target != m_surface); // can't draw on the source surface
00133 
00134         // not on the screen.  dont render
00135         if (rect.right() < 0 || rect.x > static_cast<int32_t>(target->w) || 
00136             rect.bottom() < 0 || rect.y > static_cast<int32_t>(target->h)) {
00137             return;
00138         }
00139 
00140         if (!m_texId) {
00141             generateGLTexture();
00142         } else if (m_shared) {
00143             validateShared();
00144         }
00145 
00146         rb->addImageToArray(m_texId, rect, m_tex_coords, alpha, rgb);
00147     }
00148 
00149     void GLImage::generateGLTexture() {
00150         if (m_shared) {
00151             // First make sure we loaded big image to opengl
00152             validateShared();
00153             return;
00154         }
00155 
00156         const uint32_t width = m_surface->w;
00157         const uint32_t height = m_surface->h;
00158 
00159         // With OpenGL 2.0 or GL_ARB_texture_non_power_of_two we don't really need to care
00160         // about non power of 2 textures 
00161         if(GLEE_ARB_texture_non_power_of_two && RenderBackend::instance()->isNPOTEnabled()) {
00162             m_chunk_size_w = width;
00163             m_chunk_size_h = height;
00164         }
00165         else {
00166             //calculate the nearest larger power of 2
00167             m_chunk_size_w = nextPow2(width);
00168             m_chunk_size_h = nextPow2(height);
00169         }
00170 
00171         // used to calculate the fill ratio for given chunk
00172         m_tex_coords[0] = m_tex_coords[1] = 0.0f;
00173         m_tex_coords[2] = static_cast<float>(m_surface->w%m_chunk_size_w) / static_cast<float>(m_chunk_size_w);
00174         m_tex_coords[3] = static_cast<float>(m_surface->h%m_chunk_size_h) / static_cast<float>(m_chunk_size_h);
00175 
00176         if (m_tex_coords[2] == 0.0f){
00177             m_tex_coords[2] = 1.0f;
00178         }
00179 
00180         if (m_tex_coords[3] == 0.0f){
00181             m_tex_coords[3] = 1.0f;
00182         }
00183 
00184         uint8_t* data = static_cast<uint8_t*>(m_surface->pixels);
00185         int32_t pitch = m_surface->pitch;
00186 
00187         assert(!m_texId);
00188 
00189         // get texture id from opengl
00190         glGenTextures(1, &m_texId);
00191         // set focus on that texture
00192         static_cast<RenderBackendOpenGL*>(RenderBackend::instance())->bindTexture(m_texId);
00193         // set filters for texture
00194         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00195         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00196 
00197         GLint internalFormat = GL_RGBA8;
00198         if(GLEE_ARB_texture_compression && RenderBackend::instance()->isImageCompressingEnabled()) {
00199             internalFormat = GL_COMPRESSED_RGBA;
00200             m_compressed = true;
00201         } else {
00202             m_compressed = false;
00203         }
00204 
00205         SDL_Surface* target = RenderBackend::instance()->getRenderTargetSurface();
00206         int32_t bpp_target = target->format->BitsPerPixel;
00207         int32_t bpp_source = m_surface->format->BitsPerPixel;
00208         // create 16 bit texture, RGBA_4444
00209         if (bpp_target == 16 && bpp_source == 32) {
00210             uint16_t* oglbuffer = new uint16_t[m_chunk_size_w * m_chunk_size_h];
00211             memset(oglbuffer, 0x00, m_chunk_size_w*m_chunk_size_h*sizeof(uint16_t));
00212 
00213             for (uint32_t y = 0;  y < height; ++y) {
00214                 for (uint32_t x = 0; x < width; ++x) {
00215                     uint32_t pos = (y * pitch) + (x * 4);
00216 
00217                     uint8_t r = data[pos + 0];
00218                     uint8_t g = data[pos + 1];
00219                     uint8_t b = data[pos + 2];
00220                     uint8_t a = data[pos + 3];
00221 
00222                     if (RenderBackend::instance()->isColorKeyEnabled()) {
00223                         // only set alpha to zero if colorkey feature is enabled
00224                         if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
00225                             a = 0;
00226                         }
00227                     }
00228 
00229                     oglbuffer[(y*m_chunk_size_w) + x] = ((r >> 4) << 12) |
00230                                                         ((g >> 4) << 8) |
00231                                                         ((b >> 4) << 4) |
00232                                                         ((a >> 4) << 0);
00233                 }
00234             }
00235             // in case of compression we let OpenGL handle it
00236             if (!m_compressed) {
00237                 internalFormat = GL_RGBA4;
00238             }
00239 
00240             // transfer data from sdl buffer
00241             glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
00242                 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, oglbuffer);
00243 
00244             delete[] oglbuffer;
00245             return;
00246         }
00247         
00248         if(GLEE_ARB_texture_non_power_of_two && RenderBackend::instance()->isNPOTEnabled()) {
00249             if(RenderBackend::instance()->isColorKeyEnabled()) {
00250                 uint8_t* oglbuffer = new uint8_t[width * height * 4];
00251                 memcpy(oglbuffer, data, width * height * 4 * sizeof(uint8_t));
00252 
00253                 for (uint32_t y = 0;  y < height; ++y) {
00254                     for (uint32_t x = 0; x < width * 4; x += 4) {
00255                         uint32_t gid = x + y * width;
00256 
00257                         uint8_t r = oglbuffer[gid + 0];
00258                         uint8_t g = oglbuffer[gid + 1];
00259                         uint8_t b = oglbuffer[gid + 2];
00260                         uint8_t a = oglbuffer[gid + 3];
00261 
00262                         // set alpha to zero
00263                         if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
00264                             oglbuffer[gid + 3] = 0;
00265                         }
00266                     }
00267                 }
00268 
00269                 // transfer data from sdl buffer
00270                 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
00271                     0, GL_RGBA, GL_UNSIGNED_BYTE, oglbuffer);
00272 
00273                 delete [] oglbuffer;
00274             } else {
00275                 // transfer data directly from sdl buffer
00276                 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
00277                     0, GL_RGBA, GL_UNSIGNED_BYTE, data);
00278             }
00279         // Non power of 2 textures are not supported, we need to pad the size of texture to nearest power of 2
00280         } else {
00281             uint32_t* oglbuffer = new uint32_t[m_chunk_size_w * m_chunk_size_h];
00282             memset(oglbuffer, 0x00, m_chunk_size_w*m_chunk_size_h*sizeof(uint32_t));
00283 
00284             for (uint32_t y = 0;  y < height; ++y) {
00285                 for (uint32_t x = 0; x < width; ++x) {
00286                     uint32_t pos = (y * pitch) + (x * 4);
00287 
00288                     uint8_t a = data[pos + 3];
00289                     uint8_t b = data[pos + 2];
00290                     uint8_t g = data[pos + 1];
00291                     uint8_t r = data[pos + 0];
00292 
00293                     if (RenderBackend::instance()->isColorKeyEnabled()) {
00294                         // only set alpha to zero if colorkey feature is enabled
00295                         if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
00296                             a = 0;
00297                         }
00298                     }
00299 
00300                     oglbuffer[(y*m_chunk_size_w) + x] = r | (g << 8) | (b << 16) | (a<<24);
00301                 }
00302             }
00303 
00304             // transfer data from sdl buffer
00305             glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
00306                 0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLvoid*>(oglbuffer));
00307 
00308             delete[] oglbuffer;
00309         }
00310     }
00311 
00312     void GLImage::generateGLSharedTexture(const GLImage* shared, const Rect& region) {
00313         uint32_t width = shared->getWidth();
00314         uint32_t height = shared->getHeight();
00315 
00316         if(!GLEE_ARB_texture_non_power_of_two || !RenderBackend::instance()->isNPOTEnabled()) {
00317             width = nextPow2(width);
00318             height = nextPow2(height);
00319         }
00320 
00321         m_tex_coords[0] = static_cast<GLfloat>(region.x) / static_cast<GLfloat>(width);
00322         m_tex_coords[1] = static_cast<GLfloat>(region.y) / static_cast<GLfloat>(height);
00323         m_tex_coords[2] = static_cast<GLfloat>(region.x + region.w) / static_cast<GLfloat>(width);
00324         m_tex_coords[3] = static_cast<GLfloat>(region.y + region.h) / static_cast<GLfloat>(height);
00325     }
00326 
00327     void GLImage::useSharedImage(const ImagePtr& shared, const Rect& region) {
00328         GLImage* img = static_cast<GLImage*>(shared.get());
00329 
00330         m_shared_img = img;
00331         m_texId = img->m_texId;
00332         m_shared = true;
00333         m_subimagerect = region;
00334         m_atlas_img = shared;
00335         m_surface = m_shared_img->m_surface;
00336         m_compressed = m_shared_img->m_compressed;
00337         m_atlas_name = m_shared_img->getName();
00338 
00339         if(m_texId) {
00340             generateGLSharedTexture(img, region);
00341         }
00342 
00343         setState(IResource::RES_LOADED);
00344     }
00345 
00346     void GLImage::forceLoadInternal() {
00347         if (m_texId == 0) {
00348             generateGLTexture();
00349         } else if (m_shared) {
00350             validateShared();
00351         }
00352     }
00353 
00354     void GLImage::validateShared() {
00355         // if image is valid we can return
00356         if (m_shared_img->m_texId && m_shared_img->m_texId == m_texId) {
00357             return;
00358         }
00359 
00360         if (m_shared_img->getState() == IResource::RES_NOT_LOADED) {
00361             m_shared_img->load();
00362             m_shared_img->generateGLTexture();
00363         }
00364 
00365         m_texId = m_shared_img->m_texId;
00366         m_surface = m_shared_img->m_surface;
00367         m_compressed = m_shared_img->m_compressed;
00368         generateGLSharedTexture(m_shared_img, m_subimagerect);
00369     }
00370 
00371     void GLImage::copySubimage(uint32_t xoffset, uint32_t yoffset, const ImagePtr& img) {
00372         Image::copySubimage(xoffset, yoffset, img);
00373 
00374         if(m_texId) {
00375             static_cast<RenderBackendOpenGL*>(RenderBackend::instance())->bindTexture(m_texId);
00376             glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, img->getWidth(), img->getHeight(),
00377                 GL_RGBA, GL_UNSIGNED_BYTE, img->getSurface()->pixels);
00378         }
00379     }
00380 
00381     void GLImage::load() {
00382         if (m_shared) {
00383             // check atlas image
00384             // if it does not exist, it is generated.
00385             if (!ImageManager::instance()->exists(m_atlas_name)) {
00386                 ImagePtr newAtlas = ImageManager::instance()->create(m_atlas_name);
00387                 GLImage* img = static_cast<GLImage*>(newAtlas.get());
00388                 m_atlas_img = newAtlas;
00389                 m_shared_img = img;
00390             }
00391             
00392             // check if texture ids and surfaces are identical
00393             if (m_shared_img->m_surface != m_surface || m_texId != m_shared_img->m_texId) {
00394                 m_texId = m_shared_img->m_texId;
00395                 m_surface = m_shared_img->m_surface;
00396                 m_compressed = m_shared_img->m_compressed;
00397                 if (m_texId) {
00398                     generateGLSharedTexture(m_shared_img, m_subimagerect);
00399                 }
00400             }
00401             m_state = IResource::RES_LOADED;
00402         } else {
00403             Image::load();
00404         }
00405     }
00406 
00407     void GLImage::free() {
00408         setSurface(NULL);
00409         m_state = IResource::RES_NOT_LOADED;
00410     }
00411 
00412     GLuint GLImage::getTexId() const {
00413         return m_texId;
00414     }
00415 
00416     const GLfloat* GLImage::getTexCoords() const {
00417         return m_tex_coords;
00418     }
00419 }