This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/wwlib/rlerle.h

709 lines
17 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* $Archive:: /Commando/Library/RLERLE.h $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 7/22/97 11:37a $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifndef RLERLE_H
#define RLERLE_H
/*
** This class holds the RLE enabled blitter object definitions. There is a blitter object
** type for every kind of pixel operation required of RLE shapes. These are defined as
** templates to support the different destination pixel formats.
*/
#include "blitter.h"
#include <assert.h>
#include <string.h>
/*
** This is a helper function that will skip N pixels in the RLE compressed source. This is
** necessary for clipping purposes. The return value represents the number of transparent
** pixels before actual pixel data starts when the RLE uncompression is resumed.
*/
inline int Skip_Leading_Pixels(unsigned char const * & sptr, int skipper)
{
/*
** Skip leading pixels as requested.
*/
while (skipper > 0) {
if (*sptr++ == '\0') {
skipper -= *sptr++;
} else {
skipper--;
}
}
/*
** Return with then number of leading transparent pixels in the pixel stream
** after the end of the skip process. This value must be tracked since the pixel
** skip process may have ended in the middle of a transparent pixel run.
*/
return(-skipper);
}
/*
** Blits with transparency checking and translation to destination pixel format.
*/
template<class T>
class RLEBlitTransXlat : public RLEBlitter {
public:
RLEBlitTransXlat(T const * translator) : TranslateTable(translator) {assert(TranslateTable != NULL);}
virtual void Blit(void * dest, void const * source, int length, int leadskip=0) const
{
unsigned char const * sptr = (unsigned char const *)source;
T * dptr = (T *)dest;
/*
** Skip any leading pixels as requested.
*/
if (leadskip > 0) {
int transcount = Skip_Leading_Pixels(sptr, leadskip);
dptr += transcount;
length -= transcount;
}
/*
** Uncompress and store the pixel stream until the length has been
** exhausted.
*/
while (length > 0) {
unsigned char value = *sptr++;
if (value == '\0') {
value = *sptr++;
length -= value;
dptr += value;
} else {
*dptr++ = TranslateTable[value];
length -= 1;
}
}
}
private:
T const * TranslateTable;
};
/*
** This blits RLE compressed pixels by first remapping through a 256 byte table and then
** translating the pixel to screen format.
*/
template<class T>
class RLEBlitTransRemapXlat : public RLEBlitter {
public:
RLEBlitTransRemapXlat(unsigned char const * remapper, T const * translator) : RemapTable(remapper), TranslateTable(translator) {assert(TranslateTable != NULL);assert(RemapTable != NULL);}
virtual void Blit(void * dest, void const * source, int length, int leadskip=0) const
{
unsigned char const * sptr = (unsigned char const *)source;
T * dptr = (T *)dest;
/*
** Skip any leading pixels as requested.
*/
if (leadskip > 0) {
int transcount = Skip_Leading_Pixels(sptr, leadskip);
dptr += transcount;
length -= transcount;
}
/*
** Uncompress and store the pixel stream until the length has been
** exhausted.
*/
while (length > 0) {
unsigned char value = *sptr++;
if (value == '\0') {
value = *sptr++;
length -= value;
dptr += value;
} else {
*dptr++ = TranslateTable[RemapTable[value]];
length -= 1;
}
}
}
private:
unsigned char const * RemapTable;
T const * TranslateTable;
};
/*
** This blits RLE compressed pixels by first remapping through a 256 byte table and then
** translating the pixel to screen format. The remapping table is doubly indirected so that
** it is possible to change the remapping table pointer without creating a separate blitter
** object.
*/
template<class T>
class RLEBlitTransZRemapXlat : public RLEBlitter {
public:
RLEBlitTransZRemapXlat(unsigned char const * const * remapper, T const * translator) : RemapTable(remapper), TranslateTable(translator) {assert(TranslateTable != NULL);assert(RemapTable != NULL);}
virtual void Blit(void * dest, void const * source, int length, int leadskip=0) const
{
unsigned char const * sptr = (unsigned char const *)source;
unsigned char const * remapper = *RemapTable;
T * dptr = (T *)dest;
/*
** Skip any leading pixels as requested.
*/
if (leadskip > 0) {
int transcount = Skip_Leading_Pixels(sptr, leadskip);
dptr += transcount;
length -= transcount;
}
/*
** Uncompress and store the pixel stream until the length has been
** exhausted.
*/
while (length > 0) {
unsigned char value = *sptr++;
if (value == '\0') {
value = *sptr++;
length -= value;
dptr += value;
} else {
*dptr++ = TranslateTable[remapper[value]];
length -= 1;
}
}
}
private:
unsigned char const * const * RemapTable;
T const * TranslateTable;
};
/*
** This will remap the destination pixels but under the control of the source pixels.
** Where the source pixel is not transparent, the dest pixel is remapped. This algorithm
** really only applies to lowcolor display.
*/
template<class T>
class RLEBlitTransRemapDest : public RLEBlitter {
public:
RLEBlitTransRemapDest(T const * remap) : RemapTable(remap) {}
virtual void Blit(void * dest, void const * source, int length, int leadskip=0) const
{
unsigned char const * sptr = (unsigned char const *)source;
T * dptr = (T *)dest;
/*
** Skip any leading pixels as requested.
*/
if (leadskip > 0) {
int transcount = Skip_Leading_Pixels(sptr, leadskip);
dptr += transcount;
length -= transcount;
}
/*
** Uncompress and store the pixel stream until the length has been
** exhausted.
*/
while (length > 0) {
unsigned char value = *sptr++;
if (value == '\0') {
value = *sptr++;
length -= value;
dptr += value;
} else {
*dptr = RemapTable[*dptr];
length -= 1;
dptr++;
}
}
}
private:
T const * RemapTable;
};
/*
** Algorithmic darkening of hicolor pixels controlled by the source pixels. The source
** pixels are examined only to determine if the destination pixel should be darkened.
** If the source pixel is transparent, then the dest pixel is skipped. The darkening
** algorithm works only for hicolor pixels.
*/
template<class T>
class RLEBlitTransDarken : public RLEBlitter {
public:
RLEBlitTransDarken(T mask) : Mask(mask) {}
virtual void Blit(void * dest, void const * source, int length, int leadskip=0) const
{
unsigned char const * sptr = (unsigned char const *)source;
T * dptr = (T *)dest;
/*
** Skip any leading pixels as requested.
*/
if (leadskip > 0) {
int transcount = Skip_Leading_Pixels(sptr, leadskip);
dptr += transcount;
length -= transcount;
}
/*
** Uncompress and store the pixel stream until the length has been
** exhausted.
*/
while (length > 0) {
unsigned char value = *sptr++;
if (value == '\0') {
value = *sptr++;
length -= value;
dptr += value;
} else {
*dptr = (T)((*dptr >> 1) & Mask);
length -= 1;
dptr++;
}
}
}
private:
T Mask;
};
/*
** This blitter performs 50% translucency as it draws. It is commonly used for animation
** effects and other stealth like images. It only works with hicolor pixels but is a good
** candidate for optimization.
*/
template<class T>
class RLEBlitTransLucent50 : public RLEBlitter {
public:
RLEBlitTransLucent50(T const * translator, T mask) : TranslateTable(translator), Mask(mask) {}
virtual void Blit(void * dest, void const * source, int length, int leadskip=0) const
{
unsigned char const * sptr = (unsigned char const *)source;
T * dptr = (T *)dest;
/*
** Skip any leading pixels as requested.
*/
if (leadskip > 0) {
int transcount = Skip_Leading_Pixels(sptr, leadskip);
dptr += transcount;
length -= transcount;
}
/*
** Uncompress and store the pixel stream until the length has been
** exhausted.
*/
while (length > 0) {
unsigned char value = *sptr++;
if (value == '\0') {
value = *sptr++;
length -= value;
dptr += value;
} else {
*dptr = (T)((((*dptr) >> 1) & Mask) + ((TranslateTable[value] >> 1) & Mask));
length -= 1;
dptr++;
}
}
}
private:
T const * TranslateTable;
T Mask;
};
/*
** This blitter performs 25% translucency as it draws. This effect is less than spectacular,
** but there are some uses for it. It only works with hicolor pixels.
*/
template<class T>
class RLEBlitTransLucent25 : public RLEBlitter {
public:
RLEBlitTransLucent25(T const * translator, T mask) : TranslateTable(translator), Mask(mask) {}
virtual void Blit(void * dest, void const * source, int length, int leadskip=0) const
{
unsigned char const * sptr = (unsigned char const *)source;
T * dptr = (T *)dest;
/*
** Skip any leading pixels as requested.
*/
if (leadskip > 0) {
int transcount = Skip_Leading_Pixels(sptr, leadskip);
dptr += transcount;
length -= transcount;
}
/*
** Uncompress and store the pixel stream until the length has been
** exhausted.
*/
while (length > 0) {
unsigned char value = *sptr++;
if (value == '\0') {
value = *sptr++;
length -= value;
dptr += value;
} else {
T qsource = (T)(((TranslateTable[value] >> 2) & Mask));
T qdest = (T)(((*dptr) >> 2) & Mask);
*dptr++ = (T)(qdest + qsource + qsource + qsource);
length -= 1;
}
}
}
private:
T const * TranslateTable;
T Mask;
};
/*
** This blitter performs 75% translucency as it draws. This is quite useful for explosions and
** other gas animation effects. It only works with hicolor pixels and is a good candidate
** for optimization.
*/
template<class T>
class RLEBlitTransLucent75 : public RLEBlitter {
public:
RLEBlitTransLucent75(T const * translator, T mask) : TranslateTable(translator), Mask(mask) {}
virtual void Blit(void * dest, void const * source, int length, int leadskip=0) const
{
unsigned char const * sptr = (unsigned char const *)source;
T * dptr = (T *)dest;
/*
** Skip any leading pixels as requested.
*/
if (leadskip > 0) {
int transcount = Skip_Leading_Pixels(sptr, leadskip);
dptr += transcount;
length -= transcount;
}
/*
** Uncompress and store the pixel stream until the length has been
** exhausted.
*/
while (length > 0) {
unsigned char value = *sptr++;
if (value == '\0') {
value = *sptr++;
length -= value;
dptr += value;
} else {
T qsource = (T)(((TranslateTable[value] >> 2) & Mask));
T qdest = (T)(((*dptr) >> 2) & Mask);
*dptr++ = (T)(qdest + qdest + qdest + qsource);
length -= 1;
}
}
}
private:
T const * TranslateTable;
T Mask;
};
#if defined(_MSC_VER)
void RLEBlitTransZRemapXlat<unsigned short>::Blit(void * dest, void const * source, int len, int leadskip) const
{
unsigned char const * remapper = *RemapTable;
unsigned short const * transtable = TranslateTable;
/*
** Set up the working registers for the blit operation.
*/
__asm {
mov ecx,[len]
mov edi,[dest]
mov esi,[source]
mov ebx,[remapper]
mov edx,[leadskip]
xor eax,eax
}
/*
** Skip leading pixels by analyzing the RLE data until the entire
** requested skip pixel count has been processed. This could result in
** unprocessed transparent pixels if it ended up in the middle of
** a transparent pixel run. This is handled in the next block.
*/
moreskip:
__asm {
test edx,edx
jle nomoreskip
dec edx
lodsb
test al,al
jnz moreskip
lodsb
sub edx,eax
inc edx
jmp moreskip
}
nomoreskip:
/*
** Handle any left over transparent pixels that would be part of
** a transparent pixel run that occurs at the end of the leading
** pixel skip process.
*/
__asm {
neg edx
sub ecx,edx // Account for any left over transparent pixels
lea edi,[edi+edx*2]
mov edx,[transtable]
}
/*
** Output the pixel data to the destination.
*/
moredata:
__asm {
xor eax,eax
or ecx,ecx
jle fini
lodsb
test al,al
jz transparent
mov al,[ebx+eax]
mov ax,[edx+eax*2]
dec ecx
stosw
jmp moredata
}
/*
** A transparent pixel run just causes the destination pointer
** and length count to be adjusted by the length of the run.
*/
transparent:
__asm {
lodsb
lea edi,[edi+eax*2]
sub ecx,eax
jmp moredata
}
fini:;
}
void RLEBlitTransRemapXlat<unsigned short>::Blit(void * dest, void const * source, int len, int leadskip) const
{
unsigned char const * remapper = RemapTable;
unsigned short const * transtable = TranslateTable;
/*
** Set up the working registers for the blit operation.
*/
__asm {
mov ecx,[len]
mov edi,[dest]
mov esi,[source]
mov ebx,[remapper]
mov edx,[leadskip]
xor eax,eax
}
/*
** Skip leading pixels by analyzing the RLE data until the entire
** requested skip pixel count has been processed. This could result in
** unprocessed transparent pixels if it ended up in the middle of
** a transparent pixel run. This is handled in the next block.
*/
moreskip:
__asm {
test edx,edx
jle nomoreskip
dec edx
lodsb
test al,al
jnz moreskip
lodsb
sub edx,eax
inc edx
jmp moreskip
}
nomoreskip:
/*
** Handle any left over transparent pixels that would be part of
** a transparent pixel run that occurs at the end of the leading
** pixel skip process.
*/
__asm {
neg edx
sub ecx,edx // Account for any left over transparent pixels
lea edi,[edi+edx*2]
mov edx,[transtable]
}
/*
** Output the pixel data to the destination.
*/
moredata:
__asm {
xor eax,eax
or ecx,ecx
jle fini
lodsb
test al,al
jz transparent
mov al,[ebx+eax]
mov ax,[edx+eax*2]
dec ecx
stosw
jmp moredata
}
/*
** A transparent pixel run just causes the destination pointer
** and length count to be adjusted by the length of the run.
*/
transparent:
__asm {
lodsb
lea edi,[edi+eax*2]
sub ecx,eax
jmp moredata
}
fini:;
}
void RLEBlitTransXlat<unsigned short>::Blit(void * dest, void const * source, int len, int leadskip) const
{
unsigned short const * transtable = TranslateTable;
/*
** Set up the working registers for the blit operation.
*/
__asm {
mov ecx,[len]
mov edi,[dest]
mov esi,[source]
mov ebx,[transtable]
mov edx,[leadskip]
xor eax,eax
}
/*
** Skip leading pixels by analyzing the RLE data until the entire
** requested skip pixel count has been processed. This could result in
** unprocessed transparent pixels if it ended up in the middle of
** a transparent pixel run. This is handled in the next block.
*/
moreskip:
__asm {
test edx,edx
jle nomoreskip
dec edx
lodsb
test al,al
jnz moreskip
lodsb
sub edx,eax
inc edx
jmp moreskip
}
nomoreskip:
/*
** Handle any left over transparent pixels that would be part of
** a transparent pixel run that occurs at the end of the leading
** pixel skip process.
*/
__asm {
neg edx
sub ecx,edx // Account for any left over transparent pixels
lea edi,[edi+edx*2]
}
/*
** Output the pixel data to the destination.
*/
moredata:
__asm {
xor eax,eax
or ecx,ecx
jle fini
lodsb
test al,al
jz transparent
mov ax,[ebx+eax*2]
dec ecx
stosw
jmp moredata
}
/*
** A transparent pixel run just causes the destination pointer
** and length count to be adjusted by the length of the run.
*/
transparent:
__asm {
lodsb
lea edi,[edi+eax*2]
sub ecx,eax
jmp moredata
}
fini:;
}
#endif
#endif