1 #include "png-codec.hpp"
2 #include "serialization.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>
24 void throw_zlib_error(int 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");
37 default: throw std::runtime_error("Unknown error");
41 void* zlib_alloc(void* dummy
, unsigned a
, unsigned b
)
46 void zlib_free(void* dummy
, void* addr
)
51 int size_to_bits(unsigned v
)
53 if(v
> 256) return 16;
60 size_t buffer_stride(size_t width
, bool has_pal
, bool has_trans
, size_t psize
)
63 return 1 + width
* (has_trans
? 4 : 3);
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
++)
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
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
)
137 uint32_t crc
= crc32(0, NULL
, 0);
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
);
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
);
158 std::vector
<char> stream
;
164 //=========================================================
165 //=================== AUTORELEASE =========================
166 //=========================================================
167 template <typename T
>
171 autorelease(T
& _obj
) : obj(_obj
) {}
172 ~autorelease() { delete &obj
; }
173 T
* operator->() { return &obj
; }
174 T
& operator*() { return obj
; }
177 autorelease(const autorelease
&);
178 const autorelease
& operator=(const autorelease
&);
181 //=========================================================
182 //=================== PNG DECHUNKER =======================
183 //=========================================================
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
);
192 bool eof() { return eof_flag
; }
193 bool chunk_eof() { return ptr
== size
; }
195 std::istream
& stream
;
205 png_dechunker::png_dechunker(std::istream
& _stream
)
212 uint8_t magic
[8] = {137, 80, 78, 71, 13, 10, 26, 10};
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
));
224 stream
.read(reinterpret_cast<char*>(buf
), limit
);
226 throw std::runtime_error("PNG file truncated");
227 crc
= crc32(crc
, buf
, limit
);
234 bool png_dechunker::next_chunk()
240 chunk_read(buf
, 256);
246 void png_dechunker::load_chunk()
249 stream
.read(reinterpret_cast<char*>(buf
), 8);
251 if(stream
.gcount() == 0) {
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);
270 void png_dechunker::verify_crc()
275 throw std::runtime_error("PNG file truncated");
276 uint32_t claim_crc
= read32ube(buf
);
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;
287 if(t1
< 'A' || t2
< 'A' || t3
< 'A' || t4
< 'A')
289 if(t1
> 'z' || t2
> 'z' || t3
> 'z' || t4
> 'z')
291 return (stringfmt() << t1
<< t2
<< t3
<< t4
).str();
293 return (stringfmt() << "0x" << std::hex
<< std::setw(8) << std::setfill('0') << type
).str();
296 //=========================================================
297 //=================== PNG IHDR CHUNK ======================
298 //=========================================================
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");
308 d
.chunk_read(buf
, 13);
309 width
= read32ube(buf
+ 0);
310 height
= read32ube(buf
+ 4);
313 compression
= buf
[10];
326 //=========================================================
327 //=================== PNG DECOMPRESSOR ====================
328 //=========================================================
329 class png_decompressor
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
);
337 png_dechunker
& dechunk
;
339 png_decompressor(const png_decompressor
&);
340 png_decompressor
& operator=(const png_decompressor
&);
343 class png_decompressor_zlib
: public png_decompressor
346 png_decompressor_zlib(png_dechunker
& dechunk
)
347 : png_decompressor(dechunk
)
349 memset(&z
, 0, sizeof(z
));
350 z
.zalloc
= zlib_alloc
;
352 throw_zlib_error(inflateInit(&z
));
356 ~png_decompressor_zlib()
360 void decompress(uint8_t*& out
, size_t& outsize
)
364 buflen
= dechunk
.chunk_read(buf
, sizeof(buf
));
367 z
.next_in
= buf
+ bufptr
;
370 z
.avail_out
= outsize
;
371 int r
= inflate(&z
, Z_SYNC_FLUSH
);
372 if(r
== Z_BUF_ERROR
|| r
== Z_STREAM_END
) {
374 outsize
= z
.avail_out
;
375 bufptr
= z
.next_in
- buf
;
381 outsize
= z
.avail_out
;
382 bufptr
= z
.next_in
- buf
;
388 png_decompressor_zlib(const png_decompressor_zlib
&);
389 png_decompressor_zlib
& operator=(const png_decompressor_zlib
&);
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 //=========================================================
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
);
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
,
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
;
433 png_decompressor
& decomp
;
436 virtual void adjusted(size_t _pitch
, size_t _elements
) = 0;
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
)
447 inline uint8_t predict_left(uint8_t left
, uint8_t up
, uint8_t upleft
)
452 inline uint8_t predict_up(uint8_t left
, uint8_t up
, uint8_t upleft
)
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
)
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
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);
493 tmpleft
= tmp
.size();
495 ~png_filterbank_0() {}
496 bool row(uint8_t* data
)
498 decomp
.decompress(tmp2
, tmpleft
);
501 uint8_t filter
= tmp
[0];
503 uint8_t* s
= &tmp
[1];
504 uint8_t* a
= &above
[0];
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");
514 tmpleft
= tmp
.size();
515 memcpy(a
, t
, pitch
* elements
);
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
);
527 tmpleft
= tmp
.size();
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
;
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
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
);
574 png_filterbank
& filter
;
575 std::vector
<uint8_t> tmp
;
578 return filter
.row(&tmp
[0]);
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
)
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
))
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
;
609 if(bits
== 8 && in
[0] == trans
[1] && in
[1] == trans
[3] && in
[2] == trans
[5])
611 if(bits
== 16 && !memcmp(in
, trans
, 6))
615 return ((uint32_t)in
[0] << 16) |
616 ((uint32_t)in
[1] << 8) |
620 return ((uint32_t)in
[0] << 16) |
621 ((uint32_t)in
[2] << 8) |
626 template<unsigned bits
>
627 inline uint32_t decode_type_3(const uint8_t* in
, unsigned bit
, const uint8_t* trans
)
630 case 1: return (*in
>> (7 - bit
)) & 1;
631 case 2: return (*in
>> (6 - bit
)) & 3;
632 case 4: return (*in
>> (4 - bit
)) & 15;
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
)
642 return ((uint32_t)in
[0] << 16) |
643 ((uint32_t)in
[0] << 8) |
645 ((uint32_t)in
[1] << 24);
647 return ((uint32_t)in
[0] << 16) |
648 ((uint32_t)in
[0] << 8) |
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
)
657 return ((uint32_t)in
[0] << 16) |
658 ((uint32_t)in
[1] << 8) |
660 ((uint32_t)in
[3] << 24);
662 return ((uint32_t)in
[0] << 16) |
663 ((uint32_t)in
[2] << 8) |
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
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
)
683 for(size_t i
= 0; i
< width
; i
++) {
684 output
[i
] = decodefn(&tmp
[off
], bit
, trans
);
693 png_pixel_decoder
& png_pixel_decoder::get(png_filterbank
& filter
, uint8_t type
, uint8_t depth
, size_t width
)
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");
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");
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");
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");
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
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
);
765 png_interlacing(const png_interlacing
&);
766 png_interlacing
& operator=(const png_interlacing
&);
769 class png_interlacing_progressive
: public png_interlacing
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
;
783 class png_interlacing_adam
: public png_interlacing
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
)
792 png_interlacing::pass_info p
;
794 p
.xmod
= p
.ymod
= (one
<< order
);
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);
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
,
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();
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");
841 uint8_t* _trans
= NULL
;
842 for(size_t pass
= 0; pass
< interlace
->passes(); pass
++) {
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
);
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)
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
++) {
862 dechunk
.chunk_read(buf
, 3);
863 npalette
[i
] = (npalette
[i
] & 0xFF000000U
) |
864 ((uint32_t)buf
[0] << 16) |
865 ((uint32_t)buf
[1] << 8) |
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
++) {
885 dechunk
.chunk_read(buf
, 1);
886 npalette
[i
] = (npalette
[i
] & 0x00FFFFFFU
) |
887 ((uint32_t)buf
[0] << 24);
892 case 0x49444154: //IDAT.
893 if(scanline
== resolution
.second
)
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
)] =
902 if(scanline
== resolution
.second
)
907 if((dechunk
.chunk_type() & 0x20000000U
) == 0)
908 throw std::runtime_error("Unknown critical chunk");
911 dechunk
.next_chunk();
916 while(dechunk
.next_chunk()) {
917 switch(dechunk
.chunk_type()) {
918 case 0x49454E44: //IEND.
920 case 0x504C5445: //PLTE
921 throw std::runtime_error("PLTE not allowed after image data");
922 case 0x49444154: //IDAT.
925 if((dechunk
.chunk_type() & 0x20000000U
) == 0)
926 throw std::runtime_error("Unknown critical chunk");
930 std::swap(data
, ndata
);
931 std::swap(palette
, npalette
);
932 has_palette
= (hdr
.type
== 3);
937 png_decoded_image::png_decoded_image(const std::string
& file
)
939 std::istream
& s
= open_file_relative(file
, "");
949 png_decoded_image::png_decoded_image(std::istream
& file
)
954 png_decoded_image::png_decoded_image()
961 png_encodedable_image::png_encodedable_image()
967 colorkey
= 0xFFFFFFFFU
;
970 void png_encodedable_image::encode(const std::string
& file
) const
972 std::ofstream
s(file
);
974 throw std::runtime_error("Can't open file to write PNG image to");
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
));
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
));
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());
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());
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.
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;
1039 write_row_rgba(&buf
[1], &data
[width
* i
], width
);
1041 write_row_rgb(&buf
[1], &data
[width
* i
], width
);
1042 idat_h
.write(&buf
[0], bufstride
);
1046 //Write the IEND and finish.
1047 boost::iostreams::stream
<png_chunk_output
> iend_h(file
, 0x49454E44);
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;
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] << "> ";
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