1 //********************************************************************************************
2 //*
3 //* This file is part of the opengl extensions library. This library is
4 //* distributed with Egoboo.
5 //*
6 //* Egoboo is free software: you can redistribute it and/or modify it
7 //* under the terms of the GNU General Public License as published by
8 //* the Free Software Foundation, either version 3 of the License, or
9 //* (at your option) any later version.
10 //*
11 //* Egoboo is distributed in the hope that it will be useful, but
12 //* WITHOUT ANY WARRANTY; without even the implied warranty of
13 //* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 //* General Public License for more details.
15 //*
16 //* You should have received a copy of the GNU General Public License
17 //* along with Egoboo. If not, see <http://www.gnu.org/licenses/>.
18 //*
19 //********************************************************************************************
20
21 /// @file extensions/ogl_texture.c
22 /// @ingroup _ogl_extensions_
23 /// @brief Implements OpenGL texture loading using SDL_image
24 /// @details Basic loading and managing OpenGL textures.
25 /// Uses SDL_image to load .tif, .png, .bmp, .dib, .xpm, and other formats into OpenGL texures
26
27 #include "ogl_texture.h"
28 #include "ogl_debug.h"
29
30 #include "SDL_GL_extensions.h"
31
32 #include "graphic.h"
33
34 #include "egoboo_setup.h"
35 #include "egoboo_strutil.h"
36
37 #include <SDL_image.h>
38
39 //--------------------------------------------------------------------------------------------
40 //--------------------------------------------------------------------------------------------
41 #define VALID_VALUE 0x32B04E67
42 #define ErrorImage_width 2
43 #define ErrorImage_height 2
44
45 #define VALID_TEXTURE( PTEX ) ( (NULL != (PTEX)) && (VALID_VALUE == (PTEX)->valid) )
46
47 static GLuint ErrorImage_binding = INVALID_GL_ID;
48
49 oglx_texture_parameters_t tex_params = {TX_UNFILTERED, 0};
50
51 static GLboolean ErrorImage_defined = GL_FALSE;
52
53 typedef GLubyte image_row_t[ErrorImage_width][4];
54
55 static GLubyte ErrorImage[ErrorImage_height][ErrorImage_width][4];
56
57 //--------------------------------------------------------------------------------------------
58 //--------------------------------------------------------------------------------------------
ErrorImage_create(void)59 void ErrorImage_create( void )
60 {
61 /// @details BB@> define a default "error texture"
62
63 int i, j;
64
65 if ( INVALID_GL_ID != ErrorImage_binding ) return;
66
67 GL_DEBUG( glGenTextures )( 1, &ErrorImage_binding );
68
69 for ( i = 0; i < ErrorImage_height; i++ )
70 {
71 for ( j = 0; j < ErrorImage_width; j++ )
72 {
73 if ( 0 == (( i&0x1 ) ^( j&0x1 ) ) )
74 {
75 ErrorImage[i][j][0] = ( GLubyte ) 255;
76 ErrorImage[i][j][1] = ( GLubyte ) 0;
77 ErrorImage[i][j][2] = ( GLubyte ) 0;
78 }
79 else
80 {
81 ErrorImage[i][j][0] = ( GLubyte ) 0;
82 ErrorImage[i][j][1] = ( GLubyte ) 255;
83 ErrorImage[i][j][2] = ( GLubyte ) 255;
84 }
85
86 ErrorImage[i][j][3] = ( GLubyte ) 255;
87 }
88 }
89
90 ErrorImage_bind( GL_TEXTURE_2D, ErrorImage_binding );
91
92 ErrorImage_defined = GL_TRUE;
93 }
94
95 //--------------------------------------------------------------------------------------------
ErrorImage_bind(GLenum target,GLuint id)96 void ErrorImage_bind( GLenum target, GLuint id )
97 {
98 // make sure the error texture exists
99 if ( !ErrorImage_defined ) ErrorImage_create();
100
101 GL_DEBUG( glPushClientAttrib )( GL_CLIENT_PIXEL_STORE_BIT ) ;
102 {
103 GL_DEBUG( glPixelStorei )( GL_UNPACK_ALIGNMENT, 1 );
104
105 GL_DEBUG( glBindTexture )( target, id );
106
107 GL_DEBUG( glTexParameteri )( target, GL_TEXTURE_WRAP_S, GL_REPEAT );
108 GL_DEBUG( glTexParameteri )( target, GL_TEXTURE_WRAP_T, GL_REPEAT );
109 GL_DEBUG( glTexParameteri )( target, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
110 GL_DEBUG( glTexParameteri )( target, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
111 if ( target == GL_TEXTURE_1D )
112 {
113 GL_DEBUG( glTexImage1D )( GL_TEXTURE_1D, 0, GL_RGBA, ErrorImage_width, 0, GL_RGBA, GL_UNSIGNED_BYTE, ErrorImage );
114 }
115 else
116 {
117 GL_DEBUG( glTexImage2D )( GL_TEXTURE_2D, 0, GL_RGBA, ErrorImage_width, ErrorImage_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, ErrorImage );
118 }
119 }
120 GL_DEBUG( glPopClientAttrib )();
121 }
122
123 //--------------------------------------------------------------------------------------------
ErrorImage_get_binding()124 GLuint ErrorImage_get_binding()
125 {
126 // make sure the error texture exists
127 if ( !ErrorImage_defined ) ErrorImage_create();
128
129 return ErrorImage_binding;
130 }
131 //--------------------------------------------------------------------------------------------
132 //--------------------------------------------------------------------------------------------
oglx_texture_ctor(oglx_texture_t * ptex)133 oglx_texture_t * oglx_texture_ctor( oglx_texture_t * ptex )
134 {
135 if ( NULL == ptex ) return ptex;
136
137 memset( ptex, 0, sizeof( *ptex ) );
138
139 // only need one base.binding per texture
140 // do not need to ask for a new id, even if we change the texture data
141 GL_DEBUG( glGenTextures )( 1, &( ptex->base.binding ) );
142
143 // set the flag validity flag
144 if ( VALID_BINDING( ptex->base.binding ) && !ERROR_IMAGE_BINDING( ptex->base.binding ) )
145 {
146 ptex->valid = VALID_VALUE;
147 }
148 else
149 {
150 ptex->valid = ( GLuint )( ~VALID_VALUE );
151 }
152
153 // set to 2d texture by default
154 ptex->base.target = GL_TEXTURE_2D;
155
156 // set the image to be clamped in s and t
157 ptex->base.wrap_s = GL_CLAMP;
158 ptex->base.wrap_t = GL_CLAMP;
159
160 return ptex;
161 }
162
163 //--------------------------------------------------------------------------------------------
oglx_texture_dtor(oglx_texture_t * ptex)164 void oglx_texture_dtor( oglx_texture_t * ptex )
165 {
166 if ( !VALID_TEXTURE( ptex ) ) return;
167
168 // set a bad value for ptex->valid
169 ptex->valid = ( GLuint )( ~VALID_VALUE );
170
171 // actually delete the OpenGL texture data
172 if ( VALID_BINDING( ptex->base.binding ) )
173 {
174 GL_DEBUG( glDeleteTextures )( 1, &ptex->base.binding );
175 ptex->base.binding = INVALID_GL_ID;
176 }
177
178 // set the image to be clamped in s and t
179 ptex->base.wrap_s = GL_CLAMP;
180 ptex->base.wrap_t = GL_CLAMP;
181
182 // Reset the other data
183 ptex->imgH = ptex->imgW = ptex->base.width = ptex->base.height = 0;
184 ptex->name[0] = '\0';
185
186 if ( NULL != ptex->surface )
187 {
188 SDL_FreeSurface( ptex->surface );
189 ptex->surface = NULL;
190 }
191 }
192
193 //--------------------------------------------------------------------------------------------
oglx_texture_Convert(oglx_texture_t * ptex,SDL_Surface * image,Uint32 key)194 GLuint oglx_texture_Convert( oglx_texture_t *ptex, SDL_Surface * image, Uint32 key )
195 {
196 /// @detalis BB@> an oglx_texture_t wrapper for the SDL_GL_convert_surface() function
197
198 if ( NULL == ptex ) return INVALID_GL_ID;
199
200 // make sure the old texture has been freed
201 oglx_texture_Release( ptex );
202
203 if ( NULL == image ) return INVALID_GL_ID;
204
205 /* set the color key, if valid */
206 if ( NULL != image->format && NULL != image->format->palette && INVALID_KEY != key )
207 {
208 SDL_SetColorKey( image, SDL_SRCCOLORKEY, key );
209 };
210
211 // Determine the correct power of two greater than or equal to the original image's size
212 ptex->base.binding = SDL_GL_convert_surface( ptex->base.binding, image, ptex->base.wrap_s, ptex->base.wrap_t );
213
214 ptex->base.target = (( 1 == image->h ) && ( image->w > 1 ) ) ? GL_TEXTURE_1D : GL_TEXTURE_2D;
215 ptex->base.height = powerOfTwo( image->h );
216 ptex->base.width = powerOfTwo( image->w );
217
218 // Set up some parameters for the format of the oglx_texture_t
219 ptex->base_valid = btrue;
220 ptex->surface = image;
221 ptex->imgW = image->w;
222 ptex->imgH = image->h;
223 strncpy( ptex->name, "SDL_Surface()", SDL_arraysize( ptex->name ) );
224
225 //// use the following command to grab every possible texture attribute in OpenGL v1.4 for
226 //// this texture. Useful for debugging
227 // ptex->base_valid = bfalse;
228 //oglx_grab_texture_state( tx_target, 0, ptex );
229
230 return ptex->base.binding;
231 }
232
233 //--------------------------------------------------------------------------------------------
IMG_test_alpha(SDL_Surface * psurf)234 SDL_bool IMG_test_alpha( SDL_Surface * psurf )
235 {
236 // test to see whether an image requires alpha blending
237
238 // aliases
239 SDL_PixelFormat * pformat;
240
241 // the color values
242 Uint32 pix_value;
243 Uint8 r, g, b, a;
244
245 // the info necessary to scan the image
246 int bypp, bpp, bit_mask;
247 int w, h;
248 int pitch;
249
250 // the location in the pixel data
251 int ix, iy;
252 const char * row_ptr;
253 const char * char_ptr;
254 Uint32 * ui32_ptr;
255
256 if ( NULL == psurf ) return SDL_FALSE;
257
258 // save this alias
259 pformat = psurf->format;
260
261 // if the surface is tagged as having an alpha value,
262 // it is partially transparent
263 if ( 0xff != pformat->alpha )
264 {
265 return SDL_TRUE;
266 }
267
268 // If it is an image without an alpha channel, then there is no alpha in the image
269 if ( NULL == pformat->palette && 0x00 == pformat->Amask )
270 {
271 return SDL_FALSE;
272 }
273
274 // grab the info for scanning the surface
275 bpp = pformat->BitsPerPixel;
276 bit_mask = pformat->Rmask | pformat->Gmask | pformat->Bmask | pformat->Amask;
277 bypp = pformat->BytesPerPixel;
278 w = psurf->w;
279 h = psurf->h;
280 pitch = psurf->pitch;
281
282 row_ptr = ( const char * )psurf->pixels;
283 for ( iy = 0; iy < h; iy++ )
284 {
285 char_ptr = row_ptr;
286 for ( ix = 0; ix < w; ix++ )
287 {
288 ui32_ptr = ( Uint32 * )char_ptr;
289 pix_value = ( *ui32_ptr ) & bit_mask;
290
291 SDL_GetRGBA( pix_value, pformat, &r, &g, &b, &a );
292
293 if ( 0xFF != a )
294 {
295 return SDL_TRUE;
296 }
297
298 // advance to the next entry
299 char_ptr += bypp;
300 }
301 row_ptr += pitch;
302 }
303
304 return SDL_FALSE;
305 }
306
307 //--------------------------------------------------------------------------------------------
IMG_test_alpha_key(SDL_Surface * psurf,Uint32 key)308 SDL_bool IMG_test_alpha_key( SDL_Surface * psurf, Uint32 key )
309 {
310 // test to see whether an image requires alpha blending
311
312 // aliases
313 SDL_PixelFormat * pformat;
314
315 // the color values
316 Uint32 pix_value;
317 Uint8 r, g, b, a;
318
319 // the info necessary to scan the image
320 int bypp, bpp, bit_mask;
321 int w, h;
322 int pitch;
323
324 // the location in the pixel data
325 int ix, iy;
326 const char * row_ptr;
327 const char * char_ptr;
328 Uint32 * ui32_ptr;
329
330 // flags
331 bool_t check_index = SDL_FALSE;
332
333 if ( NULL == psurf ) return SDL_FALSE;
334
335 // save this alias
336 pformat = psurf->format;
337
338 // if there is no key specified (or the image has an alpha channel), use the basic version
339 if ( INVALID_KEY == key || pformat->Aloss < 8 )
340 {
341 return IMG_test_alpha( psurf );
342 }
343
344 // if it is a colormapped image, there is one more test
345 check_index = SDL_FALSE;
346 if ( NULL != pformat->palette )
347 {
348 check_index = SDL_TRUE;
349 }
350
351 // if the surface is tagged as having an alpha value,
352 // it is partially transparent
353 if ( 0xff != pformat->alpha ) return SDL_TRUE;
354
355 // grab the info for scanning the surface
356 bpp = pformat->BitsPerPixel;
357 bit_mask = pformat->Rmask | pformat->Gmask | pformat->Bmask | pformat->Amask;
358 bypp = pformat->BytesPerPixel;
359 w = psurf->w;
360 h = psurf->h;
361 pitch = psurf->pitch;
362
363 row_ptr = ( const char * )psurf->pixels;
364 for ( iy = 0; iy < h; iy++ )
365 {
366 char_ptr = row_ptr;
367 for ( ix = 0; ix < w; ix++ )
368 {
369 ui32_ptr = ( Uint32 * )char_ptr;
370 pix_value = ( *ui32_ptr ) & bit_mask;
371
372 if ( pix_value == key )
373 {
374 return SDL_TRUE;
375 }
376 else
377 {
378 SDL_GetRGBA( pix_value, pformat, &r, &g, &b, &a );
379 if ( 0xFF != a )
380 {
381 return SDL_TRUE;
382 }
383 }
384
385 // advance to the next entry
386 char_ptr += bypp;
387 }
388 row_ptr += pitch;
389 }
390
391 return SDL_FALSE;
392 }
393
394 //--------------------------------------------------------------------------------------------
oglx_texture_Load(oglx_texture_t * ptex,const char * filename,Uint32 key)395 GLuint oglx_texture_Load( oglx_texture_t *ptex, const char *filename, Uint32 key )
396 {
397 GLuint retval;
398 SDL_Surface * image;
399
400 if ( VALID_TEXTURE( ptex ) )
401 {
402 // release any old texture
403 oglx_texture_Release( ptex );
404 }
405 else
406 {
407 // clean out any uninitialied data
408 ptex = oglx_texture_ctor( ptex );
409 if ( NULL == ptex ) return INVALID_GL_ID;
410 }
411
412 image = IMG_Load( filename );
413 if ( NULL == image ) return INVALID_GL_ID;
414
415 // test to see if the image requires alpha blanding
416 ptex->has_alpha = SDL_FALSE;
417 if ( INVALID_KEY == key )
418 {
419 ptex->has_alpha = IMG_test_alpha( image );
420 }
421 else
422 {
423 ptex->has_alpha = IMG_test_alpha_key( image, key );
424 }
425
426 // upload the SDL_surface to OpenGL
427 retval = oglx_texture_Convert( ptex, image, key );
428
429 if ( !VALID_BINDING( retval ) )
430 {
431 oglx_texture_dtor( ptex );
432 }
433 else
434 {
435 strncpy( ptex->name, filename, SDL_arraysize( ptex->name ) );
436
437 ptex->base.wrap_s = GL_REPEAT;
438 ptex->base.wrap_t = GL_REPEAT;
439 }
440
441 return retval;
442 }
443
444 //--------------------------------------------------------------------------------------------
oglx_texture_GetTextureID(oglx_texture_t * texture)445 GLuint oglx_texture_GetTextureID( oglx_texture_t *texture )
446 {
447 return ( NULL == texture ) ? INVALID_GL_ID : texture->base.binding;
448 }
449
450 //--------------------------------------------------------------------------------------------
oglx_texture_GetImageHeight(oglx_texture_t * texture)451 GLsizei oglx_texture_GetImageHeight( oglx_texture_t *texture )
452 {
453 return ( NULL == texture ) ? 0 : texture->imgH;
454 }
455
456 //--------------------------------------------------------------------------------------------
oglx_texture_GetImageWidth(oglx_texture_t * texture)457 GLsizei oglx_texture_GetImageWidth( oglx_texture_t *texture )
458 {
459 return ( NULL == texture ) ? 0 : texture->imgW;
460 }
461
462 //--------------------------------------------------------------------------------------------
oglx_texture_GetTextureWidth(oglx_texture_t * texture)463 GLsizei oglx_texture_GetTextureWidth( oglx_texture_t *texture )
464 {
465 return ( NULL == texture ) ? 0 : texture->base.width;
466 }
467
468 //--------------------------------------------------------------------------------------------
oglx_texture_GetTextureHeight(oglx_texture_t * texture)469 GLsizei oglx_texture_GetTextureHeight( oglx_texture_t *texture )
470 {
471 return ( NULL == texture ) ? 0 : texture->base.height;
472 }
473
474 //--------------------------------------------------------------------------------------------
oglx_texture_Release(oglx_texture_t * texture)475 void oglx_texture_Release( oglx_texture_t *texture )
476 {
477 if ( !VALID_TEXTURE( texture ) ) return;
478
479 // delete any existing SDL surface
480 if ( NULL != texture->surface )
481 {
482 SDL_FreeSurface( texture->surface );
483 texture->surface = NULL;
484 }
485
486 // try to get rid of any stored texture data for this texture
487 GL_DEBUG( glDeleteTextures )( 1, &( texture->base.binding ) );
488
489 if ( !ErrorImage_defined ) ErrorImage_create();
490
491 // generate a new texture binding
492 GL_DEBUG( glGenTextures )( 1, &( texture->base.binding ) );
493
494 // Bind the error texture instead of the old texture
495 ErrorImage_bind( texture->base.target, texture->base.binding );
496
497 // Reset the other data
498 texture->imgW = texture->base.width = ErrorImage_width;
499 texture->imgH = texture->base.height = ErrorImage_height;
500 strncpy( texture->name, "ErrorImage", sizeof( texture->name ) );
501
502 // set the image to be repeat in s and t
503 texture->base.wrap_s = GL_REPEAT;
504 texture->base.wrap_t = GL_REPEAT;
505
506 //// use the following command to grab every possible texture attribute in OpenGL v1.4 for
507 //// this texture. Useful for debugging
508 //oglx_grab_texture_state( GL_TEXTURE_2D, 0, texture );
509 }
510
511 //--------------------------------------------------------------------------------------------
oglx_texture_Bind(oglx_texture_t * texture)512 void oglx_texture_Bind( oglx_texture_t *texture )
513 {
514 /// @details BB@> a oglx_texture_t wrapper for oglx_bind_to_tex_params() function
515
516 GLenum target;
517 GLuint id;
518 GLint wrap_s, wrap_t;
519
520 // assume the texture is going to be the error texture
521 target = GL_TEXTURE_2D;
522 wrap_s = wrap_t = GL_REPEAT;
523 id = ErrorImage_binding;
524
525 if ( NULL == texture )
526 {
527 // NULL texture means white blob
528 id = INVALID_GL_ID;
529 }
530 else if ( VALID_TEXTURE( texture ) && VALID_BINDING( texture->base.binding ) )
531 {
532 // grab the info from the texture
533 target = texture->base.target;
534 id = texture->base.binding;
535 wrap_s = texture->base.wrap_s;
536 wrap_t = texture->base.wrap_t;
537 }
538
539 // upload the texture
540 id = oglx_bind_to_tex_params( id, target, wrap_s, wrap_t );
541
542 // if the texture binding changed, upload the change.
543 if ( VALID_TEXTURE( texture ) )
544 {
545 texture->base.binding = id;
546 }
547
548 // use the following command to grab every possible texture attribute in OpenGL v1.4 for
549 // this texture. Useful for debugging
550 //if ( NULL != texture && !texture->base_valid )
551 //{
552 // oglx_grab_texture_state( target, 0, texture );
553 //}
554 }
555
556 //--------------------------------------------------------------------------------------------
oglx_texture_Valid(oglx_texture_t * ptex)557 GLboolean oglx_texture_Valid( oglx_texture_t *ptex )
558 {
559 return VALID_TEXTURE( ptex );
560 }
561
562 //--------------------------------------------------------------------------------------------
oglx_grab_texture_state(GLenum target,GLint level,oglx_texture_t * texture)563 void oglx_grab_texture_state( GLenum target, GLint level, oglx_texture_t * texture )
564 {
565 if ( NULL == texture ) return;
566
567 gl_grab_texture_state( target, level, &( texture->base ) );
568
569 texture->base_valid = GL_TRUE;
570 }
571
572 //--------------------------------------------------------------------------------------------
573 //--------------------------------------------------------------------------------------------
oglx_bind_to_tex_params(GLuint binding,GLenum target,GLint wrap_s,GLint wrap_t)574 GLuint oglx_bind_to_tex_params( GLuint binding, GLenum target, GLint wrap_s, GLint wrap_t )
575 {
576 int filt_type, anisotropy;
577
578 GLuint local_binding;
579
580 // make sure the error texture exists
581 if ( !ErrorImage_defined ) ErrorImage_create();
582
583 // handle default parameters
584 if ( wrap_s < 0 ) wrap_s = GL_REPEAT;
585 if ( wrap_t < 0 ) wrap_t = GL_REPEAT;
586
587 local_binding = VALID_BINDING( binding ) ? binding : ErrorImage_binding;
588
589 filt_type = tex_params.texturefilter;
590 anisotropy = tex_params.userAnisotropy;
591
592 if ( !GL_DEBUG( glIsEnabled )( target ) )
593 {
594 GL_DEBUG( glEnable )( target );
595 };
596
597 if ( filt_type >= TX_ANISOTROPIC )
598 {
599 // Anisotropic filtered!
600 oglx_bind( target, local_binding, wrap_s, wrap_t, GL_LINEAR, GL_LINEAR, anisotropy );
601 }
602 else
603 {
604 switch ( filt_type )
605 {
606 // Unfiltered
607 case TX_UNFILTERED:
608 oglx_bind( target, local_binding, wrap_s, wrap_t, GL_NEAREST, GL_LINEAR, 0 );
609 break;
610
611 // Linear filtered
612 case TX_LINEAR:
613 oglx_bind( target, local_binding, wrap_s, wrap_t, GL_LINEAR, GL_LINEAR, 0 );
614 break;
615
616 // Bilinear interpolation
617 case TX_MIPMAP:
618 oglx_bind( target, local_binding, wrap_s, wrap_t, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR, 0 );
619 break;
620
621 // Bilinear interpolation
622 case TX_BILINEAR:
623 oglx_bind( target, local_binding, wrap_s, wrap_t, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, 0 );
624 break;
625
626 // Trilinear filtered (quality 1)
627 case TX_TRILINEAR_1:
628 oglx_bind( target, local_binding, wrap_s, wrap_t, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR, 0 );
629 break;
630
631 // Trilinear filtered (quality 2)
632 case TX_TRILINEAR_2:
633 oglx_bind( target, local_binding, wrap_s, wrap_t, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, 0 );
634 break;
635 };
636 }
637
638 return local_binding;
639 }