2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
22 /* This is a PNG image file loading framework */
24 #include "SDL_image.h"
26 #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
30 /*=============================================================================
32 Purpose: A PNG loader and saver for the SDL library
34 Created by: Philippe Lavoie (2 November 1998)
35 lavoie@zeus.genie.uottawa.ca
39 Copyright (C) 1998 Philippe Lavoie
41 This library is free software; you can redistribute it and/or
42 modify it under the terms of the GNU Library General Public
43 License as published by the Free Software Foundation; either
44 version 2 of the License, or (at your option) any later version.
46 This library is distributed in the hope that it will be useful,
47 but WITHOUT ANY WARRANTY; without even the implied warranty of
48 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
49 Library General Public License for more details.
51 You should have received a copy of the GNU Library General Public
52 License along with this library; if not, write to the Free
53 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
55 Comments: The load and save routine are basically the ones you can find
56 in the example.c file from the libpng distribution.
59 5/17/99 - Modified to use the new SDL data sources - Sam Lantinga
61 =============================================================================*/
63 #include "SDL_endian.h"
70 /* Check for the older version of libpng */
71 #if (PNG_LIBPNG_VER_MAJOR == 1)
72 #if (PNG_LIBPNG_VER_MINOR < 4)
73 #define LIBPNG_VERSION_12
74 typedef png_bytep png_const_bytep;
76 #if (PNG_LIBPNG_VER_MINOR < 6)
77 typedef png_structp png_const_structrp;
78 typedef png_infop png_const_inforp;
85 png_infop (*png_create_info_struct) (png_const_structrp png_ptr);
86 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);
87 void (*png_destroy_read_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr);
88 png_uint_32 (*png_get_IHDR) (png_const_structrp png_ptr, png_const_inforp 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);
89 png_voidp (*png_get_io_ptr) (png_const_structrp png_ptr);
90 png_byte (*png_get_channels) (png_const_structrp png_ptr, png_const_inforp info_ptr);
91 png_uint_32 (*png_get_PLTE) (png_const_structrp png_ptr, png_infop info_ptr, png_colorp *palette, int *num_palette);
92 png_uint_32 (*png_get_tRNS) (png_const_structrp png_ptr, png_infop info_ptr, png_bytep *trans, int *num_trans, png_color_16p *trans_values);
93 png_uint_32 (*png_get_valid) (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag);
94 void (*png_read_image) (png_structp png_ptr, png_bytepp image);
95 void (*png_read_info) (png_structp png_ptr, png_infop info_ptr);
96 void (*png_read_update_info) (png_structp png_ptr, png_infop info_ptr);
97 void (*png_set_expand) (png_structp png_ptr);
98 void (*png_set_gray_to_rgb) (png_structp png_ptr);
99 void (*png_set_packing) (png_structp png_ptr);
100 void (*png_set_read_fn) (png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn);
101 void (*png_set_strip_16) (png_structp png_ptr);
102 int (*png_sig_cmp) (png_const_bytep sig, png_size_t start, png_size_t num_to_check);
103 #ifndef LIBPNG_VERSION_12
104 jmp_buf* (*png_set_longjmp_fn) (png_structp, png_longjmp_ptr, size_t);
108 #ifdef LOAD_PNG_DYNAMIC
111 if ( lib.loaded == 0 ) {
112 lib.handle = SDL_LoadObject(LOAD_PNG_DYNAMIC);
113 if ( lib.handle == NULL ) {
116 lib.png_create_info_struct =
117 (png_infop (*) (png_const_structrp))
118 SDL_LoadFunction(lib.handle, "png_create_info_struct");
119 if ( lib.png_create_info_struct == NULL ) {
120 SDL_UnloadObject(lib.handle);
123 lib.png_create_read_struct =
124 (png_structp (*) (png_const_charp, png_voidp, png_error_ptr, png_error_ptr))
125 SDL_LoadFunction(lib.handle, "png_create_read_struct");
126 if ( lib.png_create_read_struct == NULL ) {
127 SDL_UnloadObject(lib.handle);
130 lib.png_destroy_read_struct =
131 (void (*) (png_structpp, png_infopp, png_infopp))
132 SDL_LoadFunction(lib.handle, "png_destroy_read_struct");
133 if ( lib.png_destroy_read_struct == NULL ) {
134 SDL_UnloadObject(lib.handle);
138 (png_uint_32 (*) (png_const_structrp, png_const_inforp, png_uint_32 *, png_uint_32 *, int *, int *, int *, int *, int *))
139 SDL_LoadFunction(lib.handle, "png_get_IHDR");
140 if ( lib.png_get_IHDR == NULL ) {
141 SDL_UnloadObject(lib.handle);
144 lib.png_get_channels =
145 (png_byte (*) (png_const_structrp, png_const_inforp))
146 SDL_LoadFunction(lib.handle, "png_get_channels");
147 if ( lib.png_get_channels == NULL ) {
148 SDL_UnloadObject(lib.handle);
152 (png_voidp (*) (png_const_structrp))
153 SDL_LoadFunction(lib.handle, "png_get_io_ptr");
154 if ( lib.png_get_io_ptr == NULL ) {
155 SDL_UnloadObject(lib.handle);
159 (png_uint_32 (*) (png_const_structrp, png_infop, png_colorp *, int *))
160 SDL_LoadFunction(lib.handle, "png_get_PLTE");
161 if ( lib.png_get_PLTE == NULL ) {
162 SDL_UnloadObject(lib.handle);
166 (png_uint_32 (*) (png_const_structrp, png_infop, png_bytep *, int *, png_color_16p *))
167 SDL_LoadFunction(lib.handle, "png_get_tRNS");
168 if ( lib.png_get_tRNS == NULL ) {
169 SDL_UnloadObject(lib.handle);
173 (png_uint_32 (*) (png_const_structrp, png_const_inforp, png_uint_32))
174 SDL_LoadFunction(lib.handle, "png_get_valid");
175 if ( lib.png_get_valid == NULL ) {
176 SDL_UnloadObject(lib.handle);
180 (void (*) (png_structp, png_bytepp))
181 SDL_LoadFunction(lib.handle, "png_read_image");
182 if ( lib.png_read_image == NULL ) {
183 SDL_UnloadObject(lib.handle);
187 (void (*) (png_structp, png_infop))
188 SDL_LoadFunction(lib.handle, "png_read_info");
189 if ( lib.png_read_info == NULL ) {
190 SDL_UnloadObject(lib.handle);
193 lib.png_read_update_info =
194 (void (*) (png_structp, png_infop))
195 SDL_LoadFunction(lib.handle, "png_read_update_info");
196 if ( lib.png_read_update_info == NULL ) {
197 SDL_UnloadObject(lib.handle);
201 (void (*) (png_structp))
202 SDL_LoadFunction(lib.handle, "png_set_expand");
203 if ( lib.png_set_expand == NULL ) {
204 SDL_UnloadObject(lib.handle);
207 lib.png_set_gray_to_rgb =
208 (void (*) (png_structp))
209 SDL_LoadFunction(lib.handle, "png_set_gray_to_rgb");
210 if ( lib.png_set_gray_to_rgb == NULL ) {
211 SDL_UnloadObject(lib.handle);
214 lib.png_set_packing =
215 (void (*) (png_structp))
216 SDL_LoadFunction(lib.handle, "png_set_packing");
217 if ( lib.png_set_packing == NULL ) {
218 SDL_UnloadObject(lib.handle);
221 lib.png_set_read_fn =
222 (void (*) (png_structp, png_voidp, png_rw_ptr))
223 SDL_LoadFunction(lib.handle, "png_set_read_fn");
224 if ( lib.png_set_read_fn == NULL ) {
225 SDL_UnloadObject(lib.handle);
228 lib.png_set_strip_16 =
229 (void (*) (png_structp))
230 SDL_LoadFunction(lib.handle, "png_set_strip_16");
231 if ( lib.png_set_strip_16 == NULL ) {
232 SDL_UnloadObject(lib.handle);
236 (int (*) (png_const_bytep, png_size_t, png_size_t))
237 SDL_LoadFunction(lib.handle, "png_sig_cmp");
238 if ( lib.png_sig_cmp == NULL ) {
239 SDL_UnloadObject(lib.handle);
242 #ifndef LIBPNG_VERSION_12
243 lib.png_set_longjmp_fn =
244 (jmp_buf * (*) (png_structp, png_longjmp_ptr, size_t))
245 SDL_LoadFunction(lib.handle, "png_set_longjmp_fn");
246 if ( lib.png_set_longjmp_fn == NULL ) {
247 SDL_UnloadObject(lib.handle);
258 if ( lib.loaded == 0 ) {
261 if ( lib.loaded == 1 ) {
262 SDL_UnloadObject(lib.handle);
269 if ( lib.loaded == 0 ) {
270 lib.png_create_info_struct = png_create_info_struct;
271 lib.png_create_read_struct = png_create_read_struct;
272 lib.png_destroy_read_struct = png_destroy_read_struct;
273 lib.png_get_IHDR = png_get_IHDR;
274 lib.png_get_channels = png_get_channels;
275 lib.png_get_io_ptr = png_get_io_ptr;
276 lib.png_get_PLTE = png_get_PLTE;
277 lib.png_get_tRNS = png_get_tRNS;
278 lib.png_get_valid = png_get_valid;
279 lib.png_read_image = png_read_image;
280 lib.png_read_info = png_read_info;
281 lib.png_read_update_info = png_read_update_info;
282 lib.png_set_expand = png_set_expand;
283 lib.png_set_gray_to_rgb = png_set_gray_to_rgb;
284 lib.png_set_packing = png_set_packing;
285 lib.png_set_read_fn = png_set_read_fn;
286 lib.png_set_strip_16 = png_set_strip_16;
287 lib.png_sig_cmp = png_sig_cmp;
288 #ifndef LIBPNG_VERSION_12
289 lib.png_set_longjmp_fn = png_set_longjmp_fn;
298 if ( lib.loaded == 0 ) {
301 if ( lib.loaded == 1 ) {
305 #endif /* LOAD_PNG_DYNAMIC */
307 /* See if an image is contained in a data source */
308 int IMG_isPNG(SDL_RWops *src)
318 start = SDL_RWtell(src);
320 if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
321 if ( magic[0] == 0x89 &&
328 SDL_RWseek(src, start, RW_SEEK_SET);
332 /* Load a PNG type image from an SDL datasource */
333 static void png_read_data(png_structp ctx, png_bytep area, png_size_t size)
337 src = (SDL_RWops *)lib.png_get_io_ptr(ctx);
338 SDL_RWread(src, area, size, 1);
340 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
344 SDL_Surface *volatile surface;
347 png_uint_32 width, height;
348 int bit_depth, color_type, interlace_type, num_channels;
353 SDL_Palette *palette;
354 png_bytep *volatile row_pointers;
357 png_color_16 *transv;
360 /* The error message has been set in SDL_RWFromFile */
363 start = SDL_RWtell(src);
365 if ( !IMG_Init(IMG_INIT_PNG) ) {
369 /* Initialize the data we will clean up when we're done */
371 png_ptr = NULL; info_ptr = NULL; row_pointers = NULL; surface = NULL;
373 /* Create the PNG loading context structure */
374 png_ptr = lib.png_create_read_struct(PNG_LIBPNG_VER_STRING,
376 if (png_ptr == NULL){
377 error = "Couldn't allocate memory for PNG file or incompatible PNG dll";
381 /* Allocate/initialize the memory for image information. REQUIRED. */
382 info_ptr = lib.png_create_info_struct(png_ptr);
383 if (info_ptr == NULL) {
384 error = "Couldn't create image information for PNG file";
388 /* Set error handling if you are using setjmp/longjmp method (this is
389 * the normal method of doing things with libpng). REQUIRED unless you
390 * set up your own error handlers in png_create_read_struct() earlier.
392 #ifndef LIBPNG_VERSION_12
393 if ( setjmp(*lib.png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))) )
395 if ( setjmp(png_ptr->jmpbuf) )
398 error = "Error reading the PNG file.";
402 /* Set up the input control */
403 lib.png_set_read_fn(png_ptr, src, png_read_data);
405 /* Read PNG header info */
406 lib.png_read_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 /* tell libpng to strip 16 bit/color files down to 8 bits/color */
411 lib.png_set_strip_16(png_ptr) ;
413 /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
414 * byte into separate bytes (useful for paletted and grayscale images).
416 lib.png_set_packing(png_ptr);
418 /* scale greyscale values to the range 0..255 */
419 if (color_type == PNG_COLOR_TYPE_GRAY)
420 lib.png_set_expand(png_ptr);
422 /* For images with a single "transparent colour", set colour key;
423 if more than one index has transparency, or if partially transparent
424 entries exist, use full alpha channel */
425 if (lib.png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
428 lib.png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &transv);
429 if (color_type == PNG_COLOR_TYPE_PALETTE) {
430 /* Check if all tRNS entries are opaque except one */
432 for (j = 0; j < num_trans; j++) {
438 } else if (trans[j] != 255) {
442 if (j == num_trans) {
443 /* exactly one transparent index */
446 /* more than one transparent index, or translucency */
447 lib.png_set_expand(png_ptr);
450 ckey = 0; /* actual value will be set later */
454 if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
455 lib.png_set_gray_to_rgb(png_ptr);
457 lib.png_read_update_info(png_ptr, info_ptr);
459 lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
460 &color_type, &interlace_type, NULL, NULL);
462 /* Allocate the SDL surface to hold the image */
463 Rmask = Gmask = Bmask = Amask = 0 ;
464 num_channels = lib.png_get_channels(png_ptr, info_ptr);
465 if ( num_channels >= 3 ) {
466 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
470 Amask = (num_channels == 4) ? 0xFF000000 : 0;
472 int s = (num_channels == 4) ? 0 : 8;
473 Rmask = 0xFF000000 >> s;
474 Gmask = 0x00FF0000 >> s;
475 Bmask = 0x0000FF00 >> s;
476 Amask = 0x000000FF >> s;
479 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
480 bit_depth*num_channels, Rmask,Gmask,Bmask,Amask);
481 if ( surface == NULL ) {
482 error = SDL_GetError();
487 if (color_type != PNG_COLOR_TYPE_PALETTE) {
488 /* FIXME: Should these be truncated or shifted down? */
489 ckey = SDL_MapRGB(surface->format,
491 (Uint8)transv->green,
492 (Uint8)transv->blue);
494 SDL_SetColorKey(surface, SDL_TRUE, ckey);
497 /* Create the array of pointers to image data */
498 row_pointers = (png_bytep*) SDL_malloc(sizeof(png_bytep)*height);
499 if ( (row_pointers == NULL) ) {
500 error = "Out of memory";
503 for (row = 0; row < (int)height; row++) {
504 row_pointers[row] = (png_bytep)
505 (Uint8 *)surface->pixels + row*surface->pitch;
508 /* Read the entire image in one go */
509 lib.png_read_image(png_ptr, row_pointers);
511 /* and we're done! (png_read_end() can be omitted if no processing of
512 * post-IDAT text/time/etc. is desired)
513 * In some cases it can't read PNG's created by some popular programs (ACDSEE),
514 * we do not want to process comments, so we omit png_read_end
516 lib.png_read_end(png_ptr, info_ptr);
519 /* Load the palette, if any */
520 palette = surface->format->palette;
523 png_colorp png_palette;
524 lib.png_get_PLTE(png_ptr, info_ptr, &png_palette, &png_num_palette);
525 if (color_type == PNG_COLOR_TYPE_GRAY) {
526 palette->ncolors = 256;
527 for (i = 0; i < 256; i++) {
528 palette->colors[i].r = i;
529 palette->colors[i].g = i;
530 palette->colors[i].b = i;
532 } else if (png_num_palette > 0 ) {
533 palette->ncolors = png_num_palette;
534 for ( i=0; i<png_num_palette; ++i ) {
535 palette->colors[i].b = png_palette[i].blue;
536 palette->colors[i].g = png_palette[i].green;
537 palette->colors[i].r = png_palette[i].red;
542 done: /* Clean up and return */
544 lib.png_destroy_read_struct(&png_ptr,
545 info_ptr ? &info_ptr : (png_infopp)0,
548 if ( row_pointers ) {
549 SDL_free(row_pointers);
552 SDL_RWseek(src, start, RW_SEEK_SET);
554 SDL_FreeSurface(surface);
566 IMG_SetError("PNG images are not supported");
574 /* See if an image is contained in a data source */
575 int IMG_isPNG(SDL_RWops *src)
580 /* Load a PNG type image from an SDL datasource */
581 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
586 #endif /* LOAD_PNG */
588 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */
590 /* We'll always have PNG save support */
597 int IMG_SavePNG(SDL_Surface *surface, const char *file)
599 SDL_RWops *dst = SDL_RWFromFile(file, "wb");
601 return IMG_SavePNG_RW(surface, dst, 1);
607 int IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst)
612 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
613 static const Uint32 png_format = SDL_PIXELFORMAT_ABGR8888;
615 static const Uint32 png_format = SDL_PIXELFORMAT_RGBA8888;
620 if (surface->format->format == png_format) {
621 png = tdefl_write_image_to_png_file_in_memory(surface->pixels, surface->w, surface->h, surface->pitch, surface->format->BytesPerPixel, &size);
623 SDL_Surface *cvt = SDL_ConvertSurfaceFormat(surface, png_format, 0);
625 png = tdefl_write_image_to_png_file_in_memory(cvt->pixels, cvt->w, cvt->h, cvt->pitch, cvt->format->BytesPerPixel, &size);
626 SDL_FreeSurface(cvt);
630 if (SDL_RWwrite(dst, png, size, 1)) {
635 SDL_SetError("Failed to convert and save image");
641 SDL_SetError("Passed NULL dst");
646 #endif /* SAVE_PNG */