Applying paxed's patch. "This diff makes the image size cache in mapc
[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 <SDL_image.h>
18 #include <string.h>
19 #include <math.h>
20 #include <png.h>
21 #include <stdlib.h>
22
23 #include "glext.h"
24 #include "image.h"
25 #include "base_image.h"
26 #include "config.h"
27
28 /*---------------------------------------------------------------------------*/
29
30 void image_snap(char *filename, int w, int h)
31 {
32     FILE       *filep  = NULL;
33     png_structp writep = NULL;
34     png_infop   infop  = NULL;
35     png_bytep  *bytep  = NULL;
36
37     int i;
38
39     unsigned char *p = NULL;
40
41     /* Initialize all PNG export data structures. */
42
43     if (!(filep = fopen(filename, FMODE_WB)))
44         return;
45     if (!(writep = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
46         return;
47     if (!(infop = png_create_info_struct(writep)))
48         return;
49
50     /* Enable the default PNG error handler. */
51
52     if (setjmp(png_jmpbuf(writep)) == 0)
53     {
54         /* Initialize the PNG header. */
55
56         png_init_io (writep, filep);
57         png_set_IHDR(writep, infop, w, h, 8,
58                      PNG_COLOR_TYPE_RGB,
59                      PNG_INTERLACE_NONE,
60                      PNG_COMPRESSION_TYPE_DEFAULT,
61                      PNG_FILTER_TYPE_DEFAULT);
62
63         /* Allocate the pixel buffer and copy pixels there. */
64
65         if ((p = (unsigned char *) malloc(w * h * 4)))
66         {
67             glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, p);
68
69             /* Allocate and initialize the row pointers. */
70
71             if ((bytep = png_malloc(writep, h * sizeof (png_bytep))))
72             {
73                 for (i = 0; i < h; ++i)
74                     bytep[h - i - 1] = (png_bytep) (p + i * w * 3);
75
76                 png_set_rows (writep, infop, bytep);
77
78                 /* Write the PNG image file. */
79
80                 png_write_info(writep, infop);
81                 png_write_png (writep, infop, 0, NULL);
82
83                 free(bytep);
84             }
85             free(p);
86         }
87     }
88
89     /* Release all resources. */
90
91     png_destroy_write_struct(&writep, &infop);
92     fclose(filep);
93 }
94
95 void image_size(int *W, int *H, int w, int h)
96 {
97     *W = 1;
98     *H = 1;
99
100     while (*W < w) *W *= 2;
101     while (*H < h) *H *= 2;
102 }
103
104 /*---------------------------------------------------------------------------*/
105
106 /*
107  * Create on  OpenGL texture  object using the  given SDL  surface and
108  * format,  scaled  using the  current  scale  factor.  When  scaling,
109  * assume dimensions are used only for layout and lie about the size.
110  */
111 GLuint make_image_from_surf(int *w, int *h, SDL_Surface *s)
112 {
113     int    t = config_get_d(CONFIG_TEXTURES);
114     GLuint o = 0;
115
116     glGenTextures(1, &o);
117     glBindTexture(GL_TEXTURE_2D, o);
118
119     if (t > 1)
120     {
121         SDL_Surface *d = image_scale(s, t);
122
123         /* Load the scaled image. */
124
125         if (d->format->BitsPerPixel == 32)
126             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, d->w, d->h, 0,
127                          GL_RGBA, GL_UNSIGNED_BYTE, d->pixels);
128         else
129             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,  d->w, d->h, 0,
130                          GL_RGB,  GL_UNSIGNED_BYTE, d->pixels);
131
132         SDL_FreeSurface(d);
133     }
134     else
135     {
136         /* Load the unscaled image. */
137
138         if (s->format->BitsPerPixel == 32)
139             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s->w, s->h, 0,
140                          GL_RGBA, GL_UNSIGNED_BYTE, s->pixels);
141         else
142             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,  s->w, s->h, 0,
143                          GL_RGB,  GL_UNSIGNED_BYTE, s->pixels);
144     }
145
146     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
147     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
148
149     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
150     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
151
152     if (w) *w = s->w;
153     if (h) *h = s->h;
154
155     return o;
156 }
157
158 /*---------------------------------------------------------------------------*/
159
160 /*
161  * Load  an image  from the  named file.   If the  image is  not RGBA,
162  * convert it to RGBA.  Return an OpenGL texture object.
163  */
164 GLuint make_image_from_file(int *W, int *H,
165                             int *w, int *h, const char *name)
166 {
167     SDL_Surface *src;
168     SDL_Surface *dst;
169     SDL_Rect rect;
170
171     GLuint o = 0;
172
173     /* Load the file. */
174
175     if ((src = IMG_Load(config_data(name))))
176     {
177         int w2;
178         int h2;
179
180         image_size(&w2, &h2, src->w, src->h);
181
182         if (w) *w = src->w;
183         if (h) *h = src->h;
184
185         /* Create a new destination surface. */
186         
187         if ((dst = SDL_CreateRGBSurface(SDL_SWSURFACE, w2, h2, 32,
188                                         RMASK, GMASK, BMASK, AMASK)))
189         {
190             /* Copy source pixels to the center of the destination. */
191
192             rect.x = (Sint16) (w2 - src->w) / 2;
193             rect.y = (Sint16) (h2 - src->h) / 2;
194
195             SDL_SetAlpha(src, 0, 0);
196             SDL_BlitSurface(src, NULL, dst, &rect);
197
198             o = make_image_from_surf(W, H, dst);
199
200             SDL_FreeSurface(dst);
201         }
202         SDL_FreeSurface(src);
203     }
204     return o;
205 }
206
207 /*---------------------------------------------------------------------------*/
208
209 /*
210  * Render the given  string using the given font.   Transfer the image
211  * to a  surface of  power-of-2 size large  enough to fit  the string.
212  * Return an OpenGL texture object.
213  */
214 GLuint make_image_from_font(int *W, int *H,
215                             int *w, int *h, const char *text, TTF_Font *font, int k)
216 {
217     SDL_Color fg = { 0xFF, 0xFF, 0xFF, 0xFF };
218
219     SDL_Surface *src;
220     SDL_Surface *dst;
221     SDL_Rect rect;
222
223     GLuint o = 0;
224
225     /* Render the text. */
226
227     if (text && strlen(text) > 0)
228     {
229         if ((src = TTF_RenderUTF8_Blended(font, text, fg)))
230         {
231             int w2;
232             int h2;
233
234             image_size(&w2, &h2, src->w, src->h);
235
236             if (w) *w = src->w;
237             if (h) *h = src->h;
238
239             /* Create a new destination surface. */
240             
241             if ((dst = SDL_CreateRGBSurface(SDL_SWSURFACE, w2, h2, 32,
242                                             RMASK, GMASK, BMASK, AMASK)))
243             {
244                 /* Copy source pixels to the center of the destination. */
245
246                 rect.x = (Sint16) (w2 - src->w) / 2;
247                 rect.y = (Sint16) (h2 - src->h) / 2;
248
249                 SDL_SetAlpha(src, 0, 0);
250                 SDL_BlitSurface(src, NULL, dst, &rect);
251
252                 image_white(dst);
253
254                 o = make_image_from_surf(W, H, dst);
255
256                 SDL_FreeSurface(dst);
257             }
258             SDL_FreeSurface(src);
259         }
260
261         if (W) *W *= k;
262         if (H) *H *= k;
263         if (w) *w *= k;
264         if (h) *h *= k;
265     }
266     else
267     {
268         if (W) *W = 0;
269         if (H) *H = 0;
270         if (w) *w = 0;
271         if (h) *h = 0;
272     }
273
274     return o;
275 }
276
277 /*---------------------------------------------------------------------------*/