IMG_png.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 19 Jun 2019 08:31:35 -0700
changeset 667 b4e595f1fdb8
parent 640 db8eb5e7247c
permissions -rw-r--r--
Updated version to 2.0.5
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     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.
     8 
     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:
    12 
    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.
    20 */
    21 
    22 /* This is a PNG image file loading framework */
    23 
    24 #include "SDL_image.h"
    25 
    26 /* We'll always have PNG save support */
    27 #define SAVE_PNG
    28 
    29 #if !(defined(__APPLE__) || defined(SDL_IMAGE_USE_WIC_BACKEND)) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
    30 
    31 #ifdef LOAD_PNG
    32 
    33 #define USE_LIBPNG
    34 
    35 /*=============================================================================
    36         File: SDL_png.c
    37      Purpose: A PNG loader and saver for the SDL library
    38     Revision:
    39   Created by: Philippe Lavoie          (2 November 1998)
    40               lavoie@zeus.genie.uottawa.ca
    41  Modified by:
    42 
    43  Copyright notice:
    44           Copyright (C) 1998 Philippe Lavoie
    45 
    46           This library is free software; you can redistribute it and/or
    47           modify it under the terms of the GNU Library General Public
    48           License as published by the Free Software Foundation; either
    49           version 2 of the License, or (at your option) any later version.
    50 
    51           This library is distributed in the hope that it will be useful,
    52           but WITHOUT ANY WARRANTY; without even the implied warranty of
    53           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    54           Library General Public License for more details.
    55 
    56           You should have received a copy of the GNU Library General Public
    57           License along with this library; if not, write to the Free
    58           Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    59 
    60     Comments: The load and save routine are basically the ones you can find
    61              in the example.c file from the libpng distribution.
    62 
    63   Changes:
    64     5/17/99 - Modified to use the new SDL data sources - Sam Lantinga
    65 
    66 =============================================================================*/
    67 
    68 #include "SDL_endian.h"
    69 
    70 #ifdef macintosh
    71 #define MACOS
    72 #endif
    73 #include <png.h>
    74 
    75 /* Check for the older version of libpng */
    76 #if (PNG_LIBPNG_VER_MAJOR == 1)
    77 #if (PNG_LIBPNG_VER_MINOR < 5)
    78 #define LIBPNG_VERSION_12
    79 typedef png_bytep png_const_bytep;
    80 typedef png_color *png_const_colorp;
    81 #endif
    82 #if (PNG_LIBPNG_VER_MINOR < 4)
    83 typedef png_structp png_const_structp;
    84 typedef png_infop png_const_infop;
    85 #endif
    86 #if (PNG_LIBPNG_VER_MINOR < 6)
    87 typedef png_structp png_structrp;
    88 typedef png_infop png_inforp;
    89 typedef png_const_structp png_const_structrp;
    90 typedef png_const_infop png_const_inforp;
    91 /* noconst15: version < 1.6 doesn't have const, >= 1.6 adds it */
    92 /* noconst16: version < 1.6 does have const, >= 1.6 removes it */
    93 typedef png_structp png_noconst15_structrp;
    94 typedef png_inforp png_noconst15_inforp;
    95 typedef png_const_inforp png_noconst16_inforp;
    96 #else
    97 typedef png_const_structp png_noconst15_structrp;
    98 typedef png_const_inforp png_noconst15_inforp;
    99 typedef png_inforp png_noconst16_inforp;
   100 #endif
   101 #else
   102 typedef png_const_structp png_noconst15_structrp;
   103 typedef png_const_inforp png_noconst15_inforp;
   104 typedef png_inforp png_noconst16_inforp;
   105 #endif
   106 
   107 static struct {
   108     int loaded;
   109     void *handle;
   110     png_infop (*png_create_info_struct) (png_noconst15_structrp png_ptr);
   111     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);
   112     void (*png_destroy_read_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr);
   113     png_uint_32 (*png_get_IHDR) (png_noconst15_structrp png_ptr, png_noconst15_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);
   114     png_voidp (*png_get_io_ptr) (png_noconst15_structrp png_ptr);
   115     png_byte (*png_get_channels) (png_const_structrp png_ptr, png_const_inforp info_ptr);
   116     png_uint_32 (*png_get_PLTE) (png_const_structrp png_ptr, png_noconst16_inforp info_ptr, png_colorp *palette, int *num_palette);
   117     png_uint_32 (*png_get_tRNS) (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *trans, int *num_trans, png_color_16p *trans_values);
   118     png_uint_32 (*png_get_valid) (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag);
   119     void (*png_read_image) (png_structrp png_ptr, png_bytepp image);
   120     void (*png_read_info) (png_structrp png_ptr, png_inforp info_ptr);
   121     void (*png_read_update_info) (png_structrp png_ptr, png_inforp info_ptr);
   122     void (*png_set_expand) (png_structrp png_ptr);
   123     void (*png_set_gray_to_rgb) (png_structrp png_ptr);
   124     void (*png_set_packing) (png_structrp png_ptr);
   125     void (*png_set_read_fn) (png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn);
   126     void (*png_set_strip_16) (png_structrp png_ptr);
   127     int (*png_set_interlace_handling) (png_structrp png_ptr);
   128     int (*png_sig_cmp) (png_const_bytep sig, png_size_t start, png_size_t num_to_check);
   129 #ifdef PNG_SETJMP_SUPPORTED
   130 #ifndef LIBPNG_VERSION_12
   131     jmp_buf* (*png_set_longjmp_fn) (png_structrp, png_longjmp_ptr, size_t);
   132 #endif
   133 #endif
   134 #ifdef SAVE_PNG
   135     png_structp (*png_create_write_struct) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn);
   136     void (*png_destroy_write_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr);
   137     void (*png_set_write_fn) (png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn);
   138     void (*png_set_IHDR) (png_noconst15_structrp png_ptr, png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int interlace_type, int compression_type, int filter_type);
   139     void (*png_write_info) (png_structrp png_ptr, png_noconst15_inforp info_ptr);
   140     void (*png_set_rows) (png_noconst15_structrp png_ptr, png_inforp info_ptr, png_bytepp row_pointers);
   141     void (*png_write_png) (png_structrp png_ptr, png_inforp info_ptr, int transforms, png_voidp params);
   142     void (*png_set_PLTE) (png_structrp png_ptr, png_inforp info_ptr, png_const_colorp palette, int num_palette);
   143 #endif
   144 } lib;
   145 
   146 #ifdef LOAD_PNG_DYNAMIC
   147 #define FUNCTION_LOADER(FUNC, SIG) \
   148     lib.FUNC = (SIG) SDL_LoadFunction(lib.handle, #FUNC); \
   149     if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle); return -1; }
   150 #else
   151 #define FUNCTION_LOADER(FUNC, SIG) \
   152     lib.FUNC = FUNC;
   153 #endif
   154 
   155 int IMG_InitPNG()
   156 {
   157     if ( lib.loaded == 0 ) {
   158 #ifdef LOAD_PNG_DYNAMIC
   159         lib.handle = SDL_LoadObject(LOAD_PNG_DYNAMIC);
   160         if ( lib.handle == NULL ) {
   161             return -1;
   162         }
   163 #endif
   164         FUNCTION_LOADER(png_create_info_struct, png_infop (*) (png_noconst15_structrp png_ptr))
   165         FUNCTION_LOADER(png_create_read_struct, png_structp (*) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn))
   166         FUNCTION_LOADER(png_destroy_read_struct, void (*) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr))
   167         FUNCTION_LOADER(png_get_IHDR, png_uint_32 (*) (png_noconst15_structrp png_ptr, png_noconst15_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))
   168         FUNCTION_LOADER(png_get_io_ptr, png_voidp (*) (png_noconst15_structrp png_ptr))
   169         FUNCTION_LOADER(png_get_channels, png_byte (*) (png_const_structrp png_ptr, png_const_inforp info_ptr))
   170         FUNCTION_LOADER(png_get_PLTE, png_uint_32 (*) (png_const_structrp png_ptr, png_noconst16_inforp info_ptr, png_colorp *palette, int *num_palette))
   171         FUNCTION_LOADER(png_get_tRNS, png_uint_32 (*) (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *trans, int *num_trans, png_color_16p *trans_values))
   172         FUNCTION_LOADER(png_get_valid, png_uint_32 (*) (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag))
   173         FUNCTION_LOADER(png_read_image, void (*) (png_structrp png_ptr, png_bytepp image))
   174         FUNCTION_LOADER(png_read_info, void (*) (png_structrp png_ptr, png_inforp info_ptr))
   175         FUNCTION_LOADER(png_read_update_info, void (*) (png_structrp png_ptr, png_inforp info_ptr))
   176         FUNCTION_LOADER(png_set_expand, void (*) (png_structrp png_ptr))
   177         FUNCTION_LOADER(png_set_gray_to_rgb, void (*) (png_structrp png_ptr))
   178         FUNCTION_LOADER(png_set_packing, void (*) (png_structrp png_ptr))
   179         FUNCTION_LOADER(png_set_read_fn, void (*) (png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn))
   180         FUNCTION_LOADER(png_set_strip_16, void (*) (png_structrp png_ptr))
   181         FUNCTION_LOADER(png_set_interlace_handling, int (*) (png_structrp png_ptr))
   182         FUNCTION_LOADER(png_sig_cmp, int (*) (png_const_bytep sig, png_size_t start, png_size_t num_to_check))
   183 #ifdef PNG_SETJMP_SUPPORTED
   184 #ifndef LIBPNG_VERSION_12
   185         FUNCTION_LOADER(png_set_longjmp_fn, jmp_buf* (*) (png_structrp, png_longjmp_ptr, size_t))
   186 #endif
   187 #endif
   188 #ifdef SAVE_PNG
   189         FUNCTION_LOADER(png_create_write_struct, png_structp (*) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn))
   190         FUNCTION_LOADER(png_destroy_write_struct, void (*) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr))
   191         FUNCTION_LOADER(png_set_write_fn, void (*) (png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn))
   192         FUNCTION_LOADER(png_set_IHDR, void (*) (png_noconst15_structrp png_ptr, png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int interlace_type, int compression_type, int filter_type))
   193         FUNCTION_LOADER(png_write_info, void (*) (png_structrp png_ptr, png_noconst15_inforp info_ptr))
   194         FUNCTION_LOADER(png_set_rows, void (*) (png_noconst15_structrp png_ptr, png_inforp info_ptr, png_bytepp row_pointers))
   195         FUNCTION_LOADER(png_write_png, void (*) (png_structrp png_ptr, png_inforp info_ptr, int transforms, png_voidp params))
   196         FUNCTION_LOADER(png_set_PLTE, void (*) (png_structrp png_ptr, png_inforp info_ptr, png_const_colorp palette, int num_palette))
   197 #endif
   198     }
   199     ++lib.loaded;
   200 
   201     return 0;
   202 }
   203 void IMG_QuitPNG()
   204 {
   205     if ( lib.loaded == 0 ) {
   206         return;
   207     }
   208     if ( lib.loaded == 1 ) {
   209 #ifdef LOAD_PNG_DYNAMIC
   210         SDL_UnloadObject(lib.handle);
   211 #endif
   212     }
   213     --lib.loaded;
   214 }
   215 
   216 /* See if an image is contained in a data source */
   217 int IMG_isPNG(SDL_RWops *src)
   218 {
   219     Sint64 start;
   220     int is_PNG;
   221     Uint8 magic[4];
   222 
   223     if ( !src ) {
   224         return 0;
   225     }
   226 
   227     start = SDL_RWtell(src);
   228     is_PNG = 0;
   229     if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   230         if ( magic[0] == 0x89 &&
   231              magic[1] == 'P' &&
   232              magic[2] == 'N' &&
   233              magic[3] == 'G' ) {
   234             is_PNG = 1;
   235         }
   236     }
   237     SDL_RWseek(src, start, RW_SEEK_SET);
   238     return(is_PNG);
   239 }
   240 
   241 /* Load a PNG type image from an SDL datasource */
   242 static void png_read_data(png_structp ctx, png_bytep area, png_size_t size)
   243 {
   244     SDL_RWops *src;
   245 
   246     src = (SDL_RWops *)lib.png_get_io_ptr(ctx);
   247     SDL_RWread(src, area, size, 1);
   248 }
   249 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
   250 {
   251     Sint64 start;
   252     const char *error;
   253     SDL_Surface *volatile surface;
   254     png_structp png_ptr;
   255     png_infop info_ptr;
   256     png_uint_32 width, height;
   257     int bit_depth, color_type, interlace_type, num_channels;
   258     Uint32 Rmask;
   259     Uint32 Gmask;
   260     Uint32 Bmask;
   261     Uint32 Amask;
   262     SDL_Palette *palette;
   263     png_bytep *volatile row_pointers;
   264     int row, i;
   265     int ckey = -1;
   266     png_color_16 *transv;
   267 
   268     if ( !src ) {
   269         /* The error message has been set in SDL_RWFromFile */
   270         return NULL;
   271     }
   272     start = SDL_RWtell(src);
   273 
   274     if ( (IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) == 0 ) {
   275         return NULL;
   276     }
   277 
   278     /* Initialize the data we will clean up when we're done */
   279     error = NULL;
   280     png_ptr = NULL; info_ptr = NULL; row_pointers = NULL; surface = NULL;
   281 
   282     /* Create the PNG loading context structure */
   283     png_ptr = lib.png_create_read_struct(PNG_LIBPNG_VER_STRING,
   284                       NULL,NULL,NULL);
   285     if (png_ptr == NULL){
   286         error = "Couldn't allocate memory for PNG file or incompatible PNG dll";
   287         goto done;
   288     }
   289 
   290      /* Allocate/initialize the memory for image information.  REQUIRED. */
   291     info_ptr = lib.png_create_info_struct(png_ptr);
   292     if (info_ptr == NULL) {
   293         error = "Couldn't create image information for PNG file";
   294         goto done;
   295     }
   296 
   297     /* Set error handling if you are using setjmp/longjmp method (this is
   298      * the normal method of doing things with libpng).  REQUIRED unless you
   299      * set up your own error handlers in png_create_read_struct() earlier.
   300      */
   301 
   302 #ifdef PNG_SETJMP_SUPPORTED
   303 #ifndef LIBPNG_VERSION_12
   304     if ( setjmp(*lib.png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))) )
   305 #else
   306     if ( setjmp(png_ptr->jmpbuf) )
   307 #endif
   308     {
   309         error = "Error reading the PNG file.";
   310         goto done;
   311     }
   312 #endif
   313     /* Set up the input control */
   314     lib.png_set_read_fn(png_ptr, src, png_read_data);
   315 
   316     /* Read PNG header info */
   317     lib.png_read_info(png_ptr, info_ptr);
   318     lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
   319             &color_type, &interlace_type, NULL, NULL);
   320 
   321     /* tell libpng to strip 16 bit/color files down to 8 bits/color */
   322     lib.png_set_strip_16(png_ptr);
   323 
   324     /* tell libpng to de-interlace (if the image is interlaced) */
   325     lib.png_set_interlace_handling(png_ptr);
   326 
   327     /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
   328      * byte into separate bytes (useful for paletted and grayscale images).
   329      */
   330     lib.png_set_packing(png_ptr);
   331 
   332     /* scale greyscale values to the range 0..255 */
   333     if (color_type == PNG_COLOR_TYPE_GRAY)
   334         lib.png_set_expand(png_ptr);
   335 
   336     /* For images with a single "transparent colour", set colour key;
   337        if more than one index has transparency, or if partially transparent
   338        entries exist, use full alpha channel */
   339     if (lib.png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
   340         int num_trans;
   341         Uint8 *trans;
   342         lib.png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &transv);
   343         if (color_type == PNG_COLOR_TYPE_PALETTE) {
   344             /* Check if all tRNS entries are opaque except one */
   345             int j, t = -1;
   346             for (j = 0; j < num_trans; j++) {
   347                 if (trans[j] == 0) {
   348                     if (t >= 0) {
   349                         break;
   350                     }
   351                     t = j;
   352                 } else if (trans[j] != 255) {
   353                     break;
   354                 }
   355             }
   356             if (j == num_trans) {
   357                 /* exactly one transparent index */
   358                 ckey = t;
   359             } else {
   360                 /* more than one transparent index, or translucency */
   361                 lib.png_set_expand(png_ptr);
   362             }
   363         } else {
   364             ckey = 0; /* actual value will be set later */
   365         }
   366     }
   367 
   368     if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
   369         lib.png_set_gray_to_rgb(png_ptr);
   370 
   371     lib.png_read_update_info(png_ptr, info_ptr);
   372 
   373     lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
   374             &color_type, &interlace_type, NULL, NULL);
   375 
   376     /* Allocate the SDL surface to hold the image */
   377     Rmask = Gmask = Bmask = Amask = 0 ;
   378     num_channels = lib.png_get_channels(png_ptr, info_ptr);
   379     if ( num_channels >= 3 ) {
   380 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   381         Rmask = 0x000000FF;
   382         Gmask = 0x0000FF00;
   383         Bmask = 0x00FF0000;
   384         Amask = (num_channels == 4) ? 0xFF000000 : 0;
   385 #else
   386         int s = (num_channels == 4) ? 0 : 8;
   387         Rmask = 0xFF000000 >> s;
   388         Gmask = 0x00FF0000 >> s;
   389         Bmask = 0x0000FF00 >> s;
   390         Amask = 0x000000FF >> s;
   391 #endif
   392     }
   393     surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
   394             bit_depth*num_channels, Rmask,Gmask,Bmask,Amask);
   395     if ( surface == NULL ) {
   396         error = SDL_GetError();
   397         goto done;
   398     }
   399 
   400     if (ckey != -1) {
   401         if (color_type != PNG_COLOR_TYPE_PALETTE) {
   402             /* FIXME: Should these be truncated or shifted down? */
   403             ckey = SDL_MapRGB(surface->format,
   404                          (Uint8)transv->red,
   405                          (Uint8)transv->green,
   406                          (Uint8)transv->blue);
   407         }
   408         SDL_SetColorKey(surface, SDL_TRUE, ckey);
   409     }
   410 
   411     /* Create the array of pointers to image data */
   412     row_pointers = (png_bytep*) SDL_malloc(sizeof(png_bytep)*height);
   413     if (!row_pointers) {
   414         error = "Out of memory";
   415         goto done;
   416     }
   417     for (row = 0; row < (int)height; row++) {
   418         row_pointers[row] = (png_bytep)
   419                 (Uint8 *)surface->pixels + row*surface->pitch;
   420     }
   421 
   422     /* Read the entire image in one go */
   423     lib.png_read_image(png_ptr, row_pointers);
   424 
   425     /* and we're done!  (png_read_end() can be omitted if no processing of
   426      * post-IDAT text/time/etc. is desired)
   427      * In some cases it can't read PNG's created by some popular programs (ACDSEE),
   428      * we do not want to process comments, so we omit png_read_end
   429 
   430     lib.png_read_end(png_ptr, info_ptr);
   431     */
   432 
   433     /* Load the palette, if any */
   434     palette = surface->format->palette;
   435     if ( palette ) {
   436         int png_num_palette;
   437         png_colorp png_palette;
   438         lib.png_get_PLTE(png_ptr, info_ptr, &png_palette, &png_num_palette);
   439         if (color_type == PNG_COLOR_TYPE_GRAY) {
   440             palette->ncolors = 256;
   441             for (i = 0; i < 256; i++) {
   442                 palette->colors[i].r = (Uint8)i;
   443                 palette->colors[i].g = (Uint8)i;
   444                 palette->colors[i].b = (Uint8)i;
   445             }
   446         } else if (png_num_palette > 0 ) {
   447             palette->ncolors = png_num_palette;
   448             for ( i=0; i<png_num_palette; ++i ) {
   449                 palette->colors[i].b = png_palette[i].blue;
   450                 palette->colors[i].g = png_palette[i].green;
   451                 palette->colors[i].r = png_palette[i].red;
   452             }
   453         }
   454     }
   455 
   456 done:   /* Clean up and return */
   457     if ( png_ptr ) {
   458         lib.png_destroy_read_struct(&png_ptr,
   459                                 info_ptr ? &info_ptr : (png_infopp)0,
   460                                 (png_infopp)0);
   461     }
   462     if ( row_pointers ) {
   463         SDL_free(row_pointers);
   464     }
   465     if ( error ) {
   466         SDL_RWseek(src, start, RW_SEEK_SET);
   467         if ( surface ) {
   468             SDL_FreeSurface(surface);
   469             surface = NULL;
   470         }
   471         IMG_SetError("%s", error);
   472     }
   473     return(surface);
   474 }
   475 
   476 #else
   477 
   478 int IMG_InitPNG()
   479 {
   480     IMG_SetError("PNG images are not supported");
   481     return(-1);
   482 }
   483 
   484 void IMG_QuitPNG()
   485 {
   486 }
   487 
   488 /* See if an image is contained in a data source */
   489 int IMG_isPNG(SDL_RWops *src)
   490 {
   491     return(0);
   492 }
   493 
   494 /* Load a PNG type image from an SDL datasource */
   495 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
   496 {
   497     return(NULL);
   498 }
   499 
   500 #endif /* LOAD_PNG */
   501 
   502 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */
   503 
   504 #ifdef SAVE_PNG
   505 
   506 int IMG_SavePNG(SDL_Surface *surface, const char *file)
   507 {
   508     SDL_RWops *dst = SDL_RWFromFile(file, "wb");
   509     if (dst) {
   510         return IMG_SavePNG_RW(surface, dst, 1);
   511     } else {
   512         return -1;
   513     }
   514 }
   515 
   516 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   517 static const Uint32 png_format = SDL_PIXELFORMAT_ABGR8888;
   518 #else
   519 static const Uint32 png_format = SDL_PIXELFORMAT_RGBA8888;
   520 #endif
   521 
   522 #ifdef USE_LIBPNG
   523 
   524 static void png_write_data(png_structp png_ptr, png_bytep src, png_size_t size)
   525 {
   526     SDL_RWops *dst = (SDL_RWops *)lib.png_get_io_ptr(png_ptr);
   527     SDL_RWwrite(dst, src, size, 1);
   528 }
   529 
   530 static void png_flush_data(png_structp png_ptr)
   531 {
   532 }
   533 
   534 static int IMG_SavePNG_RW_libpng(SDL_Surface *surface, SDL_RWops *dst, int freedst)
   535 {
   536     if (dst) {
   537         png_structp png_ptr;
   538         png_infop info_ptr;
   539         png_colorp color_ptr = NULL;
   540         SDL_Surface *source = surface;
   541         SDL_Palette *palette;
   542         int png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
   543 
   544         png_ptr = lib.png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
   545         if (png_ptr == NULL) {
   546             SDL_SetError("Couldn't allocate memory for PNG file or incompatible PNG dll");
   547             return -1;
   548         }
   549 
   550         info_ptr = lib.png_create_info_struct(png_ptr);
   551         if (info_ptr == NULL) {
   552             lib.png_destroy_write_struct(&png_ptr, NULL);
   553             SDL_SetError("Couldn't create image information for PNG file");
   554             return -1;
   555         }
   556 #ifdef PNG_SETJMP_SUPPORTED
   557 #ifndef LIBPNG_VERSION_12
   558         if (setjmp(*lib.png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf))))
   559 #else
   560         if (setjmp(png_ptr->jmpbuf))
   561 #endif
   562 #endif
   563         {
   564             lib.png_destroy_write_struct(&png_ptr, &info_ptr);
   565             SDL_SetError("Error writing the PNG file.");
   566             return -1;
   567         }
   568 
   569         palette = surface->format->palette;
   570         if (palette) {
   571             const int ncolors = palette->ncolors;
   572             int i;
   573 
   574             color_ptr = SDL_malloc(sizeof(png_colorp) * ncolors);
   575             if (color_ptr == NULL)
   576             {
   577                 lib.png_destroy_write_struct(&png_ptr, &info_ptr);
   578                 SDL_SetError("Couldn't create palette for PNG file");
   579                 return -1;
   580             }
   581             for (i = 0; i < ncolors; i++) {
   582                 color_ptr[i].red = palette->colors[i].r;
   583                 color_ptr[i].green = palette->colors[i].g;
   584                 color_ptr[i].blue = palette->colors[i].b;
   585             }
   586             lib.png_set_PLTE(png_ptr, info_ptr, color_ptr, ncolors);
   587             png_color_type = PNG_COLOR_TYPE_PALETTE;
   588         }
   589         else if (surface->format->format != png_format) {
   590             source = SDL_ConvertSurfaceFormat(surface, png_format, 0);
   591         }
   592 
   593         lib.png_set_write_fn(png_ptr, dst, png_write_data, png_flush_data);
   594 
   595         lib.png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h,
   596                          8, png_color_type, PNG_INTERLACE_NONE,
   597                          PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
   598 
   599         if (source) {
   600             png_bytep *row_pointers;
   601             int row;
   602 
   603             row_pointers = (png_bytep *) SDL_malloc(sizeof(png_bytep) * source->h);
   604             if (!row_pointers) {
   605                 lib.png_destroy_write_struct(&png_ptr, &info_ptr);
   606                 SDL_SetError("Out of memory");
   607                 return -1;
   608             }
   609             for (row = 0; row < (int)source->h; row++) {
   610                 row_pointers[row] = (png_bytep) (Uint8 *) source->pixels + row * source->pitch;
   611             }
   612 
   613             lib.png_set_rows(png_ptr, info_ptr, row_pointers);
   614             lib.png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
   615 
   616             SDL_free(row_pointers);
   617             if (source != surface) {
   618                 SDL_FreeSurface(source);
   619             }
   620         }
   621         lib.png_destroy_write_struct(&png_ptr, &info_ptr);
   622         if (color_ptr) {
   623             SDL_free(color_ptr);
   624         }
   625         if (freedst) {
   626             SDL_RWclose(dst);
   627         }
   628     } else {
   629         SDL_SetError("Passed NULL dst");
   630         return -1;
   631     }
   632     return 0;
   633 }
   634 
   635 #endif /* USE_LIBPNG */
   636 
   637 /* Replace C runtime functions with SDL C runtime functions for building on Windows */
   638 #define MINIZ_NO_STDIO
   639 #define MINIZ_NO_TIME
   640 #define MINIZ_SDL_MALLOC
   641 #define MZ_ASSERT(x) SDL_assert(x)
   642 #undef memcpy
   643 #define memcpy  SDL_memcpy
   644 #undef memset
   645 #define memset  SDL_memset
   646 #define strlen  SDL_strlen
   647 
   648 #include "miniz.h"
   649 
   650 static int IMG_SavePNG_RW_miniz(SDL_Surface *surface, SDL_RWops *dst, int freedst)
   651 {
   652     int result = -1;
   653 
   654     if (dst) {
   655         size_t size = 0;
   656         void *png = NULL;
   657 
   658         if (surface->format->format == png_format) {
   659             png = tdefl_write_image_to_png_file_in_memory(surface->pixels, surface->w, surface->h, surface->format->BytesPerPixel, surface->pitch, &size);
   660         } else {
   661             SDL_Surface *cvt = SDL_ConvertSurfaceFormat(surface, png_format, 0);
   662             if (cvt) {
   663                 png = tdefl_write_image_to_png_file_in_memory(cvt->pixels, cvt->w, cvt->h, cvt->format->BytesPerPixel, cvt->pitch, &size);
   664                 SDL_FreeSurface(cvt);
   665             }
   666         }
   667         if (png) {
   668             if (SDL_RWwrite(dst, png, size, 1)) {
   669                 result = 0;
   670             }
   671             SDL_free(png);
   672         } else {
   673             SDL_SetError("Failed to convert and save image");
   674         }
   675         if (freedst) {
   676             SDL_RWclose(dst);
   677         }
   678     } else {
   679         SDL_SetError("Passed NULL dst");
   680     }
   681     return result;
   682 }
   683 
   684 int IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst)
   685 {
   686     static int (*rw_func)(SDL_Surface *surface, SDL_RWops *dst, int freedst);
   687 
   688     if (!rw_func)
   689     {
   690 #ifdef USE_LIBPNG
   691         if (IMG_Init(IMG_INIT_PNG)) {
   692             rw_func = IMG_SavePNG_RW_libpng;
   693         } else
   694 #endif
   695             rw_func = IMG_SavePNG_RW_miniz;
   696     }
   697 
   698     return rw_func(surface, dst, freedst);
   699 }
   700 
   701 #endif /* SAVE_PNG */