IMG_png.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 08 Dec 2008 00:27:32 +0000
changeset 186 5fbbeabf647f
parent 167 338c85983115
child 204 dcb38b0e78a6
permissions -rw-r--r--
Updated copyright date
     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 #ifdef LOAD_PNG
    31 
    32 /*=============================================================================
    33         File: SDL_png.c
    34      Purpose: A PNG loader and saver for the SDL library      
    35     Revision: 
    36   Created by: Philippe Lavoie          (2 November 1998)
    37               lavoie@zeus.genie.uottawa.ca
    38  Modified by: 
    39 
    40  Copyright notice:
    41           Copyright (C) 1998 Philippe Lavoie
    42  
    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.
    47  
    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.
    52  
    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.
    56 
    57     Comments: The load and save routine are basically the ones you can find
    58              in the example.c file from the libpng distribution.
    59 
    60   Changes:
    61     5/17/99 - Modified to use the new SDL data sources - Sam Lantinga
    62 
    63 =============================================================================*/
    64 
    65 #include "SDL_endian.h"
    66 
    67 #ifdef macintosh
    68 #define MACOS
    69 #endif
    70 #include <png.h>
    71 
    72 
    73 static struct {
    74 	int loaded;
    75 	void *handle;
    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);
    92 } lib;
    93 
    94 #ifdef LOAD_PNG_DYNAMIC
    95 int IMG_InitPNG()
    96 {
    97 	if ( lib.loaded == 0 ) {
    98 		lib.handle = SDL_LoadObject(LOAD_PNG_DYNAMIC);
    99 		if ( lib.handle == NULL ) {
   100 			return -1;
   101 		}
   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);
   107 			return -1;
   108 		}
   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);
   114 			return -1;
   115 		}
   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);
   121 			return -1;
   122 		}
   123 		lib.png_get_IHDR =
   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);
   128 			return -1;
   129 		}
   130 		lib.png_get_io_ptr =
   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);
   135 			return -1;
   136 		}
   137 		lib.png_get_tRNS =
   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);
   142 			return -1;
   143 		}
   144 		lib.png_get_valid =
   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);
   149 			return -1;
   150 		}
   151 		lib.png_read_image =
   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);
   156 			return -1;
   157 		}
   158 		lib.png_read_info =
   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);
   163 			return -1;
   164 		}
   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);
   170 			return -1;
   171 		}
   172 		lib.png_set_expand =
   173 			(void (*) (png_structp))
   174 			SDL_LoadFunction(lib.handle, "png_set_expand");
   175 		if ( lib.png_set_expand == NULL ) {
   176 			SDL_UnloadObject(lib.handle);
   177 			return -1;
   178 		}
   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);
   184 			return -1;
   185 		}
   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);
   191 			return -1;
   192 		}
   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);
   198 			return -1;
   199 		}
   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);
   205 			return -1;
   206 		}
   207 		lib.png_sig_cmp =
   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);
   212 			return -1;
   213 		}
   214 	}
   215 	++lib.loaded;
   216 
   217 	return 0;
   218 }
   219 void IMG_QuitPNG()
   220 {
   221 	if ( lib.loaded == 0 ) {
   222 		return;
   223 	}
   224 	if ( lib.loaded == 1 ) {
   225 		SDL_UnloadObject(lib.handle);
   226 	}
   227 	--lib.loaded;
   228 }
   229 #else
   230 int IMG_InitPNG()
   231 {
   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;
   249 	}
   250 	++lib.loaded;
   251 
   252 	return 0;
   253 }
   254 void IMG_QuitPNG()
   255 {
   256 	if ( lib.loaded == 0 ) {
   257 		return;
   258 	}
   259 	if ( lib.loaded == 1 ) {
   260 	}
   261 	--lib.loaded;
   262 }
   263 #endif /* LOAD_PNG_DYNAMIC */
   264 
   265 /* See if an image is contained in a data source */
   266 int IMG_isPNG(SDL_RWops *src)
   267 {
   268 	int start;
   269 	int is_PNG;
   270 	Uint8 magic[4];
   271 
   272 	if ( !src )
   273 		return 0;
   274 	start = SDL_RWtell(src);
   275 	is_PNG = 0;
   276 	if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   277                 if ( magic[0] == 0x89 &&
   278                      magic[1] == 'P' &&
   279                      magic[2] == 'N' &&
   280                      magic[3] == 'G' ) {
   281 			is_PNG = 1;
   282 		}
   283 	}
   284 	SDL_RWseek(src, start, SEEK_SET);
   285 	return(is_PNG);
   286 }
   287 
   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)
   290 {
   291 	SDL_RWops *src;
   292 
   293 	src = (SDL_RWops *)lib.png_get_io_ptr(ctx);
   294 	SDL_RWread(src, area, size, 1);
   295 }
   296 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
   297 {
   298 	int start;
   299 	const char *error;
   300 	SDL_Surface *volatile surface;
   301 	png_structp png_ptr;
   302 	png_infop info_ptr;
   303 	png_uint_32 width, height;
   304 	int bit_depth, color_type, interlace_type;
   305 	Uint32 Rmask;
   306 	Uint32 Gmask;
   307 	Uint32 Bmask;
   308 	Uint32 Amask;
   309 	SDL_Palette *palette;
   310 	png_bytep *volatile row_pointers;
   311 	int row, i;
   312 	volatile int ckey = -1;
   313 	png_color_16 *transv;
   314 
   315 	if ( !src ) {
   316 		/* The error message has been set in SDL_RWFromFile */
   317 		return NULL;
   318 	}
   319 	start = SDL_RWtell(src);
   320 
   321 	if ( IMG_InitPNG() < 0 ) {
   322 		return NULL;
   323 	}
   324 
   325 	/* Initialize the data we will clean up when we're done */
   326 	error = NULL;
   327 	png_ptr = NULL; info_ptr = NULL; row_pointers = NULL; surface = NULL;
   328 
   329 	/* Create the PNG loading context structure */
   330 	png_ptr = lib.png_create_read_struct(PNG_LIBPNG_VER_STRING,
   331 					  NULL,NULL,NULL);
   332 	if (png_ptr == NULL){
   333 		error = "Couldn't allocate memory for PNG file or incompatible PNG dll";
   334 		goto done;
   335 	}
   336 
   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";
   341 		goto done;
   342 	}
   343 
   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.
   347 	 */
   348 	if ( setjmp(png_ptr->jmpbuf) ) {
   349 		error = "Error reading the PNG file.";
   350 		goto done;
   351 	}
   352 
   353 	/* Set up the input control */
   354 	lib.png_set_read_fn(png_ptr, src, png_read_data);
   355 
   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);
   360 
   361 	/* tell libpng to strip 16 bit/color files down to 8 bits/color */
   362 	lib.png_set_strip_16(png_ptr) ;
   363 
   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).
   366 	 */
   367 	lib.png_set_packing(png_ptr);
   368 
   369 	/* scale greyscale values to the range 0..255 */
   370 	if(color_type == PNG_COLOR_TYPE_GRAY)
   371 		lib.png_set_expand(png_ptr);
   372 
   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)) {
   377 	        int num_trans;
   378 		Uint8 *trans;
   379 		lib.png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans,
   380 			     &transv);
   381 		if(color_type == PNG_COLOR_TYPE_PALETTE) {
   382 		    /* Check if all tRNS entries are opaque except one */
   383 		    int i, t = -1;
   384 		    for(i = 0; i < num_trans; i++)
   385 			if(trans[i] == 0) {
   386 			    if(t >= 0)
   387 				break;
   388 			    t = i;
   389 			} else if(trans[i] != 255)
   390 			    break;
   391 		    if(i == num_trans) {
   392 			/* exactly one transparent index */
   393 			ckey = t;
   394 		    } else {
   395 			/* more than one transparent index, or translucency */
   396 			lib.png_set_expand(png_ptr);
   397 		    }
   398 		} else
   399 		    ckey = 0; /* actual value will be set later */
   400 	}
   401 
   402 	if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
   403 		lib.png_set_gray_to_rgb(png_ptr);
   404 
   405 	lib.png_read_update_info(png_ptr, info_ptr);
   406 
   407 	lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
   408 			&color_type, &interlace_type, NULL, NULL);
   409 
   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 ) {
   414 			Rmask = 0x000000FF;
   415 			Gmask = 0x0000FF00;
   416 			Bmask = 0x00FF0000;
   417 			Amask = (info_ptr->channels == 4) ? 0xFF000000 : 0;
   418 		} else {
   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;
   424 		}
   425 	}
   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";
   430 		goto done;
   431 	}
   432 
   433 	if(ckey != -1) {
   434 	        if(color_type != PNG_COLOR_TYPE_PALETTE)
   435 			/* FIXME: Should these be truncated or shifted down? */
   436 		        ckey = SDL_MapRGB(surface->format,
   437 			                 (Uint8)transv->red,
   438 			                 (Uint8)transv->green,
   439 			                 (Uint8)transv->blue);
   440 	        SDL_SetColorKey(surface, SDL_SRCCOLORKEY, ckey);
   441 	}
   442 
   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";
   447 		goto done;
   448 	}
   449 	for (row = 0; row < (int)height; row++) {
   450 		row_pointers[row] = (png_bytep)
   451 				(Uint8 *)surface->pixels + row*surface->pitch;
   452 	}
   453 
   454 	/* Read the entire image in one go */
   455 	lib.png_read_image(png_ptr, row_pointers);
   456 
   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
   461 
   462 	lib.png_read_end(png_ptr, info_ptr);
   463 	*/
   464 
   465 	/* Load the palette, if any */
   466 	palette = surface->format->palette;
   467 	if ( 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;
   474 		}
   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;
   481 		}
   482 	    }
   483 	}
   484 
   485 done:	/* Clean up and return */
   486 	if ( png_ptr ) {
   487 		lib.png_destroy_read_struct(&png_ptr,
   488 		                        info_ptr ? &info_ptr : (png_infopp)0,
   489 								(png_infopp)0);
   490 	}
   491 	if ( row_pointers ) {
   492 		free(row_pointers);
   493 	}
   494 	if ( error ) {
   495 		SDL_RWseek(src, start, SEEK_SET);
   496 		if ( surface ) {
   497 			SDL_FreeSurface(surface);
   498 			surface = NULL;
   499 		}
   500 		IMG_QuitPNG();
   501 		IMG_SetError(error);
   502 	} else {
   503 		IMG_QuitPNG();
   504 	}
   505 	return(surface); 
   506 }
   507 
   508 #else
   509 
   510 /* See if an image is contained in a data source */
   511 int IMG_isPNG(SDL_RWops *src)
   512 {
   513 	return(0);
   514 }
   515 
   516 /* Load a PNG type image from an SDL datasource */
   517 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
   518 {
   519 	return(NULL);
   520 }
   521 
   522 #endif /* LOAD_PNG */