FIFE  2008.0
 All Classes Namespaces Functions Variables Enumerations Enumerator Pages
gleimage.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/opengle/renderbackendopengle.h"
35 
36 #include "gleimage.h"
37 
38 namespace FIFE {
39  GLeImage::GLeImage(IResourceLoader* loader):
40  Image(loader),
41  m_compressed(false),
42  m_texId(0) {
43 
44  resetGlimage();
45  }
46 
47  GLeImage::GLeImage(const std::string& name, IResourceLoader* loader):
48  Image(name, loader),
49  m_compressed(false),
50  m_texId(0) {
51 
52  resetGlimage();
53  }
54 
55  GLeImage::GLeImage(SDL_Surface* surface):
56  Image(surface),
57  m_compressed(false),
58  m_texId(0) {
59 
60  resetGlimage();
61  }
62 
63  GLeImage::GLeImage(const std::string& name, SDL_Surface* surface):
64  Image(name, surface),
65  m_compressed(false),
66  m_texId(0) {
67 
68  resetGlimage();
69  }
70 
71  GLeImage::GLeImage(const uint8_t* data, uint32_t width, uint32_t height):
72  Image(data, width, height),
73  m_compressed(false),
74  m_texId(0) {
75 
76  assert(m_surface);
77  resetGlimage();
78  }
79 
80  GLeImage::GLeImage(const std::string& name, const uint8_t* data, uint32_t width, uint32_t height):
81  Image(name, data, width, height),
82  m_compressed(false),
83  m_texId(0) {
84 
85  assert(m_surface);
86  resetGlimage();
87  }
88 
89  GLeImage::~GLeImage() {
90  cleanup();
91  }
92 
94  resetGlimage();
95  }
96 
97  void GLeImage::setSurface(SDL_Surface* surface) {
98  reset(surface);
99  resetGlimage();
100  }
101 
102  void GLeImage::resetGlimage() {
103  cleanup();
104 
105  m_chunk_size_w = 0;
106  m_chunk_size_h = 0;
107 
108  m_colorkey = RenderBackend::instance()->getColorKey();
109  }
110 
111  void GLeImage::cleanup() {
112  if (m_texId) {
113  if (!m_shared) {
114  glDeleteTextures(1, &m_texId);
115  }
116  m_texId = 0;
117  m_compressed = false;
118  }
119 
120  m_tex_coords[0] = m_tex_coords[1] =
121  m_tex_coords[2] = m_tex_coords[3] = 0.0f;
122  }
123 
124  bool GLeImage::renderCheck(const Rect& rect, uint8_t alpha) {
125  // completely transparent so dont bother rendering
126  if (0 == alpha) {
127  return false;
128  }
129  RenderBackend* rb = RenderBackend::instance();
130  SDL_Surface* target = rb->getRenderTargetSurface();
131  assert(target != m_surface); // can't draw on the source surface
132 
133  // not on the screen. dont render
134  if (rect.right() < 0 || rect.x > static_cast<int32_t>(target->w) ||
135  rect.bottom() < 0 || rect.y > static_cast<int32_t>(target->h)) {
136  return false;
137  }
138 
139  if (!m_texId) {
140  generateGLTexture();
141  } else if (m_shared) {
142  validateShared();
143  }
144  return true;
145  }
146 
147  void GLeImage::render(const Rect& rect, uint8_t alpha, uint8_t const* rgb) {
148  if(renderCheck(rect, alpha)) {
149  // rgb only for instances (not available for grid renderer f.e.)
150  RenderBackend::instance()->addImageToArray(m_texId, rect, m_tex_coords, alpha, NULL);
151  }
152  }
153 
154  void GLeImage::renderZ(const Rect& rect, float vertexZ, uint8_t alpha, bool forceNewBatch, uint8_t const* rgb) {
155  if(renderCheck(rect, alpha)) {
156  static_cast<RenderBackendOpenGLe*>(RenderBackend::instance())->addImageToArrayZ(
157  m_texId, rect, vertexZ, m_tex_coords, alpha, forceNewBatch, rgb);
158  }
159  }
160 
161  void GLeImage::generateGLTexture() {
162  if(m_shared) {
163  // First make sure we loaded big image to opengl
164  validateShared();
165  return;
166  }
167 
168  const uint32_t width = m_surface->w;
169  const uint32_t height = m_surface->h;
170 
171  // With OpenGL 2.0 or GL_ARB_texture_non_power_of_two we don't really need to care
172  // about non power of 2 textures
173  if(GLEE_ARB_texture_non_power_of_two && RenderBackend::instance()->isNPOTEnabled()) {
174  m_chunk_size_w = width;
175  m_chunk_size_h = height;
176  }
177  else {
178  //calculate the nearest larger power of 2
179  m_chunk_size_w = nextPow2(width);
180  m_chunk_size_h = nextPow2(height);
181  }
182 
183  // used to calculate the fill ratio for given chunk
184  m_tex_coords[0] = m_tex_coords[1] = 0.0f;
185  m_tex_coords[2] = static_cast<float>(m_surface->w%m_chunk_size_w) / static_cast<float>(m_chunk_size_w);
186  m_tex_coords[3] = static_cast<float>(m_surface->h%m_chunk_size_h) / static_cast<float>(m_chunk_size_h);
187 
188  if (m_tex_coords[2] == 0.0f){
189  m_tex_coords[2] = 1.0f;
190  }
191 
192  if (m_tex_coords[3] == 0.0f){
193  m_tex_coords[3] = 1.0f;
194  }
195 
196  uint8_t* data = static_cast<uint8_t*>(m_surface->pixels);
197  int32_t pitch = m_surface->pitch;
198 
199  assert(!m_texId);
200 
201  // get texture id from opengl
202  glGenTextures(1, &m_texId);
203  // set focus on that texture
204  static_cast<RenderBackendOpenGLe*>(RenderBackend::instance())->bindTexture(m_texId);
205  // set filters for texture
206  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
207  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
208 
209  GLint internalFormat = GL_RGBA8;
210  if(GLEE_ARB_texture_compression && RenderBackend::instance()->isImageCompressingEnabled()) {
211  internalFormat = GL_COMPRESSED_RGBA;
212  m_compressed = true;
213  } else {
214  m_compressed = false;
215  }
216 
217  SDL_Surface* target = RenderBackend::instance()->getRenderTargetSurface();
218  int32_t bpp_target = target->format->BitsPerPixel;
219  int32_t bpp_source = m_surface->format->BitsPerPixel;
220  // create 16 bit texture, RGBA_4444
221  if (bpp_target == 16 && bpp_source == 32) {
222  uint16_t* oglbuffer = new uint16_t[m_chunk_size_w * m_chunk_size_h];
223  memset(oglbuffer, 0x00, m_chunk_size_w*m_chunk_size_h*sizeof(uint16_t));
224 
225  for (uint32_t y = 0; y < height; ++y) {
226  for (uint32_t x = 0; x < width; ++x) {
227  uint32_t pos = (y * pitch) + (x * 4);
228 
229  uint8_t r = data[pos + 0];
230  uint8_t g = data[pos + 1];
231  uint8_t b = data[pos + 2];
232  uint8_t a = data[pos + 3];
233 
234  if (RenderBackend::instance()->isColorKeyEnabled()) {
235  // only set alpha to zero if colorkey feature is enabled
236  if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
237  a = 0;
238  }
239  }
240 
241  oglbuffer[(y*m_chunk_size_w) + x] = ((r >> 4) << 12) |
242  ((g >> 4) << 8) |
243  ((b >> 4) << 4) |
244  ((a >> 4) << 0);
245  }
246  }
247  // in case of compression we let OpenGL handle it
248  if (!m_compressed) {
249  internalFormat = GL_RGBA4;
250  }
251 
252  // transfer data from sdl buffer
253  glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
254  0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, oglbuffer);
255 
256  delete[] oglbuffer;
257  return;
258  }
259 
260  if(GLEE_ARB_texture_non_power_of_two && RenderBackend::instance()->isNPOTEnabled()) {
261  if(RenderBackend::instance()->isColorKeyEnabled()) {
262  uint8_t* oglbuffer = new uint8_t[width * height * 4];
263  memcpy(oglbuffer, data, width * height * 4 * sizeof(uint8_t));
264 
265  for (uint32_t y = 0; y < height; ++y) {
266  for (uint32_t x = 0; x < width * 4; x += 4) {
267  uint32_t gid = x + y * width;
268 
269  uint8_t r = oglbuffer[gid + 0];
270  uint8_t g = oglbuffer[gid + 1];
271  uint8_t b = oglbuffer[gid + 2];
272  uint8_t a = oglbuffer[gid + 3];
273 
274  // set alpha to zero
275  if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
276  oglbuffer[gid + 3] = 0;
277  }
278  }
279  }
280 
281  // transfer data from sdl buffer
282  glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
283  0, GL_RGBA, GL_UNSIGNED_BYTE, oglbuffer);
284 
285  delete [] oglbuffer;
286  } else {
287 
288  // transfer data directly from sdl buffer
289  glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
290  0, GL_RGBA, GL_UNSIGNED_BYTE, data);
291  }
292  // Non power of 2 textures are not supported, we need to pad the size of texture to nearest power of 2
293  } else {
294  uint32_t* oglbuffer = new uint32_t[m_chunk_size_w * m_chunk_size_h];
295  memset(oglbuffer, 0x00, m_chunk_size_w*m_chunk_size_h*sizeof(uint32_t));
296 
297  for (uint32_t y = 0; y < height; ++y) {
298  for (uint32_t x = 0; x < width; ++x) {
299  uint32_t pos = (y * pitch) + (x * 4);
300 
301  uint8_t a = data[pos + 3];
302  uint8_t b = data[pos + 2];
303  uint8_t g = data[pos + 1];
304  uint8_t r = data[pos + 0];
305 
306  if (RenderBackend::instance()->isColorKeyEnabled()) {
307  // only set alpha to zero if colorkey feature is enabled
308  if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) {
309  a = 0;
310  }
311  }
312 
313  oglbuffer[(y*m_chunk_size_w) + x] = r | (g << 8) | (b << 16) | (a<<24);
314  }
315  }
316 
317  // transfer data from sdl buffer
318  glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_chunk_size_w, m_chunk_size_h,
319  0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLvoid*>(oglbuffer));
320 
321  delete[] oglbuffer;
322  }
323  }
324 
325  void GLeImage::generateGLSharedTexture(const GLeImage* shared, const Rect& region) {
326  uint32_t width = shared->getWidth();
327  uint32_t height = shared->getHeight();
328 
329  if(!GLEE_ARB_texture_non_power_of_two || !RenderBackend::instance()->isNPOTEnabled()) {
330  width = nextPow2(width);
331  height = nextPow2(height);
332  }
333 
334  m_tex_coords[0] = static_cast<GLfloat>(region.x) / static_cast<GLfloat>(width);
335  m_tex_coords[1] = static_cast<GLfloat>(region.y) / static_cast<GLfloat>(height);
336  m_tex_coords[2] = static_cast<GLfloat>(region.x + region.w) / static_cast<GLfloat>(width);
337  m_tex_coords[3] = static_cast<GLfloat>(region.y + region.h) / static_cast<GLfloat>(height);
338  }
339 
340  void GLeImage::useSharedImage(const ImagePtr& shared, const Rect& region) {
341  GLeImage* img = static_cast<GLeImage*>(shared.get());
342 
343  m_shared_img = img;
344  m_texId = img->m_texId;
345  m_shared = true;
346  m_subimagerect = region;
347  m_atlas_img = shared;
348  m_surface = m_shared_img->m_surface;
349  m_compressed = m_shared_img->m_compressed;
350  m_atlas_name = m_shared_img->getName();
351 
352  if(m_texId) {
353  generateGLSharedTexture(img, region);
354  }
355 
356  setState(IResource::RES_LOADED);
357  }
358 
360  if (m_texId == 0) {
361  generateGLTexture();
362  } else if (m_shared) {
363  validateShared();
364  }
365  }
366 
367  void GLeImage::validateShared() {
368  // if image is valid we can return
369  if (m_shared_img->m_texId && m_shared_img->m_texId == m_texId) {
370  return;
371  }
372 
373  if (m_shared_img->getState() == IResource::RES_NOT_LOADED) {
374  m_shared_img->load();
375  m_shared_img->generateGLTexture();
376  }
377 
378  m_texId = m_shared_img->m_texId;
379  m_surface = m_shared_img->m_surface;
380  m_compressed = m_shared_img->m_compressed;
381  generateGLSharedTexture(m_shared_img, m_subimagerect);
382  }
383 
384  void GLeImage::copySubimage(uint32_t xoffset, uint32_t yoffset, const ImagePtr& img) {
385  Image::copySubimage(xoffset, yoffset, img);
386 
387  if(m_texId) {
388  static_cast<RenderBackendOpenGLe*>(RenderBackend::instance())->bindTexture(m_texId);
389  glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, img->getWidth(), img->getHeight(),
390  GL_RGBA, GL_UNSIGNED_BYTE, img->getSurface()->pixels);
391  }
392  }
393 
394  void GLeImage::load() {
395  if (m_shared) {
396  // check atlas image
397  // if it does not exist, it is generated.
398  if (!ImageManager::instance()->exists(m_atlas_name)) {
399  ImagePtr newAtlas = ImageManager::instance()->create(m_atlas_name);
400  GLeImage* img = static_cast<GLeImage*>(newAtlas.get());
401  m_atlas_img = newAtlas;
402  m_shared_img = img;
403  }
404 
405  // check if texture ids and surfaces are identical
406  if (m_shared_img->m_surface != m_surface || m_texId != m_shared_img->m_texId) {
407  m_texId = m_shared_img->m_texId;
408  m_surface = m_shared_img->m_surface;
409  m_compressed = m_shared_img->m_compressed;
410  if (m_texId) {
411  generateGLSharedTexture(m_shared_img, m_subimagerect);
412  }
413  }
414  m_state = IResource::RES_LOADED;
415  } else {
416  Image::load();
417  }
418  }
419 
420  void GLeImage::free() {
421  setSurface(NULL);
422  m_state = IResource::RES_NOT_LOADED;
423  }
424 
425  GLuint GLeImage::getTexId() const {
426  return m_texId;
427  }
428 
429  const GLfloat* GLeImage::getTexCoords() const {
430  return m_tex_coords;
431  }
432 }
void reset(SDL_Surface *surface)
Definition: image.cpp:110
T * get() const
Definition: sharedptr.h:155
virtual void setSurface(SDL_Surface *surface)
Definition: gleimage.cpp:97
unsigned nextPow2(unsigned x)
Definition: fife_math.h:292
virtual void useSharedImage(const ImagePtr &shared, const Rect &region)
Definition: gleimage.cpp:340
virtual void copySubimage(uint32_t xoffset, uint32_t yoffset, const ImagePtr &img)
Definition: image.cpp:310
virtual void copySubimage(uint32_t xoffset, uint32_t yoffset, const ImagePtr &img)
Definition: gleimage.cpp:384
virtual void forceLoadInternal()
Definition: gleimage.cpp:359
virtual void render(const Rect &rect, uint8_t alpha=255, uint8_t const *rgb=0)
Definition: gleimage.cpp:147
virtual void invalidate()
Definition: gleimage.cpp:93