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