1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file opengl_tex.c
7  *
8  * @brief This file handles the opengl texture wrapper routines.
9  */
10 
11 
12 #include "opengl.h"
13 
14 #include "naev.h"
15 
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include "nstring.h"
20 
21 #include "log.h"
22 #include "ndata.h"
23 #include "nfile.h"
24 #include "gui.h"
25 #include "conf.h"
26 #include "npng.h"
27 #include "md5.h"
28 
29 
30 /*
31  * graphic list
32  */
33 /**
34  * @brief Represents a node in the texture list.
35  */
36 typedef struct glTexList_ {
37    struct glTexList_ *next; /**< Next in linked list */
38    glTexture *tex; /**< associated texture */
39    int used; /**< counts how many times texture is being used */
40 } glTexList;
41 static glTexList* texture_list = NULL; /**< Texture list. */
42 
43 
44 /*
45  * Extensions.
46  */
47 static int gl_tex_ext_npot = 0; /**< Support for GL_ARB_texture_non_power_of_two. */
48 
49 
50 /*
51  * prototypes
52  */
53 /* misc */
54 /*static int SDL_VFlipSurface( SDL_Surface* surface );*/
55 static int SDL_IsTrans( SDL_Surface* s, int x, int y );
56 static uint8_t* SDL_MapTrans( SDL_Surface* s, int w, int h );
57 static size_t gl_transSize( const int w, const int h );
58 /* glTexture */
59 static GLuint gl_loadSurface( SDL_Surface* surface, int *rw, int *rh, unsigned int flags, int freesur );
60 static glTexture* gl_loadNewImage( const char* path, unsigned int flags );
61 /* List. */
62 static glTexture* gl_texExists( const char* path );
63 static int gl_texAdd( glTexture *tex );
64 
65 
66 /**
67  * @brief Gets the closest power of two.
68  *    @param n Number to get closest power of two to.
69  *    @return Closest power of two to the number.
70  */
gl_pot(int n)71 int gl_pot( int n )
72 {
73    int i = 1;
74    while (i < n)
75       i <<= 1;
76    return i;
77 }
78 
79 
80 #if 0
81 /**
82  * @brief Flips the surface vertically.
83  *
84  *    @param surface Surface to flip.
85  *    @return 0 on success.
86  */
87 static int SDL_VFlipSurface( SDL_Surface* surface )
88 {
89    /* flip the image */
90    Uint8 *rowhi, *rowlo, *tmpbuf;
91    int y;
92 
93    tmpbuf = malloc(surface->pitch);
94    if ( tmpbuf == NULL ) {
95       WARN("Out of memory");
96       return -1;
97    }
98 
99    rowhi = (Uint8 *)surface->pixels;
100    rowlo = rowhi + (surface->h * surface->pitch) - surface->pitch;
101    for (y = 0; y < surface->h / 2; ++y ) {
102       memcpy(tmpbuf, rowhi, surface->pitch);
103       memcpy(rowhi, rowlo, surface->pitch);
104       memcpy(rowlo, tmpbuf, surface->pitch);
105       rowhi += surface->pitch;
106       rowlo -= surface->pitch;
107    }
108    free(tmpbuf);
109    /* flipping done */
110 
111    return 0;
112 }
113 #endif
114 
115 
116 /**
117  * @brief Checks to see if a position of the surface is transparent.
118  *
119  *    @param s Surface to check for transparency.
120  *    @param x X position of the pixel to check.
121  *    @param y Y position of the pixel to check.
122  *    @return 0 if the pixel isn't transparent, 0 if it is.
123  */
SDL_IsTrans(SDL_Surface * s,int x,int y)124 static int SDL_IsTrans( SDL_Surface* s, int x, int y )
125 {
126    int bpp;
127    Uint8 *p;
128    Uint32 pixelcolour;
129 
130    bpp = s->format->BytesPerPixel;
131    /* here p is the address to the pixel we want to retrieve */
132    p = (Uint8 *)s->pixels + y*s->pitch + x*bpp;
133 
134    pixelcolour = 0;
135    switch(bpp) {
136       case 1:
137          pixelcolour = *p;
138          break;
139 
140       case 2:
141          pixelcolour = *(Uint16 *)p;
142          break;
143 
144       case 3:
145 #if HAS_BIGENDIAN
146          pixelcolour = p[0] << 16 | p[1] << 8 | p[2];
147 #else /* HAS_BIGENDIAN */
148          pixelcolour = p[0] | p[1] << 8 | p[2] << 16;
149 #endif /* HAS_BIGENDIAN */
150          break;
151 
152       case 4:
153          pixelcolour = *(Uint32 *)p;
154          break;
155    }
156 
157    /* test whether pixels colour == colour of transparent pixels for that surface */
158    return ((pixelcolour & s->format->Amask) < (Uint32)(0.1*(double)s->format->Amask));
159 }
160 
161 
162 /**
163  * @brief Maps the surface transparency.
164  *
165  * Basically generates a map of what pixels are transparent.  Good for pixel
166  *  perfect collision routines.
167  *
168  *    @param s Surface to map it's transparency.
169  *    @param w Width to map.
170  *    @param h Height to map.
171  *    @return 0 on success.
172  */
SDL_MapTrans(SDL_Surface * s,int w,int h)173 static uint8_t* SDL_MapTrans( SDL_Surface* s, int w, int h )
174 {
175    int i,j;
176    size_t size;
177    uint8_t *t;
178 
179    /* Get limit.s */
180    if (w < 0)
181       w = s->w;
182    if (h < 0)
183       h = s->h;
184 
185    /* alloc memory for just enough bits to hold all the data we need */
186    size = gl_transSize(w, h);
187    t = malloc(size);
188    if (t==NULL) {
189       WARN("Out of Memory");
190       return NULL;
191    }
192    memset(t, 0, size); /* important, must be set to zero */
193 
194    /* Check each pixel individually. */
195    for (i=0; i<h; i++)
196       for (j=0; j<w; j++) /* sets each bit to be 1 if not transparent or 0 if is */
197          t[(i*w+j)/8] |= (SDL_IsTrans(s,j,i)) ? 0 : (1<<((i*w+j)%8));
198 
199    return t;
200 }
201 
202 
203 /*
204  * @brief Gets the size needed for a transparency map.
205  *
206  *    @param w Width of the image.
207  *    @param h Height of the image.
208  *    @return The size in bytes.
209  */
gl_transSize(const int w,const int h)210 static size_t gl_transSize( const int w, const int h )
211 {
212    /* One bit per pixel, plus remainder. */
213    return w*h/8 + ((w*h%8)?1:0);
214 }
215 
216 
217 /**
218  * @brief Prepares the surface to be loaded as a texture.
219  *
220  *    @param surface to load that is freed in the process.
221  *    @return New surface that is prepared for texture loading.
222  */
gl_prepareSurface(SDL_Surface * surface)223 SDL_Surface* gl_prepareSurface( SDL_Surface* surface )
224 {
225    SDL_Surface* temp;
226    int potw, poth;
227    SDL_Rect rtemp;
228 #if ! SDL_VERSION_ATLEAST(1,3,0)
229    Uint32 saved_flags;
230 #endif /* ! SDL_VERSION_ATLEAST(1,3,0) */
231 
232    /* Make size power of two. */
233    potw = gl_pot(surface->w);
234    poth = gl_pot(surface->h);
235    if (gl_needPOT() && ((potw != surface->w) || (poth != surface->h))) {
236 
237       /* we must blit with an SDL_Rect */
238       rtemp.x = rtemp.y = 0;
239       rtemp.w = surface->w;
240       rtemp.h = surface->h;
241 
242       /* saves alpha */
243 #if SDL_VERSION_ATLEAST(1,3,0)
244       SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
245 
246       /* create the temp POT surface */
247       temp = SDL_CreateRGBSurface( 0, potw, poth,
248             surface->format->BytesPerPixel*8, RGBAMASK );
249 #else /* SDL_VERSION_ATLEAST(1,3,0) */
250       saved_flags = surface->flags & (SDL_SRCALPHA | SDL_RLEACCELOK);
251       if ((saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA) {
252          SDL_SetAlpha( surface, 0, SDL_ALPHA_OPAQUE );
253          SDL_SetColorKey( surface, 0, surface->format->colorkey );
254       }
255 
256       /* create the temp POT surface */
257       temp = SDL_CreateRGBSurface( SDL_SRCCOLORKEY,
258             potw, poth, surface->format->BytesPerPixel*8, RGBAMASK );
259 #endif /* SDL_VERSION_ATLEAST(1,3,0) */
260 
261       if (temp == NULL) {
262          WARN("Unable to create POT surface: %s", SDL_GetError());
263          return 0;
264       }
265       if (SDL_FillRect( temp, NULL,
266                SDL_MapRGBA(surface->format,0,0,0,SDL_ALPHA_TRANSPARENT))) {
267          WARN("Unable to fill rect: %s", SDL_GetError());
268          return 0;
269       }
270 
271       /* change the surface to the new blitted one */
272       SDL_BlitSurface( surface, &rtemp, temp, &rtemp);
273       SDL_FreeSurface( surface );
274       surface = temp;
275 
276 #if ! SDL_VERSION_ATLEAST(1,3,0)
277       /* set saved alpha */
278       if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA )
279          SDL_SetAlpha( surface, 0, 0 );
280 #endif /* ! SDL_VERSION_ATLEAST(1,3,0) */
281    }
282 
283    return surface;
284 }
285 
286 /**
287  * @brief Checks to see if mipmaps are supported and enabled.
288  *
289  *    @return 1 if mipmaps are supported and enabled.
290  */
gl_texHasMipmaps(void)291 int gl_texHasMipmaps (void)
292 {
293    return (nglGenerateMipmap != NULL);
294 }
295 
296 /**
297  * @brief Checks to see if texture compression is available and enabled.
298  *
299  *    @return 1 if texture compression is available and enabled.
300  */
gl_texHasCompress(void)301 int gl_texHasCompress (void)
302 {
303    return (nglCompressedTexImage2D != NULL);
304 }
305 
306 /**
307  * @brief Loads a surface into an opengl texture.
308  *
309  *    @param surface Surface to load into a texture.
310  *    @param flags Flags to use.
311  *    @param[out] rw Real width of the texture.
312  *    @param[out] rh Real height of the texture.
313  *    @return The opengl texture id.
314  */
gl_loadSurface(SDL_Surface * surface,int * rw,int * rh,unsigned int flags,int freesur)315 static GLuint gl_loadSurface( SDL_Surface* surface, int *rw, int *rh, unsigned int flags, int freesur )
316 {
317    GLuint texture;
318    GLfloat param;
319 
320    /* Prepare the surface. */
321    surface = gl_prepareSurface( surface );
322    if (rw != NULL)
323       (*rw) = surface->w;
324    if (rh != NULL)
325       (*rh) = surface->h;
326 
327    /* opengl texture binding */
328    glGenTextures( 1, &texture ); /* Creates the texture */
329    glBindTexture( GL_TEXTURE_2D, texture ); /* Loads the texture */
330 
331    /* Filtering, LINEAR is better for scaling, nearest looks nicer, LINEAR
332     * also seems to create a bit of artifacts around the edges */
333    if ((gl_screen.scale != 1.) || (flags & OPENGL_TEX_MIPMAPS)) {
334       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
335       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
336    }
337    else {
338       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
339       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
340    }
341 
342    /* Always wrap just in case. */
343    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
344    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
345 
346    /* now lead the texture data up */
347    SDL_LockSurface( surface );
348    if (gl_texHasCompress()) {
349       glTexImage2D( GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA,
350             surface->w, surface->h, 0, GL_RGBA,
351             GL_UNSIGNED_BYTE, surface->pixels );
352    }
353    else {
354       glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,
355             surface->w, surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels );
356    }
357    SDL_UnlockSurface( surface );
358 
359    /* Create mipmaps. */
360    if ((flags & OPENGL_TEX_MIPMAPS) && gl_texHasMipmaps()) {
361       /* Do fancy stuff. */
362       if (gl_hasExt("GL_EXT_texture_filter_anisotropic")) {
363          glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &param);
364          glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, param);
365       }
366       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
367       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 9);
368 
369       /* Now generate the mipmaps. */
370       nglGenerateMipmap(GL_TEXTURE_2D);
371    }
372 
373    /* cleanup */
374    if (freesur)
375       SDL_FreeSurface( surface );
376    gl_checkErr();
377 
378    return texture;
379 }
380 
381 
382 /**
383  * @brief Wrapper for gl_loadImagePad that includes transparency mapping.
384  *
385  *    @param name Name to load with.
386  *    @param surface Surface to load.
387  *    @param rw RWops containing data to hash.
388  *    @param flags Flags to use.
389  *    @param w Non-padded width.
390  *    @param h Non-padded height.
391  *    @param sx X sprites.
392  *    @param sy Y sprites.
393  *    @param freesur Whether or not to free the surface.
394  *    @return The glTexture for surface.
395  */
gl_loadImagePadTrans(const char * name,SDL_Surface * surface,SDL_RWops * rw,unsigned int flags,int w,int h,int sx,int sy,int freesur)396 glTexture* gl_loadImagePadTrans( const char *name, SDL_Surface* surface, SDL_RWops *rw,
397       unsigned int flags, int w, int h, int sx, int sy, int freesur )
398 {
399    glTexture *texture;
400    int i, filesize;
401    size_t cachesize, pngsize;
402    uint8_t *trans;
403    char *cachefile, *data;
404    char digest[33];
405    md5_state_t md5;
406    md5_byte_t *md5val;
407 
408    if (name != NULL) {
409       texture = gl_texExists( name );
410       if (texture != NULL)
411          return texture;
412    }
413 
414    if (flags & OPENGL_TEX_MAPTRANS)
415       flags ^= OPENGL_TEX_MAPTRANS;
416 
417    /* Appropriate size for the transparency map, see SDL_MapTrans */
418    cachesize = gl_transSize(w, h);
419 
420    cachefile = NULL;
421    trans     = NULL;
422 
423    if (rw != NULL) {
424       md5val = malloc(16);
425       md5_init(&md5);
426 
427       pngsize = SDL_RWseek( rw, 0, SEEK_END );
428       SDL_RWseek( rw, 0, SEEK_SET );
429 
430       data = malloc(pngsize);
431       if (data == NULL)
432          WARN("Out of memory!");
433       else {
434          SDL_RWread( rw, data, pngsize, 1 );
435          md5_append( &md5, (md5_byte_t*)data, pngsize );
436          free(data);
437       }
438       md5_finish( &md5, md5val );
439 
440       for (i=0; i<16; i++)
441          nsnprintf( &digest[i * 2], 3, "%02x", md5val[i] );
442       free(md5val);
443 
444       cachefile = malloc( PATH_MAX );
445       nsnprintf( cachefile, PATH_MAX, "%scollisions/%s",
446          nfile_cachePath(), digest );
447 
448       /* Attempt to find a cached transparency map. */
449       if (nfile_fileExists(cachefile)) {
450          trans = (uint8_t*)nfile_readFile( &filesize, cachefile );
451 
452          /* Consider cached data invalid if the length doesn't match. */
453          if (trans != NULL && cachesize != (unsigned int)filesize) {
454             free(trans);
455             trans = NULL;
456          }
457          /* Cached data matches, no need to overwrite. */
458          else {
459             free(cachefile);
460             cachefile = NULL;
461          }
462       }
463    }
464    else {
465       /* We could hash raw pixel data here, but that's slower than just
466        * generating the map from scratch.
467        */
468       WARN("Texture '%s' has no RWops", name);
469    }
470 
471    if (trans == NULL) {
472       SDL_LockSurface(surface);
473       trans = SDL_MapTrans( surface, w, h );
474       SDL_UnlockSurface(surface);
475 
476       if (cachefile != NULL) {
477          /* Cache newly-generated transparency map. */
478          nfile_dirMakeExist( "%s/collisions/", nfile_cachePath() );
479          nfile_writeFile( (char*)trans, cachesize, cachefile );
480          free(cachefile);
481       }
482    }
483 
484    texture = gl_loadImagePad( name, surface, flags, w, h, sx, sy, freesur );
485    texture->trans = trans;
486    return texture;
487 }
488 
489 
490 /**
491  * @brief Loads the already padded SDL_Surface to a glTexture.
492  *
493  *    @param name Name to load with.
494  *    @param surface Surface to load.
495  *    @param flags Flags to use.
496  *    @param w Non-padded width.
497  *    @param h Non-padded height.
498  *    @param sx X sprites.
499  *    @param sy Y sprites.
500  *    @param freesur Whether or not to free the surface.
501  *    @return The glTexture for surface.
502  */
gl_loadImagePad(const char * name,SDL_Surface * surface,unsigned int flags,int w,int h,int sx,int sy,int freesur)503 glTexture* gl_loadImagePad( const char *name, SDL_Surface* surface,
504       unsigned int flags, int w, int h, int sx, int sy, int freesur )
505 {
506    glTexture *texture;
507    int rw, rh;
508 
509    /* Make sure doesn't already exist. */
510    if (name != NULL) {
511       texture = gl_texExists( name );
512       if (texture != NULL)
513          return texture;
514    }
515 
516    if (flags & OPENGL_TEX_MAPTRANS)
517       return gl_loadImagePadTrans( name, surface, NULL, flags, w, h,
518             sx, sy, freesur );
519 
520    /* set up the texture defaults */
521    texture = calloc( 1, sizeof(glTexture) );
522 
523    texture->w     = (double) w;
524    texture->h     = (double) h;
525    texture->sx    = (double) sx;
526    texture->sy    = (double) sy;
527 
528    texture->texture = gl_loadSurface( surface, &rw, &rh, flags, freesur );
529 
530    texture->rw    = (double) rw;
531    texture->rh    = (double) rh;
532    texture->sw    = texture->w / texture->sx;
533    texture->sh    = texture->h / texture->sy;
534    texture->srw   = texture->sw / texture->rw;
535    texture->srh   = texture->sh / texture->rh;
536 
537    if (name != NULL) {
538       texture->name = strdup(name);
539       gl_texAdd( texture );
540    }
541    else
542       texture->name = NULL;
543 
544    return texture;
545 }
546 
547 
548 /**
549  * @brief Loads the SDL_Surface to a glTexture.
550  *
551  *    @param surface Surface to load.
552  *    @param flags Flags to use.
553  *    @return The glTexture for surface.
554  */
gl_loadImage(SDL_Surface * surface,unsigned int flags)555 glTexture* gl_loadImage( SDL_Surface* surface, unsigned int flags )
556 {
557    return gl_loadImagePad( NULL, surface, flags, surface->w, surface->h, 1, 1, 1 );
558 }
559 
560 
561 /**
562  * @brief Check to see if a texture matching a path already exists.
563  *
564  *    @param path Path to the texture.
565  *    @return The texture, or NULL if none was found.
566  */
gl_texExists(const char * path)567 static glTexture* gl_texExists( const char* path )
568 {
569    glTexList *cur;
570 
571    /* check to see if it already exists */
572    if (texture_list != NULL) {
573       for (cur=texture_list; cur!=NULL; cur=cur->next) {
574          if (strcmp(path,cur->tex->name)==0) {
575             cur->used += 1;
576             return cur->tex;
577          }
578       }
579    }
580 
581    return NULL;
582 }
583 
584 
585 /**
586  * @brief Adds a texture to the list under the name of path.
587  */
gl_texAdd(glTexture * tex)588 static int gl_texAdd( glTexture *tex )
589 {
590    glTexList *new, *cur, *last;
591 
592    /* Create the new node */
593    new = malloc( sizeof(glTexList) );
594    new->next = NULL;
595    new->used = 1;
596    new->tex  = tex;
597 
598    if (texture_list == NULL) /* special condition - creating new list */
599       texture_list = new;
600    else {
601       for (cur=texture_list; cur!=NULL; cur=cur->next)
602          last = cur;
603 
604       last->next = new;
605    }
606 
607    return 0;
608 }
609 
610 
611 /**
612  * @brief Loads an image as a texture.
613  *
614  * May not necessarily load the image but use one if it's already open.
615  *
616  *    @param path Image to load.
617  *    @param flags Flags to control image parameters.
618  *    @return Texture loaded from image.
619  */
gl_newImage(const char * path,const unsigned int flags)620 glTexture* gl_newImage( const char* path, const unsigned int flags )
621 {
622    glTexture *t;
623 
624    /* Check if it already exists. */
625    t = gl_texExists( path );
626    if (t != NULL)
627       return t;
628 
629    /* Load the image */
630    return gl_loadNewImage( path, flags );
631 }
632 
633 
634 /**
635  * @brief Only loads the image, does not add to stack unlike gl_newImage.
636  *
637  *    @param path Image to load.
638  *    @param flags Flags to control image parameters.
639  *    @return Texture loaded from image.
640  */
gl_loadNewImage(const char * path,const unsigned int flags)641 static glTexture* gl_loadNewImage( const char* path, const unsigned int flags )
642 {
643    glTexture *texture;
644    SDL_Surface *surface;
645    SDL_RWops *rw;
646    npng_t *npng;
647    png_uint_32 w, h;
648    int sx, sy;
649    char *str;
650    int len;
651 
652    /* load from packfile */
653    rw = ndata_rwops( path );
654    if (rw == NULL) {
655       WARN("Failed to load surface '%s' from ndata.", path);
656       return NULL;
657    }
658    npng     = npng_open( rw );
659    if (npng == NULL) {
660       WARN("File '%s' is not a png.", path );
661       return NULL;
662    }
663    npng_dim( npng, &w, &h );
664 
665    /* Process metadata. */
666    len = npng_metadata( npng, "sx", &str );
667    sx  = (len > 0) ? atoi(str) : 1;
668    len = npng_metadata( npng, "sy", &str );
669    sy  = (len > 0) ? atoi(str) : 1;
670 
671    /* Load surface. */
672    surface  = npng_readSurface( npng, gl_needPOT(), 1 );
673    npng_close( npng );
674 
675    if (surface == NULL) {
676       WARN("'%s' could not be opened", path );
677       SDL_RWclose( rw );
678       return NULL;
679    }
680 
681    if (flags & OPENGL_TEX_MAPTRANS)
682       texture = gl_loadImagePadTrans( path, surface, rw, flags, w, h, sx, sy, 1 );
683    else
684       texture = gl_loadImagePad( path, surface, flags, w, h, sx, sy, 1 );
685 
686    SDL_RWclose( rw );
687    return texture;
688 }
689 
690 
691 /**
692  * @brief Loads the texture immediately, but also sets it as a sprite.
693  *
694  *    @param path Image to load.
695  *    @param sx Number of X sprites in image.
696  *    @param sy Number of Y sprites in image.
697  *    @param flags Flags to control image parameters.
698  *    @return Texture loaded.
699  */
gl_newSprite(const char * path,const int sx,const int sy,const unsigned int flags)700 glTexture* gl_newSprite( const char* path, const int sx, const int sy,
701       const unsigned int flags )
702 {
703    glTexture* texture;
704    texture = gl_newImage( path, flags );
705    if (texture == NULL)
706       return NULL;
707 
708    /* will possibly overwrite an existing textur properties
709     * so we have to load same texture always the same sprites */
710    texture->sx    = (double) sx;
711    texture->sy    = (double) sy;
712    texture->sw    = texture->w / texture->sx;
713    texture->sh    = texture->h / texture->sy;
714    texture->srw   = texture->sw / texture->rw;
715    texture->srh   = texture->sh / texture->rh;
716    return texture;
717 }
718 
719 
720 /**
721  * @brief Frees a texture.
722  *
723  *    @param texture Texture to free.
724  */
gl_freeTexture(glTexture * texture)725 void gl_freeTexture( glTexture* texture )
726 {
727    glTexList *cur, *last;
728 
729    /* Shouldn't be NULL (won't segfault though) */
730    if (texture == NULL) {
731       WARN("Attempting to free NULL texture!");
732       return;
733    }
734 
735    /* see if we can find it in stack */
736    last = NULL;
737    for (cur=texture_list; cur!=NULL; cur=cur->next) {
738       if (cur->tex == texture) { /* found it */
739          cur->used--;
740          if (cur->used <= 0) { /* not used anymore */
741             /* free the texture */
742             glDeleteTextures( 1, &texture->texture );
743             if (texture->trans != NULL)
744                free(texture->trans);
745             if (texture->name != NULL)
746                free(texture->name);
747             free(texture);
748 
749             /* free the list node */
750             if (last == NULL) { /* case there's no texture before it */
751                if (cur->next != NULL)
752                   texture_list = cur->next;
753                else /* case it's the last texture */
754                   texture_list = NULL;
755             }
756             else
757                last->next = cur->next;
758             free(cur);
759          }
760          return; /* we already found it so we can exit */
761       }
762       last = cur;
763    }
764 
765    /* Not found */
766    if (texture->name != NULL) /* Surfaces will have NULL names */
767       WARN("Attempting to free texture '%s' not found in stack!", texture->name);
768 
769    /* Free anyways */
770    glDeleteTextures( 1, &texture->texture );
771    if (texture->trans != NULL)
772       free(texture->trans);
773    if (texture->name != NULL)
774       free(texture->name);
775    free(texture);
776 
777    gl_checkErr();
778 }
779 
780 
781 /**
782  * @brief Duplicates a texture.
783  *
784  *    @param texture Texture to duplicate.
785  *    @return Duplicate of texture.
786  */
gl_dupTexture(glTexture * texture)787 glTexture* gl_dupTexture( glTexture *texture )
788 {
789    glTexList *cur;
790 
791    /* No segfaults kthxbye. */
792    if (texture == NULL)
793       return NULL;
794 
795    /* check to see if it already exists */
796    if (texture_list != NULL) {
797       for (cur=texture_list; cur!=NULL; cur=cur->next) {
798          if (texture == cur->tex) {
799             cur->used += 1;
800             return cur->tex;
801          }
802       }
803    }
804 
805    /* Invalid texture. */
806    WARN("Unable to duplicate texture '%s'.", texture->name);
807    return NULL;
808 }
809 
810 
811 /**
812  * @brief Checks to see if a pixel is transparent in a texture.
813  *
814  *    @param t Texture to check for transparency.
815  *    @param x X position of the pixel.
816  *    @param y Y position of the pixel.
817  *    @return 1 if the pixel is transparent or 0 if it isn't.
818  */
gl_isTrans(const glTexture * t,const int x,const int y)819 int gl_isTrans( const glTexture* t, const int x, const int y )
820 {
821    int i;
822 
823    /* Get the position in the sheet. */
824    i = y*(int)(t->w) + x ;
825    /* Now we have to pull out the individual bit. */
826    return !(t->trans[ i/8 ] & (1 << (i%8)));
827 }
828 
829 
830 /**
831  * @brief Sets x and y to be the appropriate sprite for glTexture using dir.
832  *
833  * Very slow, try to cache if possible like the pilots do instead of using
834  *  in O(n^2) or worse functions.
835  *
836  *    @param[out] x X sprite to use.
837  *    @param[out] y Y sprite to use.
838  *    @param t Texture to get sprite from.
839  *    @param dir Direction to get sprite from.
840  */
gl_getSpriteFromDir(int * x,int * y,const glTexture * t,const double dir)841 void gl_getSpriteFromDir( int* x, int* y, const glTexture* t, const double dir )
842 {
843    int s, sx, sy;
844    double shard, rdir;
845 
846 #ifdef DEBUGGING
847    if ((dir > 2.*M_PI) || (dir < 0.)) {
848       WARN("Angle not between 0 and 2.*M_PI [%f].", dir);
849       return;
850    }
851 #endif /* DEBUGGING */
852 
853    /* what each image represents in angle */
854    shard = 2.*M_PI / (t->sy*t->sx);
855 
856    /* real dir is slightly moved downwards */
857    rdir = dir + shard/2.;
858 
859    /* now calculate the sprite we need */
860    s = (int)(rdir / shard);
861    sx = t->sx;
862    sy = t->sy;
863 
864    /* makes sure the sprite is "in range" */
865    if (s > (sy*sx-1))
866       s = s % (sy*sx);
867 
868    (*x) = s % sx;
869    (*y) = s / sx;
870 }
871 
872 
873 /**
874  * @brief Checks to see if OpenGL needs POT textures.
875  *
876  *    @return 0 if OpenGL doesn't needs POT textures.
877  */
gl_needPOT(void)878 int gl_needPOT (void)
879 {
880    if (gl_tex_ext_npot && conf.npot)
881       return 0;
882    else
883       return 1;
884 }
885 
886 
887 /**
888  * @brief Initializes the opengl texture subsystem.
889  *
890  *    @return 0 on success.
891  */
gl_initTextures(void)892 int gl_initTextures (void)
893 {
894    if (gl_hasVersion(2,0) || gl_hasExt("GL_ARB_texture_non_power_of_two"))
895       gl_tex_ext_npot = 1;
896 
897    return 0;
898 }
899 
900 
901 /**
902  * @brief Cleans up the opengl texture subsystem.
903  */
gl_exitTextures(void)904 void gl_exitTextures (void)
905 {
906    glTexList *tex;
907 
908    /* Make sure there's no texture leak */
909    if (texture_list != NULL) {
910       DEBUG("Texture leak detected!");
911       for (tex=texture_list; tex!=NULL; tex=tex->next)
912          DEBUG("   '%s' opened %d times", tex->tex->name, tex->used );
913    }
914 }
915 
916