1 /*
2 * SDL_SavePNG -- libpng-based SDL_Surface writer.
3 *
4 * This code is free software, available under zlib/libpng license.
5 * http://www.libpng.org/pub/png/src/libpng-LICENSE.txt
6 */
7 #include <SDL.h>
8 #include <stdlib.h>
9 #include "png.h"
10
11 #include "savepng.hpp"
12
13 #define SUCCESS 0
14 #define ERROR -1
15
16 #define USE_ROW_POINTERS
17
18 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
19 #define rmask 0xFF000000
20 #define gmask 0x00FF0000
21 #define bmask 0x0000FF00
22 #define amask 0x000000FF
23 #else
24 #define rmask 0x000000FF
25 #define gmask 0x0000FF00
26 #define bmask 0x00FF0000
27 #define amask 0xFF000000
28 #endif
29
SDL_SavePNG(SDL_Surface * surface,const char * filename)30 int SDL_SavePNG(SDL_Surface *surface, const char *filename)
31 {
32 SDL_RWops *file = SDL_RWFromFile(filename, "wb");
33 if (file != NULL)
34 {
35 return SDL_SavePNG_RW(surface, file, 1);
36 }
37 else
38 {
39 return -1;
40 }
41 }
42
43 /* libpng callbacks */
png_error_SDL(png_structp ctx,png_const_charp str)44 static void png_error_SDL(png_structp ctx, png_const_charp str)
45 {
46 SDL_SetError("libpng: %s\n", str);
47 }
png_write_SDL(png_structp png_ptr,png_bytep data,png_size_t length)48 static void png_write_SDL(png_structp png_ptr, png_bytep data, png_size_t length)
49 {
50 SDL_RWops* rw = (SDL_RWops*)png_get_io_ptr(png_ptr);
51 SDL_RWwrite(rw, data, sizeof(png_byte), length);
52 }
53
SDL_PNGFormatAlpha(SDL_Surface * src)54 SDL_Surface* SDL_PNGFormatAlpha(SDL_Surface* src)
55 {
56 SDL_Surface* surf;
57 SDL_Rect rect = { 0 };
58
59 /* NO-OP for images < 32bpp and 32bpp images that already have Alpha channel */
60 if (src->format->BitsPerPixel <= 24 || src->format->Amask)
61 {
62 src->refcount++;
63 return src;
64 }
65
66 /* Convert 32bpp alpha-less image to 24bpp alpha-less image */
67 rect.w = src->w;
68 rect.h = src->h;
69 surf = SDL_CreateRGBSurface(src->flags, src->w, src->h, 24,
70 src->format->Rmask, src->format->Gmask, src->format->Bmask, 0);
71 SDL_LowerBlit(src, &rect, surf, &rect);
72
73 return surf;
74 }
75
SDL_SavePNG_RW(SDL_Surface * surface,SDL_RWops * dst,int freedst)76 int SDL_SavePNG_RW(SDL_Surface* surface, SDL_RWops* dst, int freedst)
77 {
78 png_structp png_ptr;
79 png_infop info_ptr;
80 png_colorp pal_ptr;
81 SDL_Palette* pal;
82 int i, colortype;
83 #ifdef USE_ROW_POINTERS
84 png_bytep* row_pointers;
85 #endif
86 /* Initialize and do basic error checking */
87 if (!dst)
88 {
89 SDL_SetError("Argument 2 to SDL_SavePNG_RW can't be NULL, expecting SDL_RWops*\n");
90 if (freedst)
91 {
92 SDL_RWclose(dst);
93 }
94 return (ERROR);
95 }
96 if (!surface)
97 {
98 SDL_SetError("Argument 1 to SDL_SavePNG_RW can't be NULL, expecting SDL_Surface*\n");
99 if (freedst)
100 {
101 SDL_RWclose(dst);
102 }
103 return (ERROR);
104 }
105 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_SDL, NULL); /* err_ptr, err_fn, warn_fn */
106 if (!png_ptr)
107 {
108 SDL_SetError("Unable to png_create_write_struct on %s\n", PNG_LIBPNG_VER_STRING);
109 if (freedst)
110 {
111 SDL_RWclose(dst);
112 }
113 return (ERROR);
114 }
115 info_ptr = png_create_info_struct(png_ptr);
116 if (!info_ptr)
117 {
118 SDL_SetError("Unable to png_create_info_struct\n");
119 png_destroy_write_struct(&png_ptr, NULL);
120 if (freedst)
121 {
122 SDL_RWclose(dst);
123 }
124 return (ERROR);
125 }
126 if (setjmp(png_jmpbuf(png_ptr))) /* All other errors, see also "png_error_SDL" */
127 {
128 png_destroy_write_struct(&png_ptr, &info_ptr);
129 if (freedst)
130 {
131 SDL_RWclose(dst);
132 }
133 return (ERROR);
134 }
135
136 /* Setup our RWops writer */
137 png_set_write_fn(png_ptr, dst, png_write_SDL, NULL); /* w_ptr, write_fn, flush_fn */
138
139 /* Prepare chunks */
140 colortype = PNG_COLOR_MASK_COLOR;
141 if (surface->format->BytesPerPixel > 0
142 && surface->format->BytesPerPixel <= 8
143 && (pal = surface->format->palette))
144 {
145 colortype |= PNG_COLOR_MASK_PALETTE;
146 pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color));
147 for (i = 0; i < pal->ncolors; i++)
148 {
149 pal_ptr[i].red = pal->colors[i].r;
150 pal_ptr[i].green = pal->colors[i].g;
151 pal_ptr[i].blue = pal->colors[i].b;
152 }
153 png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors);
154 free(pal_ptr);
155 }
156 else if (surface->format->BytesPerPixel > 3 || surface->format->Amask)
157 {
158 colortype |= PNG_COLOR_MASK_ALPHA;
159 }
160
161 png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8, colortype,
162 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
163
164 // png_set_packing(png_ptr);
165
166 /* Allow BGR surfaces */
167 if (surface->format->Rmask == bmask
168 && surface->format->Gmask == gmask
169 && surface->format->Bmask == rmask)
170 {
171 png_set_bgr(png_ptr);
172 }
173
174 /* Write everything */
175 png_write_info(png_ptr, info_ptr);
176 #ifdef USE_ROW_POINTERS
177 row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * surface->h);
178 for (i = 0; i < surface->h; i++)
179 {
180 row_pointers[i] = (png_bytep)(Uint8*)surface->pixels + i * surface->pitch;
181 }
182 png_write_image(png_ptr, row_pointers);
183 free(row_pointers);
184 #else
185 for (i = 0; i < surface->h; i++)
186 {
187 png_write_row(png_ptr, (png_bytep)(Uint8*)surface->pixels + i * surface->pitch);
188 }
189 #endif
190 png_write_end(png_ptr, info_ptr);
191
192 /* Done */
193 png_destroy_write_struct(&png_ptr, &info_ptr);
194 if (freedst)
195 {
196 SDL_RWclose(dst);
197 }
198 return (SUCCESS);
199 }
200