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 
IMG_InitPNG()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 }
IMG_QuitPNG()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 */
IMG_isPNG(SDL_RWops * src)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 */
png_read_data(png_structp ctx,png_bytep area,png_size_t size)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 }
IMG_LoadPNG_RW(SDL_RWops * src)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 
IMG_InitPNG()478 int IMG_InitPNG()
479 {
480     IMG_SetError("PNG images are not supported");
481     return(-1);
482 }
483 
IMG_QuitPNG()484 void IMG_QuitPNG()
485 {
486 }
487 
488 /* See if an image is contained in a data source */
IMG_isPNG(SDL_RWops * src)489 int IMG_isPNG(SDL_RWops *src)
490 {
491     return(0);
492 }
493 
494 /* Load a PNG type image from an SDL datasource */
IMG_LoadPNG_RW(SDL_RWops * src)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 
IMG_SavePNG(SDL_Surface * surface,const char * file)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 
png_write_data(png_structp png_ptr,png_bytep src,png_size_t size)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 
png_flush_data(png_structp png_ptr)530 static void png_flush_data(png_structp png_ptr)
531 {
532 }
533 
IMG_SavePNG_RW_libpng(SDL_Surface * surface,SDL_RWops * dst,int freedst)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 
IMG_SavePNG_RW_miniz(SDL_Surface * surface,SDL_RWops * dst,int freedst)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 
IMG_SavePNG_RW(SDL_Surface * surface,SDL_RWops * dst,int freedst)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 */
702