1 /*
2 ** m_png.cpp
3 ** Routines for manipulating PNG files.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2002-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 // HEADER FILES ------------------------------------------------------------
36 
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <zlib.h>
40 #ifdef _MSC_VER
41 #include <malloc.h>		// for alloca()
42 #endif
43 
44 #include "m_crc32.h"
45 #include "m_swap.h"
46 #include "c_cvars.h"
47 #include "r_defs.h"
48 #include "v_video.h"
49 #include "m_png.h"
50 #include "templates.h"
51 #include "files.h"
52 
53 // MACROS ------------------------------------------------------------------
54 
55 // The maximum size of an IDAT chunk ZDoom will write. This is also the
56 // size of the compression buffer it allocates on the stack.
57 #define PNG_WRITE_SIZE	32768
58 
59 // Set this to 1 to use a simple heuristic to select the filter to apply
60 // for each row of RGB image saves. As it turns out, it seems no filtering
61 // is the best for Doom screenshots, no matter what the heuristic might
62 // determine, so that's why this is 0 here.
63 #define USE_FILTER_HEURISTIC 0
64 
65 // TYPES -------------------------------------------------------------------
66 
67 struct IHDR
68 {
69 	DWORD		Width;
70 	DWORD		Height;
71 	BYTE		BitDepth;
72 	BYTE		ColorType;
73 	BYTE		Compression;
74 	BYTE		Filter;
75 	BYTE		Interlace;
76 };
77 
PNGHandle(FILE * file)78 PNGHandle::PNGHandle (FILE *file) : File(0), bDeleteFilePtr(true), ChunkPt(0)
79 {
80 	File = new FileReader(file);
81 }
82 
PNGHandle(FileReader * file)83 PNGHandle::PNGHandle (FileReader *file) : File(file), bDeleteFilePtr(false), ChunkPt(0) {}
~PNGHandle()84 PNGHandle::~PNGHandle ()
85 {
86 	for (unsigned int i = 0; i < TextChunks.Size(); ++i)
87 	{
88 		delete[] TextChunks[i];
89 	}
90 	if (bDeleteFilePtr)
91 	{
92 		delete File;
93 	}
94 }
95 
96 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
97 
98 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
99 
100 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
101 
102 static inline void MakeChunk (void *where, DWORD type, size_t len);
103 static inline void StuffPalette (const PalEntry *from, BYTE *to);
104 static bool WriteIDAT (FILE *file, const BYTE *data, int len);
105 static void UnfilterRow (int width, BYTE *dest, BYTE *stream, BYTE *prev, int bpp);
106 static void UnpackPixels (int width, int bytesPerRow, int bitdepth, const BYTE *rowin, BYTE *rowout, bool grayscale);
107 
108 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
109 
110 // PUBLIC DATA DEFINITIONS -------------------------------------------------
111 
112 CUSTOM_CVAR(Int, png_level, 5, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
113 {
114 	if (self < 0)
115 		self = 0;
116 	else if (self > 9)
117 		self = 9;
118 }
119 CVAR(Float, png_gamma, 0.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
120 
121 // PRIVATE DATA DEFINITIONS ------------------------------------------------
122 
123 // CODE --------------------------------------------------------------------
124 
125 //==========================================================================
126 //
127 // M_CreatePNG
128 //
129 // Passed a newly-created file, writes the PNG signature and IHDR, gAMA, and
130 // PLTE chunks. Returns true if everything went as expected.
131 //
132 //==========================================================================
133 
M_CreatePNG(FILE * file,const BYTE * buffer,const PalEntry * palette,ESSType color_type,int width,int height,int pitch)134 bool M_CreatePNG (FILE *file, const BYTE *buffer, const PalEntry *palette,
135 				  ESSType color_type, int width, int height, int pitch)
136 {
137 	BYTE work[8 +				// signature
138 			  12+2*4+5 +		// IHDR
139 			  12+4 +			// gAMA
140 			  12+256*3];		// PLTE
141 	DWORD *const sig = (DWORD *)&work[0];
142 	IHDR *const ihdr = (IHDR *)&work[8 + 8];
143 	DWORD *const gama = (DWORD *)((BYTE *)ihdr + 2*4+5 + 12);
144 	BYTE *const plte = (BYTE *)gama + 4 + 12;
145 	size_t work_len;
146 
147 	sig[0] = MAKE_ID(137,'P','N','G');
148 	sig[1] = MAKE_ID(13,10,26,10);
149 
150 	ihdr->Width = BigLong(width);
151 	ihdr->Height = BigLong(height);
152 	ihdr->BitDepth = 8;
153 	ihdr->ColorType = color_type == SS_PAL ? 3 : 2;
154 	ihdr->Compression = 0;
155 	ihdr->Filter = 0;
156 	ihdr->Interlace = 0;
157 	MakeChunk (ihdr, MAKE_ID('I','H','D','R'), 2*4+5);
158 
159 	// Assume a display exponent of 2.2 (100000/2.2 ~= 45454.5)
160 	*gama = BigLong (int (45454.5f * (png_gamma == 0.f ? Gamma : png_gamma)));
161 	MakeChunk (gama, MAKE_ID('g','A','M','A'), 4);
162 
163 	if (color_type == SS_PAL)
164 	{
165 		StuffPalette (palette, plte);
166 		MakeChunk (plte, MAKE_ID('P','L','T','E'), 256*3);
167 		work_len = sizeof(work);
168 	}
169 	else
170 	{
171 		work_len = sizeof(work) - (12+256*3);
172 	}
173 
174 	if (fwrite (work, 1, work_len, file) != work_len)
175 		return false;
176 
177 	return M_SaveBitmap (buffer, color_type, width, height, pitch, file);
178 }
179 
180 //==========================================================================
181 //
182 // M_CreateDummyPNG
183 //
184 // Like M_CreatePNG, but the image is always a grayscale 1x1 black square.
185 //
186 //==========================================================================
187 
M_CreateDummyPNG(FILE * file)188 bool M_CreateDummyPNG (FILE *file)
189 {
190 	static const BYTE dummyPNG[] =
191 	{
192 		137,'P','N','G',13,10,26,10,
193 		0,0,0,13,'I','H','D','R',
194 		0,0,0,1,0,0,0,1,8,0,0,0,0,0x3a,0x7e,0x9b,0x55,
195 		0,0,0,10,'I','D','A','T',
196 		104,222,99,96,0,0,0,2,0,1,0x9f,0x65,0x0e,0x18
197 	};
198 	return fwrite (dummyPNG, 1, sizeof(dummyPNG), file) == sizeof(dummyPNG);
199 }
200 
201 
202 //==========================================================================
203 //
204 // M_FinishPNG
205 //
206 // Writes an IEND chunk to a PNG file. The file is left opened.
207 //
208 //==========================================================================
209 
M_FinishPNG(FILE * file)210 bool M_FinishPNG (FILE *file)
211 {
212 	static const BYTE iend[12] = { 0,0,0,0,73,69,78,68,174,66,96,130 };
213 	return fwrite (iend, 1, 12, file) == 12;
214 }
215 
216 //==========================================================================
217 //
218 // M_AppendPNGChunk
219 //
220 // Writes a PNG-compliant chunk to the file.
221 //
222 //==========================================================================
223 
M_AppendPNGChunk(FILE * file,DWORD chunkID,const BYTE * chunkData,DWORD len)224 bool M_AppendPNGChunk (FILE *file, DWORD chunkID, const BYTE *chunkData, DWORD len)
225 {
226 	DWORD head[2] = { BigLong((unsigned int)len), chunkID };
227 	DWORD crc;
228 
229 	if (fwrite (head, 1, 8, file) == 8 &&
230 		(len == 0 || fwrite (chunkData, 1, len, file) == len))
231 	{
232 		crc = CalcCRC32 ((BYTE *)&head[1], 4);
233 		if (len != 0)
234 		{
235 			crc = AddCRC32 (crc, chunkData, len);
236 		}
237 		crc = BigLong((unsigned int)crc);
238 		return fwrite (&crc, 1, 4, file) == 4;
239 	}
240 	return false;
241 }
242 
243 //==========================================================================
244 //
245 // M_AppendPNGText
246 //
247 // Appends a PNG tEXt chunk to the file
248 //
249 //==========================================================================
250 
M_AppendPNGText(FILE * file,const char * keyword,const char * text)251 bool M_AppendPNGText (FILE *file, const char *keyword, const char *text)
252 {
253 	struct { DWORD len, id; char key[80]; } head;
254 	int len = (int)strlen (text);
255 	int keylen = MIN ((int)strlen (keyword), 79);
256 	DWORD crc;
257 
258 	head.len = BigLong(len + keylen + 1);
259 	head.id = MAKE_ID('t','E','X','t');
260 	memset (&head.key, 0, sizeof(head.key));
261 	strncpy (head.key, keyword, keylen);
262 	head.key[keylen] = 0;
263 
264 	if ((int)fwrite (&head, 1, keylen + 9, file) == keylen + 9 &&
265 		(int)fwrite (text, 1, len, file) == len)
266 	{
267 		crc = CalcCRC32 ((BYTE *)&head+4, keylen + 5);
268 		if (len != 0)
269 		{
270 			crc = AddCRC32 (crc, (BYTE *)text, len);
271 		}
272 		crc = BigLong((unsigned int)crc);
273 		return fwrite (&crc, 1, 4, file) == 4;
274 	}
275 	return false;
276 }
277 
278 //==========================================================================
279 //
280 // M_FindPNGChunk
281 //
282 // Finds a chunk in a PNG file. The file pointer will be positioned at the
283 // beginning of the chunk data, and its length will be returned. A return
284 // value of 0 indicates the chunk was either not present or had 0 length.
285 // This means there is no way to conclusively determine if a chunk is not
286 // present in a PNG file with this function, but since we're only
287 // interested in chunks with content, that's okay. The file pointer will
288 // be left sitting at the start of the chunk's data if it was found.
289 //
290 //==========================================================================
291 
M_FindPNGChunk(PNGHandle * png,DWORD id)292 unsigned int M_FindPNGChunk (PNGHandle *png, DWORD id)
293 {
294 	png->ChunkPt = 0;
295 	return M_NextPNGChunk (png, id);
296 }
297 
298 //==========================================================================
299 //
300 // M_NextPNGChunk
301 //
302 // Like M_FindPNGChunk, but it starts it search at the current chunk.
303 //
304 //==========================================================================
305 
M_NextPNGChunk(PNGHandle * png,DWORD id)306 unsigned int M_NextPNGChunk (PNGHandle *png, DWORD id)
307 {
308 	for ( ; png->ChunkPt < png->Chunks.Size(); ++png->ChunkPt)
309 	{
310 		if (png->Chunks[png->ChunkPt].ID == id)
311 		{ // Found the chunk
312 			png->File->Seek (png->Chunks[png->ChunkPt++].Offset, SEEK_SET);
313 			return png->Chunks[png->ChunkPt - 1].Size;
314 		}
315 	}
316 	return 0;
317 }
318 
319 //==========================================================================
320 //
321 // M_GetPNGText
322 //
323 // Finds a PNG text chunk with the given signature and returns a pointer
324 // to a NULL-terminated string if present. Returns NULL on failure.
325 //
326 //==========================================================================
327 
M_GetPNGText(PNGHandle * png,const char * keyword)328 char *M_GetPNGText (PNGHandle *png, const char *keyword)
329 {
330 	unsigned int i;
331 	size_t keylen, textlen;
332 
333 	for (i = 0; i < png->TextChunks.Size(); ++i)
334 	{
335 		if (strncmp (keyword, png->TextChunks[i], 80) == 0)
336 		{
337 			// Woo! A match was found!
338 			keylen = MIN<size_t> (80, strlen (keyword) + 1);
339 			textlen = strlen (png->TextChunks[i] + keylen) + 1;
340 			char *str = new char[textlen];
341 			strcpy (str, png->TextChunks[i] + keylen);
342 			return str;
343 		}
344 	}
345 	return NULL;
346 }
347 
348 // This version copies it to a supplied buffer instead of allocating a new one.
349 
M_GetPNGText(PNGHandle * png,const char * keyword,char * buffer,size_t buffsize)350 bool M_GetPNGText (PNGHandle *png, const char *keyword, char *buffer, size_t buffsize)
351 {
352 	unsigned int i;
353 	size_t keylen;
354 
355 	for (i = 0; i < png->TextChunks.Size(); ++i)
356 	{
357 		if (strncmp (keyword, png->TextChunks[i], 80) == 0)
358 		{
359 			// Woo! A match was found!
360 			keylen = MIN<size_t> (80, strlen (keyword) + 1);
361 			strncpy (buffer, png->TextChunks[i] + keylen, buffsize);
362 			return true;
363 		}
364 	}
365 	return false;
366 }
367 
368 //==========================================================================
369 //
370 // M_VerifyPNG
371 //
372 // Returns a PNGHandle if the file is a PNG or NULL if not. CRC checking of
373 // chunks is not done in order to save time.
374 //
375 //==========================================================================
376 
M_VerifyPNG(FILE * file)377 PNGHandle *M_VerifyPNG (FILE *file)
378 {
379 	PNGHandle::Chunk chunk;
380 	FileReader *filer;
381 	PNGHandle *png;
382 	DWORD data[2];
383 	bool sawIDAT = false;
384 
385 	if (fread (&data, 1, 8, file) != 8)
386 	{
387 		return NULL;
388 	}
389 	if (data[0] != MAKE_ID(137,'P','N','G') || data[1] != MAKE_ID(13,10,26,10))
390 	{ // Does not have PNG signature
391 		return NULL;
392 	}
393 	if (fread (&data, 1, 8, file) != 8)
394 	{
395 		return NULL;
396 	}
397 	if (data[1] != MAKE_ID('I','H','D','R'))
398 	{ // IHDR must be the first chunk
399 		return NULL;
400 	}
401 
402 	// It looks like a PNG so far, so start creating a PNGHandle for it
403 	png = new PNGHandle (file);
404 	filer = png->File;
405 	chunk.ID = data[1];
406 	chunk.Offset = 16;
407 	chunk.Size = BigLong((unsigned int)data[0]);
408 	png->Chunks.Push (chunk);
409 	filer->Seek (16, SEEK_SET);
410 
411 	while (filer->Seek (chunk.Size + 4, SEEK_CUR) == 0)
412 	{
413 		// If the file ended before an IEND was encountered, it's not a PNG.
414 		if (filer->Read (&data, 8) != 8)
415 		{
416 			break;
417 		}
418 		// An IEND chunk terminates the PNG and must be empty
419 		if (data[1] == MAKE_ID('I','E','N','D'))
420 		{
421 			if (data[0] == 0 && sawIDAT)
422 			{
423 				return png;
424 			}
425 			break;
426 		}
427 		// A PNG must include an IDAT chunk
428 		if (data[1] == MAKE_ID('I','D','A','T'))
429 		{
430 			sawIDAT = true;
431 		}
432 		chunk.ID = data[1];
433 		chunk.Offset = ftell (file);
434 		chunk.Size = BigLong((unsigned int)data[0]);
435 		png->Chunks.Push (chunk);
436 
437 		// If this is a text chunk, also record its contents.
438 		if (data[1] == MAKE_ID('t','E','X','t'))
439 		{
440 			char *str = new char[chunk.Size + 1];
441 
442 			if (filer->Read (str, chunk.Size) != (long)chunk.Size)
443 			{
444 				delete[] str;
445 				break;
446 			}
447 			str[chunk.Size] = 0;
448 			png->TextChunks.Push (str);
449 			chunk.Size = 0;		// Don't try to seek past its contents again.
450 		}
451 	}
452 
453 	delete png;
454 	return NULL;
455 }
456 
457 //==========================================================================
458 //
459 // M_FreePNG
460 //
461 // Just deletes the PNGHandle. The file is not closed.
462 //
463 //==========================================================================
464 
M_FreePNG(PNGHandle * png)465 void M_FreePNG (PNGHandle *png)
466 {
467 	delete png;
468 }
469 
470 //==========================================================================
471 //
472 // ReadIDAT
473 //
474 // Reads image data out of a PNG
475 //
476 //==========================================================================
477 
M_ReadIDAT(FileReader * file,BYTE * buffer,int width,int height,int pitch,BYTE bitdepth,BYTE colortype,BYTE interlace,unsigned int chunklen)478 bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitch,
479 				 BYTE bitdepth, BYTE colortype, BYTE interlace, unsigned int chunklen)
480 {
481 	// Uninterlaced images are treated as a conceptual eighth pass by these tables.
482 	static const BYTE passwidthshift[8] =  { 3, 3, 2, 2, 1, 1, 0, 0 };
483 	static const BYTE passheightshift[8] = { 3, 3, 3, 2, 2, 1, 1, 0 };
484 	static const BYTE passrowoffset[8] =   { 0, 0, 4, 0, 2, 0, 1, 0 };
485 	static const BYTE passcoloffset[8] =   { 0, 4, 0, 2, 0, 1, 0, 0 };
486 
487 	Byte *inputLine, *prev, *curr, *adam7buff[3], *bufferend;
488 	Byte chunkbuffer[4096];
489 	z_stream stream;
490 	int err;
491 	int i, pass, passbuff, passpitch, passwidth;
492 	bool lastIDAT;
493 	int bytesPerRowIn, bytesPerRowOut;
494 	int bytesPerPixel;
495 	bool initpass;
496 
497 	switch (colortype)
498 	{
499 	case 2:		bytesPerPixel = 3;		break;		// RGB
500 	case 4:		bytesPerPixel = 2;		break;		// LA
501 	case 6:		bytesPerPixel = 4;		break;		// RGBA
502 	default:	bytesPerPixel = 1;		break;
503 	}
504 
505 	bytesPerRowOut = width * bytesPerPixel;
506 	i = 4 + bytesPerRowOut * 2;
507 	if (interlace)
508 	{
509 		i += bytesPerRowOut * 2;
510 	}
511 	inputLine = (Byte *)alloca (i);
512 	adam7buff[0] = inputLine + 4 + bytesPerRowOut;
513 	adam7buff[1] = adam7buff[0] + bytesPerRowOut;
514 	adam7buff[2] = adam7buff[1] + bytesPerRowOut;
515 	bufferend = buffer + pitch * height;
516 
517 	stream.next_in = Z_NULL;
518 	stream.avail_in = 0;
519 	stream.zalloc = Z_NULL;
520 	stream.zfree = Z_NULL;
521 	err = inflateInit (&stream);
522 	if (err != Z_OK)
523 	{
524 		return false;
525 	}
526 	lastIDAT = false;
527 	initpass = true;
528 	pass = interlace ? 0 : 7;
529 
530 	// Silence GCC warnings. Due to initpass being true, these will be set
531 	// before they're used, but it doesn't know that.
532 	curr = prev = 0;
533 	passwidth = passpitch = bytesPerRowIn = 0;
534 	passbuff = 0;
535 
536 	while (err != Z_STREAM_END && pass < 8 - interlace)
537 	{
538 		if (initpass)
539 		{
540 			int rowoffset, coloffset;
541 
542 			initpass = false;
543 			pass--;
544 			do
545 			{
546 				pass++;
547 				rowoffset = passrowoffset[pass];
548 				coloffset = passcoloffset[pass];
549 			}
550 			while ((rowoffset >= height || coloffset >= width) && pass < 7);
551 			if (pass == 7 && interlace)
552 			{
553 				break;
554 			}
555 			passwidth = (width + (1 << passwidthshift[pass]) - 1 - coloffset) >> passwidthshift[pass];
556 			prev = adam7buff[0];
557 			passbuff = 1;
558 			memset (prev, 0, passwidth * bytesPerPixel);
559 			switch (bitdepth)
560 			{
561 			case 8:		bytesPerRowIn = passwidth * bytesPerPixel;	break;
562 			case 4:		bytesPerRowIn = (passwidth+1)/2;			break;
563 			case 2:		bytesPerRowIn = (passwidth+3)/4;			break;
564 			case 1:		bytesPerRowIn = (passwidth+7)/8;			break;
565 			default:	return false;
566 			}
567 			curr = buffer + rowoffset*pitch + coloffset*bytesPerPixel;
568 			passpitch = pitch << passheightshift[pass];
569 			stream.next_out = inputLine;
570 			stream.avail_out = bytesPerRowIn + 1;
571 		}
572 		if (stream.avail_in == 0 && chunklen > 0)
573 		{
574 			stream.next_in = chunkbuffer;
575 			stream.avail_in = (uInt)file->Read (chunkbuffer, MIN<long>(chunklen,sizeof(chunkbuffer)));
576 			chunklen -= stream.avail_in;
577 		}
578 
579 		err = inflate (&stream, Z_SYNC_FLUSH);
580 		if (err != Z_OK && err != Z_STREAM_END)
581 		{ // something unexpected happened
582 			inflateEnd (&stream);
583 			return false;
584 		}
585 
586 		if (stream.avail_out == 0)
587 		{
588 			if (pass >= 6)
589 			{
590 				// Store pixels directly into the output buffer
591 				UnfilterRow (bytesPerRowIn, curr, inputLine, prev, bytesPerPixel);
592 				prev = curr;
593 			}
594 			else
595 			{
596 				const BYTE *in;
597 				BYTE *out;
598 				int colstep, x;
599 
600 				// Store pixels into a temporary buffer
601 				UnfilterRow (bytesPerRowIn, adam7buff[passbuff], inputLine, prev, bytesPerPixel);
602 				prev = adam7buff[passbuff];
603 				passbuff ^= 1;
604 				in = prev;
605 				if (bitdepth < 8)
606 				{
607 					UnpackPixels (passwidth, bytesPerRowIn, bitdepth, in, adam7buff[2], colortype == 0);
608 					in = adam7buff[2];
609 				}
610 				// Distribute pixels into the output buffer
611 				out = curr;
612 				colstep = bytesPerPixel << passwidthshift[pass];
613 				switch (bytesPerPixel)
614 				{
615 				case 1:
616 					for (x = passwidth; x > 0; --x)
617 					{
618 						*out = *in;
619 						out += colstep;
620 						in += 1;
621 					}
622 					break;
623 
624 				case 2:
625 					for (x = passwidth; x > 0; --x)
626 					{
627 						*(WORD *)out = *(WORD *)in;
628 						out += colstep;
629 						in += 2;
630 					}
631 					break;
632 
633 				case 3:
634 					for (x = passwidth; x > 0; --x)
635 					{
636 						out[0] = in[0];
637 						out[1] = in[1];
638 						out[2] = in[2];
639 						out += colstep;
640 						in += 3;
641 					}
642 					break;
643 
644 				case 4:
645 					for (x = passwidth; x > 0; --x)
646 					{
647 						*(DWORD *)out = *(DWORD *)in;
648 						out += colstep;
649 						in += 4;
650 					}
651 					break;
652 				}
653 			}
654 			if ((curr += passpitch) >= bufferend)
655 			{
656 				++pass;
657 				initpass = true;
658 			}
659 			stream.next_out = inputLine;
660 			stream.avail_out = bytesPerRowIn + 1;
661 		}
662 
663 		if (chunklen == 0 && !lastIDAT)
664 		{
665 			DWORD x[3];
666 
667 			if (file->Read (x, 12) != 12)
668 			{
669 				lastIDAT = true;
670 			}
671 			else if (x[2] != MAKE_ID('I','D','A','T'))
672 			{
673 				lastIDAT = true;
674 			}
675 			else
676 			{
677 				chunklen = BigLong((unsigned int)x[1]);
678 			}
679 		}
680 	}
681 
682 	inflateEnd (&stream);
683 
684 	if (bitdepth < 8)
685 	{
686 		// Noninterlaced images must be unpacked completely.
687 		// Interlaced images only need their final pass unpacked.
688 		passpitch = pitch << interlace;
689 		for (curr = buffer + pitch * interlace; curr <= prev; curr += passpitch)
690 		{
691 			UnpackPixels (width, bytesPerRowIn, bitdepth, curr, curr, colortype == 0);
692 		}
693 	}
694 	return true;
695 }
696 
697 // PRIVATE CODE ------------------------------------------------------------
698 
699 
700 //==========================================================================
701 //
702 // MakeChunk
703 //
704 // Prepends the chunk length and type and appends the chunk's CRC32.
705 // There must be 8 bytes available before the chunk passed and 4 bytes
706 // after the chunk.
707 //
708 //==========================================================================
709 
MakeChunk(void * where,DWORD type,size_t len)710 static inline void MakeChunk (void *where, DWORD type, size_t len)
711 {
712 	BYTE *const data = (BYTE *)where;
713 	*(DWORD *)(data - 8) = BigLong ((unsigned int)len);
714 	*(DWORD *)(data - 4) = type;
715 	*(DWORD *)(data + len) = BigLong ((unsigned int)CalcCRC32 (data-4, (unsigned int)(len+4)));
716 }
717 
718 //==========================================================================
719 //
720 // StuffPalette
721 //
722 // Converts 256 4-byte palette entries to 3 bytes each.
723 //
724 //==========================================================================
725 
StuffPalette(const PalEntry * from,BYTE * to)726 static void StuffPalette (const PalEntry *from, BYTE *to)
727 {
728 	for (int i = 256; i > 0; --i)
729 	{
730 		to[0] = from->r;
731 		to[1] = from->g;
732 		to[2] = from->b;
733 		from += 1;
734 		to += 3;
735 	}
736 }
737 
738 //==========================================================================
739 //
740 // CalcSum
741 //
742 //
743 //==========================================================================
744 
CalcSum(Byte * row,int len)745 DWORD CalcSum(Byte *row, int len)
746 {
747 	DWORD sum = 0;
748 
749 	while (len-- != 0)
750 	{
751 		sum += (char)*row++;
752 	}
753 	return sum;
754 }
755 
756 //==========================================================================
757 //
758 // SelectFilter
759 //
760 // Performs the heuristic recommended by the PNG spec to decide the
761 // (hopefully) best filter to use for this row. To quate:
762 //
763 //    Select the filter that gives the smallest sum of absolute values of
764 //    outputs. (Consider the output bytes as signed differences for this
765 //    test.)
766 //
767 //==========================================================================
768 
769 #if USE_FILTER_HEURISTIC
SelectFilter(Byte row[5][1+MAXWIDTH * 3],Byte prior[MAXWIDTH * 3],int width)770 static int SelectFilter(Byte row[5][1 + MAXWIDTH*3], Byte prior[MAXWIDTH*3], int width)
771 {
772 	// As it turns out, it seems no filtering is the best for Doom screenshots,
773 	// no matter what the heuristic might determine.
774 	return 0;
775 	DWORD sum;
776 	DWORD bestsum;
777 	int bestfilter;
778 	int x;
779 
780 	width *= 3;
781 
782 	// The first byte of each row holds the filter type, filled in by the caller.
783 	// However, the prior row does not contain a filter type, since it's always 0.
784 
785 	bestsum = 0;
786 	bestfilter = 0;
787 
788 	// None
789 	for (x = 1; x <= width; ++x)
790 	{
791 		bestsum += abs((char)row[0][x]);
792 	}
793 
794 	// Sub
795 	row[1][1] = row[0][1];
796 	row[1][2] = row[0][2];
797 	row[1][3] = row[0][3];
798 	sum = abs((char)row[0][1]) + abs((char)row[0][2]) + abs((char)row[0][3]);
799 	for (x = 4; x <= width; ++x)
800 	{
801 		row[1][x] = row[0][x] - row[0][x - 3];
802 		sum += abs((char)row[1][x]);
803 		if (sum >= bestsum)
804 		{ // This isn't going to be any better.
805 			break;
806 		}
807 	}
808 	if (sum < bestsum)
809 	{
810 		bestsum = sum;
811 		bestfilter = 1;
812 	}
813 
814 	// Up
815 	sum = 0;
816 	for (x = 1; x <= width; ++x)
817 	{
818 		row[2][x] = row[0][x] - prior[x - 1];
819 		sum += abs((char)row[2][x]);
820 		if (sum >= bestsum)
821 		{ // This isn't going to be any better.
822 			break;
823 		}
824 	}
825 	if (sum < bestsum)
826 	{
827 		bestsum = sum;
828 		bestfilter = 2;
829 	}
830 
831 	// Average
832 	row[3][1] = row[0][1] - prior[0] / 2;
833 	row[3][2] = row[0][2] - prior[1] / 2;
834 	row[3][3] = row[0][3] - prior[2] / 2;
835 	sum = abs((char)row[3][1]) + abs((char)row[3][2]) + abs((char)row[3][3]);
836 	for (x = 4; x <= width; ++x)
837 	{
838 		row[3][x] = row[0][x] - (row[0][x - 3] + prior[x - 1]) / 2;
839 		sum += (char)row[3][x];
840 		if (sum >= bestsum)
841 		{ // This isn't going to be any better.
842 			break;
843 		}
844 	}
845 	if (sum < bestsum)
846 	{
847 		bestsum = sum;
848 		bestfilter = 3;
849 	}
850 
851 	// Paeth
852 	row[4][1] = row[0][1] - prior[0];
853 	row[4][2] = row[0][2] - prior[1];
854 	row[4][3] = row[0][3] - prior[2];
855 	sum = abs((char)row[4][1]) + abs((char)row[4][2]) + abs((char)row[4][3]);
856 	for (x = 4; x <= width; ++x)
857 	{
858 		Byte a = row[0][x - 3];
859 		Byte b = prior[x - 1];
860 		Byte c = prior[x - 4];
861 		int p = a + b - c;
862 		int pa = abs(p - a);
863 		int pb = abs(p - b);
864 		int pc = abs(p - c);
865 		if (pa <= pb && pa <= pc)
866 		{
867 			row[4][x] = row[0][x] - a;
868 		}
869 		else if (pb <= pc)
870 		{
871 			row[4][x] = row[0][x] - b;
872 		}
873 		else
874 		{
875 			row[4][x] = row[0][x] - c;
876 		}
877 		sum += (char)row[4][x];
878 		if (sum >= bestsum)
879 		{ // This isn't going to be any better.
880 			break;
881 		}
882 	}
883 	if (sum < bestsum)
884 	{
885 		bestfilter = 4;
886 	}
887 
888 	return bestfilter;
889 }
890 #else
891 #define SelectFilter(x,y,z)		0
892 #endif
893 
894 //==========================================================================
895 //
896 // M_SaveBitmap
897 //
898 // Given a bitmap, creates one or more IDAT chunks in the given file.
899 // Returns true on success.
900 //
901 //==========================================================================
902 
M_SaveBitmap(const BYTE * from,ESSType color_type,int width,int height,int pitch,FILE * file)903 bool M_SaveBitmap(const BYTE *from, ESSType color_type, int width, int height, int pitch, FILE *file)
904 {
905 #if USE_FILTER_HEURISTIC
906 	Byte prior[MAXWIDTH*3];
907 	Byte temprow[5][1 + MAXWIDTH*3];
908 #else
909 	Byte temprow[1][1 + MAXWIDTH*3];
910 #endif
911 	Byte buffer[PNG_WRITE_SIZE];
912 	z_stream stream;
913 	int err;
914 	int y;
915 
916 	stream.next_in = Z_NULL;
917 	stream.avail_in = 0;
918 	stream.zalloc = Z_NULL;
919 	stream.zfree = Z_NULL;
920 	err = deflateInit (&stream, png_level);
921 
922 	if (err != Z_OK)
923 	{
924 		return false;
925 	}
926 
927 	y = height;
928 	stream.next_out = buffer;
929 	stream.avail_out = sizeof(buffer);
930 
931 	temprow[0][0] = 0;
932 #if USE_FILTER_HEURISTIC
933 	temprow[1][0] = 1;
934 	temprow[2][0] = 2;
935 	temprow[3][0] = 3;
936 	temprow[4][0] = 4;
937 
938 	// Fill the prior row with 0 for RGB images. Paletted is always filter 0,
939 	// so it doesn't need this.
940 	if (color_type != SS_PAL)
941 	{
942 		memset(prior, 0, width * 3);
943 	}
944 #endif
945 
946 	while (y-- > 0 && err == Z_OK)
947 	{
948 		switch (color_type)
949 		{
950 		case SS_PAL:
951 			memcpy(&temprow[0][1], from, width);
952 			// always use filter type 0 for paletted images
953 			stream.next_in = temprow[0];
954 			stream.avail_in = width + 1;
955 			break;
956 
957 		case SS_RGB:
958 			memcpy(&temprow[0][1], from, width*3);
959 			stream.next_in = temprow[SelectFilter(temprow, prior, width)];
960 			stream.avail_in = width * 3 + 1;
961 			break;
962 
963 		case SS_BGRA:
964 			for (int x = 0; x < width; ++x)
965 			{
966 				temprow[0][x*3 + 1] = from[x*4 + 2];
967 				temprow[0][x*3 + 2] = from[x*4 + 1];
968 				temprow[0][x*3 + 3] = from[x*4];
969 			}
970 			stream.next_in = temprow[SelectFilter(temprow, prior, width)];
971 			stream.avail_in = width * 3 + 1;
972 			break;
973 		}
974 #if USE_FILTER_HEURISTIC
975 		if (color_type != SS_PAL)
976 		{
977 			// Save this row for filter calculations on the next row.
978 			memcpy (prior, &temprow[0][1], stream.avail_in - 1);
979 		}
980 #endif
981 
982 		from += pitch;
983 
984 		err = deflate (&stream, (y == 0) ? Z_FINISH : 0);
985 		if (err != Z_OK)
986 		{
987 			break;
988 		}
989 		while (stream.avail_out == 0)
990 		{
991 			if (!WriteIDAT (file, buffer, sizeof(buffer)))
992 			{
993 				return false;
994 			}
995 			stream.next_out = buffer;
996 			stream.avail_out = sizeof(buffer);
997 			if (stream.avail_in != 0)
998 			{
999 				err = deflate (&stream, (y == 0) ? Z_FINISH : 0);
1000 				if (err != Z_OK)
1001 				{
1002 					break;
1003 				}
1004 			}
1005 		}
1006 	}
1007 
1008 	while (err == Z_OK)
1009 	{
1010 		err = deflate (&stream, Z_FINISH);
1011 		if (err != Z_OK)
1012 		{
1013 			break;
1014 		}
1015 		if (stream.avail_out == 0)
1016 		{
1017 			if (!WriteIDAT (file, buffer, sizeof(buffer)))
1018 			{
1019 				return false;
1020 			}
1021 			stream.next_out = buffer;
1022 			stream.avail_out = sizeof(buffer);
1023 		}
1024 	}
1025 
1026 	deflateEnd (&stream);
1027 
1028 	if (err != Z_STREAM_END)
1029 	{
1030 		return false;
1031 	}
1032 	return WriteIDAT (file, buffer, sizeof(buffer)-stream.avail_out);
1033 }
1034 
1035 //==========================================================================
1036 //
1037 // WriteIDAT
1038 //
1039 // Writes a single IDAT chunk to the file. Returns true on success.
1040 //
1041 //==========================================================================
1042 
WriteIDAT(FILE * file,const BYTE * data,int len)1043 static bool WriteIDAT (FILE *file, const BYTE *data, int len)
1044 {
1045 	DWORD foo[2], crc;
1046 
1047 	foo[0] = BigLong (len);
1048 	foo[1] = MAKE_ID('I','D','A','T');
1049 	crc = CalcCRC32 ((BYTE *)&foo[1], 4);
1050 	crc = BigLong ((unsigned int)AddCRC32 (crc, data, len));
1051 
1052 	if (fwrite (foo, 1, 8, file) != 8 ||
1053 		fwrite (data, 1, len, file) != (size_t)len ||
1054 		fwrite (&crc, 1, 4, file) != 4)
1055 	{
1056 		return false;
1057 	}
1058 	return true;
1059 }
1060 
1061 //==========================================================================
1062 //
1063 // UnfilterRow
1064 //
1065 // Unfilters the given row. Unknown filter types are silently ignored.
1066 // bpp is bytes per pixel, not bits per pixel.
1067 // width is in bytes, not pixels.
1068 //
1069 //==========================================================================
1070 
UnfilterRow(int width,BYTE * dest,BYTE * row,BYTE * prev,int bpp)1071 void UnfilterRow (int width, BYTE *dest, BYTE *row, BYTE *prev, int bpp)
1072 {
1073 	int x;
1074 
1075 	switch (*row++)
1076 	{
1077 	case 1:		// Sub
1078 		x = bpp;
1079 		do
1080 		{
1081 			*dest++ = *row++;
1082 		}
1083 		while (--x);
1084 		for (x = width - bpp; x > 0; --x)
1085 		{
1086 			*dest = *row++ + *(dest - bpp);
1087 			dest++;
1088 		}
1089 		break;
1090 
1091 	case 2:		// Up
1092 		x = width;
1093 		do
1094 		{
1095 			*dest++ = *row++ + *prev++;
1096 		}
1097 		while (--x);
1098 		break;
1099 
1100 	case 3:		// Average
1101 		x = bpp;
1102 		do
1103 		{
1104 			*dest++ = *row++ + (*prev++)/2;
1105 		}
1106 		while (--x);
1107 		for (x = width - bpp; x > 0; --x)
1108 		{
1109 			*dest = *row++ + (BYTE)((unsigned(*(dest - bpp)) + unsigned(*prev++)) >> 1);
1110 			dest++;
1111 		}
1112 		break;
1113 
1114 	case 4:		// Paeth
1115 		x = bpp;
1116 		do
1117 		{
1118 			*dest++ = *row++ + *prev++;
1119 		}
1120 		while (--x);
1121 		for (x = width - bpp; x > 0; --x)
1122 		{
1123 			int a, b, c, pa, pb, pc;
1124 
1125 			a = *(dest - bpp);
1126 			b = *(prev);
1127 			c = *(prev - bpp);
1128 			pa = b - c;
1129 			pb = a - c;
1130 			pc = abs (pa + pb);
1131 			pa = abs (pa);
1132 			pb = abs (pb);
1133 			*dest = *row + (BYTE)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c);
1134 			dest++;
1135 			row++;
1136 			prev++;
1137 		}
1138 		break;
1139 
1140 	default:	// Treat everything else as filter type 0 (none)
1141 		memcpy (dest, row, width);
1142 		break;
1143 	}
1144 }
1145 
1146 //==========================================================================
1147 //
1148 // UnpackPixels
1149 //
1150 // Unpacks a row of pixels whose depth is less than 8 so that each pixel
1151 // occupies a single byte. The outrow must be "width" bytes long.
1152 // "bytesPerRow" is the number of bytes for the packed row. The in and out
1153 // rows may overlap, but only if rowin == rowout.
1154 //
1155 //==========================================================================
1156 
UnpackPixels(int width,int bytesPerRow,int bitdepth,const BYTE * rowin,BYTE * rowout,bool grayscale)1157 static void UnpackPixels (int width, int bytesPerRow, int bitdepth, const BYTE *rowin, BYTE *rowout, bool grayscale)
1158 {
1159 	const BYTE *in;
1160 	BYTE *out;
1161 	BYTE pack;
1162 	int lastbyte;
1163 
1164 	assert(bitdepth == 1 || bitdepth == 2 || bitdepth == 4);
1165 
1166 	out = rowout + width;
1167 	in = rowin + bytesPerRow;
1168 
1169 	switch (bitdepth)
1170 	{
1171 	case 1:
1172 
1173 		lastbyte = width & 7;
1174 		if (lastbyte != 0)
1175 		{
1176 			in--;
1177 			pack = *in;
1178 			out -= lastbyte;
1179 			out[0] = (pack >> 7) & 1;
1180 			if (lastbyte >= 2) out[1] = (pack >> 6) & 1;
1181 			if (lastbyte >= 3) out[2] = (pack >> 5) & 1;
1182 			if (lastbyte >= 4) out[3] = (pack >> 4) & 1;
1183 			if (lastbyte >= 5) out[4] = (pack >> 3) & 1;
1184 			if (lastbyte >= 6) out[5] = (pack >> 2) & 1;
1185 			if (lastbyte == 7) out[6] = (pack >> 1) & 1;
1186 		}
1187 
1188 		while (in-- > rowin)
1189 		{
1190 			pack = *in;
1191 			out -= 8;
1192 			out[0] = (pack >> 7) & 1;
1193 			out[1] = (pack >> 6) & 1;
1194 			out[2] = (pack >> 5) & 1;
1195 			out[3] = (pack >> 4) & 1;
1196 			out[4] = (pack >> 3) & 1;
1197 			out[5] = (pack >> 2) & 1;
1198 			out[6] = (pack >> 1) & 1;
1199 			out[7] = pack & 1;
1200 		}
1201 		break;
1202 
1203 	case 2:
1204 
1205 		lastbyte = width & 3;
1206 		if (lastbyte != 0)
1207 		{
1208 			in--;
1209 			pack = *in;
1210 			out -= lastbyte;
1211 			out[0] = pack >> 6;
1212 			if (lastbyte >= 2) out[1] = (pack >> 4) & 3;
1213 			if (lastbyte == 3) out[2] = (pack >> 2) & 3;
1214 		}
1215 
1216 		while (in-- > rowin)
1217 		{
1218 			pack = *in;
1219 			out -= 4;
1220 			out[0] = pack >> 6;
1221 			out[1] = (pack >> 4) & 3;
1222 			out[2] = (pack >> 2) & 3;
1223 			out[3] = pack & 3;
1224 		}
1225 		break;
1226 
1227 	case 4:
1228 		lastbyte = width & 1;
1229 		if (lastbyte != 0)
1230 		{
1231 			in--;
1232 			pack = *in;
1233 			out -= lastbyte;
1234 			out[0] = pack >> 4;
1235 		}
1236 
1237 		while (in-- > rowin)
1238 		{
1239 			pack = *in;
1240 			out -= 2;
1241 			out[0] = pack >> 4;
1242 			out[1] = pack & 15;
1243 		}
1244 		break;
1245 	}
1246 
1247 	// Expand grayscale to 8bpp
1248 	if (grayscale)
1249 	{
1250 		// Put the 2-bit lookup table on the stack, since it's probably already
1251 		// in a cache line.
1252 		union
1253 		{
1254 			uint32 bits2l;
1255 			BYTE bits2[4];
1256 		};
1257 
1258 		out = rowout + width;
1259 		switch (bitdepth)
1260 		{
1261 		case 1:
1262 			while (--out >= rowout)
1263 			{
1264 				// 1 becomes -1 (0xFF), and 0 remains untouched.
1265 				*out = 0 - *out;
1266 			}
1267 			break;
1268 
1269 		case 2:
1270 			bits2l = MAKE_ID(0x00,0x55,0xAA,0xFF);
1271 			while (--out >= rowout)
1272 			{
1273 				*out = bits2[*out];
1274 			}
1275 			break;
1276 
1277 		case 4:
1278 			while (--out >= rowout)
1279 			{
1280 				*out |= (*out << 4);
1281 			}
1282 			break;
1283 		}
1284 	}
1285 }
1286