Implement new shot name generation using a persistent index.
[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 *src;
206
207         if ((src = TTF_RenderUTF8_Blended(font, text, col)))
208         {
209             void *p;
210             int  w2;
211             int  h2;
212             int   b = src->format->BitsPerPixel / 8;
213
214             /* Pad the text to power-of-two. */
215
216             p = image_next2(src->pixels, src->w, src->h, b, &w2, &h2);
217
218             if (w) *w = src->w;
219             if (h) *h = src->h;
220             if (W) *W = w2;
221             if (H) *H = h2;
222
223             /* Saturate the color channels.  Modulate ONLY in alpha. */
224
225             image_white(p, w2, h2, b);
226
227             /* Create the OpenGL texture object. */
228
229             o = make_texture(p, w2, h2, b);
230
231             free(p);
232             SDL_FreeSurface(src);
233         }
234     }
235     else
236     {
237         /* Empty string. */
238
239         if (w) *w = 0;
240         if (h) *h = 0;
241         if (W) *W = 0;
242         if (H) *H = 0;
243     }
244
245     return o;
246 }
247
248 /*---------------------------------------------------------------------------*/
249
250 /*
251  * Load an image from the named file.  Return an SDL surface.
252  */
253 SDL_Surface *load_surface(const char *filename)
254 {
255     void  *p;
256     int    w;
257     int    h;
258     int    b;
259
260     Uint32 rmask;
261     Uint32 gmask;
262     Uint32 bmask;
263     Uint32 amask;
264
265 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
266     rmask = 0xFF000000;
267     gmask = 0x00FF0000;
268     bmask = 0x0000FF00;
269     amask = 0x000000FF;
270 #else
271     rmask = 0x000000FF;
272     gmask = 0x0000FF00;
273     bmask = 0x00FF0000;
274     amask = 0xFF000000;
275 #endif
276
277     if ((p = image_load(config_data(filename), &w, &h, &b)))
278         return SDL_CreateRGBSurfaceFrom(p, w, h, b * 8, w * b,
279                                         rmask, gmask, bmask, amask);
280     else
281         return NULL;
282 }
283
284 /*---------------------------------------------------------------------------*/