2 #include "serialization.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>
27 void throw_zlib_error(int 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");
40 default: throw std::runtime_error("Unknown error");
44 void* zlib_alloc(void* dummy
, unsigned a
, unsigned b
)
49 void zlib_free(void* dummy
, void* addr
)
54 int size_to_bits(unsigned v
)
56 if(v
> 256) return 16;
63 size_t buffer_stride(size_t width
, bool has_pal
, bool has_trans
, size_t psize
)
66 return 1 + width
* (has_trans
? 4 : 3);
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
++)
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
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
)
140 uint32_t crc
= crc32(0, NULL
, 0);
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
);
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
);
161 std::vector
<char> stream
;
167 //=========================================================
168 //=================== AUTORELEASE =========================
169 //=========================================================
170 template <typename T
>
174 autorelease(T
& _obj
) : obj(_obj
) {}
175 ~autorelease() { delete &obj
; }
176 T
* operator->() { return &obj
; }
177 T
& operator*() { return obj
; }
180 autorelease(const autorelease
&);
181 const autorelease
& operator=(const autorelease
&);
184 //=========================================================
185 //=================== PNG DECHUNKER =======================
186 //=========================================================
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
);
195 bool eof() { return eof_flag
; }
196 bool chunk_eof() { return ptr
== size
; }
198 std::istream
& stream
;
208 png_dechunker::png_dechunker(std::istream
& _stream
)
215 uint8_t magic
[8] = {137, 80, 78, 71, 13, 10, 26, 10};
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
));
227 stream
.read(reinterpret_cast<char*>(buf
), limit
);
229 throw std::runtime_error("PNG file truncated");
230 crc
= crc32(crc
, buf
, limit
);
237 bool png_dechunker::next_chunk()
243 chunk_read(buf
, 256);
249 void png_dechunker::load_chunk()
252 stream
.read(reinterpret_cast<char*>(buf
), 8);
254 if(stream
.gcount() == 0) {
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);
273 void png_dechunker::verify_crc()
278 throw std::runtime_error("PNG file truncated");
279 uint32_t claim_crc
= serialization::u32b(buf
);
281 throw std::runtime_error("PNG file chunk CRC check failed");
284 //=========================================================
285 //=================== PNG IHDR CHUNK ======================
286 //=========================================================
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");
296 d
.chunk_read(buf
, 13);
297 width
= serialization::u32b(buf
+ 0);
298 height
= serialization::u32b(buf
+ 4);
301 compression
= buf
[10];
314 //=========================================================
315 //=================== PNG DECOMPRESSOR ====================
316 //=========================================================
317 class png_decompressor
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
);
325 png_dechunker
& dechunk
;
327 png_decompressor(const png_decompressor
&);
328 png_decompressor
& operator=(const png_decompressor
&);
331 class png_decompressor_zlib
: public png_decompressor
334 png_decompressor_zlib(png_dechunker
& dechunk
)
335 : png_decompressor(dechunk
)
337 memset(&z
, 0, sizeof(z
));
338 z
.zalloc
= zlib_alloc
;
340 throw_zlib_error(inflateInit(&z
));
344 ~png_decompressor_zlib()
348 void decompress(uint8_t*& out
, size_t& outsize
)
352 buflen
= dechunk
.chunk_read(buf
, sizeof(buf
));
355 z
.next_in
= buf
+ bufptr
;
358 z
.avail_out
= outsize
;
359 int r
= inflate(&z
, Z_SYNC_FLUSH
);
360 if(r
== Z_BUF_ERROR
|| r
== Z_STREAM_END
) {
362 outsize
= z
.avail_out
;
363 bufptr
= z
.next_in
- buf
;
369 outsize
= z
.avail_out
;
370 bufptr
= z
.next_in
- buf
;
376 png_decompressor_zlib(const png_decompressor_zlib
&);
377 png_decompressor_zlib
& operator=(const png_decompressor_zlib
&);
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 //=========================================================
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
);
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
,
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
;
421 png_decompressor
& decomp
;
424 virtual void adjusted(size_t _pitch
, size_t _elements
) = 0;
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
)
435 inline uint8_t predict_left(uint8_t left
, uint8_t up
, uint8_t upleft
)
440 inline uint8_t predict_up(uint8_t left
, uint8_t up
, uint8_t upleft
)
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
)
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
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);
481 tmpleft
= tmp
.size();
483 ~png_filterbank_0() {}
484 bool row(uint8_t* data
)
486 decomp
.decompress(tmp2
, tmpleft
);
489 uint8_t filter
= tmp
[0];
491 uint8_t* s
= &tmp
[1];
492 uint8_t* a
= &above
[0];
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");
502 tmpleft
= tmp
.size();
503 memcpy(a
, t
, pitch
* elements
);
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
);
515 tmpleft
= tmp
.size();
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
;
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
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
);
562 png_filterbank
& filter
;
563 std::vector
<uint8_t> tmp
;
566 return filter
.row(&tmp
[0]);
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
)
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
))
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
;
597 if(bits
== 8 && in
[0] == trans
[1] && in
[1] == trans
[3] && in
[2] == trans
[5])
599 if(bits
== 16 && !memcmp(in
, trans
, 6))
603 return ((uint32_t)in
[0] << 16) |
604 ((uint32_t)in
[1] << 8) |
608 return ((uint32_t)in
[0] << 16) |
609 ((uint32_t)in
[2] << 8) |
614 template<unsigned bits
>
615 inline uint32_t decode_type_3(const uint8_t* in
, unsigned bit
, const uint8_t* trans
)
618 case 1: return (*in
>> (7 - bit
)) & 1;
619 case 2: return (*in
>> (6 - bit
)) & 3;
620 case 4: return (*in
>> (4 - bit
)) & 15;
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
)
630 return ((uint32_t)in
[0] << 16) |
631 ((uint32_t)in
[0] << 8) |
633 ((uint32_t)in
[1] << 24);
635 return ((uint32_t)in
[0] << 16) |
636 ((uint32_t)in
[0] << 8) |
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
)
645 return ((uint32_t)in
[0] << 16) |
646 ((uint32_t)in
[1] << 8) |
648 ((uint32_t)in
[3] << 24);
650 return ((uint32_t)in
[0] << 16) |
651 ((uint32_t)in
[2] << 8) |
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
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
)
671 for(size_t i
= 0; i
< width
; i
++) {
672 output
[i
] = decodefn(&tmp
[off
], bit
, trans
);
681 png_pixel_decoder
& png_pixel_decoder::get(png_filterbank
& filter
, uint8_t type
, uint8_t depth
, size_t width
)
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");
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");
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");
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");
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
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
);
753 png_interlacing(const png_interlacing
&);
754 png_interlacing
& operator=(const png_interlacing
&);
757 class png_interlacing_progressive
: public png_interlacing
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
;
771 class png_interlacing_adam
: public png_interlacing
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
)
780 png_interlacing::pass_info p
;
782 p
.xmod
= p
.ymod
= (one
<< order
);
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);
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
,
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();
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");
829 uint8_t* _trans
= NULL
;
830 for(size_t pass
= 0; pass
< interlace
->passes(); pass
++) {
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
);
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)
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
++) {
850 dechunk
.chunk_read(buf
, 3);
851 npalette
[i
] = (npalette
[i
] & 0xFF000000U
) |
852 ((uint32_t)buf
[0] << 16) |
853 ((uint32_t)buf
[1] << 8) |
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
++) {
873 dechunk
.chunk_read(buf
, 1);
874 npalette
[i
] = (npalette
[i
] & 0x00FFFFFFU
) |
875 ((uint32_t)buf
[0] << 24);
880 case 0x49444154: //IDAT.
881 if(scanline
== resolution
.second
)
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
)] =
890 if(scanline
== resolution
.second
)
895 if((dechunk
.chunk_type() & 0x20000000U
) == 0)
896 throw std::runtime_error("Unknown critical chunk");
899 dechunk
.next_chunk();
904 while(dechunk
.next_chunk()) {
905 switch(dechunk
.chunk_type()) {
906 case 0x49454E44: //IEND.
908 case 0x504C5445: //PLTE
909 throw std::runtime_error("PLTE not allowed after image data");
910 case 0x49444154: //IDAT.
913 if((dechunk
.chunk_type() & 0x20000000U
) == 0)
914 throw std::runtime_error("Unknown critical chunk");
918 std::swap(data
, ndata
);
919 std::swap(palette
, npalette
);
920 has_palette
= (hdr
.type
== 3);
925 decoder::decoder(const std::string
& file
)
927 std::istream
& s
= zip::openrel(file
, "");
937 decoder::decoder(std::istream
& file
)
955 colorkey
= 0xFFFFFFFFU
;
958 void encoder::encode(const std::string
& file
) const
960 std::ofstream
s(file
, std::ios::binary
);
962 throw std::runtime_error("Can't open file to write PNG image to");
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
));
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
));
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());
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());
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.
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;
1027 write_row_rgba(&buf
[1], &data
[width
* i
], width
);
1029 write_row_rgb(&buf
[1], &data
[width
* i
], width
);
1030 idat_h
.write(&buf
[0], bufstride
);
1034 //Write the IEND and finish.
1035 boost::iostreams::stream
<png_chunk_output
> iend_h(file
, 0x49454E44);
1038 throw std::runtime_error("Can't write target PNG file");
1042 int main(int argc, char** argv)
1045 decode_png(argv[1], img);
1046 std::cout << "Size: " << img.width << "*" << img.height << std::endl;
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]) << "> ";
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