a358782e98a25e7a9e90056f28fd8fd187e07706
[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,
73                                                   h * sizeof (png_bytep))))
74             {
75                 for (i = 0; i < h; ++i)
76                     bytep[h - i - 1] = (png_bytep) (p + i * w * 3);
77
78                 png_set_rows (writep, infop, bytep);
79
80                 /* Write the PNG image file. */
81
82                 png_write_info(writep, infop);
83                 png_write_png (writep, infop, 0, NULL);
84
85                 free(bytep);
86             }
87             free(p);
88         }
89     }
90
91     /* Release all resources. */
92
93     png_destroy_write_struct(&writep, &infop);
94     fclose(filep);
95 }
96
97 /*---------------------------------------------------------------------------*/
98
99 /*
100  * Create an OpenGL texture object using the given image buffer.
101  */
102 static GLuint make_texture(const void *p, int w, int h, int b)
103 {
104     static const GLenum format[] =
105         { 0, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
106
107     GLuint o = 0;
108
109     /* Scale the image as configured, or to fit the OpenGL limitations. */
110
111 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
112     int a = config_get_d(CONFIG_ANISO);
113 #endif
114     int m = config_get_d(CONFIG_MIPMAP);
115     int k = config_get_d(CONFIG_TEXTURES);
116     int W = w;
117     int H = h;
118
119     GLint max;
120
121     void *q = NULL;
122
123     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
124
125     while (w / k > (int) max || h / k > (int) max)
126         k *= 2;
127
128     if (k > 1)
129         q = image_scale(p, w, h, b, &W, &H, k);
130
131     /* Generate and configure a new OpenGL texture. */
132
133     glGenTextures(1, &o);
134     glBindTexture(GL_TEXTURE_2D, o);
135
136     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
137     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
138
139     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
140     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
141
142 #ifdef GL_GENERATE_MIPMAP_SGIS
143     if (m)
144     {
145         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
146         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
147                         GL_LINEAR_MIPMAP_LINEAR);
148     }
149 #endif
150 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
151     if (a) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, a);
152 #endif
153
154     /* Copy the image to an OpenGL texture. */
155
156     glTexImage2D(GL_TEXTURE_2D, 0,
157                  format[b], W, H, 0,
158                  format[b], GL_UNSIGNED_BYTE, q ? q : p);
159
160     if (q) free(q);
161
162
163     return o;
164 }
165
166 /*
167  * Load an image from the named file.  Return an OpenGL texture object.
168  */
169 GLuint make_image_from_file(const char *filename)
170 {
171     void  *p;
172     int    w;
173     int    h;
174     int    b;
175     GLuint o = 0;
176
177     /* Load the image. */
178
179     if ((p = image_load(config_data(filename), &w, &h, &b)))
180     {
181         o = make_texture(p, w, h, b);
182         free(p);
183     }
184
185     return o;
186 }
187
188 /*---------------------------------------------------------------------------*/
189
190 /*
191  * Render the given  string using the given font.   Transfer the image
192  * to a  surface of  power-of-2 size large  enough to fit  the string.
193  * Return an OpenGL texture object.
194  */
195 GLuint make_image_from_font(int *W, int *H,
196                             int *w, int *h,
197                             const char *text, TTF_Font *font)
198 {
199     GLuint o = 0;
200
201     /* Render the text. */
202
203     if (text && strlen(text) > 0)
204     {
205         SDL_Color    col = { 0xFF, 0xFF, 0xFF, 0xFF };
206         SDL_Surface *src;
207
208         if ((src = TTF_RenderUTF8_Blended(font, text, col)))
209         {
210             void *p;
211             int  w2;
212             int  h2;
213             int   b = src->format->BitsPerPixel / 8;
214
215             /* Pad the text to power-of-two. */
216
217             p = image_next2(src->pixels, src->w, src->h, b, &w2, &h2);
218
219             if (w) *w = src->w;
220             if (h) *h = src->h;
221             if (W) *W = w2;
222             if (H) *H = h2;
223
224             /* Saturate the color channels.  Modulate ONLY in alpha. */
225
226             image_white(p, w2, h2, b);
227
228             /* Create the OpenGL texture object. */
229
230             o = make_texture(p, w2, h2, b);
231
232             free(p);
233             SDL_FreeSurface(src);
234         }
235     }
236     else
237     {
238         /* Empty string. */
239
240         if (w) *w = 0;
241         if (h) *h = 0;
242         if (W) *W = 0;
243         if (H) *H = 0;
244     }
245
246     return o;
247 }
248
249 /*---------------------------------------------------------------------------*/
250
251 /*
252  * Load an image from the named file.  Return an SDL surface.
253  */
254 SDL_Surface *load_surface(const char *filename)
255 {
256     void  *p;
257     int    w;
258     int    h;
259     int    b;
260
261     Uint32 rmask;
262     Uint32 gmask;
263     Uint32 bmask;
264     Uint32 amask;
265
266 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
267     rmask = 0xFF000000;
268     gmask = 0x00FF0000;
269     bmask = 0x0000FF00;
270     amask = 0x000000FF;
271 #else
272     rmask = 0x000000FF;
273     gmask = 0x0000FF00;
274     bmask = 0x00FF0000;
275     amask = 0xFF000000;
276 #endif
277
278     if ((p = image_load(config_data(filename), &w, &h, &b)))
279         return SDL_CreateRGBSurfaceFrom(p, w, h, b * 8, w * b,
280                                         rmask, gmask, bmask, amask);
281     else
282         return NULL;
283 }
284
285 /*---------------------------------------------------------------------------*/