1 /* SaveSurf: an example on how to save a SDLSurface in PNG
2    Copyright (C) 2006 Angelo "Encelo" Theodorou
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 
18    NOTE:
19 
20    This program is part of "Mars, Land of No Mercy" SDL examples,
21    you can find other examples on http://marsnomercy.org
22 */
23 
24 #include <stdlib.h>
25 #include <png.h>
26 #include <SDL.h>
27 
28 enum pixelFormat {
29 	RGBA_PIXEL_FORMAT = 0,
30 	BGRA_PIXEL_FORMAT = 1
31 };
32 
surface_pixel_format(SDL_Surface * surface)33 static enum pixelFormat surface_pixel_format(SDL_Surface *surface)
34 {
35 	// This defines BGRA pixel format
36 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
37 	int rmask = 0x00FF0000;
38 	int gmask = 0x0000FF00;
39 	int bmask = 0x000000FF;
40 	//int amask = 0xFF000000;
41 #else
42 	int rmask = 0x0000FF00;
43 	int gmask = 0x00FF0000;
44 	int bmask = 0xFF000000;
45 	//int amask = 0x000000FF;
46 #endif
47 
48 	if ((surface->format->Rmask == rmask) && (surface->format->Gmask == gmask) && (surface->format->Bmask == bmask))
49 		return BGRA_PIXEL_FORMAT;
50 	else
51 		return RGBA_PIXEL_FORMAT;
52 }
53 
png_colortype_from_surface(SDL_Surface * surface)54 static int png_colortype_from_surface(SDL_Surface *surface)
55 {
56 	int colortype = PNG_COLOR_MASK_COLOR; /* grayscale not supported */
57 
58 	if (surface->format->palette)
59 		colortype |= PNG_COLOR_MASK_PALETTE;
60 	else if (surface->format->Amask)
61 		colortype |= PNG_COLOR_MASK_ALPHA;
62 
63 	return colortype;
64 }
65 
66 
png_user_warn(png_structp ctx,png_const_charp str)67 void png_user_warn(png_structp ctx, png_const_charp str)
68 {
69 	fprintf(stderr, "libpng: warning: %s\n", str);
70 }
71 
72 
png_user_error(png_structp ctx,png_const_charp str)73 void png_user_error(png_structp ctx, png_const_charp str)
74 {
75 	fprintf(stderr, "libpng: error: %s\n", str);
76 }
77 
78 
png_save_surface(const char * filename,SDL_Surface * surf)79 int png_save_surface(const char *filename, SDL_Surface *surf)
80 {
81 	FILE *fp;
82 	png_structp png_ptr;
83 	png_infop info_ptr;
84 	int i, colortype;
85 	png_bytep *row_pointers;
86 
87 	/* Opening output file */
88 	fp = fopen(filename, "wb");
89 	if (fp == NULL) {
90 		perror("fopen error");
91 		return -1;
92 	}
93 
94 	/* Initializing png structures and callbacks */
95 	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
96 		NULL, png_user_error, png_user_warn);
97 	if (png_ptr == NULL) {
98 		printf("png_create_write_struct error!\n");
99 		fclose(fp);
100 		return -1;
101 	}
102 
103 	info_ptr = png_create_info_struct(png_ptr);
104 	if (info_ptr == NULL) {
105 		png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
106 		printf("png_create_info_struct error!\n");
107 		fclose(fp);
108 		return -1;
109 	}
110 
111 	if (setjmp(png_jmpbuf(png_ptr))) {
112 		png_destroy_write_struct(&png_ptr, &info_ptr);
113 		fclose(fp);
114 		return -1;
115 	}
116 
117 	png_init_io(png_ptr, fp);
118 
119 	colortype = png_colortype_from_surface(surf);
120 	png_set_IHDR(png_ptr, info_ptr, surf->w, surf->h, 8, colortype,	PNG_INTERLACE_NONE,
121 		PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
122 
123 	/* Writing the image */
124 	png_write_info(png_ptr, info_ptr);
125 	png_set_packing(png_ptr);
126 
127     /* If the surface image is in BGR mode, then ask libpng to swap red and blue channels */
128 	if (surface_pixel_format(surf) == BGRA_PIXEL_FORMAT)
129 		png_set_bgr(png_ptr);
130 
131 	/* Strip 4th byte */
132 	if (surf->format->BytesPerPixel == 4 && !surf->format->Amask)
133 		png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
134 
135 	row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*surf->h);
136 	for (i = 0; i < surf->h; i++)
137 		row_pointers[i] = (png_bytep)(Uint8 *)surf->pixels + i*surf->pitch;
138 	png_write_image(png_ptr, row_pointers);
139 	png_write_end(png_ptr, info_ptr);
140 
141 	/* Cleaning out... */
142 	free(row_pointers);
143 	png_destroy_write_struct(&png_ptr, &info_ptr);
144 	fclose(fp);
145 
146 	return 0;
147 }
148