Add screenshotPNG command

Add screenshotPNG command.
Fix "\screenshotJPEG levelshot" to save JPEG image instead of TGA.
Allow specifying the size of levelshot using "\screenshot levelshot 256".
This commit is contained in:
Zack Middleton 2024-05-24 16:56:12 -05:00
parent 1fc83e4845
commit 6fed956d95
14 changed files with 4240 additions and 249 deletions

View file

@ -2246,10 +2246,13 @@ endif
ifeq ($(USE_INTERNAL_ZLIB),1)
Q3OBJ += \
$(B)/client/adler32.o \
$(B)/client/compress.o \
$(B)/client/crc32.o \
$(B)/client/deflate.o \
$(B)/client/inffast.o \
$(B)/client/inflate.o \
$(B)/client/inftrees.o \
$(B)/client/trees.o \
$(B)/client/zutil.o
endif

View file

@ -3193,6 +3193,28 @@ int CL_ScaledMilliseconds(void) {
return Sys_Milliseconds()*com_timescale->value;
}
void CL_GetModDescription( char *buf, int bufLength ) {
FS_GetModDescription( FS_GetCurrentGameDir(), buf, bufLength );
}
void CL_GetMapTitle( char *buf, int bufLength ) {
Q_strncpyz(buf, cl.gameState.stringData + cl.gameState.stringOffsets[CS_MESSAGE], bufLength);
}
void CL_GetPlayerLocation( char *buf, int bufLength ) {
playerState_t *ps;
if (clc.state != CA_ACTIVE || !cl.snap.valid) {
Q_strncpyz(buf, "Unknown", bufLength);
return;
}
ps = &cl.snap.ps;
Com_sprintf(buf, bufLength, "X:%d Y:%d Z:%d A:%d", (int)ps->origin[0],
(int)ps->origin[1], (int)ps->origin[2],
(int)(ps->viewangles[YAW]+360)%360);
}
/*
============
CL_InitRef
@ -3269,6 +3291,7 @@ void CL_InitRef( void ) {
ri.Cvar_CheckRange = Cvar_CheckRange;
ri.Cvar_SetDescription = Cvar_SetDescription;
ri.Cvar_VariableIntegerValue = Cvar_VariableIntegerValue;
ri.Cvar_VariableStringBuffer = Cvar_VariableStringBuffer;
// cinematic stuff
@ -3289,6 +3312,13 @@ void CL_InitRef( void ) {
ri.Sys_GLimpInit = Sys_GLimpInit;
ri.Sys_LowPhysicalMemory = Sys_LowPhysicalMemory;
ri.zlib_compress = compress;
ri.zlib_crc32 = crc32;
ri.CL_GetModDescription = CL_GetModDescription;
ri.CL_GetMapTitle = CL_GetMapTitle;
ri.CL_GetPlayerLocation = CL_GetPlayerLocation;
ret = GetRefAPI( REF_API_VERSION, &ri );
#if defined __USEA3D && defined __A3D_GEOM

View file

@ -1,7 +1,8 @@
/*
===========================================================================
ioquake3 png decoder
ioquake3 png decoder and encoder
Copyright (C) 2007,2008 Joerg Dietrich
Copyright (C) 2011 Zack Middleton
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@ -78,6 +79,7 @@ typedef uint32_t PNG_ChunkCRC;
#define MAKE_CHUNKTYPE(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d)))
#define PNG_ChunkType_IHDR MAKE_CHUNKTYPE('I', 'H', 'D', 'R')
#define PNG_ChunkType_tEXt MAKE_CHUNKTYPE('t', 'E', 'X', 't')
#define PNG_ChunkType_PLTE MAKE_CHUNKTYPE('P', 'L', 'T', 'E')
#define PNG_ChunkType_IDAT MAKE_CHUNKTYPE('I', 'D', 'A', 'T')
#define PNG_ChunkType_IEND MAKE_CHUNKTYPE('I', 'E', 'N', 'D')
@ -2483,3 +2485,296 @@ void R_LoadPNG(const char *name, byte **pic, int *width, int *height)
CloseBufferedFile(ThePNG);
}
/*
* Encode a non-interlaced 8-bit true color image
*/
static qboolean EncodeImageNonInterlaced8True(uint32_t IHDR_Width, uint32_t IHDR_Height,
byte *InBuffer,
uint32_t InBytesPerPixel,
uint32_t Padding,
uint8_t *ImageData)
{
uint32_t BytesPerScanline, BytesPerPixel, PixelsPerByte;
uint32_t w, h;
byte *InPtr;
uint8_t *OutPtr;
/*
* input verification
*/
if(!InBuffer)
{
return(qfalse);
}
/*
* information for un-filtering
* PNG_ColourType_True, PNG_BitDepth_8
*/
BytesPerPixel = PNG_NumColourComponents_True;
PixelsPerByte = 1;
/*
* Calculate the size of one scanline
*/
BytesPerScanline = (IHDR_Width * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte;
/*
* Set the working pointers to the beginning of the buffers.
*/
InPtr = InBuffer;
OutPtr = ImageData;
/*
* Create the output image.
*/
for(h = IHDR_Height; h > 0; h--)
{
InPtr = InBuffer + (BytesPerScanline + Padding) * (h - 1);
/*
* set FilterType
*/
OutPtr[0] = PNG_FilterType_None;
OutPtr++;
for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++)
{
OutPtr[0] = InPtr[0];
OutPtr[1] = InPtr[1];
OutPtr[2] = InPtr[2];
InPtr += InBytesPerPixel;
OutPtr += BytesPerPixel;
}
}
return(qtrue);
}
/*
* Write data to buffer.
*/
void WriteToBuffer(void **buffer, const void *data, size_t length)
{
memcpy(*buffer, data, length);
*(byte**)buffer += length;
}
/*
* Write PNG chuck header to buffer.
*/
void WriteChunkHeader(void **buffer, void **crcPtr, PNG_ChunkCRC *CRC, int type, int length)
{
struct PNG_ChunkHeader CH;
/*
* Write chuck header
*/
CH.Type = BigLong(type);
CH.Length = BigLong(length);
WriteToBuffer(buffer, &CH, PNG_ChunkHeader_Size);
/*
* Init CRC
*/
*CRC = ri.zlib_crc32(0, Z_NULL, 0);
*CRC = ri.zlib_crc32(*CRC, *(byte**)buffer-4, 4);
*crcPtr = *buffer;
}
/*
* Write CRC to buffer.
*/
void WriteCRC(void **buffer, void **crcPtr, PNG_ChunkCRC CRC)
{
int size = (intptr_t)*buffer-(intptr_t)*crcPtr;
/*
* Update CRC
*/
if (size > 0)
CRC = ri.zlib_crc32(CRC, *crcPtr, size);
/*
* Write CRC
*/
CRC = BigLong(CRC);
WriteToBuffer(buffer, &CRC, PNG_ChunkCRC_Size);
}
/*
* The PNG saver
*/
void RE_SavePNG(const char *filename, int width, int height, byte *data, int padding) {
void *pngData;
size_t pngSize;
void *buffer;
struct PNG_Chunk_IHDR IHDR;
PNG_ChunkCRC CRC;
void *crcPtr;
int numtEXt = 0;
#define NUMTEXT 7
struct
{
char key[80]; // PNG limits to 79+'\0'.
char text[256]; // PNG allows any length.
} tEXt[NUMTEXT];
int i;
uint8_t *imageData;
uint32_t imageLength;
Bytef *compressedData = NULL;
uLongf compressedDataLength;
/*
* Create the png image data from buffer.
*/
imageLength = (width*PNG_NumColourComponents_True + 1) * height;
imageData = ri.Malloc(imageLength);
EncodeImageNonInterlaced8True(width, height, data, 3, padding, imageData);
/*
* Compress the png image data.
*/
compressedDataLength = imageLength * 1.01f + 12;
compressedData = ri.Malloc(compressedDataLength);
if (ri.zlib_compress(compressedData, &compressedDataLength, imageData, imageLength) != Z_OK) {
ri.Free(compressedData);
ri.Free(imageData);
ri.Printf(PRINT_WARNING, "RE_SavePNG: Failed to compress image data.\n");
return;
}
/*
* Setup text data.
*/
Q_strncpyz(tEXt[numtEXt].key, "Title", sizeof (tEXt[numtEXt].key));
ri.Cvar_VariableStringBuffer("version", tEXt[numtEXt].text, sizeof (tEXt[numtEXt].text));
numtEXt++;
Q_strncpyz(tEXt[numtEXt].key, "Author", sizeof (tEXt[numtEXt].key));
ri.Cvar_VariableStringBuffer("username", tEXt[numtEXt].text, sizeof (tEXt[numtEXt].text));
numtEXt++;
Q_strncpyz(tEXt[numtEXt].key, "Description", sizeof (tEXt[numtEXt].key));
ri.CL_GetModDescription(tEXt[numtEXt].text, sizeof (tEXt[numtEXt].text));
numtEXt++;
Q_strncpyz(tEXt[numtEXt].key, "Mapname", sizeof (tEXt[numtEXt].key));
ri.Cvar_VariableStringBuffer("mapname", tEXt[numtEXt].text, sizeof (tEXt[numtEXt].text));
numtEXt++;
Q_strncpyz(tEXt[numtEXt].key, "Maptitle", sizeof (tEXt[numtEXt].key));
ri.CL_GetMapTitle(tEXt[numtEXt].text, sizeof (tEXt[numtEXt].text));
numtEXt++;
Q_strncpyz(tEXt[numtEXt].key, "Playername", sizeof (tEXt[numtEXt].key));
ri.Cvar_VariableStringBuffer("name", tEXt[numtEXt].text, sizeof (tEXt[numtEXt].text));
numtEXt++;
Q_strncpyz(tEXt[numtEXt].key, "Location", sizeof (tEXt[numtEXt].key));
ri.CL_GetPlayerLocation(tEXt[numtEXt].text, sizeof (tEXt[numtEXt].text));
numtEXt++;
/*
* Calculate the size of the image.
*/
pngSize = PNG_Signature_Size + PNG_ChunkHeader_Size + PNG_Chunk_IHDR_Size + PNG_ChunkCRC_Size;
/*
* Calculate the length of the tEXt chunks.
*/
for (i = 0; i < numtEXt; i++) {
pngSize += PNG_ChunkHeader_Size + strlen(tEXt[i].key)+1 + strlen(tEXt[i].text) + PNG_ChunkCRC_Size;
}
pngSize += PNG_ChunkHeader_Size + compressedDataLength + PNG_ChunkCRC_Size
+ PNG_ChunkHeader_Size + PNG_ChunkCRC_Size;
/*
* Allocate memory to hold the full png image data.
*/
buffer = pngData = ri.Hunk_AllocateTempMemory(pngSize);
/*
* Setup CRC.
*/
CRC = ri.zlib_crc32(0, Z_NULL, 0);
crcPtr = (byte*)buffer + PNG_Signature_Size + 4;
/*
* Header
*/
WriteToBuffer(&buffer, PNG_Signature, PNG_Signature_Size);
WriteChunkHeader(&buffer, &crcPtr, &CRC, PNG_ChunkType_IHDR, PNG_Chunk_IHDR_Size);
IHDR.Width = BigLong(width);
IHDR.Height = BigLong(height);
IHDR.BitDepth = PNG_BitDepth_8;
IHDR.ColourType = PNG_ColourType_True;
IHDR.CompressionMethod = PNG_CompressionMethod_0;
IHDR.FilterMethod = PNG_FilterMethod_0;
IHDR.InterlaceMethod = PNG_InterlaceMethod_NonInterlaced;
WriteToBuffer(&buffer, &IHDR, PNG_Chunk_IHDR_Size);
WriteCRC(&buffer, &crcPtr, CRC);
/*
* tEXt, Textual data.
*/
for (i = 0; i < numtEXt; i++) {
WriteChunkHeader(&buffer, &crcPtr, &CRC, PNG_ChunkType_tEXt, strlen(tEXt[i].key)+1 + strlen(tEXt[i].text));
/*
* Write string data.
*/
WriteToBuffer(&buffer, tEXt[i].key, strlen(tEXt[i].key)+1);
WriteToBuffer(&buffer, tEXt[i].text, strlen(tEXt[i].text));
WriteCRC(&buffer, &crcPtr, CRC);
}
/*
* IDAT, Image Data.
*/
WriteChunkHeader(&buffer, &crcPtr, &CRC, PNG_ChunkType_IDAT, compressedDataLength);
WriteToBuffer(&buffer, compressedData, compressedDataLength);
WriteCRC(&buffer, &crcPtr, CRC);
/*
* IEND, Image End.
*/
WriteChunkHeader(&buffer, &crcPtr, &CRC, PNG_ChunkType_IEND, 0);
WriteCRC(&buffer, &crcPtr, CRC);
/*
* Write the image to file.
*/
ri.FS_WriteFile(filename, pngData, pngSize);
/*
* Free memory.
*/
ri.Hunk_FreeTempMemory(pngData);
ri.Free(compressedData);
ri.Free(imageData);
}

