1 #include "png-decoder.hpp"
2 #include "serialization.hpp"
16 void throw_zlib_error(int x
)
19 case Z_NEED_DICT
: throw std::runtime_error("Dictionary needed");
20 case Z_ERRNO
: throw std::runtime_error(std::string("OS error: ") + strerror(errno
));
21 case Z_STREAM_ERROR
: throw std::runtime_error("Stream error");
22 case Z_DATA_ERROR
: throw std::runtime_error("Data error");
23 case Z_MEM_ERROR
: throw std::bad_alloc();
24 case Z_BUF_ERROR
: throw std::runtime_error("Buffer error");
25 case Z_VERSION_ERROR
: throw std::runtime_error("Version error");
29 default: throw std::runtime_error("Unknown error");
33 void* zlib_alloc(void* dummy
, unsigned a
, unsigned b
)
38 void zlib_free(void* dummy
, void* addr
)
43 //=========================================================
44 //=================== AUTORELEASE =========================
45 //=========================================================
50 autorelease(T
& _obj
) : obj(_obj
) {}
51 ~autorelease() { delete &obj
; }
52 T
* operator->() { return &obj
; }
53 T
& operator*() { return obj
; }
56 autorelease(const autorelease
&);
57 const autorelease
& operator=(const autorelease
&);
60 //=========================================================
61 //=================== PNG DECHUNKER =======================
62 //=========================================================
66 png_dechunker(std::istream
& _stream
);
67 uint32_t chunk_size() { return size
; }
68 uint32_t chunk_type() { return type
; }
69 uint32_t chunk_read(uint8_t* buf
, size_t limit
);
71 bool eof() { return eof_flag
; }
72 bool chunk_eof() { return ptr
== size
; }
84 png_dechunker::png_dechunker(std::istream
& _stream
)
91 uint8_t magic
[8] = {137, 80, 78, 71, 13, 10, 26, 10};
93 stream
.read(reinterpret_cast<char*>(magicbuf
), 8);
94 if(!stream
|| memcmp(magic
, magicbuf
, 8))
95 throw std::runtime_error("Not a PNG file");
98 uint32_t png_dechunker::chunk_read(uint8_t* buf
, size_t limit
)
100 limit
= min(limit
, static_cast<size_t>(size
- ptr
));
103 stream
.read(reinterpret_cast<char*>(buf
), limit
);
105 throw std::runtime_error("PNG file truncated");
106 crc
= crc32(crc
, buf
, limit
);
113 bool png_dechunker::next_chunk()
119 chunk_read(buf
, 256);
125 void png_dechunker::load_chunk()
128 stream
.read(reinterpret_cast<char*>(buf
), 8);
130 if(stream
.gcount() == 0) {
138 throw std::runtime_error("PNG file truncated");
140 size
= read32ube(buf
+ 0);
141 type
= read32ube(buf
+ 4);
142 crc
= crc32(0, NULL
, 0);
143 crc
= crc32(crc
, buf
+ 4, 4);
149 void png_dechunker::verify_crc()
154 throw std::runtime_error("PNG file truncated");
155 uint32_t claim_crc
= read32ube(buf
);
157 throw std::runtime_error("PNG file chunk CRC check failed");
160 std::string
format_type(uint32_t type
)
162 uint8_t t1
= type
>> 24;
163 uint8_t t2
= type
>> 16;
164 uint8_t t3
= type
>> 8;
166 if(t1
< 'A' || t2
< 'A' || t3
< 'A' || t4
< 'A')
168 if(t1
> 'z' || t2
> 'z' || t3
> 'z' || t4
> 'z')
170 return (stringfmt() << t1
<< t2
<< t3
<< t4
).str();
172 return (stringfmt() << "0x" << std::hex
<< std::setw(8) << std::setfill('0') << type
).str();
175 //=========================================================
176 //=================== PNG IHDR CHUNK ======================
177 //=========================================================
180 ihdr_chunk(png_dechunker
& d
)
182 if(d
.chunk_type() != 0x49484452)
183 throw std::runtime_error("Expected IHDR chunk");
184 if(d
.chunk_size() != 13)
185 throw std::runtime_error("Expected IHDR chunk to be 13 bytes");
187 d
.chunk_read(buf
, 13);
188 width
= read32ube(buf
+ 0);
189 height
= read32ube(buf
+ 4);
192 compression
= buf
[10];
205 //=========================================================
206 //=================== PNG DECOMPRESSOR ====================
207 //=========================================================
208 class png_decompressor
211 png_decompressor(png_dechunker
& _dechunk
) : dechunk(_dechunk
) {}
212 virtual ~png_decompressor() {}
213 virtual void decompress(uint8_t*& out
, size_t& outsize
) = 0;
214 static png_decompressor
& get(png_dechunker
& dechunk
, uint8_t compression
);
216 png_dechunker
& dechunk
;
218 png_decompressor(const png_decompressor
&);
219 png_decompressor
& operator=(const png_decompressor
&);
222 class png_decompressor_zlib
: public png_decompressor
225 png_decompressor_zlib(png_dechunker
& dechunk
)
226 : png_decompressor(dechunk
)
228 memset(&z
, 0, sizeof(z
));
229 z
.zalloc
= zlib_alloc
;
231 throw_zlib_error(inflateInit(&z
));
235 ~png_decompressor_zlib()
239 void decompress(uint8_t*& out
, size_t& outsize
)
243 buflen
= dechunk
.chunk_read(buf
, sizeof(buf
));
246 z
.next_in
= buf
+ bufptr
;
249 z
.avail_out
= outsize
;
250 int r
= inflate(&z
, Z_SYNC_FLUSH
);
251 if(r
== Z_BUF_ERROR
|| r
== Z_STREAM_END
) {
253 outsize
= z
.avail_out
;
254 bufptr
= z
.next_in
- buf
;
260 outsize
= z
.avail_out
;
261 bufptr
= z
.next_in
- buf
;
267 png_decompressor_zlib(const png_decompressor_zlib
&);
268 png_decompressor_zlib
& operator=(const png_decompressor_zlib
&);
274 png_decompressor
& png_decompressor::get(png_dechunker
& dechunk
, uint8_t compression
)
276 if(compression
== 0) return *new png_decompressor_zlib(dechunk
);
277 throw std::runtime_error("Unsupported compression method");
280 //=========================================================
281 //=================== PNG FILTERBANK ======================
282 //=========================================================
286 png_filterbank(png_decompressor
& _decomp
, size_t _pitch
, size_t _elements
)
287 : decomp(_decomp
), pitch(_pitch
), elements(_elements
)
290 virtual ~png_filterbank() {}
291 size_t outsize() { return pitch
* elements
; }
292 void adjust_row(uint8_t type
, uint8_t depth
, size_t width
)
294 size_t bits
= png_filterbank::get_bits(type
, depth
);
295 size_t n_pitch
= (bits
>= 8) ? (bits
>> 3) : 1;
296 size_t pfactor
= 8 / bits
;
297 size_t n_elements
= (bits
>= 8) ? width
: ((width
+ pfactor
- 1) / pfactor
);
298 adjusted(n_pitch
, n_elements
);
300 elements
= n_elements
;
302 virtual bool row(uint8_t* data
) = 0;
303 static png_filterbank
& get(png_decompressor
& _decomp
, uint8_t filterbank
, uint8_t type
, uint8_t depth
,
305 static size_t get_bits(uint8_t type
, uint8_t depth
)
307 size_t mul
[7] = {1, 0, 3, 1, 2, 0, 4};
308 if(type
> 6 || !mul
[type
]) throw std::runtime_error("Unrecognized color type");
309 return mul
[type
] * depth
;
312 png_decompressor
& decomp
;
315 virtual void adjusted(size_t _pitch
, size_t _elements
) = 0;
317 png_filterbank(const png_filterbank
&);
318 png_filterbank
& operator=(const png_filterbank
&);
321 inline uint8_t predict_none(uint8_t left
, uint8_t up
, uint8_t upleft
)
326 inline uint8_t predict_left(uint8_t left
, uint8_t up
, uint8_t upleft
)
331 inline uint8_t predict_up(uint8_t left
, uint8_t up
, uint8_t upleft
)
336 inline uint8_t predict_average(uint8_t left
, uint8_t up
, uint8_t upleft
)
338 return (left
>> 1) + (up
>> 1) + (left
& up
& 1);
341 inline uint8_t predict_paeth(uint8_t left
, uint8_t up
, uint8_t upleft
)
343 int16_t p
= (int16_t)up
+ left
- upleft
;
344 uint16_t pa
= (p
> left
) ? (p
- left
) : (left
- p
);
345 uint16_t pb
= (p
> up
) ? (p
- up
) : (up
- p
);
346 uint16_t pc
= (p
> upleft
) ? (p
- upleft
) : (upleft
- p
);
347 if(pa
<= pb
&& pa
<= pc
)
354 template<uint8_t(*predictor
)(uint8_t left
, uint8_t up
, uint8_t upleft
)> void do_filter_3(uint8_t* target
,
355 const uint8_t* row
, const uint8_t* above
, size_t pitch
, size_t length
)
357 for(size_t i
= 0; i
< pitch
; i
++)
358 target
[i
] = row
[i
] + predictor(0, above
[i
], 0);
359 for(size_t i
= pitch
; i
< length
; i
++)
360 target
[i
] = row
[i
] + predictor(target
[i
- pitch
], above
[i
], above
[i
- pitch
]);
363 class png_filterbank_0
: public png_filterbank
366 png_filterbank_0(png_decompressor
& decomp
, size_t pitch
, size_t elements
)
367 : png_filterbank(decomp
, pitch
, elements
)
369 above
.resize(pitch
* elements
);
370 tmp
.resize(pitch
* elements
+ 1);
372 tmpleft
= tmp
.size();
374 ~png_filterbank_0() {}
375 bool row(uint8_t* data
)
377 decomp
.decompress(tmp2
, tmpleft
);
380 uint8_t filter
= tmp
[0];
382 uint8_t* s
= &tmp
[1];
383 uint8_t* a
= &above
[0];
385 case 0: do_filter_3
<predict_none
>(t
, s
, a
, pitch
, pitch
* elements
); break;
386 case 1: do_filter_3
<predict_left
>(t
, s
, a
, pitch
, pitch
* elements
); break;
387 case 2: do_filter_3
<predict_up
>(t
, s
, a
, pitch
, pitch
* elements
); break;
388 case 3: do_filter_3
<predict_average
>(t
, s
, a
, pitch
, pitch
* elements
); break;
389 case 4: do_filter_3
<predict_paeth
>(t
, s
, a
, pitch
, pitch
* elements
); break;
390 default: throw std::runtime_error("Unknown filter for filter bank 0");
393 tmpleft
= tmp
.size();
394 memcpy(a
, t
, pitch
* elements
);
398 void adjusted(size_t _pitch
, size_t _elements
)
400 std::vector
<uint8_t> nabove
, ntmp
;
401 nabove
.resize(_pitch
* _elements
);
402 ntmp
.resize(_pitch
* _elements
+ 1);
403 std::swap(above
, nabove
);
404 std::swap(tmp
, ntmp
);
406 tmpleft
= tmp
.size();
409 png_filterbank_0(const png_filterbank_0
&);
410 png_filterbank_0
& operator=(const png_filterbank_0
&);
411 std::vector
<uint8_t> above
;
412 std::vector
<uint8_t> tmp
;
417 png_filterbank
& png_filterbank::get(png_decompressor
& decomp
, uint8_t filterbank
, uint8_t type
,
418 uint8_t depth
, size_t width
)
420 size_t bits
= png_filterbank::get_bits(type
, depth
);
421 size_t pitch
= (bits
>= 8) ? (bits
>> 3) : 1;
422 size_t elements
= (bits
>= 8) ? width
: (width
/ (8 / bits
));
423 if(filterbank
== 0) return *new png_filterbank_0(decomp
, pitch
, elements
);
424 throw std::runtime_error("Unknown scanline filter bank");
427 //=========================================================
428 //=================== PNG PIXEL DECODING ==================
429 //=========================================================
430 class png_pixel_decoder
433 png_pixel_decoder(png_filterbank
& _filter
, uint8_t _type
, uint8_t _depth
, size_t _width
)
434 : filter(_filter
), type(_type
), depth(_depth
), width(_width
)
436 tmp
.resize((png_filterbank::get_bits(type
, depth
) * width
+ 7) / 8);
438 virtual ~png_pixel_decoder() {}
439 virtual bool decode(uint32_t* output
, uint8_t* trans
) = 0;
440 static png_pixel_decoder
& get(png_filterbank
& _filter
, uint8_t type
, uint8_t depth
, size_t width
);
441 void adjust_row(size_t _width
)
443 std::vector
<uint8_t> ntmp
;
444 ntmp
.resize((png_filterbank::get_bits(type
, depth
) * _width
+ 7) / 8);
445 filter
.adjust_row(type
, depth
, _width
);
446 std::swap(tmp
, ntmp
);
453 png_filterbank
& filter
;
454 std::vector
<uint8_t> tmp
;
457 return filter
.row(&tmp
[0]);
460 png_pixel_decoder(const png_pixel_decoder
&);
461 png_pixel_decoder
& operator=(const png_pixel_decoder
&);
464 template<unsigned bits
>
465 inline uint32_t decode_type_0(const uint8_t* in
, unsigned bit
, const uint8_t* trans
)
471 case 1: v
= (*in
>> (7 - bit
)) & 1; m
= 0xFFFFFF; break;
472 case 2: v
= (*in
>> (6 - bit
)) & 3; m
= 0x555555; break;
473 case 4: v
= (*in
>> (4 - bit
)) & 15; m
= 0x111111; break;
474 case 8: v
= *in
; m
= 0xFFFFFF; m
= 0x010101; break;
475 case 16: v
= read16ube(in
); m
= 0x010101; s
= 8; break;
477 uint32_t alpha
= 0xFF000000U
;
478 if(v
== read16ube(trans
))
480 return alpha
| (m
* (v
>> s
));
483 template<unsigned bits
>
484 inline uint32_t decode_type_2(const uint8_t* in
, unsigned bit
, const uint8_t* trans
)
486 uint32_t alpha
= 0xFF000000U
;
488 if(bits
== 8 && in
[0] == trans
[1] && in
[1] == trans
[3] && in
[2] == trans
[5])
490 if(bits
== 16 && !memcmp(in
, trans
, 6))
494 return ((uint32_t)in
[0] << 16) |
495 ((uint32_t)in
[1] << 8) |
499 return ((uint32_t)in
[0] << 16) |
500 ((uint32_t)in
[2] << 8) |
505 template<unsigned bits
>
506 inline uint32_t decode_type_3(const uint8_t* in
, unsigned bit
, const uint8_t* trans
)
509 case 1: return (*in
>> (7 - bit
)) & 1;
510 case 2: return (*in
>> (6 - bit
)) & 3;
511 case 4: return (*in
>> (4 - bit
)) & 15;
513 case 16: return read16ube(in
);
517 template<unsigned bits
>
518 uint32_t decode_type_4(const uint8_t* in
, unsigned bit
, const uint8_t* trans
)
521 return ((uint32_t)in
[0] << 16) |
522 ((uint32_t)in
[0] << 8) |
524 ((uint32_t)in
[1] << 24);
526 return ((uint32_t)in
[0] << 16) |
527 ((uint32_t)in
[0] << 8) |
529 ((uint32_t)in
[2] << 24);
532 template<unsigned bits
>
533 uint32_t decode_type_6(const uint8_t* in
, unsigned bit
, const uint8_t* trans
)
536 return ((uint32_t)in
[0] << 16) |
537 ((uint32_t)in
[1] << 8) |
539 ((uint32_t)in
[3] << 24);
541 return ((uint32_t)in
[0] << 16) |
542 ((uint32_t)in
[2] << 8) |
544 ((uint32_t)in
[6] << 24);
547 template<size_t bits
, uint32_t (*decodefn
)(const uint8_t* in
, unsigned bit
, const uint8_t* trans
)>
548 class png_pixel_decoder_fn
: public png_pixel_decoder
551 png_pixel_decoder_fn(png_filterbank
& _filter
, uint8_t _type
, uint8_t _depth
, size_t _width
)
552 : png_pixel_decoder(_filter
, _type
, _depth
, _width
)
555 ~png_pixel_decoder_fn() {}
556 bool decode(uint32_t* output
, uint8_t* trans
)
562 for(size_t i
= 0; i
< width
; i
++) {
563 output
[i
] = decodefn(&tmp
[off
], bit
, trans
);
572 png_pixel_decoder
& png_pixel_decoder::get(png_filterbank
& filter
, uint8_t type
, uint8_t depth
, size_t width
)
577 case 1: return *new png_pixel_decoder_fn
<1, decode_type_0
<1>>(filter
, type
, depth
, width
);
578 case 2: return *new png_pixel_decoder_fn
<2, decode_type_0
<2>>(filter
, type
, depth
, width
);
579 case 4: return *new png_pixel_decoder_fn
<4, decode_type_0
<4>>(filter
, type
, depth
, width
);
580 case 8: return *new png_pixel_decoder_fn
<8, decode_type_0
<8>>(filter
, type
, depth
, width
);
581 case 16: return *new png_pixel_decoder_fn
<16, decode_type_0
<16>>(filter
, type
, depth
, width
);
582 default: throw std::runtime_error("Unsupported color depth for type 0");
586 case 8: return *new png_pixel_decoder_fn
<24, decode_type_2
<8>>(filter
, type
, depth
, width
);
587 case 16: return *new png_pixel_decoder_fn
<48, decode_type_2
<16>>(filter
, type
, depth
, width
);
588 default: throw std::runtime_error("Unsupported color depth for type 2");
592 case 1: return *new png_pixel_decoder_fn
<1, decode_type_3
<1>>(filter
, type
, depth
, width
);
593 case 2: return *new png_pixel_decoder_fn
<2, decode_type_3
<2>>(filter
, type
, depth
, width
);
594 case 4: return *new png_pixel_decoder_fn
<4, decode_type_3
<4>>(filter
, type
, depth
, width
);
595 case 8: return *new png_pixel_decoder_fn
<8, decode_type_3
<8>>(filter
, type
, depth
, width
);
596 case 16: return *new png_pixel_decoder_fn
<16, decode_type_3
<16>>(filter
, type
, depth
, width
);
597 default: throw std::runtime_error("Unsupported color depth for type 3");
601 case 8: return *new png_pixel_decoder_fn
<16, decode_type_4
<8>>(filter
, type
, depth
, width
);
602 case 16: return *new png_pixel_decoder_fn
<32, decode_type_4
<16>>(filter
, type
, depth
, width
);
603 default: throw std::runtime_error("Unsupported color depth for type 4");
607 case 8: return *new png_pixel_decoder_fn
<32, decode_type_6
<8>>(filter
, type
, depth
, width
);
608 case 16: return *new png_pixel_decoder_fn
<64, decode_type_6
<16>>(filter
, type
, depth
, width
);
609 default: throw std::runtime_error("Unsupported color depth for type 6");
611 default: throw std::runtime_error("Unsupported color type");
615 //=========================================================
616 //=================== PNG INTERLACING =====================
617 //=========================================================
618 class png_interlacing
631 virtual ~png_interlacing() {}
632 virtual size_t passes() = 0;
633 virtual pass_info
pass(size_t n
) = 0;
634 static std::pair
<size_t, size_t> pass_size(size_t iwidth
, size_t iheight
, pass_info pinfo
)
636 size_t width
= (iwidth
+ (pinfo
.xmod
- pinfo
.xoff
- 1)) / pinfo
.xmod
;
637 size_t height
= (iheight
+ (pinfo
.ymod
- pinfo
.yoff
- 1)) / pinfo
.ymod
;
638 if(!width
|| !height
)
639 return std::make_pair(0, 0);
640 return std::make_pair(width
, height
);
642 static png_interlacing
& get(uint8_t interlace
);
644 png_interlacing(const png_interlacing
&);
645 png_interlacing
& operator=(const png_interlacing
&);
648 class png_interlacing_progressive
: public png_interlacing
651 ~png_interlacing_progressive() {}
652 size_t passes() { return 1; }
653 png_interlacing::pass_info
pass(size_t n
)
655 png_interlacing::pass_info p
;
662 class png_interlacing_adam
: public png_interlacing
665 png_interlacing_adam(unsigned _order
) : order(_order
) {}
666 ~png_interlacing_adam() {}
667 size_t passes() { return 2 * order
+ 1; }
668 png_interlacing::pass_info
pass(size_t n
)
671 png_interlacing::pass_info p
;
673 p
.xmod
= p
.ymod
= (one
<< order
);
676 p
.xoff
= (n
% 2) ? (one
<< (order
- (n
+ 1) / 2)) : 0;
677 p
.yoff
= (n
% 2) ? 0 : (one
<< (order
- n
/ 2));
678 p
.xmod
= one
<< (order
- n
/ 2);
679 p
.ymod
= one
<< (order
- (n
- 1) / 2);
687 png_interlacing
& png_interlacing::get(uint8_t interlace
)
689 if(interlace
== 0) return *new png_interlacing_progressive();
690 if(interlace
== 1) return *new png_interlacing_adam(3);
691 throw std::runtime_error("Unknown interlace type");
695 void decode_png(std::istream
& stream
, png_decoded_image
& out
)
697 png_dechunker
dechunk(stream
);
698 if(!dechunk
.next_chunk())
699 throw std::runtime_error("PNG file has no chunks");
700 ihdr_chunk
hdr(dechunk
);
701 autorelease
<png_decompressor
> idat_decomp(png_decompressor::get(dechunk
, hdr
.compression
));
702 autorelease
<png_filterbank
> filterbank(png_filterbank::get(*idat_decomp
, hdr
.filter
, hdr
.type
,
703 hdr
.depth
, hdr
.width
));
704 autorelease
<png_pixel_decoder
> pixdecoder(png_pixel_decoder::get(*filterbank
, hdr
.type
, hdr
.depth
,
706 autorelease
<png_interlacing
> interlace(png_interlacing::get(hdr
.interlace
));
707 std::vector
<uint32_t> ndata
;
708 std::vector
<uint32_t> npalette
;
709 ndata
.resize(hdr
.width
* hdr
.height
);
710 if(ndata
.size() / hdr
.width
!= hdr
.height
)
711 throw std::bad_alloc();
713 npalette
.resize(1 << hdr
.depth
);
714 for(size_t i
= 0; i
< npalette
.size(); i
++)
715 npalette
[i
] = 0xFF000000U
;
717 if(!dechunk
.next_chunk())
718 throw std::runtime_error("PNG file has no chunks besides header");
720 uint8_t* _trans
= NULL
;
721 for(size_t pass
= 0; pass
< interlace
->passes(); pass
++) {
723 png_interlacing::pass_info pinfo
= interlace
->pass(pass
);
724 auto resolution
= png_interlacing::pass_size(hdr
.width
, hdr
.height
, pinfo
);
725 pixdecoder
->adjust_row(resolution
.first
);
726 std::vector
<uint32_t> scanlineb
;
727 scanlineb
.resize(resolution
.first
);
729 switch(dechunk
.chunk_type()) {
730 case 0x49454E44: //IEND.
731 throw std::runtime_error("Unexpected IEND chunk");
732 case 0x504C5445: //PLTE.
733 if(hdr
.type
== 0 || hdr
.type
== 4)
734 throw std::runtime_error("Illegal PLTE in types 0/4");
735 if(hdr
.type
== 2 || hdr
.type
== 6)
737 if(dechunk
.chunk_size() > 3 * npalette
.size())
738 throw std::runtime_error("PLTE too large");
739 for(size_t i
= 0; i
< dechunk
.chunk_size() / 3; i
++) {
741 dechunk
.chunk_read(buf
, 3);
742 npalette
[i
] = (npalette
[i
] & 0xFF000000U
) |
743 ((uint32_t)buf
[0] << 16) |
744 ((uint32_t)buf
[1] << 8) |
748 case 0x74524E53: //tRNS.
749 if(hdr
.type
== 4 || hdr
.type
== 6)
750 throw std::runtime_error("Illegal tRNS in types 4/6");
751 else if(hdr
.type
== 0) {
752 if(dechunk
.chunk_size() != 2)
753 throw std::runtime_error("Expected 2-byte tRNS for type0");
754 dechunk
.chunk_read(trans
, 2);
755 } else if(hdr
.type
== 2) {
756 if(dechunk
.chunk_size() != 6)
757 throw std::runtime_error("Expected 6-byte tRNS for type2");
758 dechunk
.chunk_read(trans
, 6);
759 } else if(hdr
.type
== 3) {
760 if(dechunk
.chunk_size() > npalette
.size())
761 throw std::runtime_error("tRNS too large");
762 for(size_t i
= 0; i
< dechunk
.chunk_size(); i
++) {
764 dechunk
.chunk_read(buf
, 1);
765 npalette
[i
] = (npalette
[i
] & 0x00FFFFFFU
) |
766 ((uint32_t)buf
[0] << 24);
771 case 0x49444154: //IDAT.
772 if(scanline
== resolution
.second
)
774 while(pixdecoder
->decode(&scanlineb
[0], _trans
)) {
775 size_t rline
= scanline
* pinfo
.ymod
+ pinfo
.yoff
;
776 for(size_t i
= 0; i
< resolution
.first
; i
++) {
777 ndata
[rline
* hdr
.width
+ (i
* pinfo
.xmod
+ pinfo
.xoff
)] =
781 if(scanline
== resolution
.second
)
786 if((dechunk
.chunk_type() & 0x20000000U
) == 0)
787 throw std::runtime_error("Unknown critical chunk");
790 dechunk
.next_chunk();
795 while(dechunk
.next_chunk()) {
796 switch(dechunk
.chunk_type()) {
797 case 0x49454E44: //IEND.
799 case 0x504C5445: //PLTE
800 throw std::runtime_error("PLTE not allowed after image data");
801 case 0x49444154: //IDAT.
804 if((dechunk
.chunk_type() & 0x20000000U
) == 0)
805 throw std::runtime_error("Unknown critical chunk");
809 std::swap(out
.data
, ndata
);
810 std::swap(out
.palette
, npalette
);
811 out
.has_palette
= (hdr
.type
== 3);
812 out
.width
= hdr
.width
;
813 out
.height
= hdr
.height
;
816 void decode_png(const std::string
& file
, png_decoded_image
& out
)
818 std::istream
& s
= open_file_relative(file
, "");
828 int main(int argc, char** argv)
830 png_decoded_image img;
831 decode_png(argv[1], img);
832 std::cout << "Size: " << img.width << "*" << img.height << std::endl;
834 std::cout << "Image is paletted, " << img.palette.size() << " colors." << std::endl;
835 if(img.has_palette) {
836 for(size_t i = 0; i < img.data.size(); i++) {
837 if(i > 0 && i % img.width == 0)
838 std::cout << std::endl;
839 std::cout << std::hex << std::setw(8) << std::setfill('0') << img.palette[img.data[i]]
840 << "<" << std::hex << std::setw(4) << std::setfill('0') << img.data[i] << "> ";
843 for(size_t i = 0; i < img.data.size(); i++) {
844 if(i > 0 && i % img.width == 0)
845 std::cout << std::endl;
846 std::cout << std::hex << std::setw(8) << std::setfill('0') << img.data[i] << " ";
849 std::cout << std::endl;
851 //evaluate-lua b,p=gui.bitmap_load_png("/tmp/tbgn2c16.png"); on_paint = function() gui.bitmap_draw(0,0,b,p); end