|
1 /* |
|
2 SDL_image: An example image loading library for use with SDL |
|
3 Copyright (C) 1997-2009 Sam Lantinga |
|
4 |
|
5 This library is free software; you can redistribute it and/or |
|
6 modify it under the terms of the GNU Lesser General Public |
|
7 License as published by the Free Software Foundation; either |
|
8 version 2.1 of the License, or (at your option) any later version. |
|
9 |
|
10 This library is distributed in the hope that it will be useful, |
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 Lesser General Public License for more details. |
|
14 |
|
15 You should have received a copy of the GNU Lesser General Public |
|
16 License along with this library; if not, write to the Free Software |
|
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 |
|
19 Sam Lantinga |
|
20 slouken@libsdl.org |
|
21 */ |
|
22 |
|
23 /* This is a PNG image file loading framework */ |
|
24 |
|
25 #include <stdlib.h> |
|
26 #include <stdio.h> |
|
27 |
|
28 #include "SDL_image.h" |
|
29 |
|
30 |
|
31 /*============================================================================= |
|
32 File: SDL_png.c |
|
33 Purpose: A PNG loader and saver for the SDL library |
|
34 Revision: |
|
35 Created by: Philippe Lavoie (2 November 1998) |
|
36 lavoie@zeus.genie.uottawa.ca |
|
37 Modified by: |
|
38 |
|
39 Copyright notice: |
|
40 Copyright (C) 1998 Philippe Lavoie |
|
41 |
|
42 This library is free software; you can redistribute it and/or |
|
43 modify it under the terms of the GNU Library General Public |
|
44 License as published by the Free Software Foundation; either |
|
45 version 2 of the License, or (at your option) any later version. |
|
46 |
|
47 This library is distributed in the hope that it will be useful, |
|
48 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
49 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
50 Library General Public License for more details. |
|
51 |
|
52 You should have received a copy of the GNU Library General Public |
|
53 License along with this library; if not, write to the Free |
|
54 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
55 |
|
56 Comments: The load and save routine are basically the ones you can find |
|
57 in the example.c file from the libpng distribution. |
|
58 |
|
59 Changes: |
|
60 5/17/99 - Modified to use the new SDL data sources - Sam Lantinga |
|
61 |
|
62 =============================================================================*/ |
|
63 |
|
64 #include "SDL_endian.h" |
|
65 |
|
66 #ifdef macintosh |
|
67 #define MACOS |
|
68 #endif |
|
69 #include "png.h" |
|
70 |
|
71 |
|
72 static struct { |
|
73 int loaded; |
|
74 void *handle; |
|
75 png_infop (*png_create_info_struct) (png_structp png_ptr); |
|
76 png_structp (*png_create_read_struct) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn); |
|
77 void (*png_destroy_read_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr); |
|
78 png_uint_32 (*png_get_IHDR) (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, int *bit_depth, int *color_type, int *interlace_method, int *compression_method, int *filter_method); |
|
79 png_voidp (*png_get_io_ptr) (png_structp png_ptr); |
|
80 png_uint_32 (*png_get_tRNS) (png_structp png_ptr, png_infop info_ptr, png_bytep *trans, int *num_trans, png_color_16p *trans_values); |
|
81 png_uint_32 (*png_get_valid) (png_structp png_ptr, png_infop info_ptr, png_uint_32 flag); |
|
82 void (*png_read_image) (png_structp png_ptr, png_bytepp image); |
|
83 void (*png_read_info) (png_structp png_ptr, png_infop info_ptr); |
|
84 void (*png_read_update_info) (png_structp png_ptr, png_infop info_ptr); |
|
85 void (*png_set_expand) (png_structp png_ptr); |
|
86 void (*png_set_gray_to_rgb) (png_structp png_ptr); |
|
87 void (*png_set_packing) (png_structp png_ptr); |
|
88 void (*png_set_read_fn) (png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn); |
|
89 void (*png_set_strip_16) (png_structp png_ptr); |
|
90 int (*png_sig_cmp) (png_bytep sig, png_size_t start, png_size_t num_to_check); |
|
91 } lib; |
|
92 |
|
93 #ifdef LOAD_PNG_DYNAMIC |
|
94 int IMG_InitPNG() |
|
95 { |
|
96 if ( lib.loaded == 0 ) { |
|
97 lib.handle = SDL_LoadObject(LOAD_PNG_DYNAMIC); |
|
98 if ( lib.handle == NULL ) { |
|
99 return -1; |
|
100 } |
|
101 lib.png_create_info_struct = |
|
102 (png_infop (*) (png_structp)) |
|
103 SDL_LoadFunction(lib.handle, "png_create_info_struct"); |
|
104 if ( lib.png_create_info_struct == NULL ) { |
|
105 SDL_UnloadObject(lib.handle); |
|
106 return -1; |
|
107 } |
|
108 lib.png_create_read_struct = |
|
109 (png_structp (*) (png_const_charp, png_voidp, png_error_ptr, png_error_ptr)) |
|
110 SDL_LoadFunction(lib.handle, "png_create_read_struct"); |
|
111 if ( lib.png_create_read_struct == NULL ) { |
|
112 SDL_UnloadObject(lib.handle); |
|
113 return -1; |
|
114 } |
|
115 lib.png_destroy_read_struct = |
|
116 (void (*) (png_structpp, png_infopp, png_infopp)) |
|
117 SDL_LoadFunction(lib.handle, "png_destroy_read_struct"); |
|
118 if ( lib.png_destroy_read_struct == NULL ) { |
|
119 SDL_UnloadObject(lib.handle); |
|
120 return -1; |
|
121 } |
|
122 lib.png_get_IHDR = |
|
123 (png_uint_32 (*) (png_structp, png_infop, png_uint_32 *, png_uint_32 *, int *, int *, int *, int *, int *)) |
|
124 SDL_LoadFunction(lib.handle, "png_get_IHDR"); |
|
125 if ( lib.png_get_IHDR == NULL ) { |
|
126 SDL_UnloadObject(lib.handle); |
|
127 return -1; |
|
128 } |
|
129 lib.png_get_io_ptr = |
|
130 (png_voidp (*) (png_structp)) |
|
131 SDL_LoadFunction(lib.handle, "png_get_io_ptr"); |
|
132 if ( lib.png_get_io_ptr == NULL ) { |
|
133 SDL_UnloadObject(lib.handle); |
|
134 return -1; |
|
135 } |
|
136 lib.png_get_tRNS = |
|
137 (png_uint_32 (*) (png_structp, png_infop, png_bytep *, int *, png_color_16p *)) |
|
138 SDL_LoadFunction(lib.handle, "png_get_tRNS"); |
|
139 if ( lib.png_get_tRNS == NULL ) { |
|
140 SDL_UnloadObject(lib.handle); |
|
141 return -1; |
|
142 } |
|
143 lib.png_get_valid = |
|
144 (png_uint_32 (*) (png_structp, png_infop, png_uint_32)) |
|
145 SDL_LoadFunction(lib.handle, "png_get_valid"); |
|
146 if ( lib.png_get_valid == NULL ) { |
|
147 SDL_UnloadObject(lib.handle); |
|
148 return -1; |
|
149 } |
|
150 lib.png_read_image = |
|
151 (void (*) (png_structp, png_bytepp)) |
|
152 SDL_LoadFunction(lib.handle, "png_read_image"); |
|
153 if ( lib.png_read_image == NULL ) { |
|
154 SDL_UnloadObject(lib.handle); |
|
155 return -1; |
|
156 } |
|
157 lib.png_read_info = |
|
158 (void (*) (png_structp, png_infop)) |
|
159 SDL_LoadFunction(lib.handle, "png_read_info"); |
|
160 if ( lib.png_read_info == NULL ) { |
|
161 SDL_UnloadObject(lib.handle); |
|
162 return -1; |
|
163 } |
|
164 lib.png_read_update_info = |
|
165 (void (*) (png_structp, png_infop)) |
|
166 SDL_LoadFunction(lib.handle, "png_read_update_info"); |
|
167 if ( lib.png_read_update_info == NULL ) { |
|
168 SDL_UnloadObject(lib.handle); |
|
169 return -1; |
|
170 } |
|
171 lib.png_set_expand = |
|
172 (void (*) (png_structp)) |
|
173 SDL_LoadFunction(lib.handle, "png_set_expand"); |
|
174 if ( lib.png_set_expand == NULL ) { |
|
175 SDL_UnloadObject(lib.handle); |
|
176 return -1; |
|
177 } |
|
178 lib.png_set_gray_to_rgb = |
|
179 (void (*) (png_structp)) |
|
180 SDL_LoadFunction(lib.handle, "png_set_gray_to_rgb"); |
|
181 if ( lib.png_set_gray_to_rgb == NULL ) { |
|
182 SDL_UnloadObject(lib.handle); |
|
183 return -1; |
|
184 } |
|
185 lib.png_set_packing = |
|
186 (void (*) (png_structp)) |
|
187 SDL_LoadFunction(lib.handle, "png_set_packing"); |
|
188 if ( lib.png_set_packing == NULL ) { |
|
189 SDL_UnloadObject(lib.handle); |
|
190 return -1; |
|
191 } |
|
192 lib.png_set_read_fn = |
|
193 (void (*) (png_structp, png_voidp, png_rw_ptr)) |
|
194 SDL_LoadFunction(lib.handle, "png_set_read_fn"); |
|
195 if ( lib.png_set_read_fn == NULL ) { |
|
196 SDL_UnloadObject(lib.handle); |
|
197 return -1; |
|
198 } |
|
199 lib.png_set_strip_16 = |
|
200 (void (*) (png_structp)) |
|
201 SDL_LoadFunction(lib.handle, "png_set_strip_16"); |
|
202 if ( lib.png_set_strip_16 == NULL ) { |
|
203 SDL_UnloadObject(lib.handle); |
|
204 return -1; |
|
205 } |
|
206 lib.png_sig_cmp = |
|
207 (int (*) (png_bytep, png_size_t, png_size_t)) |
|
208 SDL_LoadFunction(lib.handle, "png_sig_cmp"); |
|
209 if ( lib.png_sig_cmp == NULL ) { |
|
210 SDL_UnloadObject(lib.handle); |
|
211 return -1; |
|
212 } |
|
213 } |
|
214 ++lib.loaded; |
|
215 |
|
216 return 0; |
|
217 } |
|
218 void IMG_QuitPNG() |
|
219 { |
|
220 if ( lib.loaded == 0 ) { |
|
221 return; |
|
222 } |
|
223 if ( lib.loaded == 1 ) { |
|
224 SDL_UnloadObject(lib.handle); |
|
225 } |
|
226 --lib.loaded; |
|
227 } |
|
228 #else |
|
229 int IMG_InitPNG() |
|
230 { |
|
231 if ( lib.loaded == 0 ) { |
|
232 lib.png_create_info_struct = png_create_info_struct; |
|
233 lib.png_create_read_struct = png_create_read_struct; |
|
234 lib.png_destroy_read_struct = png_destroy_read_struct; |
|
235 lib.png_get_IHDR = png_get_IHDR; |
|
236 lib.png_get_io_ptr = png_get_io_ptr; |
|
237 lib.png_get_tRNS = png_get_tRNS; |
|
238 lib.png_get_valid = png_get_valid; |
|
239 lib.png_read_image = png_read_image; |
|
240 lib.png_read_info = png_read_info; |
|
241 lib.png_read_update_info = png_read_update_info; |
|
242 lib.png_set_expand = png_set_expand; |
|
243 lib.png_set_gray_to_rgb = png_set_gray_to_rgb; |
|
244 lib.png_set_packing = png_set_packing; |
|
245 lib.png_set_read_fn = png_set_read_fn; |
|
246 lib.png_set_strip_16 = png_set_strip_16; |
|
247 lib.png_sig_cmp = png_sig_cmp; |
|
248 } |
|
249 ++lib.loaded; |
|
250 |
|
251 return 0; |
|
252 } |
|
253 void IMG_QuitPNG() |
|
254 { |
|
255 if ( lib.loaded == 0 ) { |
|
256 return; |
|
257 } |
|
258 if ( lib.loaded == 1 ) { |
|
259 } |
|
260 --lib.loaded; |
|
261 } |
|
262 #endif /* LOAD_PNG_DYNAMIC */ |
|
263 |
|
264 /* See if an image is contained in a data source */ |
|
265 int IMG_isPNG(SDL_RWops *src) |
|
266 { |
|
267 int start; |
|
268 int is_PNG; |
|
269 Uint8 magic[4]; |
|
270 |
|
271 if ( !src ) |
|
272 return 0; |
|
273 start = SDL_RWtell(src); |
|
274 is_PNG = 0; |
|
275 if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) { |
|
276 if ( magic[0] == 0x89 && |
|
277 magic[1] == 'P' && |
|
278 magic[2] == 'N' && |
|
279 magic[3] == 'G' ) { |
|
280 is_PNG = 1; |
|
281 } |
|
282 } |
|
283 SDL_RWseek(src, start, RW_SEEK_SET); |
|
284 return(is_PNG); |
|
285 } |
|
286 |
|
287 /* Load a PNG type image from an SDL datasource */ |
|
288 static void png_read_data(png_structp ctx, png_bytep area, png_size_t size) |
|
289 { |
|
290 SDL_RWops *src; |
|
291 |
|
292 src = (SDL_RWops *)lib.png_get_io_ptr(ctx); |
|
293 SDL_RWread(src, area, size, 1); |
|
294 } |
|
295 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src) |
|
296 { |
|
297 int start; |
|
298 const char *error; |
|
299 SDL_Surface *volatile surface; |
|
300 png_structp png_ptr; |
|
301 png_infop info_ptr; |
|
302 png_uint_32 width, height; |
|
303 int bit_depth, color_type, interlace_type; |
|
304 Uint32 Rmask; |
|
305 Uint32 Gmask; |
|
306 Uint32 Bmask; |
|
307 Uint32 Amask; |
|
308 SDL_Palette *palette; |
|
309 png_bytep *volatile row_pointers; |
|
310 int row, i; |
|
311 volatile int ckey = -1; |
|
312 png_color_16 *transv; |
|
313 |
|
314 if ( !src ) { |
|
315 /* The error message has been set in SDL_RWFromFile */ |
|
316 return NULL; |
|
317 } |
|
318 start = SDL_RWtell(src); |
|
319 |
|
320 if ( !IMG_Init(IMG_INIT_PNG) ) { |
|
321 return NULL; |
|
322 } |
|
323 |
|
324 /* Initialize the data we will clean up when we're done */ |
|
325 error = NULL; |
|
326 png_ptr = NULL; info_ptr = NULL; row_pointers = NULL; surface = NULL; |
|
327 |
|
328 /* Create the PNG loading context structure */ |
|
329 png_ptr = lib.png_create_read_struct(PNG_LIBPNG_VER_STRING, |
|
330 NULL,NULL,NULL); |
|
331 if (png_ptr == NULL){ |
|
332 error = "Couldn't allocate memory for PNG file or incompatible PNG dll"; |
|
333 goto done; |
|
334 } |
|
335 |
|
336 /* Allocate/initialize the memory for image information. REQUIRED. */ |
|
337 info_ptr = lib.png_create_info_struct(png_ptr); |
|
338 if (info_ptr == NULL) { |
|
339 error = "Couldn't create image information for PNG file"; |
|
340 goto done; |
|
341 } |
|
342 |
|
343 /* Set error handling if you are using setjmp/longjmp method (this is |
|
344 * the normal method of doing things with libpng). REQUIRED unless you |
|
345 * set up your own error handlers in png_create_read_struct() earlier. |
|
346 */ |
|
347 if ( setjmp(png_ptr->jmpbuf) ) { |
|
348 error = "Error reading the PNG file."; |
|
349 goto done; |
|
350 } |
|
351 |
|
352 /* Set up the input control */ |
|
353 lib.png_set_read_fn(png_ptr, src, png_read_data); |
|
354 |
|
355 /* Read PNG header info */ |
|
356 lib.png_read_info(png_ptr, info_ptr); |
|
357 lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, |
|
358 &color_type, &interlace_type, NULL, NULL); |
|
359 |
|
360 /* tell libpng to strip 16 bit/color files down to 8 bits/color */ |
|
361 lib.png_set_strip_16(png_ptr) ; |
|
362 |
|
363 /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single |
|
364 * byte into separate bytes (useful for paletted and grayscale images). |
|
365 */ |
|
366 lib.png_set_packing(png_ptr); |
|
367 |
|
368 /* scale greyscale values to the range 0..255 */ |
|
369 if(color_type == PNG_COLOR_TYPE_GRAY) |
|
370 lib.png_set_expand(png_ptr); |
|
371 |
|
372 /* For images with a single "transparent colour", set colour key; |
|
373 if more than one index has transparency, or if partially transparent |
|
374 entries exist, use full alpha channel */ |
|
375 if (lib.png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { |
|
376 int num_trans; |
|
377 Uint8 *trans; |
|
378 lib.png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, |
|
379 &transv); |
|
380 if(color_type == PNG_COLOR_TYPE_PALETTE) { |
|
381 /* Check if all tRNS entries are opaque except one */ |
|
382 int t = -1; |
|
383 for(i = 0; i < num_trans; i++) |
|
384 if(trans[i] == 0) { |
|
385 if(t >= 0) |
|
386 break; |
|
387 t = i; |
|
388 } else if(trans[i] != 255) |
|
389 break; |
|
390 if(i == num_trans) { |
|
391 /* exactly one transparent index */ |
|
392 ckey = t; |
|
393 } else { |
|
394 /* more than one transparent index, or translucency */ |
|
395 lib.png_set_expand(png_ptr); |
|
396 } |
|
397 } else |
|
398 ckey = 0; /* actual value will be set later */ |
|
399 } |
|
400 |
|
401 if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) |
|
402 lib.png_set_gray_to_rgb(png_ptr); |
|
403 |
|
404 lib.png_read_update_info(png_ptr, info_ptr); |
|
405 |
|
406 lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, |
|
407 &color_type, &interlace_type, NULL, NULL); |
|
408 |
|
409 /* Allocate the SDL surface to hold the image */ |
|
410 Rmask = Gmask = Bmask = Amask = 0 ; |
|
411 if ( color_type != PNG_COLOR_TYPE_PALETTE ) { |
|
412 if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) { |
|
413 Rmask = 0x000000FF; |
|
414 Gmask = 0x0000FF00; |
|
415 Bmask = 0x00FF0000; |
|
416 Amask = (info_ptr->channels == 4) ? 0xFF000000 : 0; |
|
417 } else { |
|
418 int s = (info_ptr->channels == 4) ? 0 : 8; |
|
419 Rmask = 0xFF000000 >> s; |
|
420 Gmask = 0x00FF0000 >> s; |
|
421 Bmask = 0x0000FF00 >> s; |
|
422 Amask = 0x000000FF >> s; |
|
423 } |
|
424 } |
|
425 surface = SDL_AllocSurface(SDL_SWSURFACE, width, height, |
|
426 bit_depth*info_ptr->channels, Rmask,Gmask,Bmask,Amask); |
|
427 if ( surface == NULL ) { |
|
428 error = "Out of memory"; |
|
429 goto done; |
|
430 } |
|
431 |
|
432 if(ckey != -1) { |
|
433 if(color_type != PNG_COLOR_TYPE_PALETTE) |
|
434 /* FIXME: Should these be truncated or shifted down? */ |
|
435 ckey = SDL_MapRGB(surface->format, |
|
436 (Uint8)transv->red, |
|
437 (Uint8)transv->green, |
|
438 (Uint8)transv->blue); |
|
439 SDL_SetColorKey(surface, SDL_SRCCOLORKEY, ckey); |
|
440 } |
|
441 |
|
442 /* Create the array of pointers to image data */ |
|
443 row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*height); |
|
444 if ( (row_pointers == NULL) ) { |
|
445 error = "Out of memory"; |
|
446 goto done; |
|
447 } |
|
448 for (row = 0; row < (int)height; row++) { |
|
449 row_pointers[row] = (png_bytep) |
|
450 (Uint8 *)surface->pixels + row*surface->pitch; |
|
451 } |
|
452 |
|
453 /* Read the entire image in one go */ |
|
454 lib.png_read_image(png_ptr, row_pointers); |
|
455 |
|
456 /* and we're done! (png_read_end() can be omitted if no processing of |
|
457 * post-IDAT text/time/etc. is desired) |
|
458 * In some cases it can't read PNG's created by some popular programs (ACDSEE), |
|
459 * we do not want to process comments, so we omit png_read_end |
|
460 |
|
461 lib.png_read_end(png_ptr, info_ptr); |
|
462 */ |
|
463 |
|
464 /* Load the palette, if any */ |
|
465 palette = surface->format->palette; |
|
466 if ( palette ) { |
|
467 if(color_type == PNG_COLOR_TYPE_GRAY) { |
|
468 palette->ncolors = 256; |
|
469 for(i = 0; i < 256; i++) { |
|
470 palette->colors[i].r = i; |
|
471 palette->colors[i].g = i; |
|
472 palette->colors[i].b = i; |
|
473 } |
|
474 } else if (info_ptr->num_palette > 0 ) { |
|
475 palette->ncolors = info_ptr->num_palette; |
|
476 for( i=0; i<info_ptr->num_palette; ++i ) { |
|
477 palette->colors[i].b = info_ptr->palette[i].blue; |
|
478 palette->colors[i].g = info_ptr->palette[i].green; |
|
479 palette->colors[i].r = info_ptr->palette[i].red; |
|
480 } |
|
481 } |
|
482 } |
|
483 |
|
484 done: /* Clean up and return */ |
|
485 if ( png_ptr ) { |
|
486 lib.png_destroy_read_struct(&png_ptr, |
|
487 info_ptr ? &info_ptr : (png_infopp)0, |
|
488 (png_infopp)0); |
|
489 } |
|
490 if ( row_pointers ) { |
|
491 free(row_pointers); |
|
492 } |
|
493 if ( error ) { |
|
494 SDL_RWseek(src, start, RW_SEEK_SET); |
|
495 if ( surface ) { |
|
496 SDL_FreeSurface(surface); |
|
497 surface = NULL; |
|
498 } |
|
499 IMG_SetError(error); |
|
500 } |
|
501 return(surface); |
|
502 } |