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
41static png_infop _pngWriteHeader(png_structp png, unsigned width, unsigned height, int type) {
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, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
50 return info;
51}
52
53png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) {
54 png_infop info = _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB);
55 png_write_info(png, info);
56 return info;
57}
58
59png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height) {
60 png_infop info = _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB_ALPHA);
61 png_write_info(png, info);
62 return info;
63}
64
65png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height) {
66 return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_PALETTE);
67}
68
69bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries) {
70 if (!palette || !entries) {
71 return false;
72 }
73 if (setjmp(png_jmpbuf(png))) {
74 return false;
75 }
76 png_color colors[256];
77 png_byte trans[256];
78 unsigned i;
79 for (i = 0; i < entries && i < 256; ++i) {
80 colors[i].red = palette[i];
81 colors[i].green = palette[i] >> 8;
82 colors[i].blue = palette[i] >> 16;
83 trans[i] = palette[i] >> 24;
84 }
85 png_set_PLTE(png, info, colors, entries);
86 png_set_tRNS(png, info, trans, entries, NULL);
87 png_write_info(png, info);
88 return true;
89}
90
91bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
92 png_bytep row = malloc(sizeof(png_byte) * width * 3);
93 if (!row) {
94 return false;
95 }
96 const png_byte* pixelData = pixels;
97 if (setjmp(png_jmpbuf(png))) {
98 free(row);
99 return false;
100 }
101 unsigned i;
102 for (i = 0; i < height; ++i) {
103 unsigned x;
104 for (x = 0; x < width; ++x) {
105#ifdef COLOR_16_BIT
106 uint16_t c = ((uint16_t*) pixelData)[stride * i + x];
107#ifdef COLOR_5_6_5
108 row[x * 3] = (c >> 8) & 0xF8;
109 row[x * 3 + 1] = (c >> 3) & 0xFC;
110 row[x * 3 + 2] = (c << 3) & 0xF8;
111#else
112 row[x * 3] = (c >> 7) & 0xF8;
113 row[x * 3 + 1] = (c >> 2) & 0xF8;
114 row[x * 3 + 2] = (c << 3) & 0xF8;
115#endif
116#else
117#ifdef __BIG_ENDIAN__
118 row[x * 3] = pixelData[stride * i * 4 + x * 4 + 3];
119 row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 2];
120 row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 1];
121#else
122 row[x * 3] = pixelData[stride * i * 4 + x * 4];
123 row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 1];
124 row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 2];
125#endif
126#endif
127 }
128 png_write_row(png, row);
129 }
130 free(row);
131 return true;
132}
133
134bool PNGWritePixelsA(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
135 png_bytep row = malloc(sizeof(png_byte) * width * 4);
136 if (!row) {
137 return false;
138 }
139 const png_byte* pixelData = pixels;
140 if (setjmp(png_jmpbuf(png))) {
141 free(row);
142 return false;
143 }
144 unsigned i;
145 for (i = 0; i < height; ++i) {
146 unsigned x;
147 for (x = 0; x < width; ++x) {
148#ifdef COLOR_16_BIT
149 uint16_t c = ((uint16_t*) pixelData)[stride * i + x];
150#ifdef COLOR_5_6_5
151 row[x * 4] = (c >> 8) & 0xF8;
152 row[x * 4 + 1] = (c >> 3) & 0xFC;
153 row[x * 4 + 2] = (c << 3) & 0xF8;
154 row[x * 4 + 3] = 0xFF;
155#else
156 row[x * 4] = (c >> 7) & 0xF8;
157 row[x * 4 + 1] = (c >> 2) & 0xF8;
158 row[x * 4 + 2] = (c << 3) & 0xF8;
159 row[x * 4 + 3] = (c >> 15) * 0xFF;
160#endif
161#else
162#ifdef __BIG_ENDIAN__
163 row[x * 4] = pixelData[stride * i * 4 + x * 4 + 3];
164 row[x * 4 + 1] = pixelData[stride * i * 4 + x * 4 + 2];
165 row[x * 4 + 2] = pixelData[stride * i * 4 + x * 4 + 1];
166 row[x * 4 + 3] = pixelData[stride * i * 4 + x * 4];
167#else
168 row[x * 4] = pixelData[stride * i * 4 + x * 4];
169 row[x * 4 + 1] = pixelData[stride * i * 4 + x * 4 + 1];
170 row[x * 4 + 2] = pixelData[stride * i * 4 + x * 4 + 2];
171 row[x * 4 + 3] = pixelData[stride * i * 4 + x * 4 + 3];
172#endif
173#endif
174 }
175 png_write_row(png, row);
176 }
177 free(row);
178 return true;
179}
180
181bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
182 UNUSED(width);
183 const png_byte* pixelData = pixels;
184 if (setjmp(png_jmpbuf(png))) {
185 return false;
186 }
187 unsigned i;
188 for (i = 0; i < height; ++i) {
189 png_write_row(png, &pixelData[stride * i]);
190 }
191 return true;
192}
193
194bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data) {
195 char realName[5];
196 strncpy(realName, name, 4);
197 realName[0] = tolower((int) realName[0]);
198 realName[1] = tolower((int) realName[1]);
199 realName[4] = '\0';
200 if (setjmp(png_jmpbuf(png))) {
201 return false;
202 }
203 png_write_chunk(png, (png_bytep) realName, data, size);
204 return true;
205}
206
207void PNGWriteClose(png_structp png, png_infop info) {
208 if (!setjmp(png_jmpbuf(png))) {
209 png_write_end(png, info);
210 }
211 png_destroy_write_struct(&png, &info);
212}
213
214bool isPNG(struct VFile* source) {
215 png_byte header[PNG_HEADER_BYTES];
216 if (source->read(source, header, PNG_HEADER_BYTES) < PNG_HEADER_BYTES) {
217 return false;
218 }
219 return !png_sig_cmp(header, 0, PNG_HEADER_BYTES);
220}
221
222png_structp PNGReadOpen(struct VFile* source, unsigned offset) {
223 png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
224 if (!png) {
225 return 0;
226 }
227 if (setjmp(png_jmpbuf(png))) {
228 png_destroy_read_struct(&png, 0, 0);
229 return 0;
230 }
231 png_set_read_fn(png, source, _pngRead);
232 png_set_sig_bytes(png, offset);
233 return png;
234}
235
236bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName) {
237 if (setjmp(png_jmpbuf(png))) {
238 return false;
239 }
240 png_set_read_user_chunk_fn(png, context, handler);
241 int len = strlen(chunkName);
242 int chunks = 0;
243 char* chunkList = strdup(chunkName);
244 int i;
245 for (i = 4; i <= len; i += 5) {
246 chunkList[i] = '\0';
247 ++chunks;
248 }
249 png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_bytep) chunkList, chunks);
250 free(chunkList);
251 return true;
252}
253
254bool PNGReadHeader(png_structp png, png_infop info) {
255 if (setjmp(png_jmpbuf(png))) {
256 return false;
257 }
258 png_read_info(png, info);
259 return true;
260}
261
262bool PNGIgnorePixels(png_structp png, png_infop info) {
263 if (setjmp(png_jmpbuf(png))) {
264 return false;
265 }
266
267 unsigned height = png_get_image_height(png, info);
268 unsigned i;
269 for (i = 0; i < height; ++i) {
270 png_read_row(png, 0, 0);
271 }
272 return true;
273}
274
275bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
276 if (setjmp(png_jmpbuf(png))) {
277 return false;
278 }
279
280 uint8_t* pixelData = pixels;
281 unsigned pngHeight = png_get_image_height(png, info);
282 if (height < pngHeight) {
283 pngHeight = height;
284 }
285
286 unsigned pngWidth = png_get_image_width(png, info);
287 if (width < pngWidth) {
288 pngWidth = width;
289 }
290
291 unsigned i;
292 png_bytep row = malloc(png_get_rowbytes(png, info));
293 for (i = 0; i < pngHeight; ++i) {
294 png_read_row(png, row, 0);
295 unsigned x;
296 for (x = 0; x < pngWidth; ++x) {
297#ifdef COLOR_16_BIT
298 uint16_t c = row[x * 3 + 2] >> 3;
299#ifdef COLOR_5_6_5
300 c |= (row[x * 3 + 1] << 3) & 0x7E0;
301 c |= (row[x * 3] << 8) & 0xF800;
302#else
303 c |= (row[x * 3 + 1] << 2) & 0x3E0;
304 c |= (row[x * 3] << 7) & 0x7C00;
305#endif
306 ((uint16_t*) pixelData)[stride * i + x] = c;
307#else
308#if __BIG_ENDIAN__
309 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 3];
310 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 1];
311 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 2];
312 pixelData[stride * i * 4 + x * 4] = 0xFF;
313#else
314 pixelData[stride * i * 4 + x * 4] = row[x * 3];
315 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 1];
316 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2];
317 pixelData[stride * i * 4 + x * 4 + 3] = 0xFF;
318#endif
319#endif
320 }
321 }
322 free(row);
323 return true;
324}
325
326bool PNGReadPixelsA(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
327 if (setjmp(png_jmpbuf(png))) {
328 return false;
329 }
330
331 uint8_t* pixelData = pixels;
332 unsigned pngHeight = png_get_image_height(png, info);
333 if (height < pngHeight) {
334 pngHeight = height;
335 }
336
337 unsigned pngWidth = png_get_image_width(png, info);
338 if (width < pngWidth) {
339 pngWidth = width;
340 }
341
342 unsigned i;
343 png_bytep row = malloc(png_get_rowbytes(png, info));
344 for (i = 0; i < pngHeight; ++i) {
345 png_read_row(png, row, 0);
346 unsigned x;
347 for (x = 0; x < pngWidth; ++x) {
348#ifdef COLOR_16_BIT
349 uint16_t c = row[x * 4 + 2] >> 3;
350#ifdef COLOR_5_6_5
351 c |= (row[x * 4 + 1] << 3) & 0x7E0;
352 c |= (row[x * 4] << 8) & 0xF800;
353#else
354 c |= (row[x * 4 + 1] << 2) & 0x3E0;
355 c |= (row[x * 4] << 7) & 0x7C00;
356#endif
357 ((uint16_t*) pixelData)[stride * i + x] = c;
358#else
359#if __BIG_ENDIAN__
360 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 4];
361 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 4 + 1];
362 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 4 + 2];
363 pixelData[stride * i * 4 + x * 4] = row[x * 4 + 3];
364#else
365 pixelData[stride * i * 4 + x * 4] = row[x * 4];
366 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 4 + 1];
367 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 4 + 2];
368 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 4 + 3];
369#endif
370#endif
371 }
372 }
373 free(row);
374 return true;
375}
376
377bool PNGReadPixels8(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
378 if (setjmp(png_jmpbuf(png))) {
379 return false;
380 }
381
382 uint8_t* pixelData = pixels;
383 unsigned pngHeight = png_get_image_height(png, info);
384 if (height < pngHeight) {
385 pngHeight = height;
386 }
387
388 unsigned pngWidth = png_get_image_width(png, info);
389 if (width < pngWidth) {
390 pngWidth = width;
391 }
392
393 unsigned i;
394 for (i = 0; i < pngHeight; ++i) {
395 png_read_row(png, &pixelData[stride * i], 0);
396 }
397 return true;
398}
399
400
401bool PNGReadFooter(png_structp png, png_infop end) {
402 if (setjmp(png_jmpbuf(png))) {
403 return false;
404 }
405 png_read_end(png, end);
406 return true;
407}
408
409void PNGReadClose(png_structp png, png_infop info, png_infop end) {
410 png_destroy_read_struct(&png, &info, &end);
411}
412
413#endif