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