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