/* 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 . * */ #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