Upload UI
[lsnes.git] / src / library / png-codec.cpp
blob7b70984bb847a05067fcbafe59f61aa9f1325a07
1 #include "png-codec.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>
13 #include <boost/iostreams/categories.hpp>
14 #include <boost/iostreams/copy.hpp>
15 #include <boost/iostreams/stream.hpp>
16 #include <boost/iostreams/stream_buffer.hpp>
17 #include <boost/iostreams/filter/symmetric.hpp>
18 #include <boost/iostreams/filter/zlib.hpp>
19 #include <boost/iostreams/filtering_stream.hpp>
20 #include <boost/iostreams/device/back_inserter.hpp>
22 namespace
24 void throw_zlib_error(int x)
26 switch(x) {
27 case Z_NEED_DICT: throw std::runtime_error("Dictionary needed");
28 case Z_ERRNO: throw std::runtime_error(std::string("OS error: ") + strerror(errno));
29 case Z_STREAM_ERROR: throw std::runtime_error("Stream error");
30 case Z_DATA_ERROR: throw std::runtime_error("Data error");
31 case Z_MEM_ERROR: throw std::bad_alloc();
32 case Z_BUF_ERROR: throw std::runtime_error("Buffer error");
33 case Z_VERSION_ERROR: throw std::runtime_error("Version error");
34 case Z_OK:
35 case Z_STREAM_END:
36 break;
37 default: throw std::runtime_error("Unknown error");
41 void* zlib_alloc(void* dummy, unsigned a, unsigned b)
43 return calloc(a, b);
46 void zlib_free(void* dummy, void* addr)
48 free(addr);
51 int size_to_bits(unsigned v)
53 if(v > 256) return 16;
54 if(v > 16) return 8;
55 if(v > 4) return 4;
56 if(v > 2) return 2;
57 return 1;
60 size_t buffer_stride(size_t width, bool has_pal, bool has_trans, size_t psize)
62 if(!has_pal)
63 return 1 + width * (has_trans ? 4 : 3);
64 else
65 return 1 + (width * size_to_bits(psize) + 7) / 8;
68 void write_row_pal1(char* output, const uint32_t* input, size_t w)
70 memset(output, 0, (w + 7) / 8);
71 for(size_t i = 0; i < w; i++)
72 output[i >> 3] |= ((input[i] & 1) << (7 -i % 8));
75 void write_row_pal2(char* output, const uint32_t* input, size_t w)
77 memset(output, 0, (w + 3) / 4);
78 for(size_t i = 0; i < w; i++)
79 output[i >> 2] |= ((input[i] & 3) << (2 * (3 - i % 4)));
82 void write_row_pal4(char* output, const uint32_t* input, size_t w)
84 memset(output, 0, (w + 1) / 2);
85 for(size_t i = 0; i < w; i++)
86 output[i >> 1] |= ((input[i] & 15) << (4 * (1 - i % 2)));
89 void write_row_pal8(char* output, const uint32_t* input, size_t w)
91 for(size_t i = 0; i < w; i++)
92 output[i] = input[i];
95 void write_row_pal16(char* output, const uint32_t* input, size_t w)
97 for(size_t i = 0; i < w; i++) {
98 output[2 * i + 0] = input[i] >> 8;
99 output[2 * i + 1] = input[i];
103 void write_row_rgba(char* output, const uint32_t* input, size_t w)
105 for(size_t i = 0; i < w; i++) {
106 output[4 * i + 0] = input[i] >> 16;
107 output[4 * i + 1] = input[i] >> 8;
108 output[4 * i + 2] = input[i];
109 output[4 * i + 3] = input[i] >> 24;
113 void write_row_rgb(char* output, const uint32_t* input, size_t w)
115 for(size_t i = 0; i < w; i++) {
116 output[3 * i + 0] = input[i] >> 16;
117 output[3 * i + 1] = input[i] >> 8;
118 output[3 * i + 2] = input[i];
122 //=========================================================
123 //==================== PNG CHUNKER ========================
124 //=========================================================
125 class png_chunk_output
127 public:
128 typedef char char_type;
129 struct category : boost::iostreams::closable_tag, boost::iostreams::sink_tag {};
130 png_chunk_output(std::ostream& _os, uint32_t _type)
131 : os(_os), type(_type)
135 void close()
137 uint32_t crc = crc32(0, NULL, 0);
138 char fixed[12];
139 write32ube(fixed, stream.size());
140 write32ube(fixed + 4, type);
141 crc = crc32(crc, reinterpret_cast<Bytef*>(fixed + 4), 4);
142 if(stream.size() > 0)
143 crc = crc32(crc, reinterpret_cast<Bytef*>(&stream[0]), stream.size());
144 write32ube(fixed + 8, crc);
145 os.write(fixed, 8);
146 os.write(&stream[0], stream.size());
147 os.write(fixed + 8, 4);
150 std::streamsize write(const char* s, std::streamsize n)
152 size_t oldsize = stream.size();
153 stream.resize(oldsize + n);
154 memcpy(&stream[oldsize], s, n);
155 return n;
157 protected:
158 std::vector<char> stream;
159 std::ostream& os;
160 uint32_t type;
164 //=========================================================
165 //=================== AUTORELEASE =========================
166 //=========================================================
167 template <typename T>
168 class autorelease
170 public:
171 autorelease(T& _obj) : obj(_obj) {}
172 ~autorelease() { delete &obj; }
173 T* operator->() { return &obj; }
174 T& operator*() { return obj; }
175 private:
176 T& obj;
177 autorelease(const autorelease&);
178 const autorelease& operator=(const autorelease&);
181 //=========================================================
182 //=================== PNG DECHUNKER =======================
183 //=========================================================
184 class png_dechunker
186 public:
187 png_dechunker(std::istream& _stream);
188 uint32_t chunk_size() { return size; }
189 uint32_t chunk_type() { return type; }
190 uint32_t chunk_read(uint8_t* buf, size_t limit);
191 bool next_chunk();
192 bool eof() { return eof_flag; }
193 bool chunk_eof() { return ptr == size; }
194 private:
195 std::istream& stream;
196 uint32_t size;
197 uint32_t type;
198 uint32_t ptr;
199 uint32_t crc;
200 bool eof_flag;
201 void verify_crc();
202 void load_chunk();
205 png_dechunker::png_dechunker(std::istream& _stream)
206 : stream(_stream)
208 size = 0;
209 type = 0;
210 ptr = 0;
211 eof_flag = false;
212 uint8_t magic[8] = {137, 80, 78, 71, 13, 10, 26, 10};
213 uint8_t magicbuf[8];
214 stream.read(reinterpret_cast<char*>(magicbuf), 8);
215 if(!stream || memcmp(magic, magicbuf, 8))
216 throw std::runtime_error("Not a PNG file");
219 uint32_t png_dechunker::chunk_read(uint8_t* buf, size_t limit)
221 limit = min(limit, static_cast<size_t>(size - ptr));
222 if(!limit)
223 return 0;
224 stream.read(reinterpret_cast<char*>(buf), limit);
225 if(!stream)
226 throw std::runtime_error("PNG file truncated");
227 crc = crc32(crc, buf, limit);
228 ptr += limit;
229 if(ptr == size)
230 verify_crc();
231 return limit;
234 bool png_dechunker::next_chunk()
236 if(eof_flag)
237 return false;
238 while(ptr < size) {
239 uint8_t buf[256];
240 chunk_read(buf, 256);
242 load_chunk();
243 return !eof_flag;
246 void png_dechunker::load_chunk()
248 uint8_t buf[8];
249 stream.read(reinterpret_cast<char*>(buf), 8);
250 if(!stream) {
251 if(stream.gcount() == 0) {
252 //EOF.
253 size = 0;
254 type = 0;
255 ptr = 0;
256 eof_flag = true;
257 return;
259 throw std::runtime_error("PNG file truncated");
261 size = read32ube(buf + 0);
262 type = read32ube(buf + 4);
263 crc = crc32(0, NULL, 0);
264 crc = crc32(crc, buf + 4, 4);
265 ptr = 0;
266 if(!size)
267 verify_crc();
270 void png_dechunker::verify_crc()
272 char buf[4];
273 stream.read(buf, 4);
274 if(!stream)
275 throw std::runtime_error("PNG file truncated");
276 uint32_t claim_crc = read32ube(buf);
277 if(crc != claim_crc)
278 throw std::runtime_error("PNG file chunk CRC check failed");
281 std::string format_type(uint32_t type)
283 uint8_t t1 = type >> 24;
284 uint8_t t2 = type >> 16;
285 uint8_t t3 = type >> 8;
286 uint8_t t4 = type;
287 if(t1 < 'A' || t2 < 'A' || t3 < 'A' || t4 < 'A')
288 goto badtype;
289 if(t1 > 'z' || t2 > 'z' || t3 > 'z' || t4 > 'z')
290 goto badtype;
291 return (stringfmt() << t1 << t2 << t3 << t4).str();
292 badtype:
293 return (stringfmt() << "0x" << std::hex << std::setw(8) << std::setfill('0') << type).str();
296 //=========================================================
297 //=================== PNG IHDR CHUNK ======================
298 //=========================================================
299 struct ihdr_chunk
301 ihdr_chunk(png_dechunker& d)
303 if(d.chunk_type() != 0x49484452)
304 throw std::runtime_error("Expected IHDR chunk");
305 if(d.chunk_size() != 13)
306 throw std::runtime_error("Expected IHDR chunk to be 13 bytes");
307 uint8_t buf[13];
308 d.chunk_read(buf, 13);
309 width = read32ube(buf + 0);
310 height = read32ube(buf + 4);
311 depth = buf[8];
312 type = buf[9];
313 compression = buf[10];
314 filter = buf[11];
315 interlace = buf[12];
317 size_t width;
318 size_t height;
319 uint8_t type;
320 uint8_t depth;
321 uint8_t compression;
322 uint8_t interlace;
323 uint8_t filter;
326 //=========================================================
327 //=================== PNG DECOMPRESSOR ====================
328 //=========================================================
329 class png_decompressor
331 public:
332 png_decompressor(png_dechunker& _dechunk) : dechunk(_dechunk) {}
333 virtual ~png_decompressor() {}
334 virtual void decompress(uint8_t*& out, size_t& outsize) = 0;
335 static png_decompressor& get(png_dechunker& dechunk, uint8_t compression);
336 protected:
337 png_dechunker& dechunk;
338 private:
339 png_decompressor(const png_decompressor&);
340 png_decompressor& operator=(const png_decompressor&);
343 class png_decompressor_zlib : public png_decompressor
345 public:
346 png_decompressor_zlib(png_dechunker& dechunk)
347 : png_decompressor(dechunk)
349 memset(&z, 0, sizeof(z));
350 z.zalloc = zlib_alloc;
351 z.zfree = zlib_free;
352 throw_zlib_error(inflateInit(&z));
353 buflen = 0;
354 bufptr = 0;
356 ~png_decompressor_zlib()
358 inflateEnd(&z);
360 void decompress(uint8_t*& out, size_t& outsize)
362 while(true) {
363 if(!buflen) {
364 buflen = dechunk.chunk_read(buf, sizeof(buf));
365 bufptr = 0;
367 z.next_in = buf + bufptr;
368 z.avail_in = buflen;
369 z.next_out = out;
370 z.avail_out = outsize;
371 int r = inflate(&z, Z_SYNC_FLUSH);
372 if(r == Z_BUF_ERROR || r == Z_STREAM_END) {
373 out = z.next_out;
374 outsize = z.avail_out;
375 bufptr = z.next_in - buf;
376 buflen = z.avail_in;
377 return;
379 throw_zlib_error(r);
380 out = z.next_out;
381 outsize = z.avail_out;
382 bufptr = z.next_in - buf;
383 buflen = z.avail_in;
386 private:
387 z_stream z;
388 png_decompressor_zlib(const png_decompressor_zlib&);
389 png_decompressor_zlib& operator=(const png_decompressor_zlib&);
390 uint8_t buf[256];
391 size_t bufptr;
392 size_t buflen;
395 png_decompressor& png_decompressor::get(png_dechunker& dechunk, uint8_t compression)
397 if(compression == 0) return *new png_decompressor_zlib(dechunk);
398 throw std::runtime_error("Unsupported compression method");
401 //=========================================================
402 //=================== PNG FILTERBANK ======================
403 //=========================================================
404 class png_filterbank
406 public:
407 png_filterbank(png_decompressor& _decomp, size_t _pitch, size_t _elements)
408 : decomp(_decomp), pitch(_pitch), elements(_elements)
411 virtual ~png_filterbank() {}
412 size_t outsize() { return pitch * elements; }
413 void adjust_row(uint8_t type, uint8_t depth, size_t width)
415 size_t bits = png_filterbank::get_bits(type, depth);
416 size_t n_pitch = (bits >= 8) ? (bits >> 3) : 1;
417 size_t pfactor = 8 / bits;
418 size_t n_elements = (bits >= 8) ? width : ((width + pfactor - 1) / pfactor);
419 adjusted(n_pitch, n_elements);
420 pitch = n_pitch;
421 elements = n_elements;
423 virtual bool row(uint8_t* data) = 0;
424 static png_filterbank& get(png_decompressor& _decomp, uint8_t filterbank, uint8_t type, uint8_t depth,
425 size_t width);
426 static size_t get_bits(uint8_t type, uint8_t depth)
428 size_t mul[7] = {1, 0, 3, 1, 2, 0, 4};
429 if(type > 6 || !mul[type]) throw std::runtime_error("Unrecognized color type");
430 return mul[type] * depth;
432 protected:
433 png_decompressor& decomp;
434 size_t pitch;
435 size_t elements;
436 virtual void adjusted(size_t _pitch, size_t _elements) = 0;
437 private:
438 png_filterbank(const png_filterbank&);
439 png_filterbank& operator=(const png_filterbank&);
442 inline uint8_t predict_none(uint8_t left, uint8_t up, uint8_t upleft)
444 return 0;
447 inline uint8_t predict_left(uint8_t left, uint8_t up, uint8_t upleft)
449 return left;
452 inline uint8_t predict_up(uint8_t left, uint8_t up, uint8_t upleft)
454 return up;
457 inline uint8_t predict_average(uint8_t left, uint8_t up, uint8_t upleft)
459 return (left >> 1) + (up >> 1) + (left & up & 1);
462 inline uint8_t predict_paeth(uint8_t left, uint8_t up, uint8_t upleft)
464 int16_t p = (int16_t)up + left - upleft;
465 uint16_t pa = (p > left) ? (p - left) : (left - p);
466 uint16_t pb = (p > up) ? (p - up) : (up - p);
467 uint16_t pc = (p > upleft) ? (p - upleft) : (upleft - p);
468 if(pa <= pb && pa <= pc)
469 return left;
470 if(pb <= pc)
471 return up;
472 return upleft;
475 template<uint8_t(*predictor)(uint8_t left, uint8_t up, uint8_t upleft)> void do_filter_3(uint8_t* target,
476 const uint8_t* row, const uint8_t* above, size_t pitch, size_t length)
478 for(size_t i = 0; i < pitch; i++)
479 target[i] = row[i] + predictor(0, above[i], 0);
480 for(size_t i = pitch; i < length; i++)
481 target[i] = row[i] + predictor(target[i - pitch], above[i], above[i - pitch]);
484 class png_filterbank_0 : public png_filterbank
486 public:
487 png_filterbank_0(png_decompressor& decomp, size_t pitch, size_t elements)
488 : png_filterbank(decomp, pitch, elements)
490 above.resize(pitch * elements);
491 tmp.resize(pitch * elements + 1);
492 tmp2 = &tmp[0];
493 tmpleft = tmp.size();
495 ~png_filterbank_0() {}
496 bool row(uint8_t* data)
498 decomp.decompress(tmp2, tmpleft);
499 if(tmpleft)
500 return false;
501 uint8_t filter = tmp[0];
502 uint8_t* t = data;
503 uint8_t* s = &tmp[1];
504 uint8_t* a = &above[0];
505 switch(filter) {
506 case 0: do_filter_3<predict_none>(t, s, a, pitch, pitch * elements); break;
507 case 1: do_filter_3<predict_left>(t, s, a, pitch, pitch * elements); break;
508 case 2: do_filter_3<predict_up>(t, s, a, pitch, pitch * elements); break;
509 case 3: do_filter_3<predict_average>(t, s, a, pitch, pitch * elements); break;
510 case 4: do_filter_3<predict_paeth>(t, s, a, pitch, pitch * elements); break;
511 default: throw std::runtime_error("Unknown filter for filter bank 0");
513 tmp2 = &tmp[0];
514 tmpleft = tmp.size();
515 memcpy(a, t, pitch * elements);
516 return true;
518 protected:
519 void adjusted(size_t _pitch, size_t _elements)
521 std::vector<uint8_t> nabove, ntmp;
522 nabove.resize(_pitch * _elements);
523 ntmp.resize(_pitch * _elements + 1);
524 std::swap(above, nabove);
525 std::swap(tmp, ntmp);
526 tmp2 = &tmp[0];
527 tmpleft = tmp.size();
529 private:
530 png_filterbank_0(const png_filterbank_0&);
531 png_filterbank_0& operator=(const png_filterbank_0&);
532 std::vector<uint8_t> above;
533 std::vector<uint8_t> tmp;
534 uint8_t* tmp2;
535 size_t tmpleft;
538 png_filterbank& png_filterbank::get(png_decompressor& decomp, uint8_t filterbank, uint8_t type,
539 uint8_t depth, size_t width)
541 size_t bits = png_filterbank::get_bits(type, depth);
542 size_t pitch = (bits >= 8) ? (bits >> 3) : 1;
543 size_t elements = (bits >= 8) ? width : (width / (8 / bits));
544 if(filterbank == 0) return *new png_filterbank_0(decomp, pitch, elements);
545 throw std::runtime_error("Unknown scanline filter bank");
548 //=========================================================
549 //=================== PNG PIXEL DECODING ==================
550 //=========================================================
551 class png_pixel_decoder
553 public:
554 png_pixel_decoder(png_filterbank& _filter, uint8_t _type, uint8_t _depth, size_t _width)
555 : filter(_filter), type(_type), depth(_depth), width(_width)
557 tmp.resize((png_filterbank::get_bits(type, depth) * width + 7) / 8);
559 virtual ~png_pixel_decoder() {}
560 virtual bool decode(uint32_t* output, uint8_t* trans) = 0;
561 static png_pixel_decoder& get(png_filterbank& _filter, uint8_t type, uint8_t depth, size_t width);
562 void adjust_row(size_t _width)
564 std::vector<uint8_t> ntmp;
565 ntmp.resize((png_filterbank::get_bits(type, depth) * _width + 7) / 8);
566 filter.adjust_row(type, depth, _width);
567 std::swap(tmp, ntmp);
568 width = _width;
570 protected:
571 size_t width;
572 uint8_t type;
573 uint8_t depth;
574 png_filterbank& filter;
575 std::vector<uint8_t> tmp;
576 bool fill()
578 return filter.row(&tmp[0]);
580 private:
581 png_pixel_decoder(const png_pixel_decoder&);
582 png_pixel_decoder& operator=(const png_pixel_decoder&);
585 template<unsigned bits>
586 inline uint32_t decode_type_0(const uint8_t* in, unsigned bit, const uint8_t* trans)
588 uint16_t v;
589 uint32_t m;
590 uint32_t s = 0;
591 switch(bits) {
592 case 1: v = (*in >> (7 - bit)) & 1; m = 0xFFFFFF; break;
593 case 2: v = (*in >> (6 - bit)) & 3; m = 0x555555; break;
594 case 4: v = (*in >> (4 - bit)) & 15; m = 0x111111; break;
595 case 8: v = *in; m = 0xFFFFFF; m = 0x010101; break;
596 case 16: v = read16ube(in); m = 0x010101; s = 8; break;
598 uint32_t alpha = 0xFF000000U;
599 if(v == read16ube(trans))
600 alpha = 0;
601 return alpha | (m * (v >> s));
604 template<unsigned bits>
605 inline uint32_t decode_type_2(const uint8_t* in, unsigned bit, const uint8_t* trans)
607 uint32_t alpha = 0xFF000000U;
608 if(trans) {
609 if(bits == 8 && in[0] == trans[1] && in[1] == trans[3] && in[2] == trans[5])
610 alpha = 0;
611 if(bits == 16 && !memcmp(in, trans, 6))
612 alpha = 0;
614 if(bits == 8)
615 return ((uint32_t)in[0] << 16) |
616 ((uint32_t)in[1] << 8) |
617 ((uint32_t)in[2]) |
618 alpha;
619 else if(bits == 16)
620 return ((uint32_t)in[0] << 16) |
621 ((uint32_t)in[2] << 8) |
622 ((uint32_t)in[4]) |
623 alpha;
626 template<unsigned bits>
627 inline uint32_t decode_type_3(const uint8_t* in, unsigned bit, const uint8_t* trans)
629 switch(bits) {
630 case 1: return (*in >> (7 - bit)) & 1;
631 case 2: return (*in >> (6 - bit)) & 3;
632 case 4: return (*in >> (4 - bit)) & 15;
633 case 8: return *in;
634 case 16: return read16ube(in);
638 template<unsigned bits>
639 uint32_t decode_type_4(const uint8_t* in, unsigned bit, const uint8_t* trans)
641 if(bits == 8)
642 return ((uint32_t)in[0] << 16) |
643 ((uint32_t)in[0] << 8) |
644 ((uint32_t)in[0]) |
645 ((uint32_t)in[1] << 24);
646 else if(bits == 16)
647 return ((uint32_t)in[0] << 16) |
648 ((uint32_t)in[0] << 8) |
649 ((uint32_t)in[0]) |
650 ((uint32_t)in[2] << 24);
653 template<unsigned bits>
654 uint32_t decode_type_6(const uint8_t* in, unsigned bit, const uint8_t* trans)
656 if(bits == 8)
657 return ((uint32_t)in[0] << 16) |
658 ((uint32_t)in[1] << 8) |
659 ((uint32_t)in[2]) |
660 ((uint32_t)in[3] << 24);
661 else if(bits == 16)
662 return ((uint32_t)in[0] << 16) |
663 ((uint32_t)in[2] << 8) |
664 ((uint32_t)in[4]) |
665 ((uint32_t)in[6] << 24);
668 template<size_t bits, uint32_t (*decodefn)(const uint8_t* in, unsigned bit, const uint8_t* trans)>
669 class png_pixel_decoder_fn : public png_pixel_decoder
671 public:
672 png_pixel_decoder_fn(png_filterbank& _filter, uint8_t _type, uint8_t _depth, size_t _width)
673 : png_pixel_decoder(_filter, _type, _depth, _width)
676 ~png_pixel_decoder_fn() {}
677 bool decode(uint32_t* output, uint8_t* trans)
679 if(!fill())
680 return false;
681 size_t off = 0;
682 unsigned bit = 0;
683 for(size_t i = 0; i < width; i++) {
684 output[i] = decodefn(&tmp[off], bit, trans);
685 bit += bits;
686 off += (bit >> 3);
687 bit &= 7;
689 return true;
693 png_pixel_decoder& png_pixel_decoder::get(png_filterbank& filter, uint8_t type, uint8_t depth, size_t width)
695 switch(type) {
696 case 0:
697 switch(depth) {
698 case 1: return *new png_pixel_decoder_fn<1, decode_type_0<1>>(filter, type, depth, width);
699 case 2: return *new png_pixel_decoder_fn<2, decode_type_0<2>>(filter, type, depth, width);
700 case 4: return *new png_pixel_decoder_fn<4, decode_type_0<4>>(filter, type, depth, width);
701 case 8: return *new png_pixel_decoder_fn<8, decode_type_0<8>>(filter, type, depth, width);
702 case 16: return *new png_pixel_decoder_fn<16, decode_type_0<16>>(filter, type, depth, width);
703 default: throw std::runtime_error("Unsupported color depth for type 0");
705 case 2:
706 switch(depth) {
707 case 8: return *new png_pixel_decoder_fn<24, decode_type_2<8>>(filter, type, depth, width);
708 case 16: return *new png_pixel_decoder_fn<48, decode_type_2<16>>(filter, type, depth, width);
709 default: throw std::runtime_error("Unsupported color depth for type 2");
711 case 3:
712 switch(depth) {
713 case 1: return *new png_pixel_decoder_fn<1, decode_type_3<1>>(filter, type, depth, width);
714 case 2: return *new png_pixel_decoder_fn<2, decode_type_3<2>>(filter, type, depth, width);
715 case 4: return *new png_pixel_decoder_fn<4, decode_type_3<4>>(filter, type, depth, width);
716 case 8: return *new png_pixel_decoder_fn<8, decode_type_3<8>>(filter, type, depth, width);
717 case 16: return *new png_pixel_decoder_fn<16, decode_type_3<16>>(filter, type, depth, width);
718 default: throw std::runtime_error("Unsupported color depth for type 3");
720 case 4:
721 switch(depth) {
722 case 8: return *new png_pixel_decoder_fn<16, decode_type_4<8>>(filter, type, depth, width);
723 case 16: return *new png_pixel_decoder_fn<32, decode_type_4<16>>(filter, type, depth, width);
724 default: throw std::runtime_error("Unsupported color depth for type 4");
726 case 6:
727 switch(depth) {
728 case 8: return *new png_pixel_decoder_fn<32, decode_type_6<8>>(filter, type, depth, width);
729 case 16: return *new png_pixel_decoder_fn<64, decode_type_6<16>>(filter, type, depth, width);
730 default: throw std::runtime_error("Unsupported color depth for type 6");
732 default: throw std::runtime_error("Unsupported color type");
736 //=========================================================
737 //=================== PNG INTERLACING =====================
738 //=========================================================
739 class png_interlacing
741 public:
742 struct pass_info
744 size_t xoff;
745 size_t xmod;
746 size_t yoff;
747 size_t ymod;
749 png_interlacing()
752 virtual ~png_interlacing() {}
753 virtual size_t passes() = 0;
754 virtual pass_info pass(size_t n) = 0;
755 static std::pair<size_t, size_t> pass_size(size_t iwidth, size_t iheight, pass_info pinfo)
757 size_t width = (iwidth + (pinfo.xmod - pinfo.xoff - 1)) / pinfo.xmod;
758 size_t height = (iheight + (pinfo.ymod - pinfo.yoff - 1)) / pinfo.ymod;
759 if(!width || !height)
760 return std::make_pair(0, 0);
761 return std::make_pair(width, height);
763 static png_interlacing& get(uint8_t interlace);
764 private:
765 png_interlacing(const png_interlacing&);
766 png_interlacing& operator=(const png_interlacing&);
769 class png_interlacing_progressive : public png_interlacing
771 public:
772 ~png_interlacing_progressive() {}
773 size_t passes() { return 1; }
774 png_interlacing::pass_info pass(size_t n)
776 png_interlacing::pass_info p;
777 p.xoff = p.yoff = 0;
778 p.xmod = p.ymod = 1;
779 return p;
783 class png_interlacing_adam : public png_interlacing
785 public:
786 png_interlacing_adam(unsigned _order) : order(_order) {}
787 ~png_interlacing_adam() {}
788 size_t passes() { return 2 * order + 1; }
789 png_interlacing::pass_info pass(size_t n)
791 size_t one = 1;
792 png_interlacing::pass_info p;
793 if(n == 0) {
794 p.xmod = p.ymod = (one << order);
795 p.xoff = p.yoff = 0;
796 } else {
797 p.xoff = (n % 2) ? (one << (order - (n + 1) / 2)) : 0;
798 p.yoff = (n % 2) ? 0 : (one << (order - n / 2));
799 p.xmod = one << (order - n / 2);
800 p.ymod = one << (order - (n - 1) / 2);
802 return p;
804 private:
805 unsigned order;
808 png_interlacing& png_interlacing::get(uint8_t interlace)
810 if(interlace == 0) return *new png_interlacing_progressive();
811 if(interlace == 1) return *new png_interlacing_adam(3);
812 throw std::runtime_error("Unknown interlace type");
816 void png_decoded_image::decode_png(std::istream& stream)
818 png_dechunker dechunk(stream);
819 if(!dechunk.next_chunk())
820 throw std::runtime_error("PNG file has no chunks");
821 ihdr_chunk hdr(dechunk);
822 autorelease<png_decompressor> idat_decomp(png_decompressor::get(dechunk, hdr.compression));
823 autorelease<png_filterbank> filterbank(png_filterbank::get(*idat_decomp, hdr.filter, hdr.type,
824 hdr.depth, hdr.width));
825 autorelease<png_pixel_decoder> pixdecoder(png_pixel_decoder::get(*filterbank, hdr.type, hdr.depth,
826 hdr.width));
827 autorelease<png_interlacing> interlace(png_interlacing::get(hdr.interlace));
828 std::vector<uint32_t> ndata;
829 std::vector<uint32_t> npalette;
830 ndata.resize(hdr.width * hdr.height);
831 if(ndata.size() / hdr.width != hdr.height)
832 throw std::bad_alloc();
833 if(hdr.type == 3) {
834 npalette.resize(1 << hdr.depth);
835 for(size_t i = 0; i < npalette.size(); i++)
836 npalette[i] = 0xFF000000U;
838 if(!dechunk.next_chunk())
839 throw std::runtime_error("PNG file has no chunks besides header");
840 uint8_t trans[6];
841 uint8_t* _trans = NULL;
842 for(size_t pass = 0; pass < interlace->passes(); pass++) {
843 size_t scanline = 0;
844 png_interlacing::pass_info pinfo = interlace->pass(pass);
845 auto resolution = png_interlacing::pass_size(hdr.width, hdr.height, pinfo);
846 pixdecoder->adjust_row(resolution.first);
847 std::vector<uint32_t> scanlineb;
848 scanlineb.resize(resolution.first);
849 while(true) {
850 switch(dechunk.chunk_type()) {
851 case 0x49454E44: //IEND.
852 throw std::runtime_error("Unexpected IEND chunk");
853 case 0x504C5445: //PLTE.
854 if(hdr.type == 0 || hdr.type == 4)
855 throw std::runtime_error("Illegal PLTE in types 0/4");
856 if(hdr.type == 2 || hdr.type == 6)
857 break; //Advisory.
858 if(dechunk.chunk_size() > 3 * npalette.size())
859 throw std::runtime_error("PLTE too large");
860 for(size_t i = 0; i < dechunk.chunk_size() / 3; i++) {
861 uint8_t buf[3];
862 dechunk.chunk_read(buf, 3);
863 npalette[i] = (npalette[i] & 0xFF000000U) |
864 ((uint32_t)buf[0] << 16) |
865 ((uint32_t)buf[1] << 8) |
866 ((uint32_t)buf[2]);
868 break;
869 case 0x74524E53: //tRNS.
870 if(hdr.type == 4 || hdr.type == 6)
871 throw std::runtime_error("Illegal tRNS in types 4/6");
872 else if(hdr.type == 0) {
873 if(dechunk.chunk_size() != 2)
874 throw std::runtime_error("Expected 2-byte tRNS for type0");
875 dechunk.chunk_read(trans, 2);
876 } else if(hdr.type == 2) {
877 if(dechunk.chunk_size() != 6)
878 throw std::runtime_error("Expected 6-byte tRNS for type2");
879 dechunk.chunk_read(trans, 6);
880 } else if(hdr.type == 3) {
881 if(dechunk.chunk_size() > npalette.size())
882 throw std::runtime_error("tRNS too large");
883 for(size_t i = 0; i < dechunk.chunk_size(); i++) {
884 uint8_t buf[1];
885 dechunk.chunk_read(buf, 1);
886 npalette[i] = (npalette[i] & 0x00FFFFFFU) |
887 ((uint32_t)buf[0] << 24);
890 _trans = trans;
891 break;
892 case 0x49444154: //IDAT.
893 if(scanline == resolution.second)
894 goto next_pass;
895 while(pixdecoder->decode(&scanlineb[0], _trans)) {
896 size_t rline = scanline * pinfo.ymod + pinfo.yoff;
897 for(size_t i = 0; i < resolution.first; i++) {
898 ndata[rline * hdr.width + (i * pinfo.xmod + pinfo.xoff)] =
899 scanlineb[i];
901 scanline++;
902 if(scanline == resolution.second)
903 goto next_pass;
905 break;
906 default:
907 if((dechunk.chunk_type() & 0x20000000U) == 0)
908 throw std::runtime_error("Unknown critical chunk");
909 break;
911 dechunk.next_chunk();
913 next_pass:
916 while(dechunk.next_chunk()) {
917 switch(dechunk.chunk_type()) {
918 case 0x49454E44: //IEND.
919 goto out;
920 case 0x504C5445: //PLTE
921 throw std::runtime_error("PLTE not allowed after image data");
922 case 0x49444154: //IDAT.
923 break;
924 default:
925 if((dechunk.chunk_type() & 0x20000000U) == 0)
926 throw std::runtime_error("Unknown critical chunk");
929 out:
930 std::swap(data, ndata);
931 std::swap(palette, npalette);
932 has_palette = (hdr.type == 3);
933 width = hdr.width;
934 height = hdr.height;
937 png_decoded_image::png_decoded_image(const std::string& file)
939 std::istream& s = open_file_relative(file, "");
940 try {
941 decode_png(s);
942 delete &s;
943 } catch(...) {
944 delete &s;
945 throw;
949 png_decoded_image::png_decoded_image(std::istream& file)
951 decode_png(file);
954 png_decoded_image::png_decoded_image()
956 width = 0;
957 height = 0;
958 has_palette = false;
961 png_encodedable_image::png_encodedable_image()
963 width = 0;
964 height = 0;
965 has_palette = false;
966 has_alpha = false;
967 colorkey = 0xFFFFFFFFU;
970 void png_encodedable_image::encode(const std::string& file) const
972 std::ofstream s(file);
973 if(!s)
974 throw std::runtime_error("Can't open file to write PNG image to");
975 encode(s);
978 void png_encodedable_image::encode(std::ostream& file) const
980 size_t pbits = size_to_bits(palette.size());
981 //Write the PNG magic.
982 char png_magic[] = {-119, 80, 78, 71, 13, 10, 26, 10};
983 file.write(png_magic, sizeof(png_magic));
984 //Write the IHDR
985 char ihdr[13];
986 write32ube(ihdr + 0, width);
987 write32ube(ihdr + 4, height);
988 ihdr[8] = has_palette ? size_to_bits(palette.size()) : 8;
989 ihdr[9] = has_palette ? 3 : (has_alpha ? 6 : 2);
990 ihdr[10] = 0; //Deflate,
991 ihdr[11] = 0; //Filter bank 0
992 ihdr[12] = 0; //No interlacing.
993 boost::iostreams::stream<png_chunk_output> ihdr_h(file, 0x49484452);
994 ihdr_h.write(ihdr, sizeof(ihdr));
995 ihdr_h.close();
996 //Write the PLTE.
997 if(has_palette) {
998 std::vector<char> data;
999 data.resize(3 * palette.size());
1000 for(size_t i = 0; i < palette.size(); i++) {
1001 data[3 * i + 0] = palette[i] >> 16;
1002 data[3 * i + 1] = palette[i] >> 8;
1003 data[3 * i + 2] = palette[i] >> 0;
1005 boost::iostreams::stream<png_chunk_output> plte_h(file, 0x504C5445);
1006 plte_h.write(&data[0], data.size());
1007 plte_h.close();
1009 //Write the tRNS.
1010 if(has_palette && has_alpha) {
1011 std::vector<char> data;
1012 data.resize(palette.size());
1013 for(size_t i = 0; i < palette.size(); i++)
1014 data[i] = palette[i] >> 24;
1015 boost::iostreams::stream<png_chunk_output> trns_h(file, 0x74524E53);
1016 trns_h.write(&data[0], data.size());
1017 trns_h.close();
1019 //Write the IDAT
1020 boost::iostreams::filtering_ostream idat_h;
1021 boost::iostreams::zlib_params params;
1022 params.noheader = false;
1023 idat_h.push(boost::iostreams::zlib_compressor(params));
1024 idat_h.push(png_chunk_output(file, 0x49444154));
1025 std::vector<char> buf;
1026 buf.resize(1 + 4 * width);
1027 size_t bufstride = buffer_stride(width, has_palette, has_alpha, palette.size());
1028 for(size_t i = 0; i < height; i++) {
1029 buf[0] = 0; //No filter.
1030 if(has_palette)
1031 switch(pbits) {
1032 case 1: write_row_pal1(&buf[1], &data[width * i], width); break;
1033 case 2: write_row_pal2(&buf[1], &data[width * i], width); break;
1034 case 4: write_row_pal4(&buf[1], &data[width * i], width); break;
1035 case 8: write_row_pal8(&buf[1], &data[width * i], width); break;
1036 case 16: write_row_pal16(&buf[1], &data[width * i], width); break;
1038 else if(has_alpha)
1039 write_row_rgba(&buf[1], &data[width * i], width);
1040 else
1041 write_row_rgb(&buf[1], &data[width * i], width);
1042 idat_h.write(&buf[0], bufstride);
1044 idat_h.pop();
1045 idat_h.pop();
1046 //Write the IEND and finish.
1047 boost::iostreams::stream<png_chunk_output> iend_h(file, 0x49454E44);
1048 iend_h.close();
1049 if(!file)
1050 throw std::runtime_error("Can't write target PNG file");
1054 int main(int argc, char** argv)
1056 png_decoded_image img;
1057 decode_png(argv[1], img);
1058 std::cout << "Size: " << img.width << "*" << img.height << std::endl;
1059 if(img.has_palette)
1060 std::cout << "Image is paletted, " << img.palette.size() << " colors." << std::endl;
1061 if(img.has_palette) {
1062 for(size_t i = 0; i < img.data.size(); i++) {
1063 if(i > 0 && i % img.width == 0)
1064 std::cout << std::endl;
1065 std::cout << std::hex << std::setw(8) << std::setfill('0') << img.palette[img.data[i]]
1066 << "<" << std::hex << std::setw(4) << std::setfill('0') << img.data[i] << "> ";
1068 } else {
1069 for(size_t i = 0; i < img.data.size(); i++) {
1070 if(i > 0 && i % img.width == 0)
1071 std::cout << std::endl;
1072 std::cout << std::hex << std::setw(8) << std::setfill('0') << img.data[i] << " ";
1075 std::cout << std::endl;
1077 //evaluate-lua b,p=gui.bitmap_load_png("/tmp/tbgn2c16.png"); on_paint = function() gui.bitmap_draw(0,0,b,p); end