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