Fixed level data not being freed by conf state. This allowed OpenGL state to leak...
[neverball] / share / image.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <SDL.h>
16 #include <SDL_ttf.h>
17 #include <string.h>
18 #include <math.h>
19 #include <png.h>
20 #include <stdlib.h>
21
22 #include "glext.h"
23 #include "image.h"
24 #include "base_image.h"
25 #include "config.h"
26
27 /*---------------------------------------------------------------------------*/
28
29 void image_snap(char *filename)
30 {
31     FILE       *filep  = NULL;
32     png_structp writep = NULL;
33     png_infop   infop  = NULL;
34     png_bytep  *bytep  = NULL;
35
36     int w = config_get_d(CONFIG_WIDTH);
37     int h = config_get_d(CONFIG_HEIGHT);
38     int i;
39
40     unsigned char *p = NULL;
41
42     /* Initialize all PNG export data structures. */
43
44     if (!(filep = fopen(filename, FMODE_WB)))
45         return;
46     if (!(writep = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
47         return;
48     if (!(infop = png_create_info_struct(writep)))
49         return;
50
51     /* Enable the default PNG error handler. */
52
53     if (setjmp(png_jmpbuf(writep)) == 0)
54     {
55         /* Initialize the PNG header. */
56
57         png_init_io (writep, filep);
58         png_set_IHDR(writep, infop, w, h, 8,
59                      PNG_COLOR_TYPE_RGB,
60                      PNG_INTERLACE_NONE,
61                      PNG_COMPRESSION_TYPE_DEFAULT,
62                      PNG_FILTER_TYPE_DEFAULT);
63
64         /* Allocate the pixel buffer and copy pixels there. */
65
66         if ((p = (unsigned char *) malloc(w * h * 3)))
67         {
68             glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, p);
69
70             /* Allocate and initialize the row pointers. */
71
72             if ((bytep = png_malloc(writep, h * sizeof (png_bytep))))
73             {
74                 for (i = 0; i < h; ++i)
75                     bytep[h - i - 1] = (png_bytep) (p + i * w * 3);
76
77                 png_set_rows (writep, infop, bytep);
78
79                 /* Write the PNG image file. */
80
81                 png_write_info(writep, infop);
82                 png_write_png (writep, infop, 0, NULL);
83
84                 free(bytep);
85             }
86             free(p);
87         }
88     }
89
90     /* Release all resources. */
91
92     png_destroy_write_struct(&writep, &infop);
93     fclose(filep);
94 }
95
96 /*---------------------------------------------------------------------------*/
97
98 /*
99  * Create an OpenGL texture object using the given image buffer.
100  */
101 static GLuint make_texture(const void *p, int w, int h, int b)
102 {
103     static const GLenum format[] =
104         { 0, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
105
106     GLuint o = 0;
107
108     /* Scale the image as configured, or to fit the OpenGL limitations. */
109
110     int a = 8;
111     int m = config_get_d(CONFIG_MIPMAP);
112     int k = config_get_d(CONFIG_TEXTURES);
113     int W = w;
114     int H = h;
115     int max;
116
117     void *q = NULL;
118
119     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
120
121     while (w / k > max || h / k > max)
122         k *= 2;
123
124     if (k > 1)
125         q = image_scale(p, w, h, b, &W, &H, k);
126
127     /* Generate and configure a new OpenGL texture. */
128
129     glGenTextures(1, &o);
130     glBindTexture(GL_TEXTURE_2D, o);
131
132     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
133     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
134
135     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
136     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
137                     m ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
138
139 #ifdef GL_GENERATE_MIPMAP_SGIS
140     if (m) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
141 #endif
142 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
143     if (m) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, a);
144 #endif
145
146     /* Copy the image to an OpenGL texture. */
147
148     glTexImage2D(GL_TEXTURE_2D, 0,
149                  format[b], W, H, 0,
150                  format[b], GL_UNSIGNED_BYTE, q ? q : p);
151
152     if (q) free(q);
153
154
155     return o;
156 }
157
158 /*
159  * Load an image from the named file.  Return an OpenGL texture object.
160  */
161 GLuint make_image_from_file(const char *filename)
162 {
163     void  *p;
164     int    w;
165     int    h;
166     int    b;
167     GLuint o = 0;
168
169     /* Load the image. */
170
171     if ((p = image_load(config_data(filename), &w, &h, &b)))
172     {
173         o = make_texture(p, w, h, b);
174         free(p);
175     }
176
177     return o;
178 }
179
180 /*---------------------------------------------------------------------------*/
181
182 /*
183  * Render the given  string using the given font.   Transfer the image
184  * to a  surface of  power-of-2 size large  enough to fit  the string.
185  * Return an OpenGL texture object.
186  */
187 GLuint make_image_from_font(int *W, int *H,
188                             int *w, int *h,
189                             const char *text, TTF_Font *font)
190 {
191     GLuint o = 0;
192
193     /* Render the text. */
194
195     if (text && strlen(text) > 0)
196     {
197         SDL_Color    col = { 0xFF, 0xFF, 0xFF, 0xFF };
198         SDL_Surface *src;
199
200         if ((src = TTF_RenderUTF8_Blended(font, text, col)))
201         {
202             void *p;
203             int  w2;
204             int  h2;
205             int   b = src->format->BitsPerPixel / 8;
206
207             /* Pad the text to power-of-two. */
208
209             p = image_next2(src->pixels, src->w, src->h, b, &w2, &h2);
210
211             if (w) *w = src->w;
212             if (h) *h = src->h;
213             if (W) *W = w2;
214             if (H) *H = h2;
215
216             /* Saturate the color channels.  Modulate ONLY in alpha. */
217
218             image_white(p, w2, h2, b);
219
220             /* Create the OpenGL texture object. */
221
222             o = make_texture(p, w2, h2, b);
223
224             free(p);
225             SDL_FreeSurface(src);
226         }
227     }
228     else
229     {
230         /* Empty string. */
231
232         if (w) *w = 0;
233         if (h) *h = 0;
234         if (W) *W = 0;
235         if (H) *H = 0;
236     }
237
238     return o;
239 }
240
241 /*---------------------------------------------------------------------------*/
242
243 /*
244  * Load an image from the named file.  Return an SDL surface.
245  */
246 SDL_Surface *load_surface(const char *filename)
247 {
248     void  *p;
249     int    w;
250     int    h;
251     int    b;
252
253     Uint32 rmask;
254     Uint32 gmask;
255     Uint32 bmask;
256     Uint32 amask;
257
258 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
259     rmask = 0xFF000000;
260     gmask = 0x00FF0000;
261     bmask = 0x0000FF00;
262     amask = 0x000000FF;
263 #else
264     rmask = 0x000000FF;
265     gmask = 0x0000FF00;
266     bmask = 0x00FF0000;
267     amask = 0xFF000000;
268 #endif
269
270     if ((p = image_load(config_data(filename), &w, &h, &b)))
271         return SDL_CreateRGBSurfaceFrom(p, w, h, b * 8, w * b,
272                                         rmask, gmask, bmask, amask);
273     else
274         return NULL;
275 }
276
277 /*---------------------------------------------------------------------------*/