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