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