- 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)
This commit is contained in:
Randy Heit 2006-08-15 04:34:35 +00:00
parent 89396964f5
commit 21869a6c08
9 changed files with 941 additions and 94 deletions

View file

@ -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 August 12, 2006
- Added support for truecolor PNG textures. They still get resampled to the - Added support for truecolor PNG textures. They still get resampled to the
global palette, but at least they are visible now. global palette, but at least they are visible now.

View file

@ -187,7 +187,7 @@ FString savegamefile;
char savedescription[SAVESTRINGSIZE]; char savedescription[SAVESTRINGSIZE];
// [RH] Name of screenshot file to generate (usually NULL) // [RH] Name of screenshot file to generate (usually NULL)
char *shotfile; FString shotfile;
AActor* bodyque[BODYQUESIZE]; AActor* bodyque[BODYQUESIZE];
int bodyqueslot; int bodyqueslot;
@ -890,11 +890,7 @@ void G_Ticker ()
break; break;
case ga_screenshot: case ga_screenshot:
M_ScreenShot (shotfile); M_ScreenShot (shotfile);
if (shotfile) shotfile = "";
{
free (shotfile);
shotfile = NULL;
}
gameaction = ga_nothing; gameaction = ga_nothing;
break; break;
case ga_fullconsole: case ga_fullconsole:

571
src/kplib.cpp Normal file
View file

