all repos — mgba @ df082b46d9c9d0a31edc29d05f9c93fd5b17197e

mGBA Game Boy Advance Emulator

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