all repos — mgba @ e4d3aefb4abe52e2aed7a77bb962ff91560908e7

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
 41png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) {
 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, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
 50	png_write_info(png, info);
 51	return info;
 52}
 53
 54bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
 55	png_bytep row = malloc(sizeof(png_byte) * width * 3);
 56	if (!row) {
 57		return false;
 58	}
 59	const png_byte* pixelData = pixels;
 60	if (setjmp(png_jmpbuf(png))) {
 61		free(row);
 62		return false;
 63	}
 64	unsigned i;
 65	for (i = 0; i < height; ++i) {
 66		unsigned x;
 67		for (x = 0; x < width; ++x) {
 68#ifdef COLOR_16_BIT
 69			uint16_t c = ((uint16_t*) pixelData)[stride * i + x];
 70#ifdef COLOR_5_6_5
 71			row[x * 3] = (c >> 8) & 0xF8;
 72			row[x * 3 + 1] = (c >> 3) & 0xFC;
 73			row[x * 3 + 2] = (c << 3) & 0xF8;
 74#else
 75			row[x * 3] = (c >> 7) & 0xF8;
 76			row[x * 3 + 1] = (c >> 2) & 0xF8;
 77			row[x * 3 + 2] = (c << 3) & 0xF8;
 78#endif
 79#else
 80#ifdef __BIG_ENDIAN__
 81			row[x * 3] = pixelData[stride * i * 4 + x * 4 + 3];
 82			row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 2];
 83			row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 1];
 84#else
 85			row[x * 3] = pixelData[stride * i * 4 + x * 4];
 86			row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 1];
 87			row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 2];
 88#endif
 89#endif
 90		}
 91		png_write_row(png, row);
 92	}
 93	free(row);
 94	return true;
 95}
 96
 97bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data) {
 98	char realName[5];
 99	strncpy(realName, name, 4);
100	realName[0] = tolower((int) realName[0]);
101	realName[1] = tolower((int) realName[1]);
102	realName[4] = '\0';
103	if (setjmp(png_jmpbuf(png))) {
104		return false;
105	}
106	png_write_chunk(png, (png_bytep) realName, data, size);
107	return true;
108}
109
110void PNGWriteClose(png_structp png, png_infop info) {
111	if (!setjmp(png_jmpbuf(png))) {
112		png_write_end(png, info);
113	}
114	png_destroy_write_struct(&png, &info);
115}
116
117bool isPNG(struct VFile* source) {
118	png_byte header[PNG_HEADER_BYTES];
119	if (source->read(source, header, PNG_HEADER_BYTES) < PNG_HEADER_BYTES) {
120		return false;
121	}
122	return !png_sig_cmp(header, 0, PNG_HEADER_BYTES);
123}
124
125png_structp PNGReadOpen(struct VFile* source, unsigned offset) {
126	png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
127	if (!png) {
128		return 0;
129	}
130	if (setjmp(png_jmpbuf(png))) {
131		png_destroy_read_struct(&png, 0, 0);
132		return 0;
133	}
134	png_set_read_fn(png, source, _pngRead);
135	png_set_sig_bytes(png, offset);
136	return png;
137}
138
139bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName) {
140	if (setjmp(png_jmpbuf(png))) {
141		return false;
142	}
143	png_set_read_user_chunk_fn(png, context, handler);
144	int len = strlen(chunkName);
145	int chunks = 0;
146	char* chunkList = strdup(chunkName);
147	int i;
148	for (i = 4; i <= len; i += 5) {
149		chunkList[i] = '\0';
150		++chunks;
151	}
152	png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_bytep) chunkList, chunks);
153	free(chunkList);
154	return true;
155}
156
157bool PNGReadHeader(png_structp png, png_infop info) {
158	if (setjmp(png_jmpbuf(png))) {
159		return false;
160	}
161	png_read_info(png, info);
162	return true;
163}
164
165bool PNGIgnorePixels(png_structp png, png_infop info) {
166	if (setjmp(png_jmpbuf(png))) {
167		return false;
168	}
169
170	unsigned height = png_get_image_height(png, info);
171	unsigned i;
172	for (i = 0; i < height; ++i) {
173		png_read_row(png, 0, 0);
174	}
175	return true;
176}
177
178bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
179	if (setjmp(png_jmpbuf(png))) {
180		return false;
181	}
182
183	uint8_t* pixelData = pixels;
184	unsigned pngHeight = png_get_image_height(png, info);
185	if (height < pngHeight) {
186		pngHeight = height;
187	}
188
189	unsigned pngWidth = png_get_image_width(png, info);
190	if (width < pngWidth) {
191		pngWidth = width;
192	}
193
194	unsigned i;
195	png_bytep row = malloc(png_get_rowbytes(png, info));
196	for (i = 0; i < pngHeight; ++i) {
197		png_read_row(png, row, 0);
198		unsigned x;
199		for (x = 0; x < pngWidth; ++x) {
200#ifdef COLOR_16_BIT
201			uint16_t c = row[x * 3 + 2] >> 3;
202#ifdef COLOR_5_6_5
203			c |= (row[x * 3 + 1] << 3) & 0x7E0;
204			c |= (row[x * 3] << 8) & 0xF800;
205#else
206			c |= (row[x * 3 + 1] << 2) & 0x3E0;
207			c |= (row[x * 3] << 7) & 0x7C00;
208#endif
209			((uint16_t*) pixelData)[stride * i + x] = c;
210#else
211#if __BIG_ENDIAN__
212			pixelData[stride * i * 4 + x * 4 + 3] = row[x * 3];
213			pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 1];
214			pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 2];
215			pixelData[stride * i * 4 + x * 4] = 0xFF;
216#else
217			pixelData[stride * i * 4 + x * 4] = row[x * 3];
218			pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 1];
219			pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2];
220			pixelData[stride * i * 4 + x * 4 + 3] = 0xFF;
221#endif
222#endif
223		}
224	}
225	free(row);
226	return true;
227}
228
229bool PNGReadFooter(png_structp png, png_infop end) {
230	if (setjmp(png_jmpbuf(png))) {
231		return false;
232	}
233	png_read_end(png, end);
234	return true;
235}
236
237void PNGReadClose(png_structp png, png_infop info, png_infop end) {
238	png_destroy_read_struct(&png, &info, &end);
239}
240
241#endif