Implicitly redirect cmdhelp includes to corresponding JSON files
[lsnes.git] / src / library / png.cpp
blob7ffcf560f864983578eac38d71c7fee38c1400c2
1 #include "png.hpp"
2 #include "serialization.hpp"
3 #include "minmax.hpp"
4 #include "hex.hpp"
5 #include "zip.hpp"
6 #include <iostream>
7 #include <fstream>
8 #include <cstdint>
9 #include <iomanip>
10 #include <sstream>
11 #include <stdexcept>
12 #include <zlib.h>
13 #include <string.hpp>
14 #include <boost/iostreams/categories.hpp>
15 #include <boost/iostreams/copy.hpp>
16 #include <boost/iostreams/stream.hpp>
17 #include <boost/iostreams/stream_buffer.hpp>
18 #include <boost/iostreams/filter/symmetric.hpp>
19 #include <boost/iostreams/filter/zlib.hpp>
20 #include <boost/iostreams/filtering_stream.hpp>
21 #include <boost/iostreams/device/back_inserter.hpp>
23 namespace png
25 namespace
27 void throw_zlib_error(int x)
29 switch(x) {
30 case Z_NEED_DICT: throw std::runtime_error("Dictionary needed");
31 case Z_ERRNO: throw std::runtime_error(std::string("OS error: ") + strerror(errno));
32 case Z_STREAM_ERROR: throw std::runtime_error("Stream error");
33 case Z_DATA_ERROR: throw std::runtime_error("Data error");
34 case Z_MEM_ERROR: throw std::bad_alloc();
35 case Z_BUF_ERROR: throw std::runtime_error("Buffer error");
36 case Z_VERSION_ERROR: throw std::runtime_error("Version error");
37 case Z_OK:
38 case Z_STREAM_END:
39 break;
40 default: throw std::runtime_error("Unknown error");
44 void* zlib_alloc(void* dummy, unsigned a, unsigned b)
46 return calloc(a, b);
49 void zlib_free(void* dummy, void* addr)
51 free(addr);
54 int size_to_bits(unsigned v)
56 if(v > 256) return 16;
57 if(v > 16) return 8;
58 if(v > 4) return 4;
59 if(v > 2) return 2;
60 return 1;
63 size_t buffer_stride(size_t width, bool has_pal, bool has_trans, size_t psize)
65 if(!has_pal)
66 return 1 + width * (has_trans ? 4 : 3);
67 else
68 return 1 + (width * size_to_bits(psize) + 7) / 8;
71 void write_row_pal1(char* output, const uint32_t* input, size_t w)
73 memset(output, 0, (w + 7) / 8);
74 for(size_t i = 0; i < w; i++)
75 output[i >> 3] |= ((input[i] & 1) << (7 -i % 8));
78 void write_row_pal2(char* output, const uint32_t* input, size_t w)
80 memset(output, 0, (w + 3) / 4);
81 for(size_t i = 0; i < w; i++)
82 output[i >> 2] |= ((input[i] & 3) << (2 * (3 - i % 4)));
85 void write_row_pal4(char* output, const uint32_t* input, size_t w)
87 memset(output, 0, (w + 1) / 2);
88 for(size_t i = 0; i < w; i++)
89 output[i >> 1] |= ((input[i] & 15) << (4 * (1 - i % 2)));
92 void write_row_pal8(char* output, const uint32_t* input, size_t w)
94 for(size_t i = 0; i < w; i++)
95 output[i] = input[i];
98 void write_row_pal16(char* output, const uint32_t* input, size_t w)
100 for(size_t i = 0; i < w; i++) {
101 output[2 * i + 0] = input[i] >> 8;
102 output[2 * i + 1] = input[i];
106 void write_row_rgba(char* output, const uint32_t* input, size_t w)
108 for(size_t i = 0; i < w; i++) {
109 output[4 * i + 0] = input[i] >> 16;
110 output[4 * i + 1] = input[i] >> 8;
111 output[4 * i + 2] = input[i];
112 output[4 * i + 3] = input[i] >> 24;
116 void write_row_rgb(char* output, const uint32_t* input, size_t w)
118 for(size_t i = 0; i < w; i++) {
119 output[3 * i + 0] = input[i] >> 16;
120 output[3 * i + 1] = input[i] >> 8;
121 output[3 * i + 2] = input[i];
125 //=========================================================
126 //==================== PNG CHUNKER ========================
127 //=========================================================
128 class png_chunk_output
130 public:
131 typedef char char_type;
132 struct category : boost::iostreams::closable_tag, boost::iostreams::sink_tag {};
133 png_chunk_output(std::ostream& _os, uint32_t _type)
134 : os(_os), type(_type)
138 void close()
140 uint32_t crc = crc32(0, NULL, 0);
141 char fixed[12];
142 serialization::u32b(fixed, stream.size());
143 serialization::u32b(fixed + 4, type);
144 crc = crc32(crc, reinterpret_cast<Bytef*>(fixed + 4), 4);
145 if(stream.size() > 0)
146 crc = crc32(crc, reinterpret_cast<Bytef*>(&stream[0]), stream.size());
147 serialization::u32b(fixed + 8, crc);
148 os.write(fixed, 8);
149 os.write(&stream[0], stream.size());
150 os.write(fixed + 8, 4);
153 std::streamsize write(const char* s, std::streamsize n)
155 size_t oldsize = stream.size();
156 stream.resize(oldsize + n);
157 memcpy(&stream[oldsize], s, n);
158 return n;
160 protected:
161 std::vector<char> stream;
162 std::ostream& os;
163 uint32_t type;
167 //=========================================================
168 //=================== AUTORELEASE =========================
169 //=========================================================
170 template <typename T>
171 class autorelease
173 public:
174 autorelease(T& _obj) : obj(_obj) {}
175 ~autorelease() { delete &obj; }
176 T* operator->() { return &obj; }
177 T& operator*() { return obj; }
178 private:
179 T& obj;
180 autorelease(const autorelease&);
181 const autorelease& operator=(const autorelease&);
184 //=========================================================
185 //=================== PNG DECHUNKER =======================
186 //=========================================================
187 class png_dechunker
189 public:
190 png_dechunker(std::istream& _stream);
191 uint32_t chunk_size() { return size; }
192 uint32_t chunk_type() { return type; }
193 uint32_t chunk_read(uint8_t* buf, size_t limit);
194 bool next_chunk();
195 bool eof() { return eof_flag; }
196 bool chunk_eof() { return ptr == size; }
197 private:
198 std::istream& stream;
199 uint32_t size;
200 uint32_t type;
201 uint32_t ptr;
202 uint32_t crc;
203 bool eof_flag;
204 void verify_crc();
205 void load_chunk();
208 png_dechunker::png_dechunker(std::istream& _stream)
209 : stream(_stream)
211 size = 0;
212 type = 0;
213 ptr = 0;
214 eof_flag = false;
215 uint8_t magic[8] = {137, 80, 78, 71, 13, 10, 26, 10};
216 uint8_t magicbuf[8];
217 stream.read(reinterpret_cast<char*>(magicbuf), 8);
218 if(!stream || memcmp(magic, magicbuf, 8))
219 throw std::runtime_error("Not a PNG file");
222 uint32_t png_dechunker::chunk_read(uint8_t* buf, size_t limit)
224 limit = min(limit, static_cast<size_t>(size - ptr));
225 if(!limit)
226 return 0;
227 stream.read(reinterpret_cast<char*>(buf), limit);
228 if(!stream)
229 throw std::runtime_error("PNG file truncated");
230 crc = crc32(crc, buf, limit);
231 ptr += limit;
232 if(ptr == size)
233 verify_crc();
234 return limit;
237 bool png_dechunker::next_chunk()
239 if(eof_flag)
240 return false;
241 while(ptr < size) {
242 uint8_t buf[256];
243 chunk_read(buf, 256);
245 load_chunk();
246 return !eof_flag;
249 void png_dechunker::load_chunk()
251 uint8_t buf[8];
252 stream.read(reinterpret_cast<char*>(buf), 8);
253 if(!stream) {
254 if(stream.gcount() == 0) {
255 //EOF.
256 size = 0;
257 type = 0;
258 ptr = 0;
259 eof_flag = true;
260 return;
262 throw std::runtime_error("PNG file truncated");
264 size = serialization::u32b(buf + 0);
265 type = serialization::u32b(buf + 4);
266 crc = crc32(0, NULL, 0);
267 crc = crc32(crc, buf + 4, 4);
268 ptr = 0;
269 if(!size)
270 verify_crc();
273 void png_dechunker::verify_crc()
275 char buf[4];
276 stream.read(buf, 4);
277 if(!stream)
278 throw std::runtime_error("PNG file truncated");
279 uint32_t claim_crc = serialization::u32b(buf);
280 if(crc != claim_crc)
281 throw std::runtime_error("PNG file chunk CRC check failed");
284 //=========================================================
285 //=================== PNG IHDR CHUNK ======================
286 //=========================================================
287 struct ihdr_chunk
289 ihdr_chunk(png_dechunker& d)
291 if(d.chunk_type() != 0x49484452)
292 throw std::runtime_error("Expected IHDR chunk");
293 if(d.chunk_size() != 13)
294 throw std::runtime_error("Expected IHDR chunk to be 13 bytes");
295 uint8_t buf[13];
296 d.chunk_read(buf, 13);
297 width = serialization::u32b(buf + 0);
298 height = serialization::u32b(buf + 4);
299 depth = buf[8];
300 type = buf[9];
301 compression = buf[10];
302 filter = buf[11];
303 interlace = buf[12];
305 size_t width;
306 size_t height;
307 uint8_t type;
308 uint8_t depth;
309 uint8_t compression;
310 uint8_t interlace;
311 uint8_t filter;
314 //=========================================================
315 //=================== PNG DECOMPRESSOR ====================
316 //=========================================================
317 class png_decompressor
319 public:
320 png_decompressor(png_dechunker& _dechunk) : dechunk(_dechunk) {}
321 virtual ~png_decompressor() {}
322 virtual void decompress(uint8_t*& out, size_t& outsize) = 0;
323 static png_decompressor& get(png_dechunker& dechunk, uint8_t compression);
324 protected:
325 png_dechunker& dechunk;
326 private:
327 png_decompressor(const png_decompressor&);
328 png_decompressor& operator=(const png_decompressor&);
331 class png_decompressor_zlib : public png_decompressor
333 public:
334 png_decompressor_zlib(png_dechunker& dechunk)
335 : png_decompressor(dechunk)
337 memset(&z, 0, sizeof(z));
338 z.zalloc = zlib_alloc;
339 z.zfree = zlib_free;
340 throw_zlib_error(inflateInit(&z));
341 buflen = 0;
342 bufptr = 0;
344 ~png_decompressor_zlib()
346 inflateEnd(&z);
348 void decompress(uint8_t*& out, size_t& outsize)
350 while(true) {
351 if(!buflen) {
352 buflen = dechunk.chunk_read(buf, sizeof(buf));
353 bufptr = 0;
355 z.next_in = buf + bufptr;
356 z.avail_in = buflen;
357 z.next_out = out;
358 z.avail_out = outsize;
359 int r = inflate(&z, Z_SYNC_FLUSH);
360 if(r == Z_BUF_ERROR || r == Z_STREAM_END) {
361 out = z.next_out;
362 outsize = z.avail_out;
363 bufptr = z.next_in - buf;
364 buflen = z.avail_in;
365 return;
367 throw_zlib_error(r);
368 out = z.next_out;
369 outsize = z.avail_out;
370 bufptr = z.next_in - buf;
371 buflen = z.avail_in;
374 private:
375 z_stream z;
376 png_decompressor_zlib(const png_decompressor_zlib&);
377 png_decompressor_zlib& operator=(const png_decompressor_zlib&);
378 uint8_t buf[256];
379 size_t bufptr;
380 size_t buflen;
383 png_decompressor& png_decompressor::get(png_dechunker& dechunk, uint8_t compression)
385 if(compression == 0) return *new png_decompressor_zlib(dechunk);
386 throw std::runtime_error("Unsupported compression method");
389 //=========================================================
390 //=================== PNG FILTERBANK ======================
391 //=========================================================
392 class png_filterbank
394 public:
395 png_filterbank(png_decompressor& _decomp, size_t _pitch, size_t _elements)
396 : decomp(_decomp), pitch(_pitch), elements(_elements)
399 virtual ~png_filterbank() {}
400 size_t outsize() { return pitch * elements; }
401 void adjust_row(uint8_t type, uint8_t depth, size_t width)
403 size_t bits = png_filterbank::get_bits(type, depth);
404 size_t n_pitch = (bits >= 8) ? (bits >> 3) : 1;
405 size_t pfactor = 8 / bits;
406 size_t n_elements = (bits >= 8) ? width : ((width + pfactor - 1) / pfactor);
407 adjusted(n_pitch, n_elements);
408 pitch = n_pitch;
409 elements = n_elements;
411 virtual bool row(uint8_t* data) = 0;
412 static png_filterbank& get(png_decompressor& _decomp, uint8_t filterbank, uint8_t type, uint8_t depth,
413 size_t width);
414 static size_t get_bits(uint8_t type, uint8_t depth)
416 size_t mul[7] = {1, 0, 3, 1, 2, 0, 4};
417 if(type > 6 || !mul[type]) throw std::runtime_error("Unrecognized color type");
418 return mul[type] * depth;
420 protected:
421 png_decompressor& decomp;
422 size_t pitch;
423 size_t elements;
424 virtual void adjusted(size_t _pitch, size_t _elements) = 0;
425 private:
426 png_filterbank(const png_filterbank&);
427 png_filterbank& operator=(const png_filterbank&);
430 inline uint8_t predict_none(uint8_t left, uint8_t up, uint8_t upleft)
432 return 0;
435 inline uint8_t predict_left(uint8_t left, uint8_t up, uint8_t upleft)
437 return left;
440 inline uint8_t predict_up(uint8_t left, uint8_t up, uint8_t upleft)
442 return up;
445 inline uint8_t predict_average(uint8_t left, uint8_t up, uint8_t upleft)
447 return (left >> 1) + (up >> 1) + (left & up & 1);
450 inline uint8_t predict_paeth(uint8_t left, uint8_t up, uint8_t upleft)
452 int16_t p = (int16_t)up + left - upleft;
453 uint16_t pa = (p > left) ? (p - left) : (left - p);
454 uint16_t pb = (p > up) ? (p - up) : (up - p);
455 uint16_t pc = (p > upleft) ? (p - upleft) : (upleft - p);
456 if(pa <= pb && pa <= pc)
457 return left;
458 if(pb <= pc)
459 return up;
460 return upleft;
463 template<uint8_t(*predictor)(uint8_t left, uint8_t up, uint8_t upleft)> void do_filter_3(uint8_t* target,
464 const uint8_t* row, const uint8_t* above, size_t pitch, size_t length)
466 for(size_t i = 0; i < pitch; i++)
467 target[i] = row[i] + predictor(0, above[i], 0);
468 for(size_t i = pitch; i < length; i++)
469 target[i] = row[i] + predictor(target[i - pitch], above[i], above[i - pitch]);
472 class png_filterbank_0 : public png_filterbank
474 public:
475 png_filterbank_0(png_decompressor& decomp, size_t pitch, size_t elements)
476 : png_filterbank(decomp, pitch, elements)
478 above.resize(pitch * elements);
479 tmp.resize(pitch * elements + 1);
480 tmp2 = &tmp[0];
481 tmpleft = tmp.size();
483 ~png_filterbank_0() {}
484 bool row(uint8_t* data)
486 decomp.decompress(tmp2, tmpleft);
487 if(tmpleft)
488 return false;
489 uint8_t filter = tmp[0];
490 uint8_t* t = data;
491 uint8_t* s = &tmp[1];
492 uint8_t* a = &above[0];
493 switch(filter) {
494 case 0: do_filter_3<predict_none>(t, s, a, pitch, pitch * elements); break;
495 case 1: do_filter_3<predict_left>(t, s, a, pitch, pitch * elements); break;
496 case 2: do_filter_3<predict_up>(t, s, a, pitch, pitch * elements); break;
497 case 3: do_filter_3<predict_average>(t, s, a, pitch, pitch * elements); break;
498 case 4: do_filter_3<predict_paeth>(t, s, a, pitch, pitch * elements); break;
499 default: throw std::runtime_error("Unknown filter for filter bank 0");
501 tmp2 = &tmp[0];
502 tmpleft = tmp.size();
503 memcpy(a, t, pitch * elements);
504 return true;
506 protected:
507 void adjusted(size_t _pitch, size_t _elements)
509 std::vector<uint8_t> nabove, ntmp;
510 nabove.resize(_pitch * _elements);
511 ntmp.resize(_pitch * _elements + 1);
512 std::swap(above, nabove);
513 std::swap(tmp, ntmp);
514 tmp2 = &tmp[0];
515 tmpleft = tmp.size();
517 private:
518 png_filterbank_0(const png_filterbank_0&);
519 png_filterbank_0& operator=(const png_filterbank_0&);
520 std::vector<uint8_t> above;
521 std::vector<uint8_t> tmp;
522 uint8_t* tmp2;
523 size_t tmpleft;
526 png_filterbank& png_filterbank::get(png_decompressor& decomp, uint8_t filterbank, uint8_t type,
527 uint8_t depth, size_t width)
529 size_t bits = png_filterbank::get_bits(type, depth);
530 size_t pitch = (bits >= 8) ? (bits >> 3) : 1;
531 size_t elements = (bits >= 8) ? width : (width / (8 / bits));
532 if(filterbank == 0) return *new png_filterbank_0(decomp, pitch, elements);
533 throw std::runtime_error("Unknown scanline filter bank");
536 //=========================================================
537 //=================== PNG PIXEL DECODING ==================
538 //=========================================================
539 class png_pixel_decoder
541 public:
542 png_pixel_decoder(png_filterbank& _filter, uint8_t _type, uint8_t _depth, size_t _width)
543 : width(_width), type(_type), depth(_depth), filter(_filter)
545 tmp.resize((png_filterbank::get_bits(type, depth) * width + 7) / 8);
547 virtual ~png_pixel_decoder() {}
548 virtual bool decode(uint32_t* output, uint8_t* trans) = 0;
549 static png_pixel_decoder& get(png_filterbank& _filter, uint8_t type, uint8_t depth, size_t width);
550 void adjust_row(size_t _width)
552 std::vector<uint8_t> ntmp;
553 ntmp.resize((png_filterbank::get_bits(type, depth) * _width + 7) / 8);
554 filter.adjust_row(type, depth, _width);
555 std::swap(tmp, ntmp);
556 width = _width;
558 protected:
559 size_t width;
560 uint8_t type;
561 uint8_t depth;
562 png_filterbank& filter;
563 std::vector<uint8_t> tmp;
564 bool fill()
566 return filter.row(&tmp[0]);
568 private:
569 png_pixel_decoder(const png_pixel_decoder&);
570 png_pixel_decoder& operator=(const png_pixel_decoder&);
573 template<unsigned bits>
574 inline uint32_t decode_type_0(const uint8_t* in, unsigned bit, const uint8_t* trans)
576 uint16_t v;
577 uint32_t m;
578 uint32_t s = 0;
579 switch(bits) {
580 case 1: v = (*in >> (7 - bit)) & 1; m = 0xFFFFFF; break;
581 case 2: v = (*in >> (6 - bit)) & 3; m = 0x555555; break;
582 case 4: v = (*in >> (4 - bit)) & 15; m = 0x111111; break;
583 case 8: v = *in; m = 0xFFFFFF; m = 0x010101; break;
584 case 16: v = serialization::u16b(in); m = 0x010101; s = 8; break;
586 uint32_t alpha = 0xFF000000U;
587 if(v == serialization::u16b(trans))
588 alpha = 0;
589 return alpha | (m * (v >> s));
592 template<unsigned bits>
593 inline uint32_t decode_type_2(const uint8_t* in, unsigned bit, const uint8_t* trans)
595 uint32_t alpha = 0xFF000000U;
596 if(trans) {
597 if(bits == 8 && in[0] == trans[1] && in[1] == trans[3] && in[2] == trans[5])
598 alpha = 0;
599 if(bits == 16 && !memcmp(in, trans, 6))
600 alpha = 0;
602 if(bits == 8)
603 return ((uint32_t)in[0] << 16) |
604 ((uint32_t)in[1] << 8) |
605 ((uint32_t)in[2]) |
606 alpha;
607 else if(bits == 16)
608 return ((uint32_t)in[0] << 16) |
609 ((uint32_t)in[2] << 8) |
610 ((uint32_t)in[4]) |
611 alpha;
614 template<unsigned bits>
615 inline uint32_t decode_type_3(const uint8_t* in, unsigned bit, const uint8_t* trans)
617 switch(bits) {
618 case 1: return (*in >> (7 - bit)) & 1;
619 case 2: return (*in >> (6 - bit)) & 3;
620 case 4: return (*in >> (4 - bit)) & 15;
621 case 8: return *in;
622 case 16: return serialization::u16b(in);
626 template<unsigned bits>
627 uint32_t decode_type_4(const uint8_t* in, unsigned bit, const uint8_t* trans)
629 if(bits == 8)
630 return ((uint32_t)in[0] << 16) |
631 ((uint32_t)in[0] << 8) |
632 ((uint32_t)in[0]) |
633 ((uint32_t)in[1] << 24);
634 else if(bits == 16)
635 return ((uint32_t)in[0] << 16) |
636 ((uint32_t)in[0] << 8) |
637 ((uint32_t)in[0]) |
638 ((uint32_t)in[2] << 24);
641 template<unsigned bits>
642 uint32_t decode_type_6(const uint8_t* in, unsigned bit, const uint8_t* trans)
644 if(bits == 8)
645 return ((uint32_t)in[0] << 16) |
646 ((uint32_t)in[1] << 8) |
647 ((uint32_t)in[2]) |
648 ((uint32_t)in[3] << 24);
649 else if(bits == 16)
650 return ((uint32_t)in[0] << 16) |
651 ((uint32_t)in[2] << 8) |
652 ((uint32_t)in[4]) |
653 ((uint32_t)in[6] << 24);
656 template<size_t bits, uint32_t (*decodefn)(const uint8_t* in, unsigned bit, const uint8_t* trans)>
657 class png_pixel_decoder_fn : public png_pixel_decoder
659 public:
660 png_pixel_decoder_fn(png_filterbank& _filter, uint8_t _type, uint8_t _depth, size_t _width)
661 : png_pixel_decoder(_filter, _type, _depth, _width)
664 ~png_pixel_decoder_fn() {}
665 bool decode(uint32_t* output, uint8_t* trans)
667 if(!fill())
668 return false;
669 size_t off = 0;
670 unsigned bit = 0;
671 for(size_t i = 0; i < width; i++) {
672 output[i] = decodefn(&tmp[off], bit, trans);
673 bit += bits;
674 off += (bit >> 3);
675 bit &= 7;
677 return true;
681 png_pixel_decoder& png_pixel_decoder::get(png_filterbank& filter, uint8_t type, uint8_t depth, size_t width)
683 switch(type) {
684 case 0:
685 switch(depth) {
686 case 1: return *new png_pixel_decoder_fn<1, decode_type_0<1>>(filter, type, depth, width);
687 case 2: return *new png_pixel_decoder_fn<2, decode_type_0<2>>(filter, type, depth, width);
688 case 4: return *new png_pixel_decoder_fn<4, decode_type_0<4>>(filter, type, depth, width);
689 case 8: return *new png_pixel_decoder_fn<8, decode_type_0<8>>(filter, type, depth, width);
690 case 16: return *new png_pixel_decoder_fn<16, decode_type_0<16>>(filter, type, depth, width);
691 default: throw std::runtime_error("Unsupported color depth for type 0");
693 case 2:
694 switch(depth) {
695 case 8: return *new png_pixel_decoder_fn<24, decode_type_2<8>>(filter, type, depth, width);
696 case 16: return *new png_pixel_decoder_fn<48, decode_type_2<16>>(filter, type, depth, width);
697 default: throw std::runtime_error("Unsupported color depth for type 2");
699 case 3:
700 switch(depth) {
701 case 1: return *new png_pixel_decoder_fn<1, decode_type_3<1>>(filter, type, depth, width);
702 case 2: return *new png_pixel_decoder_fn<2, decode_type_3<2>>(filter, type, depth, width);
703 case 4: return *new png_pixel_decoder_fn<4, decode_type_3<4>>(filter, type, depth, width);
704 case 8: return *new png_pixel_decoder_fn<8, decode_type_3<8>>(filter, type, depth, width);
705 case 16: return *new png_pixel_decoder_fn<16, decode_type_3<16>>(filter, type, depth, width);
706 default: throw std::runtime_error("Unsupported color depth for type 3");
708 case 4:
709 switch(depth) {
710 case 8: return *new png_pixel_decoder_fn<16, decode_type_4<8>>(filter, type, depth, width);
711 case 16: return *new png_pixel_decoder_fn<32, decode_type_4<16>>(filter, type, depth, width);
712 default: throw std::runtime_error("Unsupported color depth for type 4");
714 case 6:
715 switch(depth) {
716 case 8: return *new png_pixel_decoder_fn<32, decode_type_6<8>>(filter, type, depth, width);
717 case 16: return *new png_pixel_decoder_fn<64, decode_type_6<16>>(filter, type, depth, width);
718 default: throw std::runtime_error("Unsupported color depth for type 6");
720 default: throw std::runtime_error("Unsupported color type");
724 //=========================================================
725 //=================== PNG INTERLACING =====================
726 //=========================================================
727 class png_interlacing
729 public:
730 struct pass_info
732 size_t xoff;
733 size_t xmod;
734 size_t yoff;
735 size_t ymod;
737 png_interlacing()
740 virtual ~png_interlacing() {}
741 virtual size_t passes() = 0;
742 virtual pass_info pass(size_t n) = 0;
743 static std::pair<size_t, size_t> pass_size(size_t iwidth, size_t iheight, pass_info pinfo)
745 size_t width = (iwidth + (pinfo.xmod - pinfo.xoff - 1)) / pinfo.xmod;
746 size_t height = (iheight + (pinfo.ymod - pinfo.yoff - 1)) / pinfo.ymod;
747 if(!width || !height)
748 return std::make_pair(0, 0);
749 return std::make_pair(width, height);
751 static png_interlacing& get(uint8_t interlace);
752 private:
753 png_interlacing(const png_interlacing&);
754 png_interlacing& operator=(const png_interlacing&);
757 class png_interlacing_progressive : public png_interlacing
759 public:
760 ~png_interlacing_progressive() {}
761 size_t passes() { return 1; }
762 png_interlacing::pass_info pass(size_t n)
764 png_interlacing::pass_info p;
765 p.xoff = p.yoff = 0;
766 p.xmod = p.ymod = 1;
767 return p;
771 class png_interlacing_adam : public png_interlacing
773 public:
774 png_interlacing_adam(unsigned _order) : order(_order) {}
775 ~png_interlacing_adam() {}
776 size_t passes() { return 2 * order + 1; }
777 png_interlacing::pass_info pass(size_t n)
779 size_t one = 1;
780 png_interlacing::pass_info p;
781 if(n == 0) {
782 p.xmod = p.ymod = (one << order);
783 p.xoff = p.yoff = 0;
784 } else {
785 p.xoff = (n % 2) ? (one << (order - (n + 1) / 2)) : 0;
786 p.yoff = (n % 2) ? 0 : (one << (order - n / 2));
787 p.xmod = one << (order - n / 2);
788 p.ymod = one << (order - (n - 1) / 2);
790 return p;
792 private:
793 unsigned order;
796 png_interlacing& png_interlacing::get(uint8_t interlace)
798 if(interlace == 0) return *new png_interlacing_progressive();
799 if(interlace == 1) return *new png_interlacing_adam(3);
800 throw std::runtime_error("Unknown interlace type");
804 void decoder::decode_png(std::istream& stream)
806 png_dechunker dechunk(stream);
807 if(!dechunk.next_chunk())
808 throw std::runtime_error("PNG file has no chunks");
809 ihdr_chunk hdr(dechunk);
810 autorelease<png_decompressor> idat_decomp(png_decompressor::get(dechunk, hdr.compression));
811 autorelease<png_filterbank> filterbank(png_filterbank::get(*idat_decomp, hdr.filter, hdr.type,
812 hdr.depth, hdr.width));
813 autorelease<png_pixel_decoder> pixdecoder(png_pixel_decoder::get(*filterbank, hdr.type, hdr.depth,
814 hdr.width));
815 autorelease<png_interlacing> interlace(png_interlacing::get(hdr.interlace));
816 std::vector<uint32_t> ndata;
817 std::vector<uint32_t> npalette;
818 ndata.resize(hdr.width * hdr.height);
819 if(ndata.size() / hdr.width != hdr.height)
820 throw std::bad_alloc();
821 if(hdr.type == 3) {
822 npalette.resize(1 << hdr.depth);
823 for(size_t i = 0; i < npalette.size(); i++)
824 npalette[i] = 0xFF000000U;
826 if(!dechunk.next_chunk())
827 throw std::runtime_error("PNG file has no chunks besides header");
828 uint8_t trans[6];
829 uint8_t* _trans = NULL;
830 for(size_t pass = 0; pass < interlace->passes(); pass++) {
831 size_t scanline = 0;
832 png_interlacing::pass_info pinfo = interlace->pass(pass);
833 auto resolution = png_interlacing::pass_size(hdr.width, hdr.height, pinfo);
834 pixdecoder->adjust_row(resolution.first);
835 std::vector<uint32_t> scanlineb;
836 scanlineb.resize(resolution.first);
837 while(true) {
838 switch(dechunk.chunk_type()) {
839 case 0x49454E44: //IEND.
840 throw std::runtime_error("Unexpected IEND chunk");
841 case 0x504C5445: //PLTE.
842 if(hdr.type == 0 || hdr.type == 4)
843 throw std::runtime_error("Illegal PLTE in types 0/4");
844 if(hdr.type == 2 || hdr.type == 6)
845 break; //Advisory.
846 if(dechunk.chunk_size() > 3 * npalette.size())
847 throw std::runtime_error("PLTE too large");
848 for(size_t i = 0; i < dechunk.chunk_size() / 3; i++) {
849 uint8_t buf[3];
850 dechunk.chunk_read(buf, 3);
851 npalette[i] = (npalette[i] & 0xFF000000U) |
852 ((uint32_t)buf[0] << 16) |
853 ((uint32_t)buf[1] << 8) |
854 ((uint32_t)buf[2]);
856 break;
857 case 0x74524E53: //tRNS.
858 if(hdr.type == 4 || hdr.type == 6)
859 throw std::runtime_error("Illegal tRNS in types 4/6");
860 else if(hdr.type == 0) {
861 if(dechunk.chunk_size() != 2)
862 throw std::runtime_error("Expected 2-byte tRNS for type0");
863 dechunk.chunk_read(trans, 2);
864 } else if(hdr.type == 2) {
865 if(dechunk.chunk_size() != 6)
866 throw std::runtime_error("Expected 6-byte tRNS for type2");
867 dechunk.chunk_read(trans, 6);
868 } else if(hdr.type == 3) {
869 if(dechunk.chunk_size() > npalette.size())
870 throw std::runtime_error("tRNS too large");
871 for(size_t i = 0; i < dechunk.chunk_size(); i++) {
872 uint8_t buf[1];
873 dechunk.chunk_read(buf, 1);
874 npalette[i] = (npalette[i] & 0x00FFFFFFU) |
875 ((uint32_t)buf[0] << 24);
878 _trans = trans;
879 break;
880 case 0x49444154: //IDAT.
881 if(scanline == resolution.second)
882 goto next_pass;
883 while(pixdecoder->decode(&scanlineb[0], _trans)) {
884 size_t rline = scanline * pinfo.ymod + pinfo.yoff;
885 for(size_t i = 0; i < resolution.first; i++) {
886 ndata[rline * hdr.width + (i * pinfo.xmod + pinfo.xoff)] =
887 scanlineb[i];
889 scanline++;
890 if(scanline == resolution.second)
891 goto next_pass;
893 break;
894 default:
895 if((dechunk.chunk_type() & 0x20000000U) == 0)
896 throw std::runtime_error("Unknown critical chunk");
897 break;
899 dechunk.next_chunk();
901 next_pass:
904 while(dechunk.next_chunk()) {
905 switch(dechunk.chunk_type()) {
906 case 0x49454E44: //IEND.
907 goto out;
908 case 0x504C5445: //PLTE
909 throw std::runtime_error("PLTE not allowed after image data");
910 case 0x49444154: //IDAT.
911 break;
912 default:
913 if((dechunk.chunk_type() & 0x20000000U) == 0)
914 throw std::runtime_error("Unknown critical chunk");
917 out:
918 std::swap(data, ndata);
919 std::swap(palette, npalette);
920 has_palette = (hdr.type == 3);
921 width = hdr.width;
922 height = hdr.height;
925 decoder::decoder(const std::string& file)
927 std::istream& s = zip::openrel(file, "");
928 try {
929 decode_png(s);
930 delete &s;
931 } catch(...) {
932 delete &s;
933 throw;
937 decoder::decoder(std::istream& file)
939 decode_png(file);
942 decoder::decoder()
944 width = 0;
945 height = 0;
946 has_palette = false;
949 encoder::encoder()
951 width = 0;
952 height = 0;
953 has_palette = false;
954 has_alpha = false;
955 colorkey = 0xFFFFFFFFU;
958 void encoder::encode(const std::string& file) const
960 std::ofstream s(file, std::ios::binary);
961 if(!s)
962 throw std::runtime_error("Can't open file to write PNG image to");
963 encode(s);
966 void encoder::encode(std::ostream& file) const
968 size_t pbits = size_to_bits(palette.size());
969 //Write the PNG magic.
970 char png_magic[] = {-119, 80, 78, 71, 13, 10, 26, 10};
971 file.write(png_magic, sizeof(png_magic));
972 //Write the IHDR
973 char ihdr[13];
974 serialization::u32b(ihdr + 0, width);
975 serialization::u32b(ihdr + 4, height);
976 ihdr[8] = has_palette ? size_to_bits(palette.size()) : 8;
977 ihdr[9] = has_palette ? 3 : (has_alpha ? 6 : 2);
978 ihdr[10] = 0; //Deflate,
979 ihdr[11] = 0; //Filter bank 0
980 ihdr[12] = 0; //No interlacing.
981 boost::iostreams::stream<png_chunk_output> ihdr_h(file, 0x49484452);
982 ihdr_h.write(ihdr, sizeof(ihdr));
983 ihdr_h.close();
984 //Write the PLTE.
985 if(has_palette) {
986 std::vector<char> data;
987 data.resize(3 * palette.size());
988 for(size_t i = 0; i < palette.size(); i++) {
989 data[3 * i + 0] = palette[i] >> 16;
990 data[3 * i + 1] = palette[i] >> 8;
991 data[3 * i + 2] = palette[i] >> 0;
993 boost::iostreams::stream<png_chunk_output> plte_h(file, 0x504C5445);
994 plte_h.write(&data[0], data.size());
995 plte_h.close();
997 //Write the tRNS.
998 if(has_palette && has_alpha) {
999 std::vector<char> data;
1000 data.resize(palette.size());
1001 for(size_t i = 0; i < palette.size(); i++)
1002 data[i] = palette[i] >> 24;
1003 boost::iostreams::stream<png_chunk_output> trns_h(file, 0x74524E53);
1004 trns_h.write(&data[0], data.size());
1005 trns_h.close();
1007 //Write the IDAT
1008 boost::iostreams::filtering_ostream idat_h;
1009 boost::iostreams::zlib_params params;
1010 params.noheader = false;
1011 idat_h.push(boost::iostreams::zlib_compressor(params));
1012 idat_h.push(png_chunk_output(file, 0x49444154));
1013 std::vector<char> buf;
1014 buf.resize(1 + 4 * width);
1015 size_t bufstride = buffer_stride(width, has_palette, has_alpha, palette.size());
1016 for(size_t i = 0; i < height; i++) {
1017 buf[0] = 0; //No filter.
1018 if(has_palette)
1019 switch(pbits) {
1020 case 1: write_row_pal1(&buf[1], &data[width * i], width); break;
1021 case 2: write_row_pal2(&buf[1], &data[width * i], width); break;
1022 case 4: write_row_pal4(&buf[1], &data[width * i], width); break;
1023 case 8: write_row_pal8(&buf[1], &data[width * i], width); break;
1024 case 16: write_row_pal16(&buf[1], &data[width * i], width); break;
1026 else if(has_alpha)
1027 write_row_rgba(&buf[1], &data[width * i], width);
1028 else
1029 write_row_rgb(&buf[1], &data[width * i], width);
1030 idat_h.write(&buf[0], bufstride);
1032 idat_h.pop();
1033 idat_h.pop();
1034 //Write the IEND and finish.
1035 boost::iostreams::stream<png_chunk_output> iend_h(file, 0x49454E44);
1036 iend_h.close();
1037 if(!file)
1038 throw std::runtime_error("Can't write target PNG file");
1042 int main(int argc, char** argv)
1044 png::decoder img;
1045 decode_png(argv[1], img);
1046 std::cout << "Size: " << img.width << "*" << img.height << std::endl;
1047 if(img.has_palette)
1048 std::cout << "Image is paletted, " << img.palette.size() << " colors." << std::endl;
1049 if(img.has_palette) {
1050 for(size_t i = 0; i < img.data.size(); i++) {
1051 if(i > 0 && i % img.width == 0)
1052 std::cout << std::endl;
1053 std::cout << hex::to(img.palette[img.data[i]])
1054 << "<" << hex::to(img.data[i]) << "> ";
1056 } else {
1057 for(size_t i = 0; i < img.data.size(); i++) {
1058 if(i > 0 && i % img.width == 0)
1059 std::cout << std::endl;
1060 std::cout << hex::to(img.data[i]) << " ";
1063 std::cout << std::endl;
1065 //evaluate-lua b,p=gui.bitmap_load_png("/tmp/tbgn2c16.png"); on_paint = function() gui.bitmap_draw(0,0,b,p); end