FIFE  2008.0
 All Classes Namespaces Functions Variables Enumerations Enumerator Pages
glimage.cpp
1 /***************************************************************************
2  * Copyright (C) 2005-2011 by the FIFE team *
3  * http://www.fifengine.net *
4  * This file is part of FIFE. *
5  * *
6  * FIFE is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU Lesser General Public *
8  * License as published by the Free Software Foundation; either *
9  * version 2.1 of the License, or (at your option) any later version. *
10  * *
11  * This library is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14  * Lesser General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU Lesser General Public *
17  * License along with this library; if not, write to the *
18  * Free Software Foundation, Inc., *
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20  ***************************************************************************/
21 
22 // Standard C++ library includes
23 #include <cassert>
24 #include <iostream>
25 // 3rd party library includes
26 
27 // FIFE includes
28 // These includes are split up in two parts, separated by one empty line
29 // First block: files included from the FIFE root src directory
30 // Second block: files included from the same folder
31 #include "util/structures/rect.h"
32 #include "video/imagemanager.h"
33 #include "video/sdl/sdlimage.h"
34 #include "video/renderbackend.h"
35 #include "video/opengl/renderbackendopengl.h"
36 
37 #include "glimage.h"
38 
39 namespace FIFE {
40  GLImage::GLImage(IResourceLoader* loader):
41  Image(loader),
42  m_compressed(false),
43  m_texId(0) {
44 
45  resetGlimage();
46  }
47 
48  GLImage::GLImage(const std::string& name, IResourceLoader* loader):
49  Image(name, loader),
50  m_compressed(false),
51  m_texId(0) {
52 
53  resetGlimage();
54  }
55 
56  GLImage::GLImage(SDL_Surface* surface):
57  Image(surface),
58  m_compressed(false),
59  m_texId(0) {
60 
61  resetGlimage();
62  }
63 
64  GLImage::GLImage(const std::string& name, SDL_Surface* surface):
65  Image(name, surface),
66  m_compressed(false),
67  m_texId(0) {
68 
69  resetGlimage();
70  }
71 
72  GLImage::GLImage(const uint8_t* data, uint32_t width, uint32_t height):
73  Image(data, width, height),
74  m_compressed(false),
75  m_texId(0) {
76 
77  assert(m_surface);
78  resetGlimage();
79  }
80 
81  GLImage::GLImage(const std::string& name, const uint8_t* data, uint32_t width, uint32_t height):
82  Image(name, data, width, height),
83  m_compressed(false),
84  m_texId(0) {
85 
86  assert(m_surface);
87  resetGlimage();
88  }
89 
90  GLImage::~GLImage() {
91  cleanup();
92  }
93 
95  resetGlimage();
96  }
97 
98  void GLImage::setSurface(SDL_Surface* surface) {
99  reset(surface);
100  resetGlimage();
101  }
102 
103  void GLImage::resetGlimage() {
104  cleanup();
105 
106  m_chunk_size_w = 0;
107  m_chunk_size_h = 0;
108 
109  m_colorkey = RenderBackend::instance()->getColorKey();
110  }
111 
112  void GLImage::cleanup() {
113  if (m_texId) {
114  if(!m_shared) {
115  glDeleteTextures(1, &m_texId);
116  }
117  m_texId = 0;
118  m_compressed = false;
119  }
120 
121  m_tex_coords[0] = m_tex_coords[1] =
122  m_tex_coords[2] = m_tex_coords[3] = 0.0f;
123  }
124 
125  void GLImage::render(const Rect& rect, uint8_t alpha, uint8_t const* rgb) {
126  // completely transparent so dont bother rendering
127  if (0 == alpha) {
128  return;
129  }
130  RenderBackend* rb = RenderBackend::instance();
131  SDL_Surface* target = rb->getRenderTargetSurface();
132  assert(target != m_surface); // can't draw on the source surface
133 
134  // not on the screen. dont render
135  if (rect.right() < 0 || rect.x > static_cast<int32_t>(target->w) ||
136  rect.bottom() < 0 || rect.y > static_cast<int32_t>(target->h)) {
137  return;
138  }
139 
140  if (!m_texId) {
141  generateGLTexture();
142  } else if (m_shared) {
143  validateShared();
144  }
145 
146  rb->addImageToArray(m_texId, rect, m_tex_coords, alpha, rgb);
147  }
148 
149  void GLImage::generateGLTexture() {
150  if (m_shared) {
151  // First make sure we loaded big image to opengl
152  validateShared();
153  return;
154  }
155 
156  const uint32_t width = m_surface->w;
157  const uint32_t height = m_surface->h;
158 
159  // With OpenGL 2.0 or GL_ARB_texture_non_power_of_two we don't really need to care
160  // about non power of 2 textures
161  if(GLEE_ARB_texture_non_power_of_two && RenderBackend::instance()->isNPOTEnabled()) {
162  m_chunk_size_w = width;
163  m_chunk_size_h = height;
164  }
165  else {
166  //calculate the nearest larger power of 2
167  m_chunk_size_w = nextPow2(width);
168  m_chunk_size_h = nextPow2(height);
169  }
170 
171  // used to calculate the fill ratio for given chunk
172  m_tex_coords[0] = m_tex_coords[1] = 0.0f;
173  m_tex_coords[2] = static_cast<float>(m_surface->w%m_chunk_size_w) / static_cast<float>(m_chunk_size_w);
174  m_tex_coords[3] = static_cast<float>(m_surface->h%m_chunk_size_h) / static_cast<float>(m_chunk_size_h);
175 
176  if (m_tex_coords[2] == 0.0f){
177  m_tex_coords[2] = 1.0f;
178  }
179 
180  if (m_tex_coords[3] == 0.0f){
181  m_tex_coords[3] = 1.0f;
182  }
183 
184  uint8_t* data = static_cast<uint8_t*>(m_surface->pixels);
185  int32_t pitch = m_surface->pitch;
186 
187  assert(!m_texId);
188 
189  // get texture id from opengl
190  glGenTextures(1, &m_texId);
191  // set focus on that texture
192  static_cast<RenderBackendOpenGL*>(RenderBackend::instance())->bindTexture(m_texId);
193  // set filters for texture
194  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
195  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
196 
197  GLint internalFormat = GL_RGBA8;
198  if(GLEE_ARB_texture_compression && RenderBackend::instance()->isImageCompressingEnabled()) {
199  internalFormat = GL_COMPRESSED_RGBA;
200  m_compressed = true;
201  } else {
202  m_compressed = false;
203  }
204 
205  SDL_Surface* target = RenderBackend::instance()->getRenderTargetSurface();
206  int32_t bpp_target = target->format->BitsPerPixel;
207  int32_t bpp_source = m_surface->format->BitsPerPixel;
208  // create 16 bit texture, RGBA_4444
209  if (bpp_target == 16 && bpp_source == 32) {
210  uint16_t* oglbuffer = new uint16_t[m_chunk_size_w * m_chunk_size_h];
211  memset(oglbuffer, 0x00, m_chunk_size_w*m_chunk_size_h*sizeof(uint16_t));
212 
213  for (uint32_t y = 0; y < height; ++y) {
214  for (uint32_t x = 0; x < width; ++x) {
215  uint32_t pos = (y * pitch) + (x * 4);
216 
217  uint8_t r = data[pos + 0];
218  uint8_t g = data[pos + 1];
219  uint8_t b = data[pos + 2];
220  uint8_t a = data[pos + 3];
221 
222  if (RenderBackend::instance()->isColorKeyEnabled()) {
223  // only set alpha to zero if colorkey feature is enabled
224  if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
225  a = 0;
226  }
227  }
228 
229  oglbuffer[(y*m_chunk_size_w) + x] = ((r >> 4) << 12) |
230  ((g >> 4) << 8) |
231  ((b >> 4) << 4) |
232  ((a >> 4) << 0);
233  }
234  }
235  // in case of compression we let OpenGL handle it
236  if (!m_compressed) {
237  internalFormat = GL_RGBA4;
238  }
239 
240  // transfer data from sdl buffer
241  glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
242  0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, oglbuffer);
243 
244  delete[] oglbuffer;
245  return;
246  }
247 
248  if(GLEE_ARB_texture_non_power_of_two && RenderBackend::instance()->isNPOTEnabled()) {
249  if(RenderBackend::instance()->isColorKeyEnabled()) {
250  uint8_t* oglbuffer = new uint8_t[width * height * 4];
251  memcpy(oglbuffer, data, width * height * 4 * sizeof(uint8_t));
252 
253  for (uint32_t y = 0; y < height; ++y) {
254  for (uint32_t x = 0; x < width * 4; x += 4) {
255  uint32_t gid = x + y * width;
256 
257  uint8_t r = oglbuffer[gid + 0];
258  uint8_t g = oglbuffer[gid + 1];
259  uint8_t b = oglbuffer[gid + 2];
260  uint8_t a = oglbuffer[gid + 3];
261 
262  // set alpha to zero
263  if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
264  oglbuffer[gid + 3] = 0;
265  }
266  }
267  }
268 
269  // transfer data from sdl buffer
270  glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
271  0, GL_RGBA, GL_UNSIGNED_BYTE, oglbuffer);
272 
273  delete [] oglbuffer;
274  } else {
275  // transfer data directly from sdl buffer
276  glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
277  0, GL_RGBA, GL_UNSIGNED_BYTE, data);
278  }
279  // Non power of 2 textures are not supported, we need to pad the size of texture to nearest power of 2
280  } else {
281  uint32_t* oglbuffer = new uint32_t[m_chunk_size_w * m_chunk_size_h];
282  memset(oglbuffer, 0x00, m_chunk_size_w*m_chunk_size_h*sizeof(uint32_t));
283 
284  for (uint32_t y = 0; y < height; ++y) {
285  for (uint32_t x = 0; x < width; ++x) {
286  uint32_t pos = (y * pitch) + (x * 4);
287 
288  uint8_t a = data[pos + 3];
289  uint8_t b = data[pos + 2];
290  uint8_t g = data[pos + 1];
291  uint8_t r = data[pos + 0];
292 
293  if (RenderBackend::instance()->isColorKeyEnabled()) {
294  // only set alpha to zero if colorkey feature is enabled
295  if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
296  a = 0;
297  }
298  }
299 
300  oglbuffer[(y*m_chunk_size_w) + x] = r | (g << 8) | (b << 16) | (a<<24);
301  }
302  }
303 
304  // transfer data from sdl buffer
305  glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
306  0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLvoid*>(oglbuffer));
307 
308  delete[] oglbuffer;
309  }
310  }
311 
312  void GLImage::generateGLSharedTexture(const GLImage* shared, const Rect& region) {
313  uint32_t width = shared->getWidth();
314  uint32_t height = shared->getHeight();
315 
316  if(!GLEE_ARB_texture_non_power_of_two || !RenderBackend::instance()->isNPOTEnabled()) {
317  width = nextPow2(width);
318  height = nextPow2(height);
319  }
320 
321  m_tex_coords[0] = static_cast<GLfloat>(region.x) / static_cast<GLfloat>(width);
322  m_tex_coords[1] = static_cast<GLfloat>(region.y) / static_cast<GLfloat>(height);
323  m_tex_coords[2] = static_cast<GLfloat>(region.x + region.w) / static_cast<GLfloat>(width);
324  m_tex_coords[3] = static_cast<GLfloat>(region.y + region.h) / static_cast<GLfloat>(height);
325  }
326 
327  void GLImage::useSharedImage(const ImagePtr& shared, const Rect& region) {
328  GLImage* img = static_cast<GLImage*>(shared.get());
329 
330  m_shared_img = img;
331  m_texId = img->m_texId;
332  m_shared = true;
333  m_subimagerect = region;
334  m_atlas_img = shared;
335  m_surface = m_shared_img->m_surface;
336  m_compressed = m_shared_img->m_compressed;
337  m_atlas_name = m_shared_img->getName();
338 
339  if(m_texId) {
340  generateGLSharedTexture(img, region);
341  }
342 
343  setState(IResource::RES_LOADED);
344  }
345 
347  if (m_texId == 0) {
348  generateGLTexture();
349  } else if (m_shared) {
350  validateShared();
351  }
352  }
353 
354  void GLImage::validateShared() {
355  // if image is valid we can return
356  if (m_shared_img->m_texId && m_shared_img->m_texId == m_texId) {
357  return;
358  }
359 
360  if (m_shared_img->getState() == IResource::RES_NOT_LOADED) {
361  m_shared_img->load();
362  m_shared_img->generateGLTexture();
363  }
364 
365  m_texId = m_shared_img->m_texId;
366  m_surface = m_shared_img->m_surface;
367  m_compressed = m_shared_img->m_compressed;
368  generateGLSharedTexture(m_shared_img, m_subimagerect);
369  }
370 
371  void GLImage::copySubimage(uint32_t xoffset, uint32_t yoffset, const ImagePtr& img) {
372  Image::copySubimage(xoffset, yoffset, img);
373 
374  if(m_texId) {
375  static_cast<RenderBackendOpenGL*>(RenderBackend::instance())->bindTexture(m_texId);
376  glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, img->getWidth(), img->getHeight(),
377  GL_RGBA, GL_UNSIGNED_BYTE, img->getSurface()->pixels);
378  }
379  }
380 
381  void GLImage::load() {
382  if (m_shared) {
383  // check atlas image
384  // if it does not exist, it is generated.
385  if (!ImageManager::instance()->exists(m_atlas_name)) {
386  ImagePtr newAtlas = ImageManager::instance()->create(m_atlas_name);
387  GLImage* img = static_cast<GLImage*>(newAtlas.get());
388  m_atlas_img = newAtlas;
389  m_shared_img = img;
390  }
391 
392  // check if texture ids and surfaces are identical
393  if (m_shared_img->m_surface != m_surface || m_texId != m_shared_img->m_texId) {
394  m_texId = m_shared_img->m_texId;
395  m_surface = m_shared_img->m_surface;
396  m_compressed = m_shared_img->m_compressed;
397  if (m_texId) {
398  generateGLSharedTexture(m_shared_img, m_subimagerect);
399  }
400  }
401  m_state = IResource::RES_LOADED;
402  } else {
403  Image::load();
404  }
405  }
406 
407  void GLImage::free() {
408  setSurface(NULL);
409  m_state = IResource::RES_NOT_LOADED;
410  }
411 
412  GLuint GLImage::getTexId() const {
413  return m_texId;
414  }
415 
416  const GLfloat* GLImage::getTexCoords() const {
417  return m_tex_coords;
418  }
419 }
void reset(SDL_Surface *surface)
Definition: image.cpp:110
T * get() const
Definition: sharedptr.h:155
virtual void invalidate()
Definition: glimage.cpp:94
virtual void setSurface(SDL_Surface *surface)
Definition: glimage.cpp:98
unsigned nextPow2(unsigned x)
Definition: fife_math.h:292
virtual void addImageToArray(uint32_t id, const Rect &rec, float const *st, uint8_t alpha, uint8_t const *rgb)=0
virtual void copySubimage(uint32_t xoffset, uint32_t yoffset, const ImagePtr &img)
Definition: glimage.cpp:371
virtual void useSharedImage(const ImagePtr &shared, const Rect &region)
Definition: glimage.cpp:327
T bottom() const
Definition: rect.h:173
virtual void copySubimage(uint32_t xoffset, uint32_t yoffset, const ImagePtr &img)
Definition: image.cpp:310
T right() const
Definition: rect.h:168
virtual void forceLoadInternal()
Definition: glimage.cpp:346
SDL_Surface * getRenderTargetSurface()
virtual void render(const Rect &rect, uint8_t alpha=255, uint8_t const *rgb=0)
Definition: glimage.cpp:125