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 if (source->read(source, header, PNG_HEADER_BYTES) < PNG_HEADER_BYTES) {
214 return false;
215 }
216 return !png_sig_cmp(header, 0, PNG_HEADER_BYTES);
217}
218
219png_structp PNGReadOpen(struct VFile* source, unsigned offset) {
220 png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
221 if (!png) {
222 return 0;
223 }
224 if (setjmp(png_jmpbuf(png))) {
225 png_destroy_read_struct(&png, 0, 0);
226 return 0;
227 }
228 png_set_read_fn(png, source, _pngRead);
229 png_set_sig_bytes(png, offset);
230 return png;
231}
232
233bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName) {
234 if (setjmp(png_jmpbuf(png))) {
235 return false;
236 }
237 png_set_read_user_chunk_fn(png, context, handler);
238 int len = strlen(chunkName);
239 int chunks = 0;
240 char* chunkList = strdup(chunkName);
241 int i;
242 for (i = 4; i <= len; i += 5) {
243 chunkList[i] = '\0';
244 ++chunks;
245 }
246 png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_bytep) chunkList, chunks);
247 free(chunkList);
248 return true;
249}
250
251bool PNGReadHeader(png_structp png, png_infop info) {
252 if (setjmp(png_jmpbuf(png))) {
253 return false;
254 }
255 png_read_info(png, info);
256 return true;
257}
258
259bool PNGIgnorePixels(png_structp png, png_infop info) {
260 if (setjmp(png_jmpbuf(png))) {
261 return false;
262 }
263
264 unsigned height = png_get_image_height(png, info);
265 unsigned i;
266 for (i = 0; i < height; ++i) {
267 png_read_row(png, 0, 0);
268 }
269 return true;
270}
271
272bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
273 if (png_get_channels(png, info) != 3) {
274 return false;
275 }
276
277 if (setjmp(png_jmpbuf(png))) {
278 return false;
279 }
280
281 uint8_t* pixelData = pixels;
282 unsigned pngHeight = png_get_image_height(png, info);
283 if (height < pngHeight) {
284 pngHeight = height;
285 }
286
287 unsigned pngWidth = png_get_image_width(png, info);
288 if (width < pngWidth) {
289 pngWidth = width;
290 }
291
292 unsigned i;
293 png_bytep row = malloc(png_get_rowbytes(png, info));
294 for (i = 0; i < pngHeight; ++i) {
295 png_read_row(png, row, 0);
296 unsigned x;
297 for (x = 0; x < pngWidth; ++x) {
298#ifdef COLOR_16_BIT
299 uint16_t c = row[x * 3 + 2] >> 3;
300#ifdef COLOR_5_6_5
301 c |= (row[x * 3 + 1] << 3) & 0x7E0;
302 c |= (row[x * 3] << 8) & 0xF800;
303#else
304 c |= (row[x * 3 + 1] << 2) & 0x3E0;
305 c |= (row[x * 3] << 7) & 0x7C00;
306#endif
307 ((uint16_t*) pixelData)[stride * i + x] = c;
308#else
309#if __BIG_ENDIAN__
310 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 3];
311 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 1];
312 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 2];
313 pixelData[stride * i * 4 + x * 4] = 0xFF;
314#else
315 pixelData[stride * i * 4 + x * 4] = row[x * 3];
316 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 1];
317 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2];
318 pixelData[stride * i * 4 + x * 4 + 3] = 0xFF;
319#endif
320#endif
321 }
322 }
323 free(row);
324 return true;
325}
326
327bool PNGReadPixelsA(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
328 if (png_get_channels(png, info) != 4) {
329 return false;
330 }
331
332 if (setjmp(png_jmpbuf(png))) {
333 return false;
334 }
335
336 uint8_t* pixelData = pixels;
337 unsigned pngHeight = png_get_image_height(png, info);
338 if (height < pngHeight) {
339 pngHeight = height;
340 }
341
342 unsigned pngWidth = png_get_image_width(png, info);
343 if (width < pngWidth) {
344 pngWidth = width;
345 }
346
347 unsigned i;
348 png_bytep row = malloc(png_get_rowbytes(png, info));
349 for (i = 0; i < pngHeight; ++i) {
350 png_read_row(png, row, 0);
351 unsigned x;
352 for (x = 0; x < pngWidth; ++x) {
353#ifdef COLOR_16_BIT
354 uint16_t c = row[x * 4 + 2] >> 3;
355#ifdef COLOR_5_6_5
356 c |= (row[x * 4 + 1] << 3) & 0x7E0;
357 c |= (row[x * 4] << 8) & 0xF800;
358#else
359 c |= (row[x * 4 + 1] << 2) & 0x3E0;
360 c |= (row[x * 4] << 7) & 0x7C00;
361#endif
362 ((uint16_t*) pixelData)[stride * i + x] = c;
363#else
364#if __BIG_ENDIAN__
365 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 4];
366 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 4 + 1];
367 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 4 + 2];
368 pixelData[stride * i * 4 + x * 4] = row[x * 4 + 3];
369#else
370 pixelData[stride * i * 4 + x * 4] = row[x * 4];
371 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 4 + 1];
372 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 4 + 2];
373 pixelData[stride * i * 4 + x * 4 + 3] = row[x * 4 + 3];
374#endif
375#endif
376 }
377 }
378 free(row);
379 return true;
380}
381
382bool PNGReadPixels8(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
383 if (png_get_channels(png, info) != 1) {
384 return false;
385 }
386
387 if (setjmp(png_jmpbuf(png))) {
388 return false;
389 }
390
391 uint8_t* pixelData = pixels;
392 unsigned pngHeight = png_get_image_height(png, info);
393 if (height < pngHeight) {
394 pngHeight = height;
395 }
396
397 unsigned pngWidth = png_get_image_width(png, info);
398 if (width < pngWidth) {
399 pngWidth = width;
400 }
401
402 unsigned i;
403 for (i = 0; i < pngHeight; ++i) {
404 png_read_row(png, &pixelData[stride * i], 0);
405 }
406 return true;
407}
408
409
410bool PNGReadFooter(png_structp png, png_infop end) {
411 if (setjmp(png_jmpbuf(png))) {
412 return false;
413 }
414 png_read_end(png, end);
415 return true;
416}
417
418void PNGReadClose(png_structp png, png_infop info, png_infop end) {
419 png_destroy_read_struct(&png, &info, &end);
420}
421
422#endif