mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@1871 3b4a5df2-a311-0410-b5c6-a8a6f20db521
834 lines
23 KiB
C++
834 lines
23 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Utils All.h"
|
|
#else
|
|
#include "builddefines.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "types.h"
|
|
#include "compression.h"
|
|
#include "debug.h"
|
|
#include "fileman.h"
|
|
#include "imgfmt.h"
|
|
#include "himage.h"
|
|
#include "pcx.h"
|
|
#include "impTGA.h"
|
|
#include "wcheck.h"
|
|
#endif
|
|
|
|
//CONVERT_TO_16_BIT
|
|
BOOLEAN ConvertToETRLE( UINT8 ** ppDest, UINT32 * puiDestLen, UINT8 ** ppSubImageBuffer, UINT16 * pusNumberOfSubImages, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, UINT32 fFlags );
|
|
|
|
#define CONVERT_ADD_APPDATA 0x0001
|
|
#define CONVERT_ADD_JA2DATA 0x0003
|
|
#define CONVERT_ZLIB_COMPRESS 0x0010
|
|
#define CONVERT_ETRLE_COMPRESS 0x0020
|
|
#define CONVERT_ETRLE_COMPRESS_SINGLE 0x0040
|
|
#define CONVERT_ETRLE_NO_SUBIMAGE_SHRINKING 0x0080
|
|
#define CONVERT_ETRLE_DONT_SKIP_BLANKS 0x0100
|
|
#define CONVERT_ETRLE_FLIC 0x0200
|
|
#define CONVERT_ETRLE_FLIC_TRIM 0x0400
|
|
#define CONVERT_ETRLE_FLIC_NAME 0x0800
|
|
#define CONVERT_TO_8_BIT 0x1000
|
|
#define CONVERT_TO_16_BIT 0x2000
|
|
// NB 18-bit is actually 24 bit but with only 6 bits used in each byte. I implemented
|
|
// it to see how well such images would compress with ZLIB.
|
|
#define CONVERT_TO_18_BIT 0x4000
|
|
|
|
// Defines for inserting red/green/blue values into a 16-bit pixel.
|
|
// MASK is the mask to use to get the proper bits out of a byte (part of a 24-bit pixel)
|
|
// use SHIFT_RIGHT to move the masked bits to the lowest bits of the byte
|
|
// use SHIFT_LEFT to put the bits in their proper place in the 16-bit pixel
|
|
#define RED_DEPTH_16 5
|
|
#define GREEN_DEPTH_16 6
|
|
#define BLUE_DEPTH_16 5
|
|
#define RED_MASK_16 0xF8
|
|
#define RED_SHIFT_RIGHT_16 3
|
|
#define RED_SHIFT_LEFT_16 11
|
|
#define GREEN_MASK_16 0xFC
|
|
#define GREEN_SHIFT_RIGHT_16 2
|
|
#define GREEN_SHIFT_LEFT_16 5
|
|
#define BLUE_MASK_16 0xF8
|
|
#define BLUE_SHIFT_RIGHT_16 3
|
|
#define BLUE_SHIFT_LEFT_16 0
|
|
|
|
#define RED_DEPTH_24 8
|
|
#define GREEN_DEPTH_24 8
|
|
#define BLUE_DEPTH_24 8
|
|
#define RED_MASK_24 0x00FF0000
|
|
#define GREEN_MASK_24 0x0000FF00
|
|
#define BLUE_MASK_24 0x000000FF
|
|
|
|
//#define JA2_OBJECT_DATA_SIZE 16
|
|
|
|
// this funky union is used for fast 16-bit pixel format conversions
|
|
typedef union
|
|
{
|
|
struct
|
|
{
|
|
UINT16 usLower;
|
|
UINT16 usHigher;
|
|
};
|
|
UINT32 uiValue;
|
|
} SplitUINT32;
|
|
|
|
|
|
|
|
void ConvertRGBDistribution555To565( UINT16 * p16BPPData, UINT32 uiNumberOfPixels )
|
|
{
|
|
UINT16 * pPixel;
|
|
UINT32 uiLoop;
|
|
|
|
SplitUINT32 Pixel;
|
|
|
|
pPixel = p16BPPData;
|
|
for (uiLoop = 0; uiLoop < uiNumberOfPixels; uiLoop++)
|
|
{
|
|
// we put the 16 pixel bits in the UPPER word of uiPixel, so that we can
|
|
// right shift the blue value (at the bottom) into the LOWER word to keep it
|
|
// out of the way
|
|
Pixel.usHigher = *pPixel;
|
|
Pixel.uiValue >>= 5;
|
|
// add a least significant bit to green
|
|
Pixel.usHigher <<= 1;
|
|
// now shift back into the upper word
|
|
Pixel.uiValue <<= 5;
|
|
// and copy back
|
|
*pPixel = Pixel.usHigher;
|
|
pPixel++;
|
|
}
|
|
}
|
|
|
|
void WriteSTIFile( INT8 *pData, SGPPaletteEntry *pPalette, INT16 sWidth, INT16 sHeight, STR cOutputName, UINT32 fFlags, UINT32 uiAppDataSize )
|
|
{
|
|
|
|
FILE * pOutput;
|
|
|
|
UINT32 uiOriginalSize;
|
|
UINT8 * pOutputBuffer = NULL;
|
|
UINT32 uiCompressedSize;
|
|
|
|
STCIHeader Header;
|
|
UINT32 uiLoop;
|
|
image_type Image;
|
|
|
|
SGPPaletteEntry * pSGPPaletteEntry;
|
|
STCIPaletteElement STCIPaletteEntry;
|
|
|
|
STCISubImage * pSubImageBuffer = NULL;
|
|
UINT16 usNumberOfSubImages;
|
|
UINT32 uiSubImageBufferSize=0;
|
|
|
|
//UINT16 usLoop;
|
|
|
|
memset( &Header, 0, STCI_HEADER_SIZE );
|
|
memset( &Image, 0, sizeof( image_type ));
|
|
|
|
|
|
uiOriginalSize = sWidth * sHeight * (8 / 8);
|
|
|
|
// set up STCI header for output
|
|
memcpy( Header.cID, STCI_ID_STRING, STCI_ID_LEN );
|
|
Header.uiTransparentValue = 0;
|
|
Header.usHeight = sHeight;
|
|
Header.usWidth = sWidth;
|
|
Header.ubDepth = 8;
|
|
Header.uiOriginalSize = uiOriginalSize;
|
|
Header.uiStoredSize = uiOriginalSize;
|
|
Header.uiAppDataSize = uiAppDataSize;
|
|
|
|
Header.fFlags |= STCI_INDEXED;
|
|
if (Header.ubDepth == 8)
|
|
{
|
|
// assume 8-bit pixels indexing into 256 colour palette with 24 bit values in
|
|
// the palette
|
|
Header.Indexed.uiNumberOfColours = 256;
|
|
Header.Indexed.ubRedDepth = 8;
|
|
Header.Indexed.ubGreenDepth = 8;
|
|
Header.Indexed.ubBlueDepth = 8;
|
|
}
|
|
|
|
if ((Header.fFlags & STCI_INDEXED) && (fFlags & CONVERT_ETRLE_COMPRESS))
|
|
{
|
|
if( !ConvertToETRLE( &pOutputBuffer, &uiCompressedSize, (UINT8 **) &pSubImageBuffer, &usNumberOfSubImages, (UINT8 *)pData, sWidth, sHeight, fFlags ) )
|
|
{
|
|
}
|
|
uiSubImageBufferSize = (UINT32) usNumberOfSubImages * STCI_SUBIMAGE_SIZE;
|
|
|
|
Header.Indexed.usNumberOfSubImages = usNumberOfSubImages;
|
|
Header.uiStoredSize = uiCompressedSize;
|
|
Header.fFlags |= STCI_ETRLE_COMPRESSED;
|
|
}
|
|
|
|
//
|
|
// save file
|
|
//
|
|
|
|
pOutput = fopen( cOutputName, "wb" );
|
|
if (pOutput == NULL )
|
|
{
|
|
return;
|
|
}
|
|
// write header
|
|
fwrite( &Header, STCI_HEADER_SIZE, 1, pOutput );
|
|
// write palette and subimage structs, if any
|
|
if (Header.fFlags & STCI_INDEXED)
|
|
{
|
|
if (pPalette != NULL)
|
|
{
|
|
// have to convert palette to STCI format!
|
|
pSGPPaletteEntry = pPalette;
|
|
for (uiLoop = 0; uiLoop < 256; uiLoop++)
|
|
{
|
|
STCIPaletteEntry.ubRed = pSGPPaletteEntry[uiLoop].peRed;
|
|
STCIPaletteEntry.ubGreen = pSGPPaletteEntry[uiLoop].peGreen;
|
|
STCIPaletteEntry.ubBlue = pSGPPaletteEntry[uiLoop].peBlue;
|
|
fwrite( &STCIPaletteEntry, STCI_PALETTE_ELEMENT_SIZE, 1, pOutput );
|
|
}
|
|
}
|
|
if (Header.fFlags & STCI_ETRLE_COMPRESSED)
|
|
{
|
|
fwrite( pSubImageBuffer, uiSubImageBufferSize, 1, pOutput );
|
|
}
|
|
}
|
|
|
|
// write file data
|
|
if (Header.fFlags & STCI_ZLIB_COMPRESSED || Header.fFlags & STCI_ETRLE_COMPRESSED)
|
|
{
|
|
fwrite( pOutputBuffer, Header.uiStoredSize, 1, pOutput );
|
|
}
|
|
else
|
|
{
|
|
fwrite( Image.pImageData, Header.uiStoredSize, 1, pOutput );
|
|
}
|
|
|
|
// write app-specific data (blanked to 0)
|
|
if (Image.pAppData == NULL )
|
|
{
|
|
if (Header.uiAppDataSize > 0)
|
|
{
|
|
for (uiLoop = 0; uiLoop < Header.uiAppDataSize; uiLoop++)
|
|
{
|
|
fputc( 0, pOutput );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fwrite( Image.pAppData, Header.uiAppDataSize, 1, pOutput );
|
|
}
|
|
|
|
fclose( pOutput );
|
|
|
|
if( pOutputBuffer != NULL )
|
|
{
|
|
MemFree( pOutputBuffer );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define COMPRESS_TRANSPARENT 0x80
|
|
#define COMPRESS_NON_TRANSPARENT 0x00
|
|
#define COMPRESS_RUN_LIMIT 0x7F
|
|
|
|
#define TCI 0x00
|
|
#define WI 0xFF
|
|
|
|
UINT32 ETRLECompressSubImage( UINT8 * pDest, UINT32 uiDestLen, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage );
|
|
UINT32 ETRLECompress( UINT8 * pDest, UINT32 uiDestLen, UINT8 * pSource, UINT32 uiSourceLen );
|
|
BOOLEAN DetermineOffset( UINT32 * puiOffset, UINT16 usWidth, UINT16 usHeight, INT16 sX, INT16 sY );
|
|
BOOLEAN GoPastWall( INT16 * psNewX, INT16 * psNewY, UINT16 usWidth, UINT16 usHeight, UINT8 * pCurrent, INT16 sCurrX, INT16 sCurrY );
|
|
BOOLEAN GoToNextSubImage( INT16 * psNewX, INT16 * psNewY, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, INT16 sOrigX, INT16 sOrigY );
|
|
BOOLEAN DetermineSubImageSize( UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage );
|
|
BOOLEAN DetermineSubImageUsedSize( UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage );
|
|
BOOLEAN CheckForDataInRows( INT16 * psXValue, INT16 sXIncrement, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage );
|
|
BOOLEAN CheckForDataInCols( INT16 * psXValue, INT16 sXIncrement, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage );
|
|
UINT8 * CheckForDataInRowOrColumn( UINT8 * pPixel, UINT16 usIncrement, UINT16 usNumberOfPixels );
|
|
|
|
BOOLEAN ConvertToETRLE( UINT8 ** ppDest, UINT32 * puiDestLen, UINT8 ** ppSubImageBuffer, UINT16 * pusNumberOfSubImages, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, UINT32 fFlags )
|
|
{
|
|
INT16 sCurrX;
|
|
INT16 sCurrY;
|
|
INT16 sNextX;
|
|
INT16 sNextY;
|
|
UINT8 * pOutputNext;
|
|
UINT8 * pTemp;
|
|
BOOLEAN fContinue = TRUE;
|
|
BOOLEAN fOk = TRUE;
|
|
BOOLEAN fStore;
|
|
BOOLEAN fNextExists;
|
|
STCISubImage * pCurrSubImage;
|
|
STCISubImage TempSubImage;
|
|
UINT32 uiSubImageCompressedSize;
|
|
UINT32 uiSpaceLeft;
|
|
|
|
// worst-case situation estimate
|
|
uiSpaceLeft = (UINT32) usWidth * (UINT32) usHeight * 3;
|
|
*ppDest = (UINT8 *) MemAlloc( uiSpaceLeft );
|
|
CHECKF( *ppDest );
|
|
*puiDestLen = uiSpaceLeft;
|
|
|
|
pOutputNext = *ppDest;
|
|
|
|
if (fFlags & CONVERT_ETRLE_COMPRESS_SINGLE)
|
|
{
|
|
// there are no walls in this image, but we treat it as a "subimage" for
|
|
// the purposes of calling the compressor
|
|
|
|
// we want a 1-element SubImage array for this...
|
|
// allocate!
|
|
*pusNumberOfSubImages = 1;
|
|
*ppSubImageBuffer = (UINT8 *) MemAlloc( STCI_SUBIMAGE_SIZE );
|
|
if (!(*ppSubImageBuffer))
|
|
{
|
|
MemFree( *ppDest );
|
|
return( FALSE );
|
|
}
|
|
pCurrSubImage = (STCISubImage *) *ppSubImageBuffer;
|
|
pCurrSubImage->sOffsetX = 0;
|
|
pCurrSubImage->sOffsetY = 0;
|
|
pCurrSubImage->usWidth = usWidth;
|
|
pCurrSubImage->usHeight = usHeight;
|
|
if (!(fFlags & CONVERT_ETRLE_NO_SUBIMAGE_SHRINKING))
|
|
{
|
|
if (!(DetermineSubImageUsedSize( p8BPPBuffer, usWidth, usHeight, pCurrSubImage )))
|
|
{
|
|
MemFree( *ppDest );
|
|
return( FALSE );
|
|
}
|
|
}
|
|
uiSubImageCompressedSize = ETRLECompressSubImage( pOutputNext, uiSpaceLeft, p8BPPBuffer, usWidth, usHeight, pCurrSubImage );
|
|
if (uiSubImageCompressedSize == 0)
|
|
{
|
|
MemFree( *ppDest );
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
pCurrSubImage->uiDataOffset = 0;
|
|
pCurrSubImage->uiDataLength = uiSubImageCompressedSize;
|
|
*puiDestLen = uiSubImageCompressedSize;
|
|
return( TRUE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// skip any initial wall bytes to find the first subimage
|
|
if (!GoPastWall( &sCurrX, &sCurrY, usWidth, usHeight, p8BPPBuffer, 0, 0 ))
|
|
{ // no subimages!
|
|
MemFree( *ppDest );
|
|
return( FALSE );
|
|
}
|
|
*ppSubImageBuffer = NULL;
|
|
*pusNumberOfSubImages = 0;
|
|
|
|
while (fContinue)
|
|
{
|
|
// allocate more memory for SubImage structures, and set the current pointer to the last one
|
|
pTemp = (UINT8 *) MemRealloc( *ppSubImageBuffer, (*pusNumberOfSubImages + 1) * STCI_SUBIMAGE_SIZE );
|
|
if (pTemp == NULL)
|
|
{
|
|
fOk = FALSE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
*ppSubImageBuffer = pTemp;
|
|
}
|
|
pCurrSubImage = (STCISubImage *) (*ppSubImageBuffer + (*pusNumberOfSubImages) * STCI_SUBIMAGE_SIZE);
|
|
|
|
pCurrSubImage->sOffsetX = sCurrX;
|
|
pCurrSubImage->sOffsetY = sCurrY;
|
|
// determine the subimage's full size
|
|
if (!DetermineSubImageSize( p8BPPBuffer, usWidth, usHeight, pCurrSubImage ))
|
|
{
|
|
fOk = FALSE;
|
|
break;
|
|
}
|
|
if (*pusNumberOfSubImages == 0 && pCurrSubImage->usWidth == usWidth && pCurrSubImage->usHeight == usHeight)
|
|
{
|
|
printf( "\tWarning: no walls (subimage delimiters) found.\n" );
|
|
}
|
|
|
|
memcpy( &TempSubImage, pCurrSubImage, STCI_SUBIMAGE_SIZE );
|
|
if (DetermineSubImageUsedSize( p8BPPBuffer, usWidth, usHeight, &TempSubImage))
|
|
{
|
|
// image has nontransparent data; we definitely want to store it
|
|
fStore = TRUE;
|
|
if (!(fFlags & CONVERT_ETRLE_NO_SUBIMAGE_SHRINKING))
|
|
{
|
|
memcpy( pCurrSubImage, &TempSubImage, STCI_SUBIMAGE_SIZE );
|
|
}
|
|
}
|
|
else if (fFlags & CONVERT_ETRLE_DONT_SKIP_BLANKS)
|
|
{
|
|
// image is transparent; we will store it if there is another subimage
|
|
// to the right of it on the same line
|
|
// find the next subimage
|
|
fNextExists = GoToNextSubImage( &sNextX, &sNextY, p8BPPBuffer, usWidth, usHeight, sCurrX, sCurrY );
|
|
if (fNextExists && sNextY == sCurrY )
|
|
{
|
|
fStore = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// junk transparent section at the end of the line!
|
|
fStore = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// transparent data; discarding
|
|
fStore = FALSE;
|
|
}
|
|
|
|
if (fStore)
|
|
{
|
|
// we want to store this subimage!
|
|
uiSubImageCompressedSize = ETRLECompressSubImage( pOutputNext, uiSpaceLeft, p8BPPBuffer, usWidth, usHeight, pCurrSubImage );
|
|
if (uiSubImageCompressedSize == 0)
|
|
{
|
|
fOk = FALSE;
|
|
break;
|
|
}
|
|
pCurrSubImage->uiDataOffset = (*puiDestLen - uiSpaceLeft);
|
|
pCurrSubImage->uiDataLength = uiSubImageCompressedSize;
|
|
// this is a cheap hack; the sOffsetX and sOffsetY values have been used
|
|
// to store the location of the subimage within the whole image. Now
|
|
// we want the offset within the subimage, so, we subtract the coordatines
|
|
// for the upper-left corner of the subimage.
|
|
pCurrSubImage->sOffsetX -= sCurrX;
|
|
pCurrSubImage->sOffsetY -= sCurrY;
|
|
(*pusNumberOfSubImages)++;
|
|
pOutputNext += uiSubImageCompressedSize;
|
|
uiSpaceLeft -= uiSubImageCompressedSize;
|
|
}
|
|
// find the next subimage
|
|
fContinue = GoToNextSubImage( &sCurrX, &sCurrY, p8BPPBuffer, usWidth, usHeight, sCurrX, sCurrY );
|
|
}
|
|
}
|
|
if (fOk)
|
|
{
|
|
*puiDestLen -= uiSpaceLeft;
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
MemFree( *ppDest );
|
|
if (*ppSubImageBuffer != NULL)
|
|
{
|
|
MemFree( *ppSubImageBuffer );
|
|
}
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
UINT32 ETRLECompressSubImage( UINT8 * pDest, UINT32 uiDestLen, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage )
|
|
{
|
|
UINT16 usLoop;
|
|
UINT32 uiScanLineCompressedSize;
|
|
UINT32 uiSpaceLeft = uiDestLen;
|
|
UINT32 uiOffset;
|
|
UINT8 * pCurrent;
|
|
|
|
CHECKF( DetermineOffset( &uiOffset, usWidth, usHeight, pSubImage->sOffsetX, pSubImage->sOffsetY ) )
|
|
pCurrent = p8BPPBuffer + uiOffset;
|
|
|
|
for (usLoop = 0; usLoop < pSubImage->usHeight; usLoop++)
|
|
{
|
|
uiScanLineCompressedSize = ETRLECompress( pDest, uiSpaceLeft, pCurrent, pSubImage->usWidth );
|
|
if (uiScanLineCompressedSize == 0 )
|
|
{ // there wasn't enough room to complete the compression!
|
|
return( 0 );
|
|
}
|
|
// reduce the amount of available space
|
|
uiSpaceLeft -= uiScanLineCompressedSize;
|
|
pDest += uiScanLineCompressedSize;
|
|
// go to the next scanline
|
|
pCurrent += usWidth;
|
|
}
|
|
return( uiDestLen - uiSpaceLeft );
|
|
}
|
|
|
|
UINT32 ETRLECompress( UINT8 * pDest, UINT32 uiDestLen, UINT8 * pSource, UINT32 uiSourceLen )
|
|
{
|
|
// Compress a buffer (a scanline) into ETRLE format, which is a series of runs.
|
|
// Each run starts with a byte whose high bit is 1 if the run is compressed, 0 otherwise.
|
|
// The lower seven bits of that byte indicate the length of the run
|
|
|
|
// ETRLECompress returns the number of bytes used by the compressed buffer, or 0 if an error
|
|
// occurred
|
|
|
|
// uiSourceLoc keeps track of our current position in the
|
|
// source
|
|
UINT32 uiSourceLoc = 0;
|
|
// uiCurrentSourceLoc is used to look ahead in the source to
|
|
// determine the length of runs
|
|
UINT32 uiCurrentSourceLoc = 0;
|
|
UINT32 uiDestLoc = 0;
|
|
UINT8 ubLength = 0;
|
|
|
|
|
|
while (uiSourceLoc < uiSourceLen && uiDestLoc < uiDestLen)
|
|
{
|
|
if (pSource[uiSourceLoc] == TCI)
|
|
{ // transparent run - determine its length
|
|
do
|
|
{
|
|
uiCurrentSourceLoc++;
|
|
ubLength++;
|
|
}
|
|
while ((uiCurrentSourceLoc < uiSourceLen) && pSource[uiCurrentSourceLoc] == TCI && (ubLength < COMPRESS_RUN_LIMIT));
|
|
// output run-byte
|
|
pDest[uiDestLoc] = ubLength | COMPRESS_TRANSPARENT;
|
|
|
|
// update location
|
|
uiSourceLoc += ubLength;
|
|
uiDestLoc += 1;
|
|
}
|
|
else
|
|
{ // non-transparent run - determine its length
|
|
do
|
|
{
|
|
uiCurrentSourceLoc++;
|
|
ubLength++;
|
|
}
|
|
while ((uiCurrentSourceLoc < uiSourceLen) && (pSource[uiCurrentSourceLoc] != TCI) && (ubLength < COMPRESS_RUN_LIMIT));
|
|
if (uiDestLoc + ubLength < uiDestLen)
|
|
{
|
|
// output run-byte
|
|
pDest[uiDestLoc++] = ubLength | COMPRESS_NON_TRANSPARENT;
|
|
|
|
// output run (and update location)
|
|
memcpy( pDest + uiDestLoc, pSource + uiSourceLoc, ubLength );
|
|
uiSourceLoc += ubLength;
|
|
uiDestLoc += ubLength;
|
|
}
|
|
else
|
|
{ // not enough room in dest buffer to copy the run!
|
|
return( 0 );
|
|
}
|
|
}
|
|
uiCurrentSourceLoc = uiSourceLoc;
|
|
ubLength = 0;
|
|
}
|
|
if (uiDestLoc >= uiDestLen)
|
|
{
|
|
return( 0 );
|
|
}
|
|
else
|
|
{
|
|
// end with a run of 0 length (which might as well be non-transparent,
|
|
// giving a 0-byte
|
|
pDest[uiDestLoc++] = 0;
|
|
return( uiDestLoc );
|
|
}
|
|
}
|
|
|
|
BOOLEAN DetermineOffset( UINT32 * puiOffset, UINT16 usWidth, UINT16 usHeight, INT16 sX, INT16 sY )
|
|
{
|
|
if (sX < 0 || sY < 0)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
*puiOffset = (UINT32) sY * (UINT32) usWidth + (UINT32) sX;
|
|
if (*puiOffset >= (UINT32) usWidth * (UINT32) usHeight)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN GoPastWall( INT16 * psNewX, INT16 * psNewY, UINT16 usWidth, UINT16 usHeight, UINT8 * pCurrent, INT16 sCurrX, INT16 sCurrY )
|
|
{
|
|
// If the current pixel is a wall, we assume that it is on a horizontal wall and
|
|
// search right, wrapping around the end of scanlines, until we find non-wall data.
|
|
while (*pCurrent == WI)
|
|
{
|
|
sCurrX++;
|
|
pCurrent++;
|
|
if (sCurrX == usWidth)
|
|
{ // wrap our logical coordinates!
|
|
sCurrX = 0;
|
|
sCurrY++;
|
|
if( sCurrY == usHeight)
|
|
{
|
|
// no more images!
|
|
return( FALSE );
|
|
}
|
|
}
|
|
}
|
|
|
|
*psNewX = sCurrX;
|
|
*psNewY = sCurrY;
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN GoToNextSubImage( INT16 * psNewX, INT16 * psNewY, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, INT16 sOrigX, INT16 sOrigY )
|
|
{
|
|
// return the coordinates of the next subimage in the image
|
|
// (either to the right, or the first of the next row down
|
|
INT16 sCurrX = sOrigX;
|
|
INT16 sCurrY = sOrigY;
|
|
UINT32 uiOffset;
|
|
UINT8 * pCurrent;
|
|
BOOLEAN fFound = TRUE;
|
|
|
|
CHECKF( DetermineOffset( &uiOffset, usWidth, usHeight, sCurrX, sCurrY ) )
|
|
pCurrent = p8BPPBuffer + uiOffset;
|
|
|
|
if (*pCurrent == WI)
|
|
{
|
|
return( GoPastWall( psNewX, psNewY, usWidth, usHeight, pCurrent, sCurrX, sCurrY ) );
|
|
}
|
|
else
|
|
{
|
|
// The current pixel is not a wall. We scan right past all non-wall data to skip to
|
|
// the right-hand end of the subimage, then right past all wall data to skip a vertical
|
|
// wall, and should find ourselves at another subimage.
|
|
|
|
// If we hit the right edge of the image, we back up to our start point, go DOWN to
|
|
// the bottom of the image to the horizontal wall, and then recurse to go along it
|
|
// to the right place on the next scanline
|
|
|
|
while (*pCurrent != WI)
|
|
{
|
|
sCurrX++;
|
|
pCurrent++;
|
|
if (sCurrX == usWidth)
|
|
{ // there are no more images to the right!
|
|
fFound = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (sCurrX < usWidth)
|
|
{
|
|
// skip all wall data to the right, starting at the new current position
|
|
while (*pCurrent == WI)
|
|
{
|
|
sCurrX++;
|
|
pCurrent++;
|
|
if (sCurrX == usWidth)
|
|
{ // there are no more images to the right!
|
|
fFound = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (fFound)
|
|
{
|
|
*psNewX = sCurrX;
|
|
*psNewY = sCurrY;
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
// go back to the beginning of the subimage and scan down
|
|
sCurrX = sOrigX;
|
|
pCurrent = p8BPPBuffer + uiOffset;
|
|
|
|
// skip all non-wall data below, starting at the current position
|
|
while (*pCurrent != WI)
|
|
{
|
|
sCurrY++;
|
|
pCurrent += usWidth;
|
|
if (sCurrY == usHeight)
|
|
{ // there are no more images!
|
|
return( FALSE );
|
|
}
|
|
}
|
|
// We are now at the horizontal wall at the bottom of the current image
|
|
return( GoPastWall( psNewX, psNewY, usWidth, usHeight, pCurrent, sCurrX, sCurrY ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN DetermineSubImageSize( UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage )
|
|
{
|
|
UINT32 uiOffset;
|
|
UINT8 * pCurrent;
|
|
INT16 sCurrX = pSubImage->sOffsetX;
|
|
INT16 sCurrY = pSubImage->sOffsetY;
|
|
|
|
if (!DetermineOffset( &uiOffset, usWidth, usHeight, sCurrX, sCurrY ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// determine width
|
|
pCurrent = p8BPPBuffer + uiOffset;
|
|
do
|
|
{
|
|
sCurrX++;
|
|
pCurrent++;
|
|
} while( *pCurrent != WI && sCurrX < usWidth );
|
|
pSubImage->usWidth = sCurrX - pSubImage->sOffsetX;
|
|
|
|
// determine height
|
|
pCurrent = p8BPPBuffer + uiOffset;
|
|
do
|
|
{
|
|
sCurrY++;
|
|
pCurrent += usWidth;
|
|
} while( *pCurrent != WI && sCurrY < usHeight );
|
|
pSubImage->usHeight = sCurrY - pSubImage->sOffsetY;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN DetermineSubImageUsedSize( UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage )
|
|
{
|
|
INT16 sNewValue;
|
|
// to do our search loops properly, we can't change the height and width of the
|
|
// subimages until we're done all of our shrinks
|
|
UINT16 usNewHeight;
|
|
UINT16 usNewWidth;
|
|
UINT16 usNewX;
|
|
UINT16 usNewY;
|
|
|
|
// shrink from the top
|
|
if (CheckForDataInRows( &sNewValue, 1, p8BPPBuffer, usWidth, usHeight, pSubImage ))
|
|
{
|
|
usNewY = sNewValue;
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
// shrink from the bottom
|
|
if (CheckForDataInRows( &sNewValue, -1, p8BPPBuffer, usWidth, usHeight, pSubImage ))
|
|
{
|
|
usNewHeight = (UINT16) sNewValue - usNewY + 1;
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
// shrink from the left
|
|
if (CheckForDataInCols( &sNewValue, 1, p8BPPBuffer, usWidth, usHeight, pSubImage ))
|
|
{
|
|
usNewX = sNewValue;
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
// shrink from the right
|
|
if (CheckForDataInCols( &sNewValue, -1, p8BPPBuffer, usWidth, usHeight, pSubImage ))
|
|
{
|
|
usNewWidth = (UINT16) sNewValue - usNewX + 1;
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
pSubImage->sOffsetX = usNewX;
|
|
pSubImage->sOffsetY = usNewY;
|
|
pSubImage->usHeight = usNewHeight;
|
|
pSubImage->usWidth = usNewWidth;
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN CheckForDataInRows( INT16 * psYValue, INT16 sYIncrement, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage )
|
|
{
|
|
INT16 sCurrY;
|
|
UINT32 uiOffset;
|
|
UINT8 * pCurrent;
|
|
UINT16 usLoop;
|
|
|
|
if (sYIncrement == 1)
|
|
{
|
|
sCurrY = pSubImage->sOffsetY;
|
|
}
|
|
else if (sYIncrement == -1)
|
|
{
|
|
sCurrY = pSubImage->sOffsetY + (INT16) pSubImage->usHeight - 1;
|
|
}
|
|
else
|
|
{
|
|
// invalid value!
|
|
return( FALSE );
|
|
}
|
|
for (usLoop = 0; usLoop < pSubImage->usHeight; usLoop++)
|
|
{
|
|
if (!DetermineOffset( &uiOffset, usWidth, usHeight, pSubImage->sOffsetX, (INT16) sCurrY))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
pCurrent = p8BPPBuffer + uiOffset;
|
|
pCurrent = CheckForDataInRowOrColumn( pCurrent, 1, pSubImage->usWidth );
|
|
if (pCurrent)
|
|
{
|
|
// non-null data found!
|
|
*psYValue = sCurrY;
|
|
return( TRUE );
|
|
}
|
|
sCurrY += sYIncrement;
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOLEAN CheckForDataInCols( INT16 * psXValue, INT16 sXIncrement, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage )
|
|
{
|
|
INT16 sCurrX;
|
|
UINT32 uiOffset;
|
|
UINT8 * pCurrent;
|
|
UINT16 usLoop;
|
|
|
|
if (sXIncrement == 1)
|
|
{
|
|
sCurrX = pSubImage->sOffsetX;
|
|
}
|
|
else if (sXIncrement == -1)
|
|
{
|
|
sCurrX = pSubImage->sOffsetX + (INT16) pSubImage->usWidth - 1;
|
|
}
|
|
else
|
|
{
|
|
// invalid value!
|
|
return( FALSE );
|
|
}
|
|
for (usLoop = 0; usLoop < pSubImage->usWidth; usLoop++)
|
|
{
|
|
if (!DetermineOffset( &uiOffset, usWidth, usHeight, (UINT16) sCurrX, pSubImage->sOffsetY))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
pCurrent = p8BPPBuffer + uiOffset;
|
|
pCurrent = CheckForDataInRowOrColumn( pCurrent, usWidth, pSubImage->usHeight );
|
|
if (pCurrent)
|
|
{
|
|
// non-null data found!
|
|
*psXValue = sCurrX;
|
|
return( TRUE );
|
|
}
|
|
sCurrX += sXIncrement;
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
UINT8 * CheckForDataInRowOrColumn( UINT8 * pPixel, UINT16 usIncrement, UINT16 usNumberOfPixels )
|
|
{
|
|
// This function, passed the right increment value, can scan either across or
|
|
// down an image to find a non-transparent pixel
|
|
|
|
UINT16 usLoop;
|
|
|
|
for (usLoop = 0; usLoop < usNumberOfPixels; usLoop++)
|
|
{
|
|
if (*pPixel != TCI)
|
|
{
|
|
return( pPixel );
|
|
}
|
|
else
|
|
{
|
|
pPixel += usIncrement;
|
|
}
|
|
}
|
|
return( NULL );
|
|
}
|
|
|
|
|