1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles, Vas Crabb
3 /*********************************************************************
4
5 png.cpp
6
7 PNG reading functions.
8
9 ***************************************************************************/
10
11 #include "localpng.h"
12
13 #include "unicode.h"
14
15 #include <zlib.h>
16
17 #include <algorithm>
18 #include <cassert>
19 #include <cmath>
20 #include <cstdlib>
21 #include <cstring>
22 #include <new>
23
24
25 namespace util {
26
27 /***************************************************************************
28 GENERAL FUNCTIONS
29 ***************************************************************************/
30
31 /*-------------------------------------------------
32 free_data - free all memory allocated in a
33 pnginfo structure
34 -------------------------------------------------*/
35
free_data()36 void png_info::free_data()
37 {
38 textlist.clear();
39 palette.reset();
40 trans.reset();
41 image.reset();
42 }
43
44
45
46 namespace {
47
48 /***************************************************************************
49 GLOBAL VARIABLES
50 ***************************************************************************/
51
52 constexpr int samples[] = { 1, 0, 3, 1, 2, 0, 4 };
53
54 constexpr std::uint8_t PNG_SIGNATURE[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
55 #define MNG_Signature "\x8A\x4D\x4E\x47\x0D\x0A\x1A\x0A"
56
57 // Chunk names
58 constexpr std::uint32_t PNG_CN_IHDR = 0x49484452L;
59 constexpr std::uint32_t PNG_CN_PLTE = 0x504C5445L;
60 constexpr std::uint32_t PNG_CN_IDAT = 0x49444154L;
61 constexpr std::uint32_t PNG_CN_IEND = 0x49454E44L;
62 constexpr std::uint32_t PNG_CN_gAMA = 0x67414D41L;
63 //constexpr std::uint32_t PNG_CN_sBIT = 0x73424954L;
64 //constexpr std::uint32_t PNG_CN_cHRM = 0x6348524DL;
65 constexpr std::uint32_t PNG_CN_tRNS = 0x74524E53L;
66 //constexpr std::uint32_t PNG_CN_bKGD = 0x624B4744L;
67 //constexpr std::uint32_t PNG_CN_hIST = 0x68495354L;
68 constexpr std::uint32_t PNG_CN_tEXt = 0x74455874L;
69 //constexpr std::uint32_t PNG_CN_zTXt = 0x7A545874L;
70 constexpr std::uint32_t PNG_CN_pHYs = 0x70485973L;
71 //constexpr std::uint32_t PNG_CN_oFFs = 0x6F464673L;
72 //constexpr std::uint32_t PNG_CN_tIME = 0x74494D45L;
73 //constexpr std::uint32_t PNG_CN_sCAL = 0x7343414CL;
74
75 // MNG Chunk names
76 constexpr std::uint32_t MNG_CN_MHDR = 0x4D484452L;
77 constexpr std::uint32_t MNG_CN_MEND = 0x4D454E44L;
78 //constexpr std::uint32_t MNG_CN_TERM = 0x5445524DL;
79 //constexpr std::uint32_t MNG_CN_BACK = 0x4241434BL;
80
81 // Prediction filters
82 constexpr std::uint8_t PNG_PF_None = 0;
83 constexpr std::uint8_t PNG_PF_Sub = 1;
84 constexpr std::uint8_t PNG_PF_Up = 2;
85 constexpr std::uint8_t PNG_PF_Average = 3;
86 constexpr std::uint8_t PNG_PF_Paeth = 4;
87
88
89 /***************************************************************************
90 INLINE FUNCTIONS
91 ***************************************************************************/
92
compute_rowbytes(const png_info & pnginfo)93 inline int compute_rowbytes(const png_info &pnginfo) { return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8; }
94
fetch_8bit(uint8_t const * v)95 inline uint8_t fetch_8bit(uint8_t const *v) { return *v; }
fetch_16bit(uint8_t const * v)96 inline uint16_t fetch_16bit(uint8_t const *v) { return big_endianize_int16(*reinterpret_cast<uint16_t const *>(v)); }
fetch_32bit(uint8_t const * v)97 inline uint32_t fetch_32bit(uint8_t const *v) { return big_endianize_int32(*reinterpret_cast<uint32_t const *>(v)); }
98
put_8bit(uint8_t * v,uint8_t data)99 inline void put_8bit(uint8_t *v, uint8_t data) { *v = data; }
put_16bit(uint8_t * v,uint16_t data)100 inline void put_16bit(uint8_t *v, uint16_t data) { *reinterpret_cast<uint16_t *>(v) = big_endianize_int16(data); }
put_32bit(uint8_t * v,uint32_t data)101 inline void put_32bit(uint8_t *v, uint32_t data) { *reinterpret_cast<uint32_t *>(v) = big_endianize_int32(data); }
102
103
104 class png_private
105 {
106 private:
107 static constexpr unsigned ADAM7_X_BIAS[7] = { 7, 3, 3, 1, 1, 0, 0 };
108 static constexpr unsigned ADAM7_Y_BIAS[7] = { 7, 7, 3, 3, 1, 1, 0 };
109 static constexpr unsigned ADAM7_X_SHIFT[7] = { 3, 3, 2, 2, 1, 1, 0 };
110 static constexpr unsigned ADAM7_Y_SHIFT[7] = { 3, 3, 3, 2, 2, 1, 1 };
111 static constexpr unsigned ADAM7_X_OFFS[7] = { 0, 4, 0, 2, 0, 1, 0 };
112 static constexpr unsigned ADAM7_Y_OFFS[7] = { 0, 0, 4, 0, 2, 0, 1 };
113
114 struct image_data_chunk
115 {
image_data_chunkutil::__anond67a778a0111::png_private::image_data_chunk116 image_data_chunk(std::uint32_t l, std::unique_ptr<std::uint8_t []> &&d) : length(l), data(std::move(d)) { }
117
118 std::uint32_t length;
119 std::unique_ptr<std::uint8_t []> data;
120 };
121
process(std::list<image_data_chunk> const & idata)122 png_error process(std::list<image_data_chunk> const &idata)
123 {
124 // do some basic checks for unsupported images
125 if (!pnginfo.bit_depth || (ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
126 return png_error::UNSUPPORTED_FORMAT; // unknown colour format
127 if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method))
128 return png_error::UNSUPPORTED_FORMAT; // unknown interlace method
129 if ((3 == pnginfo.color_type) && (!pnginfo.num_palette || !pnginfo.palette))
130 return png_error::FILE_CORRUPT; // indexed colour with no palette
131
132 // calculate the offset for each pass of the interlace
133 unsigned const pass_count(get_pass_count());
134 std::uint32_t pass_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
135 for (unsigned pass = 0; pass_count > pass; ++pass)
136 pass_offset[pass + 1] = pass_offset[pass] + get_pass_bytes(pass);
137
138 // allocate memory for the filtered image
139 try { pnginfo.image.reset(new std::uint8_t [pass_offset[pass_count]]); }
140 catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
141
142 // decompress image data
143 png_error error = png_error::NONE;
144 error = decompress(idata, pass_offset[pass_count]);
145 std::uint32_t const bpp(get_bytes_per_pixel());
146 for (unsigned pass = 0; (pass_count > pass) && (png_error::NONE == error); ++pass)
147 {
148 // compute some basic parameters
149 std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
150 std::uint32_t const rowbytes(get_row_bytes(dimensions.first));
151
152 // we de-filter in place, stripping the filter bytes off the rows
153 uint8_t *dst(&pnginfo.image[pass_offset[pass]]);
154 uint8_t const *src(dst);
155 for (std::uint32_t y = 0; (dimensions.second > y) && (png_error::NONE == error); ++y)
156 {
157 // first byte of each row is the filter type
158 uint8_t const filter(*src++);
159 error = unfilter_row(filter, src, dst, y ? (dst - rowbytes) : nullptr, bpp, rowbytes);
160 src += rowbytes;
161 dst += rowbytes;
162 }
163 }
164
165 // if we errored, free the image data
166 if (error != png_error::NONE)
167 pnginfo.image.reset();
168
169 return error;
170 }
171
decompress(std::list<image_data_chunk> const & idata,std::uint32_t expected)172 png_error decompress(std::list<image_data_chunk> const &idata, std::uint32_t expected)
173 {
174 // only deflate is permitted
175 if (0 != pnginfo.compression_method)
176 return png_error::DECOMPRESS_ERROR;
177
178 // allocate zlib stream
179 z_stream stream;
180 int zerr;
181 std::memset(&stream, 0, sizeof(stream));
182 stream.zalloc = Z_NULL;
183 stream.zfree = Z_NULL;
184 stream.opaque = Z_NULL;
185 stream.avail_in = 0;
186 stream.next_in = Z_NULL;
187 zerr = inflateInit(&stream);
188 if (Z_OK != zerr)
189 return png_error::DECOMPRESS_ERROR;
190
191 // decompress IDAT blocks
192 stream.next_out = pnginfo.image.get();
193 stream.avail_out = expected;
194 stream.avail_in = 0;
195 auto it = idata.begin();
196 while ((idata.end() != it) && ((Z_OK == zerr) || (Z_BUF_ERROR == zerr)) && !stream.avail_in)
197 {
198 stream.avail_in = it->length;
199 stream.next_in = it->data.get();
200 do
201 {
202 zerr = inflate(&stream, Z_NO_FLUSH);
203 }
204 while (stream.avail_in && (Z_OK == zerr));
205 if (!stream.avail_in)
206 ++it;
207 }
208
209 // it's all good if we got end-of-stream or we have with no data remaining
210 if ((Z_OK == inflateEnd(&stream)) && ((Z_STREAM_END == zerr) || ((Z_OK == zerr) && (idata.end() == it) && !stream.avail_in)))
211 return png_error::NONE;
212 else
213 return png_error::DECOMPRESS_ERROR;
214 }
215
unfilter_row(std::uint8_t type,uint8_t const * src,uint8_t * dst,uint8_t const * dstprev,int bpp,std::uint32_t rowbytes)216 png_error unfilter_row(std::uint8_t type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, std::uint32_t rowbytes)
217 {
218 if (0 != pnginfo.filter_method)
219 return png_error::UNKNOWN_FILTER;
220
221 switch (type)
222 {
223 case PNG_PF_None: // no filter, just copy
224 std::copy_n(src, rowbytes, dst);
225 return png_error::NONE;
226
227 case PNG_PF_Sub: // SUB = previous pixel
228 dst = std::copy_n(src, bpp, dst);
229 src += bpp;
230 for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst)
231 *dst = *src + dst[-bpp];
232 return png_error::NONE;
233
234 case PNG_PF_Up: // UP = pixel above
235 if (dstprev)
236 {
237 for (std::uint32_t x = 0; rowbytes > x; ++x, ++src, ++dst, ++dstprev)
238 *dst = *src + *dstprev;
239 }
240 else
241 {
242 std::copy_n(src, rowbytes, dst);
243 }
244 return png_error::NONE;
245
246 case PNG_PF_Average: // AVERAGE = average of pixel above and previous pixel
247 if (dstprev)
248 {
249 for (std::uint32_t x = 0; bpp > x; ++x, ++src, ++dst, ++dstprev)
250 *dst = *src + (*dstprev >> 1);
251 for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst, ++dstprev)
252 *dst = *src + ((*dstprev + dst[-bpp]) >> 1);
253 }
254 else
255 {
256 dst = std::copy_n(src, bpp, dst);
257 src += bpp;
258 for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst)
259 *dst = *src + (dst[-bpp] >> 1);
260 }
261 return png_error::NONE;
262
263 case PNG_PF_Paeth: // PAETH = special filter
264 for (std::uint32_t x = 0; rowbytes > x; ++x, ++src, ++dst)
265 {
266 int32_t const pa((x < bpp) ? 0 : dst[-bpp]);
267 int32_t const pc(((x < bpp) || !dstprev) ? 0 : dstprev[-bpp]);
268 int32_t const pb(!dstprev ? 0 : *dstprev++);
269 int32_t const prediction(pa + pb - pc);
270 int32_t const da(std::abs(prediction - pa));
271 int32_t const db(std::abs(prediction - pb));
272 int32_t const dc(std::abs(prediction - pc));
273 *dst = ((da <= db) && (da <= dc)) ? (*src + pa) : (db <= dc) ? (*src + pb) : (*src + pc);
274 }
275 return png_error::NONE;
276
277 default: // unknown filter type
278 return png_error::UNKNOWN_FILTER;
279 }
280 }
281
process_chunk(std::list<image_data_chunk> & idata,std::unique_ptr<std::uint8_t[]> && data,uint32_t type,uint32_t length)282 png_error process_chunk(std::list<image_data_chunk> &idata, std::unique_ptr<std::uint8_t []> &&data, uint32_t type, uint32_t length)
283 {
284 switch (type)
285 {
286 case PNG_CN_IHDR: // image header
287 if (13 > length)
288 return png_error::FILE_CORRUPT;
289 pnginfo.width = fetch_32bit(&data[0]);
290 pnginfo.height = fetch_32bit(&data[4]);
291 pnginfo.bit_depth = fetch_8bit(&data[8]);
292 pnginfo.color_type = fetch_8bit(&data[9]);
293 pnginfo.compression_method = fetch_8bit(&data[10]);
294 pnginfo.filter_method = fetch_8bit(&data[11]);
295 pnginfo.interlace_method = fetch_8bit(&data[12]);
296 break;
297
298 case PNG_CN_PLTE: // palette
299 pnginfo.num_palette = length / 3;
300 if ((length % 3) || ((3 == pnginfo.color_type) && ((1 << pnginfo.bit_depth) < pnginfo.num_palette)))
301 return png_error::FILE_CORRUPT;
302 pnginfo.palette = std::move(data);
303 break;
304
305 case PNG_CN_tRNS: // transparency information
306 if (((0 == pnginfo.color_type) && (2 > length)) || ((2 == pnginfo.color_type) && (6 > length)))
307 return png_error::FILE_CORRUPT;
308 pnginfo.num_trans = length;
309 pnginfo.trans = std::move(data);
310 break;
311
312 case PNG_CN_IDAT: // image data
313 try { idata.emplace_back(length, std::move(data)); }
314 catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
315 break;
316
317 case PNG_CN_gAMA: // gamma
318 if (4 > length)
319 return png_error::FILE_CORRUPT;
320 pnginfo.source_gamma = fetch_32bit(data.get()) / 100000.0;
321 break;
322
323 case PNG_CN_pHYs: // physical information
324 if (9 > length)
325 return png_error::FILE_CORRUPT;
326 pnginfo.xres = fetch_32bit(&data[0]);
327 pnginfo.yres = fetch_32bit(&data[4]);
328 pnginfo.resolution_unit = fetch_8bit(&data[8]);
329 break;
330
331 case PNG_CN_tEXt: // text
332 try
333 {
334 // split into keyword and string
335 std::uint8_t const *kwbegin(&data[0]);
336 std::uint8_t const *const textend(kwbegin + length);
337 std::uint8_t const *const kwend(std::find(kwbegin, textend, '\0'));
338 std::uint8_t const *textbegin(kwend + ((textend == kwend) ? 0 : 1));
339
340 // text is ISO-8859-1 but MAME likes UTF-8
341 std::size_t buflen(2 * std::max(kwend - kwbegin, textend - textbegin));
342 std::unique_ptr<char []> utf8buf(new char [buflen]);
343 char const *const bufend(utf8buf.get() + buflen);
344 char *dst;
345 for (dst = utf8buf.get(); kwend > kwbegin; dst += utf8_from_uchar(dst, bufend - dst, *kwbegin++)) { }
346 std::string keyword(utf8buf.get(), dst);
347 for (dst = utf8buf.get(); textend > textbegin; dst += utf8_from_uchar(dst, bufend - dst, *textbegin++)) { }
348 std::string text(utf8buf.get(), dst);
349
350 // allocate a new text item
351 pnginfo.textlist.emplace_back(std::move(keyword), std::move(text));
352 }
353 catch (std::bad_alloc const &)
354 {
355 return png_error::OUT_OF_MEMORY;
356 }
357 break;
358
359 /* anything else */
360 default:
361 if ((type & 0x20000000) == 0)
362 return png_error::UNKNOWN_CHUNK;
363 break;
364 }
365 return png_error::NONE;
366 }
367
get_pass_count() const368 unsigned get_pass_count() const
369 {
370 return (1 == pnginfo.interlace_method) ? 7 : 1;
371 }
372
get_pass_dimensions(unsigned pass) const373 std::pair<uint32_t, uint32_t> get_pass_dimensions(unsigned pass) const
374 {
375 if (0 == pnginfo.interlace_method)
376 return std::make_pair(pnginfo.width, pnginfo.height);
377 else
378 return std::make_pair((pnginfo.width + ADAM7_X_BIAS[pass]) >> ADAM7_X_SHIFT[pass], (pnginfo.height + ADAM7_Y_BIAS[pass]) >> ADAM7_Y_SHIFT[pass]);
379 }
380
get_pass_bytes(unsigned pass) const381 std::uint32_t get_pass_bytes(unsigned pass) const
382 {
383 return get_pass_bytes(pass, pnginfo.bit_depth);
384 }
385
get_pass_bytes(unsigned pass,uint8_t bit_depth) const386 std::uint32_t get_pass_bytes(unsigned pass, uint8_t bit_depth) const
387 {
388 std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
389 return (get_row_bytes(dimensions.first, bit_depth) + 1) * dimensions.second;
390 }
391
get_row_bytes(std::uint32_t width) const392 std::uint32_t get_row_bytes(std::uint32_t width) const
393 {
394 return get_row_bytes(width, pnginfo.bit_depth);
395 }
396
get_row_bytes(std::uint32_t width,uint8_t bit_depth) const397 std::uint32_t get_row_bytes(std::uint32_t width, uint8_t bit_depth) const
398 {
399 return ((width * samples[pnginfo.color_type] * bit_depth) + 7) >> 3;
400 }
401
get_bytes_per_pixel() const402 std::uint32_t get_bytes_per_pixel() const
403 {
404 return ((samples[pnginfo.color_type] * pnginfo.bit_depth) + 7) >> 3;
405 }
406
read_chunk(core_file & fp,std::unique_ptr<std::uint8_t[]> & data,std::uint32_t & type,std::uint32_t & length)407 static png_error read_chunk(core_file &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length)
408 {
409 std::uint8_t tempbuff[4];
410
411 /* fetch the length of this chunk */
412 if (fp.read(tempbuff, 4) != 4)
413 return png_error::FILE_TRUNCATED;
414 length = fetch_32bit(tempbuff);
415
416 /* fetch the type of this chunk */
417 if (fp.read(tempbuff, 4) != 4)
418 return png_error::FILE_TRUNCATED;
419 type = fetch_32bit(tempbuff);
420
421 /* stop when we hit an IEND chunk */
422 if (type == PNG_CN_IEND)
423 return png_error::NONE;
424
425 /* start the CRC with the chunk type (but not the length) */
426 std::uint32_t crc = crc32(0, tempbuff, 4);
427
428 /* read the chunk itself into an allocated memory buffer */
429 if (length)
430 {
431 /* allocate memory for this chunk */
432 try { data.reset(new std::uint8_t [length]); }
433 catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
434
435 /* read the data from the file */
436 if (fp.read(data.get(), length) != length)
437 {
438 data.reset();
439 return png_error::FILE_TRUNCATED;
440 }
441
442 /* update the CRC */
443 crc = crc32(crc, data.get(), length);
444 }
445
446 /* read the CRC */
447 if (fp.read(tempbuff, 4) != 4)
448 {
449 data.reset();
450 return png_error::FILE_TRUNCATED;
451 }
452 std::uint32_t const chunk_crc = fetch_32bit(tempbuff);
453
454 /* validate the CRC */
455 if (crc != chunk_crc)
456 {
457 data.reset();
458 return png_error::FILE_CORRUPT;
459 }
460
461 return png_error::NONE;
462 }
463
464 png_info & pnginfo;
465
466 public:
png_private(png_info & info)467 png_private(png_info &info) : pnginfo(info)
468 {
469 }
470
copy_to_bitmap(bitmap_argb32 & bitmap,bool & hasalpha) const471 png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const
472 {
473 // do some basic checks for unsupported images
474 if ((8 > pnginfo.bit_depth) || (pnginfo.bit_depth % 8))
475 return png_error::UNSUPPORTED_FORMAT; // only do multiples of 8bps here - expand lower bit depth first
476 if ((ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
477 return png_error::UNSUPPORTED_FORMAT; // unknown colour sample format
478 if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method))
479 return png_error::UNSUPPORTED_FORMAT; // unknown interlace method
480 if ((3 == pnginfo.color_type) && (8 != pnginfo.bit_depth))
481 return png_error::UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp
482
483 // everything looks sane, allocate the bitmap and deinterlace into it
484 bitmap.allocate(pnginfo.width, pnginfo.height);
485 std::uint8_t accumalpha(0xff);
486 uint32_t const bps(pnginfo.bit_depth >> 3);
487 uint32_t const bpp(bps * samples[pnginfo.color_type]);
488 unsigned const pass_count(get_pass_count());
489 std::uint32_t pass_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
490 for (unsigned pass = 0; pass_count > pass; ++pass)
491 {
492 // calculate parameters for interlace pass
493 pass_offset[pass + 1] = pass_offset[pass] + get_pass_bytes(pass);
494 unsigned const x_shift(pnginfo.interlace_method ? ADAM7_X_SHIFT[pass] : 0);
495 unsigned const y_shift(pnginfo.interlace_method ? ADAM7_Y_SHIFT[pass] : 0);
496 unsigned const x_offs(pnginfo.interlace_method ? ADAM7_X_OFFS[pass] : 0);
497 unsigned const y_offs(pnginfo.interlace_method ? ADAM7_Y_OFFS[pass] : 0);
498 std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
499 std::uint8_t const *src(&pnginfo.image[pass_offset[pass]]);
500
501 if (3 == pnginfo.color_type)
502 {
503 // handle 8bpp palettized case
504 for (std::uint32_t y = 0; dimensions.second > y; ++y)
505 {
506 for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp)
507 {
508 // determine alpha and expand to 32bpp
509 std::uint8_t const alpha((*src < pnginfo.num_trans) ? pnginfo.trans[*src] : 0xff);
510 accumalpha &= alpha;
511 std::uint16_t const paloffs(std::uint16_t(*src) * 3);
512 rgb_t const pix(alpha, pnginfo.palette[paloffs], pnginfo.palette[paloffs + 1], pnginfo.palette[paloffs + 2]);
513 bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix;
514 }
515 }
516 }
517 else if (0 == pnginfo.color_type)
518 {
519 // handle grayscale non-alpha case
520 uint32_t const bpp(pnginfo.bit_depth >> 3);
521 std::uint16_t const transpen(pnginfo.trans ? fetch_16bit(pnginfo.trans.get()) : 0U);
522 unsigned const samp_shift((8 < pnginfo.bit_depth) ? 8 : 0);
523 for (std::uint32_t y = 0; dimensions.second > y; ++y)
524 {
525 for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp)
526 {
527 std::uint16_t i_val((8 < pnginfo.bit_depth) ? fetch_16bit(src) : fetch_8bit(src));
528 std::uint8_t const a_val((pnginfo.trans && (transpen == i_val)) ? 0x00 : 0xff);
529 i_val >>= samp_shift;
530 accumalpha &= a_val;
531 bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, i_val, i_val, i_val);
532 }
533 }
534 }
535 else if (4 == pnginfo.color_type)
536 {
537 // handle grayscale alpha case
538 uint32_t const i(0 * bps);
539 uint32_t const a(1 * bps);
540 for (std::uint32_t y = 0; dimensions.second > y; ++y)
541 {
542 for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp)
543 {
544 accumalpha &= src[a];
545 rgb_t const pix(src[a], src[i], src[i], src[i]);
546 bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix;
547 }
548 }
549 }
550 else if (2 == pnginfo.color_type)
551 {
552 // handle RGB non-alpha case
553 uint32_t const r(0 * bps);
554 uint32_t const g(1 * bps);
555 uint32_t const b(2 * bps);
556 std::uint16_t const transpen_r(pnginfo.trans ? fetch_16bit(&pnginfo.trans[0]) : 0U);
557 std::uint16_t const transpen_g(pnginfo.trans ? fetch_16bit(&pnginfo.trans[2]) : 0U);
558 std::uint16_t const transpen_b(pnginfo.trans ? fetch_16bit(&pnginfo.trans[4]) : 0U);
559 unsigned const samp_shift((8 < pnginfo.bit_depth) ? 8 : 0);
560 for (std::uint32_t y = 0; dimensions.second > y; ++y)
561 {
562 for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp)
563 {
564 uint16_t r_val((8 < pnginfo.bit_depth) ? fetch_16bit(src) : fetch_8bit(src + r));
565 uint16_t g_val((8 < pnginfo.bit_depth) ? fetch_16bit(src) : fetch_8bit(src + g));
566 uint16_t b_val((8 < pnginfo.bit_depth) ? fetch_16bit(src) : fetch_8bit(src + b));
567 std::uint8_t const a_val((pnginfo.trans && (transpen_r == r_val) && (transpen_g == g_val) && (transpen_b == b_val)) ? 0x00 : 0xff);
568 r_val >>= samp_shift;
569 g_val >>= samp_shift;
570 b_val >>= samp_shift;
571 accumalpha &= a_val;
572 bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, r_val, g_val, b_val);
573 }
574 }
575 }
576 else
577 {
578 // handle RGB alpha case
579 uint32_t const r(0 * bps);
580 uint32_t const g(1 * bps);
581 uint32_t const b(2 * bps);
582 uint32_t const a(3 * bps);
583 for (std::uint32_t y = 0; dimensions.second > y; ++y)
584 {
585 for (std::uint32_t x = 0; dimensions.first > x; ++x, src += bpp)
586 {
587 accumalpha &= src[a];
588 rgb_t const pix(src[a], src[r], src[g], src[b]);
589 bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix;
590 }
591 }
592 }
593 }
594
595 // set hasalpha flag and return
596 hasalpha = 0xffU != accumalpha;
597 return png_error::NONE;
598 }
599
expand_buffer_8bit()600 png_error expand_buffer_8bit()
601 {
602 // nothing to do if we're at 8 or greater already
603 if (pnginfo.bit_depth >= 8)
604 return png_error::NONE;
605
606 // do some basic checks for unsupported images
607 if (!pnginfo.bit_depth || (8 % pnginfo.bit_depth))
608 return png_error::UNSUPPORTED_FORMAT; // bit depth must be a factor of eight
609 if ((0 != pnginfo.color_type) && (3 != pnginfo.color_type))
610 return png_error::UNSUPPORTED_FORMAT; // only upsample monochrome and indexed colour
611 if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method))
612 return png_error::UNSUPPORTED_FORMAT; // unknown interlace method
613
614 // calculate the offset for each pass of the interlace on the input and output
615 unsigned const pass_count(get_pass_count());
616 std::uint32_t inp_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
617 std::uint32_t outp_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
618 for (unsigned pass = 0; pass_count > pass; ++pass)
619 {
620 inp_offset[pass + 1] = inp_offset[pass] + get_pass_bytes(pass);
621 outp_offset[pass + 1] = outp_offset[pass] + get_pass_bytes(pass, 8);
622 }
623
624 // allocate a new buffer at 8-bit
625 std::unique_ptr<std::uint8_t []> outbuf;
626 try { outbuf.reset(new std::uint8_t [outp_offset[pass_count]]); }
627 catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
628
629 // upsample bitmap
630 std::uint8_t const bytesamples(8 / pnginfo.bit_depth);
631 for (unsigned pass = 0; pass_count > pass; ++pass)
632 {
633 std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
634 std::uint32_t const rowsamples(samples[pnginfo.color_type] * dimensions.first);
635 std::uint32_t const wholebytes(rowsamples / bytesamples);
636 std::uint32_t const leftover(rowsamples % bytesamples);
637 std::uint8_t const *inp(&pnginfo.image[inp_offset[pass]]);
638 std::uint8_t *outp(&outbuf[outp_offset[pass]]);
639
640 for (std::uint32_t y = 0; dimensions.second > y; ++y)
641 {
642 for (std::uint32_t i = 0; wholebytes > i; ++i, ++inp)
643 {
644 for (std::int8_t j = bytesamples - 1; 0 <= j; --j, ++outp)
645 {
646 *outp = (*inp >> (j * pnginfo.bit_depth)) & (0xffU >> (8 - pnginfo.bit_depth));
647 if (!pnginfo.color_type)
648 {
649 for (unsigned k = 4; pnginfo.bit_depth <= k; k >>= 1)
650 *outp |= *outp << k;
651 }
652 }
653 }
654 if (leftover)
655 {
656 for (std::int8_t j = leftover - 1; 0 <= j; --j,++outp)
657 {
658 *outp = (*inp >> (j * pnginfo.bit_depth)) & (0xffU >> (8 - pnginfo.bit_depth));
659 if (!pnginfo.color_type)
660 {
661 for (unsigned k = 4; pnginfo.bit_depth <= k; k >>= 1)
662 *outp |= *outp << k;
663 }
664 }
665 inp++;
666 }
667 }
668 }
669
670 // upsample transparent pen as well
671 if ((0 == pnginfo.color_type) && pnginfo.trans)
672 {
673 std::uint16_t pen(fetch_16bit(&pnginfo.trans[0]));
674 for (unsigned k = 4; pnginfo.bit_depth <= k; k >>= 1)
675 pen |= pen << k;
676 put_16bit(&pnginfo.trans[0], pen);
677 }
678
679 pnginfo.image = std::move(outbuf);
680 pnginfo.bit_depth = 8;
681 return png_error::NONE;
682 }
683
read_file(core_file & fp)684 png_error read_file(core_file &fp)
685 {
686 // initialize the data structures
687 png_error error = png_error::NONE;
688 pnginfo.reset();
689 std::list<image_data_chunk> idata;
690
691 // verify the signature at the start of the file
692 error = verify_header(fp);
693
694 // loop until we hit an IEND chunk
695 while (png_error::NONE == error)
696 {
697 // read a chunk
698 std::unique_ptr<std::uint8_t []> chunk_data;
699 std::uint32_t chunk_type = 0, chunk_length;
700 error = read_chunk(fp, chunk_data, chunk_type, chunk_length);
701 if (png_error::NONE == error)
702 {
703 if (chunk_type == PNG_CN_IEND)
704 break; // stop when we hit an IEND chunk
705 else
706 error = process_chunk(idata, std::move(chunk_data), chunk_type, chunk_length);
707 }
708 }
709
710 // finish processing the image
711 if (png_error::NONE == error)
712 error = process(idata);
713
714 // if we have an error, free all the output data
715 if (error != png_error::NONE)
716 pnginfo.reset();
717
718 return error;
719 }
720
verify_header(core_file & fp)721 static png_error verify_header(core_file &fp)
722 {
723 EQUIVALENT_ARRAY(PNG_SIGNATURE, std::uint8_t) signature;
724
725 // read 8 bytes
726 if (fp.read(signature, sizeof(signature)) != sizeof(signature))
727 return png_error::FILE_TRUNCATED;
728
729 // return an error if we don't match
730 if (std::memcmp(signature, PNG_SIGNATURE, sizeof(PNG_SIGNATURE)))
731 return png_error::BAD_SIGNATURE;
732
733 return png_error::NONE;
734 }
735 };
736
737 constexpr unsigned png_private::ADAM7_X_BIAS[7];
738 constexpr unsigned png_private::ADAM7_Y_BIAS[7];
739 constexpr unsigned png_private::ADAM7_X_SHIFT[7];
740 constexpr unsigned png_private::ADAM7_Y_SHIFT[7];
741 constexpr unsigned png_private::ADAM7_X_OFFS[7];
742 constexpr unsigned png_private::ADAM7_Y_OFFS[7];
743
744 } // anonymous namespace
745
746
747
748
749 /*-------------------------------------------------
750 verify_header - verify PNG file header from a
751 core stream
752 -------------------------------------------------*/
753
verify_header(core_file & fp)754 png_error png_info::verify_header(core_file &fp)
755 {
756 return png_private::verify_header(fp);
757 }
758
759
760 /*-------------------------------------------------
761 read_file - read a PNG from a core stream
762 -------------------------------------------------*/
763
read_file(core_file & fp)764 png_error png_info::read_file(core_file &fp)
765 {
766 return png_private(*this).read_file(fp);
767 }
768
769
770 /*-------------------------------------------------
771 png_read_bitmap - load a PNG file into a
772 bitmap
773 -------------------------------------------------*/
774
png_read_bitmap(core_file & fp,bitmap_argb32 & bitmap)775 png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
776 {
777 png_error result;
778 png_info pnginfo;
779 png_private png(pnginfo);
780
781 // read the PNG data
782 result = png.read_file(fp);
783 if (png_error::NONE != result)
784 return result;
785
786 // resample to 8bpp if necessary
787 result = png.expand_buffer_8bit();
788 if (png_error::NONE != result)
789 {
790 pnginfo.free_data();
791 return result;
792 }
793
794 // allocate a bitmap of the appropriate size and copy it
795 bool hasalpha;
796 return png.copy_to_bitmap(bitmap, hasalpha);
797 }
798
799
800 /*-------------------------------------------------
801 expand_buffer_8bit - copy PNG data into a
802 bitmap
803 -------------------------------------------------*/
804
copy_to_bitmap(bitmap_argb32 & bitmap,bool & hasalpha)805 png_error png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha)
806 {
807 return png_private(*this).copy_to_bitmap(bitmap, hasalpha);
808 }
809
810
811 /*-------------------------------------------------
812 expand_buffer_8bit - expand a buffer from
813 sub 8-bit to 8-bit
814 -------------------------------------------------*/
815
expand_buffer_8bit()816 png_error png_info::expand_buffer_8bit()
817 {
818 return png_private(*this).expand_buffer_8bit();
819 }
820
821
822
823 /***************************************************************************
824 PNG WRITING FUNCTIONS
825 ***************************************************************************/
826
827 /*-------------------------------------------------
828 add_text - add a text entry to the png_info
829 -------------------------------------------------*/
830
add_text(const char * keyword,const char * text)831 png_error png_info::add_text(const char *keyword, const char *text)
832 {
833 // apply rules to keyword
834 char32_t prev(0);
835 std::size_t cnt(0);
836 char const *const kwend(keyword + std::strlen(keyword));
837 for (char const *ptr = keyword; kwend > ptr; )
838 {
839 char32_t ch;
840 int const len(uchar_from_utf8(&ch, ptr, kwend - ptr));
841 if ((0 >= len) || (32 > ch) || (255 < ch) || ((126 < ch) && (161 > ch)) || (((32 == prev) || (keyword == ptr)) && (32 == ch)))
842 return png_error::UNSUPPORTED_FORMAT;
843 prev = ch;
844 ++cnt;
845 ptr += len;
846 }
847 if ((32 == prev) || (1 > cnt) || (79 < cnt))
848 return png_error::UNSUPPORTED_FORMAT;
849
850 // apply rules to text
851 char const *const textend(text + std::strlen(text));
852 for (char const *ptr = text; textend > ptr; )
853 {
854 char32_t ch;
855 int const len(uchar_from_utf8(&ch, ptr, textend - ptr));
856 if ((0 >= len) || (1 > ch) || (255 < ch))
857 return png_error::UNSUPPORTED_FORMAT;
858 ptr += len;
859 }
860
861 // allocate a new text element
862 try { textlist.emplace_back(std::piecewise_construct, std::forward_as_tuple(keyword, kwend), std::forward_as_tuple(text, textend)); }
863 catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
864 return png_error::NONE;
865 }
866
867
868 /*-------------------------------------------------
869 write_chunk - write an in-memory chunk to
870 the given file
871 -------------------------------------------------*/
872
write_chunk(core_file & fp,const uint8_t * data,uint32_t type,uint32_t length)873 static png_error write_chunk(core_file &fp, const uint8_t *data, uint32_t type, uint32_t length)
874 {
875 uint8_t tempbuff[8];
876 uint32_t crc;
877
878 /* stuff the length/type into the buffer */
879 put_32bit(tempbuff + 0, length);
880 put_32bit(tempbuff + 4, type);
881 crc = crc32(0, tempbuff + 4, 4);
882
883 /* write that data */
884 if (fp.write(tempbuff, 8) != 8)
885 return png_error::FILE_ERROR;
886
887 /* append the actual data */
888 if (length > 0)
889 {
890 if (fp.write(data, length) != length)
891 return png_error::FILE_ERROR;
892 crc = crc32(crc, data, length);
893 }
894
895 /* write the CRC */
896 put_32bit(tempbuff, crc);
897 if (fp.write(tempbuff, 4) != 4)
898 return png_error::FILE_ERROR;
899
900 return png_error::NONE;
901 }
902
903
904 /*-------------------------------------------------
905 write_deflated_chunk - write an in-memory
906 chunk to the given file by deflating it
907 -------------------------------------------------*/
908
write_deflated_chunk(core_file & fp,uint8_t * data,uint32_t type,uint32_t length)909 static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t type, uint32_t length)
910 {
911 uint64_t lengthpos = fp.tell();
912 uint8_t tempbuff[8192];
913 uint32_t zlength = 0;
914 z_stream stream;
915 uint32_t crc;
916 int zerr;
917
918 /* stuff the length/type into the buffer */
919 put_32bit(tempbuff + 0, length);
920 put_32bit(tempbuff + 4, type);
921 crc = crc32(0, tempbuff + 4, 4);
922
923 /* write that data */
924 if (fp.write(tempbuff, 8) != 8)
925 return png_error::FILE_ERROR;
926
927 /* initialize the stream */
928 memset(&stream, 0, sizeof(stream));
929 stream.next_in = data;
930 stream.avail_in = length;
931 zerr = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
932 if (zerr != Z_OK)
933 return png_error::COMPRESS_ERROR;
934
935 /* now loop until we run out of data */
936 for ( ; ; )
937 {
938 /* compress this chunk */
939 stream.next_out = tempbuff;
940 stream.avail_out = sizeof(tempbuff);
941 zerr = deflate(&stream, Z_FINISH);
942
943 /* if there's data to write, do it */
944 if (stream.avail_out < sizeof(tempbuff))
945 {
946 int bytes = sizeof(tempbuff) - stream.avail_out;
947 if (fp.write(tempbuff, bytes) != bytes)
948 {
949 deflateEnd(&stream);
950 return png_error::FILE_ERROR;
951 }
952 crc = crc32(crc, tempbuff, bytes);
953 zlength += bytes;
954 }
955
956 /* stop at the end of the stream */
957 if (zerr == Z_STREAM_END)
958 break;
959
960 /* other errors are fatal */
961 if (zerr != Z_OK)
962 {
963 deflateEnd(&stream);
964 return png_error::COMPRESS_ERROR;
965 }
966 }
967
968 /* clean up deflater(maus) */
969 zerr = deflateEnd(&stream);
970 if (zerr != Z_OK)
971 return png_error::COMPRESS_ERROR;
972
973 /* write the CRC */
974 put_32bit(tempbuff, crc);
975 if (fp.write(tempbuff, 4) != 4)
976 return png_error::FILE_ERROR;
977
978 /* seek back and update the length */
979 fp.seek(lengthpos, SEEK_SET);
980 put_32bit(tempbuff + 0, zlength);
981 if (fp.write(tempbuff, 4) != 4)
982 return png_error::FILE_ERROR;
983
984 /* return to the end */
985 fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET);
986 return png_error::NONE;
987 }
988
989
990 /*-------------------------------------------------
991 convert_bitmap_to_image_palette - convert a
992 bitmap to a palettized image
993 -------------------------------------------------*/
994
convert_bitmap_to_image_palette(png_info & pnginfo,const bitmap_t & bitmap,int palette_length,const rgb_t * palette)995 static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
996 {
997 int rowbytes;
998 int x, y;
999
1000 /* set the common info */
1001 pnginfo.width = bitmap.width();
1002 pnginfo.height = bitmap.height();
1003 pnginfo.bit_depth = 8;
1004 pnginfo.color_type = 3;
1005 pnginfo.num_palette = 256;
1006 rowbytes = pnginfo.width;
1007
1008 /* allocate memory for the palette */
1009 try { pnginfo.palette.reset(new std::uint8_t [3 * 256]); }
1010 catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
1011
1012 /* build the palette */
1013 std::fill_n(pnginfo.palette.get(), 3 * 256, 0);
1014 for (x = 0; x < palette_length; x++)
1015 {
1016 rgb_t color = palette[x];
1017 pnginfo.palette[3 * x + 0] = color.r();
1018 pnginfo.palette[3 * x + 1] = color.g();
1019 pnginfo.palette[3 * x + 2] = color.b();
1020 }
1021
1022 /* allocate memory for the image */
1023 try
1024 {
1025 pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]);
1026 }
1027 catch (std::bad_alloc const &)
1028 {
1029 pnginfo.palette.reset();
1030 return png_error::OUT_OF_MEMORY;
1031 }
1032
1033 /* copy in the pixels, specifying a nullptr filter */
1034 for (y = 0; y < pnginfo.height; y++)
1035 {
1036 uint16_t const *src = reinterpret_cast<uint16_t const *>(bitmap.raw_pixptr(y));
1037 uint8_t *dst = &pnginfo.image[y * (rowbytes + 1)];
1038
1039 /* store the filter byte, then copy the data */
1040 *dst++ = 0;
1041 for (x = 0; x < pnginfo.width; x++)
1042 *dst++ = *src++;
1043 }
1044
1045 return png_error::NONE;
1046 }
1047
1048
1049 /*-------------------------------------------------
1050 convert_bitmap_to_image_rgb - convert a
1051 bitmap to an RGB image
1052 -------------------------------------------------*/
1053
convert_bitmap_to_image_rgb(png_info & pnginfo,const bitmap_t & bitmap,int palette_length,const rgb_t * palette)1054 static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
1055 {
1056 int alpha = (bitmap.format() == BITMAP_FORMAT_ARGB32);
1057 int rowbytes;
1058 int x, y;
1059
1060 /* set the common info */
1061 pnginfo.width = bitmap.width();
1062 pnginfo.height = bitmap.height();
1063 pnginfo.bit_depth = 8;
1064 pnginfo.color_type = alpha ? 6 : 2;
1065 rowbytes = pnginfo.width * (alpha ? 4 : 3);
1066
1067 /* allocate memory for the image */
1068 try { pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]); }
1069 catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
1070
1071 /* copy in the pixels, specifying a nullptr filter */
1072 for (y = 0; y < pnginfo.height; y++)
1073 {
1074 uint8_t *dst = &pnginfo.image[y * (rowbytes + 1)];
1075
1076 /* store the filter byte, then copy the data */
1077 *dst++ = 0;
1078
1079 /* 16bpp palettized format */
1080 if (bitmap.format() == BITMAP_FORMAT_IND16)
1081 {
1082 uint16_t const *src16 = reinterpret_cast<uint16_t const *>(bitmap.raw_pixptr(y));
1083 for (x = 0; x < pnginfo.width; x++)
1084 {
1085 rgb_t color = palette[*src16++];
1086 *dst++ = color.r();
1087 *dst++ = color.g();
1088 *dst++ = color.b();
1089 }
1090 }
1091
1092 /* 32-bit RGB direct */
1093 else if (bitmap.format() == BITMAP_FORMAT_RGB32)
1094 {
1095 uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y));
1096 for (x = 0; x < pnginfo.width; x++)
1097 {
1098 rgb_t raw = *src32++;
1099 *dst++ = raw.r();
1100 *dst++ = raw.g();
1101 *dst++ = raw.b();
1102 }
1103 }
1104
1105 /* 32-bit ARGB direct */
1106 else if (bitmap.format() == BITMAP_FORMAT_ARGB32)
1107 {
1108 uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y));
1109 for (x = 0; x < pnginfo.width; x++)
1110 {
1111 rgb_t raw = *src32++;
1112 *dst++ = raw.r();
1113 *dst++ = raw.g();
1114 *dst++ = raw.b();
1115 *dst++ = raw.a();
1116 }
1117 }
1118
1119 /* unsupported format */
1120 else
1121 return png_error::UNSUPPORTED_FORMAT;
1122 }
1123
1124 return png_error::NONE;
1125 }
1126
1127
1128 /*-------------------------------------------------
1129 write_png_stream - stream a series of PNG
1130 chunks to the given file
1131 -------------------------------------------------*/
1132
write_png_stream(core_file & fp,png_info & pnginfo,const bitmap_t & bitmap,int palette_length,const rgb_t * palette)1133 static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
1134 {
1135 uint8_t tempbuff[16];
1136 png_error error;
1137
1138 // create an unfiltered image in either palette or RGB form
1139 if (bitmap.format() == BITMAP_FORMAT_IND16 && palette_length <= 256)
1140 error = convert_bitmap_to_image_palette(pnginfo, bitmap, palette_length, palette);
1141 else
1142 error = convert_bitmap_to_image_rgb(pnginfo, bitmap, palette_length, palette);
1143 if (error != png_error::NONE)
1144 return error;
1145
1146 // if we wanted to get clever and do filtering, we would do it here
1147
1148 // write the IHDR chunk
1149 put_32bit(tempbuff + 0, pnginfo.width);
1150 put_32bit(tempbuff + 4, pnginfo.height);
1151 put_8bit(tempbuff + 8, pnginfo.bit_depth);
1152 put_8bit(tempbuff + 9, pnginfo.color_type);
1153 put_8bit(tempbuff + 10, pnginfo.compression_method);
1154 put_8bit(tempbuff + 11, pnginfo.filter_method);
1155 put_8bit(tempbuff + 12, pnginfo.interlace_method);
1156 error = write_chunk(fp, tempbuff, PNG_CN_IHDR, 13);
1157 if (error != png_error::NONE)
1158 return error;
1159
1160 // write the PLTE chunk
1161 if (pnginfo.num_palette > 0)
1162 error = write_chunk(fp, pnginfo.palette.get(), PNG_CN_PLTE, pnginfo.num_palette * 3);
1163 if (error != png_error::NONE)
1164 return error;
1165
1166 // write a single IDAT chunk
1167 error = write_deflated_chunk(fp, pnginfo.image.get(), PNG_CN_IDAT, pnginfo.height * (compute_rowbytes(pnginfo) + 1));
1168 if (error != png_error::NONE)
1169 return error;
1170
1171 // write TEXT chunks
1172 std::vector<std::uint8_t> textbuf;
1173 for (png_info::png_text const &text : pnginfo.textlist)
1174 {
1175 try { textbuf.resize(text.first.length() + 1 + text.second.length()); }
1176 catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
1177 std::uint8_t *dst(&textbuf[0]);
1178
1179 // convert keyword to ISO-8859-1
1180 char const *const kwend(text.first.c_str() + text.first.length());
1181 for (char const *src = text.first.c_str(); kwend > src; ++dst)
1182 {
1183 char32_t ch;
1184 int const len(uchar_from_utf8(&ch, src, kwend - src));
1185 if (0 >= len)
1186 break;
1187 *dst = std::uint8_t(ch);
1188 src += len;
1189 }
1190
1191 // NUL separator between keword and text
1192 *dst++ = 0;
1193
1194 // convert text to ISO-8859-1
1195 char const *const textend(text.second.c_str() + text.second.length());
1196 for (char const *src = text.second.c_str(); textend > src; ++dst)
1197 {
1198 char32_t ch;
1199 int const len(uchar_from_utf8(&ch, src, textend - src));
1200 if (0 >= len)
1201 break;
1202 *dst = std::uint8_t(ch);
1203 src += len;
1204 }
1205
1206 error = write_chunk(fp, &textbuf[0], PNG_CN_tEXt, dst - &textbuf[0]);
1207 if (error != png_error::NONE)
1208 return error;
1209 }
1210
1211 // write an IEND chunk
1212 return write_chunk(fp, nullptr, PNG_CN_IEND, 0);
1213 }
1214
1215
png_write_bitmap(core_file & fp,png_info * info,bitmap_t const & bitmap,int palette_length,const rgb_t * palette)1216 png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
1217 {
1218 // use a dummy pnginfo if none passed to us
1219 png_info pnginfo;
1220 if (info == nullptr)
1221 info = &pnginfo;
1222
1223 // write the PNG signature
1224 if (fp.write(PNG_SIGNATURE, sizeof(PNG_SIGNATURE)) != sizeof(PNG_SIGNATURE))
1225 return png_error::FILE_ERROR;
1226
1227 /* write the rest of the PNG data */
1228 return write_png_stream(fp, *info, bitmap, palette_length, palette);
1229 }
1230
1231
1232
1233 /********************************************************************************
1234
1235 MNG write functions
1236
1237 ********************************************************************************/
1238
1239 /**
1240 * @fn png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
1241 *
1242 * @brief Mng capture start.
1243 *
1244 * @param [in,out] fp If non-null, the fp.
1245 * @param [in,out] bitmap The bitmap.
1246 * @param rate The framerate.
1247 *
1248 * @return A png_error.
1249 */
1250
mng_capture_start(core_file & fp,bitmap_t & bitmap,unsigned rate)1251 png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
1252 {
1253 uint8_t mhdr[28];
1254
1255 if (fp.write(MNG_Signature, 8) != 8)
1256 return png_error::FILE_ERROR;
1257
1258 memset(mhdr, 0, 28);
1259 put_32bit(mhdr + 0, bitmap.width());
1260 put_32bit(mhdr + 4, bitmap.height());
1261 put_32bit(mhdr + 8, rate);
1262 put_32bit(mhdr + 24, 0x0041); // Simplicity profile - frame count and play time unspecified because we don't know at this stage
1263 return write_chunk(fp, mhdr, MNG_CN_MHDR, 28);
1264 }
1265
1266 /**
1267 * @fn png_error mng_capture_frame(core_file &fp, png_info *info, bitmap_t &bitmap, int palette_length, const rgb_t *palette)
1268 *
1269 * @brief Mng capture frame.
1270 *
1271 * @param [in,out] fp If non-null, the fp.
1272 * @param [in,out] info If non-null, the information.
1273 * @param [in,out] bitmap The bitmap.
1274 * @param palette_length Length of the palette.
1275 * @param palette The palette.
1276 *
1277 * @return A png_error.
1278 */
1279
mng_capture_frame(core_file & fp,png_info & info,bitmap_t const & bitmap,int palette_length,const rgb_t * palette)1280 png_error mng_capture_frame(core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
1281 {
1282 return write_png_stream(fp, info, bitmap, palette_length, palette);
1283 }
1284
1285 /**
1286 * @fn png_error mng_capture_stop(core_file &fp)
1287 *
1288 * @brief Mng capture stop.
1289 *
1290 * @param [in,out] fp If non-null, the fp.
1291 *
1292 * @return A png_error.
1293 */
1294
mng_capture_stop(core_file & fp)1295 png_error mng_capture_stop(core_file &fp)
1296 {
1297 return write_chunk(fp, nullptr, MNG_CN_MEND, 0);
1298 }
1299
1300 } // namespace util
1301