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/blitblit.h

615 lines
19 KiB
C
Raw Permalink Normal View History

/*
** 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/blitblit.h $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 7/22/97 11:37a $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifndef BLITBLIT_H
#define BLITBLIT_H
/*
** This module contains the pixel-pushing blitter objects. These objects only
** serve one purpose. That is, to move pixels from one location to another. These
** are prime candidates for optimization since they are called frequently and
** loop greatly.
**
** The large variety of blitter objects is necessary because there is a rich
** set of pixel operations required by the game engine. Complicating this is that
** the game engine must support both 16 bit and 8 bit pixel formats. Some of these
** blitter objects are templates (this reduces the need for both 8 and 16 bit
** counterparts if the algorithm is constant between pixel formats). Also note
** that there are some assembly implementations where it seems appropriate.
**
** If the blitter object has "Xlat" in the name, then this means that the source
** pixel is 8 bit and the destination pixel is 16 bit (probably). This hybrid system
** allows the game artwork to be shared between the two pixel format displays. To
** accomplish this, a translation table is supplied to the blit operation so that
** the 8 bit pixel can be converted into the appropriate 16 bit destination pixel.
** If the destination surface is also 8 bit, then the translation table converts
** the pixel to the logical palette color index appropriate for the display.
*/
#include "blitter.h"
#include <assert.h>
#include <string.h>
/*
** Blits without translation and source and dest are same pixel format. Note that
** this uses the memcpy and memmove routines. The C library has optimized these for
** maximum performance. This includes alignment issues and performing REP MOVSD
** instruction. This might be further optimized by using MMX instructions. However,
** this blitter process is not often required by the game.
*/
template<class T>
class BlitPlain : public Blitter {
public:
virtual void BlitForward(void * dest, void const * source, int length) const {memcpy(dest, source, length*sizeof(T));}
virtual void BlitBackward(void * dest, void const * source, int length) const {memmove(dest, source, length*sizeof(T));}
};
/*
** Blits with transparency checking when and source and dest are same pixel format.
** This process is not often used.
*/
template<class T>
class BlitTrans : public Blitter {
public:
virtual void BlitForward(void * dest, void const * source, int len) const
{
for (int index = 0; index < len; index++) {
T color = *(T const *)source;
source = ((T *)source) + 1;
if (color != 0) *((T *)dest) = color;
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
};
/*
** Blits when source 8 bits and dest is T. This process is typically used
** when loading screen bitmaps or perform other non-transparent image blitting.
** It is used fairly frequently and is a good candidate for optimization.
*/
template<class T>
class BlitPlainXlat : public Blitter {
public:
BlitPlainXlat(T const * translator) : TranslateTable(translator) {assert(TranslateTable != NULL);}
virtual void BlitForward(void * dest, void const * source, int len) const
{
for (int index = 0; index < len; index++) {
unsigned char color = *(unsigned char const *)source;
source = ((unsigned char const *)source)+1;
*((T *)dest) = TranslateTable[color];
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
private:
T const * TranslateTable;
};
/*
** Blits with source 8 bit with transparency and dest is T. This process is used
** frequently by trees and other terrain objects. It is a good candidate for
** optimization.
*/
template<class T>
class BlitTransXlat : public Blitter {
public:
BlitTransXlat(T const * translator) : TranslateTable(translator) {assert(TranslateTable != NULL);}
virtual void BlitForward(void * dest, void const * source, int len) const
{
for (int index = 0; index < len; index++) {
unsigned char color = *(unsigned char const *)source;
source = ((unsigned char const *)source)+1;
if (color != 0) {
*((T *)dest) = TranslateTable[color];
}
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
private:
T const * TranslateTable;
};
/*
** Blits with source 8 bit, transparency check, then translate to pixel format T. This
** is occasionally used to render special remapping effects. Since the remap table is
** not doubly indirected, it is fixed to only using the remap table specified in the
** constructor. As such, it has limited value.
*/
template<class T>
class BlitTransRemapXlat : public Blitter {
public:
BlitTransRemapXlat(unsigned char const * remapper, T const * translator) : RemapTable(remapper), TranslateTable(translator) {assert(RemapTable != NULL);assert(TranslateTable != NULL);}
virtual void BlitForward(void * dest, void const * source, int length) const
{
for (int index = 0; index < length; index++) {
unsigned char color = *(unsigned char const *)source;
source = ((unsigned char const *)source)+1;
if (color != 0) {
*((T *)dest) = TranslateTable[RemapTable[color]];
}
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
private:
unsigned char const * RemapTable;
T const * TranslateTable;
};
/*
** Blits with source 8 bit with transparency then remap and dest is T. This is probably
** the most used blitter process. Units, infantry, buildings, and aircraft use this for
** their normal drawing needs. If any blitter process is to be optimized, this would be
** the one. Take note that the remapper table is doubly indirected. This allows a single
** blitter object to dynamically use alternate remap tables.
*/
template<class T>
class BlitTransZRemapXlat : public Blitter {
public:
BlitTransZRemapXlat(unsigned char const * const * remapper, T const * translator) : RemapTable(remapper), TranslateTable(translator) {assert(RemapTable != NULL);assert(TranslateTable != NULL);}
virtual void BlitForward(void * dest, void const * source, int length) const
{
unsigned char const * rtable = *RemapTable;
for (int index = 0; index < length; index++) {
unsigned char color = *(unsigned char const *)source;
source = ((unsigned char const *)source)+1;
if (color != 0) {
*((T *)dest) = TranslateTable[rtable[color]];
}
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
private:
unsigned char const * const * RemapTable;
T const * TranslateTable;
};
/*
** 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 BlitTransDarken : public Blitter {
public:
BlitTransDarken(T mask) : Mask(mask) {}
virtual void BlitForward(void * dest, void const * source, int length) const
{
for (int index = 0; index < length; index++) {
unsigned char color = *(unsigned char const *)source;
source = ((unsigned char const *)source)+1;
if (color != 0) {
*((T *)dest) = (T)((((*(T *)dest) >> 1) & Mask));
}
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
private:
T Mask;
};
/*
** 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 BlitTransRemapDest : public Blitter {
public:
BlitTransRemapDest(T const * remap) : RemapTable(remap) {}
virtual void BlitForward(void * dest, void const * source, int length) const
{
for (int index = 0; index < length; index++) {
unsigned char color = *(unsigned char const *)source;
source = ((unsigned char const *)source)+1;
if (color != 0) {
*((T *)dest) = RemapTable[*((T *)dest)];
}
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
private:
T const * RemapTable;
};
/*
** This is similar to BlitTransDarken but instead of examining the source to determine what
** pixels should be darkened, every destination pixel is darkened. This means that the source
** pointer is unused.
*/
template<class T>
class BlitDarken : public Blitter {
public:
BlitDarken(T mask) : Mask(mask) {}
virtual void BlitForward(void * dest, void const * , int length) const
{
for (int index = 0; index < length; index++) {
*((T *)dest) = (T)(((*(T *)dest) >> 1) & Mask);
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
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 BlitTransLucent50 : public Blitter {
public:
BlitTransLucent50(T const * translator, T mask) : TranslateTable(translator), Mask(mask) {assert(TranslateTable != NULL);}
virtual void BlitForward(void * dest, void const * source, int length) const
{
for (int index = 0; index < length; index++) {
unsigned char color = *(unsigned char const *)source;
source = ((unsigned char *)source) + 1;
if (color != 0) {
*((T *)dest) = (T)((((*(T *)dest) >> 1) & Mask) + ((TranslateTable[color] >> 1) & Mask));
}
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
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 BlitTransLucent25 : public Blitter {
public:
BlitTransLucent25(T const * translator, T mask) : TranslateTable(translator), Mask(mask) {assert(TranslateTable != NULL);}
virtual void BlitForward(void * dest, void const * source, int length) const
{
for (int index = 0; index < length; index++) {
unsigned char color = *(unsigned char const *)source;
source = ((unsigned char *)source) + 1;
if (color != 0) {
T qsource = (T)(((TranslateTable[color] >> 2) & Mask));
T qdest = (T)((((*(T *)dest) >> 2) & Mask));
*((T *)dest) = (T)(qdest + qsource + qsource + qsource);
}
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
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 BlitTransLucent75 : public Blitter {
public:
BlitTransLucent75(T const * translator, T mask) : TranslateTable(translator), Mask(mask) {assert(TranslateTable != NULL);}
virtual void BlitForward(void * dest, void const * source, int length) const
{
for (int index = 0; index < length; index++) {
unsigned char color = *(unsigned char const *)source;
source = ((unsigned char *)source) + 1;
if (color != 0) {
T qsource = (T)(((TranslateTable[color] >> 2) & Mask));
T qdest = (T)(((*(T *)dest) >> 2) & Mask);
*((T *)dest) = (T)(qdest + qdest + qdest + qsource);
}
dest = ((T *)dest) + 1;
}
}
/*
** The backward moving method will probably never be called in actual practice.
** Implement in terms of the forward copying method until the need for this
** version arrises.
*/
virtual void BlitBackward(void * dest, void const * source, int length) const {BlitForward(dest, source, length);}
private:
T const * TranslateTable;
T Mask;
};
/*
** Assembly versions of some of the templated blitter object functions. Borland and
** Visual C++ support a compatible inline-assembly formats. However, Borland compiler
** does not allow inline-assembly to be part of an inline function -- go figure.
** It will still compile, it just generates warning messages.
*/
#if defined(_MSC_VER)
inline void BlitTrans<unsigned char>::BlitForward(void * dest, void const * source, int len) const
{
__asm {
mov esi,[source]
mov edi,[dest]
mov ecx,[len]
dec edi
inc ecx
}
again:
__asm {
dec ecx
jz fini
mov al,[esi]
inc edi
inc esi
test al,al
jz again
mov [edi],al
jmp again
}
fini:;
}
inline void BlitTransXlat<unsigned short>::BlitForward(void * dest, void const * source, int len) const
{
unsigned short const * xlator = TranslateTable;
__asm {
mov ebx,[xlator]
mov ecx,[len]
inc ecx
mov edi,[dest]
sub edi,2
mov esi,[source]
xor eax,eax
}
again:
__asm {
dec ecx
jz over
add edi,2
mov al,[esi]
inc esi
or al,al
jz again
mov dx,[ebx+eax*2]
mov [edi],dx
jmp again
}
over:;
}
inline void BlitTransRemapXlat<unsigned short>::BlitForward(void * dest, void const * source, int len) const
{
unsigned short const * translator = TranslateTable;
unsigned char const * remapper = RemapTable;
__asm {
mov ecx,[len]
mov edi,[dest]
sub edi,2
mov esi,[source]
mov ebx,[remapper]
mov edx,[translator]
xor eax,eax
}
/*
** This block is 11 cycles per pixel, if not transparent, and 5
** cycles per pixel, if transparent.
*/
again:
__asm {
dec ecx
jz over
add edi,2
xor eax,eax
lodsb
or al,al
jz again
mov al,[ebx+eax] // First remap step (8 bit to 8 bit).
mov ax,[edx+eax*2] // Second remap step (8 bit to 16 bit).
mov [edi],ax
jmp again
}
over:;
}
inline void BlitTransZRemapXlat<unsigned short>::BlitForward(void * dest, void const * source, int len) const
{
unsigned short const * translator = TranslateTable;
unsigned char const * remapper = *RemapTable;
__asm {
mov ecx,[len]
mov edi,[dest]
sub edi,2
mov esi,[source]
mov ebx,[remapper]
mov edx,[translator]
xor eax,eax
}
/*
** This block is 11 cycles per pixel, if not transparent, and 5
** cycles per pixel, if transparent.
*/
again:
__asm {
dec ecx
jz over
add edi,2
xor eax,eax
lodsb
or al,al
jz again
mov al,[ebx+eax] // First remap step (8 bit to 8 bit).
mov ax,[edx+eax*2] // Second remap step (8 bit to 16 bit).
mov [edi],ax
jmp again
}
over:;
}
inline void BlitPlainXlat<unsigned short>::BlitForward(void * dest, void const * source, int len) const
{
unsigned short const * remapper = TranslateTable;
__asm {
mov ebx,[remapper]
mov ecx,[len]
mov esi,[source]
mov edi,[dest]
sub edi,2
}
again:
/*
** This block processes pixels at 7 clocks per pixel.
*/
__asm {
xor eax,eax
add edi,2
mov al,[esi]
inc esi
mov ax,[ebx+eax*2]
mov [edi],ax
dec ecx
jnz again
}
}
#endif
#endif