src/util/png-io.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba-util/png-io.h>
7
8#ifdef USE_PNG
9
10#include <mgba-util/vfs.h>
11
12static void _pngWrite(png_structp png, png_bytep buffer, png_size_t size) {
13 struct VFile* vf = png_get_io_ptr(png);
14 size_t written = vf->write(vf, buffer, size);
15 if (written != size) {
16 png_error(png, "Could not write PNG");
17 }
18}
19
20static void _pngRead(png_structp png, png_bytep buffer, png_size_t size) {
21 struct VFile* vf = png_get_io_ptr(png);
22 size_t read = vf->read(vf, buffer, size);
23 if (read != size) {
24 png_error(png, "Could not read PNG");
25 }
26}
27
28png_structp PNGWriteOpen(struct VFile* source) {
29 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
30 if (!png) {
31 return 0;
32 }
33 if (setjmp(png_jmpbuf(png))) {
34 png_destroy_write_struct(&png, 0);
35 return 0;
36 }
37 png_set_write_fn(png, source, _pngWrite, 0);
38 return png;
39}
40
41png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) {
42 png_infop info = png_create_info_struct(png);
43 if (!info) {
44 return 0;
45 }
46 if (setjmp(png_jmpbuf(png))) {
47 return 0;
48 }
49 png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
50 png_write_info(png, info);
51 return info;
52}
53
54png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height) {
55 png_infop info = png_create_info_struct(png);
56 if (!info) {
57 return 0;
58 }
59 if (setjmp(png_jmpbuf(png))) {
60 return 0;
61 }
62 png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
63 return info;
64}
65
66bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries) {
67 if (!palette || !entries) {
68 return false;
69 }
70 if (setjmp(png_jmpbuf(png))) {
71 return false;
72 }
73 png_color colors[256];
74 png_byte trans[256];
75 unsigned i;
76 for (i = 0; i < entries && i < 256; ++i) {
77 colors[i].red = palette[i];
78 colors[i].green = palette[i] >> 8;
79 colors[i].blue = palette[i] >> 16;
80 trans[i] = palette[i] >> 24;
81 }
82 png_set_PLTE(png, info, colors, entries);
83 png_set_tRNS(png, info, trans, entries, NULL);
84 png_write_info(png, info);
85 return true;
86}
87
88bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
89 png_bytep row = malloc(sizeof(png_byte) * width * 3);
90 if (!row) {
91 return false;
92 }
93 const png_byte* pixelData = pixels;
94 if (setjmp(png_jmpbuf(png))) {
95 free(row);
96 return false;
97 }
98 unsigned i;
99 for (i = 0; i < height; ++i) {
100 unsigned x;
101 for (x = 0; x < width; ++x) {
102#ifdef COLOR_16_BIT
103 uint16_t c = ((uint16_t*) pixelData)[stride * i + x];
104#ifdef COLOR_5_6_5
105 row[x * 3] = (c >> 8) & 0xF8;
106 row[x * 3 + 1] = (c >> 3) & 0xFC;
107 row[x * 3 + 2] = (c << 3) & 0xF8;
108#else
109 row[x * 3] = (c >> 7) & 0xF8;
110 row[x * 3 + 1] = (c >> 2) & 0xF8;
111 row[x * 3 + 2] = (c << 3) & 0xF8;
112#endif
113#else
114#ifdef __BIG_ENDIAN__
115 row[x * 3] = pixelData[stride * i * 4 + x * 4 + 3];
116 row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 2];
117 row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 1];
118#else
119 row[x * 3] = pixelData[stride * i * 4 + x * 4];
120 row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 1];
121 row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 2];
122#endif
123#endif
124 }
125 png_write_row(png, row);
126 }
127 free(row);
128 return true;
129}
130
131bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
132 UNUSED(width);
133 const png_byte* pixelData = pixels;
134 if (setjmp(png_jmpbuf(png))) {
135 return false;
136 }
137 unsigned i;
138 for (i = 0; i < height; ++i) {
139 png_write_row(png, &pixelData[stride * i]);
140 }
141 return true;
142}
143
144bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data) {
145 char realName[5];
146 strncpy(realName, name, 4);
147 realName[0] = tolower((int) realName[0]);
148 realName[1] = tolower((int) realName[1]);
149 realName[4] = '\0';
150 if (setjmp(png_jmpbuf(png))) {
151 return false;
152 }
153 png_write_chunk(png, (png_bytep) realName, data, size);
154 return true;
155}
156
157void PNGWriteClose(png_structp png, png_infop info) {
158 if (!setjmp(png_jmpbuf(png))) {
159 png_write_end(png, info);
160 }
161 png_destroy_write_struct(&png, &info);
162}
163
164bool isPNG(struct VFile* source) {
165 png_byte header[PNG_HEADER_BYTES];
166 if (source->read(source, header, PNG_HEADER_BYTES) < PNG_HEADER_BYTES) {
167 return false;
168 }
169 return !png_sig_cmp(header, 0, PNG_HEADER_BYTES);
170}
171
172png_structp PNGReadOpen(struct VFile* source, unsigned offset) {
173 png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
174 if (!png) {
175 return 0;
176 }
177 if (setjmp(png_jmpbuf(png))) {
178 png_destroy_read_struct(&png, 0, 0);
179 return 0;
180 }
181 png_set_read_fn(png, source, _pngRead);
182 png_set_sig_bytes(png, offset);
183 return png;
184}
185
186bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName) {
187 if (setjmp(png_jmpbuf(png))) {
188 return false;
189 }
190 png_set_read_user_chunk_fn(png, context, handler);
191 int len = strlen(chunkName);
192 int chunks = 0;
193 char* chunkList = strdup(chunkName);
194 int i;
195 for (i = 4; i <= len; i += 5) {
196 chunkList[i] = '\0';
197 ++chunks;
198 }
199 png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_bytep) chunkList, chunks);
200 free(chunkList);
201 return true;
202}
203
204bool PNGReadHeader(png_structp png, png_infop info) {
205 if (setjmp(png_jmpbuf(png))) {
206 return false;
207 }
208 png_read_info(png, info);
209 return true;
210}
211
212bool PNGIgnorePixels(png_structp png, png_infop info) {
213 if (setjmp(png_jmpbuf(png))) {
214 return false;
215 }
216
217 unsigned height = png_get_image_height(png, info);
218 unsigned i;
219 for (i = 0; i < height; ++i) {
220 png_read_row(png, 0, 0);
221 }
222 return true;
223}
224
225bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
226 if (setjmp(png_jmpbuf(png))) {
227 return false;
228 }
229
230 uint8_t* pixelData = pixels;
231 unsigned pngHeight = png_get_image_height(png, info);
232 if (height < pngHeight) {
233 pngHeight = height;
234 }
235
236 unsigned pngWidth = png_get_image_width(png, info);
237 if (width < pngWidth) {
238 pngWidth = width;
239 }
240
241 unsigned i;
242 png_bytep row = malloc(png_get_rowbytes(png, info));
243 for (i = 0; i < pngHeight; ++i) {
244 png_read_row(png, row, 0);
245 unsigned x;
246 for (x = 0; x < pngWidth; ++x) {
247#ifdef COLOR_16_BIT
248 uint16_t c = row[x * 3 + 2] >> 3;
249#ifdef COLOR_5_6_5
250 c |= (row[x * 3 + 1] << 3) & 0x7E0;
251 c |= (row[x * 3] << 8) & 0xF800;
252#else
253 c |= (row[x * 3 + 1] << 2) & 0x3E0;
254 c |= (row[x * 3] << 7) & 0x7C00;
255#endif
256 ((uint16_t*) pixelData)[stride * i + x] = c;
257#else
258#if __BIG_ENDIAN__
259 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 3];
260 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 1];
261 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 2];
262 pixelData[stride * i * 4 + x * 4] = 0xFF;
263#else
264 pixelData[stride * i * 4 + x * 4] = row[x * 3];
265 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 1];
266 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2];
267 pixelData[stride * i * 4 + x * 4 + 3] = 0xFF;
268#endif
269#endif
270 }
271 }
272 free(row);
273 return true;
274}
275
276bool PNGReadFooter(png_structp png, png_infop end) {
277 if (setjmp(png_jmpbuf(png))) {
278 return false;
279 }
280 png_read_end(png, end);
281 return true;
282}
283
284void PNGReadClose(png_structp png, png_infop info, png_infop end) {
285 png_destroy_read_struct(&png, &info, &end);
286}
287
288#endif