2 #include "parseval.hpp"
17 const unsigned stype_1bit
= 0;
18 const unsigned stype_2bit
= 1;
19 const unsigned stype_4bit
= 2;
20 const unsigned stype_8bit
= 3;
21 const unsigned stype_mask
= 3;
22 const unsigned pstride_s
= 0;
23 const unsigned pstride_2
= 4;
24 const unsigned pstride_3
= 8;
25 const unsigned pstride_4
= 12;
26 const unsigned pstride_mask
= 12;
27 const unsigned nopalette
= 0;
28 const unsigned paletted
= 16;
29 const unsigned greyscale_trans
= 32;
30 const unsigned rgb_trans
= 64;
32 size_t bulk_read(std::istream
& stream
, void* buffer
, size_t size
)
35 char* tmp
= reinterpret_cast<char*>(buffer
);
40 stream
.read(tmp
, bsize
);
41 size_t r
= stream
.gcount();
51 void load_file(std::vector
<uint8_t>& data
, std::istream
& in
, const char* edata
, size_t edatasize
)
53 data
.resize(edatasize
);
54 for(size_t i
= 0; i
< edatasize
; i
++)
57 size_t pos
= data
.size();
58 data
.resize(pos
+ BLOCK
);
59 size_t r
= bulk_read(in
, &data
[pos
], BLOCK
);
67 uint32_t read32(const uint8_t* p
)
69 return ((uint32_t)p
[0] << 24) | ((uint32_t)p
[1] << 16) | ((uint32_t)p
[2] << 8) | (uint32_t)p
[3];
72 void* zlib_alloc(void* dummy
, uInt s1
, uInt s2
)
74 return malloc(static_cast<size_t>(s1
) * s2
);
77 void zlib_free(void* dummy
, void* addr
)
82 int throw_zlib_error(int err
)
86 throw std::runtime_error(std::string("OS error: ") + strerror(_err
));
88 if(err
== Z_STREAM_ERROR
)
89 throw std::runtime_error("Stream error");
90 if(err
== Z_DATA_ERROR
)
91 throw std::runtime_error("Compressed data corrupt");
92 if(err
== Z_MEM_ERROR
)
93 throw std::bad_alloc();
94 if(err
== Z_BUF_ERROR
)
95 throw std::runtime_error("Buffer error");
96 if(err
== Z_VERSION_ERROR
)
97 throw std::runtime_error("Zlib version incompatiblity");
100 x
<< "Unknown zlib error #" << -err
<< std::endl
;
101 throw std::runtime_error(x
.str());
108 png_header(const uint8_t* buffer
, size_t bufsize
, size_t& nextptr
);
113 png_chunk(const uint8_t* buffer
, size_t bufsize
, size_t chunkptr
, size_t& nextptr
);
119 class png_idat_decoder
122 png_idat_decoder(std::vector
<png_chunk
>& _chunks
);
124 size_t decode(uint8_t* buffer
, size_t maxbytes
);
126 png_idat_decoder(png_idat_decoder
&);
127 png_idat_decoder
& operator=(png_idat_decoder
&);
128 std::vector
<png_chunk
>& chunks
;
131 size_t block_pointer
;
136 png_header::png_header(const uint8_t* buffer
, size_t bufsize
, size_t& nextptr
)
138 if(bufsize
< 8 || memcmp(buffer
, "\x89PNG\r\n\x1A\n", 8))
139 throw std::runtime_error("Wrong PNG magic");
143 png_chunk::png_chunk(const uint8_t* buffer
, size_t bufsize
, size_t chunkptr
, size_t& nextptr
)
145 if(chunkptr
> bufsize
- 12)
146 throw std::runtime_error("Incomplete PNG chunk");
147 size
= read32(buffer
+ chunkptr
);
148 if(chunkptr
> bufsize
- 12 - size
)
149 throw std::runtime_error("Incomplete PNG chunk");
150 uint32_t alleged_crc
= read32(buffer
+ chunkptr
+ size
+ 8);
151 uint32_t actual_crc
= crc32(crc32(0, NULL
, 0), buffer
+ chunkptr
+ 4, size
+ 4);
152 if(alleged_crc
!= actual_crc
)
153 throw std::runtime_error("PNG chunk CRC mismatch");
154 data
= buffer
+ chunkptr
+ 8;
155 type
= read32(buffer
+ chunkptr
+ 4);
156 nextptr
= chunkptr
+ size
+ 12;
159 png_idat_decoder::png_idat_decoder(std::vector
<png_chunk
>& _chunks
)
162 memset(&inflater
, 0, sizeof(inflater
));
163 inflater
.zalloc
= zlib_alloc
;
164 inflater
.zfree
= zlib_free
;
165 throw_zlib_error(inflateInit(&inflater
));
168 while(block_num
< chunks
.size() && chunks
[block_num
].type
!= 0x49444154)
170 if(block_num
== chunks
.size())
171 throw std::runtime_error("PNG file has no IDAT chunks");
176 png_idat_decoder::~png_idat_decoder()
178 inflateEnd(&inflater
);
181 size_t png_idat_decoder::decode(uint8_t* buffer
, size_t maxbytes
)
186 while(maxbytes
> 0) {
188 if(block_num
< chunks
.size()) {
189 inflater
.next_in
= const_cast<uint8_t*>(chunks
[block_num
].data
+ block_pointer
);
190 inflater
.avail_in
= chunks
[block_num
].size
- block_pointer
;
192 inflater
.next_in
= &dummy
;
193 inflater
.avail_in
= 0;
195 if(inflater
.avail_in
< chunks
[block_num
].size
- block_pointer
) {
197 inflater
.avail_in
= 0;
200 inflater
.next_out
= buffer
;
201 inflater
.avail_out
= maxbytes
;
202 if(inflater
.avail_out
< maxbytes
) {
204 inflater
.avail_out
= 0;
205 inflater
.avail_out
--;
207 size_t orig_avail_in
= inflater
.avail_in
;
208 size_t orig_avail_out
= inflater
.avail_out
;
209 int c
= throw_zlib_error(inflate(&inflater
, Z_SYNC_FLUSH
));
211 throw std::runtime_error("PNG decoding needs a dictionary?");
212 block_pointer
+= (orig_avail_in
- inflater
.avail_in
);
213 buffer
+= (orig_avail_out
- inflater
.avail_out
);
214 done
+= (orig_avail_out
- inflater
.avail_out
);
215 maxbytes
-= (orig_avail_out
- inflater
.avail_out
);
216 if(c
== Z_STREAM_END
) {
220 if(block_num
== chunks
.size() && inflater
.avail_out
&& c
== Z_OK
)
221 throw std::runtime_error("PNG compressed data underrun");
222 if(block_num
< chunks
.size() && block_pointer
== chunks
[block_num
].size
) {
225 } while(block_num
< chunks
.size() && chunks
[block_num
].type
!= 0x49444154);
232 void load_palette(uint32_t* palette
, std::vector
<png_chunk
>& chunks
)
235 for(auto i
: chunks
) {
236 if(i
.type
== 0x504C5445) {
237 unsigned colors
= i
.size
/ 3;
238 for(unsigned j
= 0; j
< colors
; j
++) {
239 palette
[j
] = static_cast<uint32_t>(i
.data
[3 * j
+ 0]);
240 palette
[j
] |= (static_cast<uint32_t>(i
.data
[3 * j
+ 1] << 8));
241 palette
[j
] |= (static_cast<uint32_t>(i
.data
[3 * j
+ 2] << 16));
242 palette
[j
] |= 0xFF000000U
;
244 for(unsigned j
= colors
; j
< 256; j
++)
245 palette
[j
] = 0xFF000000U
;
247 } else if(i
.type
== 0x74524E53) {
248 for(unsigned j
= 0; j
< i
.size
; j
++) {
249 palette
[j
] &= 0xFFFFFF;
250 palette
[j
] |= (static_cast<uint32_t>(i
.data
[j
] << 24));
255 throw std::runtime_error("PNG file is marked as indexed but has no palette");
258 void load_gtrans(uint32_t* palette
, std::vector
<png_chunk
>& chunks
, unsigned stype
)
260 palette
[0] = 65536; //Invalid.
261 for(auto i
: chunks
) {
262 if(i
.type
== 0x74524E53) {
264 throw std::runtime_error("tRNS chunk too short");
265 palette
[0] = static_cast<uint32_t>(i
.data
[1]);
270 void load_ctrans(uint32_t* palette
, std::vector
<png_chunk
>& chunks
)
272 palette
[0] = 0; //Invalid.
273 for(auto i
: chunks
) {
274 if(i
.type
== 0x74524E53) {
276 throw std::runtime_error("tRNS chunk too short");
277 palette
[0] = static_cast<uint32_t>(i
.data
[1]);
278 palette
[0] |= (static_cast<uint32_t>(i
.data
[3] << 8));
279 palette
[0] |= (static_cast<uint32_t>(i
.data
[5] << 16));
280 palette
[0] |= 0xFF000000U
;
285 void scanline_inverse_filter(uint8_t* data
, const uint8_t* last
, size_t bytes
, size_t psize
, uint8_t filter
)
290 for(unsigned i
= psize
; i
< bytes
; i
++)
291 data
[i
] += data
[i
- psize
];
293 for(unsigned i
= 0; i
< bytes
; i
++)
295 else if(filter
== 3) {
296 for(unsigned i
= 0; i
< psize
; i
++)
297 data
[i
] += last
[i
] / 2;
298 for(unsigned i
= psize
; i
< bytes
; i
++)
299 data
[i
] += ((uint16_t)last
[i
] + data
[i
- psize
]) / 2;
300 } else if(filter
== 4) {
301 for(unsigned i
= 0; i
< psize
; i
++)
303 for(unsigned i
= psize
; i
< bytes
; i
++) {
304 int16_t p
= (int16_t)last
[i
] + data
[i
- psize
] - last
[i
- psize
];
305 uint16_t pa
= abs(p
- data
[i
- psize
]);
306 uint16_t pb
= abs(p
- last
[i
]);
307 uint16_t pc
= abs(p
- last
[i
- psize
]);
308 if(pa
<= pb
&& pa
<= pc
)
309 data
[i
] += data
[i
- psize
];
313 data
[i
] += last
[i
- psize
];
316 throw std::runtime_error("Unsupported scanline filter");
319 template<unsigned stype
>
320 uint32_t grey_expand(uint8_t val
)
322 if(stype
== stype_1bit
) return (val
& 1) * 0xFFFFFF;
323 if(stype
== stype_2bit
) return (val
& 3) * 0x555555;
324 if(stype
== stype_4bit
) return (val
& 15) * 0x111111;
325 if(stype
== stype_8bit
) return val
* 0x010101;
328 template<unsigned stype
, bool grey
>
329 void _decode_palette(parsed_png
& adata
, png_idat_decoder
& idata
, uint32_t* palette
)
331 uint32_t gtrans
= palette
[0];
333 if(stype
== stype_1bit
) scan_bytes
= (adata
.width
+ 7) / 8;
334 if(stype
== stype_2bit
) scan_bytes
= (adata
.width
+ 3) / 4;
335 if(stype
== stype_4bit
) scan_bytes
= (adata
.width
+ 1) / 2;
336 if(stype
== stype_8bit
) scan_bytes
= (adata
.width
+ 0) / 1;
337 std::vector
<uint8_t> prevline
, thisline
;
338 prevline
.resize(scan_bytes
);
339 thisline
.resize(scan_bytes
+ 1);
340 adata
.data
= new uint32_t[adata
.width
* adata
.height
];
341 memset(&prevline
[0], 0, scan_bytes
);
343 for(size_t i
= 0; i
< adata
.height
; i
++) {
344 size_t r
= idata
.decode(&thisline
[0], scan_bytes
+ 1);
345 if(r
< scan_bytes
+ 1)
346 throw std::runtime_error("PNG uncompressed data underrun");
347 scanline_inverse_filter(&thisline
[1], &prevline
[0], scan_bytes
, 1, thisline
[0]);
348 uint32_t* dest2
= adata
.data
+ i
* adata
.width
;
349 for(size_t i
= 0; i
< adata
.width
; i
++) {
351 if(stype
== stype_1bit
)
352 s
= (thisline
[i
/ 8 + 1] >> (7 - (i
% 8))) & 1;
353 if(stype
== stype_2bit
)
354 s
= (thisline
[i
/ 4 + 1] >> (6 - (i
% 4) * 2)) & 3;
355 if(stype
== stype_4bit
)
356 s
= (thisline
[i
/ 2 + 1] >> (4 - (i
% 2) * 4)) & 15;
357 if(stype
== stype_8bit
)
360 dest2
[i
] = grey_expand
<stype
>(s
) | ((s
!= gtrans
) ? 0xFF000000U
: 0);
362 dest2
[i
] = palette
[s
];
364 memcpy(&prevline
[0], &thisline
[1], scan_bytes
);
369 void decode_palette(parsed_png
& adata
, png_idat_decoder
& idata
, unsigned stype
, uint32_t* palette
)
371 if(stype
== stype_1bit
) _decode_palette
<stype_1bit
, grey
>(adata
, idata
, palette
);
372 if(stype
== stype_2bit
) _decode_palette
<stype_2bit
, grey
>(adata
, idata
, palette
);
373 if(stype
== stype_4bit
) _decode_palette
<stype_4bit
, grey
>(adata
, idata
, palette
);
374 if(stype
== stype_8bit
) _decode_palette
<stype_8bit
, grey
>(adata
, idata
, palette
);
377 template<unsigned ppitch
>
378 void decode_rgb(parsed_png
& adata
, png_idat_decoder
& idata
, uint32_t ckey
)
380 size_t scan_bytes
= ppitch
* adata
.width
;
381 std::vector
<uint8_t> prevline
, thisline
;
382 prevline
.resize(scan_bytes
);
383 thisline
.resize(scan_bytes
+ 1);
384 adata
.data
= new uint32_t[adata
.width
* adata
.height
];
385 memset(&prevline
[0], 0, scan_bytes
);
387 for(size_t i
= 0; i
< adata
.height
; i
++) {
388 size_t r
= idata
.decode(&thisline
[0], scan_bytes
+ 1);
389 if(r
< scan_bytes
+ 1)
390 throw std::runtime_error("PNG uncompressed data underrun");
391 scanline_inverse_filter(&thisline
[1], &prevline
[0], scan_bytes
, ppitch
, thisline
[0]);
392 uint32_t* dest
= adata
.data
+ i
* adata
.width
;
393 for(size_t i
= 0; i
< adata
.width
; i
++) {
395 dest
[i
] = static_cast<uint32_t>(thisline
[ppitch
* i
+ 1]);
396 dest
[i
] |= (static_cast<uint32_t>(thisline
[ppitch
* i
+ 1]) << 8);
397 dest
[i
] |= (static_cast<uint32_t>(thisline
[ppitch
* i
+ 1]) << 16);
398 dest
[i
] |= (static_cast<uint32_t>(thisline
[ppitch
* i
+ 2]) << 24);
400 dest
[i
] = static_cast<uint32_t>(thisline
[ppitch
* i
+ 1]);
401 dest
[i
] |= (static_cast<uint32_t>(thisline
[ppitch
* i
+ 2]) << 8);
402 dest
[i
] |= (static_cast<uint32_t>(thisline
[ppitch
* i
+ 3]) << 16);
404 dest
[i
] |= (static_cast<uint32_t>(thisline
[ppitch
* i
+ 4]) << 24);
406 dest
[i
] |= 0xFF000000U
;
412 memcpy(&prevline
[0], &thisline
[1], scan_bytes
);
416 void load_png3(parsed_png
& adata
, std::vector
<png_chunk
>& chunks
, unsigned ctype
)
418 unsigned stype
= ctype
& stype_mask
;
419 unsigned pstride
= ctype
& pstride_mask
;
420 bool palette_lookup
= ctype
& paletted
;
421 bool gtrans
= ctype
& greyscale_trans
;
422 bool ctrans
= ctype
& rgb_trans
;
424 uint32_t palette
[256] = {0};
426 load_palette(palette
, chunks
);
428 load_gtrans(palette
, chunks
, stype
);
430 load_ctrans(palette
, chunks
);
431 png_idat_decoder
idat(chunks
);
432 if(pstride
== pstride_s
&& palette_lookup
)
433 decode_palette
<false>(adata
, idat
, stype
, palette
);
434 if(pstride
== pstride_s
&& !palette_lookup
)
435 decode_palette
<true>(adata
, idat
, stype
, palette
);
436 if(pstride
== pstride_2
)
437 decode_rgb
<2>(adata
, idat
, palette
[0]);
438 if(pstride
== pstride_3
)
439 decode_rgb
<3>(adata
, idat
, palette
[0]);
440 if(pstride
== pstride_4
)
441 decode_rgb
<4>(adata
, idat
, palette
[0]);
444 void load_png2(parsed_png
& adata
, std::vector
<png_chunk
>& chunks
)
446 if(chunks
[0].size
< 13)
447 throw std::runtime_error("IHDR chunk too small");
448 adata
.width
= read32(chunks
[0].data
+ 0);
449 adata
.height
= read32(chunks
[0].data
+ 4);
450 uint8_t bitdepth
= chunks
[0].data
[8];
451 uint8_t colortype
= chunks
[0].data
[9];
452 uint8_t compression
= chunks
[0].data
[10];
453 uint8_t filterbank
= chunks
[0].data
[11];
454 uint8_t interlace
= chunks
[0].data
[11];
457 throw std::runtime_error("Unsupported compression method");
459 throw std::runtime_error("Unsupported filter bank");
461 throw std::runtime_error("Unsupported interlace type");
462 if(colortype
== 0 && bitdepth
== 1)
463 ctype
= stype_1bit
| pstride_s
| nopalette
| greyscale_trans
;
464 else if(colortype
== 0 && bitdepth
== 2)
465 ctype
= stype_2bit
| pstride_s
| nopalette
| greyscale_trans
;
466 else if(colortype
== 0 && bitdepth
== 4)
467 ctype
= stype_4bit
| pstride_s
| nopalette
| greyscale_trans
;
468 else if(colortype
== 0 && bitdepth
== 8)
469 ctype
= stype_8bit
| pstride_s
| nopalette
| greyscale_trans
;
470 else if(colortype
== 2 && bitdepth
== 8)
471 ctype
= stype_8bit
| pstride_3
| nopalette
| rgb_trans
;
472 else if(colortype
== 3 && bitdepth
== 1)
473 ctype
= stype_1bit
| pstride_s
| paletted
;
474 else if(colortype
== 3 && bitdepth
== 2)
475 ctype
= stype_2bit
| pstride_s
| paletted
;
476 else if(colortype
== 3 && bitdepth
== 4)
477 ctype
= stype_4bit
| pstride_s
| paletted
;
478 else if(colortype
== 3 && bitdepth
== 8)
479 ctype
= stype_8bit
| pstride_s
| paletted
;
480 else if(colortype
== 4 && bitdepth
== 8)
481 ctype
= stype_8bit
| pstride_2
| nopalette
;
482 else if(colortype
== 6 && bitdepth
== 8)
483 ctype
= stype_8bit
| pstride_4
| nopalette
;
485 throw std::runtime_error("Unsupported color type or bit depth");
486 load_png3(adata
, chunks
, ctype
);
489 void load_png(std::istream
& in
, parsed_png
& adata
, const char* data
, size_t datasize
)
491 std::vector
<uint8_t> filedata
;
492 load_file(filedata
, in
, data
, datasize
);
493 const uint8_t* fdata
= &filedata
[0];
494 size_t fsize
= filedata
.size();
497 std::vector
<png_chunk
> chunks
;
498 png_header
header(fdata
, fsize
, iptr
);
500 chunks
.push_back(png_chunk(fdata
, fsize
, iptr
, iptr
));
501 if(chunks
.empty() || chunks
[0].type
!= 0x49484452)
502 throw std::runtime_error("First chunk must be IHDR");
503 if(chunks
.empty() || chunks
[chunks
.size() - 1].type
!= 0x49454E44)
504 throw std::runtime_error("Last chunk must be IEND");
505 load_png2(adata
, chunks
);
506 } catch(std::exception
& e
) {
507 throw std::runtime_error(std::string("Can't load PNG image: ") + e
.what());
513 parsed_png::parsed_png(size_t w
, size_t h
)
515 data
= new uint32_t[w
* h
];
520 parsed_png::parsed_png(const std::string
& pngfile
)
524 std::ifstream
x(pngfile
);
526 throw std::runtime_error("Can't open PNG file");
527 load_png(x
, *this, &dummy
, 0);
530 parsed_png::~parsed_png()
532 if(data
) delete[] data
;
535 #ifdef TEST_PNG_DECODER
536 int main(int argc
, char** argv
)
538 parsed_png
p(argv
[1]);
540 std::cerr
<< "Parsed as RGBA, " << p
.width
<< "*" << p
.height
<< std::endl
;
541 std::ofstream
y("png.dump");
542 for(size_t i
= 0; i
< p
.width
* p
.height
; i
++)
543 y
.write(reinterpret_cast<char*>(p
.adata
+ i
), 4);
545 std::cerr
<< "Parsed as RGB, " << p
.width
<< "*" << p
.height
<< std::endl
;
546 std::ofstream
y("png.dump");
547 for(size_t i
= 0; i
< p
.width
* p
.height
; i
++)
548 y
.write(reinterpret_cast<char*>(p
.tdata
+ i
), 3);
550 std::cerr
<< "Parsed as indexed, " << p
.width
<< "*" << p
.height
<< std::endl
;
551 std::ofstream
y("png.dump");
552 for(size_t i
= 0; i
< p
.width
* p
.height
; i
++) {
554 buf
[0] = buf
[1] = buf
[2] = p
.pdata
[i
] ? 255 : 0;
558 std::cerr
<< "Unknown type" << std::endl
;