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