Mercurial > fife-parpg
diff engine/core/video/opengl/glimage.cpp @ 0:4a0efb7baf70
* Datasets becomes the new trunk and retires after that :-)
author | mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sun, 29 Jun 2008 18:44:17 +0000 |
parents | |
children | 90005975cdbb |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/engine/core/video/opengl/glimage.cpp Sun Jun 29 18:44:17 2008 +0000 @@ -0,0 +1,324 @@ +/*************************************************************************** + * Copyright (C) 2005-2008 by the FIFE team * + * http://www.fifengine.de * + * This file is part of FIFE. * + * * + * FIFE 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 * + * (at your option) 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +// Standard C++ library includes +#include <cassert> + +// 3rd party library includes + +// FIFE includes +// These includes are split up in two parts, separated by one empty line +// First block: files included from the FIFE root src directory +// Second block: files included from the same folder +#include "util/structures/rect.h" +#include "video/sdl/sdlimage.h" +#include "video/renderbackend.h" + +#include "glimage.h" + +namespace FIFE { + GLImage::GLImage(SDL_Surface* surface): + Image(surface) { + m_sdlimage = new SDLImage(surface); + resetGlimage(); + } + + GLImage::GLImage(const uint8_t* data, unsigned int width, unsigned int height): + Image(data, width, height) { + resetGlimage(); + } + + GLImage::~GLImage() { + // remove surface so that deletion happens correctly (by base class destructor) + m_sdlimage->detachSurface(); + delete m_sdlimage; + cleanup(); + } + + void GLImage::resetGlimage() { + m_last_col_fill_ratio = 0; + m_last_row_fill_ratio = 0; + m_textureids = NULL; + m_rows = 0; + m_cols = 0; + m_last_col_width = 0; + m_last_row_height = 0; + m_chunk_size = RenderBackend::instance()->getChunkingSize(); + } + + void GLImage::cleanup() { + for (unsigned int i = 0; i < m_rows*m_cols; ++i) { + glDeleteTextures(1, &m_textureids[i]); + } + delete[] m_textureids; + m_textureids = NULL; + resetGlimage(); + } + + void GLImage::render(const Rect& rect, SDL_Surface* screen, unsigned char alpha) { + if (!m_textureids) { + generateTextureChunks(); + } + + if (rect.right() < 0 || rect.x > static_cast<int>(screen->w) || rect.bottom() < 0 || rect.y > static_cast<int>(screen->h)) { + return; + } + + if (0 == alpha) { + return; + } + + // used to calculate the fill ratio for given chunk + float col_fill_ratio; + float row_fill_ratio; + + // the amount of "zooming" for the image + float scale_x = static_cast<float>(rect.w) / static_cast<float>(m_surface->w); + float scale_y = static_cast<float>(rect.h) / static_cast<float>(m_surface->h); + + // rectangle used for drawing + Rect target; + // zooming causes scaling sometimes to round pixels incorrectly. Instead of + // recalculating it all, store the values from previous round and calculate + // new x & y + Rect prev; + + /// setting transparency for the whole primitive: + glColor4ub( 255, 255, 255, alpha ); + + glEnable(GL_TEXTURE_2D); + for (unsigned int i = 0; i < m_cols; ++i) { + if (i == m_cols-1) { + col_fill_ratio = m_last_col_fill_ratio; + target.w = static_cast<int>(round(scale_y*m_last_col_width*m_last_col_fill_ratio)); + } else { + col_fill_ratio = 1.0; + target.w = static_cast<int>(round(scale_y*m_chunk_size)); + } + if (i > 0) { + target.x = prev.x + prev.w; + } else { + target.x = rect.x; + } + + for (unsigned int j = 0; j < m_rows; ++j) { + if (j == m_rows-1) { + row_fill_ratio = m_last_row_fill_ratio; + target.h = static_cast<int>(round(scale_y*m_last_row_height*m_last_row_fill_ratio)); + } else { + row_fill_ratio = 1.0; + target.h = static_cast<int>(round(scale_y*m_chunk_size)); + } + if (j > 0) { + target.y = prev.y + prev.h; + } else { + target.y = rect.y; + } + prev = target; + + glBindTexture(GL_TEXTURE_2D, m_textureids[j*m_cols + i]); + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); + glVertex2i(target.x, target.y); + + glTexCoord2f(0.0f, row_fill_ratio); + glVertex2i(target.x, target.y + target.h); + + glTexCoord2f(col_fill_ratio, row_fill_ratio); + glVertex2i(target.x + target.w, target.y + target.h); + + glTexCoord2f(col_fill_ratio, 0.0f); + glVertex2i(target.x + target.w, target.y); + glEnd(); + } + } + glDisable(GL_TEXTURE_2D); + } + + void GLImage::generateTextureChunks() { + const unsigned int width = m_surface->w; + const unsigned int height = m_surface->h; + uint8_t* data = static_cast<uint8_t*>(m_surface->pixels); + int pitch = m_surface->pitch; + + m_last_col_width = 1; + m_cols = static_cast<int>(width/m_chunk_size); + if (width%m_chunk_size) { + ++m_cols; + while(m_last_col_width < width%m_chunk_size) { + m_last_col_width <<= 1; + } + } else { + m_last_col_width = m_chunk_size; + } + + m_last_row_height = 1; + m_rows = static_cast<int>(height/m_chunk_size); + if (height%m_chunk_size) { + ++m_rows; + while(m_last_row_height < height%m_chunk_size) { + m_last_row_height <<= 1; + } + } else { + m_last_row_height = m_chunk_size; + } + + m_textureids = new GLuint[m_rows*m_cols]; + memset(m_textureids, 0x00, m_rows*m_cols*sizeof(GLuint)); + + if(width%m_chunk_size) { + m_last_col_fill_ratio = static_cast<float>(width%m_chunk_size) / static_cast<float>(m_last_col_width); + m_last_row_fill_ratio = static_cast<float>(height%m_chunk_size) / static_cast<float>(m_last_row_height); + } + else { // (width%m_chunk_size) / m_last_col_width == 0 == m_chunk_size (mod m_chunk_size) + m_last_col_fill_ratio = 1.0f; + m_last_row_fill_ratio = 1.0f; + } + + unsigned int chunk_width; + unsigned int chunk_height; + unsigned int data_chunk_height; + unsigned int data_chunk_width; + + for (unsigned int i = 0; i < m_cols; ++i) { + for (unsigned int j = 0; j < m_rows; ++j) { + if (i==m_cols-1) { + chunk_width = m_last_col_width; + data_chunk_width = width%m_chunk_size; + if(data_chunk_width == 0) { // 0 == m_chunk_size (mod m_chunk_size) + data_chunk_width = m_chunk_size; + } + } else { + chunk_width = m_chunk_size; + data_chunk_width = m_chunk_size; + } + if (j==m_rows-1) { + chunk_height = m_last_row_height; + data_chunk_height = height%m_chunk_size; + if(data_chunk_height == 0) { // 0 = m_chunk_size (mod m_chunk_size) + data_chunk_height = m_chunk_size; + } + } else { + chunk_height = m_chunk_size; + data_chunk_height = m_chunk_size; + } + + uint32_t* oglbuffer = new uint32_t[chunk_width * chunk_height]; + memset(oglbuffer, 0x00, chunk_width*chunk_height*4); + + for (unsigned int y = 0; y < data_chunk_height; ++y) { + for (unsigned int x = 0; x < data_chunk_width; ++x) { + unsigned int pos = (y + j*m_chunk_size)*pitch + (x + i*m_chunk_size) * 4; + + // FIXME + // The following code might not be endianness correct + + uint8_t r = data[pos + 0]; + uint8_t g = data[pos + 1]; + uint8_t b = data[pos + 2]; + uint8_t a = data[pos + 3]; + + oglbuffer[(y*chunk_width) + x] = (r << 24) | (g << 16) | (b << 8) | a; + } + } + + // get texture id from opengl + glGenTextures(1, &m_textureids[j*m_cols + i]); + // set focus on that texture + glBindTexture(GL_TEXTURE_2D, m_textureids[j*m_cols + i]); + // set filters for texture + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + // transfer data from sdl buffer + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, chunk_width, chunk_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLvoid*>(oglbuffer)); + + delete[] oglbuffer; + } + } + } + + void GLImage::saveImage(const std::string& filename) { + const unsigned int swidth = getWidth(); + const unsigned int sheight = getHeight(); + Uint32 rmask, gmask, bmask, amask; + SDL_Surface *surface = NULL; + uint8_t *pixels; + + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; + #else + rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; + #endif + + surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth, + sheight, 24, + rmask, gmask, bmask, 0); + + if(surface == NULL) { + return; + } + + SDL_LockSurface(surface); + pixels = new uint8_t[swidth * sheight * 3]; + glReadPixels(0, 0, swidth, sheight, GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid*>(pixels)); + + uint8_t *imagepixels = reinterpret_cast<uint8_t*>(surface->pixels); + // Copy the "reversed_image" memory to the "image" memory + for (int y = (sheight - 1); y >= 0; --y) { + uint8_t *rowbegin = pixels + y * swidth * 3; + uint8_t *rowend = rowbegin + swidth * 3; + + std::copy(rowbegin, rowend, imagepixels); + + // Advance a row in the output surface. + imagepixels += surface->pitch; + } + + SDL_UnlockSurface(surface); + saveAsPng(filename, *surface); + SDL_FreeSurface(surface); + delete [] pixels; + + + } + + void GLImage::setClipArea(const Rect& cliparea, bool clear) { + glScissor(cliparea.x, getHeight() - cliparea.y - cliparea.h, cliparea.w, cliparea.h); + if (clear) { + glClear(GL_COLOR_BUFFER_BIT); + } + } + + bool GLImage::putPixel(int x, int y, int r, int g, int b) { + cleanup(); + return m_sdlimage->putPixel(x, y, r, g, b); + } + + void GLImage::drawLine(const Point& p1, const Point& p2, int r, int g, int b) { + cleanup(); + m_sdlimage->drawLine(p1, p2, r, g, b); + } + + void GLImage::drawQuad(const Point& p1, const Point& p2, const Point& p3, const Point& p4, int r, int g, int b) { + cleanup(); + m_sdlimage->drawQuad(p1, p2, p3, p4, r, g, b); + } +}