Make teleport particles visible in batch level shots
[neverball] / share / base_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 <png.h>
16 #include <jpeglib.h>
17 #include <stdlib.h>
18 #include <assert.h>
19
20 #include "glext.h"
21 #include "base_config.h"
22 #include "base_image.h"
23
24 /*---------------------------------------------------------------------------*/
25
26 void image_size(int *W, int *H, int w, int h)
27 {
28     /* Round the image size up to the next power-of-two. */
29
30     *W = w ? 1 : 0;
31     *H = h ? 1 : 0;
32
33     while (*W < w) *W *= 2;
34     while (*H < h) *H *= 2;
35 }
36
37 /*---------------------------------------------------------------------------*/
38
39 static void *image_load_png(const char *filename, int *width,
40                                                   int *height,
41                                                   int *bytes)
42 {
43     FILE *fp;
44
45     png_structp readp = NULL;
46     png_infop   infop = NULL;
47     png_bytep  *bytep = NULL;
48     unsigned char  *p = NULL;
49
50     /* Initialize all PNG import data structures. */
51
52     if (!(fp = fopen(filename, FMODE_RB)))
53         return NULL;
54
55     if (!(readp = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
56         return NULL;
57
58     if (!(infop = png_create_info_struct(readp)))
59         return NULL;
60
61     /* Enable the default PNG error handler. */
62
63     if (setjmp(png_jmpbuf(readp)) == 0)
64     {
65         int w, h, b, i;
66
67         /* Read the PNG header. */
68
69         png_init_io(readp, fp);
70         png_read_info(readp, infop);
71
72         png_set_expand(readp);
73         png_set_strip_16(readp);
74         png_set_packing(readp);
75
76         png_read_update_info(readp, infop);
77
78         /* Extract and check image properties. */
79
80         w = (int) png_get_image_width (readp, infop);
81         h = (int) png_get_image_height(readp, infop);
82
83         switch (png_get_color_type(readp, infop))
84         {
85         case PNG_COLOR_TYPE_GRAY:       b = 1; break;
86         case PNG_COLOR_TYPE_GRAY_ALPHA: b = 2; break;
87         case PNG_COLOR_TYPE_RGB:        b = 3; break;
88         case PNG_COLOR_TYPE_RGB_ALPHA:  b = 4; break;
89
90         default: longjmp(png_jmpbuf(readp), -1);
91         }
92
93         if (!(bytep = png_malloc(readp, h * png_sizeof(png_bytep))))
94             longjmp(png_jmpbuf(readp), -1);
95
96         /* Allocate the final pixel buffer and read pixels there. */
97
98         if ((p = (unsigned char *) malloc(w * h * b)))
99         {
100             for (i = 0; i < h; i++)
101                 bytep[i] = p + w * b * (h - i - 1);
102
103             png_read_image(readp, bytep);
104             png_read_end(readp, NULL);
105
106             if (width)  *width  = w;
107             if (height) *height = h;
108             if (bytes)  *bytes  = b;
109         }
110
111         png_free(readp, bytep);
112     }
113     else p = NULL;
114
115     /* Free all resources. */
116
117     png_destroy_read_struct(&readp, &infop, NULL);
118     fclose(fp);
119
120     return p;
121 }
122
123 static void *image_load_jpg(const char *filename, int *width,
124                                                   int *height,
125                                                   int *bytes)
126 {
127     GLubyte *p = NULL;
128     FILE   *fp;
129
130     if ((fp = fopen(filename, FMODE_RB)))
131     {
132         struct jpeg_decompress_struct cinfo;
133         struct jpeg_error_mgr         jerr;
134
135         int w, h, b, i = 0;
136
137         /* Initialize the JPG decompressor. */
138
139         cinfo.err = jpeg_std_error(&jerr);
140         jpeg_create_decompress(&cinfo);
141         jpeg_stdio_src(&cinfo, fp);
142
143         /* Grab the JPG header info. */
144
145         jpeg_read_header(&cinfo, TRUE);
146         jpeg_start_decompress(&cinfo);
147
148         w = cinfo.output_width;
149         h = cinfo.output_height;
150         b = cinfo.output_components;
151
152         /* Allocate the final pixel buffer and copy pixels there. */
153
154         if ((p = (GLubyte *) malloc (w * h * b)))
155         {
156             while (cinfo.output_scanline < cinfo.output_height)
157             {
158                 GLubyte *buffer = p + w * b * (h - i - 1);
159                 i += jpeg_read_scanlines(&cinfo, &buffer, 1);
160             }
161
162             if (width)  *width  = w;
163             if (height) *height = h;
164             if (bytes)  *bytes  = b;
165         }
166
167         jpeg_finish_decompress(&cinfo);
168         jpeg_destroy_decompress(&cinfo);
169
170         fclose(fp);
171     }
172
173     return p;
174 }
175
176 void *image_load(const char *filename, int *width,
177                                        int *height,
178                                        int *bytes)
179 {
180     const char *ext = filename + strlen(filename) - 4;
181
182     if      (strcmp(ext, ".png") == 0 || strcmp(ext, ".PNG") == 0)
183         return image_load_png(filename, width, height, bytes);
184     else if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".JPG") == 0)
185         return image_load_jpg(filename, width, height, bytes);
186
187     return NULL;
188 }
189
190 /*---------------------------------------------------------------------------*/
191
192 /*
193  * Allocate and return a power-of-two image buffer with the given pixel buffer
194  * centered within in.
195  */
196 void *image_next2(const void *p, int w, int h, int b, int *w2, int *h2)
197 {
198     unsigned char *src = (unsigned char *) p;
199     unsigned char *dst = NULL;
200
201     int W;
202     int H;
203
204     image_size(&W, &H, w, h);
205
206     if ((dst = (unsigned char *) calloc(W * H * b, sizeof (unsigned char))))
207     {
208         int r, dr = (H - h) / 2;
209         int c, dc = (W - w) / 2;
210         int i;
211
212         for (r = 0; r < h; ++r)
213             for (c = 0; c < w; ++c)
214                 for (i = 0; i < b; ++i)
215                 {
216                     int R = r + dr;
217                     int C = c + dc;
218
219                     dst[(R * W + C) * b + i] = src[(r * w + c) * b + i];
220                 }
221
222         if (w2) *w2 = W;
223         if (h2) *h2 = H;
224     }
225
226     return dst;
227 }
228
229 /*
230  * Allocate and return a new down-sampled image buffer.
231  */
232 void *image_scale(const void *p, int w, int h, int b, int *wn, int *hn, int n)
233 {
234     unsigned char *src = (unsigned char *) p;
235     unsigned char *dst = NULL;
236
237     int W = w / n;
238     int H = h / n;
239
240     if ((dst = (unsigned char *) calloc(W * H * b, sizeof (unsigned char))))
241     {
242         int si, di;
243         int sj, dj;
244         int i;
245
246         /* Iterate each component of each destination pixel. */
247
248         for (di = 0; di < H; di++)
249             for (dj = 0; dj < W; dj++)
250                 for (i = 0; i < b; i++)
251                 {
252                     int c = 0;
253
254                     /* Average the NxN source pixel block for each. */
255
256                     for (si = di * n; si < (di + 1) * n; si++)
257                         for (sj = dj * n; sj < (dj + 1) * n; sj++)
258                             c += src[(si * w + sj) * b + i];
259
260                     dst[(di * W + dj) * b + i] =
261                         (unsigned char) (c / (n * n));
262                 }
263
264         if (wn) *wn = W;
265         if (hn) *hn = H;
266     }
267
268     return dst;
269 }
270
271 /*
272  * Whiten the RGB channels of the given image without touching any alpha.
273  */
274 void image_white(void *p, int w, int h, int b)
275 {
276     unsigned char *s = (unsigned char *) p;
277
278     int r;
279     int c;
280
281     for (r = 0; r < h; r++)
282         for (c = 0; c < w; c++)
283         {
284             int k = (r * w + c) * b;
285
286             s[k + 0] = 0xFF;
287
288             if (b > 2)
289             {
290                 s[k + 1] = 0xFF;
291                 s[k + 2] = 0xFF;
292             }
293         }
294 }
295
296 /*
297  * Allocate and return an image buffer of the given image flipped horizontally
298  * and/or vertically.
299  */
300 void *image_flip(const void *p, int w, int h, int b, int hflip, int vflip)
301 {
302     unsigned char *q;
303
304     assert(hflip || vflip);
305
306     if (!p)
307         return NULL;
308
309     if ((q = malloc(w * b * h)))
310     {
311         int r, c, i;
312
313         for (r = 0; r < h; r++)
314             for (c = 0; c < w; c++)
315                 for (i = 0; i < b; i++)
316                 {
317                     int pr = vflip ? h - r - 1 : r;
318                     int pc = hflip ? w - c - 1 : c;
319
320                     int qi = r  * w * b + c  * b + i;
321                     int pi = pr * w * b + pc * b + i;
322
323                     q[qi] = ((const unsigned char *) p)[pi];
324                 }
325         return q;
326     }
327     return NULL;
328 }
329
330 /*---------------------------------------------------------------------------*/