Mercurial > fife-parpg
comparison engine/core/video/opengl/glimage.cpp @ 620:853d25234671
* Moved the GLEnable and GLDisable structures from opengl_gui_graphics.cpp to fife_opengl.h as they may prove to be useful elsewhere.
* Fixed the color mask definitions in fife_stdint.h
* Added the nextPow2() function to calculate the nearest (greater) power of 2
* Removed a bunch of re-definitions of RGB masks
* Modified GLImage to only generate one "texture chunk". I hope this makes better use of memory and speeds things up a hair
* Made use of the GLEnable structure when clearing the screen
author | prock@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Thu, 30 Sep 2010 21:24:55 +0000 |
parents | 739d8a43d771 |
children | 92290efadab7 |
comparison
equal
deleted
inserted
replaced
619:f648bfbae5fe | 620:853d25234671 |
---|---|
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * | 19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * |
20 ***************************************************************************/ | 20 ***************************************************************************/ |
21 | 21 |
22 // Standard C++ library includes | 22 // Standard C++ library includes |
23 #include <cassert> | 23 #include <cassert> |
24 | 24 #include <iostream> |
25 // 3rd party library includes | 25 // 3rd party library includes |
26 | 26 |
27 // FIFE includes | 27 // FIFE includes |
28 // These includes are split up in two parts, separated by one empty line | 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 | 29 // First block: files included from the FIFE root src directory |
36 | 36 |
37 namespace FIFE { | 37 namespace FIFE { |
38 GLImage::GLImage(SDL_Surface* surface): | 38 GLImage::GLImage(SDL_Surface* surface): |
39 Image(surface) { | 39 Image(surface) { |
40 m_sdlimage = new SDLImage(surface); | 40 m_sdlimage = new SDLImage(surface); |
41 | |
42 m_textureids = NULL; | |
43 | |
41 resetGlimage(); | 44 resetGlimage(); |
42 } | 45 } |
43 | 46 |
44 GLImage::GLImage(const uint8_t* data, unsigned int width, unsigned int height): | 47 GLImage::GLImage(const uint8_t* data, unsigned int width, unsigned int height): |
45 Image(data, width, height) { | 48 Image(data, width, height) { |
49 | |
50 m_textureids = NULL; | |
51 | |
46 resetGlimage(); | 52 resetGlimage(); |
47 } | 53 } |
48 | 54 |
49 GLImage::~GLImage() { | 55 GLImage::~GLImage() { |
50 // remove surface so that deletion happens correctly (by base class destructor) | 56 // remove surface so that deletion happens correctly (by base class destructor) |
51 m_sdlimage->detachSurface(); | 57 m_sdlimage->detachSurface(); |
52 delete m_sdlimage; | 58 delete m_sdlimage; |
59 | |
53 cleanup(); | 60 cleanup(); |
54 } | 61 } |
55 | 62 |
56 void GLImage::resetGlimage() { | 63 void GLImage::resetGlimage() { |
57 m_last_col_fill_ratio = 0; | 64 cleanup(); |
58 m_last_row_fill_ratio = 0; | 65 |
59 m_textureids = NULL; | 66 m_chunk_size = 0; |
60 m_rows = 0; | |
61 m_cols = 0; | |
62 m_last_col_width = 0; | |
63 m_last_row_height = 0; | |
64 m_chunk_size = RenderBackend::instance()->getChunkingSize(); | |
65 m_colorkey = RenderBackend::instance()->getColorKey(); | 67 m_colorkey = RenderBackend::instance()->getColorKey(); |
66 } | 68 } |
67 | 69 |
68 void GLImage::cleanup() { | 70 void GLImage::cleanup() { |
69 for (unsigned int i = 0; i < m_rows*m_cols; ++i) { | 71 if (m_textureids) { |
70 glDeleteTextures(1, &m_textureids[i]); | 72 glDeleteTextures(1, &m_textureids[0]); |
71 } | 73 |
72 delete[] m_textureids; | 74 delete[] m_textureids; |
73 m_textureids = NULL; | 75 m_textureids = NULL; |
74 resetGlimage(); | 76 } |
77 | |
78 m_col_tex_coord = 0; | |
79 m_row_tex_coord = 0; | |
75 } | 80 } |
76 | 81 |
77 void GLImage::render(const Rect& rect, SDL_Surface* screen, unsigned char alpha) { | 82 void GLImage::render(const Rect& rect, SDL_Surface* screen, unsigned char alpha) { |
78 if (!m_textureids) { | 83 if (!m_textureids) { |
79 generateTextureChunks(); | 84 generateGLTexture(); |
80 } | 85 } |
81 | 86 |
87 //not on the screen. dont render | |
82 if (rect.right() < 0 || rect.x > static_cast<int>(screen->w) || rect.bottom() < 0 || rect.y > static_cast<int>(screen->h)) { | 88 if (rect.right() < 0 || rect.x > static_cast<int>(screen->w) || rect.bottom() < 0 || rect.y > static_cast<int>(screen->h)) { |
83 return; | 89 return; |
84 } | 90 } |
85 | 91 |
92 //completely transparent so dont bother rendering | |
86 if (0 == alpha) { | 93 if (0 == alpha) { |
87 return; | 94 return; |
88 } | 95 } |
89 | |
90 // used to calculate the fill ratio for given chunk | |
91 float col_fill_ratio; | |
92 float row_fill_ratio; | |
93 | 96 |
94 // the amount of "zooming" for the image | 97 // the amount of "zooming" for the image |
95 float scale_x = static_cast<float>(rect.w) / static_cast<float>(m_surface->w); | 98 float scale_x = static_cast<float>(rect.w) / static_cast<float>(m_surface->w); |
96 float scale_y = static_cast<float>(rect.h) / static_cast<float>(m_surface->h); | 99 float scale_y = static_cast<float>(rect.h) / static_cast<float>(m_surface->h); |
97 | 100 |
98 // rectangle used for drawing | 101 // apply the scale to the width and height of the image |
99 Rect target; | 102 uint16_t w = static_cast<int>(round(scale_x*m_surface->w)); |
100 // zooming causes scaling sometimes to round pixels incorrectly. Instead of | 103 uint16_t h = static_cast<int>(round(scale_y*m_surface->h)); |
101 // recalculating it all, store the values from previous round and calculate | |
102 // new x & y | |
103 Rect prev; | |
104 | 104 |
105 /// setting transparency for the whole primitive: | 105 /// setting transparency for the whole primitive: |
106 glColor4ub( 255, 255, 255, alpha ); | 106 glColor4ub( 255, 255, 255, alpha ); |
107 | 107 |
108 glEnable(GL_TEXTURE_2D); | 108 GLEnable flag(GL_TEXTURE_2D); |
109 for (unsigned int i = 0; i < m_cols; ++i) { | 109 glBindTexture(GL_TEXTURE_2D, m_textureids[0]); |
110 if (i == m_cols-1) { | 110 |
111 col_fill_ratio = m_last_col_fill_ratio; | 111 glBegin(GL_QUADS); |
112 target.w = static_cast<int>(round(scale_x*m_last_col_width*m_last_col_fill_ratio)); | 112 glTexCoord2f(0.0f, 0.0f); |
113 } else { | 113 glVertex2i(rect.x, rect.y); |
114 col_fill_ratio = 1.0; | 114 |
115 target.w = static_cast<int>(round(scale_x*m_chunk_size)); | 115 glTexCoord2f(0.0f, m_row_tex_coord); |
116 } | 116 glVertex2i(rect.x, rect.y + h); |
117 if (i > 0) { | 117 |
118 target.x = prev.x + prev.w; | 118 glTexCoord2f(m_col_tex_coord, m_row_tex_coord); |
119 } else { | 119 glVertex2i(rect.x + w, rect.y + h); |
120 target.x = rect.x; | 120 |
121 } | 121 glTexCoord2f(m_col_tex_coord, 0.0f); |
122 | 122 glVertex2i(rect.x + w, rect.y); |
123 for (unsigned int j = 0; j < m_rows; ++j) { | 123 glEnd(); |
124 if (j == m_rows-1) { | 124 |
125 row_fill_ratio = m_last_row_fill_ratio; | 125 } |
126 target.h = static_cast<int>(round(scale_y*m_last_row_height*m_last_row_fill_ratio)); | 126 |
127 } else { | 127 void GLImage::generateGLTexture() { |
128 row_fill_ratio = 1.0; | |
129 target.h = static_cast<int>(round(scale_y*m_chunk_size)); | |
130 } | |
131 if (j > 0) { | |
132 target.y = prev.y + prev.h; | |
133 } else { | |
134 target.y = rect.y; | |
135 } | |
136 prev = target; | |
137 | |
138 glBindTexture(GL_TEXTURE_2D, m_textureids[j*m_cols + i]); | |
139 glBegin(GL_QUADS); | |
140 glTexCoord2f(0.0f, 0.0f); | |
141 glVertex2i(target.x, target.y); | |
142 | |
143 glTexCoord2f(0.0f, row_fill_ratio); | |
144 glVertex2i(target.x, target.y + target.h); | |
145 | |
146 glTexCoord2f(col_fill_ratio, row_fill_ratio); | |
147 glVertex2i(target.x + target.w, target.y + target.h); | |
148 | |
149 glTexCoord2f(col_fill_ratio, 0.0f); | |
150 glVertex2i(target.x + target.w, target.y); | |
151 glEnd(); | |
152 } | |
153 } | |
154 glDisable(GL_TEXTURE_2D); | |
155 } | |
156 | |
157 void GLImage::generateTextureChunks() { | |
158 const unsigned int width = m_surface->w; | 128 const unsigned int width = m_surface->w; |
159 const unsigned int height = m_surface->h; | 129 const unsigned int height = m_surface->h; |
130 | |
131 uint32_t size; | |
132 | |
133 if (width > height){ | |
134 size = width; | |
135 } | |
136 else { | |
137 size = height; | |
138 } | |
139 | |
140 //calculate the nearest larger power of 2 | |
141 m_chunk_size = nextPow2(size); | |
142 | |
143 // used to calculate the fill ratio for given chunk | |
144 m_col_tex_coord = static_cast<float>(m_surface->w%m_chunk_size) / static_cast<float>(m_chunk_size); | |
145 m_row_tex_coord = static_cast<float>(m_surface->h%m_chunk_size) / static_cast<float>(m_chunk_size); | |
146 | |
147 if (m_col_tex_coord == 0.0f){ | |
148 m_col_tex_coord = 1.0f; | |
149 } | |
150 | |
151 if (m_row_tex_coord == 0.0f){ | |
152 m_row_tex_coord = 1.0f; | |
153 } | |
154 | |
160 uint8_t* data = static_cast<uint8_t*>(m_surface->pixels); | 155 uint8_t* data = static_cast<uint8_t*>(m_surface->pixels); |
161 int pitch = m_surface->pitch; | 156 int pitch = m_surface->pitch; |
162 | 157 |
163 m_last_col_width = 1; | 158 |
164 m_cols = static_cast<int>(width/m_chunk_size); | 159 assert(!m_textureids); |
165 if (width%m_chunk_size) { | 160 |
166 ++m_cols; | 161 m_textureids = new GLuint[1]; |
167 while(m_last_col_width < width%m_chunk_size) { | 162 memset(m_textureids, 0x00, 1*sizeof(GLuint)); |
168 m_last_col_width <<= 1; | 163 |
169 } | 164 |
170 } else { | 165 uint32_t* oglbuffer = new uint32_t[m_chunk_size * m_chunk_size]; |
171 m_last_col_width = m_chunk_size; | 166 memset(oglbuffer, 0x00, m_chunk_size*m_chunk_size*sizeof(uint32_t)); |
172 } | 167 |
173 | 168 for (unsigned int y = 0; y < height; ++y) { |
174 m_last_row_height = 1; | 169 for (unsigned int x = 0; x < width; ++x) { |
175 m_rows = static_cast<int>(height/m_chunk_size); | 170 unsigned int pos = (y * pitch) + (x * 4); |
176 if (height%m_chunk_size) { | 171 |
177 ++m_rows; | 172 uint8_t r = data[pos + 3]; |
178 while(m_last_row_height < height%m_chunk_size) { | 173 uint8_t g = data[pos + 2]; |
179 m_last_row_height <<= 1; | 174 uint8_t b = data[pos + 1]; |
180 } | 175 uint8_t a = data[pos + 0]; |
181 } else { | 176 |
182 m_last_row_height = m_chunk_size; | 177 if (RenderBackend::instance()->isColorKeyEnabled()) { |
183 } | 178 // only set alpha to zero if colorkey feature is enabled |
184 | 179 if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) { |
185 m_textureids = new GLuint[m_rows*m_cols]; | 180 a = 0; |
186 memset(m_textureids, 0x00, m_rows*m_cols*sizeof(GLuint)); | |
187 | |
188 if(width%m_chunk_size) { | |
189 m_last_col_fill_ratio = static_cast<float>(width%m_chunk_size) / static_cast<float>(m_last_col_width); | |
190 } else { // (width%m_chunk_size) / m_last_col_width == 0 == m_chunk_size (mod m_chunk_size) | |
191 m_last_col_fill_ratio = 1.0f; | |
192 } | |
193 | |
194 if (height%m_chunk_size) { | |
195 m_last_row_fill_ratio = static_cast<float>(height%m_chunk_size) / static_cast<float>(m_last_row_height); | |
196 } else { | |
197 m_last_row_fill_ratio = 1.0f; | |
198 } | |
199 | |
200 unsigned int chunk_width; | |
201 unsigned int chunk_height; | |
202 unsigned int data_chunk_height; | |
203 unsigned int data_chunk_width; | |
204 | |
205 for (unsigned int i = 0; i < m_cols; ++i) { | |
206 for (unsigned int j = 0; j < m_rows; ++j) { | |
207 if (i==m_cols-1) { | |
208 chunk_width = m_last_col_width; | |
209 data_chunk_width = width%m_chunk_size; | |
210 if(data_chunk_width == 0) { // 0 == m_chunk_size (mod m_chunk_size) | |
211 data_chunk_width = m_chunk_size; | |
212 } | |
213 } else { | |
214 chunk_width = m_chunk_size; | |
215 data_chunk_width = m_chunk_size; | |
216 } | |
217 if (j==m_rows-1) { | |
218 chunk_height = m_last_row_height; | |
219 data_chunk_height = height%m_chunk_size; | |
220 if(data_chunk_height == 0) { // 0 = m_chunk_size (mod m_chunk_size) | |
221 data_chunk_height = m_chunk_size; | |
222 } | |
223 } else { | |
224 chunk_height = m_chunk_size; | |
225 data_chunk_height = m_chunk_size; | |
226 } | |
227 | |
228 uint32_t* oglbuffer = new uint32_t[chunk_width * chunk_height]; | |
229 memset(oglbuffer, 0x00, chunk_width*chunk_height*4); | |
230 | |
231 for (unsigned int y = 0; y < data_chunk_height; ++y) { | |
232 for (unsigned int x = 0; x < data_chunk_width; ++x) { | |
233 unsigned int pos = (y + j*m_chunk_size)*pitch + (x + i*m_chunk_size) * 4; | |
234 | |
235 uint8_t r = data[pos + 3]; | |
236 uint8_t g = data[pos + 2]; | |
237 uint8_t b = data[pos + 1]; | |
238 uint8_t a = data[pos + 0]; | |
239 | |
240 if (RenderBackend::instance()->isColorKeyEnabled()) { | |
241 // only set alpha to zero if colorkey feature is enabled | |
242 if (r == m_colorkey.r && g == m_colorkey.g && b == m_colorkey.b) { | |
243 a = 0; | |
244 } | |
245 } | |
246 | |
247 oglbuffer[(y*chunk_width) + x] = r | (g << 8) | (b << 16) | (a<<24); | |
248 } | 181 } |
249 } | 182 } |
250 | 183 |
251 // get texture id from opengl | 184 oglbuffer[(y*m_chunk_size) + x] = r | (g << 8) | (b << 16) | (a<<24); |
252 glGenTextures(1, &m_textureids[j*m_cols + i]); | |
253 // set focus on that texture | |
254 glBindTexture(GL_TEXTURE_2D, m_textureids[j*m_cols + i]); | |
255 // set filters for texture | |
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
257 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
258 // transfer data from sdl buffer | |
259 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, chunk_width, chunk_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLvoid*>(oglbuffer)); | |
260 | |
261 delete[] oglbuffer; | |
262 } | 185 } |
263 } | 186 } |
187 | |
188 // get texture id from opengl | |
189 glGenTextures(1, &m_textureids[0]); | |
190 // set focus on that texture | |
191 glBindTexture(GL_TEXTURE_2D, m_textureids[0]); | |
192 // set filters for texture | |
193 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
194 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
195 // transfer data from sdl buffer | |
196 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_chunk_size, m_chunk_size, 0, GL_RGBA, GL_UNSIGNED_BYTE, static_cast<GLvoid*>(oglbuffer)); | |
197 | |
198 delete[] oglbuffer; | |
264 } | 199 } |
265 | 200 |
266 void GLImage::saveImage(const std::string& filename) { | 201 void GLImage::saveImage(const std::string& filename) { |
267 const unsigned int swidth = getWidth(); | 202 const unsigned int swidth = getWidth(); |
268 const unsigned int sheight = getHeight(); | 203 const unsigned int sheight = getHeight(); |
269 Uint32 rmask, gmask, bmask, amask; | |
270 SDL_Surface *surface = NULL; | 204 SDL_Surface *surface = NULL; |
271 uint8_t *pixels; | 205 uint8_t *pixels; |
272 | 206 |
273 #if SDL_BYTEORDER == SDL_BIG_ENDIAN | |
274 rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; | |
275 #else | |
276 rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; | |
277 #endif | |
278 | |
279 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth, | 207 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth, |
280 sheight, 24, | 208 sheight, 24, |
281 rmask, gmask, bmask, 0); | 209 RMASK,GMASK,BMASK, NULLMASK); |
282 | 210 |
283 if(surface == NULL) { | 211 if(surface == NULL) { |
284 return; | 212 return; |
285 } | 213 } |
286 | 214 |
302 | 230 |
303 SDL_UnlockSurface(surface); | 231 SDL_UnlockSurface(surface); |
304 saveAsPng(filename, *surface); | 232 saveAsPng(filename, *surface); |
305 SDL_FreeSurface(surface); | 233 SDL_FreeSurface(surface); |
306 delete [] pixels; | 234 delete [] pixels; |
307 | |
308 | |
309 } | 235 } |
310 | 236 |
311 void GLImage::setClipArea(const Rect& cliparea, bool clear) { | 237 void GLImage::setClipArea(const Rect& cliparea, bool clear) { |
312 glScissor(cliparea.x, getHeight() - cliparea.y - cliparea.h, cliparea.w, cliparea.h); | 238 glScissor(cliparea.x, getHeight() - cliparea.y - cliparea.h, cliparea.w, cliparea.h); |
239 | |
313 if (clear) { | 240 if (clear) { |
314 glClear(GL_COLOR_BUFFER_BIT); | 241 glClear(GL_COLOR_BUFFER_BIT); |
315 } | 242 } |
316 } | 243 } |
317 | 244 |