Correct logic of BSP back/front tests
[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(const 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_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 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
111     int a = config_get_d(CONFIG_ANISO);
112 #endif
113     int m = config_get_d(CONFIG_MIPMAP);
114     int k = config_get_d(CONFIG_TEXTURES);
115     int W = w;
116     int H = h;
117
118     GLint max;
119
120     void *q = NULL;
121
122     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
123
124     while (w / k > (int) max || h / k > (int) max)
125         k *= 2;
126
127     if (k > 1)
128         q = image_scale(p, w, h, b, &W, &H, k);
129
130     /* Generate and configure a new OpenGL texture. */
131
132     glGenTextures(1, &o);
133     glBindTexture(GL_TEXTURE_2D, o);
134
135     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
136     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
137
138     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
139     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
140
141 #ifdef GL_GENERATE_MIPMAP_SGIS
142     if (m)
143     {
144         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
145         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
146                         GL_LINEAR_MIPMAP_LINEAR);
147     }
148 #endif
149 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
150     if (a) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, a);
151 #endif
152
153     /* Copy the image to an OpenGL texture. */
154
155     glTexImage2D(GL_TEXTURE_2D, 0,
156                  format[b], W, H, 0,
157                  format[b], GL_UNSIGNED_BYTE, q ? q : p);
158
159     if (q) free(q);
160
161
162     return o;
163 }
164
165 /*
166  * Load an image from the named file.  Return an OpenGL texture object.
167  */
168 GLuint make_image_from_file(const char *filename)
169 {
170     void  *p;
171     int    w;
172     int    h;
173     int    b;
174     GLuint o = 0;
175
176     /* Load the image. */
177
178     if ((p = image_load(config_data(filename), &w, &h, &b)))
179     {
180         o = make_texture(p, w, h, b);
181         free(p);
182     }
183
184     return o;
185 }
186
187 /*---------------------------------------------------------------------------*/
188
189 /*
190  * Render the given  string using the given font.   Transfer the image
191  * to a  surface of  power-of-2 size large  enough to fit  the string.
192  * Return an OpenGL texture object.
193  */
194 GLuint make_image_from_font(int *W, int *H,
195                             int *w, int *h,
196                             const char *text, TTF_Font *font)
197 {
198     GLuint o = 0;
199
200     /* Render the text. */
201
202     if (text && strlen(text) > 0)
203     {
204         SDL_Color    col = { 0xFF, 0xFF, 0xFF, 0xFF };
205         SDL_Surface *orig;
206
207         if ((orig = TTF_RenderUTF8_Blended(font, text, col)))
208         {
209             void *p;
210             int  w2;
211             int  h2;
212             int   b = orig->format->BitsPerPixel / 8;
213
214             SDL_Surface *src;
215             SDL_PixelFormat fmt;
216
217             fmt = *orig->format;
218
219             fmt.Rmask = RMASK;
220             fmt.Gmask = GMASK;
221             fmt.Bmask = BMASK;
222             fmt.Amask = AMASK;
223
224             if ((src = SDL_ConvertSurface(orig, &fmt, orig->flags)) == NULL)
225             {
226                 fprintf(stderr, _("Failed to convert SDL_ttf surface: %s\n"),
227                         SDL_GetError());
228
229                 /* Pretend everything's just fine. */
230
231                 src = orig;
232             }
233             else
234                 SDL_FreeSurface(orig);
235
236             /* Pad the text to power-of-two. */
237
238             p = image_next2(src->pixels, src->w, src->h, b, &w2, &h2);
239
240             if (w) *w = src->w;
241             if (h) *h = src->h;
242             if (W) *W = w2;
243             if (H) *H = h2;
244
245             /* Saturate the color channels.  Modulate ONLY in alpha. */
246
247             image_white(p, w2, h2, b);
248
249             /* Create the OpenGL texture object. */
250
251             o = make_texture(p, w2, h2, b);
252
253             free(p);
254             SDL_FreeSurface(src);
255         }
256     }
257     else
258     {
259         /* Empty string. */
260
261         if (w) *w = 0;
262         if (h) *h = 0;
263         if (W) *W = 0;
264         if (H) *H = 0;
265     }
266
267     return o;
268 }
269
270 /*---------------------------------------------------------------------------*/
271
272 /*
273  * Load an image from the named file.  Return an SDL surface.
274  */
275 SDL_Surface *load_surface(const char *filename)
276 {
277     void  *p;
278     int    w;
279     int    h;
280     int    b;
281
282     SDL_Surface *srf = NULL;
283
284     Uint32 rmask;
285     Uint32 gmask;
286     Uint32 bmask;
287     Uint32 amask;
288
289 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
290     rmask = 0xFF000000;
291     gmask = 0x00FF0000;
292     bmask = 0x0000FF00;
293     amask = 0x000000FF;
294 #else
295     rmask = 0x000000FF;
296     gmask = 0x0000FF00;
297     bmask = 0x00FF0000;
298     amask = 0xFF000000;
299 #endif
300
301     if ((p = image_load(config_data(filename), &w, &h, &b)))
302     {
303         void *q;
304
305         if ((q = image_flip(p, w, h, b, 0, 1)))
306             srf = SDL_CreateRGBSurfaceFrom(q, w, h, b * 8, w * b,
307                                            rmask, gmask, bmask, amask);
308         free(p);
309     }
310     return srf;
311 }
312
313 /*---------------------------------------------------------------------------*/