1 /*
2   Based on zlib license - see http://www.gzip.org/zlib/zlib_license.html
3 
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7 
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11 
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19 
20   "Philip D. Bober" <wildfire1138@mchsi.com>
21 */
22 
23 /**
24  * 4/17/04 - IMG_SavePNG & IMG_SavePNG_RW - Philip D. Bober
25  * 11/08/2004 - Compr fix, levels -1,1-7 now work - Tyler Montbriand
26  */
27 #include <stdlib.h>
28 #include <png.h>
29 #include <zlib.h>
30 #include "write_png.h"
31 
32 #ifndef png_voidp
33 #define png_voidp voidp
34 #endif
35 
Pygame_SDL2_SavePNG(const char * file,SDL_Surface * surf,int compression)36 int Pygame_SDL2_SavePNG(const char *file, SDL_Surface *surf,int compression){
37 	SDL_RWops *fp;
38 	int ret;
39 
40 	fp=SDL_RWFromFile(file,"wb");
41 
42 	if( fp == NULL ) {
43 		return (-1);
44 	}
45 
46 	ret=Pygame_SDL2_SavePNG_RW(fp,surf,compression);
47 	SDL_RWclose(fp);
48 	return ret;
49 }
50 
png_write_data(png_structp png_ptr,png_bytep data,png_size_t length)51 static void png_write_data(png_structp png_ptr,png_bytep data, png_size_t length){
52 	SDL_RWops *rp = (SDL_RWops*) png_get_io_ptr(png_ptr);
53 	SDL_RWwrite(rp,data,1,length);
54 }
55 
Pygame_SDL2_SavePNG_RW(SDL_RWops * src,SDL_Surface * surf,int compression)56 int Pygame_SDL2_SavePNG_RW(SDL_RWops *src, SDL_Surface *surf,int compression){
57 	png_structp png_ptr;
58 	png_infop info_ptr;
59 	SDL_PixelFormat *fmt=NULL;
60 	SDL_Surface *tempsurf=NULL;
61 	int ret;
62 	int i;
63 	png_colorp palette;
64 	Uint8 *palette_alpha=NULL;
65 	png_byte **row_pointers=NULL;
66 	unsigned int target_format;
67 
68 	png_ptr=NULL;info_ptr=NULL;palette=NULL;ret=-1;
69 
70 	if( !src || !surf) {
71 		goto savedone2; /* Nothing to do. */
72 	}
73 
74 	row_pointers=(png_byte **)malloc(surf->h * sizeof(png_byte*));
75 	if (!row_pointers) {
76 		SDL_SetError("Couldn't allocate memory for rowpointers");
77 		goto savedone2;
78 	}
79 
80 	png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
81 	if (!png_ptr){
82 		SDL_SetError("Couldn't allocate memory for PNG file version: " PNG_LIBPNG_VER_STRING);
83 		goto savedone2;
84 	}
85 	info_ptr= png_create_info_struct(png_ptr);
86 	if (!info_ptr){
87 		SDL_SetError("Couldn't allocate image information for PNG file");
88 		goto savedone;
89 	}
90 	/* setup custom writer functions */
91 	png_set_write_fn(png_ptr,(png_voidp)src,png_write_data,NULL);
92 
93 	if (setjmp(png_jmpbuf(png_ptr))){
94 		SDL_SetError("Unknown error writing PNG");
95 		goto savedone;
96 	}
97 
98 	if(compression>Z_BEST_COMPRESSION)
99 		compression=Z_BEST_COMPRESSION;
100 
101 	if(compression == Z_NO_COMPRESSION) // No compression
102 	{
103 		png_set_filter(png_ptr,0,PNG_FILTER_NONE);
104 		png_set_compression_level(png_ptr,Z_NO_COMPRESSION);
105 	}
106 	else if(compression<0) // Default compression
107 		png_set_compression_level(png_ptr,Z_DEFAULT_COMPRESSION);
108 	else
109 		png_set_compression_level(png_ptr,compression);
110 
111 	fmt=surf->format;
112 
113 	if (fmt->Amask) {
114 		png_set_IHDR(png_ptr,info_ptr,
115 			surf->w,surf->h,8,PNG_COLOR_TYPE_RGB_ALPHA,
116 			PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,
117 			PNG_FILTER_TYPE_DEFAULT);
118 	} else {
119 		png_set_IHDR(png_ptr,info_ptr,
120 			surf->w,surf->h,8,PNG_COLOR_TYPE_RGB,
121 			PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,
122 			PNG_FILTER_TYPE_DEFAULT);
123 	}
124 
125 	png_write_info(png_ptr, info_ptr);
126 
127 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
128 	if (fmt->Amask) {
129 		target_format = SDL_PIXELFORMAT_ABGR8888;
130 	} else {
131 		target_format = SDL_PIXELFORMAT_BGR888;
132 	}
133 #else
134 	if (fmt->Amask) {
135 		target_format = SDL_PIXELFORMAT_RGBA8888;
136 	} else {
137 		target_format = SDL_PIXELFORMAT_RGB888;
138 	}
139 #endif
140 
141 	if (surf->format->format != target_format) {
142 		tempsurf = SDL_ConvertSurfaceFormat(surf, target_format, 0);
143 		surf = tempsurf;
144 
145 		if (!tempsurf){
146 			SDL_SetError("Couldn't allocate temp surface");
147 			goto savedone;
148 		}
149 	}
150 
151 	for(i=0;i < surf->h;i++){
152 		row_pointers[i]= ((png_byte*) surf->pixels) + i * surf->pitch;
153 	}
154 
155 	png_write_image(png_ptr, row_pointers);
156 
157 	if (tempsurf) {
158 		SDL_FreeSurface(tempsurf);
159 	}
160 
161 	png_write_end(png_ptr, NULL);
162 
163 	ret=0; /* got here, so nothing went wrong. YAY! */
164 
165 savedone: /* clean up and return */
166 	png_destroy_write_struct(&png_ptr,&info_ptr);
167 
168 savedone2:
169 	if (palette) {
170 		free(palette);
171 	}
172 	if (palette_alpha) {
173 		free(palette_alpha);
174 	}
175 	if (row_pointers) {
176 		free(row_pointers);
177 	}
178 	return ret;
179 }
180