View file

@ -313,3 +313,50 @@ void R_LoadTGA ( const char *name, byte **pic, int *width, int *height)
ri.FS_FreeFile (buffer.v);
}
void RE_SaveTGA(char * filename, int image_width, int image_height, byte *image_buffer, int padding) {
byte *srcptr, *destptr;
byte *endline, *endmem;
byte *out;
size_t bufSize;
int linelen;
bufSize = image_width * image_height * 3 + 18;
out = ri.Hunk_AllocateTempMemory(bufSize);
Com_Memset (out, 0, 18);
out[2] = 2; // uncompressed type
out[12] = image_width & 255;
out[13] = image_width >> 8;
out[14] = image_height & 255;
out[15] = image_height >> 8;
out[16] = 24; // pixel size
// swap rgb to bgr and remove padding from line endings
linelen = image_width * 3;
srcptr = image_buffer;
destptr = out + 18;
endmem = srcptr + (linelen + padding) * image_height;
while(srcptr < endmem)
{
endline = srcptr + linelen;
while(srcptr < endline)
{
*destptr++ = srcptr[2];
*destptr++ = srcptr[1];
*destptr++ = srcptr[0];
srcptr += 3;
}
// Skip the pad
srcptr += padding;
}
ri.FS_WriteFile(filename, out, bufSize);
ri.Hunk_FreeTempMemory(out);
}

View file

