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