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