@ -24,6 +24,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "tr_types.h"
#ifdef USE_LOCAL_HEADERS
#include "../zlib/zlib.h"
#else
#include <zlib.h>
#endif
#define REF_API_VERSION 8
//
@ -180,6 +186,17 @@ typedef struct {
void (*Sys_GLimpSafeInit)( void );
void (*Sys_GLimpInit)( void );
qboolean (*Sys_LowPhysicalMemory)( void );
void (*Cvar_VariableStringBuffer) (const char *var_name, char *buffer, int bufsize);
// zlib for png screenshots
int (*zlib_compress) (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
uLong (*zlib_crc32) (uLong crc, const Bytef *buf, uInt len);
// get extra info for png screenshots
void (*CL_GetModDescription)( char *buf, int bufLength );
void (*CL_GetMapTitle)( char *buf, int bufLength );
void (*CL_GetPlayerLocation)( char *buf, int bufLength );
} refimport_t;

View file

@ -387,63 +387,24 @@ byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *pa
/*
==================
RB_TakeScreenshot
RB_TakeScreenshotTGA
==================
*/
void RB_TakeScreenshot(int x, int y, int width, int height, char *fileName)
void RB_TakeScreenshotTGA(int x, int y, int width, int height, char *fileName)
{
byte *allbuf, *buffer;
byte *srcptr, *destptr;
byte *endline, *endmem;
byte temp;
int linelen, padlen;
size_t offset = 18, memcount;
allbuf = RB_ReadPixels(x, y, width, height, &offset, &padlen);
buffer = allbuf + offset - 18;
Com_Memset (buffer, 0, 18);
buffer[2] = 2; // uncompressed type
buffer[12] = width & 255;
buffer[13] = width >> 8;
buffer[14] = height & 255;
buffer[15] = height >> 8;
buffer[16] = 24; // pixel size
byte *buffer;
size_t offset = 0, memcount;
int padlen;
// swap rgb to bgr and remove padding from line endings
linelen = width * 3;
srcptr = destptr = allbuf + offset;
endmem = srcptr + (linelen + padlen) * height;
while(srcptr < endmem)
{
endline = srcptr + linelen;
while(srcptr < endline)
{
temp = srcptr[0];
*destptr++ = srcptr[2];
*destptr++ = srcptr[1];
*destptr++ = temp;
srcptr += 3;
}
// Skip the pad
srcptr += padlen;
}
memcount = linelen * height;
buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen);
memcount = (width * 3 + padlen) * height;
// gamma correct
if(glConfig.deviceSupportsGamma)
R_GammaCorrect(allbuf + offset, memcount);
R_GammaCorrect(buffer + offset, memcount);
ri.FS_WriteFile(fileName, buffer, memcount + 18);
ri.Hunk_FreeTempMemory(allbuf);
RE_SaveTGA(fileName, width, height, buffer + offset, padlen);
ri.Hunk_FreeTempMemory(buffer);
}
/*
@ -469,6 +430,28 @@ void RB_TakeScreenshotJPEG(int x, int y, int width, int height, char *fileName)
ri.Hunk_FreeTempMemory(buffer);
}
/*
==================
RB_TakeScreenshotPNG
==================
*/
void RB_TakeScreenshotPNG(int x, int y, int width, int height, char *fileName)
{
byte *buffer;
size_t offset = 0, memcount;
int padlen;
buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen);
memcount = (width * 3 + padlen) * height;
// gamma correct
if(glConfig.deviceSupportsGamma)
R_GammaCorrect(buffer + offset, memcount);
RE_SavePNG(fileName, width, height, buffer + offset, padlen);
ri.Hunk_FreeTempMemory(buffer);
}
/*
==================
RB_TakeScreenshotCmd
@ -479,10 +462,12 @@ const void *RB_TakeScreenshotCmd( const void *data ) {
cmd = (const screenshotCommand_t *)data;
if (cmd->jpeg)
RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName);
else
RB_TakeScreenshot( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName);
if (cmd->type == ST_TGA)
RB_TakeScreenshotTGA( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
else if (cmd->type == ST_JPEG)
RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
else if (cmd->type == ST_PNG)
RB_TakeScreenshotPNG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
return (const void *)(cmd + 1);
}
@ -492,7 +477,7 @@ const void *RB_TakeScreenshotCmd( const void *data ) {
R_TakeScreenshot
==================
*/
void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean jpeg ) {
void R_TakeScreenshot( int x, int y, int width, int height, char *name, screenshotType_e type ) {
static char fileName[MAX_OSPATH]; // bad things if two screenshots per frame?
screenshotCommand_t *cmd;
@ -508,7 +493,7 @@ void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean
cmd->height = height;
Q_strncpyz( fileName, name, sizeof(fileName) );
cmd->fileName = fileName;
cmd->jpeg = jpeg;
cmd->type = type;
}
/*
@ -516,11 +501,11 @@ void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean
R_ScreenshotFilename
==================
*/
void R_ScreenshotFilename( int lastNumber, char *fileName ) {
void R_ScreenshotFilename( int lastNumber, char *fileName, char *ext ) {
int a,b,c,d;
if ( lastNumber < 0 || lastNumber > 9999 ) {
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.tga" );
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999%s", ext );
return;
}
@ -532,33 +517,8 @@ void R_ScreenshotFilename( int lastNumber, char *fileName ) {
lastNumber -= c*10;
d = lastNumber;
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.tga"
, a, b, c, d );
}
/*
==================
R_ScreenshotFilename
==================
*/
void R_ScreenshotFilenameJPEG( int lastNumber, char *fileName ) {
int a,b,c,d;
if ( lastNumber < 0 || lastNumber > 9999 ) {
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.jpg" );
return;
}
a = lastNumber / 1000;
lastNumber -= a*1000;
b = lastNumber / 100;
lastNumber -= b*100;
c = lastNumber / 10;
lastNumber -= c*10;
d = lastNumber;
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.jpg"
, a, b, c, d );
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i%s"
, a, b, c, d, ext );
}
/*
@ -569,63 +529,93 @@ levelshots are specialized 128*128 thumbnails for
the menu system, sampled down from full screen distorted images
====================
*/
void R_LevelShot( void ) {
char checkname[MAX_OSPATH];
byte *buffer;
byte *source, *allsource;
void R_LevelShot( screenshotType_e type, const char *ext ) {
char fileName[MAX_OSPATH];
byte *source;
byte *resample, *resamplestart;
size_t offset = 0, memcount;
int spadlen, rpadlen;
int padwidth, linelen;
GLint packAlign;
byte *src, *dst;
size_t offset = 0;
int padlen;
int x, y;
int r, g, b;
float xScale, yScale;
int xx, yy;
int width, height;
int arg;
Com_sprintf(checkname, sizeof(checkname), "levelshots/%s.tga", tr.world->baseName);
// Allow custom resample width/height
arg = atoi(ri.Cmd_Argv(2));
if (arg > 0)
width = height = arg;
else
width = height = 128;
allsource = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &padlen);
source = allsource + offset;
if (width > glConfig.vidWidth)
width = glConfig.vidWidth;
if (height > glConfig.vidHeight)
height = glConfig.vidHeight;
buffer = ri.Hunk_AllocateTempMemory(128 * 128*3 + 18);
Com_Memset (buffer, 0, 18);
buffer[2] = 2; // uncompressed type
buffer[12] = 128;
buffer[14] = 128;
buffer[16] = 24; // pixel size
Com_sprintf(fileName, sizeof(fileName), "levelshots/%s%s", tr.world->baseName, ext);
source = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &spadlen);
//
// Based on RB_ReadPixels
qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign);
linelen = width * 3;
padwidth = PAD(linelen, packAlign);
// Allocate a few more bytes so that we can choose an alignment we like
resample = ri.Hunk_AllocateTempMemory(padwidth * height + offset + packAlign - 1);
resamplestart = PADP((intptr_t) resample + offset, packAlign);
offset = resamplestart - resample;
rpadlen = padwidth - linelen;
//
// resample from source
xScale = glConfig.vidWidth / 512.0f;
yScale = glConfig.vidHeight / 384.0f;
for ( y = 0 ; y < 128 ; y++ ) {
for ( x = 0 ; x < 128 ; x++ ) {
xScale = glConfig.vidWidth / (float)(width * 4.0f);
yScale = glConfig.vidHeight / (float)(height * 3.0f);
for ( y = 0 ; y < height ; y++ ) {
for ( x = 0 ; x < width ; x++ ) {
r = g = b = 0;
for ( yy = 0 ; yy < 3 ; yy++ ) {
for ( xx = 0 ; xx < 4 ; xx++ ) {
src = source + (3 * glConfig.vidWidth + padlen) * (int)((y*3 + yy) * yScale) +
src = source + (3 * glConfig.vidWidth + spadlen) * (int)((y*3 + yy) * yScale) +
3 * (int) ((x*4 + xx) * xScale);
r += src[0];
g += src[1];
b += src[2];
}
}
dst = buffer + 18 + 3 * ( y * 128 + x );
dst[0] = b / 12;
dst = resample + 3 * ( y * width + x );
dst[0] = r / 12;
dst[1] = g / 12;
dst[2] = r / 12;
dst[2] = b / 12;
}
}
memcount = (width * 3 + rpadlen) * height;
// gamma correct
if ( glConfig.deviceSupportsGamma ) {
R_GammaCorrect( buffer + 18, 128 * 128 * 3 );
}
if(glConfig.deviceSupportsGamma)
R_GammaCorrect(resample + offset, memcount);
ri.FS_WriteFile( checkname, buffer, 128 * 128*3 + 18 );
if ( type == ST_TGA )
RE_SaveTGA(fileName, width, height, resample + offset, rpadlen);
else if ( type == ST_JPEG )
RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, width, height, resample + offset, rpadlen);
else if ( type == ST_PNG )
RE_SavePNG(fileName, width, height, resample + offset, rpadlen);
ri.Hunk_FreeTempMemory(buffer);
ri.Hunk_FreeTempMemory(allsource);
ri.Hunk_FreeTempMemory(resample);
ri.Hunk_FreeTempMemory(source);
ri.Printf( PRINT_ALL, "Wrote %s\n", checkname );
ri.Printf( PRINT_ALL, "Wrote %s\n", fileName );
}
/*
@ -640,13 +630,13 @@ screenshot [filename]
Doesn't print the pacifier message if there is a second arg
==================
*/
void R_ScreenShot_f (void) {
void R_ScreenShotTGA_f (void) {
char checkname[MAX_OSPATH];
static int lastNumber = -1;
qboolean silent;
if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) {
R_LevelShot();
R_LevelShot( ST_TGA, ".tga" );
return;
}
@ -670,7 +660,7 @@ void R_ScreenShot_f (void) {
}
// scan for a free number
for ( ; lastNumber <= 9999 ; lastNumber++ ) {
R_ScreenshotFilename( lastNumber, checkname );
R_ScreenshotFilename( lastNumber, checkname, ".tga" );
if (!ri.FS_FileExists( checkname ))
{
@ -686,7 +676,7 @@ void R_ScreenShot_f (void) {
lastNumber++;
}
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qfalse );
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, ST_TGA );
if ( !silent ) {
ri.Printf (PRINT_ALL, "Wrote %s\n", checkname);
@ -699,7 +689,7 @@ void R_ScreenShotJPEG_f (void) {
qboolean silent;
if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) {
R_LevelShot();
R_LevelShot( ST_JPEG, ".jpg" );
return;
}
@ -723,7 +713,7 @@ void R_ScreenShotJPEG_f (void) {
}
// scan for a free number
for ( ; lastNumber <= 9999 ; lastNumber++ ) {
R_ScreenshotFilenameJPEG( lastNumber, checkname );
R_ScreenshotFilename( lastNumber, checkname, ".jpg" );
if (!ri.FS_FileExists( checkname ))
{
@ -731,7 +721,7 @@ void R_ScreenShotJPEG_f (void) {
}
}
if ( lastNumber == 10000 ) {
if ( lastNumber >= 9999 ) {
ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n");
return;
}
@ -739,13 +729,66 @@ void R_ScreenShotJPEG_f (void) {
lastNumber++;
}
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qtrue );
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, ST_JPEG );
if ( !silent ) {
ri.Printf (PRINT_ALL, "Wrote %s\n", checkname);
}
}
void R_ScreenShotPNG_f (void) {
char checkname[MAX_OSPATH];
static int lastNumber = -1;
qboolean silent;
if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) {
R_LevelShot( ST_PNG, ".png" );
return;
}
if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) {
silent = qtrue;
} else {
silent = qfalse;
}
if ( ri.Cmd_Argc() == 2 && !silent ) {
// explicit filename
Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.png", ri.Cmd_Argv( 1 ) );
} else {
// scan for a free filename
// if we have saved a previous screenshot, don't scan
// again, because recording demo avis can involve
// thousands of shots
if ( lastNumber == -1 ) {
lastNumber = 0;
}
// scan for a free number
for ( ; lastNumber <= 9999 ; lastNumber++ ) {
R_ScreenshotFilename( lastNumber, checkname, ".png" );
if (!ri.FS_FileExists( checkname ))
{
break; // file doesn't exist
}
}
if ( lastNumber >= 9999 ) {
ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n");
return;
}
lastNumber++;
}
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, ST_PNG );
if ( !silent ) {
ri.Printf (PRINT_ALL, "Wrote %s\n", checkname);
}
}
//============================================================================
/*
@ -1159,8 +1202,9 @@ void R_Register( void )
ri.Cmd_AddCommand( "skinlist", R_SkinList_f );
ri.Cmd_AddCommand( "modellist", R_Modellist_f );
ri.Cmd_AddCommand( "modelist", R_ModeList_f );
ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f );
ri.Cmd_AddCommand( "screenshot", R_ScreenShotTGA_f );
ri.Cmd_AddCommand( "screenshotJPEG", R_ScreenShotJPEG_f );
ri.Cmd_AddCommand( "screenshotPNG", R_ScreenShotPNG_f );
ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f );
ri.Cmd_AddCommand( "minimize", GLimp_Minimize );
}
@ -1277,6 +1321,7 @@ void RE_Shutdown( qboolean destroyWindow ) {
ri.Cmd_RemoveCommand( "modelist" );
ri.Cmd_RemoveCommand( "screenshot" );
ri.Cmd_RemoveCommand( "screenshotJPEG" );
ri.Cmd_RemoveCommand( "screenshotPNG" );
ri.Cmd_RemoveCommand( "gfxinfo" );
ri.Cmd_RemoveCommand( "minimize" );

View file

@ -1517,6 +1517,12 @@ typedef struct {
int numDrawSurfs;
} drawSurfsCommand_t;
typedef enum {
ST_TGA,
ST_JPEG,
ST_PNG
} screenshotType_e;
typedef struct {
int commandId;
int x;
@ -1524,7 +1530,7 @@ typedef struct {
int width;
int height;
char *fileName;
qboolean jpeg;
screenshotType_e type;
} screenshotCommand_t;
typedef struct {
@ -1597,10 +1603,12 @@ void RE_StretchPic ( float x, float y, float w, float h,
float s1, float t1, float s2, float t2, qhandle_t hShader );
void RE_BeginFrame( stereoFrame_t stereoFrame );
void RE_EndFrame( int *frontEndMsec, int *backEndMsec );
void RE_SavePNG(const char *filename, int width, int height, byte *data, int padding);
void RE_SaveJPG(char * filename, int quality, int image_width, int image_height,
unsigned char *image_buffer, int padding);
size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality,
int image_width, int image_height, byte *image_buffer, int padding);
void RE_SaveTGA(char * filename, int image_width, int image_height, byte *image_buffer, int padding);
void RE_TakeVideoFrame( int width, int height,
byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg );

View file

@ -474,63 +474,24 @@ byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *pa
/*
==================
RB_TakeScreenshot
RB_TakeScreenshotTGA
==================
*/
void RB_TakeScreenshot(int x, int y, int width, int height, char *fileName)
void RB_TakeScreenshotTGA(int x, int y, int width, int height, char *fileName)
{
byte *allbuf, *buffer;
byte *srcptr, *destptr;
byte *endline, *endmem;
byte temp;
int linelen, padlen;
size_t offset = 18, memcount;
allbuf = RB_ReadPixels(x, y, width, height, &offset, &padlen);
buffer = allbuf + offset - 18;
Com_Memset (buffer, 0, 18);
buffer[2] = 2; // uncompressed type
buffer[12] = width & 255;
buffer[13] = width >> 8;
buffer[14] = height & 255;
buffer[15] = height >> 8;
buffer[16] = 24; // pixel size
byte *buffer;
size_t offset = 0, memcount;
int padlen;
// swap rgb to bgr and remove padding from line endings
linelen = width * 3;
srcptr = destptr = allbuf + offset;
endmem = srcptr + (linelen + padlen) * height;
while(srcptr < endmem)
{
endline = srcptr + linelen;
while(srcptr < endline)
{
temp = srcptr[0];
*destptr++ = srcptr[2];
*destptr++ = srcptr[1];
*destptr++ = temp;
srcptr += 3;
}
// Skip the pad
srcptr += padlen;
}
memcount = linelen * height;
buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen);
memcount = (width * 3 + padlen) * height;
// gamma correct
if(glConfig.deviceSupportsGamma)
R_GammaCorrect(allbuf + offset, memcount);
R_GammaCorrect(buffer + offset, memcount);
ri.FS_WriteFile(fileName, buffer, memcount + 18);
ri.Hunk_FreeTempMemory(allbuf);
RE_SaveTGA(fileName, width, height, buffer + offset, padlen);
ri.Hunk_FreeTempMemory(buffer);
}
/*
@ -556,6 +517,28 @@ void RB_TakeScreenshotJPEG(int x, int y, int width, int height, char *fileName)
ri.Hunk_FreeTempMemory(buffer);
}
/*
==================
RB_TakeScreenshotPNG
==================
*/
void RB_TakeScreenshotPNG(int x, int y, int width, int height, char *fileName)
{
byte *buffer;
size_t offset = 0, memcount;
int padlen;
buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen);
memcount = (width * 3 + padlen) * height;
// gamma correct
if(glConfig.deviceSupportsGamma)
R_GammaCorrect(buffer + offset, memcount);
RE_SavePNG(fileName, width, height, buffer + offset, padlen);
ri.Hunk_FreeTempMemory(buffer);
}
/*
==================
RB_TakeScreenshotCmd
@ -570,10 +553,12 @@ const void *RB_TakeScreenshotCmd( const void *data ) {
if(tess.numIndexes)
RB_EndSurface();
if (cmd->jpeg)
RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName);
else
RB_TakeScreenshot( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName);
if (cmd->type == ST_TGA)
RB_TakeScreenshotTGA( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
else if (cmd->type == ST_JPEG)
RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
else if (cmd->type == ST_PNG)
RB_TakeScreenshotPNG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName );
return (const void *)(cmd + 1);
}
@ -583,7 +568,7 @@ const void *RB_TakeScreenshotCmd( const void *data ) {
R_TakeScreenshot
==================
*/
void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean jpeg ) {
void R_TakeScreenshot( int x, int y, int width, int height, char *name, screenshotType_e type ) {
static char fileName[MAX_OSPATH]; // bad things if two screenshots per frame?
screenshotCommand_t *cmd;
@ -599,7 +584,7 @@ void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean
cmd->height = height;
Q_strncpyz( fileName, name, sizeof(fileName) );
cmd->fileName = fileName;
cmd->jpeg = jpeg;
cmd->type = type;
}
/*
@ -607,11 +592,11 @@ void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean
R_ScreenshotFilename
==================
*/
void R_ScreenshotFilename( int lastNumber, char *fileName ) {
void R_ScreenshotFilename( int lastNumber, char *fileName, char *ext ) {
int a,b,c,d;
if ( lastNumber < 0 || lastNumber > 9999 ) {
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.tga" );
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999%s", ext );
return;
}
@ -623,33 +608,8 @@ void R_ScreenshotFilename( int lastNumber, char *fileName ) {
lastNumber -= c*10;
d = lastNumber;
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.tga"
, a, b, c, d );
}
/*
==================
R_ScreenshotFilename
==================
*/
void R_ScreenshotFilenameJPEG( int lastNumber, char *fileName ) {
int a,b,c,d;
if ( lastNumber < 0 || lastNumber > 9999 ) {
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.jpg" );
return;
}
a = lastNumber / 1000;
lastNumber -= a*1000;
b = lastNumber / 100;
lastNumber -= b*100;
c = lastNumber / 10;
lastNumber -= c*10;
d = lastNumber;
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.jpg"
, a, b, c, d );
Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i%s"
, a, b, c, d, ext );
}
/*
@ -660,63 +620,93 @@ levelshots are specialized 128*128 thumbnails for
the menu system, sampled down from full screen distorted images
====================
*/
void R_LevelShot( void ) {
char checkname[MAX_OSPATH];
byte *buffer;
byte *source, *allsource;
void R_LevelShot( screenshotType_e type, const char *ext ) {
char fileName[MAX_OSPATH];
byte *source;
byte *resample, *resamplestart;
size_t offset = 0, memcount;
int spadlen, rpadlen;
int padwidth, linelen;
GLint packAlign;
byte *src, *dst;
size_t offset = 0;
int padlen;
int x, y;
int r, g, b;
float xScale, yScale;
int xx, yy;
int width, height;
int arg;
Com_sprintf(checkname, sizeof(checkname), "levelshots/%s.tga", tr.world->baseName);
// Allow custom resample width/height
arg = atoi(ri.Cmd_Argv(2));
if (arg > 0)
width = height = arg;
else
width = height = 128;
allsource = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &padlen);
source = allsource + offset;
if (width > glConfig.vidWidth)
width = glConfig.vidWidth;
if (height > glConfig.vidHeight)
height = glConfig.vidHeight;
buffer = ri.Hunk_AllocateTempMemory(128 * 128*3 + 18);
Com_Memset (buffer, 0, 18);
buffer[2] = 2; // uncompressed type
buffer[12] = 128;
buffer[14] = 128;
buffer[16] = 24; // pixel size
Com_sprintf(fileName, sizeof(fileName), "levelshots/%s%s", tr.world->baseName, ext);
source = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &spadlen);
//
// Based on RB_ReadPixels
qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign);
linelen = width * 3;
padwidth = PAD(linelen, packAlign);
// Allocate a few more bytes so that we can choose an alignment we like
resample = ri.Hunk_AllocateTempMemory(padwidth * height + offset + packAlign - 1);
resamplestart = PADP((intptr_t) resample + offset, packAlign);
offset = resamplestart - resample;
rpadlen = padwidth - linelen;
//
// resample from source
xScale = glConfig.vidWidth / 512.0f;
yScale = glConfig.vidHeight / 384.0f;
for ( y = 0 ; y < 128 ; y++ ) {
for ( x = 0 ; x < 128 ; x++ ) {
xScale = glConfig.vidWidth / (float)(width * 4.0f);
yScale = glConfig.vidHeight / (float)(height * 3.0f);
for ( y = 0 ; y < height ; y++ ) {
for ( x = 0 ; x < width ; x++ ) {
r = g = b = 0;
for ( yy = 0 ; yy < 3 ; yy++ ) {
for ( xx = 0 ; xx < 4 ; xx++ ) {
src = source + (3 * glConfig.vidWidth + padlen) * (int)((y*3 + yy) * yScale) +
src = source + (3 * glConfig.vidWidth + spadlen) * (int)((y*3 + yy) * yScale) +
3 * (int) ((x*4 + xx) * xScale);
r += src[0];
g += src[1];
b += src[2];
}
}
dst = buffer + 18 + 3 * ( y * 128 + x );
dst[0] = b / 12;
dst = resample + 3 * ( y * width + x );
dst[0] = r / 12;
dst[1] = g / 12;
dst[2] = r / 12;
dst[2] = b / 12;
}
}
memcount = (width * 3 + rpadlen) * height;
// gamma correct
if ( glConfig.deviceSupportsGamma ) {
R_GammaCorrect( buffer + 18, 128 * 128 * 3 );
}
if(glConfig.deviceSupportsGamma)
R_GammaCorrect(resample + offset, memcount);
ri.FS_WriteFile( checkname, buffer, 128 * 128*3 + 18 );
if ( type == ST_TGA )
RE_SaveTGA(fileName, width, height, resample + offset, rpadlen);
else if ( type == ST_JPEG )
RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, width, height, resample + offset, rpadlen);
else if ( type == ST_PNG )
RE_SavePNG(fileName, width, height, resample + offset, rpadlen);
ri.Hunk_FreeTempMemory(buffer);
ri.Hunk_FreeTempMemory(allsource);
ri.Hunk_FreeTempMemory(resample);
ri.Hunk_FreeTempMemory(source);
ri.Printf( PRINT_ALL, "Wrote %s\n", checkname );
ri.Printf( PRINT_ALL, "Wrote %s\n", fileName );
}
/*
@ -731,13 +721,13 @@ screenshot [filename]
Doesn't print the pacifier message if there is a second arg
==================
*/
void R_ScreenShot_f (void) {
void R_ScreenShotTGA_f (void) {
char checkname[MAX_OSPATH];
static int lastNumber = -1;
qboolean silent;
if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) {
R_LevelShot();
R_LevelShot( ST_TGA, ".tga" );
return;
}
@ -761,7 +751,7 @@ void R_ScreenShot_f (void) {
}
// scan for a free number
for ( ; lastNumber <= 9999 ; lastNumber++ ) {
R_ScreenshotFilename( lastNumber, checkname );
R_ScreenshotFilename( lastNumber, checkname, ".tga" );
if (!ri.FS_FileExists( checkname ))
{
@ -777,7 +767,7 @@ void R_ScreenShot_f (void) {
lastNumber++;
}
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qfalse );
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, ST_TGA );
if ( !silent ) {
ri.Printf (PRINT_ALL, "Wrote %s\n", checkname);
@ -790,7 +780,7 @@ void R_ScreenShotJPEG_f (void) {
qboolean silent;
if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) {
R_LevelShot();
R_LevelShot( ST_JPEG, ".jpg" );
return;
}
@ -814,7 +804,7 @@ void R_ScreenShotJPEG_f (void) {
}
// scan for a free number
for ( ; lastNumber <= 9999 ; lastNumber++ ) {
R_ScreenshotFilenameJPEG( lastNumber, checkname );
R_ScreenshotFilename( lastNumber, checkname, ".jpg" );
if (!ri.FS_FileExists( checkname ))
{
@ -822,7 +812,7 @@ void R_ScreenShotJPEG_f (void) {
}
}
if ( lastNumber == 10000 ) {
if ( lastNumber >= 9999 ) {
ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n");
return;
}
@ -830,13 +820,66 @@ void R_ScreenShotJPEG_f (void) {
lastNumber++;
}
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qtrue );
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, ST_JPEG );
if ( !silent ) {
ri.Printf (PRINT_ALL, "Wrote %s\n", checkname);
}
}
void R_ScreenShotPNG_f (void) {
char checkname[MAX_OSPATH];
static int lastNumber = -1;
qboolean silent;
if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) {
R_LevelShot( ST_PNG, ".png" );
return;
}
if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) {
silent = qtrue;
} else {
silent = qfalse;
}
if ( ri.Cmd_Argc() == 2 && !silent ) {
// explicit filename
Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.png", ri.Cmd_Argv( 1 ) );
} else {
// scan for a free filename
// if we have saved a previous screenshot, don't scan
// again, because recording demo avis can involve
// thousands of shots
if ( lastNumber == -1 ) {
lastNumber = 0;
}
// scan for a free number
for ( ; lastNumber <= 9999 ; lastNumber++ ) {
R_ScreenshotFilename( lastNumber, checkname, ".png" );
if (!ri.FS_FileExists( checkname ))
{
break; // file doesn't exist
}
}
if ( lastNumber >= 9999 ) {
ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n");
return;
}
lastNumber++;
}
R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, ST_PNG );
if ( !silent ) {
ri.Printf (PRINT_ALL, "Wrote %s\n", checkname);
}
}
//============================================================================
/*
@ -1382,8 +1425,9 @@ void R_Register( void )
ri.Cmd_AddCommand( "skinlist", R_SkinList_f );
ri.Cmd_AddCommand( "modellist", R_Modellist_f );
ri.Cmd_AddCommand( "modelist", R_ModeList_f );
ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f );
ri.Cmd_AddCommand( "screenshot", R_ScreenShotTGA_f );
ri.Cmd_AddCommand( "screenshotJPEG", R_ScreenShotJPEG_f );
ri.Cmd_AddCommand( "screenshotPNG", R_ScreenShotPNG_f );
ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f );
ri.Cmd_AddCommand( "minimize", GLimp_Minimize );
ri.Cmd_AddCommand( "gfxmeminfo", GfxMemInfo_f );
@ -1529,6 +1573,7 @@ void RE_Shutdown( qboolean destroyWindow ) {
ri.Cmd_RemoveCommand( "modelist" );
ri.Cmd_RemoveCommand( "screenshot" );
ri.Cmd_RemoveCommand( "screenshotJPEG" );
ri.Cmd_RemoveCommand( "screenshotPNG" );
ri.Cmd_RemoveCommand( "gfxinfo" );
ri.Cmd_RemoveCommand( "minimize" );
ri.Cmd_RemoveCommand( "gfxmeminfo" );

View file

@ -2393,6 +2393,12 @@ typedef struct {
int numDrawSurfs;
} drawSurfsCommand_t;
typedef enum {
ST_TGA,
ST_JPEG,
ST_PNG
} screenshotType_e;
typedef struct {
int commandId;
int x;
@ -2400,7 +2406,7 @@ typedef struct {
int width;
int height;
char *fileName;
qboolean jpeg;
screenshotType_e type;
} screenshotCommand_t;
typedef struct {
@ -2495,10 +2501,12 @@ void RE_StretchPic ( float x, float y, float w, float h,
float s1, float t1, float s2, float t2, qhandle_t hShader );
void RE_BeginFrame( stereoFrame_t stereoFrame );
void RE_EndFrame( int *frontEndMsec, int *backEndMsec );
void RE_SavePNG(const char *filename, int width, int height, byte *data, int padding);
void RE_SaveJPG(char * filename, int quality, int image_width, int image_height,
unsigned char *image_buffer, int padding);
size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality,
int image_width, int image_height, byte *image_buffer, int padding);
void RE_SaveTGA(char * filename, int image_width, int image_height, byte *image_buffer, int padding);
void RE_TakeVideoFrame( int width, int height,
byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg );

79
code/zlib/compress.c Normal file
View file

@ -0,0 +1,79 @@
/* compress.c -- compress a memory buffer
* Copyright (C) 1995-2003 Jean-loup Gailly.
* For conditions of distribution and use, see copyright notice in zlib.h
*/
/* @(#) $Id$ */
#define ZLIB_INTERNAL
#include "zlib.h"
/* ===========================================================================
Compresses the source buffer into the destination buffer. The level
parameter has the same meaning as in deflateInit. sourceLen is the byte
length of the source buffer. Upon entry, destLen is the total size of the
destination buffer, which must be at least 0.1% larger than sourceLen plus
12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
memory, Z_BUF_ERROR if there was not enough room in the output buffer,
Z_STREAM_ERROR if the level parameter is invalid.
*/
int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
Bytef *dest;
uLongf *destLen;
const Bytef *source;
uLong sourceLen;
int level;
{
z_stream stream;
int err;
stream.next_in = (Bytef*)source;
stream.avail_in = (uInt)sourceLen;
#ifdef MAXSEG_64K
/* Check for source > 64K on 16-bit machine: */
if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
#endif
stream.next_out = dest;
stream.avail_out = (uInt)*destLen;
if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
stream.zalloc = (alloc_func)0;
stream.zfree = (free_func)0;
stream.opaque = (voidpf)0;
err = deflateInit(&stream, level);
if (err != Z_OK) return err;
err = deflate(&stream, Z_FINISH);
if (err != Z_STREAM_END) {
deflateEnd(&stream);
return err == Z_OK ? Z_BUF_ERROR : err;
}
*destLen = stream.total_out;
err = deflateEnd(&stream);
return err;
}
/* ===========================================================================
*/
int ZEXPORT compress (dest, destLen, source, sourceLen)
Bytef *dest;
uLongf *destLen;
const Bytef *source;
uLong sourceLen;
{
return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
}
/* ===========================================================================
If the default memLevel or windowBits for deflateInit() is changed, then
this function needs to be updated.
*/
uLong ZEXPORT compressBound (sourceLen)
uLong sourceLen;
{
return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11;
}

