comparison png.c @ 1692:5dacaef602a7 segacd

Merge from default
author Michael Pavone <pavone@retrodev.com>
date Sat, 05 Jan 2019 00:58:08 -0800
parents 31effaadf877
children 81eebbe6b2e3
comparison
equal deleted inserted replaced
1504:95b3a1a8b26c 1692:5dacaef602a7
1 #include <stdint.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include "zlib/zlib.h"
6
7 static const char png_magic[] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n'};
8 static const char ihdr[] = {'I', 'H', 'D', 'R'};
9 static const char plte[] = {'P', 'L', 'T', 'E'};
10 static const char idat[] = {'I', 'D', 'A', 'T'};
11 static const char iend[] = {'I', 'E', 'N', 'D'};
12
13 enum {
14 COLOR_GRAY,
15 COLOR_TRUE = 2,
16 COLOR_INDEXED,
17 COLOR_GRAY_ALPHA,
18 COLOR_TRUE_ALPHA=6
19 };
20
21 static void write_chunk(FILE *f, const char*id, uint8_t *buffer, uint32_t size)
22 {
23 uint8_t tmp[4] = {size >> 24, size >> 16, size >> 8, size};
24 uint8_t warn = 0;
25 warn = warn || (sizeof(tmp) != fwrite(tmp, 1, sizeof(tmp), f));
26 warn = warn || (4 != fwrite(id, 1, 4, f));
27 if (size) {
28 warn = warn || (size != fwrite(buffer, 1, size, f));
29 }
30
31 uint32_t crc = crc32(0, NULL, 0);
32 crc = crc32(crc, id, 4);
33 if (size) {
34 crc = crc32(crc, buffer, size);
35 }
36 tmp[0] = crc >> 24;
37 tmp[1] = crc >> 16;
38 tmp[2] = crc >> 8;
39 tmp[3] = crc;
40 warn = warn || (sizeof(tmp) != fwrite(tmp, 1, sizeof(tmp), f));
41 if (warn) {
42 fprintf(stderr, "Failure during write of %c%c%c%c chunk\n", id[0], id[1], id[2], id[3]);
43 }
44 }
45
46 static void write_header(FILE *f, uint32_t width, uint32_t height, uint8_t color_type)
47 {
48 uint8_t chunk[13] = {
49 width >> 24, width >> 16, width >> 8, width,
50 height >> 24, height >> 16, height >> 8, height,
51 8, color_type, 0, 0, 0
52 };
53 if (sizeof(png_magic) != fwrite(png_magic, 1, sizeof(png_magic), f)) {
54 fputs("Error writing PNG magic\n", stderr);
55 }
56 write_chunk(f, ihdr, chunk, sizeof(chunk));
57 }
58
59 void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
60 {
61 uint32_t idat_size = (1 + width*3) * height;
62 uint8_t *idat_buffer = malloc(idat_size);
63 uint32_t *pixel = buffer;
64 uint8_t *cur = idat_buffer;
65 for (uint32_t y = 0; y < height; y++)
66 {
67 //save filter type
68 *(cur++) = 0;
69 uint32_t *start = pixel;
70 for (uint32_t x = 0; x < width; x++, pixel++)
71 {
72 uint32_t value = *pixel;
73 *(cur++) = value >> 16;
74 *(cur++) = value >> 8;
75 *(cur++) = value;
76 }
77 pixel = start + pitch / sizeof(uint32_t);
78 }
79 write_header(f, width, height, COLOR_TRUE);
80 uLongf compress_buffer_size = idat_size + 5 * (idat_size/16383 + 1) + 3;
81 uint8_t *compressed = malloc(compress_buffer_size);
82 compress(compressed, &compress_buffer_size, idat_buffer, idat_size);
83 free(idat_buffer);
84 write_chunk(f, idat, compressed, compress_buffer_size);
85 write_chunk(f, iend, NULL, 0);
86 free(compressed);
87 }
88
89 void save_png(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
90 {
91 uint32_t palette[256];
92 uint8_t pal_buffer[256*3];
93 uint32_t num_pal = 0;
94 uint32_t index_size = (1 + width) * height;
95 uint8_t *index_buffer = malloc(index_size);
96 uint8_t *cur = index_buffer;
97 uint32_t *pixel = buffer;
98 for (uint32_t y = 0; y < height; y++)
99 {
100 //save filter type
101 *(cur++) = 0;
102 uint32_t *start = pixel;
103 for (uint32_t x = 0; x < width; x++, pixel++, cur++)
104 {
105 uint32_t value = (*pixel) & 0xFFFFFF;
106 uint32_t i;
107 for (i = 0; i < num_pal; i++)
108 {
109 if (palette[i] == value) {
110 break;
111 }
112 }
113 if (i == num_pal) {
114 if (num_pal == 256) {
115 free(index_buffer);
116 save_png24(f, buffer, width, height, pitch);
117 return;
118 }
119 palette[i] = value;
120 num_pal++;
121 }
122 *cur = i;
123 }
124 pixel = start + pitch / sizeof(uint32_t);
125 }
126 write_header(f, width, height, COLOR_INDEXED);
127 cur = pal_buffer;
128 for (uint32_t i = 0; i < num_pal; i++)
129 {
130 *(cur++) = palette[i] >> 16;
131 *(cur++) = palette[i] >> 8;
132 *(cur++) = palette[i];
133 }
134 write_chunk(f, plte, pal_buffer, num_pal * 3);
135 uLongf compress_buffer_size = index_size + 5 * (index_size/16383 + 1) + 3;
136 uint8_t *compressed = malloc(compress_buffer_size);
137 compress(compressed, &compress_buffer_size, index_buffer, index_size);
138 free(index_buffer);
139 write_chunk(f, idat, compressed, compress_buffer_size);
140 write_chunk(f, iend, NULL, 0);
141 free(compressed);
142 }
143
144 typedef uint8_t (*filter_fun)(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x);
145 typedef uint32_t (*pixel_fun)(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun);
146
147 static uint8_t filter_none(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
148 {
149 return *cur;
150 }
151
152 static uint8_t filter_sub(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
153 {
154 if (x) {
155 return *cur + *(cur - bpp);
156 } else {
157 return *cur;
158 }
159 }
160
161 static uint8_t filter_up(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
162 {
163 if (last) {
164 return *cur + *last;
165 } else {
166 return *cur;
167 }
168 }
169
170 static uint8_t filter_avg(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
171 {
172 uint8_t prev = x ? *(cur - bpp) : 0;
173 uint8_t prior = last ? *last : 0;
174 return *cur + ((prev + prior) >> 1);
175 }
176
177 static uint8_t paeth(uint8_t a, uint8_t b, uint8_t c)
178 {
179 int32_t p = a + b - c;
180 int32_t pa = abs(p - a);
181 int32_t pb = abs(p - b);
182 int32_t pc = abs(p - c);
183 if (pa <= pb && pa <= pc) {
184 return a;
185 }
186 if (pb <= pc) {
187 return b;
188 }
189 return c;
190 }
191
192 static uint8_t filter_paeth(uint8_t *cur, uint8_t *last, uint8_t bpp, uint32_t x)
193 {
194 uint8_t prev, prev_prior;
195 if (x) {
196 prev = *(cur - bpp);
197 prev_prior = *(last - bpp);
198 } else {
199 prev = prev_prior = 0;
200 }
201 uint8_t prior = last ? *last : 0;
202 return *cur + paeth(prev, prior, prev_prior);
203 }
204
205 static uint32_t pixel_gray(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
206 {
207 uint8_t value = filter(*cur, *last, bpp, x);
208 (*cur)++;
209 if (*last) {
210 (*last)++;
211 }
212 return 0xFF000000 | value << 16 | value << 8 | value;
213 }
214
215 static uint32_t pixel_true(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
216 {
217 uint8_t red = filter(*cur, *last, bpp, x);
218 (*cur)++;
219 if (*last) {
220 (*last)++;
221 }
222 uint8_t green = filter(*cur, *last, bpp, x);
223 (*cur)++;
224 if (*last) {
225 (*last)++;
226 }
227 uint8_t blue = filter(*cur, *last, bpp, x);
228 (*cur)++;
229 if (*last) {
230 (*last)++;
231 }
232 return 0xFF000000 | red << 16 | green << 8 | blue;
233 }
234
235 static uint32_t pixel_gray_alpha(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
236 {
237 uint8_t value = filter(*cur, *last, bpp, x);
238 (*cur)++;
239 if (*last) {
240 (*last)++;
241 }
242 uint8_t alpha = filter(*cur, *last, bpp, x);
243 (*cur)++;
244 if (*last) {
245 (*last)++;
246 }
247 return alpha << 24 | value << 16 | value << 8 | value;
248 }
249
250 static uint32_t pixel_true_alpha(uint8_t **cur, uint8_t **last, uint8_t bpp, uint32_t x, filter_fun filter)
251 {
252 uint8_t red = filter(*cur, *last, bpp, x);
253 (*cur)++;
254 if (*last) {
255 (*last)++;
256 }
257 uint8_t green = filter(*cur, *last, bpp, x);
258 (*cur)++;
259 if (*last) {
260 (*last)++;
261 }
262 uint8_t blue = filter(*cur, *last, bpp, x);
263 (*cur)++;
264 if (*last) {
265 (*last)++;
266 }
267 uint8_t alpha = filter(*cur, *last, bpp, x);
268 (*cur)++;
269 if (*last) {
270 (*last)++;
271 }
272 return alpha << 24 | red << 16 | green << 8 | blue;
273 }
274
275 static filter_fun filters[] = {filter_none, filter_sub, filter_up, filter_avg, filter_paeth};
276
277 #define MIN_CHUNK_SIZE 12
278 #define MIN_IHDR_SIZE 0xD
279 #define MAX_SUPPORTED_DIM 32767 //chosen to avoid possibility of overflow when calculating uncompressed size
280 uint32_t *load_png(uint8_t *buffer, uint32_t buf_size, uint32_t *width, uint32_t *height)
281 {
282 if (buf_size < sizeof(png_magic) || memcmp(buffer, png_magic, sizeof(png_magic))) {
283 return NULL;
284 }
285 uint32_t cur = sizeof(png_magic);
286 uint8_t has_header = 0;
287 uint8_t bits, color_type, comp_type, filter_type, interlace;
288 uint8_t *idat_buf = NULL;
289 uint8_t idat_needs_free = 0;
290 uint32_t idat_size;
291 uint32_t *out = NULL;
292 uint32_t *palette = NULL;
293 while(cur + MIN_CHUNK_SIZE <= buf_size)
294 {
295 uint32_t chunk_size = buffer[cur++] << 24;
296 chunk_size |= buffer[cur++] << 16;
297 chunk_size |= buffer[cur++] << 8;
298 chunk_size |= buffer[cur++];
299 if (!memcmp(ihdr, buffer + cur, sizeof(ihdr))) {
300 if (chunk_size < MIN_IHDR_SIZE || cur + MIN_IHDR_SIZE > buf_size) {
301 return NULL;
302 }
303 cur += sizeof(ihdr);
304 *width = buffer[cur++] << 24;
305 *width |= buffer[cur++] << 16;
306 *width |= buffer[cur++] << 8;
307 *width |= buffer[cur++];
308 *height = buffer[cur++] << 24;
309 *height |= buffer[cur++] << 16;
310 *height |= buffer[cur++] << 8;
311 *height |= buffer[cur++];
312 if (*width > MAX_SUPPORTED_DIM || *height > MAX_SUPPORTED_DIM) {
313 return NULL;
314 }
315 bits = buffer[cur++];
316 if (bits != 8) {
317 //only support 8-bits per element for now
318 return NULL;
319 }
320 color_type = buffer[cur++];
321 if (color_type > COLOR_TRUE_ALPHA || color_type == 1 || color_type == 5) {
322 //reject invalid color type
323 return NULL;
324 }
325 comp_type = buffer[cur++];
326 if (comp_type) {
327 //only compression type 0 is defined by the spec
328 return NULL;
329 }
330 filter_type = buffer[cur++];
331 interlace = buffer[cur++];
332 if (interlace) {
333 //interlacing not supported for now
334 return NULL;
335 }
336 cur += chunk_size - MIN_IHDR_SIZE;
337 has_header = 1;
338 } else {
339 if (!has_header) {
340 //IHDR is required to be the first chunk, fail if it isn't
341 break;
342 }
343 if (!memcmp(plte, buffer + cur, sizeof(plte))) {
344 //TODO: implement paletted images
345 } else if (!memcmp(idat, buffer + cur, sizeof(idat))) {
346 cur += sizeof(idat);
347 if (idat_buf) {
348 if (idat_needs_free) {
349 idat_buf = realloc(idat_buf, idat_size + chunk_size);
350 } else {
351 uint8_t *tmp = idat_buf;
352 idat_buf = malloc(idat_size + chunk_size);
353 memcpy(idat_buf, tmp, idat_size);
354 }
355 memcpy(idat_buf + idat_size, buffer + cur, chunk_size);
356 idat_size += chunk_size;
357 } else {
358 idat_buf = buffer + cur;
359 idat_size = chunk_size;
360 }
361 cur += chunk_size;
362 } else if (!memcmp(iend, buffer + cur, sizeof(iend))) {
363 if (!idat_buf) {
364 break;
365 }
366 if (!palette && color_type == COLOR_INDEXED) {
367 //indexed color, but no PLTE chunk found
368 return NULL;
369 }
370 uLongf uncompressed_size = *width * *height;
371 uint8_t bpp;
372 pixel_fun pixel;
373 switch (color_type)
374 {
375 case COLOR_GRAY:
376 uncompressed_size *= bits / 8;
377 bpp = bits/8;
378 pixel = pixel_gray;
379 break;
380 case COLOR_TRUE:
381 uncompressed_size *= 3 * bits / 8;
382 bpp = 3 * bits/8;
383 pixel = pixel_true;
384 break;
385 case COLOR_INDEXED: {
386 uint32_t pixels_per_byte = 8 / bits;
387 uncompressed_size = (*width / pixels_per_byte) * *height;
388 if (*width % pixels_per_byte) {
389 uncompressed_size += *height;
390 }
391 bpp = 1;
392 break;
393 }
394 case COLOR_GRAY_ALPHA:
395 uncompressed_size *= bits / 4;
396 bpp = bits / 4;
397 pixel = pixel_gray_alpha;
398 break;
399 case COLOR_TRUE_ALPHA:
400 uncompressed_size *= bits / 2;
401 bpp = bits / 2;
402 pixel = pixel_true_alpha;
403 break;
404 }
405 //add filter type byte
406 uncompressed_size += *height;
407 uint8_t *decomp_buffer = malloc(uncompressed_size);
408 if (Z_OK != uncompress(decomp_buffer, &uncompressed_size, idat_buf, idat_size)) {
409 free(decomp_buffer);
410 break;
411 }
412 out = calloc(*width * *height, sizeof(uint32_t));
413 uint32_t *cur_pixel = out;
414 uint8_t *cur_byte = decomp_buffer;
415 uint8_t *last_line = NULL;
416 for (uint32_t y = 0; y < *height; y++)
417 {
418 uint8_t filter_type = *(cur_byte++);
419 if (filter_type >= sizeof(filters)/sizeof(*filters)) {
420 free(out);
421 out = NULL;
422 free(decomp_buffer);
423 break;
424 }
425 filter_fun filter = filters[filter_type];
426 uint8_t *line_start = cur_byte;
427 for (uint32_t x = 0; x < *width; x++)
428 {
429 *(cur_pixel++) = pixel(&cur_byte, &last_line, bpp, x, filter);
430 }
431 last_line = line_start;
432 }
433 free(decomp_buffer);
434 } else {
435 //skip uncrecognized chunks
436 cur += 4 + chunk_size;
437 }
438 }
439 //skip CRC for now
440 cur += sizeof(uint32_t);
441 }
442 if (idat_needs_free) {
443 free(idat_buf);
444 }
445 free(palette);
446 return out;
447 }