hostile-takeover/game/bitmap.cpp
2016-08-31 23:55:30 -04:00

547 lines
14 KiB
C++

#include "ht.h"
namespace wi {
// Dib bitmaps
DibBitmap *CreateDibBitmap(byte *pb, int cx, int cy)
{
DibBitmap *pbm = new DibBitmap;
Assert(pbm != NULL, "out of memory!");
if (pbm == NULL)
return NULL;
if (!pbm->Init(pb, cx, cy)) {
delete pbm;
return NULL;
}
return pbm;
}
DibBitmap::DibBitmap()
{
m_cbRow = 0;
m_cb = 0;
m_pb = NULL;
m_siz.cx = 0;
m_siz.cy = 0;
m_wf = 0;
}
DibBitmap::~DibBitmap()
{
if (m_wf & kfDibFreeMem)
delete[] m_pb;
m_pb = NULL;
}
bool DibBitmap::Init(byte *pb, int cx, int cy)
{
m_cbRow = (cx + 1) & ~1;
m_cb = (int)cy * m_cbRow;
if (pb == NULL) {
m_pb = new byte[m_cb];
Assert(m_pb != NULL, "out of memory!");
m_wf |= kfDibFreeMem;
} else {
m_pb = pb;
}
m_siz.cx = cx;
m_siz.cy = cy;
return m_pb != NULL;
}
byte *DibBitmap::GetBits()
{
return m_pb;
}
int DibBitmap::GetRowBytes()
{
return m_cbRow;
}
#define TopToBottomBltThunk LeftToRightBltThunk
#define FastestBltThunk LeftToRightBltThunk
#define BottomToTopBltThunk RightToLeftBltThunk
void DibBitmap::Blt(DibBitmap *pbmSrc, Rect *prcSrc, int xDst, int yDst)
{
// Get src dib dimensions
Size sizSrc;
pbmSrc->GetSize(&sizSrc);
// Clip to source rect
if (prcSrc->left < 0)
prcSrc->left = 0;
if (prcSrc->top < 0)
prcSrc->top = 0;
if (prcSrc->right > sizSrc.cx)
prcSrc->right = sizSrc.cx;
if (prcSrc->bottom > sizSrc.cy)
prcSrc->bottom = sizSrc.cy;
// Clip to dest
if (xDst < 0) {
prcSrc->left -= xDst;
xDst = 0;
}
if (yDst < 0) {
prcSrc->top -= yDst;
yDst = 0;
}
int xRightDst = xDst + prcSrc->Width();
if (xRightDst > m_siz.cx)
prcSrc->right -= xRightDst - m_siz.cx;
int yBottomDst = yDst + prcSrc->Height();
if (yBottomDst > m_siz.cy)
prcSrc->bottom -= yBottomDst - m_siz.cy;
// Anything to blt?
if (prcSrc->IsEmpty())
return;
// Figure out direction to blt if we're doing a blt in the same dib
if (this == pbmSrc) {
// Calc addresses
byte *pbSrc = m_pb + (long)prcSrc->top * m_cbRow + prcSrc->left;
byte *pbDst = m_pb + (long)yDst * m_cbRow + xDst;
// If same y, ...
if (yDst == prcSrc->top) {
// Overlap?
int cxInterval = prcSrc->right - xDst;
if (cxInterval > 0 && cxInterval < prcSrc->Width() * 2) {
// Overlap. If bltting to the left, copy from left to right
if (xDst < prcSrc->left) {
LeftToRightBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height());
} else {
RightToLeftBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height());
}
} else {
// No overlap. Do the fastest blt
FastestBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height());
}
} else {
int cyInterval = prcSrc->bottom - yDst;
if (cyInterval > 0 && cyInterval < prcSrc->Height() * 2) {
// Overlap. If bltting upwards, copy from top to bottom
if (yDst < prcSrc->top) {
TopToBottomBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height());
} else {
BottomToTopBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height());
}
} else {
// No overlap. Do the fastest blt
FastestBltThunk(pbSrc, m_cbRow, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height());
}
}
} else {
// No overlap. Do the fastest blt
int cbRowBytesSrc = pbmSrc->GetRowBytes();
byte *pbSrc = pbmSrc->GetBits() + (long)prcSrc->top * cbRowBytesSrc + prcSrc->left;
byte *pbDst = m_pb + (long)yDst * m_cbRow + xDst;
FastestBltThunk(pbSrc, cbRowBytesSrc, pbDst, m_cbRow, prcSrc->Width(), prcSrc->Height());
}
}
void DibBitmap::Clear(Color clr)
{
#ifdef __CPU_68K
if (gfArmPresent) {
memsetArm(m_pb, (byte)clr, m_cb);
return;
}
#endif
memset(m_pb, (byte)clr, m_cb);
}
void DibBitmap::Fill(int x, int y, int cx, int cy, Color clr)
{
// Destination clipping
if (x < 0) {
cx += x;
x = 0;
}
if (y < 0) {
cy += y;
y = 0;
}
if (x + cx > m_siz.cx)
cx = m_siz.cx - x;
if (y + cy > m_siz.cy)
cy = m_siz.cy - y;
if (cx <= 0 || cy <= 0)
return;
// Drawing
byte *pbDst = m_pb + (long)y * m_cbRow + x;
FillThunk(pbDst, cx, cy, m_cbRow, (byte)clr);
}
void DibBitmap::Shadow(int x, int y, int cx, int cy)
{
// Destination clipping
if (x < 0) {
cx += x;
x = 0;
}
if (y < 0) {
cy += y;
y = 0;
}
if (x + cx > m_siz.cx)
cx = m_siz.cx - x;
if (y + cy > m_siz.cy)
cy = m_siz.cy - y;
if (cx <= 0 || cy <= 0)
return;
byte *pbRow = m_pb + (long)y * m_cbRow + x;
FillShadowThunk(pbRow, m_cbRow, cx, cy, gmpiclriclrShadow);
}
void DibBitmap::GetSize(Size *psiz)
{
*psiz = m_siz;
}
#ifdef DRAW_LINES
#define LEFT 1
#define RIGHT 2
#define BOTTOM 4
#define TOP 8
#define SWAP(x, y) { int _t = x; x = y; y = _t; }
#define OUTCODE(x, y, outcode, type) \
{ \
if (x < xl) outcode = LEFT, type = 1; \
else if (x > xr) outcode = RIGHT, type = 1; \
else outcode = type = 0; \
if (y < yb) outcode |= BOTTOM, type++; \
else if (y > yt) outcode |= TOP, type++; \
}
#define CLIP(a1, a2, b1, da, da2, db2, as, bs, sa, sb, \
amin, AMIN, amax, AMAX, bmin, BMIN, bmax, BMAX) \
{ \
if (out1) { \
if (out1 & AMIN) { ca = db2 * (amin - a1); as = amin; } \
else if (out1 & AMAX) { ca = db2 * (a1 - amax); as = amax; } \
if (out1 & BMIN) { cb = da2 * (bmin - b1); bs = bmin; } \
else if (out1 & BMAX) { cb = da2 * (b1 - bmax); bs = bmax; } \
if (type1 == 2) \
out1 &= (ca + da < cb) ? ~(AMIN | AMAX) : ~(BMAX | BMIN); \
if (out1 & (AMIN | AMAX)) { \
cb = (ca + da) / da2; \
if (sb >= 0) { if ((bs = b1 + cb) > bmax) return; } \
else { if ((bs = b1 - cb) < bmin) return; } \
r += ca - da2 * cb; \
} \
else { \
ca = (cb - da + db2 - 1) / db2; \
if (sa >= 0) { if ((as = a1 + ca) > amax) return; } \
else { if ((as = a1 - ca) < amin) return; } \
r += db2 * ca - cb; \
} \
} \
else { as = a1; bs = b1; } \
alt = 0; \
if (out2) { \
if (type2 == 2) { \
ca = db2 * ((out2 & AMIN) ? a1 - amin : amax - a1); \
cb = da2 * ((out2 & BMIN) ? b1 - bmin : bmax - b1); \
out2 &= (cb + da < ca + 1) ? ~(AMIN | AMAX) : ~(BMIN | BMAX); \
} \
if (out2 & (AMIN | AMAX)) n = (out2 & AMIN) ? as - amin : amax - as; \
else { n = (out2 & BMIN) ? bs - bmin : bmax - bs; alt = 1; } \
} \
else n = (a2 >= as) ? a2 - as : as - a2; \
}
#define plot(x, y) (*(m_pb + (y * m_cbRow) + x) = (byte)clr)
void DibBitmap::DrawLine(short x1, short y1, short x2, short y2, Color clr)
{
int xl = 0, yb = 0;
int xr = m_siz.cx - 1;
int yt = m_siz.cy - 1;
int adx, ady, adx2, ady2, sx, sy;
int out1, out2, type1, type2;
int ca, cb, r, diff, xs, ys, n, alt;
OUTCODE(x1, y1, out1, type1);
OUTCODE(x2, y2, out2, type2);
if (out1 & out2) return;
if ((type1 != 0 && type2 == 0) || (type1 == 2 && type2 == 1)){
SWAP(out1, out2);
SWAP(type1, type2);
SWAP(x1, x2);
SWAP(y1, y2);
}
xs = x1;
ys = y1;
sx = 1;
adx = x2 - x1;
if (adx < 0) { adx = -adx; sx = -1; }
sy = 1;
ady = y2 - y1;
if (ady < 0) { ady = -ady; sy = -1; }
adx2 = adx + adx;
ady2 = ady + ady;
if (adx >= ady) {
/*
* line is semi-horizontal
*/
r = ady2 - adx;
CLIP(x1, x2, y1, adx, adx2, ady2, xs, ys, sx, sy,
xl, LEFT, xr, RIGHT, yb, BOTTOM, yt, TOP);
diff = ady2 - adx2;
if (alt) {
for (;; xs += sx) { /* alternate Bresenham */
plot(xs, ys);
if (r >= 0 ) {
if (--n < 0) break;
r += diff;
ys += sy;
}
else r += ady2;
}
}
else{
for (;; xs += sx) { /* standard Bresenham */
plot(xs, ys);
if (--n < 0) break;
if (r >= 0 ) { r += diff; ys += sy; }
else r += ady2;
}
}
}
else {
/*
* line is semi-vertical
*/
r = adx2 - ady;
CLIP(y1, y2, x1, ady, ady2, adx2, ys, xs, sy, sx,
yb, BOTTOM, yt, TOP, xl, LEFT, xr, RIGHT);
diff = adx2 - ady2;
if (alt) {
for (;; ys += sy) { /* alternate Bresenham */
plot(xs, ys);
if (r >= 0 ) {
if (--n < 0) break;
r += diff;
xs += sx;
}
else r += adx2;
}
}
else {
for (;; ys += sy) { /* standard Bresenham */
plot(xs, ys);
if (--n < 0) break;
if (r >= 0 ) { r += diff; xs += sx; }
else r += adx2;
}
}
}
}
#endif // DRAW_LINES
DibBitmap *DibBitmap::Suballoc(int yTop, int cy)
{
Assert(yTop < m_siz.cy && yTop + cy <= m_siz.cy);
byte *pb = m_pb + (long)m_siz.cx * yTop;
return CreateDibBitmap(pb, m_siz.cx, cy);
}
void DibBitmap::Scroll(Rect *prcSrc, int xDst, int yDst)
{
// Some implementations do blts differently (such as rotated dibs).
// That is why this method - essentially a blt from and to the
// destination - exists.
Blt(this, prcSrc, xDst, yDst);
}
#ifdef __CPU_68K
void DibBitmap::BltTiles(DibBitmap *pbmSrc, UpdateMap *pupd, int yTopDst)
{
// OS5 path usually not executed
if (IsOS50Compat()) {
bool fFirst = true;
Rect rc;
while (pupd->EnumUpdateRects(fFirst, NULL, &rc)) {
fFirst = false;
Blt(pbmSrc, &rc, rc.left, rc.top + yTopDst);
}
return;
}
// Init
bool *pfMap = pupd->GetInvalidMap();
MapInfo *pmnfo = pupd->GetMapInfo();
Size sizMap;
pupd->GetMapSize(&sizMap);
dword cbOffset = yTopDst * (dword)m_siz.cx;
byte *pbSrc = pbmSrc->GetBits();
byte *pbDst = m_pb + cbOffset;
switch (gcxTile) {
case 16:
UpdateScreen816(pfMap, sizMap.cx, sizMap.cy, pbSrc, pbDst, m_siz.cx,
pmnfo->cxLeftTile, pmnfo->cyTopTile, pmnfo->cxRightTile, pmnfo->cyBottomTile, pmnfo->ctxInside, pmnfo->ctyInside);
break;
case 24:
UpdateScreen824(pfMap, sizMap.cx, sizMap.cy, pbSrc, pbDst, m_siz.cx,
pmnfo->cxLeftTile, pmnfo->cyTopTile, pmnfo->cxRightTile, pmnfo->cyBottomTile, pmnfo->ctxInside, pmnfo->ctyInside);
break;
}
}
#else
void DibBitmap::BltTiles(DibBitmap *pbmSrc, UpdateMap *pupd, int yTopDst)
{
bool fFirst = true;
Rect rc;
while (pupd->EnumUpdateRects(fFirst, NULL, &rc)) {
fFirst = false;
Blt(pbmSrc, &rc, rc.left, rc.top + yTopDst);
}
}
#if 0
void DibBitmap::BltTiles(DibBitmap *pbmSrc, UpdateMap *pupd, int yTopDst)
{
MapInfo *pmnfo = pupd->GetMapInfo();
bool *pfInvalid = pupd->GetInvalidMap();
Size sizMap;
pupd->GetMapSize(&sizMap);
int cbReturn = sizMap.cx - ((pmnfo->cxLeftTile != 0 ? 1 : 0) + pmnfo->ctxInside + (pmnfo->cxRightTile != 0 ? 1 : 0));
Rect rcSrc;
int xT = 0;
int yDst = yTopDst;
int ySrc = 0;
if (pmnfo->cyTopTile != 0) {
// Upper left corner
if (pmnfo->cxLeftTile != 0 && *pfInvalid++) {
rcSrc.Set(xT, ySrc, xT + pmnfo->cxLeftTile, ySrc + pmnfo->cyTopTile);
Blt(pbmSrc, &rcSrc, xT, yDst);
}
xT += pmnfo->cxLeftTile;
// Upper edge
for (int tx = 0; tx < pmnfo->ctxInside; tx++) {
if (*pfInvalid++) {
rcSrc.Set(xT, ySrc, xT + gcxTile, ySrc + pmnfo->cyTopTile);
Blt(pbmSrc, &rcSrc, xT, yDst);
}
xT += gcxTile;
}
// Upper right corner
if (pmnfo->cxRightTile != 0 && *pfInvalid++) {
rcSrc.Set(xT, ySrc, xT + pmnfo->cxRightTile, ySrc + pmnfo->cyTopTile);
Blt(pbmSrc, &rcSrc, xT, yDst);
}
xT = 0;
yDst += pmnfo->cyTopTile;
ySrc += pmnfo->cyTopTile;
pfInvalid += cbReturn;
}
// Inside
for (int ty = 0; ty < pmnfo->ctyInside; ty++) {
// Inside left
if (pmnfo->cxLeftTile != 0 && *pfInvalid++) {
rcSrc.Set(xT, ySrc, xT + pmnfo->cxLeftTile, ySrc + gcyTile);
Blt(pbmSrc, &rcSrc, xT, yDst);
}
xT += pmnfo->cxLeftTile;
// Inside
for (int tx = 0; tx < pmnfo->ctxInside; tx++) {
if (*pfInvalid++) {
rcSrc.Set(xT, ySrc, xT + gcxTile, ySrc + gcyTile);
Blt(pbmSrc, &rcSrc, xT, yDst);
}
xT += gcxTile;
}
// Inside right
if (pmnfo->cxRightTile != 0 && *pfInvalid++) {
rcSrc.Set(xT, ySrc, xT + pmnfo->cxRightTile, ySrc + gcyTile);
Blt(pbmSrc, &rcSrc, xT, yDst);
}
xT = 0;
yDst += gcyTile;
ySrc += gcyTile;
pfInvalid += cbReturn;
}
if (pmnfo->cyBottomTile != 0) {
// Bottom left tile
if (pmnfo->cxLeftTile != 0 && *pfInvalid++) {
rcSrc.Set(xT, ySrc, xT + pmnfo->cxLeftTile, ySrc + pmnfo->cyBottomTile);
Blt(pbmSrc, &rcSrc, xT, yDst);
}
xT += pmnfo->cxLeftTile;
// Bottom edge
for (int tx = 0; tx < pmnfo->ctxInside; tx++) {
if (*pfInvalid++) {
rcSrc.Set(xT, ySrc, xT + gcxTile, ySrc + pmnfo->cyBottomTile);
Blt(pbmSrc, &rcSrc, xT, yDst);
}
xT += gcxTile;
}
// Bottom right corner
if (pmnfo->cxRightTile != 0 && *pfInvalid++) {
rcSrc.Set(xT, ySrc, xT + pmnfo->cxRightTile, ySrc + pmnfo->cyBottomTile);
Blt(pbmSrc, &rcSrc, xT, yDst);
}
}
}
#endif
#endif
} // namespace wi