#pragma once
#ifndef __SPRITEUTILS_H__
#define __SPRITEUTILS_H__
// 16 bit pixels of interest
#define k_CHROMAKEY_PIXEL 0x0000
#define k_SHADOW_PIXEL_565 0xF81F
#define k_SHADOW_PIXEL_555 0x7C1F
#define k_SHADOWBACKGD_PIXEL 0xFFFF
// IDs in the RLE sprite stream
#define k_CHROMAKEY_RUN_ID 0x0A
#define k_COPY_RUN_ID 0x0C
#define k_SHADOW_RUN_ID 0x0E
#define k_FEATHERED_RUN_ID 0x0F
// EOLN mask for tag
#define k_EOLN_ID 0xF0
// Alpha values
#define k_NO_ALPHA 0x00
#define k_ALL_ALPHA 0xFF
// Flag for empty row in sprite table
#define k_EMPTY_TABLE_ENTRY 0xFFFF
void spriteutils_MergeShadowMap(Pixel32 *buf, Pixel32 *shadowBuf, uint16 width, uint16 height);
// Sprite Encoding
void spriteutils_EncodeShadowRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr);
void spriteutils_EncodeCopyRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr);
char spriteutils_EncodeChromakeyRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr);
void spriteutils_EncodeFeatheredRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr);
char spriteutils_EncodeScanline(Pixel32 *scanline, sint32 width, Pixel16 **outBufPtr);
Pixel16 *spriteutils_RGB32ToEncoded(Pixel32 *buf, Pixel32 *shadowBuf, uint16 width, uint16 height);
Pixel16 *spriteutils_RGB32ToEncoded(Pixel32 *buf, uint16 width, uint16 height);
#endif//__SPRITEUTILS_H__
--------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
void spriteutils_EncodeShadowRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
uint8 alpha;
sint32 runLen = 0;
Pixel16 footer=0;
Pixel16 shadowPixel;
if (g_is565Format) shadowPixel = k_SHADOW_PIXEL_565;
else shadowPixel = k_SHADOW_PIXEL_555;
RGB32Info(**inBuf, &pix16, &alpha);
while (pix16 == shadowPixel && (*pos < width)) {
// Increment input buffer
(*inBuf)++;
// Increment position counter
(*pos)++;
// Increment run length
runLen++;
RGB32Info(**inBuf, &pix16, &alpha);
}
// Build header/footer ID
footer = (Pixel16)(k_SHADOW_RUN_ID << 8 | runLen);
// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width) footer |= k_EOLN_ID << 8;
// Append header/footer ID to output buffer
**outBufPtr = footer;
(*outBufPtr)++;
}
//////////////////////////////////////////////////////////////////////////////
void spriteutils_EncodeCopyRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
uint8 alpha;
sint32 runLen = 0;
Pixel16 footer=0;
Pixel16 *headerPtr;
Pixel16 shadowPixel;
if (g_is565Format) {
shadowPixel = k_SHADOW_PIXEL_565;
} else {
shadowPixel = k_SHADOW_PIXEL_555;
}
headerPtr = *outBufPtr;
(*outBufPtr)++;
RGB32Info(**inBuf, &pix16, &alpha);
while (pix16 != shadowPixel && alpha == k_ALL_ALPHA && (*pos < width)) {
// Filter pure-black
if (pix16 == 0x0000) pix16 = 0x0001;
// Write pixel to output buffer
**outBufPtr = pix16;
// Increment output buffer
(*outBufPtr)++;
// Increment input buffer
(*inBuf)++;
// Increment position counter
(*pos)++;
// Increment run length
runLen++;
RGB32Info(**inBuf, &pix16, &alpha);
}
// Build the tag with the ID and length
footer = (Pixel16) (k_COPY_RUN_ID << 8 | runLen);
// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width) footer |= k_EOLN_ID << 8;
*headerPtr = footer;
}
//////////////////////////////////////////////////////////////////////////////
char spriteutils_EncodeChromakeyRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
uint8 alpha;
sint32 runLen = 0;
Pixel16 footer=0;
RGB32Info(**inBuf, &pix16, &alpha);
while (pix16 == k_CHROMAKEY_PIXEL && alpha == k_NO_ALPHA && (*pos < width)) {
// Increment input buffer
(*inBuf)++;
// Increment position counter
(*pos)++;
// Increment run length
runLen++;
RGB32Info(**inBuf, &pix16, &alpha);
}
// If it's a full-row transparent run, it's a skipped row
if (runLen < width) {
// Build header/footer ID
footer = (Pixel16)(k_CHROMAKEY_RUN_ID << 8 | runLen);
// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width) footer |= k_EOLN_ID << 8;
// Append header/footer ID to output buffer
**outBufPtr = footer;
(*outBufPtr)++;
} else {
// return TRUE that it's an empty row
return TRUE;
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////////////
char spriteutils_EncodeChromakeyWshadowRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
uint8 alpha;
sint32 runLen = 0;
Pixel16 footer=0;
RGB32Info(**inBuf, &pix16, &alpha);
while ( ((pix16 == k_CHROMAKEY_PIXEL && alpha == k_NO_ALPHA) || (pix16 == k_SHADOWBACKGD_PIXEL)) && (*pos < width) ) {
// Increment input buffer
(*inBuf)++;
// Increment position counter
(*pos)++;
// Increment run length
runLen++;
RGB32Info(**inBuf, &pix16, &alpha);
}
// If it's a full-row transparent run, it's a skipped row
if (runLen < width) {
// Build header/footer ID
footer = (Pixel16)(k_CHROMAKEY_RUN_ID << 8 | runLen);
// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width) footer |= k_EOLN_ID << 8;
// Append header/footer ID to output buffer
**outBufPtr = footer;
(*outBufPtr)++;
} else {
// return TRUE that it's an empty row
return TRUE;
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////////////
void spriteutils_EncodeFeatheredRun(Pixel32 **inBuf, sint32 *pos, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
Pixel16 footer=0;
uint8 alpha;
RGB32Info(**inBuf, &pix16, &alpha);
(*inBuf)++;
(*pos)++;
footer = (k_FEATHERED_RUN_ID << 8) | alpha;
// Set the high nybble of the tag if it's an EOLN condition
if (*pos >= width)
footer |= k_EOLN_ID << 8;
// Write leading footer tag
**outBufPtr = footer;
(*outBufPtr)++;
// Write pixel value
**outBufPtr = pix16;
(*outBufPtr)++;
}
//////////////////////////////////////////////////////////////////////////////
// this will encode a normal image with shadow merged into it.
char spriteutils_EncodeScanline(Pixel32 *scanline, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
Pixel32 pix32;
Pixel32 *scanPtr = scanline;
uint8 alpha;
sint32 pos;
Pixel16 *startPtr;
BOOL empty;
Pixel16 shadowPixel;
if (g_is565Format) {
shadowPixel = k_SHADOW_PIXEL_565;
} else {
shadowPixel = k_SHADOW_PIXEL_555;
}
pos = 0;
// Save starting position of the scanline's data stream
startPtr = *outBufPtr;
// EOLN needs to be at both ends
while (scanPtr < (scanline + width)) {
pix32 = *scanPtr;
empty = FALSE;
RGB32Info(pix32, &pix16, &alpha);
if (pix16 == k_CHROMAKEY_PIXEL && alpha == k_NO_ALPHA)
{
// Whitespace Run
empty = spriteutils_EncodeChromakeyRun(&scanPtr, &pos, width, outBufPtr);
}
else
if (pix16 == shadowPixel) {
// Shadow Run
spriteutils_EncodeShadowRun(&scanPtr, &pos, width, outBufPtr);
}
else
if (alpha != k_NO_ALPHA && alpha != k_ALL_ALPHA) {
// Feathered pixel
spriteutils_EncodeFeatheredRun(&scanPtr, &pos, width, outBufPtr);
}
else {
// Jus' a plain ol' pixel. This is a copy run.
if (alpha == k_ALL_ALPHA)
spriteutils_EncodeCopyRun(&scanPtr, &pos, width, outBufPtr);
else {
printf("\nError in bitmap data. Pixel with no associated alpha.\n");
exit(-1);
}
}
}
return empty;
}
//////////////////////////////////////////////////////////////////////////////
// this will encode a shadow just like a regular image, taking into account shadow's with white backgrounds
char spriteutils_EncodeScanlineWshadow(Pixel32 *scanline, sint32 width, Pixel16 **outBufPtr)
{
Pixel16 pix16;
Pixel32 pix32;
Pixel32 *scanPtr = scanline;
uint8 alpha;
sint32 pos;
Pixel16 *startPtr;
BOOL empty;
Pixel16 shadowPixel;
if (g_is565Format) {
shadowPixel = k_SHADOW_PIXEL_565;
} else {
shadowPixel = k_SHADOW_PIXEL_555;
}
pos = 0;
// Save starting position of the scanline's data stream
startPtr = *outBufPtr;
// EOLN needs to be at both ends
while (scanPtr < (scanline + width)) {
pix32 = *scanPtr;
empty = FALSE;
RGB32Info(pix32, &pix16, &alpha);
if ( (pix16 == k_CHROMAKEY_PIXEL && alpha == k_NO_ALPHA) || (pix16 == k_SHADOWBACKGD_PIXEL) )
{
// Whitespace Run
empty = spriteutils_EncodeChromakeyWshadowRun(&scanPtr, &pos, width, outBufPtr);
}
else
if (pix16 == shadowPixel) {
// Shadow Run
spriteutils_EncodeShadowRun(&scanPtr, &pos, width, outBufPtr);
}
else
if (alpha != k_NO_ALPHA && alpha != k_ALL_ALPHA) {
// Feathered pixel
spriteutils_EncodeFeatheredRun(&scanPtr, &pos, width, outBufPtr);
}
else {
// Jus' a plain ol' pixel. This is a copy run.
if (alpha == k_ALL_ALPHA)
spriteutils_EncodeCopyRun(&scanPtr, &pos, width, outBufPtr);
else {
printf("\nError in bitmap data. Pixel with no associated alpha.\n");
exit(-1);
}
}
}
return empty;
}
//////////////////////////////////////////////////////////////////////////////
void spriteutils_MergeShadowMap(Pixel32 *buf, Pixel32 *shadowBuf, uint16 width, uint16 height)
{
Pixel32 *pixPtr, pix;
Pixel32 *shadowPixPtr, shadowPix;
// if the upper-left pixel is pure white, assume this is a white background with a black shadow on it
// otherwise, it's a black background with a pure-magenta shadow on it
BOOL whiteBackground = FALSE;
if ((*shadowBuf & 0x00FFFFFF) == 0x00FFFFFF) {
whiteBackground = TRUE;
} else {
if ((*shadowBuf & 0x00FFFFFF) != 0x00000000) {
printf("\nShadow file is in invalid format.\n");
exit(-1);
}
}
for (sint32 j=0; j<height; j++) {
for (sint32 i=0; i<width; i++) {
pixPtr = buf + j*width + i;
shadowPixPtr = shadowBuf + j*width + i;
pix = *pixPtr;
shadowPix = *shadowPixPtr;
shadowPix = shadowPix & 0x00FFFFFF;
if (whiteBackground) {
// See if there's shadow information there
if (shadowPix != 0x00FFFFFF) {
// There's a shadow pixel, copy it over to the source image's bitmap
// only if there's null alpha
Pixel16 r, g, b, a;
RGB32Components(pix, &r, &g, &b, &a);
// check against empty and full alpha
if (a != 0xFF) {
*pixPtr = 0x00FF00FF;
}
}
} else {
// See if there's shadow information there
if (shadowPix) {
// There's a shadow pixel, copy it over to the source image's bitmap
// only if there's null alpha
Pixel16 r, g, b, a;
RGB32Components(pix, &r, &g, &b, &a);
// check against empty and full alpha
if (a != 0xFF) {
*pixPtr = shadowPix;
}
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////////
// this takes an image and a shadow and makes one image with them
Pixel16 *spriteutils_RGB32ToEncoded(Pixel32 *buf, Pixel32 *shadowBuf, uint16 width, uint16 height)
{
Pixel32 *srcPixel = buf;
Pixel16 *outBuf = new Pixel16[(1+height+width*height)*8];
Pixel16 *returnBuf = NULL;
uint16 *table = (uint16 *)outBuf;
Pixel16 *startOfData;
Pixel16 *dataPtr, *startDataPtr;
BOOL empty;
// One solution to the shadow encoding problem is to merge the shadow information sint32o the source bitmap
if (shadowBuf != NULL)
spriteutils_MergeShadowMap(buf, shadowBuf, width, height);
// Write the height of the sprite as the first 16 bits of the table data
*table++ = (uint16)height;
startOfData = outBuf + 1 + height;
dataPtr = startOfData;
for(sint32 y=0; y<height; y++) {
// Start at the beginning of the scanline
srcPixel = buf + width * y;
startDataPtr = dataPtr;
// Encode runs for this line
empty = spriteutils_EncodeScanline(srcPixel, width, &dataPtr);
if (empty) {
*table++ = k_EMPTY_TABLE_ENTRY;
} else {
// Write the line start sint32o the table
*table++ = startDataPtr - startOfData;
}
}
sint32 resultSize = (dataPtr - outBuf);
returnBuf = new Pixel16[resultSize];
memcpy(returnBuf, outBuf, resultSize * sizeof(Pixel16));
return (Pixel16 *)returnBuf;
}
//////////////////////////////////////////////////////////////////////////////
// this takes any buffer and encodes it as it's own image; Including shadows treated as seperate images
Pixel16 *spriteutils_RGB32ToEncoded(Pixel32 *buf, uint16 width, uint16 height)
{
Pixel32 *srcPixel = buf;
Pixel16 *outBuf = new Pixel16[(1+height+width*height)*8];
Pixel16 *returnBuf = NULL;
uint16 *table = (uint16 *)outBuf;
Pixel16 *startOfData;
Pixel16 *dataPtr, *startDataPtr;
BOOL empty;
// Write the height of the sprite as the first 16 bits of the table data
*table++ = (uint16)height;
startOfData = outBuf + 1 + height;
dataPtr = startOfData;
for(sint32 y=0; y<height; y++) {
// Start at the beginning of the scanline
srcPixel = buf + width * y;
startDataPtr = dataPtr;
// Encode runs for this line
empty = spriteutils_EncodeScanlineWshadow(srcPixel, width, &dataPtr);
if (empty) {
*table++ = k_EMPTY_TABLE_ENTRY;
} else {
// Write the line start sint32o the table
*table++ = startDataPtr - startOfData;
}
}
sint32 resultSize = (dataPtr - outBuf);
returnBuf = new Pixel16[resultSize];
memcpy(returnBuf, outBuf, resultSize * sizeof(Pixel16));
return (Pixel16 *)returnBuf;
}