Files
2026-02-02 04:50:13 +01:00

1126 lines
26 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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/>.
*
*/
#include "common/file.h"
#include "common/system.h"
#include "image/bmp.h"
#include "image/png.h"
#include "bagel/spacebar/boflib/gfx/bitmap.h"
#include "bagel/spacebar/boflib/app.h"
#include "bagel/spacebar/boflib/file.h"
#include "bagel/boflib/file_functions.h"
#include "bagel/boflib/misc.h"
namespace Bagel {
namespace SpaceBar {
#define TEST_DEBUG 0
bool CBofBitmap::_bUseBackdrop = false;
extern bool g_bRealizePalette;
CBofBitmap::CBofBitmap() {
_szFileName[0] = '\0';
}
CBofBitmap::CBofBitmap(int dx, int dy, CBofPalette *pPalette, bool bOwnPalette, byte *pPrivateBuff) {
assert((dx > 0) && (dy > 0));
// Use application's palette if not supplied
//
if (pPalette == nullptr) {
bOwnPalette = false;
pPalette = CBofApp::getApp()->getPalette();
}
// Inits
//
_nDX = dx;
_nDY = dy;
_nScanDX = ((dx + 3) & ~3);
_bTopDown = true;
_bOwnPalette = bOwnPalette;
_bReadOnly = false;
_szFileName[0] = '\0';
_pPalette = nullptr;
_bInitialized = true;
// Allow privatization of the bitmap (used only on mac from displayTextEx).;
_pBits = pPrivateBuff;
if (pPrivateBuff != nullptr) {
_bitmap.w = dx;
_bitmap.h = dy;
_bitmap.pitch = _nScanDX;
_bitmap.format = Graphics::PixelFormat::createFormatCLUT8();
_bitmap.setPixels(pPrivateBuff);
}
_pPalette = pPalette;
load();
}
CBofBitmap::CBofBitmap(const char *pszFileName, CBofPalette *pPalette, bool bOwnPalette) {
assert(pszFileName != nullptr);
_bOwnPalette = bOwnPalette;
_szFileName[0] = '\0';
_bInitialized = true;
if (pPalette == nullptr) {
pPalette = new CBofPalette(pszFileName);
_bOwnPalette = true;
}
// Init the info needed to load a bitmap from disk
_pPalette = pPalette;
if (fileGetFullPath(_szFileName, pszFileName) != nullptr) {
// Load this bitmap into the cache
load();
} else {
reportError(ERR_FFIND, "Could not build full path to %s", pszFileName);
}
}
CBofBitmap::~CBofBitmap() {
assert(isValidObject(this));
release();
if (_bOwnPalette && (_pPalette != nullptr)) {
delete _pPalette;
_bOwnPalette = false;
}
_pPalette = nullptr;
_bInitialized = false;
}
ErrorCode CBofBitmap::buildBitmap(CBofPalette *pPalette) {
assert(isValidObject(this));
assert(pPalette != nullptr);
if (_errCode == ERR_NONE) {
_bitmap.create(_nDX, _nDY, Graphics::PixelFormat::createFormatCLUT8());
_pBits = (byte *)_bitmap.getBasePtr(0, 0);
_nScanDX = _bitmap.pitch;
// Set this bitmap's palette
setPalette(pPalette, _bOwnPalette);
}
return _errCode;
}
bool CBofBitmap::alloc() {
if (_szFileName[0] != '\0') {
loadBitmap(_szFileName, _pPalette);
} else {
buildBitmap(_pPalette);
}
return !errorOccurred();
}
void CBofBitmap::free() {
releaseBitmap();
}
void CBofBitmap::releaseBitmap() {
assert(isValidObject(this));
_bitmap.clear();
}
ErrorCode CBofBitmap::loadBitmap(const char *pszFileName, CBofPalette *pPalette) {
assert(isValidObject(this));
assert(pszFileName != nullptr);
assert(pPalette != nullptr);
// Release any previous bitmap info
releaseBitmap();
if (_errCode == ERR_NONE) {
CBofFile *pFile = new CBofFile(pszFileName, CBOFFILE_READONLY);
// Open bitmap
// filename must fit into our buffer
assert(strlen(pszFileName) < MAX_FNAME);
// Keep track of this filename
Common::strcpy_s(_szFileName, pszFileName);
// Decode the bitmap
Image::BitmapDecoder decoder;
Common::SeekableReadStream *rs = *pFile;
if (!rs || !decoder.loadStream(*rs))
error("Could not load bitmap %s", pszFileName);
// Load up the decoded bitmap
_bitmap.copyFrom(*decoder.getSurface());
// Load the bitmap palette
_bitmap.setPalette(decoder.getPalette().data(), 0, decoder.getPalette().size());
_nDX = _bitmap.w;
_nDY = _bitmap.h;
_nScanDX = _bitmap.pitch;
_pBits = (byte*)_bitmap.getBasePtr(0, 0);
// Close bitmap file
delete pFile;
}
return _errCode;
}
ErrorCode CBofBitmap::paint(CBofWindow *pWnd, int x, int y, CBofRect *pSrcRect, int nMaskColor) {
assert(isValidObject(this));
assert(pWnd != nullptr);
CBofRect cRect(x, y, x + _nDX - 1, y + _nDY - 1);
if (pSrcRect != nullptr) {
cRect.setRect(x, y, x + pSrcRect->width() - 1, y + pSrcRect->height() - 1);
}
return paint(pWnd, &cRect, pSrcRect, nMaskColor);
}
ErrorCode CBofBitmap::paint(CBofWindow *pWnd, CBofRect *pDstRect, CBofRect *pSrcRect, int nMaskColor) {
assert(isValidObject(this));
assert(pWnd != nullptr);
Graphics::ManagedSurface *dstSurf = pWnd->getSurface();
CBofRect cDestRect(0, 0, _nDX - 1, _nDY - 1), cSourceRect(0, 0, _nDX - 1, _nDY - 1);
if (_errCode == ERR_NONE) {
if (pDstRect != nullptr) {
assert((pDstRect->width() > 0) && (pDstRect->height() > 0));
cDestRect = *pDstRect;
}
if (pSrcRect != nullptr) {
assert((pSrcRect->width() > 0) && (pSrcRect->height() > 0));
cSourceRect = *pSrcRect;
}
if (_bitmap.format.bytesPerPixel == 1) {
// Bitmap is paletted, so ensure it's palette is updated
const auto &pal = _pPalette->getPalette();
_bitmap.setPalette(pal._data, 0, pal._numColors);
}
// Handle the blitting
if (nMaskColor == NOT_TRANSPARENT) {
dstSurf->blitFrom(_bitmap, cSourceRect, cDestRect);
} else {
dstSurf->transBlitFrom(_bitmap, cSourceRect, cDestRect, nMaskColor);
}
}
return _errCode;
}
ErrorCode CBofBitmap::paintMaskBackdrop(CBofWindow *pWnd, CBofRect *pDstRect, CBofRect *pSrcRect, int nMaskColor) {
assert(isValidObject(this));
assert(pWnd != nullptr);
// This function requires the use of a backdrop
assert(pWnd->getBackdrop() != nullptr);
CBofRect cDestRect(0, 0, _nDX - 1, _nDY - 1), cSourceRect(0, 0, _nDX - 1, _nDY - 1);
if (_errCode == ERR_NONE) {
if (pDstRect != nullptr) {
assert((pDstRect->width() > 0) && (pDstRect->height() > 0));
cDestRect = *pDstRect;
}
if (pSrcRect != nullptr) {
assert((pSrcRect->width() > 0) && (pSrcRect->height() > 0));
cSourceRect = *pSrcRect;
}
CBofBitmap cTempBitmap(cSourceRect.width(), cSourceRect.height(), _pPalette);
CBofBitmap *pBackdrop = pWnd->getBackdrop();
// Use the backdrop to get the background instead of trying to
// capture the screen
if (pBackdrop != nullptr) {
CBofRect cTempRect = cTempBitmap.getRect();
pBackdrop->paint(&cTempBitmap, &cTempRect, &cDestRect);
}
paint(&cTempBitmap, 0, 0, &cSourceRect, nMaskColor);
cTempBitmap.paint(pWnd, &cDestRect);
unlock();
}
return _errCode;
}
ErrorCode CBofBitmap::paint(CBofBitmap *pBmp, int x, int y, CBofRect *pSrcRect, int nMaskColor) {
assert(isValidObject(this));
assert(pBmp != nullptr);
CBofRect cRect(x, y, x + _nDX - 1, y + _nDY - 1);
if (pSrcRect != nullptr) {
cRect.setRect(x, y, x + pSrcRect->width() - 1, y + pSrcRect->height() - 1);
}
return paint(pBmp, &cRect, pSrcRect, nMaskColor);
}
ErrorCode CBofBitmap::paint(CBofBitmap *pBmp, CBofRect *pDstRect, CBofRect *pSrcRect, int nMaskColor) {
assert(isValidObject(this));
assert(pBmp != nullptr);
CBofRect cDestRect(0, 0, _nDX - 1, _nDY - 1), cSourceRect(0, 0, _nDX - 1, _nDY - 1);
CBofRect cClipRect;
if (!errorOccurred() && !pBmp->errorOccurred()) {
if (pDstRect != nullptr) {
assert((pDstRect->width() > 0) && (pDstRect->height() > 0));
cDestRect = *pDstRect;
}
if (pSrcRect != nullptr) {
assert((pSrcRect->width() > 0) && (pSrcRect->height() > 0));
cSourceRect = *pSrcRect;
}
// If painting entirely outside destination bitmap, then no need to paint.
CBofRect cRect = pBmp->getRect();
if (cClipRect.intersectRect(&cRect, &cDestRect) == 0) {
return _errCode;
}
// Clip so we don't paint outside of the bitmap's memory buffer
cSourceRect.left += cClipRect.left - cDestRect.left;
cSourceRect.right += cClipRect.right - cDestRect.right;
cSourceRect.top += cClipRect.top - cDestRect.top;
cSourceRect.bottom += cClipRect.bottom - cDestRect.bottom;
cDestRect = cClipRect;
// If painting from entirely outside this bitmap, then don't paint at all.
cRect = getRect();
if (cClipRect.intersectRect(&cRect, &cSourceRect) == 0) {
return _errCode;
}
// Clip so we don't paint outside of the bitmap's memory buffer
cDestRect.left += cClipRect.left - cSourceRect.left;
cDestRect.right += cClipRect.right - cSourceRect.right;
cDestRect.top += cClipRect.top - cSourceRect.top;
cDestRect.bottom += cClipRect.bottom - cSourceRect.bottom;
cSourceRect = cClipRect;
// Lock these bitmaps down so we can start painting
lock();
pBmp->lock();
// Do the actual painting.
// Since we are copying from bitmap to bitmap, using the
// blit bitmap routine is a kosher thing to do, there is no screen drawing
if (nMaskColor == NOT_TRANSPARENT) {
pBmp->_bitmap.blitFrom(_bitmap, cSourceRect, cDestRect);
} else {
pBmp->_bitmap.transBlitFrom(_bitmap, cSourceRect, cDestRect, nMaskColor);
}
// Don't need a lock on these guys anymore
pBmp->unlock();
unlock();
}
return _errCode;
}
ErrorCode CBofBitmap::paint1To1(CBofBitmap *pBmp) {
assert(isValidObject(this));
assert(pBmp != nullptr);
if (!errorOccurred() && !pBmp->errorOccurred()) {
lock();
pBmp->lock();
// Direct 1 to 1 copy
memcpy(pBmp->_pBits, _pBits, _nScanDX * _nDY);
pBmp->unlock();
unlock();
}
return _errCode;
}
ErrorCode CBofBitmap::paintStretch4(CBofBitmap *pBmp, CBofRect *pDstRect, CBofRect *pSrcRect) {
assert(isValidObject(this));
assert(pBmp != nullptr);
assert(pDstRect != nullptr);
assert(pSrcRect != nullptr);
// These bitmaps MUST be locked down before hand
assert(isLocked());
assert(pBmp->isLocked());
if (_errCode == ERR_NONE) {
int dy1 = _nDY;
int dx1 = _nScanDX;
int dy2 = pBmp->_nDY;
int dx2 = pBmp->_nScanDX;
byte *pDestBits = pBmp->_pBits;
byte *pSrcBits = _pBits;
int32 dy = pSrcRect->height();
int32 nDstHeight = pDstRect->height();
int32 x1 = pSrcRect->left;
int32 y1 = pSrcRect->top;
int32 x2 = pDstRect->left;
int32 y2 = pDstRect->top;
if (_bTopDown) {
pSrcBits += y1 * dx1 + x1;
} else {
pSrcBits += (dy1 - y1 - 1) * dx1 + x1;
dx1 = -dx1;
}
if (pBmp->_bTopDown) {
pDestBits += y2 * dx2 + x2;
} else {
pDestBits += (dy2 - y2 - 1) * dx2 + x2;
dx2 = -dx2;
}
Fixed sourceStepY = fixedDivide(intToFixed(dy), intToFixed(nDstHeight));
Fixed posY = 0;
byte *pSrcEnd = pSrcBits + (dy - 1) * dx1;
byte *pDestEnd = pDestBits + (nDstHeight - 1) * dx2;
nDstHeight >>= 1;
while (nDstHeight > 0) {
nDstHeight -= 4;
if (posY >= 0x00010000) {
pSrcBits += dx1;
pSrcEnd -= dx1;
posY &= 0x0000FFFF;
}
*(uint32 *)pDestBits = *(uint32 *)pSrcBits;
*(uint32 *)pDestEnd = *(uint32 *)pSrcEnd;
posY += sourceStepY;
pDestBits += dx2;
pDestEnd -= dx2;
if (posY >= 0x00010000) {
pSrcBits += dx1;
pSrcEnd -= dx1;
posY &= 0x0000FFFF;
}
*(uint32 *)pDestBits = *(uint32 *)pSrcBits;
*(uint32 *)pDestEnd = *(uint32 *)pSrcEnd;
posY += sourceStepY;
pDestBits += dx2;
pDestEnd -= dx2;
if (posY >= 0x00010000) {
pSrcBits += dx1;
pSrcEnd -= dx1;
posY &= 0x0000FFFF;
}
*(uint32 *)pDestBits = *(uint32 *)pSrcBits;
*(uint32 *)pDestEnd = *(uint32 *)pSrcEnd;
posY += sourceStepY;
pDestBits += dx2;
pDestEnd -= dx2;
if (posY >= 0x00010000) {
pSrcBits += dx1;
pSrcEnd -= dx1;
posY &= 0x0000FFFF;
}
*(uint32 *)pDestBits = *(uint32 *)pSrcBits;
*(uint32 *)pDestEnd = *(uint32 *)pSrcEnd;
posY += sourceStepY;
pDestBits += dx2;
pDestEnd -= dx2;
}
}
return _errCode;
}
ErrorCode CBofBitmap::paintStretchOpt(CBofBitmap *pBmp, CBofRect *pDstRect, CBofRect *pSrcRect, int nOptSize) {
assert(isValidObject(this));
assert(pBmp != nullptr);
assert(pDstRect != nullptr);
assert(pSrcRect != nullptr);
// These bitmaps MUST be locked down before hand
assert(isLocked());
assert(pBmp->isLocked());
if (_errCode == ERR_NONE) {
int dy1 = _nDY;
int dx1 = _nScanDX;
int dy2 = pBmp->_nDY;
int dx2 = pBmp->_nScanDX;
byte *pDestBits = pBmp->_pBits;
byte *pSrcBits = _pBits;
int32 dy = pSrcRect->height();
int32 nDstHeight = pDstRect->height();
int32 x1 = pSrcRect->left;
int32 y1 = pSrcRect->top;
int32 x2 = pDstRect->left;
int32 y2 = pDstRect->top;
if (_bTopDown) {
pSrcBits += y1 * dx1 + x1;
} else {
pSrcBits += (dy1 - y1 - 1) * dx1 + x1;
dx1 = -dx1;
}
if (pBmp->_bTopDown) {
pDestBits += y2 * dx2 + x2;
} else {
pDestBits += (dy2 - y2 - 1) * dx2 + x2;
dx2 = -dx2;
}
Fixed sourceStepY = fixedDivide(intToFixed(dy), intToFixed(nDstHeight));
Fixed posY = 0;
byte *pSrcEnd = pSrcBits + (dy - 1) * dx1;
byte *pDestEnd = pDestBits + (nDstHeight - 1) * dx2;
int nMod = (int)nDstHeight & 1;
nDstHeight >>= 1;
while (nDstHeight-- > 0) {
if (posY >= 0x00010000) {
int32 lInc = fixedToInt(posY) * dx1;
pSrcBits += lInc;
pSrcEnd -= lInc;
posY &= 0x0000FFFF;
}
for (int i = 0; i < nOptSize; i += 4) {
*(uint32 *)(pDestBits + i) = *(uint32 *)(pSrcBits + i);
*(uint32 *)(pDestEnd + i) = *(uint32 *)(pSrcEnd + i);
}
posY += sourceStepY;
pDestBits += dx2;
pDestEnd -= dx2;
}
if (nMod != 0) {
*(uint32 *)pDestEnd = *(uint32 *)pSrcEnd;
}
}
return _errCode;
}
ErrorCode CBofBitmap::captureScreen(CBofWindow *pWnd, CBofRect *pSrcRect, CBofRect *pDstRect) {
assert(isValidObject(this));
assert(pWnd != nullptr);
assert(pSrcRect != nullptr);
if (_errCode == ERR_NONE) {
CBofRect cDestRect(0, 0, _nDX - 1, _nDY - 1);
if (pDstRect != nullptr) {
cDestRect = *pDstRect;
}
CBofRect cSrcRect = *pSrcRect;
CBofBitmap *pBackdrop = pWnd->getBackdrop();
// If we're capturing the screen, we have to convert the format first.
if (!_bUseBackdrop || pBackdrop == nullptr) {
Graphics::Surface *tmp = pWnd->getSurface()->rawSurface().convertTo(_bitmap.format, nullptr, 0, _pPalette->getData(), Graphics::PALETTE_COUNT);
_bitmap.blitFrom(*tmp, cSrcRect, cDestRect);
tmp->free();
delete tmp;
} else {
// Optimization to use the window's backdrop bitmap instead of doing
// an actual screen capture.
pBackdrop->paint(this, &cDestRect, &cSrcRect);
}
}
return _errCode;
}
void CBofBitmap::setPalette(CBofPalette *pBofPalette, bool bOwnPalette) {
assert(isValidObject(this));
assert(pBofPalette != nullptr);
if ((_errCode == ERR_NONE) && (pBofPalette != nullptr)) {
if (_bOwnPalette && (_pPalette != nullptr) && (_pPalette != pBofPalette)) {
delete _pPalette;
}
_bOwnPalette = bOwnPalette;
_pPalette = pBofPalette;
_bitmap.setPalette(_pPalette->getData(), 0, Graphics::PALETTE_COUNT);
}
}
byte *CBofBitmap::getPixelAddress(int x, int y) {
assert(isValidObject(this));
// You can not call this function unless you manually lock this bitmap
assert(isLocked());
// The pixel in question must be in the bitmap area
assert(getRect().ptInRect(CBofPoint(x, y)));
return (byte *)_bitmap.getBasePtr(x, y);
}
byte CBofBitmap::readPixel(int x, int y) {
assert(isValidObject(this));
lock();
byte chPixel = *getPixelAddress(x, y);
unlock();
return chPixel;
}
void CBofBitmap::writePixel(int x, int y, byte iColor) {
assert(isValidObject(this));
lock();
byte *pPixel = getPixelAddress(x, y);
*pPixel = iColor;
unlock();
}
void CBofBitmap::circle(int xCenter, int yCenter, uint16 nRadius, byte iColor) {
assert(isValidObject(this));
if (_errCode == ERR_NONE) {
int x = 0;
int y = nRadius;
int i = 3 - 2 * y;
while (x < y) {
writePixel(xCenter + x, yCenter + y, iColor);
writePixel(xCenter - x, yCenter + y, iColor);
writePixel(xCenter + y, yCenter + x, iColor);
writePixel(xCenter - y, yCenter + x, iColor);
writePixel(xCenter - x, yCenter - y, iColor);
writePixel(xCenter + x, yCenter - y, iColor);
writePixel(xCenter - y, yCenter - x, iColor);
writePixel(xCenter + y, yCenter - x, iColor);
if (i <= 0) {
i += 4 * x + 6;
} else {
i += 4 * (x - y) + 10;
y--;
}
x++;
}
if (x == y) {
writePixel(xCenter + x, yCenter + y, iColor);
writePixel(xCenter - x, yCenter + y, iColor);
writePixel(xCenter + y, yCenter + x, iColor);
writePixel(xCenter - y, yCenter + x, iColor);
writePixel(xCenter - x, yCenter - y, iColor);
writePixel(xCenter + x, yCenter - y, iColor);
writePixel(xCenter - y, yCenter - x, iColor);
writePixel(xCenter + y, yCenter - x, iColor);
}
}
}
void CBofBitmap::drawRect(CBofRect *pRect, byte iColor) {
assert(isValidObject(this));
assert(pRect != nullptr);
if (_errCode == ERR_NONE) {
line(pRect->left, pRect->top, pRect->right, pRect->top, iColor);
line(pRect->right, pRect->top, pRect->right, pRect->bottom, iColor);
line(pRect->right, pRect->bottom, pRect->left, pRect->bottom, iColor);
line(pRect->left, pRect->bottom, pRect->left, pRect->top, iColor);
}
}
void CBofBitmap::fillRect(CBofRect *pRect, byte iColor) {
assert(isValidObject(this));
if (_errCode == ERR_NONE) {
if (pRect == nullptr) {
_bitmap.clear(iColor);
} else {
Common::Rect rect(pRect->left, pRect->top, pRect->right, pRect->bottom);
_bitmap.fillRect(rect, iColor);
}
}
}
void CBofBitmap::line(int nSrcX, int nSrcY, int nDstX, int nDstY, byte iColor) {
assert(isValidObject(this));
// The source and destination points must be in the bitmap area
assert(getRect().ptInRect(CBofPoint(nSrcX, nSrcY)));
assert(getRect().ptInRect(CBofPoint(nDstX, nDstY)));
if (_errCode == ERR_NONE) {
// Horizontal lines are a special case that can be done optimally
if (nSrcY == nDstY) {
lock();
memset(getPixelAddress(MIN(nSrcX, nDstX), nSrcY), iColor, ABS(nDstX - nSrcX));
unlock();
} else {
// Otherwise use standard Bresenham Line algorithm
int dx = nDstX - nSrcX;
int dy = nDstY - nSrcY;
int ix = dx < 0 ? (dx = -dx, -1) : !!dx;
int iy = dy < 0 ? (dy = -dy, -1) : !!dy;
int distance = MAX(dx, dy);
int xerr = 0;
int yerr = 0;
for (int i = -2; i < distance; i++) {
writePixel(nSrcX, nSrcY, iColor);
xerr += dx;
yerr += dy;
if (xerr > distance) {
xerr -= distance;
nSrcX += ix;
}
if (yerr > distance) {
yerr -= distance;
nSrcY += iy;
}
}
}
}
}
void CBofBitmap::line(CBofPoint *pSrc, CBofPoint *pDest, byte iColor) {
assert(isValidObject(this));
assert(pSrc != nullptr);
assert(pDest != nullptr);
line(pSrc->x, pSrc->y, pDest->x, pDest->y, iColor);
}
ErrorCode CBofBitmap::scrollRight(int nPixels, CBofRect * /*pRect*/) {
assert(isValidObject(this));
if (_errCode == ERR_NONE) {
if (nPixels != 0) {
assert(_pBits != nullptr);
byte *pTemp = (byte *)bofAlloc(abs(nPixels));
int nBytes = _nDX - nPixels;
if (nPixels < 0) {
nBytes = _nDX + nPixels;
}
byte *p = _pBits;
lock();
if (nPixels > 0) {
for (int i = 0; i < _nDY; i++) {
memcpy(pTemp, p + nBytes, nPixels);
memmove(p + nPixels, p, nBytes);
memcpy(p, pTemp, nPixels);
p += _nScanDX;
}
} else {
nPixels = -nPixels;
for (int i = 0; i < _nDY; i++) {
memcpy(pTemp, p, nPixels);
memmove(p, p + nPixels, nBytes);
memcpy(p + nBytes, pTemp, nPixels);
p += _nScanDX;
}
}
unlock();
bofFree(pTemp);
}
}
return _errCode;
}
ErrorCode CBofBitmap::scrollUp(int nPixels) {
assert(isValidObject(this));
if (_errCode == ERR_NONE) {
lock();
CBofRect cRect(0, 0, _nDX - 1, _nDY - 1);
int32 x = cRect.left;
int32 y = cRect.top;
int32 dx = cRect.width();
int32 dy = cRect.height();
// Height must be valid or we're hosed
assert(dy > 0);
// We don't have to scroll more than the height of the bitmap, because
// scrolling (bitmap height + 1) pixels is the same as scrolling 1 pixel,
// and scrolling 1 pixel is obviously more efficient then scrolling
// (bitmap height + 1) pixels.
//
// Also, we can handle down scrolling as scrolling up:
// For example, if the bitmap-height is 10 pixels, and we want to
// scroll down 6 pixels, that has the same effect as scrolling up
// 4 pixels (10 - 6 = 4). So, when we get negative nPixels, we will just
// scroll (height + nPixels) in the opposite direction.
if (nPixels >= 0) {
nPixels = nPixels % dy;
} else {
nPixels = -(-nPixels % dy);
if (nPixels < 0)
nPixels = dy + nPixels;
assert(nPixels >= 0 && nPixels < dy);
}
// Only scroll if we need to
if (nPixels != 0) {
// Allocate a buffer to hold one horizontal line
byte *pRowBuf = (byte *)bofAlloc(dx);
byte *pStart = _pBits;
byte *pEnd = _pBits;
int32 dx1 = _nScanDX;
int32 dy1 = _nDY;
// Is bitmap top-down or bottom up?
if (_bTopDown) {
pStart += y * dx1 + x;
pEnd += (y + dy - 1) * dx1 + x;
} else {
pStart += (dy1 - y - 1) * dx1 + x;
pEnd += (dy1 - (y + dy - 1) - 1) * dx1 + x;
dx1 = -dx1;
}
byte *pCurRow = pStart;
// Copy 1st row into temp row buffer
memcpy(pRowBuf, pCurRow, dx);
int32 lJump = dx1 * nPixels;
byte *pLastRow = pCurRow;
pCurRow += lJump;
byte *p1stRow = pStart;
// Working row by row
for (int32 i = 1; i < dy; i++) {
// Copy this row to row above it
memcpy(pLastRow, pCurRow, dx);
pLastRow = pCurRow;
pCurRow += lJump;
if (pCurRow < pEnd && !_bTopDown) {
pCurRow = pStart - (pEnd - pCurRow) - dx1;
if (pCurRow == p1stRow) {
i++;
// Copy 1st row into this row
memcpy(pLastRow, pRowBuf, dx);
pCurRow += dx1;
p1stRow = pLastRow = pCurRow;
// Copy this next row into temp row buffer
memcpy(pRowBuf, p1stRow, dx);
pCurRow += lJump;
}
}
}
// Copy 1st row into last row
memcpy(pLastRow, pRowBuf, dx);
bofFree(pRowBuf);
}
unlock();
}
return _errCode;
}
#define BIT0 0x00000001
#define BIT2 0x00000004
#define BIT15 0x00008000
#define BIT16 0x00010000
#define BIT17 0x00020000
#define SEQ (BIT17)
ErrorCode CBofBitmap::fadeIn(CBofWindow *pWnd, int xStart, int yStart, int nMaskColor, int nBlockSize, int /*nSpeed*/) {
assert(isValidObject(this));
assert(pWnd != nullptr);
assert(xStart >= 0);
assert(yStart >= 0);
if (_errCode == ERR_NONE) {
CBofRect cSrcRect, cDstRect;
uint32 mask = (BIT16 | BIT15 | BIT2 | BIT0);
uint32 width = _nDX / nBlockSize;
uint32 height = _nDY;
uint32 maxvalue = height / nBlockSize * width;
uint32 seed = SEQ - 1;
uint32 value = seed;
for (;;) {
uint32 tmp = (value & mask);
uint32 shft = BIT0;
uint32 cnt = 0;
while (shft < SEQ) {
if (tmp & shft) {
cnt++;
}
shft <<= 1;
}
value <<= 1;
if (cnt & BIT0) {
value |= BIT0;
}
value &= (SEQ - 1);
if (value == seed)
break;
if (value > maxvalue)
continue;
uint32 y = (value / width) * nBlockSize;
uint32 x = (value % width) * nBlockSize;
cSrcRect.setRect((int)x, (int)y, (int)x + nBlockSize - 1, (int)y + nBlockSize - 1);
x += xStart;
y += yStart;
cDstRect.setRect((int)x, (int)y, (int)x + nBlockSize - 1, (int)y + nBlockSize - 1);
paint(pWnd, &cDstRect, &cSrcRect, nMaskColor);
}
cSrcRect.setRect(0, 0, nBlockSize - 1, nBlockSize - 1);
paint(pWnd, &cSrcRect, &cSrcRect, nMaskColor);
}
return _errCode;
}
ErrorCode CBofBitmap::curtain(CBofWindow *pWnd, int nSpeed, int nMaskColor) {
assert(isValidObject(this));
assert(pWnd != nullptr);
if (_errCode == ERR_NONE) {
CBofRect cRect;
int nWidth = pWnd->width();
int nHeight = pWnd->height();
for (int i = 0; i < nHeight; i += nSpeed) {
cRect.setRect(0, i, nWidth - 1, i + nSpeed - 1);
paint(pWnd, &cRect, &cRect, nMaskColor);
bofSleep(1);
}
}
return _errCode;
}
ErrorCode CBofBitmap::fadeLines(CBofWindow *pWnd, int nSpeed, int nMaskColor) {
assert(isValidObject(this));
assert(pWnd != nullptr);
assert(nSpeed != 0);
if (_errCode == ERR_NONE) {
CBofRect cDstRect, cSrcRect, cWindowRect, cBmpRect;
// Entire window?
//
cWindowRect = pWnd->getRect();
CBofRect *pDstRect = &cWindowRect;
cBmpRect = getRect();
CBofRect *pSrcRect = &cBmpRect;
int x1 = pDstRect->left;
int y1 = pDstRect->top;
int nWidth1 = pDstRect->width();
int nHeight1 = pDstRect->height();
int x2 = pSrcRect->left;
int y2 = pSrcRect->top;
int nWidth2 = pSrcRect->width();
for (int j = 0; j < 4; j++) {
for (int i = 0; i < nHeight1 ; i += 4) {
cDstRect.setRect(x1, y1 + i + j, x1 + nWidth1 - 1, y1 + i + j);
cSrcRect.setRect(x2, y2 + i + j, x2 + nWidth2 - 1, y2 + i + j);
paint(pWnd, &cDstRect, &cSrcRect, nMaskColor);
if (i % nSpeed == 0)
bofSleep(1);
}
}
}
return _errCode;
}
const char *CBofBitmap::getFileName() {
assert(isValidObject(this));
const char *p = nullptr;
if (_szFileName[0] != '\0')
p = (const char *)&_szFileName[0];
return p;
}
//////////////////////////////////////////////////////////////////////////////
//
// Misc global graphics routines
//
//////////////////////////////////////////////////////////////////////////////
CBofBitmap *loadBitmap(const char *pszFileName, CBofPalette *pPalette, bool bUseShared) {
CBofPalette *pUsePal = pPalette;
// If no palette was passed in and a shared palette was requested, then
// use our default one established by "SHAREDPAL=" in the script
if (bUseShared && pPalette == nullptr) {
pUsePal = CBofPalette::getSharedPalette();
}
CBofBitmap *pBmp = new CBofBitmap(pszFileName, pUsePal);
return pBmp;
}
ErrorCode paintBitmap(CBofWindow *pWindow, const char *pszFileName, CBofRect *pDstRect, CBofRect *pSrcRect, CBofPalette *pPalette, int nMaskColor) {
assert(pWindow != nullptr);
assert(pszFileName != nullptr);
CBofBitmap *pBmp = new CBofBitmap(pszFileName, pPalette);
CBofRect cRect = pBmp->getRect();
if (pSrcRect == nullptr)
pSrcRect = &cRect;
if (pDstRect == nullptr)
pDstRect = &cRect;
ErrorCode errorCode = pBmp->paint(pWindow, pDstRect, pSrcRect, nMaskColor);
delete pBmp;
return errorCode;
}
Graphics::ManagedSurface CBofBitmap::getSurface() {
Graphics::ManagedSurface s;
s.w = _nDX;
s.h = _nDY;
s.pitch = _nScanDX;
s.format = Graphics::PixelFormat::createFormatCLUT8();
s.setPalette(_pPalette->getData(), 0, Graphics::PALETTE_COUNT);
s.setPixels(_pBits);
return s;
}
} // namespace SpaceBar
} // namespace Bagel