From 21869a6c08a1f88e1a3d11aeda01564831879893 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 15 Aug 2006 04:34:35 +0000 Subject: [PATCH] - Added JPEG texture support, courtesy of Ken's Picture Library. I will probably switch to the IJG library once I pare it down. (Ken's code is 18K of C source but does not support progressive JPEG. The IJG library is over a megabyte of source and supports pretty much everything you would ever need ever.) - Fixed endianness issue in FTextureManager::CreateTexture(). - Added support for interlaced PNGs. Now ZDoom is a mostly complete PNG reader. The only thing missing is 48-bit RGB and 16-bit grayscale support, which are just wastes of bits here, but also less likely to appear than an interlaced PNG. (However, if you are using interlaced PNGs for textures, then you are needlessly wasting space, since the image won't display progressively.) - Fixed: Writing named screenshots didn't work. SVN r292 (trunk) --- docs/rh-log.txt | 15 ++ src/g_game.cpp | 8 +- src/kplib.cpp | 571 ++++++++++++++++++++++++++++++++++++++++++++++++ src/m_misc.cpp | 4 +- src/m_misc.h | 2 +- src/m_png.cpp | 254 ++++++++++++++------- src/r_data.cpp | 154 ++++++++++++- src/r_data.h | 19 ++ zdoom.vcproj | 8 +- 9 files changed, 941 insertions(+), 94 deletions(-) create mode 100644 src/kplib.cpp diff --git a/docs/rh-log.txt b/docs/rh-log.txt index cad1fc30c..39e85c915 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,18 @@ +August 14, 2006 +- Added JPEG texture support, courtesy of Ken's Picture Library. I will + probably switch to the IJG library once I pare it down. (Ken's code is 18K + of C source but does not support progressive JPEG. The IJG library is over + a megabyte of source and supports pretty much everything you would ever + need ever.) +- Fixed endianness issue in FTextureManager::CreateTexture(). +- Added support for interlaced PNGs. Now ZDoom is a mostly complete PNG + reader. The only thing missing is 48-bit RGB and 16-bit grayscale support, + which are just wastes of bits here, but also less likely to appear than + an interlaced PNG. (However, if you are using interlaced PNGs for textures, + then you are needlessly wasting space, since the image won't display + progressively.) +- Fixed: Writing named screenshots didn't work. + August 12, 2006 - Added support for truecolor PNG textures. They still get resampled to the global palette, but at least they are visible now. diff --git a/src/g_game.cpp b/src/g_game.cpp index 653cef3fe..b51046803 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -187,7 +187,7 @@ FString savegamefile; char savedescription[SAVESTRINGSIZE]; // [RH] Name of screenshot file to generate (usually NULL) -char *shotfile; +FString shotfile; AActor* bodyque[BODYQUESIZE]; int bodyqueslot; @@ -890,11 +890,7 @@ void G_Ticker () break; case ga_screenshot: M_ScreenShot (shotfile); - if (shotfile) - { - free (shotfile); - shotfile = NULL; - } + shotfile = ""; gameaction = ga_nothing; break; case ga_fullconsole: diff --git a/src/kplib.cpp b/src/kplib.cpp new file mode 100644 index 000000000..923f93409 --- /dev/null +++ b/src/kplib.cpp @@ -0,0 +1,571 @@ +/************************************************************************************************** +KPLIB.C: Ken's Picture LIBrary written by Ken Silverman +Copyright (c) 1998-2005 Ken Silverman +Ken Silverman's official web site: http://www.advsys.net/ken + +This source file includes routines for decompression of the following picture formats: + JPG,PNG,GIF,PCX,TGA,BMP,CEL +It also includes code to handle ZIP decompression. + +Brief history: +1998?: Wrote KPEG, a JPEG viewer for DOS +2000: Wrote KPNG, a PNG viewer for DOS +2001: Combined KPEG&KPNG, ported to Visual C, and made it into a nice library called KPLIB.C +2002: Added support for: TGA,GIF,CEL,ZIP +2003: Added support for: BMP +05/18/2004: Added support for 8/24 bit PCX + +I offer this code to the community for the benefit of Jonathon Fowler's Duke3D port. + +-Ken S. +**************************************************************************************************/ +// [RH] Removed everything but JPEG support. + +#include +#include +#include +#include +#include +#include + +#include "m_swap.h" +#include "m_fixed.h" + +#if !defined(_WIN32) && !defined(__DOS__) +#include +static __inline unsigned long _lrotl (unsigned long i, int sh) + { return((i>>(-sh))|(i< +#endif + +#if !defined(max) +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#if !defined(min) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#if defined(__GNUC__) +#define _inline inline +#endif + + //use GCC-specific extension to force symbol name to be something in particular to override underscoring. + +static int bytesperline, xres, yres, globxoffs, globyoffs; +static unsigned char *frameplace; + +static const int pow2mask[32] = +{ + 0x00000000,0x00000001,0x00000003,0x00000007, + 0x0000000f,0x0000001f,0x0000003f,0x0000007f, + 0x000000ff,0x000001ff,0x000003ff,0x000007ff, + 0x00000fff,0x00001fff,0x00003fff,0x00007fff, + 0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff, + 0x00ffffff,0x01ffffff,0x03ffffff,0x07ffffff, + 0x0fffffff,0x1fffffff,0x3fffffff,0x7fffffff, +}; + +//Initialized tables (can't be in union) +//jpg: png: +// crmul 16384 abstab10 4096 +// cbmul 16384 hxbit 472 +// dct 4608 pow2mask 128* +// colclip 4096 +// colclipup8 4096 +// colclipup16 4096 +// unzig 256 +// pow2mask 128* +// dcflagor 64 + +int palcol[256], paleng; +unsigned char coltype, bitdepth; + +//============================ KPEGILIB begins =============================== + + //11/01/2000: This code was originally from KPEG.C + // All non 32-bit color drawing was removed + // "Motion" JPG code was removed + // A lot of parameters were added to kpeg() for library usage +static int clipxdim, clipydim; + +static int hufquickcnt[8]; +static int hufmaxatbit[8][20], hufvalatbit[8][20], hufcnt[8]; +static unsigned char hufnumatbit[8][20], huftable[8][256]; +static unsigned char gnumcomponents, dcflagor[64]; +static int gcompid[4], gcomphsamp[4], gcompvsamp[4], gcompquantab[4]; +static int lcompid[4], lcompdc[4], lcompac[4]; +static int lcomphsamp[4], lcompvsamp[4], lcomphvsamp[4], lcompquantab[4]; +static int lcomphsampmask[4], lcomphsampshift[4], lcompvsampshift[4]; +static int quantab[4][64], dct[19][64], lastdc[4], unzig[64]; +static const unsigned char pow2char[8] = {1,2,4,8,16,32,64,128}; + +#define SQRT2 23726566 //(sqrt(2))<<24 +#define C182 31000253 //(cos(PI/8)*2)<<24 +#define C18S22 43840978 //(cos(PI/8)*sqrt(2)*2)<<24 +#define C38S22 18159528 //(cos(PI*3/8)*sqrt(2)*2)<<24 +static const int cosqr16[8] = //cosqr16[i] = ((cos(PI*i/16)*sqrt(2))<<24); + {23726566,23270667,21920489,19727919,16777216,13181774,9079764,4628823}; + +// [RH] Moved the bigger tables into a dynamically allocated struct so that +// they don't waste space if a JPEG is never loaded. +struct kpegtables +{ + int hufquickval[8][1024], hufquickbits[8][1024]; + int colclip[1024], colclipup8[1024], colclipup16[1024]; + int crmul[4096], cbmul[4096]; +}; +static kpegtables *kpeg; + +static void initkpeg () +{ + int i, x, y; + + kpeg = new kpegtables; + + x = 0; //Back & forth diagonal pattern (aligning bytes for best compression) + for(i=0;i<16;i+=2) + { + for(y=7;y>=0;y--) + if ((unsigned)(i-y) < (unsigned)8) unzig[x++] = (y<<3)+i-y; + for(y=0;y<8;y++) + if ((unsigned)(i+1-y) < (unsigned)8) unzig[x++] = (y<<3)+i+1-y; + } + for(i=63;i>=0;i--) dcflagor[i] = (unsigned char)(1<<(unzig[i]>>3)); + + for(i=0;i<128;i++) kpeg->colclip[i] = i+128; + for(i=128;i<512;i++) kpeg->colclip[i] = 255; + for(i=512;i<896;i++) kpeg->colclip[i] = 0; + for(i=896;i<1024;i++) kpeg->colclip[i] = i-896; + for(i=0;i<1024;i++) + { + kpeg->colclipup8[i] = (kpeg->colclip[i]<<8); + kpeg->colclipup16[i] = (kpeg->colclip[i]<<16)+0xff000000; //Hack: set alphas to 255 + } + + for(i=0;i<2048;i++) + { + kpeg->crmul[(i<<1)+0] = (i-1024)*1470104; //1.402*1048576 + kpeg->crmul[(i<<1)+1] = (i-1024)*-748830; //-0.71414*1048576 + kpeg->cbmul[(i<<1)+0] = (i-1024)*-360857; //-0.34414*1048576 + kpeg->cbmul[(i<<1)+1] = (i-1024)*1858077; //1.772*1048576 + } + + memset((void *)&dct[16][0],0,64*2*sizeof(dct[0][0])); + memset((void *)&dct[18][0],0xa0,64*sizeof(dct[0][0])); +} + +static void huffgetval (int index, int curbits, int num, int *daval, int *dabits) +{ + int b, v, pow2, *hmax; + + hmax = &hufmaxatbit[index][0]; + pow2 = pow2mask[curbits-1]+1; + if (num&pow2) v = 1; else v = 0; + for(b=1;b<=16;b++) + { + if (v < hmax[b]) + { + *dabits = b; + *daval = huftable[index][hufvalatbit[index][b]+v]; + return; + } + pow2 >>= 1; v <<= 1; + if (num&pow2) v++; + } + *dabits = 16; + *daval = 0; +} + +int kpegrend (const char *kfilebuf, int kfilength, + unsigned char *daframeplace, int dabytesperline, int daxres, int dayres, + int daglobxoffs, int daglobyoffs) +{ + int i, j, v, leng, xdim = 0, ydim = 0, index, prec, restartinterval; + int restartcnt, num, curbits, x, y, z, dctcnt, c, cc, daval, dabits; + int xx, yy, zz, xxx, yyy, r, g, b, t0, t1, t2, t3, t4, t5, t6, t7; + int yv, cr = 0, cb = 0, *dc, *dc2, xxxend, yyyend; + int *hqval, *hqbits, hqcnt, *quanptr; + unsigned char ch, marker, numbits, lnumcomponents, dcflag, *p; + const unsigned char *kfileptr; + + if (kpeg == NULL) { initkpeg(); } + + kfileptr = (unsigned char *)kfilebuf; + + if (*(unsigned short *)kfileptr == 0xd8ff) kfileptr += 2; + else return(-1); //"%s is not a JPEG file\n",filename + + restartinterval = 0; + for(i=0;i<4;i++) lastdc[i] = 0; + for(i=0;i<8;i++) hufcnt[i] = 0; + + coltype = 0; bitdepth = 8; //For PNGOUT + do + { + ch = *kfileptr++; if (ch != 255) continue; + marker = *kfileptr++; + leng = ((int)kfileptr[0]<<8)+(int)kfileptr[1]-2; + kfileptr += 2; + switch(marker) + { + case 0xc0: case 0xc1: case 0xc2: + //processit! + numbits = *kfileptr++; + + ydim = ((int)kfileptr[0]<<8)+(int)kfileptr[1]; + xdim = ((int)kfileptr[2]<<8)+(int)kfileptr[3]; + //printf("%s: %ld / %ld = %ld\n",filename,xdim*ydim*3,kfilength,(xdim*ydim*3)/kfilength); + + frameplace = daframeplace; + bytesperline = dabytesperline; + xres = daxres; + yres = dayres; + globxoffs = daglobxoffs; + globyoffs = daglobyoffs; + + gnumcomponents = kfileptr[4]; + kfileptr += 5; + for(z=0;z>4); + gcompvsamp[z] = (kfileptr[1]&15); + gcompquantab[z] = kfileptr[2]; + kfileptr += 3; + } + break; + case 0xc4: //Huffman table + do + { + ch = *kfileptr++; leng--; + if (ch >= 16) { index = ch-12; } + else { index = ch; } + memcpy((void *)&hufnumatbit[index][1],(void *)kfileptr,16); kfileptr += 16; + leng -= 16; + + v = 0; hufcnt[index] = 0; + hufquickcnt[index] = 0; c = 0; + for(b=1;b<=16;b++) + { + hufmaxatbit[index][b] = v+hufnumatbit[index][b]; + hufvalatbit[index][b] = hufcnt[index]-v; + memcpy((void *)&huftable[index][hufcnt[index]],(void *)kfileptr,(int)hufnumatbit[index][b]); + if (b <= 10) + for(c=0;c0;j--) + { + kpeg->hufquickval[index][hufquickcnt[index]] = huftable[index][hufcnt[index]+c]; + kpeg->hufquickbits[index][hufquickcnt[index]] = b; + hufquickcnt[index]++; + } + kfileptr += hufnumatbit[index][b]; + leng -= hufnumatbit[index][b]; + hufcnt[index] += hufnumatbit[index][b]; + v = ((v+hufnumatbit[index][b])<<1); + } + + } while (leng > 0); + break; + case 0xdb: + do + { + ch = *kfileptr++; leng--; + index = (ch&15); + prec = (ch>>4); + for(z=0;z<64;z++) + { + v = (int)(*kfileptr++); + if (prec) v = (v<<8)+((int)(*kfileptr++)); + v <<= 19; + if (unzig[z]&7) v = MulScale24(v,cosqr16[unzig[z]&7]); + if (unzig[z]>>3) v = MulScale24(v,cosqr16[unzig[z]>>3]); + if (index) v >>= 6; + quantab[index][unzig[z]] = v; + } + leng -= 64; + if (prec) leng -= 64; + } while (leng > 0); + break; + case 0xdd: + restartinterval = (((int)kfileptr[0])<<8)+((int)kfileptr[1]); + kfileptr += leng; + break; + case 0xda: case 0xd9: + if ((xdim <= 0) || (ydim <= 0)) return(-1); + + lnumcomponents = *kfileptr++; + if (lnumcomponents > 1) coltype = 2; + for(z=0;z>4); + lcompac[z] = (kfileptr[1]&15); + kfileptr += 2; + for(zz=0;zz>4); + //Al = (kfileptr[2]&15); + kfileptr += 3; + + if ((hufcnt[0] == 0) || (hufcnt[4] == 0)) return(-1); + + clipxdim = min(xdim+globxoffs,xres); + clipydim = min(ydim+globyoffs,yres); + + xx = max(globxoffs,0); xxx = min(globxoffs+xdim,xres); + yy = max(globyoffs,0); yyy = min(globyoffs+ydim,yres); + if ((xx >= xres) || (yy >= yres) || (xxx <= 0) || (yyy <= 0)) return(0); + + restartcnt = restartinterval; marker = 0xd0; + num = 0; curbits = 0; x = 0; y = 0; + while (1) + { + if (kfileptr-(unsigned char *)kfilebuf >= kfilength) + lnumcomponents = 0; //rest of file is missing! + + dctcnt = 0; + for(c=0;chufquickval[lcompac[c]+4][0]; + hqbits = &kpeg->hufquickbits[lcompac[c]+4][0]; + hqcnt = hufquickcnt[lcompac[c]+4]; + quanptr = &quantab[lcompquantab[c]][0]; + for(cc=lcomphvsamp[c];cc>0;cc--) + { + dc = &dct[dctcnt][0]; + + //Get DC + while (curbits < 16) //Getbits + { + ch = *kfileptr++; if (ch == 255) kfileptr++; + num = (num<<8)+((int)ch); curbits += 8; + } + + i = ((num>>(curbits-10))&1023); + if (i < hufquickcnt[lcompdc[c]]) + { dabits = kpeg->hufquickbits[lcompdc[c]][i]; daval = kpeg->hufquickval[lcompdc[c]][i]; } + else + huffgetval(lcompdc[c],curbits,num,&daval,&dabits); + + curbits -= dabits; + if (daval) + { + while (curbits < 16) //Getbits + { + ch = *kfileptr++; if (ch == 255) kfileptr++; + num = (num<<8)+((int)ch); curbits += 8; + } + + v = ((unsigned)num >> (curbits-daval)) & pow2mask[daval]; + if (v <= pow2mask[daval-1]) v -= pow2mask[daval]; + lastdc[c] += v; + curbits -= daval; + } + dc[0] = lastdc[c]*quanptr[0]; + + //Get AC + memset((void *)&dc[1],0,63*4); + dcflag = 1; + for(z=1;z<64;z++) + { + while (curbits < 16) //Getbits + { + ch = *kfileptr++; if (ch == 255) kfileptr++; + num = (num<<8)+((int)ch); curbits += 8; + } + + i = ((num>>(curbits-10))&1023); + if (i < hqcnt) + { daval = hqval[i]; curbits -= hqbits[i]; } + else + { + huffgetval(lcompac[c]+4,curbits,num,&daval,&dabits); + curbits -= dabits; + } + + if (!daval) break; + z += (daval>>4); if (z >= 64) break; + daval &= 15; + + while (curbits < 16) //Getbits + { + ch = *kfileptr++; if (ch == 255) kfileptr++; + num = (num<<8)+((int)ch); curbits += 8; + } + + v = ((unsigned)num >> (curbits-daval)) & pow2mask[daval]; + if (v <= pow2mask[daval-1]) v -= pow2mask[daval]; + dcflag |= dcflagor[z]; + dc[unzig[z]] = v*quanptr[unzig[z]]; + curbits -= daval; + } + + for(z=0;z<8;z++,dc+=8) + { + if (!(dcflag&pow2char[z])) continue; + t3 = dc[2] + dc[6]; + t2 = (MulScale32(dc[2]-dc[6],SQRT2<<6)<<2) - t3; + t4 = dc[0] + dc[4]; t5 = dc[0] - dc[4]; + t0 = t4+t3; t3 = t4-t3; t1 = t5+t2; t2 = t5-t2; + t4 = (MulScale32(dc[5]-dc[3]+dc[1]-dc[7],C182<<6)<<2); + t7 = dc[1] + dc[7] + dc[5] + dc[3]; + t6 = (MulScale32(dc[3]-dc[5],C18S22<<5)<<3) + t4 - t7; + t5 = (MulScale32(dc[1]+dc[7]-dc[5]-dc[3],SQRT2<<6)<<2) - t6; + t4 = (MulScale32(dc[1]-dc[7],C38S22<<6)<<2) - t4 + t5; + dc[0] = t0+t7; dc[7] = t0-t7; dc[1] = t1+t6; dc[6] = t1-t6; + dc[2] = t2+t5; dc[5] = t2-t5; dc[4] = t3+t4; dc[3] = t3-t4; + } + dc = &dct[dctcnt][0]; + for(z=7;z>=0;z--,dc++) + { + t3 = dc[2<<3] + dc[6<<3]; + t2 = (MulScale32(dc[2<<3]-dc[6<<3],SQRT2<<6)<<2) - t3; + t4 = dc[0<<3] + dc[4<<3]; t5 = dc[0<<3] - dc[4<<3]; + t0 = t4+t3; t3 = t4-t3; t1 = t5+t2; t2 = t5-t2; + t4 = (MulScale32(dc[5<<3]-dc[3<<3]+dc[1<<3]-dc[7<<3],C182<<6)<<2); + t7 = dc[1<<3] + dc[7<<3] + dc[5<<3] + dc[3<<3]; + t6 = (MulScale32(dc[3<<3]-dc[5<<3],C18S22<<5)<<3) + t4 - t7; + t5 = (MulScale32(dc[1<<3]+dc[7<<3]-dc[5<<3]-dc[3<<3],SQRT2<<6)<<2) - t6; + t4 = (MulScale32(dc[1<<3]-dc[7<<3],C38S22<<6)<<2) - t4 + t5; + dc[0<<3] = t0+t7; dc[7<<3] = t0-t7; dc[1<<3] = t1+t6; dc[6<<3] = t1-t6; + dc[2<<3] = t2+t5; dc[5<<3] = t2-t5; dc[4<<3] = t3+t4; dc[3<<3] = t3-t4; + } + + dctcnt++; + } + } + + dctcnt = 0; dc = &dct[18][0]; dc2 = &dct[16][0]; + r = g = b = 0; cc = 0; + for(yy=0;yy<(lcompvsamp[0]<<3);yy+=8) + for(xx=0;xx<(lcomphsamp[0]<<3);xx+=8,dctcnt++) + { + yyy = y+yy+globyoffs; if ((unsigned)yyy >= (unsigned)clipydim) continue; + xxx = x+xx+globxoffs; if ((unsigned)xxx >= (unsigned)clipxdim) continue; + p = yyy*bytesperline + xxx*4 + frameplace; + if (lnumcomponents > 0) dc = &dct[dctcnt][0]; + if (lnumcomponents > 1) dc2 = &dct[lcomphvsamp[0]][((yy>>lcompvsampshift[0])<<3)+(xx>>lcomphsampshift[0])]; + xxxend = min(clipxdim-(x+xx+globxoffs),8); + yyyend = min(clipydim-(y+yy+globyoffs),8); + if ((lcomphsamp[0] == 1) && (xxxend == 8)) + { + for(yyy=0;yyy>13)&~1; + cb = (dc2[xxx]>>13)&~1; + ((int *)p)[xxx] = kpeg->colclipup16[(unsigned)(yv+kpeg->crmul[cr+2048])>>22]+ + kpeg->colclipup8[(unsigned)(yv+kpeg->crmul[cr+2049]+kpeg->cbmul[cb+2048])>>22]+ + kpeg->colclip[(unsigned)(yv+kpeg->cbmul[cb+2049])>>22]; + } + p += bytesperline; + dc += 8; + if (!((yyy+1)&(lcompvsamp[0]-1))) dc2 += 8; + } + } + else if ((lcomphsamp[0] == 2) && (xxxend == 8)) + { + for(yyy=0;yyy>1)+64]>>13)&~1; + cb = (dc2[(xxx>>1)]>>13)&~1; + i = kpeg->crmul[cr+2049]+kpeg->cbmul[cb+2048]; + cr = kpeg->crmul[cr+2048]; + cb = kpeg->cbmul[cb+2049]; + ((int *)p)[xxx] = kpeg->colclipup16[(unsigned)(yv+cr)>>22]+ + kpeg->colclipup8[(unsigned)(yv+i)>>22]+ + kpeg->colclip[(unsigned)(yv+cb)>>22]; + yv = dc[xxx+1]; + ((int *)p)[xxx+1] = kpeg->colclipup16[(unsigned)(yv+cr)>>22]+ + kpeg->colclipup8[(unsigned)(yv+i)>>22]+ + kpeg->colclip[(unsigned)(yv+cb)>>22]; + } + p += bytesperline; + dc += 8; + if (!((yyy+1)&(lcompvsamp[0]-1))) dc2 += 8; + } + } + else + { + for(yyy=0;yyy>13)&~1; + cb = (dc2[i]>>13)&~1; + i++; + } + ((int *)p)[xxx] = kpeg->colclipup16[(unsigned)(yv+kpeg->crmul[cr+2048])>>22]+ + kpeg->colclipup8[(unsigned)(yv+kpeg->crmul[cr+2049]+kpeg->cbmul[cb+2048])>>22]+ + kpeg->colclip[(unsigned)(yv+kpeg->cbmul[cb+2049])>>22]; + } + p += bytesperline; + dc += 8; + if (!((yyy+1)&(lcompvsamp[0]-1))) dc2 += 8; + } + } + } + + if (lnumcomponents) //do only when not EOF... + { + restartcnt--; + if (!restartcnt) + { + kfileptr += 1-(curbits>>3); curbits = 0; + if ((kfileptr[-2] != 255) || (kfileptr[-1] != marker)) kfileptr--; + marker++; if (marker >= 0xd8) marker = 0xd0; + restartcnt = restartinterval; + for(i=0;i<4;i++) lastdc[i] = 0; + } + } + + x += (lcomphsamp[0]<<3); + if (x >= xdim) { x = 0; y += (lcompvsamp[0]<<3); if (y >= ydim) return(0); } + } + default: + kfileptr += leng; + break; + } + } while (kfileptr-(unsigned char *)kfilebuf < kfilength); + return(0); +} + +//============================== KPEGILIB ends ============================== diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 2a6e6c9e3..54757d32a 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -537,14 +537,14 @@ static BOOL FindFreeName (FString &fullname, const char *extension) return false; } -void M_ScreenShot (char *filename) +void M_ScreenShot (const char *filename) { FILE *file; FString autoname; bool writepcx = (stricmp (screenshot_type, "pcx") == 0); // PNG is the default // find a file name to save it to - if (filename == NULL) + if (filename == NULL || filename[0] == '\0') { #ifndef unix if (Args.CheckParm ("-cdrom")) diff --git a/src/m_misc.h b/src/m_misc.h index 64b02edd0..15dcc12ea 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -36,7 +36,7 @@ void M_FindResponseFile (void); // [RH] M_ScreenShot now accepts a filename parameter. // Pass a NULL to get the original behavior. -void M_ScreenShot (char *filename); +void M_ScreenShot (const char *filename); void M_LoadDefaults (); diff --git a/src/m_png.cpp b/src/m_png.cpp index 38e56ee5e..86baca2d5 100644 --- a/src/m_png.cpp +++ b/src/m_png.cpp @@ -100,7 +100,7 @@ static inline void StuffPalette (const PalEntry *from, BYTE *to); static bool StuffBitmap (const DCanvas *canvas, FILE *file); static bool WriteIDAT (FILE *file, const BYTE *data, int len); static void UnfilterRow (int width, BYTE *dest, BYTE *stream, BYTE *prev, int bpp); -static void UnpackPixels (int width, int bytesPerRow, int bitdepth, BYTE *row); +static void UnpackPixels (int width, int bytesPerRow, int bitdepth, const BYTE *rowin, BYTE *rowout); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- @@ -525,14 +525,21 @@ DCanvas *M_CreateCanvasFromPNG (PNGHandle *png) bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitch, BYTE bitdepth, BYTE colortype, BYTE interlace, unsigned int chunklen) { - Byte *inputLine, *prev, *curr; + // Uninterlaced images are treated as a conceptual eighth pass by these tables. + static const BYTE passwidthshift[8] = { 3, 3, 2, 2, 1, 1, 0, 0 }; + static const BYTE passheightshift[8] = { 3, 3, 3, 2, 2, 1, 1, 0 }; + static const BYTE passrowoffset[8] = { 0, 0, 4, 0, 2, 0, 1, 0 }; + static const BYTE passcoloffset[8] = { 0, 4, 0, 2, 0, 1, 0, 0 }; + + Byte *inputLine, *prev, *curr, *adam7buff[3], *bufferend; Byte chunkbuffer[4096]; z_stream stream; int err; - int y; + int i, pass, passbuff, passpitch, passwidth; bool lastIDAT; - int bytesPerRowIn; + int bytesPerRowIn, bytesPerRowOut; int bytesPerPixel; + bool initpass; switch (colortype) { @@ -541,23 +548,18 @@ bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitc case 6: bytesPerPixel = 4; break; // RGBA default: bytesPerPixel = 1; break; } - inputLine = (Byte *)alloca (1 + width*bytesPerPixel*2); - prev = inputLine + 1 + width*bytesPerPixel; - memset (prev, 0, width*bytesPerPixel); - if (bytesPerPixel == 1 && bitdepth != 8) - { // This is an invalid combination for PNG files - return false; - } - - switch (bitdepth) + bytesPerRowOut = width * bytesPerPixel; + i = 4 + bytesPerRowOut * 2; + if (interlace) { - case 8: bytesPerRowIn = width * bytesPerPixel; break; - case 4: bytesPerRowIn = (width+1)/2; break; - case 2: bytesPerRowIn = (width+3)/4; break; - case 1: bytesPerRowIn = (width+7)/8; break; - default: return false; + i += bytesPerRowOut * 2; } + inputLine = (Byte *)alloca (i); + adam7buff[0] = inputLine + 4 + bytesPerRowOut; + adam7buff[1] = adam7buff[0] + bytesPerRowOut; + adam7buff[2] = adam7buff[1] + bytesPerRowOut; + bufferend = buffer + pitch * height; stream.next_in = Z_NULL; stream.avail_in = 0; @@ -568,67 +570,166 @@ bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitc { return false; } - y = 0; - curr = buffer; - stream.next_out = inputLine; - stream.avail_out = bytesPerRowIn+1; lastIDAT = false; + initpass = true; + pass = interlace ? 0 : 7; - do + while (err != Z_STREAM_END && pass < 8 - interlace) { - while (err != Z_STREAM_END) + if (initpass) { - if (stream.avail_in == 0 && chunklen > 0) - { - stream.next_in = chunkbuffer; - stream.avail_in = (uInt)file->Read (chunkbuffer, MIN(chunklen,sizeof(chunkbuffer))); - chunklen -= stream.avail_in; - } + int rowoffset, coloffset; - err = inflate (&stream, Z_SYNC_FLUSH); - if (err != Z_OK && err != Z_STREAM_END) - { // something unexpected happened - inflateEnd (&stream); - return false; - } - - if (stream.avail_out == 0) + initpass = false; + pass--; + do { + pass++; + rowoffset = passrowoffset[pass]; + coloffset = passcoloffset[pass]; + } + while ((rowoffset >= height || coloffset >= width) && pass < 7); + if (pass == 7 && interlace) + { + break; + } + passwidth = (width + (1 << passwidthshift[pass]) - 1 - coloffset) >> passwidthshift[pass]; + prev = adam7buff[0]; + passbuff = 1; + memset (prev, 0, passwidth * bytesPerPixel); + switch (bitdepth) + { + case 8: bytesPerRowIn = passwidth * bytesPerPixel; break; + case 4: bytesPerRowIn = (passwidth+1)/2; break; + case 2: bytesPerRowIn = (passwidth+3)/4; break; + case 1: bytesPerRowIn = (passwidth+7)/8; break; + default: return false; + } + curr = buffer + rowoffset*pitch + coloffset*bytesPerPixel; + passpitch = pitch << passheightshift[pass]; + stream.next_out = inputLine; + stream.avail_out = bytesPerRowIn + 1; + } + if (stream.avail_in == 0 && chunklen > 0) + { + stream.next_in = chunkbuffer; + stream.avail_in = (uInt)file->Read (chunkbuffer, MIN(chunklen,sizeof(chunkbuffer))); + chunklen -= stream.avail_in; + } + + err = inflate (&stream, Z_SYNC_FLUSH); + if (err != Z_OK && err != Z_STREAM_END) + { // something unexpected happened + inflateEnd (&stream); + return false; + } + + if (stream.avail_out == 0) + { + if (pass >= 6) + { + // Store pixels directly into the output buffer UnfilterRow (bytesPerRowIn, curr, inputLine, prev, bytesPerPixel); prev = curr; - curr += pitch; - y++; - stream.next_out = inputLine; - stream.avail_out = bytesPerRowIn+1; } - - if (chunklen == 0 && !lastIDAT) + else { - DWORD x[3]; + const BYTE *in; + BYTE *out; + int colstep, x; - if (file->Read (x, 12) != 12) + // Store pixels into a temporary buffer + UnfilterRow (bytesPerRowIn, adam7buff[passbuff], inputLine, prev, bytesPerPixel); + prev = adam7buff[passbuff]; + passbuff ^= 1; + in = prev; + if (bitdepth < 8) { - lastIDAT = true; + UnpackPixels (passwidth, bytesPerRowIn, bitdepth, in, adam7buff[2]); + in = adam7buff[2]; } - else if (x[2] != MAKE_ID('I','D','A','T')) + // Distribute pixels into the output buffer + out = curr; + colstep = bytesPerPixel << passwidthshift[pass]; + switch (bytesPerPixel) { - lastIDAT = true; - } - else - { - chunklen = BigLong((unsigned int)x[1]); + case 1: + for (x = passwidth; x > 0; --x) + { + *out = *in; + out += colstep; + in += 1; + } + break; + + case 2: + for (x = passwidth; x > 0; --x) + { + *(WORD *)out = *(WORD *)in; + out += colstep; + in += 2; + } + break; + + case 3: + for (x = passwidth; x > 0; --x) + { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out += colstep; + in += 3; + } + break; + + case 4: + for (x = passwidth; x > 0; --x) + { + *(DWORD *)out = *(DWORD *)in; + out += colstep; + in += 4; + } + break; } } + if ((curr += passpitch) >= bufferend) + { + ++pass; + initpass = true; + } + stream.next_out = inputLine; + stream.avail_out = bytesPerRowIn + 1; + } + + if (chunklen == 0 && !lastIDAT) + { + DWORD x[3]; + + if (file->Read (x, 12) != 12) + { + lastIDAT = true; + } + else if (x[2] != MAKE_ID('I','D','A','T')) + { + lastIDAT = true; + } + else + { + chunklen = BigLong((unsigned int)x[1]); + } } - } while (err == Z_OK && y < height); + } inflateEnd (&stream); if (bitdepth < 8) { - for (curr = buffer; curr <= prev; curr += pitch) + // Noninterlaced images must be unpacked completely. + // Interlaced images only need their final pass unpacked. + passpitch = pitch << interlace; + for (curr = buffer + pitch * interlace; curr <= prev; curr += passpitch) { - UnpackPixels (width, bytesPerRowIn, bitdepth, curr); + UnpackPixels (width, bytesPerRowIn, bitdepth, curr, curr); } } return true; @@ -897,32 +998,33 @@ void UnfilterRow (int width, BYTE *dest, BYTE *row, BYTE *prev, int bpp) // // UnpackPixels // -// Unpacks a row of pixels whose depth is less than 8 into so that each -// pixel occupies a single byte. The packed pixels must be at the start -// of the row, and the row must be "width" bytes long. "bytesPerRow" is -// the number of bytes for the packed row. +// Unpacks a row of pixels whose depth is less than 8 so that each pixel +// occupies a single byte. The outrow must be "width" bytes long. +// "bytesPerRow" is the number of bytes for the packed row. The in and out +// rows may overlap, but only if rowin == rowout. // //========================================================================== -static void UnpackPixels (int width, int bytesPerRow, int bitdepth, BYTE *row) +static void UnpackPixels (int width, int bytesPerRow, int bitdepth, const BYTE *rowin, BYTE *rowout) { - BYTE *out, *in; + const BYTE *in; + BYTE *out; BYTE pack; int lastbyte; - out = row + width; - in = row + bytesPerRow; + out = rowout + width; + in = rowin + bytesPerRow; switch (bitdepth) { case 1: - lastbyte=width&7; - if (lastbyte!=0) + lastbyte = width & 7; + if (lastbyte != 0) { in--; pack = *in; - out-=lastbyte; + out -= lastbyte; out[0] = (pack >> 7) & 1; if (lastbyte >= 2) out[1] = (pack >> 6) & 1; if (lastbyte >= 3) out[2] = (pack >> 5) & 1; @@ -932,7 +1034,7 @@ static void UnpackPixels (int width, int bytesPerRow, int bitdepth, BYTE *row) if (lastbyte == 7) out[6] = (pack >> 1) & 1; } - while (in-- > row) + while (in-- > rowin) { pack = *in; out -= 8; @@ -949,18 +1051,18 @@ static void UnpackPixels (int width, int bytesPerRow, int bitdepth, BYTE *row) case 2: - lastbyte=width&3; - if (lastbyte!=0) + lastbyte = width & 3; + if (lastbyte != 0) { in--; pack = *in; - out-=lastbyte; + out -= lastbyte; out[0] = pack >> 6; if (lastbyte >= 2) out[1] = (pack >> 4) & 3; if (lastbyte == 3) out[2] = (pack >> 2) & 3; } - while (in-- > row) + while (in-- > rowin) { pack = *in; out -= 4; @@ -972,16 +1074,16 @@ static void UnpackPixels (int width, int bytesPerRow, int bitdepth, BYTE *row) break; case 4: - lastbyte=width&1; - if (lastbyte!=0) + lastbyte = width & 1; + if (lastbyte != 0) { in--; pack = *in; - out-=lastbyte; + out -= lastbyte; out[0] = pack >> 4; } - while (in-- > row) + while (in-- > rowin) { pack = *in; out -= 2; diff --git a/src/r_data.cpp b/src/r_data.cpp index 3fc4628c6..c5f8b5959 100644 --- a/src/r_data.cpp +++ b/src/r_data.cpp @@ -238,6 +238,7 @@ int FTextureManager::CreateTexture (int lumpnum, int usetype) { DWORD dw; WORD w[2]; + BYTE b[4]; } first4bytes; // Must check the length of the lump. Zero length flat markers (F1_START etc.) will come through here. @@ -250,7 +251,7 @@ int FTextureManager::CreateTexture (int lumpnum, int usetype) { FWadLump data = Wads.OpenLumpNum (lumpnum); - data >> first4bytes.dw; + data.Read (first4bytes.b, 4); if (first4bytes.dw == MAKE_ID('I','M','G','Z')) { type = t_imgz; @@ -263,11 +264,11 @@ int FTextureManager::CreateTexture (int lumpnum, int usetype) // This is most likely a PNG, but make sure. (Note that if the // first 4 bytes match, but later bytes don't, we assume it's // a corrupt PNG.) - data >> first4bytes.dw; + data.Read (first4bytes.b, 4); if (first4bytes.dw != MAKE_ID(13,10,26,10)) return -1; - data >> first4bytes.dw; + data.Read (first4bytes.b, 4); if (first4bytes.dw != MAKE_ID(0,0,0,13)) return -1; - data >> first4bytes.dw; + data.Read (first4bytes.b, 4); if (first4bytes.dw != MAKE_ID('I','H','D','R')) return -1; // The PNG looks valid so far. Check the IHDR to make sure it's a @@ -275,7 +276,7 @@ int FTextureManager::CreateTexture (int lumpnum, int usetype) data >> width >> height >> bitdepth >> colortype >> compression >> filter >> interlace; - if (compression != 0 || filter != 0 || interlace != 0) + if (compression != 0 || filter != 0 || interlace > 1) { return -1; } @@ -290,10 +291,11 @@ int FTextureManager::CreateTexture (int lumpnum, int usetype) // Just for completeness, make sure the PNG has something more than an // IHDR. - data >> first4bytes.dw >> first4bytes.dw; + data.Seek (4, SEEK_CUR); + data.Read (first4bytes.b, 4); if (first4bytes.dw == 0) { - data >> first4bytes.dw; + data.Read (first4bytes.b, 4); if (first4bytes.dw == MAKE_ID('I','E','N','D')) { return -1; @@ -304,6 +306,39 @@ int FTextureManager::CreateTexture (int lumpnum, int usetype) out = new FPNGTexture (lumpnum, BigLong((int)width), BigLong((int)height), bitdepth, colortype, interlace); } + else if (first4bytes.b[0] == 0xFF && first4bytes.b[1] == 0xD8 && first4bytes.b[2] == 0xFF) + { + // JPEG, I presume + + // Find the SOFn marker to extract the image dimensions, + // where n is 0, 1, or 2 (other types are unsupported). + while ((unsigned)first4bytes.b[3] - 0xC0 >= 3) + { + if (data.Read (first4bytes.w, 2) != 2) + { + return -1; + } + data.Seek (BigShort(first4bytes.w[0]) - 2, SEEK_CUR); + if (data.Read (first4bytes.b + 2, 2) != 2 || first4bytes.b[2] != 0xFF) + { + return -1; + } + } + if (data.Read (first4bytes.b, 3) != 3) + { + return -1; + } + if (BigShort (first4bytes.w[0]) <5) + { + return -1; + } + if (data.Read (first4bytes.b, 4) != 4) + { + return -1; + } + type = t_png; + out = new FJPEGTexture (lumpnum, BigShort(first4bytes.w[1]), BigShort(first4bytes.w[0])); + } else if (usetype == FTexture::TEX_Flat) { // allow PNGs as flats but not Doom patches. @@ -2091,6 +2126,111 @@ void FPNGTexture::MakeTexture () } } +int kpegrend (const char *kfilebuf, int kfilength, + unsigned char *daframeplace, int dabytesperline, int daxres, int dayres, + int daglobxoffs, int daglobyoffs); + +FJPEGTexture::FJPEGTexture (int lumpnum, int width, int height) +: SourceLump(lumpnum), Pixels(0) +{ + Wads.GetLumpName (Name, lumpnum); + Name[8] = 0; + + UseType = TEX_MiscPatch; + LeftOffset = 0; + TopOffset = 0; + bMasked = false; + + Width = width; + Height = height; + CalcBitSize (); + + DummySpans[0].TopOffset = 0; + DummySpans[0].Length = Height; + DummySpans[1].TopOffset = 0; + DummySpans[1].Length = 0; +} + +FJPEGTexture::~FJPEGTexture () +{ + Unload (); +} + +void FJPEGTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +const BYTE *FJPEGTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + *spans_out = DummySpans; + } + return Pixels + column*Height; +} + +const BYTE *FJPEGTexture::GetPixels () +{ + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +void FJPEGTexture::MakeTexture () +{ + FMemLump lump = Wads.ReadLump (SourceLump); + BYTE *rgb = new BYTE[Width * Height * 4]; + + Pixels = new BYTE[Width * Height]; + if (kpegrend ((char *)lump.GetMem(), Wads.LumpLength (SourceLump), rgb, Width * 4, Width, Height, 0, 0) < 0) + { // Failed to read the JPEG + memset (Pixels, 0xBA, Width * Height); + } + else + { + BYTE *in, *out; + int x, y, pitch, backstep; + + in = rgb; + out = Pixels; + + // Convert from source format to paletted, column-major. + pitch = Width * 4; + backstep = Height * pitch - 4; + for (x = Width; x > 0; --x) + { + for (y = Height; y > 0; --y) + { + *out++ = RGB32k[in[2]>>3][in[1]>>3][in[0]>>3]; + in += pitch; + } + in -= backstep; + } + } + delete[] rgb; +} FBuildTexture::FBuildTexture (int tilenum, const BYTE *pixels, int width, int height, int left, int top) : Pixels (pixels), Spans (NULL) diff --git a/src/r_data.h b/src/r_data.h index 721cd0fe5..865e40bf0 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -212,6 +212,25 @@ protected: }; +// A JPEG image +class FJPEGTexture : public FTexture +{ +public: + FJPEGTexture (int lumpnum, int width, int height); + ~FJPEGTexture (); + + const BYTE *GetColumn (unsigned int column, const Span **spans_out); + const BYTE *GetPixels (); + void Unload (); + +protected: + int SourceLump; + BYTE *Pixels; + Span DummySpans[2]; + + void MakeTexture (); +}; + // A texture that returns a wiggly version of another texture. class FWarpTexture : public FTexture { diff --git a/zdoom.vcproj b/zdoom.vcproj index 135097062..618ec9e81 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1924,6 +1924,10 @@ /> + + @@ -5381,7 +5385,7 @@ @@ -5399,7 +5403,7 @@