Files
scummvm-cursorfix/engines/bagel/spacebar/baglib/pan_bitmap.cpp
2026-02-02 04:50:13 +01:00

436 lines
12 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 "bagel/spacebar/baglib/pan_bitmap.h"
#include "bagel/spacebar/baglib/bagel.h"
#include "bagel/spacebar/baglib/paint_table.h"
namespace Bagel {
namespace SpaceBar {
const CBofRect CBagPanBitmap::getMaxView(CBofSize s) {
CBofRect r;
if (_bPanorama) {
int h = 3 * (int)((double)width() / (MAX_DIV_VIEW * 4));
if (h > height())
h = height();
r.setRect(0, 0, (int)(width() / MAX_DIV_VIEW - 2), h - 1);
} else {
r.setRect(0, 0, width() - 1, height() - 1);
}
if (s.cx > 0 && r.right > s.cx) {
// r.left = s.cx;
r.right = s.cx - 1;
}
if (s.cy > 0 && r.bottom > s.cy) {
r.bottom = s.cy - 1;
}
return r;
}
CBagPanBitmap::CBagPanBitmap(const char *pszFileName, CBofPalette *pPalette, const CBofRect &xViewSize) :
CBofBitmap(pszFileName, pPalette, true) {
int nW = width();
int nH = height();
_bPanorama = false;
_pCosineTable = nullptr;
_bActiveScrolling = false; // The scrolling is not active
_xDirection = kDirNONE; // Direction is not moving
_xFOVAngle = 0;
_nCorrWidth = 0;
_nNumDegrees = 0;
if (nW && nH) {
CBofRect xMaxViewSize(0, 0, nW - 1, nH - 1);
if (nW > 1000) {
xMaxViewSize.left = (long)(nW / MAX_DIV_VIEW);
_bPanorama = true;
}
pPalette = getPalette();
if (xViewSize.isRectEmpty())
_xCurrView = xMaxViewSize;
else
_xCurrView = xViewSize;
if (_xCurrView.width() > xMaxViewSize.width()) {
_xCurrView.setRect(0, _xCurrView.top, xMaxViewSize.width() - 1, _xCurrView.bottom);
}
_xRotateRate.x = (nW - _xCurrView.width()) / 64 + 1;
_xRotateRate.y = (nH - _xCurrView.height()) / 64 + 1;
normalizeViewSize();
// If FOV is set to 0 then unity FOV is assumed (faster redraws)
// The update parameter is set to false to avoid to update the cosine table as _nCorrWidth isn't set yet
setFOV(DEF_FOV, false);
// Initialize _nCorrWidth and generate the cosine table.
if (_bPanorama)
setCorrWidth(4, true);
else
setCorrWidth(0, true);
_bIsValid = true;
return;
}
_bIsValid = false;
}
CBagPanBitmap::~CBagPanBitmap() {
assert(isValidObject(this));
delete[] _pCosineTable;
_pCosineTable = nullptr;
}
// This must be updated whenever the size, view size, or correction width changes
void CBagPanBitmap::generateCosineTable() {
int nWidth = 1 << _nCorrWidth;
int offset = nWidth >> 1; // This is not really needed just more correction to move angle to center
int viewWidth = _xCurrView.width();
_nNumDegrees = (viewWidth >> _nCorrWidth) + 1;
delete[] _pCosineTable;
_pCosineTable = new CBofFixed[_nNumDegrees];
for (int i = 0; i < _nNumDegrees; i++) {
double inArea = (double)(offset + i * nWidth) / viewWidth;
_pCosineTable[i] = (CBofFixed)(cos(_xFOVAngle * (-1.0 + 2.0 * inArea)));
}
}
CBofRect CBagPanBitmap::getWarpSrcRect() {
int nH2 = height() >> 1;
return CBofRect(_xCurrView.left,
nH2 + (int)(*_pCosineTable * CBofFixed(_xCurrView.top - nH2)),
_xCurrView.right,
nH2 + (int)(*_pCosineTable * CBofFixed(_xCurrView.bottom - nH2)));
}
CBofPoint CBagPanBitmap::warpedPoint(CBofPoint &xPoint) {
CBofRect r = getRect();
int nH2 = height() >> 1;
int nW = width();
int nWidth = 1 << _nCorrWidth; // It may no longer be necessary to store corr width as a shift arg
int nCenter = r.top + nH2;
int nOffset = (xPoint.x - _xCurrView.left); // nWidth;
if (nOffset < 0)
nOffset += nW;
nOffset /= nWidth;
if ((nOffset < 0) || ((xPoint.x - _xCurrView.left) > _xCurrView.width()) || (nOffset >= _nNumDegrees))
return CBofPoint(0, 0);
CBofFixed srcHeight = _pCosineTable[nOffset];
return CBofPoint(xPoint.x, nCenter + (int)(srcHeight * CBofFixed(xPoint.y - nH2)));
}
ErrorCode CBagPanBitmap::paintWarped(CBofBitmap *pBmp, const CBofRect &dstRect, const CBofRect &srcRect, const int offset, CBofBitmap *pSrcBmp, const CBofRect &preSrcRect) {
int nH2 = _nDY >> 1;
int nWidth = 1 << _nCorrWidth; // It may no longer be necessary to store corr width as a shift arg
CBofFixed *pSrcHeight = &_pCosineTable[offset >> _nCorrWidth];
CBofFixed srcTop = preSrcRect.top + srcRect.top - nH2;
CBofFixed srcBottom = preSrcRect.top + srcRect.bottom - nH2;
int nCenter = nH2;
int nTop = nCenter - preSrcRect.top;
int nRight = (srcRect.left + nWidth) - 1;
CBofRect PanSrcRect;
CBofRect WndDstRect(dstRect.left + 0, dstRect.top, (dstRect.left + 0 + nWidth) - 1, dstRect.bottom);
pBmp->lock();
pSrcBmp->lock();
int nIncrement = 1;
if (nWidth < 4) {
for (int i = 0; i < dstRect.width(); i += nWidth) {
// Set the source
//
PanSrcRect.setRect(srcRect.left + i,
nTop + (int)(*pSrcHeight * srcTop),
nRight + i,
nTop + (int)(*pSrcHeight * srcBottom));
pSrcBmp->paint(pBmp, &WndDstRect, &PanSrcRect);
WndDstRect.left = WndDstRect.right + 1;
WndDstRect.right = WndDstRect.right + nWidth;
pSrcHeight += nIncrement;
}
} else if (nWidth == 4) {
int tableSlot = srcRect.top + preSrcRect.top;
int stripNumber = 0;
for (int i = 0; i < dstRect.width(); i += nWidth, stripNumber++) {
// Set the source
PanSrcRect.setRect(srcRect.left + i,
STRIP_POINTS[tableSlot][stripNumber]._top,
nRight + i,
STRIP_POINTS[tableSlot][stripNumber]._bottom);
pSrcBmp->paintStretch4(pBmp, &WndDstRect, &PanSrcRect);
WndDstRect.left = WndDstRect.right + 1;
WndDstRect.right = WndDstRect.right + nWidth;
}
} else { // nWidth > 4
for (int i = 0; i < dstRect.width(); i += nWidth) {
// Set the source
PanSrcRect.setRect(srcRect.left + i,
nTop + (int)(*pSrcHeight * srcTop),
nRight + i,
nTop + (int)(*pSrcHeight * srcBottom));
pSrcBmp->paintStretchOpt(pBmp, &WndDstRect, &PanSrcRect, nWidth);
WndDstRect.left = WndDstRect.right + 1;
WndDstRect.right = WndDstRect.right + nWidth;
pSrcHeight += nIncrement;
}
}
pSrcBmp->unlock();
pBmp->unlock();
return _errCode;
}
ErrorCode CBagPanBitmap::paintUncorrected(CBofBitmap *pBmp, CBofRect &dstRect) {
int tmp = _nCorrWidth;
_nCorrWidth = 0;
CBofFixed fONE(1);
CBofFixed fH2(height() / 2);
CBofFixed fCos(_pCosineTable[0]);
int nOffset = (int)((fONE - fCos) * fH2);
dstRect = getCurrView();
CBofRect tmpRect = dstRect;
dstRect.top -= nOffset;
dstRect.bottom += nOffset;
if (dstRect.top < 0) {
dstRect.bottom = dstRect.height() - 1;
dstRect.top = 0;
}
if (dstRect.height() >= pBmp->height()) {
dstRect.bottom = dstRect.top + pBmp->height() - 2;
}
_xCurrView = dstRect;
paint(pBmp);
_xCurrView = tmpRect;
_nCorrWidth = tmp;
return _errCode;
}
ErrorCode CBagPanBitmap::paint(CBofBitmap *pBmp, const CBofPoint xDstOffset) {
CBofRect dstRect;
CBofRect srcRect = _xCurrView;
int nW = width();
int viewWidth = _xCurrView.width();
srcRect.right = _xCurrView.left + viewWidth - 1;
int nOffset = srcRect.right - nW;
dstRect.top = xDstOffset.y;
dstRect.bottom = dstRect.top + srcRect.height() - 1;
// If the right side of the view is the beginning of the panorama
// paint the un-wrapped side (right) first.
dstRect.left = xDstOffset.x;
dstRect.right = xDstOffset.x + viewWidth - 1;
if (nOffset > 0) {
CBofRect srcRect2 = srcRect;
srcRect2.left = 0;
srcRect2.right = nOffset;
dstRect.right = xDstOffset.x + viewWidth - 1;
dstRect.left = dstRect.right - nOffset;
CBofBitmap::paint(pBmp, &dstRect, &srcRect2);
srcRect.right = nW - 1;
dstRect.right = dstRect.left;
dstRect.left = xDstOffset.x;
}
CBofBitmap::paint(pBmp, &dstRect, &srcRect);
return _errCode;
}
CBagPanBitmap::Direction CBagPanBitmap::updateView() {
if (_bActiveScrolling) {
if (_xDirection & kDirLEFT)
rotateRight();
else if (_xDirection & kDirRIGHT)
rotateLeft();
if (_xDirection & kDirUP)
rotateUp();
else if (_xDirection & kDirDOWN)
rotateDown();
return _xDirection;
}
return kDirNONE;
}
void CBagPanBitmap::setCorrWidth(int nWidth, bool bUpdate) {
int i = 0;
while (nWidth >>= 1)
++i;
if (i < 6) {
_nCorrWidth = i;
if (bUpdate) {
generateCosineTable();
}
}
}
void CBagPanBitmap::rotateRight(int nXRotRate) {
if (nXRotRate > 0)
offsetCurrView(CBofPoint(nXRotRate, 0));
else
offsetCurrView(CBofPoint(_xRotateRate.x, 0));
}
void CBagPanBitmap::rotateLeft(int nXRotRate) {
if (nXRotRate > 0)
offsetCurrView(CBofPoint(-1 * nXRotRate, 0));
else
offsetCurrView(CBofPoint(-1 * _xRotateRate.x, 0));
}
void CBagPanBitmap::rotateUp(int nYRotRate) {
if (nYRotRate > 0)
offsetCurrView(CBofPoint(0, nYRotRate));
else
offsetCurrView(CBofPoint(0, _xRotateRate.y));
}
void CBagPanBitmap::rotateDown(int nYRotRate) {
if (nYRotRate > 0)
offsetCurrView(CBofPoint(0, -1 * nYRotRate));
else
offsetCurrView(CBofPoint(0, -1 * _xRotateRate.y));
}
void CBagPanBitmap::normalizeViewSize() {
int nW = width();
int nH = height();
if (_bPanorama) {
// The CurrView can not be more than 0.25Width x Height of the Bitmap
if ((_xCurrView.width() >= nW / MAX_DIV_VIEW) || (_xCurrView.width() <= 0))
_xCurrView.right = (long)(_xCurrView.left + nW / MAX_DIV_VIEW - 1);
if ((_xCurrView.height() >= nH) || (_xCurrView.height() <= 0))
_xCurrView.bottom = _xCurrView.top + nH - 1;
// The Base coords of CurrView must exist within the rectangle
while (_xCurrView.left < 0)
_xCurrView.offsetRect(nW, 0);
while (_xCurrView.left >= nW)
_xCurrView.offsetRect(-1 * nW, 0);
} else { // Not a panorama
// The Base coords of CurrView must exist within the rectangle
if (_xCurrView.left < 0)
_xCurrView.offsetRect(-_xCurrView.left, 0);
else if (_xCurrView.right > nW)
_xCurrView.offsetRect(nW - _xCurrView.right, 0);
}
// We never have up and down wrap around
if (_xCurrView.top < 0)
_xCurrView.offsetRect(0, -_xCurrView.top);
if (_xCurrView.bottom >= nH)
_xCurrView.offsetRect(0, (nH - _xCurrView.bottom) - 1);
}
void CBagPanBitmap::setCurrView(const CBofRect &xCurrView) {
_xCurrView = xCurrView;
normalizeViewSize();
}
void CBagPanBitmap::offsetCurrView(const CBofPoint &xOffset) {
CBofRect xCurrView = _xCurrView;
xCurrView.offsetRect(xOffset);
setCurrView(xCurrView);
}
void CBagPanBitmap::setFOV(double degrees, bool bUpdate) {
_xFOVAngle = degrees / 114.5916558176;
if (bUpdate) {
generateCosineTable();
}
}
void CBagPanBitmap::setViewSize(const CBofSize &xViewSize, bool bUpdate) {
_xCurrView.right = _xCurrView.left + xViewSize.cx;
_xCurrView.bottom = _xCurrView.top + xViewSize.cy;
normalizeViewSize();
if (bUpdate) {
generateCosineTable();
}
}
CBofSize CBagPanBitmap::setUnityViewSize() {
int w = (int)(width() * _xFOVAngle / 3.14159);
_xCurrView.setRect(0, _xCurrView.top, w, _xCurrView.bottom);
generateCosineTable();
return getViewSize();
}
double CBagPanBitmap::setUnityFOV() {
setFOV(360.0 * _xCurrView.width() / width(), false); // If FOV is set to 0 then unity FOV is assumed (faster redraws)
generateCosineTable();
return getFOV();
}
} // namespace SpaceBar
} // namespace Bagel