Fix integer overflow in ft_rendered_size_line
[ilaris-y4m-tools.git] / png.cpp
blobcbf6c770dacd73b1dda7cca9b5587a4cbd9fdd75
1 #include "png.hpp"
2 #include "parseval.hpp"
3 #include <iomanip>
4 #include <zlib.h>
5 #include <stdexcept>
6 #include <cstring>
7 #include <sstream>
8 #include <fstream>
9 #include <iostream>
10 #include <vector>
12 #define BLOCK 8192
13 #define MAXBLOCK 8192
15 namespace
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)
34 size_t done = 0;
35 char* tmp = reinterpret_cast<char*>(buffer);
36 while(size > 0) {
37 size_t bsize = size;
38 if(bsize > MAXBLOCK)
39 bsize = MAXBLOCK;
40 stream.read(tmp, bsize);
41 size_t r = stream.gcount();
42 done += r;
43 tmp += r;
44 size -= r;
45 if(!stream)
46 return done;
48 return done;
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++)
55 data[i] = edata[i];
56 while(true) {
57 size_t pos = data.size();
58 data.resize(pos + BLOCK);
59 size_t r = bulk_read(in, &data[pos], BLOCK);
60 if(r < BLOCK) {
61 data.resize(pos + r);
62 return;
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)
79 free(addr);
82 int throw_zlib_error(int err)
84 if(err == Z_ERRNO) {
85 int _err = errno;
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");
98 if(err < 0) {
99 std::ostringstream x;
100 x << "Unknown zlib error #" << -err << std::endl;
101 throw std::runtime_error(x.str());
103 return err;
106 struct png_header
108 png_header(const uint8_t* buffer, size_t bufsize, size_t& nextptr);
111 struct png_chunk
113 png_chunk(const uint8_t* buffer, size_t bufsize, size_t chunkptr, size_t& nextptr);
114 uint32_t type;
115 const uint8_t* data;
116 size_t size;
119 class png_idat_decoder
121 public:
122 png_idat_decoder(std::vector<png_chunk>& _chunks);
123 ~png_idat_decoder();
124 size_t decode(uint8_t* buffer, size_t maxbytes);
125 private:
126 png_idat_decoder(png_idat_decoder&);
127 png_idat_decoder& operator=(png_idat_decoder&);
128 std::vector<png_chunk>& chunks;
129 z_stream inflater;
130 size_t block_num;
131 size_t block_pointer;
132 bool eof;
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");
140 nextptr = 8;
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)
160 : chunks(_chunks)
162 memset(&inflater, 0, sizeof(inflater));
163 inflater.zalloc = zlib_alloc;
164 inflater.zfree = zlib_free;
165 throw_zlib_error(inflateInit(&inflater));
166 block_num = 0;
167 //Find first IDAT.
168 while(block_num < chunks.size() && chunks[block_num].type != 0x49444154)
169 block_num++;
170 if(block_num == chunks.size())
171 throw std::runtime_error("PNG file has no IDAT chunks");
172 block_pointer = 0;
173 eof = false;
176 png_idat_decoder::~png_idat_decoder()
178 inflateEnd(&inflater);
181 size_t png_idat_decoder::decode(uint8_t* buffer, size_t maxbytes)
183 if(eof)
184 return 0;
185 size_t done = 0;
186 while(maxbytes > 0) {
187 unsigned char dummy;
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;
191 } else {
192 inflater.next_in = &dummy;
193 inflater.avail_in = 0;
195 if(inflater.avail_in < chunks[block_num].size - block_pointer) {
196 //Set to maximum.
197 inflater.avail_in = 0;
198 inflater.avail_in--;
200 inflater.next_out = buffer;
201 inflater.avail_out = maxbytes;
202 if(inflater.avail_out < maxbytes) {
203 //Set to maximum.
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));
210 if(c == Z_NEED_DICT)
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) {
217 eof = true;
218 break;
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) {
223 do {
224 block_num++;
225 } while(block_num < chunks.size() && chunks[block_num].type != 0x49444154);
226 block_pointer = 0;
229 return done;
232 void load_palette(uint32_t* palette, std::vector<png_chunk>& chunks)
234 bool ok = false;
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;
246 ok = true;
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));
254 if(!ok)
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) {
263 if(i.size < 2)
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) {
275 if(i.size < 6)
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)
287 if(filter == 0)
289 else if(filter == 1)
290 for(unsigned i = psize; i < bytes; i++)
291 data[i] += data[i - psize];
292 else if(filter == 2)
293 for(unsigned i = 0; i < bytes; i++)
294 data[i] += last[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++)
302 data[i] += last[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];
310 else if(pb <= pc)
311 data[i] += last[i];
312 else
313 data[i] += last[i - psize];
315 } else
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];
332 size_t scan_bytes;
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++) {
350 uint8_t s;
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)
358 s = thisline[i + 1];
359 if(grey) {
360 dest2[i] = grey_expand<stype>(s) | ((s != gtrans) ? 0xFF000000U : 0);
361 } else
362 dest2[i] = palette[s];
364 memcpy(&prevline[0], &thisline[1], scan_bytes);
368 template<bool grey>
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++) {
394 if(ppitch == 2) {
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);
399 } else {
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);
403 if(ppitch > 3)
404 dest[i] |= (static_cast<uint32_t>(thisline[ppitch * i + 4]) << 24);
405 else {
406 dest[i] |= 0xFF000000U;
407 if(dest[i] == ckey)
408 dest[i] &= 0xFFFFFF;
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};
425 if(palette_lookup)
426 load_palette(palette, chunks);
427 else if(gtrans)
428 load_gtrans(palette, chunks, stype);
429 else if(ctrans)
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];
455 uint8_t ctype;
456 if(compression != 0)
457 throw std::runtime_error("Unsupported compression method");
458 if(filterbank != 0)
459 throw std::runtime_error("Unsupported filter bank");
460 if(interlace != 0)
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;
484 else
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();
495 size_t iptr = 0;
496 try {
497 std::vector<png_chunk> chunks;
498 png_header header(fdata, fsize, iptr);
499 while(iptr < fsize)
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];
516 width = w;
517 height = h;
520 parsed_png::parsed_png(const std::string& pngfile)
522 data = NULL;
523 char dummy;
524 std::ifstream x(pngfile);
525 if(!x)
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]);
539 if(p.adata) {
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);
544 } else if(p.tdata) {
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);
549 } else if(p.pdata) {
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++) {
553 char buf[3];
554 buf[0] = buf[1] = buf[2] = p.pdata[i] ? 255 : 0;
555 y.write(buf, 3);
557 } else {
558 std::cerr << "Unknown type" << std::endl;
561 #endif