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