Oops! Fixed newly envmapped glass being scheduled as opaque.
[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 #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     int max;
118
119     void *q = NULL;
120
121     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
122
123     while (w / k > max || h / k > max)
124         k *= 2;
125
126     if (k > 1)
127         q = image_scale(p, w, h, b, &W, &H, k);
128
129     /* Generate and configure a new OpenGL texture. */
130
131     glGenTextures(1, &o);
132     glBindTexture(GL_TEXTURE_2D, o);
133
134     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
135     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
136
137     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
138     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
139
140 #ifdef GL_GENERATE_MIPMAP_SGIS
141     if (m)
142     {
143         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
144         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
145                         GL_LINEAR_MIPMAP_LINEAR);
146     }
147 #endif
148 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
149     if (a) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, a);
150 #endif
151
152     /* Copy the image to an OpenGL texture. */
153
154     glTexImage2D(GL_TEXTURE_2D, 0,
155                  format[b], W, H, 0,
156                  format[b], GL_UNSIGNED_BYTE, q ? q : p);
157
158     if (q) free(q);
159
160
161     return o;
162 }
163
164 /*
165  * Load an image from the named file.  Return an OpenGL texture object.
166  */
167 GLuint make_image_from_file(const char *filename)
168 {
169     void  *p;
170     int    w;
171     int    h;
172     int    b;
173     GLuint o = 0;
174
175     /* Load the image. */
176
177     if ((p = image_load(config_data(filename), &w, &h, &b)))
178     {
179         o = make_texture(p, w, h, b);
180         free(p);
181     }
182
183     return o;
184 }
185
186 /*---------------------------------------------------------------------------*/
187
188 /*
189  * Render the given  string using the given font.   Transfer the image
190  * to a  surface of  power-of-2 size large  enough to fit  the string.
191  * Return an OpenGL texture object.
192  */
193 GLuint make_image_from_font(int *W, int *H,
194                             int *w, int *h,
195                             const char *text, TTF_Font *font)
196 {
197     GLuint o = 0;
198
199     /* Render the text. */
200
201     if (text && strlen(text) > 0)
202     {
203         SDL_Color    col = { 0xFF, 0xFF, 0xFF, 0xFF };
204         SDL_Surface *src;
205
206         if ((src = TTF_RenderUTF8_Blended(font, text, col)))
207         {
208             void *p;
209             int  w2;
210             int  h2;
211             int   b = src->format->BitsPerPixel / 8;
212
213             /* Pad the text to power-of-two. */
214
215             p = image_next2(src->pixels, src->w, src->h, b, &w2, &h2);
216
217             if (w) *w = src->w;
218             if (h) *h = src->h;
219             if (W) *W = w2;
220             if (H) *H = h2;
221
222             /* Saturate the color channels.  Modulate ONLY in alpha. */
223
224             image_white(p, w2, h2, b);
225
226             /* Create the OpenGL texture object. */
227
228             o = make_texture(p, w2, h2, b);
229
230             free(p);
231             SDL_FreeSurface(src);
232         }
233     }
234     else
235     {
236         /* Empty string. */
237
238         if (w) *w = 0;
239         if (h) *h = 0;
240         if (W) *W = 0;
241         if (H) *H = 0;
242     }
243
244     return o;
245 }
246
247 /*---------------------------------------------------------------------------*/
248
249 /*
250  * Load an image from the named file.  Return an SDL surface.
251  */
252 SDL_Surface *load_surface(const char *filename)
253 {
254     void  *p;
255     int    w;
256     int    h;
257     int    b;
258
259     Uint32 rmask;
260     Uint32 gmask;
261     Uint32 bmask;
262     Uint32 amask;
263
264 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
265     rmask = 0xFF000000;
266     gmask = 0x00FF0000;
267     bmask = 0x0000FF00;
268     amask = 0x000000FF;
269 #else
270     rmask = 0x000000FF;
271     gmask = 0x0000FF00;
272     bmask = 0x00FF0000;
273     amask = 0xFF000000;
274 #endif
275
276     if ((p = image_load(config_data(filename), &w, &h, &b)))
277         return SDL_CreateRGBSurfaceFrom(p, w, h, b * 8, w * b,
278                                         rmask, gmask, bmask, amask);
279     else
280         return NULL;
281 }
282
283 /*---------------------------------------------------------------------------*/