1736
code/zlib/deflate.c Normal file

File diff suppressed because it is too large Load diff

331
code/zlib/deflate.h Normal file
View file

@ -0,0 +1,331 @@
/* deflate.h -- internal compression state
* Copyright (C) 1995-2004 Jean-loup Gailly
* For conditions of distribution and use, see copyright notice in zlib.h
*/
/* WARNING: this file should *not* be used by applications. It is
part of the implementation of the compression library and is
subject to change. Applications should only use zlib.h.
*/
/* @(#) $Id$ */
#ifndef DEFLATE_H
#define DEFLATE_H
#include "zutil.h"
/* define NO_GZIP when compiling if you want to disable gzip header and
trailer creation by deflate(). NO_GZIP would be used to avoid linking in
the crc code when it is not needed. For shared libraries, gzip encoding
should be left enabled. */
#ifndef NO_GZIP
# define GZIP
#endif
/* ===========================================================================
* Internal compression state.
*/
#define LENGTH_CODES 29
/* number of length codes, not counting the special END_BLOCK code */
#define LITERALS 256
/* number of literal bytes 0..255 */
#define L_CODES (LITERALS+1+LENGTH_CODES)
/* number of Literal or Length codes, including the END_BLOCK code */
#define D_CODES 30
/* number of distance codes */
#define BL_CODES 19
/* number of codes used to transfer the bit lengths */
#define HEAP_SIZE (2*L_CODES+1)
/* maximum heap size */
#define MAX_BITS 15
/* All codes must not exceed MAX_BITS bits */
#define INIT_STATE 42
#define EXTRA_STATE 69
#define NAME_STATE 73
#define COMMENT_STATE 91
#define HCRC_STATE 103
#define BUSY_STATE 113
#define FINISH_STATE 666
/* Stream status */
/* Data structure describing a single value and its code string. */
typedef struct ct_data_s {
union {
ush freq; /* frequency count */
ush code; /* bit string */
} fc;
union {
ush dad; /* father node in Huffman tree */
ush len; /* length of bit string */
} dl;
} FAR ct_data;
#define Freq fc.freq
#define Code fc.code
#define Dad dl.dad
#define Len dl.len
typedef struct static_tree_desc_s static_tree_desc;
typedef struct tree_desc_s {
ct_data *dyn_tree; /* the dynamic tree */
int max_code; /* largest code with non zero frequency */
static_tree_desc *stat_desc; /* the corresponding static tree */
} FAR tree_desc;
typedef ush Pos;
typedef Pos FAR Posf;
typedef unsigned IPos;
/* A Pos is an index in the character window. We use short instead of int to
* save space in the various tables. IPos is used only for parameter passing.
*/
typedef struct internal_state {
z_streamp strm; /* pointer back to this zlib stream */
int status; /* as the name implies */
Bytef *pending_buf; /* output still pending */
ulg pending_buf_size; /* size of pending_buf */
Bytef *pending_out; /* next pending byte to output to the stream */
uInt pending; /* nb of bytes in the pending buffer */
int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
gz_headerp gzhead; /* gzip header information to write */
uInt gzindex; /* where in extra, name, or comment */
Byte method; /* STORED (for zip only) or DEFLATED */
int last_flush; /* value of flush param for previous deflate call */
/* used by deflate.c: */
uInt w_size; /* LZ77 window size (32K by default) */
uInt w_bits; /* log2(w_size) (8..16) */
uInt w_mask; /* w_size - 1 */
Bytef *window;
/* Sliding window. Input bytes are read into the second half of the window,
* and move to the first half later to keep a dictionary of at least wSize
* bytes. With this organization, matches are limited to a distance of
* wSize-MAX_MATCH bytes, but this ensures that IO is always
* performed with a length multiple of the block size. Also, it limits
* the window size to 64K, which is quite useful on MSDOS.
* To do: use the user input buffer as sliding window.
*/
ulg window_size;
/* Actual size of window: 2*wSize, except when the user input buffer
* is directly used as sliding window.
*/
Posf *prev;
/* Link to older string with same hash index. To limit the size of this
* array to 64K, this link is maintained only for the last 32K strings.
* An index in this array is thus a window index modulo 32K.
*/
Posf *head; /* Heads of the hash chains or NIL. */
uInt ins_h; /* hash index of string to be inserted */
uInt hash_size; /* number of elements in hash table */
uInt hash_bits; /* log2(hash_size) */
uInt hash_mask; /* hash_size-1 */
uInt hash_shift;
/* Number of bits by which ins_h must be shifted at each input
* step. It must be such that after MIN_MATCH steps, the oldest
* byte no longer takes part in the hash key, that is:
* hash_shift * MIN_MATCH >= hash_bits
*/
long block_start;
/* Window position at the beginning of the current output block. Gets
* negative when the window is moved backwards.
*/
uInt match_length; /* length of best match */
IPos prev_match; /* previous match */
int match_available; /* set if previous match exists */
uInt strstart; /* start of string to insert */
uInt match_start; /* start of matching string */
uInt lookahead; /* number of valid bytes ahead in window */
uInt prev_length;
/* Length of the best match at previous step. Matches not greater than this
* are discarded. This is used in the lazy match evaluation.
*/
uInt max_chain_length;
/* To speed up deflation, hash chains are never searched beyond this
* length. A higher limit improves compression ratio but degrades the
* speed.
*/
uInt max_lazy_match;
/* Attempt to find a better match only when the current match is strictly
* smaller than this value. This mechanism is used only for compression
* levels >= 4.
*/
# define max_insert_length max_lazy_match
/* Insert new strings in the hash table only if the match length is not
* greater than this length. This saves time but degrades compression.
* max_insert_length is used only for compression levels <= 3.
*/
int level; /* compression level (1..9) */
int strategy; /* favor or force Huffman coding*/
uInt good_match;
/* Use a faster search when the previous match is longer than this */
int nice_match; /* Stop searching when current match exceeds this */
/* used by trees.c: */
/* Didn't use ct_data typedef below to supress compiler warning */
struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
struct tree_desc_s l_desc; /* desc. for literal tree */
struct tree_desc_s d_desc; /* desc. for distance tree */
struct tree_desc_s bl_desc; /* desc. for bit length tree */
ush bl_count[MAX_BITS+1];
/* number of codes at each bit length for an optimal tree */
int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
int heap_len; /* number of elements in the heap */
int heap_max; /* element of largest frequency */
/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
* The same heap array is used to build all trees.
*/
uch depth[2*L_CODES+1];
/* Depth of each subtree used as tie breaker for trees of equal frequency
*/
uchf *l_buf; /* buffer for literals or lengths */
uInt lit_bufsize;
/* Size of match buffer for literals/lengths. There are 4 reasons for
* limiting lit_bufsize to 64K:
* - frequencies can be kept in 16 bit counters
* - if compression is not successful for the first block, all input
* data is still in the window so we can still emit a stored block even
* when input comes from standard input. (This can also be done for
* all blocks if lit_bufsize is not greater than 32K.)
* - if compression is not successful for a file smaller than 64K, we can
* even emit a stored file instead of a stored block (saving 5 bytes).
* This is applicable only for zip (not gzip or zlib).
* - creating new Huffman trees less frequently may not provide fast
* adaptation to changes in the input data statistics. (Take for
* example a binary file with poorly compressible code followed by
* a highly compressible string table.) Smaller buffer sizes give
* fast adaptation but have of course the overhead of transmitting
* trees more frequently.
* - I can't count above 4
*/
uInt last_lit; /* running index in l_buf */
ushf *d_buf;
/* Buffer for distances. To simplify the code, d_buf and l_buf have
* the same number of elements. To use different lengths, an extra flag
* array would be necessary.
*/
ulg opt_len; /* bit length of current block with optimal trees */
ulg static_len; /* bit length of current block with static trees */
uInt matches; /* number of string matches in current block */
int last_eob_len; /* bit length of EOB code for last block */
#ifdef DEBUG
ulg compressed_len; /* total bit length of compressed file mod 2^32 */
ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
#endif
ush bi_buf;
/* Output buffer. bits are inserted starting at the bottom (least
* significant bits).
*/
int bi_valid;
/* Number of valid bits in bi_buf. All bits above the last valid bit
* are always zero.
*/
} FAR deflate_state;
/* Output a byte on the stream.
* IN assertion: there is enough room in pending_buf.
*/
#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
/* Minimum amount of lookahead, except at the end of the input file.
* See deflate.c for comments about the MIN_MATCH+1.
*/
#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
/* In order to simplify the code, particularly on 16 bit machines, match
* distances are limited to MAX_DIST instead of WSIZE.
*/
/* in trees.c */
void _tr_init OF((deflate_state *s));
int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
int eof));
void _tr_align OF((deflate_state *s));
void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
int eof));
#define d_code(dist) \
((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
/* Mapping from a distance to a distance code. dist is the distance - 1 and
* must not have side effects. _dist_code[256] and _dist_code[257] are never
* used.
*/
#ifndef DEBUG
/* Inline versions of _tr_tally for speed: */
#if defined(GEN_TREES_H) || !defined(STDC)
extern uch _length_code[];
extern uch _dist_code[];
#else
extern const uch _length_code[];
extern const uch _dist_code[];
#endif
# define _tr_tally_lit(s, c, flush) \
{ uch cc = (c); \
s->d_buf[s->last_lit] = 0; \
s->l_buf[s->last_lit++] = cc; \
s->dyn_ltree[cc].Freq++; \
flush = (s->last_lit == s->lit_bufsize-1); \
}
# define _tr_tally_dist(s, distance, length, flush) \
{ uch len = (length); \
ush dist = (distance); \
s->d_buf[s->last_lit] = dist; \
s->l_buf[s->last_lit++] = len; \
dist--; \
s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
s->dyn_dtree[d_code(dist)].Freq++; \
flush = (s->last_lit == s->lit_bufsize-1); \
}
#else
# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
# define _tr_tally_dist(s, distance, length, flush) \
flush = _tr_tally(s, distance, length)
#endif
#endif /* DEFLATE_H */

1219
code/zlib/trees.c Normal file

File diff suppressed because it is too large Load diff

128
code/zlib/trees.h Normal file
View file

@ -0,0 +1,128 @@
/* header created automatically with -DGEN_TREES_H */
local const ct_data static_ltree[L_CODES+2] = {
{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
};
local const ct_data static_dtree[D_CODES] = {
{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
};
const uch _dist_code[DIST_CODE_LEN] = {
0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
};
const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
};
local const int base_length[LENGTH_CODES] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
64, 80, 96, 112, 128, 160, 192, 224, 0
};
local const int base_dist[D_CODES] = {
0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
};