2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2009 Sam Lantinga
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.
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.
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
23 /* This is a PNG image file loading framework */
28 #include "SDL_image.h"
32 /*=============================================================================
34 Purpose: A PNG loader and saver for the SDL library
36 Created by: Philippe Lavoie (2 November 1998)
37 lavoie@zeus.genie.uottawa.ca
41 Copyright (C) 1998 Philippe Lavoie
43 This library is free software; you can redistribute it and/or
44 modify it under the terms of the GNU Library General Public
45 License as published by the Free Software Foundation; either
46 version 2 of the License, or (at your option) any later version.
48 This library is distributed in the hope that it will be useful,
49 but WITHOUT ANY WARRANTY; without even the implied warranty of
50 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
51 Library General Public License for more details.
53 You should have received a copy of the GNU Library General Public
54 License along with this library; if not, write to the Free
55 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
57 Comments: The load and save routine are basically the ones you can find
58 in the example.c file from the libpng distribution.
61 5/17/99 - Modified to use the new SDL data sources - Sam Lantinga
63 =============================================================================*/
65 #include "SDL_endian.h"
76 png_infop (*png_create_info_struct) (png_structp png_ptr);
77 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);
78 void (*png_destroy_read_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr);
79 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);
80 png_voidp (*png_get_io_ptr) (png_structp png_ptr);
81 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);
82 png_uint_32 (*png_get_valid) (png_structp png_ptr, png_infop info_ptr, png_uint_32 flag);
83 void (*png_read_image) (png_structp png_ptr, png_bytepp image);
84 void (*png_read_info) (png_structp png_ptr, png_infop info_ptr);
85 void (*png_read_update_info) (png_structp png_ptr, png_infop info_ptr);
86 void (*png_set_expand) (png_structp png_ptr);
87 void (*png_set_gray_to_rgb) (png_structp png_ptr);
88 void (*png_set_packing) (png_structp png_ptr);
89 void (*png_set_read_fn) (png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn);
90 void (*png_set_strip_16) (png_structp png_ptr);
91 int (*png_sig_cmp) (png_bytep sig, png_size_t start, png_size_t num_to_check);
94 #ifdef LOAD_PNG_DYNAMIC
97 if ( lib.loaded == 0 ) {
98 lib.handle = SDL_LoadObject(LOAD_PNG_DYNAMIC);
99 if ( lib.handle == NULL ) {
102 lib.png_create_info_struct =
103 (png_infop (*) (png_structp))
104 SDL_LoadFunction(lib.handle, "png_create_info_struct");
105 if ( lib.png_create_info_struct == NULL ) {
106 SDL_UnloadObject(lib.handle);
109 lib.png_create_read_struct =
110 (png_structp (*) (png_const_charp, png_voidp, png_error_ptr, png_error_ptr))
111 SDL_LoadFunction(lib.handle, "png_create_read_struct");
112 if ( lib.png_create_read_struct == NULL ) {
113 SDL_UnloadObject(lib.handle);
116 lib.png_destroy_read_struct =
117 (void (*) (png_structpp, png_infopp, png_infopp))
118 SDL_LoadFunction(lib.handle, "png_destroy_read_struct");
119 if ( lib.png_destroy_read_struct == NULL ) {
120 SDL_UnloadObject(lib.handle);
124 (png_uint_32 (*) (png_structp, png_infop, png_uint_32 *, png_uint_32 *, int *, int *, int *, int *, int *))
125 SDL_LoadFunction(lib.handle, "png_get_IHDR");
126 if ( lib.png_get_IHDR == NULL ) {
127 SDL_UnloadObject(lib.handle);
131 (png_voidp (*) (png_structp))
132 SDL_LoadFunction(lib.handle, "png_get_io_ptr");
133 if ( lib.png_get_io_ptr == NULL ) {
134 SDL_UnloadObject(lib.handle);
138 (png_uint_32 (*) (png_structp, png_infop, png_bytep *, int *, png_color_16p *))
139 SDL_LoadFunction(lib.handle, "png_get_tRNS");
140 if ( lib.png_get_tRNS == NULL ) {
141 SDL_UnloadObject(lib.handle);
145 (png_uint_32 (*) (png_structp, png_infop, png_uint_32))
146 SDL_LoadFunction(lib.handle, "png_get_valid");
147 if ( lib.png_get_valid == NULL ) {
148 SDL_UnloadObject(lib.handle);
152 (void (*) (png_structp, png_bytepp))
153 SDL_LoadFunction(lib.handle, "png_read_image");
154 if ( lib.png_read_image == NULL ) {
155 SDL_UnloadObject(lib.handle);
159 (void (*) (png_structp, png_infop))
160 SDL_LoadFunction(lib.handle, "png_read_info");
161 if ( lib.png_read_info == NULL ) {
162 SDL_UnloadObject(lib.handle);
165 lib.png_read_update_info =
166 (void (*) (png_structp, png_infop))
167 SDL_LoadFunction(lib.handle, "png_read_update_info");
168 if ( lib.png_read_update_info == NULL ) {
169 SDL_UnloadObject(lib.handle);
173 (void (*) (png_structp))
174 SDL_LoadFunction(lib.handle, "png_set_expand");
175 if ( lib.png_set_expand == NULL ) {
176 SDL_UnloadObject(lib.handle);
179 lib.png_set_gray_to_rgb =
180 (void (*) (png_structp))
181 SDL_LoadFunction(lib.handle, "png_set_gray_to_rgb");
182 if ( lib.png_set_gray_to_rgb == NULL ) {
183 SDL_UnloadObject(lib.handle);
186 lib.png_set_packing =
187 (void (*) (png_structp))
188 SDL_LoadFunction(lib.handle, "png_set_packing");
189 if ( lib.png_set_packing == NULL ) {
190 SDL_UnloadObject(lib.handle);
193 lib.png_set_read_fn =
194 (void (*) (png_structp, png_voidp, png_rw_ptr))
195 SDL_LoadFunction(lib.handle, "png_set_read_fn");
196 if ( lib.png_set_read_fn == NULL ) {
197 SDL_UnloadObject(lib.handle);
200 lib.png_set_strip_16 =
201 (void (*) (png_structp))
202 SDL_LoadFunction(lib.handle, "png_set_strip_16");
203 if ( lib.png_set_strip_16 == NULL ) {
204 SDL_UnloadObject(lib.handle);
208 (int (*) (png_bytep, png_size_t, png_size_t))
209 SDL_LoadFunction(lib.handle, "png_sig_cmp");
210 if ( lib.png_sig_cmp == NULL ) {
211 SDL_UnloadObject(lib.handle);
221 if ( lib.loaded == 0 ) {
224 if ( lib.loaded == 1 ) {
225 SDL_UnloadObject(lib.handle);
232 if ( lib.loaded == 0 ) {
233 lib.png_create_info_struct = png_create_info_struct;
234 lib.png_create_read_struct = png_create_read_struct;
235 lib.png_destroy_read_struct = png_destroy_read_struct;
236 lib.png_get_IHDR = png_get_IHDR;
237 lib.png_get_io_ptr = png_get_io_ptr;
238 lib.png_get_tRNS = png_get_tRNS;
239 lib.png_get_valid = png_get_valid;
240 lib.png_read_image = png_read_image;
241 lib.png_read_info = png_read_info;
242 lib.png_read_update_info = png_read_update_info;
243 lib.png_set_expand = png_set_expand;
244 lib.png_set_gray_to_rgb = png_set_gray_to_rgb;
245 lib.png_set_packing = png_set_packing;
246 lib.png_set_read_fn = png_set_read_fn;
247 lib.png_set_strip_16 = png_set_strip_16;
248 lib.png_sig_cmp = png_sig_cmp;
256 if ( lib.loaded == 0 ) {
259 if ( lib.loaded == 1 ) {
263 #endif /* LOAD_PNG_DYNAMIC */
265 /* See if an image is contained in a data source */
266 int IMG_isPNG(SDL_RWops *src)
274 start = SDL_RWtell(src);
276 if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
277 if ( magic[0] == 0x89 &&
284 SDL_RWseek(src, start, SEEK_SET);
288 /* Load a PNG type image from an SDL datasource */
289 static void png_read_data(png_structp ctx, png_bytep area, png_size_t size)
293 src = (SDL_RWops *)lib.png_get_io_ptr(ctx);
294 SDL_RWread(src, area, size, 1);
296 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
300 SDL_Surface *volatile surface;
303 png_uint_32 width, height;
304 int bit_depth, color_type, interlace_type;
309 SDL_Palette *palette;
310 png_bytep *volatile row_pointers;
312 volatile int ckey = -1;
313 png_color_16 *transv;
316 /* The error message has been set in SDL_RWFromFile */
319 start = SDL_RWtell(src);
321 if ( IMG_Init(IMG_INIT_PNG) < 0 ) {
325 /* Initialize the data we will clean up when we're done */
327 png_ptr = NULL; info_ptr = NULL; row_pointers = NULL; surface = NULL;
329 /* Create the PNG loading context structure */
330 png_ptr = lib.png_create_read_struct(PNG_LIBPNG_VER_STRING,
332 if (png_ptr == NULL){
333 error = "Couldn't allocate memory for PNG file or incompatible PNG dll";
337 /* Allocate/initialize the memory for image information. REQUIRED. */
338 info_ptr = lib.png_create_info_struct(png_ptr);
339 if (info_ptr == NULL) {
340 error = "Couldn't create image information for PNG file";
344 /* Set error handling if you are using setjmp/longjmp method (this is
345 * the normal method of doing things with libpng). REQUIRED unless you
346 * set up your own error handlers in png_create_read_struct() earlier.
348 if ( setjmp(png_ptr->jmpbuf) ) {
349 error = "Error reading the PNG file.";
353 /* Set up the input control */
354 lib.png_set_read_fn(png_ptr, src, png_read_data);
356 /* Read PNG header info */
357 lib.png_read_info(png_ptr, info_ptr);
358 lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
359 &color_type, &interlace_type, NULL, NULL);
361 /* tell libpng to strip 16 bit/color files down to 8 bits/color */
362 lib.png_set_strip_16(png_ptr) ;
364 /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
365 * byte into separate bytes (useful for paletted and grayscale images).
367 lib.png_set_packing(png_ptr);
369 /* scale greyscale values to the range 0..255 */
370 if(color_type == PNG_COLOR_TYPE_GRAY)
371 lib.png_set_expand(png_ptr);
373 /* For images with a single "transparent colour", set colour key;
374 if more than one index has transparency, or if partially transparent
375 entries exist, use full alpha channel */
376 if (lib.png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
379 lib.png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans,
381 if(color_type == PNG_COLOR_TYPE_PALETTE) {
382 /* Check if all tRNS entries are opaque except one */
384 for(i = 0; i < num_trans; i++)
389 } else if(trans[i] != 255)
392 /* exactly one transparent index */
395 /* more than one transparent index, or translucency */
396 lib.png_set_expand(png_ptr);
399 ckey = 0; /* actual value will be set later */
402 if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
403 lib.png_set_gray_to_rgb(png_ptr);
405 lib.png_read_update_info(png_ptr, info_ptr);
407 lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
408 &color_type, &interlace_type, NULL, NULL);
410 /* Allocate the SDL surface to hold the image */
411 Rmask = Gmask = Bmask = Amask = 0 ;
412 if ( color_type != PNG_COLOR_TYPE_PALETTE ) {
413 if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
417 Amask = (info_ptr->channels == 4) ? 0xFF000000 : 0;
419 int s = (info_ptr->channels == 4) ? 0 : 8;
420 Rmask = 0xFF000000 >> s;
421 Gmask = 0x00FF0000 >> s;
422 Bmask = 0x0000FF00 >> s;
423 Amask = 0x000000FF >> s;
426 surface = SDL_AllocSurface(SDL_SWSURFACE, width, height,
427 bit_depth*info_ptr->channels, Rmask,Gmask,Bmask,Amask);
428 if ( surface == NULL ) {
429 error = "Out of memory";
434 if(color_type != PNG_COLOR_TYPE_PALETTE)
435 /* FIXME: Should these be truncated or shifted down? */
436 ckey = SDL_MapRGB(surface->format,
438 (Uint8)transv->green,
439 (Uint8)transv->blue);
440 SDL_SetColorKey(surface, SDL_SRCCOLORKEY, ckey);
443 /* Create the array of pointers to image data */
444 row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*height);
445 if ( (row_pointers == NULL) ) {
446 error = "Out of memory";
449 for (row = 0; row < (int)height; row++) {
450 row_pointers[row] = (png_bytep)
451 (Uint8 *)surface->pixels + row*surface->pitch;
454 /* Read the entire image in one go */
455 lib.png_read_image(png_ptr, row_pointers);
457 /* and we're done! (png_read_end() can be omitted if no processing of
458 * post-IDAT text/time/etc. is desired)
459 * In some cases it can't read PNG's created by some popular programs (ACDSEE),
460 * we do not want to process comments, so we omit png_read_end
462 lib.png_read_end(png_ptr, info_ptr);
465 /* Load the palette, if any */
466 palette = surface->format->palette;
468 if(color_type == PNG_COLOR_TYPE_GRAY) {
469 palette->ncolors = 256;
470 for(i = 0; i < 256; i++) {
471 palette->colors[i].r = i;
472 palette->colors[i].g = i;
473 palette->colors[i].b = i;
475 } else if (info_ptr->num_palette > 0 ) {
476 palette->ncolors = info_ptr->num_palette;
477 for( i=0; i<info_ptr->num_palette; ++i ) {
478 palette->colors[i].b = info_ptr->palette[i].blue;
479 palette->colors[i].g = info_ptr->palette[i].green;
480 palette->colors[i].r = info_ptr->palette[i].red;
485 done: /* Clean up and return */
487 lib.png_destroy_read_struct(&png_ptr,
488 info_ptr ? &info_ptr : (png_infopp)0,
491 if ( row_pointers ) {
495 SDL_RWseek(src, start, SEEK_SET);
497 SDL_FreeSurface(surface);
509 IMG_SetError("PNG images are not supported");
517 /* See if an image is contained in a data source */
518 int IMG_isPNG(SDL_RWops *src)
523 /* Load a PNG type image from an SDL datasource */
524 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
529 #endif /* LOAD_PNG */