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 <SDL.h>
29 #include <png.h>
30 #include <zlib.h>
31 #include "IMG_savepng.h"
32 
33 #ifndef png_voidp
34 #define png_voidp voidp
35 #endif
36 
renpy_IMG_SavePNG(const char * file,SDL_Surface * surf,int compression)37 int renpy_IMG_SavePNG(const char *file, SDL_Surface *surf,int compression){
38 	SDL_RWops *fp;
39 	int ret;
40 
41 	fp=SDL_RWFromFile(file,"wb");
42 
43 	if( fp == NULL ) {
44 		return (-1);
45 	}
46 
47 	ret=renpy_IMG_SavePNG_RW(fp,surf,compression);
48 	SDL_RWclose(fp);
49 	return ret;
50 }
51 
png_write_data(png_structp png_ptr,png_bytep data,png_size_t length)52 static void png_write_data(png_structp png_ptr,png_bytep data, png_size_t length){
53 	SDL_RWops *rp = (SDL_RWops*) png_get_io_ptr(png_ptr);
54 	SDL_RWwrite(rp,data,1,length);
55 }
56 
renpy_IMG_SavePNG_RW(SDL_RWops * src,SDL_Surface * surf,int compression)57 int renpy_IMG_SavePNG_RW(SDL_RWops *src, SDL_Surface *surf,int compression){
58 	png_structp png_ptr;
59 	png_infop info_ptr;
60 	SDL_PixelFormat *fmt=NULL;
61 	SDL_Surface *tempsurf=NULL;
62 	int ret;
63 	unsigned int i;
64 	png_colorp palette;
65 	Uint8 *palette_alpha=NULL;
66 	png_byte **row_pointers=NULL;
67 	png_ptr=NULL;info_ptr=NULL;palette=NULL;ret=-1;
68 
69 	Uint32 target_format;
70 
71 	if( !src || !surf) {
72 		goto savedone2; /* Nothing to do. */
73 	}
74 
75 	row_pointers=(png_byte **)malloc(surf->h * sizeof(png_byte*));
76 	if (!row_pointers) {
77 		SDL_SetError("Couldn't allocate memory for rowpointers");
78 		goto savedone2;
79 	}
80 
81 	png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
82 	if (!png_ptr){
83 		SDL_SetError("Couldn't allocate memory for PNG file version: " PNG_LIBPNG_VER_STRING);
84 		goto savedone2;
85 	}
86 	info_ptr= png_create_info_struct(png_ptr);
87 	if (!info_ptr){
88 		SDL_SetError("Couldn't allocate image information for PNG file");
89 		goto savedone;
90 	}
91 	/* setup custom writer functions */
92 	png_set_write_fn(png_ptr,(png_voidp)src,png_write_data,NULL);
93 
94 	if (setjmp(png_jmpbuf(png_ptr))){
95 		SDL_SetError("Unknown error writing PNG");
96 		goto savedone;
97 	}
98 
99 	if(compression>Z_BEST_COMPRESSION)
100 		compression=Z_BEST_COMPRESSION;
101 
102 	if(compression == Z_NO_COMPRESSION) // No compression
103 	{
104 		png_set_filter(png_ptr,0,PNG_FILTER_NONE);
105 		png_set_compression_level(png_ptr,Z_NO_COMPRESSION);
106 	}
107 	else if(compression<0) // Default compression
108 		png_set_compression_level(png_ptr,Z_DEFAULT_COMPRESSION);
109 	else
110 		png_set_compression_level(png_ptr,compression);
111 
112 	fmt=surf->format;
113 
114 	if (fmt->Amask) {
115 		png_set_IHDR(png_ptr,info_ptr,
116 			surf->w,surf->h,8,PNG_COLOR_TYPE_RGB_ALPHA,
117 			PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,
118 			PNG_FILTER_TYPE_DEFAULT);
119 	} else {
120 		png_set_IHDR(png_ptr,info_ptr,
121 			surf->w,surf->h,8,PNG_COLOR_TYPE_RGB,
122 			PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,
123 			PNG_FILTER_TYPE_DEFAULT);
124 	}
125 
126 	png_write_info(png_ptr, info_ptr);
127 
128 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
129 	if (fmt->Amask) {
130 		target_format = SDL_PIXELFORMAT_ABGR8888;
131 	} else {
132 		target_format = SDL_PIXELFORMAT_BGR888;
133 	}
134 #else
135 	if (fmt->Amask) {
136 		target_format = SDL_PIXELFORMAT_RGBA8888;
137 	} else {
138 		target_format = SDL_PIXELFORMAT_RGB888;
139 	}
140 #endif
141 
142 	if (surf->format->format != target_format) {
143 		tempsurf = SDL_ConvertSurfaceFormat(surf, target_format, 0);
144 		surf = tempsurf;
145 
146 		if (!tempsurf){
147 			SDL_SetError("Couldn't allocate temp surface");
148 			goto savedone;
149 		}
150 	}
151 
152 	for(i=0;i < surf->h;i++){
153 		row_pointers[i]= ((png_byte*) surf->pixels) + i * surf->pitch;
154 	}
155 
156 	png_write_image(png_ptr, row_pointers);
157 
158 	if (tempsurf) {
159 		SDL_FreeSurface(tempsurf);
160 	}
161 
162 	png_write_end(png_ptr, NULL);
163 
164 	ret=0; /* got here, so nothing went wrong. YAY! */
165 
166 savedone: /* clean up and return */
167 	png_destroy_write_struct(&png_ptr,&info_ptr);
168 
169 savedone2:
170 	if (palette) {
171 		free(palette);
172 	}
173 	if (palette_alpha) {
174 		free(palette_alpha);
175 	}
176 	if (row_pointers) {
177 		free(row_pointers);
178 	}
179 	return ret;
180 }
181