naev 0.12.6
opengl_tex.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include "SDL_image.h"
11#include "physfsrwops.h"
12#include <stdio.h>
13#include <stdlib.h>
14
15#include "naev.h"
17
18#include "array.h"
19#include "distance_field.h"
20#include "log.h"
21#include "md5.h"
22#include "nfile.h"
23#include "opengl.h"
24
25/*
26 * graphic list
27 */
31typedef struct glTexList_ {
33 const char *path;
34 int used;
35 /* TODO We currently treat images with different number of sprites as
36 * different images, i.e., they get reloaded and use more memory. However,
37 * it should be possible to do something fancier and share the texture to
38 * avoid this increase of memory (without sharing other parameters). */
39 int sx;
40 int sy;
41 unsigned int flags;
42} glTexList;
43static glTexList *texture_list = NULL;
44static SDL_threadID tex_mainthread;
45static SDL_mutex *gl_lock = NULL;
46static SDL_mutex *tex_lock = NULL;
47
48/*
49 * prototypes
50 */
51/* misc */
52static uint8_t SDL_GetAlpha( SDL_Surface *s, int x, int y );
53static int SDL_IsTrans( SDL_Surface *s, int x, int y );
54static USE_RESULT uint8_t *SDL_MapAlpha( SDL_Surface *s, int tight );
55static size_t gl_transSize( const int w, const int h );
56/* glTexture */
57static USE_RESULT GLuint gl_texParameters( unsigned int flags );
58static USE_RESULT GLuint gl_loadSurface( SDL_Surface *surface,
59 unsigned int flags, int freesur,
60 double *vmax );
61static int gl_loadNewImage( glTexture *tex, const char *path, int sx, int sy,
62 unsigned int flags );
63static int gl_loadNewImageRWops( glTexture *tex, const char *path,
64 SDL_RWops *rw, int sx, int sy,
65 unsigned int flags );
66/* List. */
67static glTexture *gl_texCreate( const char *path, int sx, int sy,
68 unsigned int flags );
69static int gl_texAdd( glTexture *tex, int sx, int sy, unsigned int flags );
70static int tex_cmp( const void *p1, const void *p2 );
71
72static void tex_ctxSet( void )
73{
74 if ( SDL_ThreadID() != tex_mainthread )
75 SDL_GL_MakeCurrent( gl_screen.window, gl_screen.context );
76}
77
78static void tex_ctxUnset( void )
79{
80 if ( SDL_ThreadID() != tex_mainthread )
81 SDL_GL_MakeCurrent( gl_screen.window, NULL );
82}
83
84void gl_contextSet( void )
85{
86 SDL_mutexP( gl_lock );
87 tex_ctxSet();
88}
89
90void gl_contextUnset( void )
91{
92 tex_ctxUnset();
93 SDL_mutexV( gl_lock );
94}
95
96static int tex_cmp( const void *p1, const void *p2 )
97{
98 const glTexList *t1 = (const glTexList *)p1;
99 const glTexList *t2 = (const glTexList *)p2;
100 const unsigned int testflags = OPENGL_TEX_SDF | OPENGL_TEX_VFLIP |
101 OPENGL_TEX_MAPTRANS | OPENGL_TEX_NOTSRGB;
102 int ret = strcmp( t1->path, t2->path );
103 if ( ret != 0 )
104 return ret;
105 ret = t1->sx - t2->sx;
106 if ( ret != 0 )
107 return ret;
108 ret = t1->sy - t2->sy;
109 if ( ret != 0 )
110 return ret;
111 return ( t1->flags & testflags ) - ( t2->flags & testflags );
112}
113
122static uint8_t SDL_GetAlpha( SDL_Surface *s, int x, int y )
123{
124 size_t bytes_per_pixel = s->format->BytesPerPixel;
125 void *p;
126 Uint32 pixel;
127 Uint8 r, g, b, a;
128
129 SDL_LockSurface( s );
130 p = (Uint8 *)s->pixels + y * s->pitch + x * bytes_per_pixel;
131#if SDL_BYTEORDER == SDL_BIG_ENDIAN
132 SDL_memcpy( ( (Uint8 *)&pixel ) + ( sizeof( pixel ) - bytes_per_pixel ), p,
133 bytes_per_pixel );
134#else /* SDL_BYTEORDER == SDL_BIG_ENDIAN */
135 SDL_memcpy( &pixel, p, bytes_per_pixel );
136#endif /* SDL_BYTEORDER == SDL_BIG_ENDIAN */
137 SDL_GetRGBA( pixel, s->format, &r, &g, &b, &a );
138 SDL_UnlockSurface( s );
139 return a;
140}
141
150static int SDL_IsTrans( SDL_Surface *s, int x, int y )
151{
152 uint8_t a = SDL_GetAlpha( s, x, y );
153 /* Test whether pixels colour == colour of transparent pixels for that
154 * surface */
155
156 return a > 127;
157}
158
169static uint8_t *SDL_MapAlpha( SDL_Surface *s, int tight )
170{
171 uint8_t *t;
172 int w = s->w;
173 int h = s->h;
174
175 if ( tight ) {
176 /* alloc memory for just enough bits to hold all the data we need */
177 size_t size = gl_transSize( w, h );
178 t = malloc( size );
179 if ( t == NULL ) {
180 WARN( _( "Out of Memory" ) );
181 return NULL;
182 }
183 memset( t, 0, size ); /* important, must be set to zero */
184
185 /* Check each pixel individually. */
186 for ( int i = 0; i < h; i++ )
187 for ( int j = 0; j < w;
188 j++ ) /* sets each bit to be 1 if not transparent or 0 if is */
189 t[( i * w + j ) / 8] |=
190 ( SDL_IsTrans( s, j, i ) ) ? 0 : ( 1 << ( ( i * w + j ) % 8 ) );
191 } else {
192 t = malloc( w * h );
193 /* Check each pixel individually. */
194 for ( int i = 0; i < h; i++ )
195 for ( int j = 0; j < w;
196 j++ ) /* sets each bit to be 1 if not transparent or 0 if is */
197 t[i * w + j] = SDL_GetAlpha(
198 s, j, i ); /* Flipped with tight version, this is not good :/ */
199 }
200
201 return t;
202}
203
204/*
205 * @brief Gets the size needed for a transparency map.
206 *
207 * @param w Width of the image.
208 * @param h Height of the image.
209 * @return The size in bytes.
210 */
211static size_t gl_transSize( const int w, const int h )
212{
213 /* One bit per pixel, plus remainder. */
214 return w * h / 8 + ( ( w * h % 8 ) ? 1 : 0 );
215}
216
220static GLuint gl_texParameters( unsigned int flags )
221{
222 GLuint texture;
223
224 /* opengl texture binding */
225 glGenTextures( 1, &texture ); /* Creates the texture */
226 glBindTexture( GL_TEXTURE_2D, texture ); /* Loads the texture */
227
228 /* Filtering, LINEAR is better for scaling, nearest looks nicer, LINEAR
229 * also seems to create a bit of artifacts around the edges */
230 if ( ( gl_screen.scale != 1. ) || ( flags & OPENGL_TEX_MIPMAPS ) ) {
231 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
232 if ( flags & OPENGL_TEX_MIPMAPS )
233 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
234 GL_LINEAR_MIPMAP_LINEAR );
235 else
236 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
237 } else {
238 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
239 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
240 }
241
242 /* Always wrap just in case. */
243 if ( flags & OPENGL_TEX_CLAMP_ALPHA ) {
244 const float border[] = { 0., 0., 0., 0. };
245 glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border );
246 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
247 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
248 } else {
249 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
250 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
251 }
252
253 /* Check errors. */
254 gl_checkErr();
255
256 return texture;
257}
258
268int gl_fboCreate( GLuint *fbo, GLuint *tex, GLsizei width, GLsizei height )
269{
270 GLenum status;
271
272 SDL_mutexP( gl_lock );
273 // tex_ctxSet();
274
275 /* Create the render buffer. */
276 glGenTextures( 1, tex );
277 glBindTexture( GL_TEXTURE_2D, *tex );
278 glTexImage2D( GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, width, height, 0, GL_RGBA,
279 GL_UNSIGNED_BYTE, NULL );
280 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
281 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
282 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
283 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
284
285 /* Create the frame buffer. */
286 glGenFramebuffers( 1, fbo );
287 glBindFramebuffer( GL_FRAMEBUFFER, *fbo );
288
289 /* Attach the colour buffer. */
290 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
291 *tex, 0 );
292
293 /* Check status. */
294 status = glCheckFramebufferStatus( GL_FRAMEBUFFER );
295 if ( status != GL_FRAMEBUFFER_COMPLETE )
296 WARN( _( "Error setting up framebuffer!" ) );
297
298 /* Restore state. */
299 glBindTexture( GL_TEXTURE_2D, 0 );
300 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.current_fbo );
301
302 // tex_ctxUnset();
303 SDL_mutexV( gl_lock );
304
305 gl_checkErr();
306
307 return ( status == GL_FRAMEBUFFER_COMPLETE );
308}
309
313int gl_fboAddDepth( GLuint fbo, GLuint *tex, GLsizei width, GLsizei height )
314{
315 GLenum status;
316
317 SDL_mutexP( gl_lock );
318 // tex_ctxSet();
319
320 /* Create the render buffer. */
321 glGenTextures( 1, tex );
322 glBindTexture( GL_TEXTURE_2D, *tex );
323 glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0,
324 GL_DEPTH_COMPONENT, GL_FLOAT, NULL );
325 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
326 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
327 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
328 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
329
330 /* Attach the depth. */
331 glBindFramebuffer( GL_FRAMEBUFFER, fbo );
332 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
333 *tex, 0 );
334
335 /* Check status. */
336 status = glCheckFramebufferStatus( GL_FRAMEBUFFER );
337 if ( status != GL_FRAMEBUFFER_COMPLETE )
338 WARN( _( "Error attaching depth to framebuffer!" ) );
339
340 /* Restore state. */
341 glBindTexture( GL_TEXTURE_2D, 0 );
342 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.current_fbo );
343
344 // tex_ctxUnset();
345 SDL_mutexV( gl_lock );
346
347 gl_checkErr();
348
349 return ( status == GL_FRAMEBUFFER_COMPLETE );
350}
351
352glTexture *gl_loadImageData( float *data, int w, int h, int sx, int sy,
353 const char *name )
354{
355 /* Set up the texture defaults */
356 glTexture *texture = calloc( 1, sizeof( glTexture ) );
357
358 texture->w = (double)w;
359 texture->h = (double)h;
360 texture->sx = (double)sx;
361 texture->sy = (double)sy;
362
363 /* Set up texture. */
364 tex_ctxSet();
365 texture->texture = gl_texParameters( 0 );
366
367 /* Copy over. */
368 glTexImage2D( GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, w, h, 0, GL_RGBA, GL_FLOAT,
369 data );
370 glBindTexture( GL_TEXTURE_2D, 0 );
371
372 /* Check errors. */
373 gl_checkErr();
374 tex_ctxUnset();
375
376 /* Set up values. */
377 texture->sw = texture->w / texture->sx;
378 texture->sh = texture->h / texture->sy;
379 texture->srw = texture->sw / texture->w;
380 texture->srh = texture->sh / texture->h;
381
382 /* Add to list. */
383 if ( name != NULL ) {
384 texture->name = strdup( name );
385 SDL_mutexP( tex_lock );
386 gl_texAdd( texture, sx, sy, OPENGL_TEX_SKIPCACHE );
387 SDL_mutexV( tex_lock );
388 }
389
390 return texture;
391}
392
402static GLuint gl_loadSurface( SDL_Surface *surface, unsigned int flags,
403 int freesur, double *vmax )
404{
405 const SDL_PixelFormatEnum fmt = SDL_PIXELFORMAT_ABGR8888;
406 GLuint texture;
407 SDL_Surface *rgba;
408 int has_alpha = surface->format->Amask;
409
410 gl_contextSet();
411
412 /* Get texture. */
413 texture = gl_texParameters( flags );
414
415 /* Now load the texture data up
416 * It doesn't work with indexed ones, so I guess converting is best bet. */
417 if ( surface->format->format != fmt )
418 rgba = SDL_ConvertSurfaceFormat( surface, fmt, 0 );
419 else
420 rgba = surface;
421
422 SDL_LockSurface( rgba );
423 if ( flags & OPENGL_TEX_SDF ) {
424 const float border[] = { 0., 0., 0., 0. };
425 uint8_t *trans = SDL_MapAlpha( rgba, 0 );
426 GLfloat *dataf = make_distance_mapbf( trans, rgba->w, rgba->h, vmax );
427 free( trans );
428 glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border );
429 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
430 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
431 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
432 glTexImage2D( GL_TEXTURE_2D, 0, GL_RED, rgba->w, rgba->h, 0, GL_RED,
433 GL_FLOAT, dataf );
434 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
435 free( dataf );
436 } else {
437 GLint internalformat;
438 if ( flags & OPENGL_TEX_NOTSRGB )
439 internalformat = has_alpha ? GL_RGBA : GL_RGB;
440 else
441 internalformat = has_alpha ? GL_SRGB_ALPHA : GL_SRGB;
442
443 *vmax = 1.;
444 glPixelStorei( GL_UNPACK_ALIGNMENT,
445 MIN( rgba->pitch & -rgba->pitch, 8 ) );
446 glTexImage2D( GL_TEXTURE_2D, 0, internalformat, rgba->w, rgba->h, 0,
447 GL_RGBA, GL_UNSIGNED_BYTE, rgba->pixels );
448 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
449 }
450 SDL_UnlockSurface( rgba );
451 if ( rgba != surface )
452 SDL_FreeSurface( rgba );
453
454 /* Create mipmaps. */
455 if ( flags & OPENGL_TEX_MIPMAPS ) {
456 /* Do fancy stuff. */
457 if ( GLAD_GL_ARB_texture_filter_anisotropic ) {
458 GLfloat param;
459 glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY, &param );
460 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, param );
461 }
462
463 /* Now generate the mipmaps. */
464 glGenerateMipmap( GL_TEXTURE_2D );
465 }
466
467 /* Unbind the texture. */
468 glBindTexture( GL_TEXTURE_2D, 0 );
469
470 /* cleanup */
471 if ( freesur )
472 SDL_FreeSurface( surface );
473 gl_checkErr();
474
475 gl_contextUnset();
476
477 return texture;
478}
479
483static glTexture *gl_texCreate( const char *path, int sx, int sy,
484 unsigned int flags )
485{
486 glTexture *tex = calloc( 1, sizeof( glTexture ) );
487 tex->name = strdup( path );
488 tex->sx = (double)sx;
489 tex->sy = (double)sy;
490 tex->flags = flags;
491 gl_texAdd( tex, sx, sy, flags );
492 return tex;
493}
494
508USE_RESULT glTexture *gl_texExistsOrCreate( const char *path,
509 unsigned int flags, int sx, int sy,
510 int *created )
511{
512 char buf[STRMAX];
513 const char *realdir;
514
515 /* Null does never exist. */
516 if ( ( path == NULL ) || ( flags & OPENGL_TEX_SKIPCACHE ) ) {
517 *created = 1;
518 return gl_texCreate( path, sx, sy, flags );
519 }
520
521 /* Get the real path name. */
522 realdir = PHYSFS_getRealDir( path );
523 snprintf( buf, sizeof( buf ), "%s/%s", realdir ? realdir : "[NULL]", path );
524 SDL_mutexP( tex_lock );
525
526 /* Check to see if it already exists */
527 if ( texture_list == NULL ) {
528 glTexture *tex = gl_texCreate( buf, sx, sy, flags );
529 *created = 1;
530 SDL_mutexV( tex_lock );
531 return tex;
532 }
533
534 /* Do some fancy binary search. */
535 const glTexList q = { .path = buf, .sx = sx, .sy = sy, .flags = flags };
536 glTexList *t = bsearch( &q, texture_list, array_size( texture_list ),
537 sizeof( glTexList ), tex_cmp );
538 if ( t == NULL ) {
539 glTexture *tex = gl_texCreate( buf, sx, sy, flags );
540 *created = 1;
541 SDL_mutexV( tex_lock );
542 return tex;
543 }
544
545 /* Use new texture. */
546 t->used++;
547 *created = 0;
548 SDL_mutexV( tex_lock );
549 return t->tex;
550}
551
555static int gl_texAdd( glTexture *tex, int sx, int sy, unsigned int flags )
556{
557 glTexList *new;
558
559 /* Get the new list element. */
560 if ( texture_list == NULL )
562
563 /* Create the new node */
564 new = &array_grow( &texture_list );
565 new->used = 1;
566 new->tex = tex;
567 new->sx = sx;
568 new->sy = sy;
569 new->flags = flags;
570 new->path = tex->name;
571
572 /* Sort the list. */
573 qsort( texture_list, array_size( texture_list ), sizeof( glTexList ),
574 tex_cmp );
575 return 0;
576}
577
587glTexture *gl_newImage( const char *path, const unsigned int flags )
588{
589 int created;
590 glTexture *t = gl_texExistsOrCreate( path, flags, 1, 1, &created );
591 if ( !created )
592 return t;
593
594 /* Load the image */
595 gl_loadNewImage( t, path, 1, 1, flags );
596 return t;
597}
598
611glTexture *gl_newImageRWops( const char *path, SDL_RWops *rw,
612 const unsigned int flags )
613{
614 int created;
615 glTexture *t = gl_texExistsOrCreate( path, flags, 1, 1, &created );
616 if ( !created )
617 return t;
618
619 /* Load the image */
620 gl_loadNewImageRWops( t, path, rw, 1, 1, flags );
621 return t;
622}
623
634static int gl_loadNewImage( glTexture *tex, const char *path, int sx, int sy,
635 unsigned int flags )
636{
637 SDL_RWops *rw;
638
639 if ( path == NULL ) {
640 WARN( _( "Trying to load image from NULL path." ) );
641 return -1;
642 }
643
644 /* Load from packfile */
645 rw = PHYSFSRWOPS_openRead( path );
646 if ( rw == NULL ) {
647 WARN( _( "Failed to load surface '%s' from ndata." ), path );
648 return -1;
649 }
650
651 gl_loadNewImageRWops( tex, path, rw, sx, sy, flags );
652 SDL_RWclose( rw );
653 return 0;
654}
655
668static int gl_loadNewImageRWops( glTexture *tex, const char *path,
669 SDL_RWops *rw, int sx, int sy,
670 unsigned int flags )
671{
672 SDL_Surface *surface;
673
674 /* Placeholder for warnings. */
675 if ( path == NULL ) {
676 path = _( "unknown" );
677 flags |= OPENGL_TEX_SKIPCACHE; /* Don't want caching here. */
678 }
679
680 surface = IMG_Load_RW( rw, 0 );
681
682 flags |= OPENGL_TEX_VFLIP;
683 if ( surface == NULL ) {
684 WARN( _( "'%s' could not be opened" ), path );
685 return -1;
686 }
687
688 /* Create a transparency map if necessary. */
689 if ( flags & OPENGL_TEX_MAPTRANS ) {
690 size_t pngsize, filesize, cachesize;
691 md5_state_t md5;
692 char *data;
693 char *cachefile = NULL;
694 uint8_t *trans = NULL;
695 md5_byte_t *md5val = malloc( 16 );
696 md5_init( &md5 );
697 char digest[33];
698
699 /* Appropriate size for the transparency map, see SDL_MapAlpha */
700 cachesize = gl_transSize( surface->w, surface->h );
701
702 /* Go to the start of the file. */
703 pngsize = SDL_RWseek( rw, 0, SEEK_END );
704 SDL_RWseek( rw, 0, SEEK_SET );
705
706 data = malloc( pngsize );
707 if ( data == NULL )
708 WARN( _( "Out of Memory" ) );
709 else {
710 SDL_RWread( rw, data, pngsize, 1 );
711 md5_append( &md5, (md5_byte_t *)data, pngsize );
712 free( data );
713 }
714 md5_finish( &md5, md5val );
715
716 for ( int i = 0; i < 16; i++ )
717 snprintf( &digest[i * 2], 3, "%02x", md5val[i] );
718 free( md5val );
719
720 SDL_asprintf( &cachefile, "%scollisions/%s", nfile_cachePath(), digest );
721
722 /* Attempt to find a cached transparency map. */
723 if ( nfile_fileExists( cachefile ) ) {
724 trans = (uint8_t *)nfile_readFile( &filesize, cachefile );
725
726 /* Consider cached data invalid if the length doesn't match. */
727 if ( trans != NULL && cachesize != (unsigned int)filesize ) {
728 free( trans );
729 trans = NULL;
730 }
731 /* Cached data matches, no need to overwrite. */
732 else {
733 free( cachefile );
734 cachefile = NULL;
735 }
736 }
737
738 if ( trans == NULL ) {
739 SDL_LockSurface( surface );
740 trans = SDL_MapAlpha( surface, 1 );
741 SDL_UnlockSurface( surface );
742
743 if ( cachefile != NULL ) {
744 /* Cache newly-generated transparency map. */
745 char dirpath[PATH_MAX];
746 snprintf( dirpath, sizeof( dirpath ), "%s/%s", nfile_cachePath(),
747 "collisions/" );
748 nfile_dirMakeExist( dirpath );
749 nfile_writeFile( (char *)trans, cachesize, cachefile );
750 free( cachefile );
751 }
752 }
753
754 tex->trans = trans;
755 }
756
757 /* Load image if necessary. */
758 tex->w = (double)surface->w;
759 tex->h = (double)surface->h;
760 tex->sx = (double)sx;
761 tex->sy = (double)sy;
762
763 tex->texture = gl_loadSurface( surface, flags, 0, &tex->vmax );
764
765 tex->sw = tex->w / tex->sx;
766 tex->sh = tex->h / tex->sy;
767 tex->srw = tex->sw / tex->w;
768 tex->srh = tex->sh / tex->h;
769 tex->flags = flags;
770
771 /* Clean up. */
772 SDL_FreeSurface( surface );
773 return 0;
774}
775
785glTexture *gl_newSprite( const char *path, const int sx, const int sy,
786 const unsigned int flags )
787{
788 int created;
789 glTexture *t = gl_texExistsOrCreate( path, flags, sx, sy, &created );
790 if ( !created )
791 return t;
792
793 /* Create new image. */
794 gl_loadNewImage( t, path, sx, sy, flags );
795 return t;
796}
797
808glTexture *gl_newSpriteRWops( const char *path, SDL_RWops *rw, const int sx,
809 const int sy, const unsigned int flags )
810{
811 int created;
812 glTexture *t = gl_texExistsOrCreate( path, flags, sx, sy, &created );
813 if ( !created )
814 return t;
815
816 /* Create new image. */
817 gl_loadNewImageRWops( t, path, rw, sx, sy, flags | OPENGL_TEX_SKIPCACHE );
818
819 /* will possibly overwrite an existing texture properties
820 * so we have to load same texture always the same sprites */
821 t->sx = (double)sx;
822 t->sy = (double)sy;
823 t->sw = t->w / t->sx;
824 t->sh = t->h / t->sy;
825 t->srw = t->sw / t->w;
826 t->srh = t->sh / t->h;
827 return t;
828}
829
835void gl_freeTexture( glTexture *texture )
836{
837 if ( texture == NULL )
838 return;
839
840 SDL_mutexP( gl_lock );
841
842 /* see if we can find it in stack */
843 for ( int i = 0; i < array_size( texture_list ); i++ ) {
844 glTexList *cur = &texture_list[i];
845
846 if ( cur->tex != texture )
847 continue;
848
849 /* found it */
850 cur->used--;
851 if ( cur->used <= 0 ) { /* not used anymore */
852 /* free the texture */
853 glDeleteTextures( 1, &texture->texture );
854 free( texture->trans );
855 free( texture->name );
856 free( texture );
857
858 /* free the list node */
860 }
861 SDL_mutexV( gl_lock );
862 return; /* we already found it so we can exit */
863 }
864
865 /* Not found */
866 if ( texture->name != NULL ) /* Surfaces will have NULL names */
867 WARN( _( "Attempting to free texture '%s' not found in stack!" ),
868 texture->name );
869
870 /* Have to set context. */
871 tex_ctxSet();
872
873 /* Free anyways */
874 glDeleteTextures( 1, &texture->texture );
875 free( texture->trans );
876 free( texture->name );
877 free( texture );
878
879 gl_checkErr();
880
881 tex_ctxUnset();
882 SDL_mutexV( gl_lock );
883}
884
892{
893 /* No segfaults kthxbye. */
894 if ( texture == NULL )
895 return NULL;
896
897 /* check to see if it already exists */
898 SDL_mutexP( gl_lock );
899 for ( int i = 0; i < array_size( texture_list ); i++ ) {
900 glTexList *cur = &texture_list[i];
901 if ( texture == cur->tex ) {
902 cur->used++;
903 SDL_mutexV( gl_lock );
904 return cur->tex;
905 }
906 }
907 SDL_mutexV( gl_lock );
908
909 /* Invalid texture. */
910 WARN( _( "Unable to duplicate texture '%s'." ), texture->name );
911 return NULL;
912}
913
917USE_RESULT glTexture *gl_rawTexture( const char *name, GLuint texid, double w,
918 double h )
919{
920 glTexture *texture;
921
922 /* set up the texture defaults */
923 texture = calloc( 1, sizeof( glTexture ) );
924
925 texture->w = (double)w;
926 texture->h = (double)h;
927 texture->sx = (double)1.;
928 texture->sy = (double)1.;
929
930 texture->texture = texid;
931
932 texture->sw = texture->w / texture->sx;
933 texture->sh = texture->h / texture->sy;
934 texture->srw = texture->sw / texture->w;
935 texture->srh = texture->sh / texture->h;
936 texture->flags = 0;
937
938 if ( name != NULL ) {
939 texture->name = strdup( name );
940 SDL_mutexP( tex_lock );
941 gl_texAdd( texture, 1, 1, OPENGL_TEX_SKIPCACHE );
942 SDL_mutexV( tex_lock );
943 } else
944 texture->name = NULL;
945
946 return texture;
947}
948
957int gl_isTrans( const glTexture *t, const int x, const int y )
958{
959 /* Get the position in the sheet. */
960 int i = y * (int)( t->w ) + x;
961 /* Now we have to pull out the individual bit. */
962 return !( t->trans[i / 8] & ( 1 << ( i % 8 ) ) );
963}
964
977void gl_getSpriteFromDir( int *x, int *y, int sx, int sy, double dir )
978{
979 int s;
980 double shard, rdir;
981
982#ifdef DEBUGGING
983 if ( ( dir > 2. * M_PI ) || ( dir < 0. ) ) {
984 WARN( _( "Angle not between 0 and 2.*M_PI [%f]." ), dir );
985 *x = *y = 0;
986 return;
987 }
988#endif /* DEBUGGING */
989
990 /* what each image represents in angle */
991 shard = 2. * M_PI / ( sy * sx );
992
993 /* real dir is slightly moved downwards */
994 rdir = dir + shard / 2.;
995
996 /* now calculate the sprite we need */
997 s = (int)( rdir / shard );
998
999 /* makes sure the sprite is "in range" */
1000 if ( s > ( sy * sx - 1 ) )
1001 s = s % ( sy * sx );
1002
1003 ( *x ) = s % sx;
1004 ( *y ) = s / sx;
1005}
1006
1013{
1014 gl_lock = SDL_CreateMutex();
1015 tex_lock = SDL_CreateMutex();
1016 tex_mainthread = SDL_ThreadID();
1017 return 0;
1018}
1019
1024{
1025 SDL_DestroyMutex( tex_lock );
1026 SDL_DestroyMutex( gl_lock );
1027
1028 if ( array_size( texture_list ) <= 0 ) {
1030 return;
1031 }
1032
1033 /* Make sure there's no texture leak */
1034#if DEBUGGING
1035 DEBUG( _( "Texture leak detected!" ) );
1036 for ( int i = 0; i < array_size( texture_list ); i++ ) {
1037 const glTexList *cur = &texture_list[i];
1038 DEBUG(
1039 n_( " '%s' opened %d time", " '%s' opened %d times", cur->used ),
1040 cur->tex->name, cur->used );
1041 }
1042#endif /* DEBUGGING */
1043
1045}
1046
1051{
1052 glTexture **t;
1053 int n = array_size( tex );
1054
1055 if ( n <= 0 )
1056 return NULL;
1057
1058 t = array_create_size( glTexture *, n );
1059 for ( int i = 0; i < array_size( tex ); i++ )
1060 array_push_back( &t, gl_dupTexture( tex[i] ) );
1061 return t;
1062}
1063
1068{
1069 if ( tex == NULL )
1070 tex = array_create_size( glTexture *, 1 );
1071 array_push_back( &tex, t );
1072 return tex;
1073}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
Definition array.h:102
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:148
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
float * make_distance_mapbf(unsigned char *img, unsigned int width, unsigned int height, double *vmax)
Perform a Euclidean Distance Transform on the input and normalize to [0,1], with a value of 0....
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
#define PATH_MAX
Definition naev.h:57
int nfile_writeFile(const char *data, size_t len, const char *path)
Tries to write a file.
Definition nfile.c:559
char * nfile_readFile(size_t *filesize, const char *path)
Tries to read a file.
Definition nfile.c:442
int nfile_dirMakeExist(const char *path)
Creates a directory if it doesn't exist.
Definition nfile.c:277
const char * nfile_cachePath(void)
Gets Naev's cache path (for cached data such as generated textures)
Definition nfile.c:162
int nfile_fileExists(const char *path)
Checks to see if a file exists.
Definition nfile.c:329
glInfo gl_screen
Definition opengl.c:47
static int gl_loadNewImageRWops(glTexture *tex, const char *path, SDL_RWops *rw, int sx, int sy, unsigned int flags)
The heavy loading image backend image function. It loads images and does transparency mapping if nece...
Definition opengl_tex.c:668
glTexture * gl_dupTexture(const glTexture *texture)
Duplicates a texture.
Definition opengl_tex.c:891
USE_RESULT glTexture * gl_texExistsOrCreate(const char *path, unsigned int flags, int sx, int sy, int *created)
Check to see if a texture matching a path already exists.
Definition opengl_tex.c:508
glTexture * gl_newSprite(const char *path, const int sx, const int sy, const unsigned int flags)
Loads the texture immediately, but also sets it as a sprite.
Definition opengl_tex.c:785
static int gl_loadNewImage(glTexture *tex, const char *path, int sx, int sy, unsigned int flags)
Only loads the image, does not add to stack unlike gl_newImage.
Definition opengl_tex.c:634
void gl_exitTextures(void)
Cleans up the opengl texture subsystem.
glTexture * gl_newImageRWops(const char *path, SDL_RWops *rw, const unsigned int flags)
Loads an image as a texture.
Definition opengl_tex.c:611
USE_RESULT glTexture * gl_rawTexture(const char *name, GLuint texid, double w, double h)
Creates a texture from a raw opengl index.
Definition opengl_tex.c:917
int gl_fboCreate(GLuint *fbo, GLuint *tex, GLsizei width, GLsizei height)
Creates a framebuffer and its associated texture.
Definition opengl_tex.c:268
static int gl_texAdd(glTexture *tex, int sx, int sy, unsigned int flags)
Adds a texture to the list under the name of path.
Definition opengl_tex.c:555
glTexture * gl_newSpriteRWops(const char *path, SDL_RWops *rw, const int sx, const int sy, const unsigned int flags)
Loads the texture immediately, but also sets it as a sprite.
Definition opengl_tex.c:808
static glTexture * gl_texCreate(const char *path, int sx, int sy, unsigned int flags)
Creates a new texture.
Definition opengl_tex.c:483
static USE_RESULT GLuint gl_loadSurface(SDL_Surface *surface, unsigned int flags, int freesur, double *vmax)
Loads a surface into an opengl texture.
Definition opengl_tex.c:402
glTexture ** gl_addTexArray(glTexture **tex, glTexture *t)
Adds an element to a texture array.
int gl_isTrans(const glTexture *t, const int x, const int y)
Checks to see if a pixel is transparent in a texture.
Definition opengl_tex.c:957
static int SDL_IsTrans(SDL_Surface *s, int x, int y)
Checks to see if a position of the surface is transparent.
Definition opengl_tex.c:150
static USE_RESULT uint8_t * SDL_MapAlpha(SDL_Surface *s, int tight)
Maps the surface transparency.
Definition opengl_tex.c:169
glTexture ** gl_copyTexArray(glTexture **tex)
Copy a texture array.
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition opengl_tex.c:587
int gl_fboAddDepth(GLuint fbo, GLuint *tex, GLsizei width, GLsizei height)
Adds a depth attachment to an FBO.
Definition opengl_tex.c:313
static SDL_mutex * gl_lock
Definition opengl_tex.c:45
static uint8_t SDL_GetAlpha(SDL_Surface *s, int x, int y)
Gets the alpha value of a pixel.
Definition opengl_tex.c:122
static SDL_mutex * tex_lock
Definition opengl_tex.c:46
static USE_RESULT GLuint gl_texParameters(unsigned int flags)
Sets default texture parameters.
Definition opengl_tex.c:220
void gl_getSpriteFromDir(int *x, int *y, int sx, int sy, double dir)
Sets x and y to be the appropriate sprite for glTexture using dir.
Definition opengl_tex.c:977
static glTexList * texture_list
Definition opengl_tex.c:43
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition opengl_tex.c:835
int gl_initTextures(void)
Initializes the opengl texture subsystem.
Represents a node in the texture list.
Definition opengl_tex.c:31
unsigned int flags
Definition opengl_tex.c:41
const char * path
Definition opengl_tex.c:33
glTexture * tex
Definition opengl_tex.c:32
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
double sw
Definition opengl_tex.h:53
double vmax
Definition opengl_tex.h:61
uint8_t * trans
Definition opengl_tex.h:60
double sh
Definition opengl_tex.h:54
double w
Definition opengl_tex.h:47
uint8_t flags
Definition opengl_tex.h:64
double sx
Definition opengl_tex.h:51
double srh
Definition opengl_tex.h:56
char * name
Definition opengl_tex.h:44
GLuint texture
Definition opengl_tex.h:59
double sy
Definition opengl_tex.h:52
double h
Definition opengl_tex.h:48
double srw
Definition opengl_tex.h:55
Define the state of the MD5 Algorithm.
Definition md5.h:73