@ -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 <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include "m_swap.h"
#include "m_fixed.h"
#if !defined(_WIN32) && !defined(__DOS__)
#include <unistd.h>
static __inline unsigned long _lrotl (unsigned long i, int sh)
{ return((i>>(-sh))|(i<<sh)); }
#define _fileno fileno
#else
#include <io.h>
#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<gnumcomponents;z++)
{
gcompid[z] = kfileptr[0];
gcomphsamp[z] = (kfileptr[1]>>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;c<hufnumatbit[index][b];c++)
for(j=(1<<(10-b));j>0;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<lnumcomponents;z++)
{
lcompid[z] = kfileptr[0];
lcompdc[z] = (kfileptr[1]>>4);
lcompac[z] = (kfileptr[1]&15);
kfileptr += 2;
for(zz=0;zz<gnumcomponents;zz++)
if (lcompid[z] == gcompid[zz])
{
lcomphsamp[z] = gcomphsamp[zz];
lcompvsamp[z] = gcompvsamp[zz];
lcomphvsamp[z] = lcomphsamp[z]*lcompvsamp[z];
lcompquantab[z] = gcompquantab[zz];
for(i=0;i<8;i++)
if (pow2mask[i]+1 == lcomphsamp[z])
{
lcomphsampmask[z] = pow2mask[i];
lcomphsampshift[z] = i;
break;
}
for(i=0;i<8;i++)
if (pow2mask[i]+1 == lcompvsamp[z])
{
lcompvsampshift[z] = i;
break;
}
}
}
//Ss = kfileptr[0];
//Se = kfileptr[1];
//Ah = (kfileptr[2]>>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;c<lnumcomponents;c++)
{
hqval = &kpeg->hufquickval[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<yyyend;yyy++)
{
for(xxx=0;xxx<8;xxx++)
{
yv = dc[xxx];
cr = (dc2[xxx+64]>>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<yyyend;yyy++)
{
for(xxx=0;xxx<8;xxx+=2)
{
yv = dc[xxx];
cr = (dc2[(xxx>>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<yyyend;yyy++)
{
i = 0; j = 1;
for(xxx=0;xxx<xxxend;xxx++)
{
yv = dc[xxx];
j--;
if (!j)
{
j = lcomphsamp[0];
cr = (dc2[i+64]>>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 ==============================

View file

@ -537,14 +537,14 @@ static BOOL FindFreeName (FString &fullname, const char *extension)
return false; return false;
} }
void M_ScreenShot (char *filename) void M_ScreenShot (const char *filename)
{ {
FILE *file; FILE *file;
FString autoname; FString autoname;
bool writepcx = (stricmp (screenshot_type, "pcx") == 0); // PNG is the default bool writepcx = (stricmp (screenshot_type, "pcx") == 0); // PNG is the default
// find a file name to save it to // find a file name to save it to
if (filename == NULL) if (filename == NULL || filename[0] == '\0')
{ {
#ifndef unix #ifndef unix
if (Args.CheckParm ("-cdrom")) if (Args.CheckParm ("-cdrom"))

View file

@ -36,7 +36,7 @@ void M_FindResponseFile (void);
// [RH] M_ScreenShot now accepts a filename parameter. // [RH] M_ScreenShot now accepts a filename parameter.
// Pass a NULL to get the original behavior. // Pass a NULL to get the original behavior.
void M_ScreenShot (char *filename); void M_ScreenShot (const char *filename);
void M_LoadDefaults (); void M_LoadDefaults ();

View file

@ -100,7 +100,7 @@ static inline void StuffPalette (const PalEntry *from, BYTE *to);
static bool StuffBitmap (const DCanvas *canvas, FILE *file); static bool StuffBitmap (const DCanvas *canvas, FILE *file);
static bool WriteIDAT (FILE *file, const BYTE *data, int len); 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 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 ---------------------------------------------- // 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, bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitch,
BYTE bitdepth, BYTE colortype, BYTE interlace, unsigned int chunklen) 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]; Byte chunkbuffer[4096];
z_stream stream; z_stream stream;
int err; int err;
int y; int i, pass, passbuff, passpitch, passwidth;
bool lastIDAT; bool lastIDAT;
int bytesPerRowIn; int bytesPerRowIn, bytesPerRowOut;
int bytesPerPixel; int bytesPerPixel;
bool initpass;
switch (colortype) 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 case 6: bytesPerPixel = 4; break; // RGBA
default: bytesPerPixel = 1; break; 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) bytesPerRowOut = width * bytesPerPixel;
{ // This is an invalid combination for PNG files i = 4 + bytesPerRowOut * 2;
return false; if (interlace)
}
switch (bitdepth)
{ {
case 8: bytesPerRowIn = width * bytesPerPixel; break; i += bytesPerRowOut * 2;
case 4: bytesPerRowIn = (width+1)/2; break;
case 2: bytesPerRowIn = (width+3)/4; break;
case 1: bytesPerRowIn = (width+7)/8; break;
default: return false;
} }
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.next_in = Z_NULL;
stream.avail_in = 0; stream.avail_in = 0;
@ -568,16 +570,46 @@ bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitc
{ {
return false; return false;
} }
y = 0;
curr = buffer;
stream.next_out = inputLine;
stream.avail_out = bytesPerRowIn+1;
lastIDAT = false; lastIDAT = false;
initpass = true;
pass = interlace ? 0 : 7;
while (err != Z_STREAM_END && pass < 8 - interlace)
{
if (initpass)
{
int rowoffset, coloffset;
initpass = false;
pass--;
do do
{ {
while (err != Z_STREAM_END) 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) if (stream.avail_in == 0 && chunklen > 0)
{ {
stream.next_in = chunkbuffer; stream.next_in = chunkbuffer;
@ -594,12 +626,79 @@ bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitc
if (stream.avail_out == 0) if (stream.avail_out == 0)
{ {
if (pass >= 6)
{
// Store pixels directly into the output buffer
UnfilterRow (bytesPerRowIn, curr, inputLine, prev, bytesPerPixel); UnfilterRow (bytesPerRowIn, curr, inputLine, prev, bytesPerPixel);
prev = curr; prev = curr;
curr += pitch; }
y++; else
{
const BYTE *in;
BYTE *out;
int colstep, x;
// Store pixels into a temporary buffer
UnfilterRow (bytesPerRowIn, adam7buff[passbuff], inputLine, prev, bytesPerPixel);
prev = adam7buff[passbuff];
passbuff ^= 1;
in = prev;
if (bitdepth < 8)
{
UnpackPixels (passwidth, bytesPerRowIn, bitdepth, in, adam7buff[2]);
in = adam7buff[2];
}
// Distribute pixels into the output buffer
out = curr;
colstep = bytesPerPixel << passwidthshift[pass];
switch (bytesPerPixel)
{
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.next_out = inputLine;
stream.avail_out = bytesPerRowIn+1; stream.avail_out = bytesPerRowIn + 1;
} }
if (chunklen == 0 && !lastIDAT) if (chunklen == 0 && !lastIDAT)
@ -620,15 +719,17 @@ bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitc
} }
} }
} }
} while (err == Z_OK && y < height);
inflateEnd (&stream); inflateEnd (&stream);
if (bitdepth < 8) 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; return true;
@ -897,32 +998,33 @@ void UnfilterRow (int width, BYTE *dest, BYTE *row, BYTE *prev, int bpp)
// //
// UnpackPixels // UnpackPixels
// //
// Unpacks a row of pixels whose depth is less than 8 into so that each // Unpacks a row of pixels whose depth is less than 8 so that each pixel
// pixel occupies a single byte. The packed pixels must be at the start // occupies a single byte. The outrow must be "width" bytes long.
// of the row, and the row must be "width" bytes long. "bytesPerRow" is // "bytesPerRow" is the number of bytes for the packed row. The in and out
// the number of bytes for the packed row. // 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; BYTE pack;
int lastbyte; int lastbyte;
out = row + width; out = rowout + width;
in = row + bytesPerRow; in = rowin + bytesPerRow;
switch (bitdepth) switch (bitdepth)
{ {
case 1: case 1:
lastbyte=width&7; lastbyte = width & 7;
if (lastbyte!=0) if (lastbyte != 0)
{ {
in--; in--;
pack = *in; pack = *in;
out-=lastbyte; out -= lastbyte;
out[0] = (pack >> 7) & 1; out[0] = (pack >> 7) & 1;
if (lastbyte >= 2) out[1] = (pack >> 6) & 1; if (lastbyte >= 2) out[1] = (pack >> 6) & 1;
if (lastbyte >= 3) out[2] = (pack >> 5) & 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; if (lastbyte == 7) out[6] = (pack >> 1) & 1;
} }
while (in-- > row) while (in-- > rowin)
{ {
pack = *in; pack = *in;
out -= 8; out -= 8;
@ -949,18 +1051,18 @@ static void UnpackPixels (int width, int bytesPerRow, int bitdepth, BYTE *row)
case 2: case 2:
lastbyte=width&3; lastbyte = width & 3;
if (lastbyte!=0) if (lastbyte != 0)
{ {
in--; in--;
pack = *in; pack = *in;
out-=lastbyte; out -= lastbyte;
out[0] = pack >> 6; out[0] = pack >> 6;
if (lastbyte >= 2) out[1] = (pack >> 4) & 3; if (lastbyte >= 2) out[1] = (pack >> 4) & 3;
if (lastbyte == 3) out[2] = (pack >> 2) & 3; if (lastbyte == 3) out[2] = (pack >> 2) & 3;
} }
while (in-- > row) while (in-- > rowin)
{ {
pack = *in; pack = *in;
out -= 4; out -= 4;
@ -972,16 +1074,16 @@ static void UnpackPixels (int width, int bytesPerRow, int bitdepth, BYTE *row)
break; break;
case 4: case 4:
lastbyte=width&1; lastbyte = width & 1;
if (lastbyte!=0) if (lastbyte != 0)
{ {
in--; in--;
pack = *in; pack = *in;
out-=lastbyte; out -= lastbyte;
out[0] = pack >> 4; out[0] = pack >> 4;
} }
while (in-- > row) while (in-- > rowin)
{ {
pack = *in; pack = *in;
out -= 2; out -= 2;

View file

@ -238,6 +238,7 @@ int FTextureManager::CreateTexture (int lumpnum, int usetype)
{ {
DWORD dw; DWORD dw;
WORD w[2]; WORD w[2];
BYTE b[4];
} first4bytes; } first4bytes;
// Must check the length of the lump. Zero length flat markers (F1_START etc.) will come through here. // 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); FWadLump data = Wads.OpenLumpNum (lumpnum);
data >> first4bytes.dw; data.Read (first4bytes.b, 4);
if (first4bytes.dw == MAKE_ID('I','M','G','Z')) if (first4bytes.dw == MAKE_ID('I','M','G','Z'))
{ {
type = t_imgz; 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 // 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 // first 4 bytes match, but later bytes don't, we assume it's
// a corrupt PNG.) // a corrupt PNG.)
data >> first4bytes.dw; data.Read (first4bytes.b, 4);
if (first4bytes.dw != MAKE_ID(13,10,26,10)) return -1; 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; 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; 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 // 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 data >> width >> height
>> bitdepth >> colortype >> compression >> filter >> interlace; >> bitdepth >> colortype >> compression >> filter >> interlace;
if (compression != 0 || filter != 0 || interlace != 0) if (compression != 0 || filter != 0 || interlace > 1)
{ {
return -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 // Just for completeness, make sure the PNG has something more than an
// IHDR. // IHDR.
data >> first4bytes.dw >> first4bytes.dw; data.Seek (4, SEEK_CUR);
data.Read (first4bytes.b, 4);
if (first4bytes.dw == 0) if (first4bytes.dw == 0)
{ {
data >> first4bytes.dw; data.Read (first4bytes.b, 4);
if (first4bytes.dw == MAKE_ID('I','E','N','D')) if (first4bytes.dw == MAKE_ID('I','E','N','D'))
{ {
return -1; return -1;
@ -304,6 +306,39 @@ int FTextureManager::CreateTexture (int lumpnum, int usetype)
out = new FPNGTexture (lumpnum, BigLong((int)width), BigLong((int)height), out = new FPNGTexture (lumpnum, BigLong((int)width), BigLong((int)height),
bitdepth, colortype, interlace); 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) else if (usetype == FTexture::TEX_Flat)
{ {
// allow PNGs as flats but not Doom patches. // 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) FBuildTexture::FBuildTexture (int tilenum, const BYTE *pixels, int width, int height, int left, int top)
: Pixels (pixels), Spans (NULL) : Pixels (pixels), Spans (NULL)

View file

@ -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. // A texture that returns a wiggly version of another texture.
class FWarpTexture : public FTexture class FWarpTexture : public FTexture
{ {

View file

@ -1924,6 +1924,10 @@
/> />
</FileConfiguration> </FileConfiguration>
</File> </File>
<File
RelativePath=".\src\kplib.cpp"
>
</File>
<File <File
RelativePath=".\src\lumpconfigfile.cpp" RelativePath=".\src\lumpconfigfile.cpp"
> >
@ -5381,7 +5385,7 @@
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..." Description="Assembling $(InputPath)..."
CommandLine="nasmw -o &quot;$(IntDir)\$(InputName).obj&quot; -d OBJ_FORMAT_win32 -f win32 &quot;$(InputPath)&quot;" CommandLine="nasmw -o &quot;$(IntDir)\$(InputName).obj&quot; -d OBJ_FORMAT_win32 -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj" Outputs="$(IntDir)\$(InputName).obj"
/> />
</FileConfiguration> </FileConfiguration>
@ -5399,7 +5403,7 @@
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..." Description="Assembling $(InputPath)..."
CommandLine="nasmw -o &quot;$(IntDir)\$(InputName).obj&quot; -d OBJ_FORMAT_win32 -f win32 &quot;$(InputPath)&quot;" CommandLine="nasmw -o &quot;$(IntDir)\$(InputName).obj&quot; -d OBJ_FORMAT_win32 -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj" Outputs="$(IntDir)\$(InputName).obj"
/> />
</FileConfiguration> </FileConfiguration>