JSON-based controller descriptions
[lsnes.git] / src / library / png-decoder.cpp
blob6289fb5539be322bd33526e8eca1fcd49642f04d
1 #include "png-decoder.hpp"
2 #include "serialization.hpp"
3 #include "minmax.hpp"
4 #include "zip.hpp"
5 #include <iostream>
6 #include <fstream>
7 #include <cstdint>
8 #include <iomanip>
9 #include <sstream>
10 #include <stdexcept>
11 #include <zlib.h>
12 #include <string.hpp>
14 namespace
16 void throw_zlib_error(int x)
18 switch(x) {
19 case Z_NEED_DICT: throw std::runtime_error("Dictionary needed");
20 case Z_ERRNO: throw std::runtime_error(std::string("OS error: ") + strerror(errno));
21 case Z_STREAM_ERROR: throw std::runtime_error("Stream error");
22 case Z_DATA_ERROR: throw std::runtime_error("Data error");
23 case Z_MEM_ERROR: throw std::bad_alloc();
24 case Z_BUF_ERROR: throw std::runtime_error("Buffer error");
25 case Z_VERSION_ERROR: throw std::runtime_error("Version error");
26 case Z_OK:
27 case Z_STREAM_END:
28 break;
29 default: throw std::runtime_error("Unknown error");
33 void* zlib_alloc(void* dummy, unsigned a, unsigned b)
35 return calloc(a, b);
38 void zlib_free(void* dummy, void* addr)
40 free(addr);
43 //=========================================================
44 //=================== AUTORELEASE =========================
45 //=========================================================
46 template <typename T>
47 class autorelease
49 public:
50 autorelease(T& _obj) : obj(_obj) {}
51 ~autorelease() { delete &obj; }
52 T* operator->() { return &obj; }
53 T& operator*() { return obj; }
54 private:
55 T& obj;
56 autorelease(const autorelease&);
57 const autorelease& operator=(const autorelease&);
60 //=========================================================
61 //=================== PNG DECHUNKER =======================
62 //=========================================================
63 class png_dechunker
65 public:
66 png_dechunker(std::istream& _stream);
67 uint32_t chunk_size() { return size; }
68 uint32_t chunk_type() { return type; }
69 uint32_t chunk_read(uint8_t* buf, size_t limit);
70 bool next_chunk();
71 bool eof() { return eof_flag; }
72 bool chunk_eof() { return ptr == size; }
73 private:
74 std::istream& stream;
75 uint32_t size;
76 uint32_t type;
77 uint32_t ptr;
78 uint32_t crc;
79 bool eof_flag;
80 void verify_crc();
81 void load_chunk();
84 png_dechunker::png_dechunker(std::istream& _stream)
85 : stream(_stream)
87 size = 0;
88 type = 0;
89 ptr = 0;
90 eof_flag = false;
91 uint8_t magic[8] = {137, 80, 78, 71, 13, 10, 26, 10};
92 uint8_t magicbuf[8];
93 stream.read(reinterpret_cast<char*>(magicbuf), 8);
94 if(!stream || memcmp(magic, magicbuf, 8))
95 throw std::runtime_error("Not a PNG file");
98 uint32_t png_dechunker::chunk_read(uint8_t* buf, size_t limit)
100 limit = min(limit, static_cast<size_t>(size - ptr));
101 if(!limit)
102 return 0;
103 stream.read(reinterpret_cast<char*>(buf), limit);
104 if(!stream)
105 throw std::runtime_error("PNG file truncated");
106 crc = crc32(crc, buf, limit);
107 ptr += limit;
108 if(ptr == size)
109 verify_crc();
110 return limit;
113 bool png_dechunker::next_chunk()
115 if(eof_flag)
116 return false;
117 while(ptr < size) {
118 uint8_t buf[256];
119 chunk_read(buf, 256);
121 load_chunk();
122 return !eof_flag;
125 void png_dechunker::load_chunk()
127 uint8_t buf[8];
128 stream.read(reinterpret_cast<char*>(buf), 8);
129 if(!stream) {
130 if(stream.gcount() == 0) {
131 //EOF.
132 size = 0;
133 type = 0;
134 ptr = 0;
135 eof_flag = true;
136 return;
138 throw std::runtime_error("PNG file truncated");
140 size = read32ube(buf + 0);
141 type = read32ube(buf + 4);
142 crc = crc32(0, NULL, 0);
143 crc = crc32(crc, buf + 4, 4);
144 ptr = 0;
145 if(!size)
146 verify_crc();
149 void png_dechunker::verify_crc()
151 char buf[4];
152 stream.read(buf, 4);
153 if(!stream)
154 throw std::runtime_error("PNG file truncated");
155 uint32_t claim_crc = read32ube(buf);
156 if(crc != claim_crc)
157 throw std::runtime_error("PNG file chunk CRC check failed");
160 std::string format_type(uint32_t type)
162 uint8_t t1 = type >> 24;
163 uint8_t t2 = type >> 16;
164 uint8_t t3 = type >> 8;
165 uint8_t t4 = type;
166 if(t1 < 'A' || t2 < 'A' || t3 < 'A' || t4 < 'A')
167 goto badtype;
168 if(t1 > 'z' || t2 > 'z' || t3 > 'z' || t4 > 'z')
169 goto badtype;
170 return (stringfmt() << t1 << t2 << t3 << t4).str();
171 badtype:
172 return (stringfmt() << "0x" << std::hex << std::setw(8) << std::setfill('0') << type).str();
175 //=========================================================
176 //=================== PNG IHDR CHUNK ======================
177 //=========================================================
178 struct ihdr_chunk
180 ihdr_chunk(png_dechunker& d)
182 if(d.chunk_type() != 0x49484452)
183 throw std::runtime_error("Expected IHDR chunk");
184 if(d.chunk_size() != 13)
185 throw std::runtime_error("Expected IHDR chunk to be 13 bytes");
186 uint8_t buf[13];
187 d.chunk_read(buf, 13);
188 width = read32ube(buf + 0);
189 height = read32ube(buf + 4);
190 depth = buf[8];
191 type = buf[9];
192 compression = buf[10];
193 filter = buf[11];
194 interlace = buf[12];
196 size_t width;
197 size_t height;
198 uint8_t type;
199 uint8_t depth;
200 uint8_t compression;
201 uint8_t interlace;
202 uint8_t filter;
205 //=========================================================
206 //=================== PNG DECOMPRESSOR ====================
207 //=========================================================
208 class png_decompressor
210 public:
211 png_decompressor(png_dechunker& _dechunk) : dechunk(_dechunk) {}
212 virtual ~png_decompressor() {}
213 virtual void decompress(uint8_t*& out, size_t& outsize) = 0;
214 static png_decompressor& get(png_dechunker& dechunk, uint8_t compression);
215 protected:
216 png_dechunker& dechunk;
217 private:
218 png_decompressor(const png_decompressor&);
219 png_decompressor& operator=(const png_decompressor&);
222 class png_decompressor_zlib : public png_decompressor
224 public:
225 png_decompressor_zlib(png_dechunker& dechunk)
226 : png_decompressor(dechunk)
228 memset(&z, 0, sizeof(z));
229 z.zalloc = zlib_alloc;
230 z.zfree = zlib_free;
231 throw_zlib_error(inflateInit(&z));
232 buflen = 0;
233 bufptr = 0;
235 ~png_decompressor_zlib()
237 inflateEnd(&z);
239 void decompress(uint8_t*& out, size_t& outsize)
241 while(true) {
242 if(!buflen) {
243 buflen = dechunk.chunk_read(buf, sizeof(buf));
244 bufptr = 0;
246 z.next_in = buf + bufptr;
247 z.avail_in = buflen;
248 z.next_out = out;
249 z.avail_out = outsize;
250 int r = inflate(&z, Z_SYNC_FLUSH);
251 if(r == Z_BUF_ERROR || r == Z_STREAM_END) {
252 out = z.next_out;
253 outsize = z.avail_out;
254 bufptr = z.next_in - buf;
255 buflen = z.avail_in;
256 return;
258 throw_zlib_error(r);
259 out = z.next_out;
260 outsize = z.avail_out;
261 bufptr = z.next_in - buf;
262 buflen = z.avail_in;
265 private:
266 z_stream z;
267 png_decompressor_zlib(const png_decompressor_zlib&);
268 png_decompressor_zlib& operator=(const png_decompressor_zlib&);
269 uint8_t buf[256];
270 size_t bufptr;
271 size_t buflen;
274 png_decompressor& png_decompressor::get(png_dechunker& dechunk, uint8_t compression)
276 if(compression == 0) return *new png_decompressor_zlib(dechunk);
277 throw std::runtime_error("Unsupported compression method");
280 //=========================================================
281 //=================== PNG FILTERBANK ======================
282 //=========================================================
283 class png_filterbank
285 public:
286 png_filterbank(png_decompressor& _decomp, size_t _pitch, size_t _elements)
287 : decomp(_decomp), pitch(_pitch), elements(_elements)
290 virtual ~png_filterbank() {}
291 size_t outsize() { return pitch * elements; }
292 void adjust_row(uint8_t type, uint8_t depth, size_t width)
294 size_t bits = png_filterbank::get_bits(type, depth);
295 size_t n_pitch = (bits >= 8) ? (bits >> 3) : 1;
296 size_t pfactor = 8 / bits;
297 size_t n_elements = (bits >= 8) ? width : ((width + pfactor - 1) / pfactor);
298 adjusted(n_pitch, n_elements);
299 pitch = n_pitch;
300 elements = n_elements;
302 virtual bool row(uint8_t* data) = 0;
303 static png_filterbank& get(png_decompressor& _decomp, uint8_t filterbank, uint8_t type, uint8_t depth,
304 size_t width);
305 static size_t get_bits(uint8_t type, uint8_t depth)
307 size_t mul[7] = {1, 0, 3, 1, 2, 0, 4};
308 if(type > 6 || !mul[type]) throw std::runtime_error("Unrecognized color type");
309 return mul[type] * depth;
311 protected:
312 png_decompressor& decomp;
313 size_t pitch;
314 size_t elements;
315 virtual void adjusted(size_t _pitch, size_t _elements) = 0;
316 private:
317 png_filterbank(const png_filterbank&);
318 png_filterbank& operator=(const png_filterbank&);
321 inline uint8_t predict_none(uint8_t left, uint8_t up, uint8_t upleft)
323 return 0;
326 inline uint8_t predict_left(uint8_t left, uint8_t up, uint8_t upleft)
328 return left;
331 inline uint8_t predict_up(uint8_t left, uint8_t up, uint8_t upleft)
333 return up;
336 inline uint8_t predict_average(uint8_t left, uint8_t up, uint8_t upleft)
338 return (left >> 1) + (up >> 1) + (left & up & 1);
341 inline uint8_t predict_paeth(uint8_t left, uint8_t up, uint8_t upleft)
343 int16_t p = (int16_t)up + left - upleft;
344 uint16_t pa = (p > left) ? (p - left) : (left - p);
345 uint16_t pb = (p > up) ? (p - up) : (up - p);
346 uint16_t pc = (p > upleft) ? (p - upleft) : (upleft - p);
347 if(pa <= pb && pa <= pc)
348 return left;
349 if(pb <= pc)
350 return up;
351 return upleft;
354 template<uint8_t(*predictor)(uint8_t left, uint8_t up, uint8_t upleft)> void do_filter_3(uint8_t* target,
355 const uint8_t* row, const uint8_t* above, size_t pitch, size_t length)
357 for(size_t i = 0; i < pitch; i++)
358 target[i] = row[i] + predictor(0, above[i], 0);
359 for(size_t i = pitch; i < length; i++)
360 target[i] = row[i] + predictor(target[i - pitch], above[i], above[i - pitch]);
363 class png_filterbank_0 : public png_filterbank
365 public:
366 png_filterbank_0(png_decompressor& decomp, size_t pitch, size_t elements)
367 : png_filterbank(decomp, pitch, elements)
369 above.resize(pitch * elements);
370 tmp.resize(pitch * elements + 1);
371 tmp2 = &tmp[0];
372 tmpleft = tmp.size();
374 ~png_filterbank_0() {}
375 bool row(uint8_t* data)
377 decomp.decompress(tmp2, tmpleft);
378 if(tmpleft)
379 return false;
380 uint8_t filter = tmp[0];
381 uint8_t* t = data;
382 uint8_t* s = &tmp[1];
383 uint8_t* a = &above[0];
384 switch(filter) {
385 case 0: do_filter_3<predict_none>(t, s, a, pitch, pitch * elements); break;
386 case 1: do_filter_3<predict_left>(t, s, a, pitch, pitch * elements); break;
387 case 2: do_filter_3<predict_up>(t, s, a, pitch, pitch * elements); break;
388 case 3: do_filter_3<predict_average>(t, s, a, pitch, pitch * elements); break;
389 case 4: do_filter_3<predict_paeth>(t, s, a, pitch, pitch * elements); break;
390 default: throw std::runtime_error("Unknown filter for filter bank 0");
392 tmp2 = &tmp[0];
393 tmpleft = tmp.size();
394 memcpy(a, t, pitch * elements);
395 return true;
397 protected:
398 void adjusted(size_t _pitch, size_t _elements)
400 std::vector<uint8_t> nabove, ntmp;
401 nabove.resize(_pitch * _elements);
402 ntmp.resize(_pitch * _elements + 1);
403 std::swap(above, nabove);
404 std::swap(tmp, ntmp);
405 tmp2 = &tmp[0];
406 tmpleft = tmp.size();
408 private:
409 png_filterbank_0(const png_filterbank_0&);
410 png_filterbank_0& operator=(const png_filterbank_0&);
411 std::vector<uint8_t> above;
412 std::vector<uint8_t> tmp;
413 uint8_t* tmp2;
414 size_t tmpleft;
417 png_filterbank& png_filterbank::get(png_decompressor& decomp, uint8_t filterbank, uint8_t type,
418 uint8_t depth, size_t width)
420 size_t bits = png_filterbank::get_bits(type, depth);
421 size_t pitch = (bits >= 8) ? (bits >> 3) : 1;
422 size_t elements = (bits >= 8) ? width : (width / (8 / bits));
423 if(filterbank == 0) return *new png_filterbank_0(decomp, pitch, elements);
424 throw std::runtime_error("Unknown scanline filter bank");
427 //=========================================================
428 //=================== PNG PIXEL DECODING ==================
429 //=========================================================
430 class png_pixel_decoder
432 public:
433 png_pixel_decoder(png_filterbank& _filter, uint8_t _type, uint8_t _depth, size_t _width)
434 : filter(_filter), type(_type), depth(_depth), width(_width)
436 tmp.resize((png_filterbank::get_bits(type, depth) * width + 7) / 8);
438 virtual ~png_pixel_decoder() {}
439 virtual bool decode(uint32_t* output, uint8_t* trans) = 0;
440 static png_pixel_decoder& get(png_filterbank& _filter, uint8_t type, uint8_t depth, size_t width);
441 void adjust_row(size_t _width)
443 std::vector<uint8_t> ntmp;
444 ntmp.resize((png_filterbank::get_bits(type, depth) * _width + 7) / 8);
445 filter.adjust_row(type, depth, _width);
446 std::swap(tmp, ntmp);
447 width = _width;
449 protected:
450 size_t width;
451 uint8_t type;
452 uint8_t depth;
453 png_filterbank& filter;
454 std::vector<uint8_t> tmp;
455 bool fill()
457 return filter.row(&tmp[0]);
459 private:
460 png_pixel_decoder(const png_pixel_decoder&);
461 png_pixel_decoder& operator=(const png_pixel_decoder&);
464 template<unsigned bits>
465 inline uint32_t decode_type_0(const uint8_t* in, unsigned bit, const uint8_t* trans)
467 uint16_t v;
468 uint32_t m;
469 uint32_t s = 0;
470 switch(bits) {
471 case 1: v = (*in >> (7 - bit)) & 1; m = 0xFFFFFF; break;
472 case 2: v = (*in >> (6 - bit)) & 3; m = 0x555555; break;
473 case 4: v = (*in >> (4 - bit)) & 15; m = 0x111111; break;
474 case 8: v = *in; m = 0xFFFFFF; m = 0x010101; break;
475 case 16: v = read16ube(in); m = 0x010101; s = 8; break;
477 uint32_t alpha = 0xFF000000U;
478 if(v == read16ube(trans))
479 alpha = 0;
480 return alpha | (m * (v >> s));
483 template<unsigned bits>
484 inline uint32_t decode_type_2(const uint8_t* in, unsigned bit, const uint8_t* trans)
486 uint32_t alpha = 0xFF000000U;
487 if(trans) {
488 if(bits == 8 && in[0] == trans[1] && in[1] == trans[3] && in[2] == trans[5])
489 alpha = 0;
490 if(bits == 16 && !memcmp(in, trans, 6))
491 alpha = 0;
493 if(bits == 8)
494 return ((uint32_t)in[0] << 16) |
495 ((uint32_t)in[1] << 8) |
496 ((uint32_t)in[2]) |
497 alpha;
498 else if(bits == 16)
499 return ((uint32_t)in[0] << 16) |
500 ((uint32_t)in[2] << 8) |
501 ((uint32_t)in[4]) |
502 alpha;
505 template<unsigned bits>
506 inline uint32_t decode_type_3(const uint8_t* in, unsigned bit, const uint8_t* trans)
508 switch(bits) {
509 case 1: return (*in >> (7 - bit)) & 1;
510 case 2: return (*in >> (6 - bit)) & 3;
511 case 4: return (*in >> (4 - bit)) & 15;
512 case 8: return *in;
513 case 16: return read16ube(in);
517 template<unsigned bits>
518 uint32_t decode_type_4(const uint8_t* in, unsigned bit, const uint8_t* trans)
520 if(bits == 8)
521 return ((uint32_t)in[0] << 16) |
522 ((uint32_t)in[0] << 8) |
523 ((uint32_t)in[0]) |
524 ((uint32_t)in[1] << 24);
525 else if(bits == 16)
526 return ((uint32_t)in[0] << 16) |
527 ((uint32_t)in[0] << 8) |
528 ((uint32_t)in[0]) |
529 ((uint32_t)in[2] << 24);
532 template<unsigned bits>
533 uint32_t decode_type_6(const uint8_t* in, unsigned bit, const uint8_t* trans)
535 if(bits == 8)
536 return ((uint32_t)in[0] << 16) |
537 ((uint32_t)in[1] << 8) |
538 ((uint32_t)in[2]) |
539 ((uint32_t)in[3] << 24);
540 else if(bits == 16)
541 return ((uint32_t)in[0] << 16) |
542 ((uint32_t)in[2] << 8) |
543 ((uint32_t)in[4]) |
544 ((uint32_t)in[6] << 24);
547 template<size_t bits, uint32_t (*decodefn)(const uint8_t* in, unsigned bit, const uint8_t* trans)>
548 class png_pixel_decoder_fn : public png_pixel_decoder
550 public:
551 png_pixel_decoder_fn(png_filterbank& _filter, uint8_t _type, uint8_t _depth, size_t _width)
552 : png_pixel_decoder(_filter, _type, _depth, _width)
555 ~png_pixel_decoder_fn() {}
556 bool decode(uint32_t* output, uint8_t* trans)
558 if(!fill())
559 return false;
560 size_t off = 0;
561 unsigned bit = 0;
562 for(size_t i = 0; i < width; i++) {
563 output[i] = decodefn(&tmp[off], bit, trans);
564 bit += bits;
565 off += (bit >> 3);
566 bit &= 7;
568 return true;
572 png_pixel_decoder& png_pixel_decoder::get(png_filterbank& filter, uint8_t type, uint8_t depth, size_t width)
574 switch(type) {
575 case 0:
576 switch(depth) {
577 case 1: return *new png_pixel_decoder_fn<1, decode_type_0<1>>(filter, type, depth, width);
578 case 2: return *new png_pixel_decoder_fn<2, decode_type_0<2>>(filter, type, depth, width);
579 case 4: return *new png_pixel_decoder_fn<4, decode_type_0<4>>(filter, type, depth, width);
580 case 8: return *new png_pixel_decoder_fn<8, decode_type_0<8>>(filter, type, depth, width);
581 case 16: return *new png_pixel_decoder_fn<16, decode_type_0<16>>(filter, type, depth, width);
582 default: throw std::runtime_error("Unsupported color depth for type 0");
584 case 2:
585 switch(depth) {
586 case 8: return *new png_pixel_decoder_fn<24, decode_type_2<8>>(filter, type, depth, width);
587 case 16: return *new png_pixel_decoder_fn<48, decode_type_2<16>>(filter, type, depth, width);
588 default: throw std::runtime_error("Unsupported color depth for type 2");
590 case 3:
591 switch(depth) {
592 case 1: return *new png_pixel_decoder_fn<1, decode_type_3<1>>(filter, type, depth, width);
593 case 2: return *new png_pixel_decoder_fn<2, decode_type_3<2>>(filter, type, depth, width);
594 case 4: return *new png_pixel_decoder_fn<4, decode_type_3<4>>(filter, type, depth, width);
595 case 8: return *new png_pixel_decoder_fn<8, decode_type_3<8>>(filter, type, depth, width);
596 case 16: return *new png_pixel_decoder_fn<16, decode_type_3<16>>(filter, type, depth, width);
597 default: throw std::runtime_error("Unsupported color depth for type 3");
599 case 4:
600 switch(depth) {
601 case 8: return *new png_pixel_decoder_fn<16, decode_type_4<8>>(filter, type, depth, width);
602 case 16: return *new png_pixel_decoder_fn<32, decode_type_4<16>>(filter, type, depth, width);
603 default: throw std::runtime_error("Unsupported color depth for type 4");
605 case 6:
606 switch(depth) {
607 case 8: return *new png_pixel_decoder_fn<32, decode_type_6<8>>(filter, type, depth, width);
608 case 16: return *new png_pixel_decoder_fn<64, decode_type_6<16>>(filter, type, depth, width);
609 default: throw std::runtime_error("Unsupported color depth for type 6");
611 default: throw std::runtime_error("Unsupported color type");
615 //=========================================================
616 //=================== PNG INTERLACING =====================
617 //=========================================================
618 class png_interlacing
620 public:
621 struct pass_info
623 size_t xoff;
624 size_t xmod;
625 size_t yoff;
626 size_t ymod;
628 png_interlacing()
631 virtual ~png_interlacing() {}
632 virtual size_t passes() = 0;
633 virtual pass_info pass(size_t n) = 0;
634 static std::pair<size_t, size_t> pass_size(size_t iwidth, size_t iheight, pass_info pinfo)
636 size_t width = (iwidth + (pinfo.xmod - pinfo.xoff - 1)) / pinfo.xmod;
637 size_t height = (iheight + (pinfo.ymod - pinfo.yoff - 1)) / pinfo.ymod;
638 if(!width || !height)
639 return std::make_pair(0, 0);
640 return std::make_pair(width, height);
642 static png_interlacing& get(uint8_t interlace);
643 private:
644 png_interlacing(const png_interlacing&);
645 png_interlacing& operator=(const png_interlacing&);
648 class png_interlacing_progressive : public png_interlacing
650 public:
651 ~png_interlacing_progressive() {}
652 size_t passes() { return 1; }
653 png_interlacing::pass_info pass(size_t n)
655 png_interlacing::pass_info p;
656 p.xoff = p.yoff = 0;
657 p.xmod = p.ymod = 1;
658 return p;
662 class png_interlacing_adam : public png_interlacing
664 public:
665 png_interlacing_adam(unsigned _order) : order(_order) {}
666 ~png_interlacing_adam() {}
667 size_t passes() { return 2 * order + 1; }
668 png_interlacing::pass_info pass(size_t n)
670 size_t one = 1;
671 png_interlacing::pass_info p;
672 if(n == 0) {
673 p.xmod = p.ymod = (one << order);
674 p.xoff = p.yoff = 0;
675 } else {
676 p.xoff = (n % 2) ? (one << (order - (n + 1) / 2)) : 0;
677 p.yoff = (n % 2) ? 0 : (one << (order - n / 2));
678 p.xmod = one << (order - n / 2);
679 p.ymod = one << (order - (n - 1) / 2);
681 return p;
683 private:
684 unsigned order;
687 png_interlacing& png_interlacing::get(uint8_t interlace)
689 if(interlace == 0) return *new png_interlacing_progressive();
690 if(interlace == 1) return *new png_interlacing_adam(3);
691 throw std::runtime_error("Unknown interlace type");
695 void decode_png(std::istream& stream, png_decoded_image& out)
697 png_dechunker dechunk(stream);
698 if(!dechunk.next_chunk())
699 throw std::runtime_error("PNG file has no chunks");
700 ihdr_chunk hdr(dechunk);
701 autorelease<png_decompressor> idat_decomp(png_decompressor::get(dechunk, hdr.compression));
702 autorelease<png_filterbank> filterbank(png_filterbank::get(*idat_decomp, hdr.filter, hdr.type,
703 hdr.depth, hdr.width));
704 autorelease<png_pixel_decoder> pixdecoder(png_pixel_decoder::get(*filterbank, hdr.type, hdr.depth,
705 hdr.width));
706 autorelease<png_interlacing> interlace(png_interlacing::get(hdr.interlace));
707 std::vector<uint32_t> ndata;
708 std::vector<uint32_t> npalette;
709 ndata.resize(hdr.width * hdr.height);
710 if(ndata.size() / hdr.width != hdr.height)
711 throw std::bad_alloc();
712 if(hdr.type == 3) {
713 npalette.resize(1 << hdr.depth);
714 for(size_t i = 0; i < npalette.size(); i++)
715 npalette[i] = 0xFF000000U;
717 if(!dechunk.next_chunk())
718 throw std::runtime_error("PNG file has no chunks besides header");
719 uint8_t trans[6];
720 uint8_t* _trans = NULL;
721 for(size_t pass = 0; pass < interlace->passes(); pass++) {
722 size_t scanline = 0;
723 png_interlacing::pass_info pinfo = interlace->pass(pass);
724 auto resolution = png_interlacing::pass_size(hdr.width, hdr.height, pinfo);
725 pixdecoder->adjust_row(resolution.first);
726 std::vector<uint32_t> scanlineb;
727 scanlineb.resize(resolution.first);
728 while(true) {
729 switch(dechunk.chunk_type()) {
730 case 0x49454E44: //IEND.
731 throw std::runtime_error("Unexpected IEND chunk");
732 case 0x504C5445: //PLTE.
733 if(hdr.type == 0 || hdr.type == 4)
734 throw std::runtime_error("Illegal PLTE in types 0/4");
735 if(hdr.type == 2 || hdr.type == 6)
736 break; //Advisory.
737 if(dechunk.chunk_size() > 3 * npalette.size())
738 throw std::runtime_error("PLTE too large");
739 for(size_t i = 0; i < dechunk.chunk_size() / 3; i++) {
740 uint8_t buf[3];
741 dechunk.chunk_read(buf, 3);
742 npalette[i] = (npalette[i] & 0xFF000000U) |
743 ((uint32_t)buf[0] << 16) |
744 ((uint32_t)buf[1] << 8) |
745 ((uint32_t)buf[2]);
747 break;
748 case 0x74524E53: //tRNS.
749 if(hdr.type == 4 || hdr.type == 6)
750 throw std::runtime_error("Illegal tRNS in types 4/6");
751 else if(hdr.type == 0) {
752 if(dechunk.chunk_size() != 2)
753 throw std::runtime_error("Expected 2-byte tRNS for type0");
754 dechunk.chunk_read(trans, 2);
755 } else if(hdr.type == 2) {
756 if(dechunk.chunk_size() != 6)
757 throw std::runtime_error("Expected 6-byte tRNS for type2");
758 dechunk.chunk_read(trans, 6);
759 } else if(hdr.type == 3) {
760 if(dechunk.chunk_size() > npalette.size())
761 throw std::runtime_error("tRNS too large");
762 for(size_t i = 0; i < dechunk.chunk_size(); i++) {
763 uint8_t buf[1];
764 dechunk.chunk_read(buf, 1);
765 npalette[i] = (npalette[i] & 0x00FFFFFFU) |
766 ((uint32_t)buf[0] << 24);
769 _trans = trans;
770 break;
771 case 0x49444154: //IDAT.
772 if(scanline == resolution.second)
773 goto next_pass;
774 while(pixdecoder->decode(&scanlineb[0], _trans)) {
775 size_t rline = scanline * pinfo.ymod + pinfo.yoff;
776 for(size_t i = 0; i < resolution.first; i++) {
777 ndata[rline * hdr.width + (i * pinfo.xmod + pinfo.xoff)] =
778 scanlineb[i];
780 scanline++;
781 if(scanline == resolution.second)
782 goto next_pass;
784 break;
785 default:
786 if((dechunk.chunk_type() & 0x20000000U) == 0)
787 throw std::runtime_error("Unknown critical chunk");
788 break;
790 dechunk.next_chunk();
792 next_pass:
795 while(dechunk.next_chunk()) {
796 switch(dechunk.chunk_type()) {
797 case 0x49454E44: //IEND.
798 goto out;
799 case 0x504C5445: //PLTE
800 throw std::runtime_error("PLTE not allowed after image data");
801 case 0x49444154: //IDAT.
802 break;
803 default:
804 if((dechunk.chunk_type() & 0x20000000U) == 0)
805 throw std::runtime_error("Unknown critical chunk");
808 out:
809 std::swap(out.data, ndata);
810 std::swap(out.palette, npalette);
811 out.has_palette = (hdr.type == 3);
812 out.width = hdr.width;
813 out.height = hdr.height;
816 void decode_png(const std::string& file, png_decoded_image& out)
818 std::istream& s = open_file_relative(file, "");
819 try {
820 decode_png(s, out);
821 delete &s;
822 } catch(...) {
823 delete &s;
824 throw;
828 int main(int argc, char** argv)
830 png_decoded_image img;
831 decode_png(argv[1], img);
832 std::cout << "Size: " << img.width << "*" << img.height << std::endl;
833 if(img.has_palette)
834 std::cout << "Image is paletted, " << img.palette.size() << " colors." << std::endl;
835 if(img.has_palette) {
836 for(size_t i = 0; i < img.data.size(); i++) {
837 if(i > 0 && i % img.width == 0)
838 std::cout << std::endl;
839 std::cout << std::hex << std::setw(8) << std::setfill('0') << img.palette[img.data[i]]
840 << "<" << std::hex << std::setw(4) << std::setfill('0') << img.data[i] << "> ";
842 } else {
843 for(size_t i = 0; i < img.data.size(); i++) {
844 if(i > 0 && i % img.width == 0)
845 std::cout << std::endl;
846 std::cout << std::hex << std::setw(8) << std::setfill('0') << img.data[i] << " ";
849 std::cout << std::endl;
851 //evaluate-lua b,p=gui.bitmap_load_png("/tmp/tbgn2c16.png"); on_paint = function() gui.bitmap_draw(0,0,b,p); end