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 (png_get_channels(png, info) != 3) {
277 return false;
278 }
279
280 if (setjmp(png_jmpbuf(png))) {
281 return false;
282 }
283
284 uint8_t* pixelData = pixels;
285 unsigned pngHeight = png_get_image_height(png, info);
286 if (height < pngHeight) {
287 pngHeight = height;
288 }
289
290 unsigned pngWidth = png_get_image_width(png, info);
291 if (width < pngWidth) {
292 pngWidth = width;
293 }
294
295 unsigned i;
296 png_bytep row = malloc(png_get_rowbytes(png, info));
297 for (i = 0; i < pngHeight; ++i) {
298 png_read_row(png, row, 0);
299 unsigned x;
300 for (x = 0; x < pngWidth; ++x) {
301#ifdef COLOR_16_BIT
302 uint16_t c = row[x * 3 + 2] >> 3;
303#ifdef COLOR_5_6_5
304 c |= (row[x * 3 + 1] << 3) & 0x7E0;
305 c |= (row[x * 3] << 8) & 0xF800;
306#else
307 c |= (row[x * 3 + 1] << 2) & 0x3E0;
308 c |= (row[x * 3] << 7) & 0x7C00;
309#endif
310 ((uint16_t*) pixelData)[stride * i + x] = c;
311#else
312#if __BIG_ENDIAN__
313 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 3];
314 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 1];
315 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 2];
316 pixelData[stride * i * 4 + x * 4] = 0xFF;
317#else
318 pixelData[stride * i * 4 + x * 4] = row[x * 3];
319 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 1];
320 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2];
321 pixelData[stride * i * 4 + x * 4 + 3] = 0xFF;
322#endif
323#endif
324 }
325 }
326 free(row);
327 return true;
328}
329
330bool PNGReadPixelsA(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
331 if (png_get_channels(png, info) != 4) {
332 return false;
333 }
334
335 if (setjmp(png_jmpbuf(png))) {
336 return false;
337 }
338
339 uint8_t* pixelData = pixels;
340 unsigned pngHeight = png_get_image_height(png, info);
341 if (height < pngHeight) {
342 pngHeight = height;
343 }
344
345 unsigned pngWidth = png_get_image_width(png, info);
346 if (width < pngWidth) {
347 pngWidth = width;
348 }
349
350 unsigned i;
351 png_bytep row = malloc(png_get_rowbytes(png, info));
352 for (i = 0; i < pngHeight; ++i) {
353 png_read_row(png, row, 0);
354 unsigned x;
355 for (x = 0; x < pngWidth; ++x) {
356#ifdef COLOR_16_BIT
357 uint16_t c = row[x * 4 + 2] >> 3;
358#ifdef COLOR_5_6_5
359 c |= (row[x * 4 + 1] << 3) & 0x7E0;
360 c |= (row[x * 4] << 8) & 0xF800;
361#else
362 c |= (row[x * 4 + 1] << 2) & 0x3E0;
363 c |= (row[x * 4] << 7) & 0x7C00;
364#endif
365 ((uint16_t*) pixelData)[stride * i + x] = c;
366#else
367#if __BIG_ENDIAN__
368 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 4];
369 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 4 + 1];
370 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 4 + 2];
371 pixelData[stride * i * 4 + x * 4] = row[x * 4 + 3];
372#else
373 pixelData[stride * i * 4 + x * 4] = row[x * 4];
374 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 4 + 1];
375 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 4 + 2];
376 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 4 + 3];
377#endif
378#endif
379 }
380 }
381 free(row);
382 return true;
383}
384
385bool PNGReadPixels8(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
386 if (png_get_channels(png, info) != 1) {
387 return false;
388 }
389
390 if (setjmp(png_jmpbuf(png))) {
391 return false;
392 }
393
394 uint8_t* pixelData = pixels;
395 unsigned pngHeight = png_get_image_height(png, info);
396 if (height < pngHeight) {
397 pngHeight = height;
398 }
399
400 unsigned pngWidth = png_get_image_width(png, info);
401 if (width < pngWidth) {
402 pngWidth = width;
403 }
404
405 unsigned i;
406 for (i = 0; i < pngHeight; ++i) {
407 png_read_row(png, &pixelData[stride * i], 0);
408 }
409 return true;
410}
411
412
413bool PNGReadFooter(png_structp png, png_infop end) {
414 if (setjmp(png_jmpbuf(png))) {
415 return false;
416 }
417 png_read_end(png, end);
418 return true;
419}
420
421void PNGReadClose(png_structp png, png_infop info, png_infop end) {
422 png_destroy_read_struct(&png, &info, &end);
423}
424
425#endif