all repos — mgba @ 9de8f084ba55460b02d300c1dd8b8e6c56f691d5

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	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