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