glimage.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2008 by the FIFE team                              *
00003  *   http://www.fifengine.de                                               *
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 
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/sdl/sdlimage.h"
00033 #include "video/renderbackend.h"
00034 
00035 #include "glimage.h"
00036 
00037 namespace FIFE {
00038     GLImage::GLImage(SDL_Surface* surface):
00039         Image(surface) {
00040         m_sdlimage = new SDLImage(surface);
00041         resetGlimage();
00042     }
00043 
00044     GLImage::GLImage(const uint8_t* data, unsigned int width, unsigned int height):
00045         Image(data, width, height) {
00046         resetGlimage();
00047     }
00048 
00049     GLImage::~GLImage() {
00050         // remove surface so that deletion happens correctly (by base class destructor)
00051         m_sdlimage->detachSurface();
00052         delete m_sdlimage;
00053         cleanup();
00054     }
00055 
00056     void GLImage::resetGlimage() {
00057         m_last_col_fill_ratio = 0;
00058         m_last_row_fill_ratio = 0;
00059         m_textureids = NULL;
00060         m_rows = 0;
00061         m_cols = 0;
00062         m_last_col_width = 0;
00063         m_last_row_height = 0;
00064         m_chunk_size = RenderBackend::instance()->getChunkingSize();
00065         m_colorkey = RenderBackend::instance()->getColorKey();
00066     }
00067 
00068     void GLImage::cleanup() {
00069         for (unsigned int i = 0; i < m_rows*m_cols; ++i) {
00070             glDeleteTextures(1, &m_textureids[i]);
00071         }
00072         delete[] m_textureids;
00073         m_textureids = NULL;
00074         resetGlimage();
00075     }
00076 
00077     void GLImage::render(const Rect& rect, SDL_Surface* screen, unsigned char alpha) {
00078         if (!m_textureids) {
00079             generateTextureChunks();
00080         }
00081 
00082         if (rect.right() < 0 || rect.x > static_cast<int>(screen->w) || rect.bottom() < 0 || rect.y > static_cast<int>(screen->h)) {
00083             return;
00084         }
00085 
00086         if (0 == alpha) {
00087             return;
00088         }
00089 
00090         // used to calculate the fill ratio for given chunk
00091         float col_fill_ratio;
00092         float row_fill_ratio;
00093 
00094         // the amount of "zooming" for the image
00095         float scale_x = static_cast<float>(rect.w) / static_cast<float>(m_surface->w);
00096         float scale_y = static_cast<float>(rect.h) / static_cast<float>(m_surface->h);
00097 
00098         // rectangle used for drawing
00099         Rect target;
00100         // zooming causes scaling sometimes to round pixels incorrectly. Instead of
00101         //  recalculating it all, store the values from previous round and calculate
00102         //  new x & y
00103         Rect prev;
00104 
00106         glColor4ub( 255, 255, 255, alpha );
00107 
00108         glEnable(GL_TEXTURE_2D);
00109         for (unsigned int i = 0; i < m_cols; ++i) {
00110             if (i == m_cols-1) {
00111                 col_fill_ratio = m_last_col_fill_ratio;
00112                 target.w = static_cast<int>(round(scale_y*m_last_col_width*m_last_col_fill_ratio));
00113             } else {
00114                 col_fill_ratio = 1.0;
00115                 target.w = static_cast<int>(round(scale_y*m_chunk_size));
00116             }
00117             if (i > 0) {
00118                 target.x = prev.x + prev.w;
00119             } else {
00120                 target.x = rect.x;
00121             }
00122 
00123             for (unsigned int j = 0; j < m_rows; ++j) {
00124                 if (j == m_rows-1) {
00125                     row_fill_ratio = m_last_row_fill_ratio;
00126                     target.h = static_cast<int>(round(scale_y*m_last_row_height*m_last_row_fill_ratio));
00127                 } else {
00128                     row_fill_ratio = 1.0;
00129                     target.h = static_cast<int>(round(scale_y*m_chunk_size));
00130                 }
00131                 if (j > 0) {
00132                     target.y = prev.y + prev.h;
00133                 } else {
00134                     target.y = rect.y;
00135                 }
00136                 prev = target;
00137 
00138                 glBindTexture(GL_TEXTURE_2D, m_textureids[j*m_cols + i]);
00139                 glBegin(GL_QUADS);
00140                     glTexCoord2f(0.0f, 0.0f);
00141                     glVertex2i(target.x, target.y);
00142 
00143                     glTexCoord2f(0.0f, row_fill_ratio);
00144                     glVertex2i(target.x, target.y + target.h);
00145 
00146                     glTexCoord2f(col_fill_ratio, row_fill_ratio);
00147                     glVertex2i(target.x + target.w, target.y + target.h);
00148 
00149                     glTexCoord2f(col_fill_ratio, 0.0f);
00150                     glVertex2i(target.x + target.w, target.y);
00151                 glEnd();
00152             }
00153         }
00154         glDisable(GL_TEXTURE_2D);
00155     }
00156 
00157     void GLImage::generateTextureChunks() {
00158         const unsigned int width = m_surface->w;
00159         const unsigned int height = m_surface->h;
00160         uint8_t* data = static_cast<uint8_t*>(m_surface->pixels);
00161         int pitch = m_surface->pitch;
00162 
00163         m_last_col_width = 1;
00164         m_cols = static_cast<int>(width/m_chunk_size);
00165         if (width%m_chunk_size) {
00166             ++m_cols;
00167             while(m_last_col_width < width%m_chunk_size) {
00168                 m_last_col_width <<= 1;
00169             }
00170         } else {
00171             m_last_col_width = m_chunk_size;
00172         }
00173 
00174         m_last_row_height = 1;
00175         m_rows = static_cast<int>(height/m_chunk_size);
00176         if (height%m_chunk_size) {
00177             ++m_rows;
00178             while(m_last_row_height < height%m_chunk_size) {
00179                 m_last_row_height <<= 1;
00180             }
00181         } else {
00182             m_last_row_height = m_chunk_size;
00183         }
00184 
00185         m_textureids = new GLuint[m_rows*m_cols];
00186         memset(m_textureids, 0x00, m_rows*m_cols*sizeof(GLuint));
00187 
00188         if(width%m_chunk_size) {
00189             m_last_col_fill_ratio = static_cast<float>(width%m_chunk_size) / static_cast<float>(m_last_col_width);
00190         } else {  // (width%m_chunk_size) / m_last_col_width == 0 == m_chunk_size (mod m_chunk_size)
00191             m_last_col_fill_ratio = 1.0f;
00192         }
00193 
00194         if (height%m_chunk_size) {
00195             m_last_row_fill_ratio = static_cast<float>(height%m_chunk_size) / static_cast<float>(m_last_row_height);
00196         } else {
00197             m_last_row_fill_ratio = 1.0f;
00198         }
00199 
00200         unsigned int chunk_width;
00201         unsigned int chunk_height;
00202         unsigned int data_chunk_height;
00203         unsigned int data_chunk_width;
00204 
00205         for (unsigned int i = 0; i < m_cols; ++i) {
00206             for (unsigned int j = 0; j < m_rows; ++j) {
00207                 if (i==m_cols-1) {
00208                     chunk_width = m_last_col_width;
00209                     data_chunk_width = width%m_chunk_size;
00210                     if(data_chunk_width == 0) {  // 0 == m_chunk_size (mod m_chunk_size)
00211                         data_chunk_width = m_chunk_size;
00212                     }
00213                 } else {
00214                     chunk_width = m_chunk_size;
00215                     data_chunk_width = m_chunk_size;
00216                 }
00217                 if (j==m_rows-1) {
00218                     chunk_height = m_last_row_height;
00219                     data_chunk_height = height%m_chunk_size;
00220                     if(data_chunk_height == 0) {  // 0 = m_chunk_size (mod m_chunk_size)
00221                         data_chunk_height = m_chunk_size;
00222                     }
00223                 } else {
00224                     chunk_height = m_chunk_size;
00225                     data_chunk_height = m_chunk_size;
00226                 }
00227 
00228                 uint32_t* oglbuffer = new uint32_t[chunk_width * chunk_height];
00229                 memset(oglbuffer, 0x00, chunk_width*chunk_height*4);
00230 
00231                 for (unsigned int y = 0;  y < data_chunk_height; ++y) {
00232                     for (unsigned int x = 0; x < data_chunk_width; ++x) {
00233                         unsigned int pos = (y + j*m_chunk_size)*pitch + (x + i*m_chunk_size) * 4;
00234 
00235                         uint8_t r = data[pos + 3];
00236                         uint8_t g = data[pos + 2];
00237                         uint8_t b = data[pos + 1];
00238                         uint8_t a = data[pos + 0];
00239 
00240                         if (RenderBackend::instance()->isColorKeyEnabled()) {
00241                             // only set alpha to zero if colorkey feature is enabled
00242                             if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
00243                                 a = 0;
00244                             }
00245                         }
00246 
00247                         oglbuffer[(y*chunk_width) + x] = r | (g << 8) | (b << 16) | (a<<24);
00248                     }
00249                 }
00250 
00251                 // get texture id from opengl
00252                 glGenTextures(1, &m_textureids[j*m_cols + i]);
00253                 // set focus on that texture
00254                 glBindTexture(GL_TEXTURE_2D, m_textureids[j*m_cols + i]);
00255                 // set filters for texture
00256                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00257                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00258                 // transfer data from sdl buffer
00259                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, chunk_width, chunk_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLvoid*>(oglbuffer));
00260 
00261                 delete[] oglbuffer;
00262             }
00263         }
00264     }
00265 
00266     void GLImage::saveImage(const std::string& filename) {
00267         const unsigned int swidth = getWidth();
00268         const unsigned int sheight = getHeight();
00269         Uint32 rmask, gmask, bmask, amask;
00270         SDL_Surface *surface = NULL;
00271         uint8_t *pixels;
00272 
00273         #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00274         rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff;
00275         #else
00276         rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000;
00277         #endif
00278 
00279         surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth,
00280             sheight, 24,
00281             rmask, gmask, bmask, 0);
00282 
00283         if(surface == NULL) {
00284             return;
00285         }
00286 
00287         SDL_LockSurface(surface);
00288         pixels = new uint8_t[swidth * sheight * 3];
00289         glReadPixels(0, 0, swidth, sheight, GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid*>(pixels));
00290 
00291         uint8_t *imagepixels = reinterpret_cast<uint8_t*>(surface->pixels);
00292         // Copy the "reversed_image" memory to the "image" memory
00293         for (int y = (sheight - 1); y >= 0; --y) {
00294             uint8_t *rowbegin = pixels + y * swidth * 3;
00295             uint8_t *rowend = rowbegin + swidth * 3;
00296 
00297             std::copy(rowbegin, rowend, imagepixels);
00298 
00299             // Advance a row in the output surface.
00300             imagepixels += surface->pitch;
00301         }
00302 
00303         SDL_UnlockSurface(surface);
00304         saveAsPng(filename, *surface);
00305         SDL_FreeSurface(surface);
00306         delete [] pixels;
00307 
00308 
00309     }
00310 
00311     void GLImage::setClipArea(const Rect& cliparea, bool clear) {
00312             glScissor(cliparea.x, getHeight() - cliparea.y - cliparea.h, cliparea.w, cliparea.h);
00313         if (clear) {
00314                 glClear(GL_COLOR_BUFFER_BIT);
00315         }
00316     }
00317 
00318     bool GLImage::putPixel(int x, int y, int r, int g, int b) {
00319         cleanup();
00320         return m_sdlimage->putPixel(x, y, r, g, b);
00321     }
00322 
00323     void GLImage::drawLine(const Point& p1, const Point& p2, int r, int g, int b) {
00324         cleanup();
00325         m_sdlimage->drawLine(p1, p2, r, g, b);
00326     }
00327 
00328     void GLImage::drawQuad(const Point& p1, const Point& p2, const Point& p3, const Point& p4,  int r, int g, int b) {
00329         cleanup();
00330         m_sdlimage->drawQuad(p1, p2, p3, p4, r, g, b);
00331     }
00332 
00333     void GLImage::drawVertex(const Point& p, const uint8_t size, int r, int g, int b) {
00334         cleanup();
00335         m_sdlimage->drawVertex(p, size, r, g, b);
00336     }
00337 }