Port image_save to GLES 1.1
[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 #include "fs.h"
28 #include "fs_png.h"
29
30 /*---------------------------------------------------------------------------*/
31
32 void image_snap(const char *filename)
33 {
34     fs_file     filep  = NULL;
35     png_structp writep = NULL;
36     png_infop   infop  = NULL;
37     png_bytep  *bytep  = NULL;
38
39     int w = config_get_d(CONFIG_WIDTH);
40     int h = config_get_d(CONFIG_HEIGHT);
41     int i;
42
43     unsigned char *p = NULL;
44
45     /* Initialize all PNG export data structures. */
46
47     if (!(filep = fs_open(filename, "w")))
48         return;
49     if (!(writep = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
50         return;
51     if (!(infop = png_create_info_struct(writep)))
52         return;
53
54     /* Enable the default PNG error handler. */
55
56     if (setjmp(png_jmpbuf(writep)) == 0)
57     {
58         /* Initialize the PNG header. */
59
60         png_set_write_fn(writep, filep, fs_png_write, fs_png_flush);
61         png_set_IHDR(writep, infop, w, h, 8,
62                      PNG_COLOR_TYPE_RGB_ALPHA,
63                      PNG_INTERLACE_NONE,
64                      PNG_COMPRESSION_TYPE_DEFAULT,
65                      PNG_FILTER_TYPE_DEFAULT);
66
67         /* Allocate the pixel buffer and copy pixels there. */
68
69         if ((p = (unsigned char *) malloc(w * h * 4)))
70         {
71             glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
72
73             /* Allocate and initialize the row pointers. */
74
75             if ((bytep = (png_bytep *) png_malloc(writep, h * sizeof (png_bytep))))
76             {
77                 for (i = 0; i < h; ++i)
78                     bytep[h - i - 1] = (png_bytep) (p + i * w * 4);
79
80                 png_set_rows (writep, infop, bytep);
81
82                 /* Write the PNG image file. */
83
84                 png_write_info(writep, infop);
85                 png_write_png (writep, infop, 0, NULL);
86
87                 free(bytep);
88             }
89             free(p);
90         }
91     }
92
93     /* Release all resources. */
94
95     png_destroy_write_struct(&writep, &infop);
96     fs_close(filep);
97 }
98
99 /*---------------------------------------------------------------------------*/
100
101 /*
102  * Create an OpenGL texture object using the given image buffer.
103  */
104 static GLuint make_texture(const void *p, int w, int h, int b)
105 {
106     static const GLenum format[] =
107         { 0, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
108     static const GLenum type[] =
109         { 0, GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
110             GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4 };
111
112     GLuint o = 0;
113
114     /* Scale the image as configured, or to fit the OpenGL limitations. */
115
116 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
117     int a = config_get_d(CONFIG_ANISO);
118 #endif
119 #ifdef GL_GENERATE_MIPMAP_SGIS
120     int m = config_get_d(CONFIG_MIPMAP);
121 #endif
122     int k = config_get_d(CONFIG_TEXTURES);
123     int W = w;
124     int H = h;
125
126     GLint max = gli.max_texture_size;
127
128     void *q = NULL;
129
130     while (w / k > (int) max || h / k > (int) max)
131         k *= 2;
132
133     if (k > 1)
134         q = image_scale(p, w, h, b, &W, &H, k);
135
136     /* Generate and configure a new OpenGL texture. */
137
138     glGenTextures(1, &o);
139     glBindTexture(GL_TEXTURE_2D, o);
140
141     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
142     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
143
144     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
145     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
146
147 #ifdef GL_GENERATE_MIPMAP_SGIS
148     if (m)
149     {
150         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
151         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
152                         GL_LINEAR_MIPMAP_LINEAR);
153     }
154 #endif
155 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
156     if (a) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, a);
157 #endif
158
159     /* Copy the image to an OpenGL texture. */
160
161     glTexImage2D(GL_TEXTURE_2D, 0,
162                  format[b], W, H, 0,
163                  format[b], type[b], q ? q : p);
164
165     if (q) free(q);
166
167
168     return o;
169 }
170
171 static void *image_to_16bpp(unsigned char *data, int w, int h, int b)
172 {
173     unsigned char *p, *row;
174     int i, j;
175
176     if (b <= 2) return data;
177
178     if ((p = malloc(w * h * 2)))
179     {
180         for (i = 0; i < h; i++)
181         {
182             row = data + i * w * b;
183             for (j = 0; j < w; j++)
184             {
185                 unsigned short packed, *pp;
186                 if (b == 4)
187                     packed =
188                         (row[j * b] >> 4) << 12 |
189                         (row[j * b + 1] >> 4) << 8 |
190                         (row[j * b + 2] >> 4) << 4 |
191                         row[j * b + 3] >> 4;
192                 else
193                     packed =
194                         (row[j * b] >> 3) << 11 |
195                         (row[j * b + 1] >> 2) << 5 |
196                         (row[j * b + 2] >> 3);
197                 pp = (unsigned short *)&p[i * w * 2 + j * 2];
198                 *pp = packed;
199             }
200         }
201     }
202     free(data);
203     return p;
204 }
205
206 /*
207  * Load an image from the named file.  Return an OpenGL texture object.
208  */
209 GLuint make_image_from_file(const char *filename)
210 {
211     void  *p;
212     int    w;
213     int    h;
214     int    b;
215     GLuint o = 0;
216
217     /* Load the image. */
218
219     if ((p = image_load(filename, &w, &h, &b)))
220     {
221         p = image_to_16bpp(p, w, h, b);
222         o = make_texture(p, w, h, b);
223         free(p);
224     }
225
226     return o;
227 }
228
229 /*---------------------------------------------------------------------------*/
230
231 /*
232  * Render the given  string using the given font.   Transfer the image
233  * to a  surface of  power-of-2 size large  enough to fit  the string.
234  * Return an OpenGL texture object.
235  */
236 GLuint make_image_from_font(int *W, int *H,
237                             int *w, int *h,
238                             const char *text, TTF_Font *font)
239 {
240     GLuint o = 0;
241
242     /* Render the text. */
243
244     if (font && text && strlen(text) > 0)
245     {
246         SDL_Color    col = { 0xFF, 0xFF, 0xFF, 0xFF };
247         SDL_Surface *orig;
248
249         if ((orig = TTF_RenderUTF8_Blended(font, text, col)))
250         {
251             void *p;
252             int  w2;
253             int  h2;
254             int   b = orig->format->BitsPerPixel / 8;
255
256             SDL_Surface *src;
257             SDL_PixelFormat fmt;
258
259             fmt = *orig->format;
260
261             fmt.Rmask = RMASK;
262             fmt.Gmask = GMASK;
263             fmt.Bmask = BMASK;
264             fmt.Amask = AMASK;
265
266             if ((src = SDL_ConvertSurface(orig, &fmt, orig->flags)) == NULL)
267             {
268                 fprintf(stderr, _("Failed to convert SDL_ttf surface: %s\n"),
269                         SDL_GetError());
270
271                 /* Pretend everything's just fine. */
272
273                 src = orig;
274             }
275             else
276                 SDL_FreeSurface(orig);
277
278             /* Pad the text to power-of-two. */
279
280             p = image_next2(src->pixels, src->w, src->h, b, &w2, &h2);
281
282             if (w) *w = src->w;
283             if (h) *h = src->h;
284             if (W) *W = w2;
285             if (H) *H = h2;
286
287             /* Saturate the color channels.  Modulate ONLY in alpha. */
288
289             image_white(p, w2, h2, b);
290
291             /* Create the OpenGL texture object. */
292
293             p = image_to_16bpp(p, w2, h2, b);
294             o = make_texture(p, w2, h2, b);
295
296             free(p);
297             SDL_FreeSurface(src);
298         }
299     }
300     else
301     {
302         /* Empty string. */
303
304         if (w) *w = 0;
305         if (h) *h = 0;
306         if (W) *W = 0;
307         if (H) *H = 0;
308     }
309
310     return o;
311 }
312
313 /*---------------------------------------------------------------------------*/
314
315 /*
316  * Load an image from the named file.  Return an SDL surface.
317  */
318 SDL_Surface *load_surface(const char *filename)
319 {
320     void  *p;
321     int    w;
322     int    h;
323     int    b;
324
325     SDL_Surface *srf = NULL;
326
327     if ((p = image_load(filename, &w, &h, &b)))
328     {
329         void *q;
330
331         if ((q = image_flip(p, w, h, b, 0, 1)))
332             srf = SDL_CreateRGBSurfaceFrom(q, w, h, b * 8, w * b,
333                                            RMASK, GMASK, BMASK, AMASK);
334         free(p);
335     }
336     return srf;
337 }
338
339 /*---------------------------------------------------------------------------*/