Files
scummvm-cursorfix/engines/asylum/system/screen.cpp
2026-02-02 04:50:13 +01:00

1245 lines
40 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/scummsys.h"
#include "graphics/paletteman.h"
#include "asylum/system/screen.h"
#include "asylum/resources/actor.h"
#include "asylum/resources/script.h"
#include "asylum/resources/worldstats.h"
#include "asylum/system/graphics.h"
#include "asylum/views/scene.h"
#include "asylum/asylum.h"
#include "asylum/respack.h"
namespace Asylum {
int g_debugDrawRects;
#define TRANSPARENCY_TABLE_SIZE (256 * 256)
Screen::Screen(AsylumEngine *vm) : _vm(vm) ,
_useColorKey(false), _transTableCount(0), _transTable(nullptr), _transTableBuffer(nullptr) {
_backBuffer.create(640, 480, Graphics::PixelFormat::createFormatCLUT8());
_flag = -1;
_clipRect = Common::Rect(0, 0, 640, 480);
memset(&_currentPalette, 0, sizeof(_currentPalette));
memset(&_mainPalette, 0, sizeof(_mainPalette));
memset(&_fromPalette, 0, sizeof(_fromPalette));
memset(&_toPalette, 0, sizeof(_toPalette));
_isFading = false;
_fadeStop = false;
g_debugDrawRects = 0;
}
Screen::~Screen() {
_backBuffer.free();
clearTransTables();
}
//////////////////////////////////////////////////////////////////////////
// Drawing
//////////////////////////////////////////////////////////////////////////
void Screen::draw(ResourceId resourceId) {
draw(resourceId, 0, Common::Point(0, 0), kDrawFlagNone, kResourceNone, Common::Point(0, 0), false);
}
void Screen::draw(ResourceId resourceId, uint32 frameIndex, const Common::Point &source, DrawFlags flags, bool colorKey) {
draw(resourceId, frameIndex, source, flags, kResourceNone, Common::Point(0, 0), colorKey);
}
void Screen::draw(ResourceId resourceId, uint32 frameIndex, const int16 (*srcPtr)[2], DrawFlags flags, bool colorKey) {
draw(resourceId, frameIndex, Common::Point((*srcPtr)[0], (*srcPtr)[1]), flags, kResourceNone, Common::Point(0, 0), colorKey);
}
void Screen::drawTransparent(ResourceId resourceId, uint32 frameIndex, const Common::Point &source, DrawFlags flags, uint32 transTableNum) {
byte *index = _transTable;
selectTransTable(transTableNum);
draw(resourceId, frameIndex, source, (DrawFlags)(flags | 0x90000000));
_transTable = index;
}
void Screen::draw(ResourceId resourceId, uint32 frameIndex, const Common::Point &source, DrawFlags flags, ResourceId resourceIdDestination, const Common::Point &destination, bool colorKey) {
// Get the frame to draw
GraphicResource *resource = new GraphicResource(_vm, resourceId);
draw(resource, frameIndex, source, flags, resourceIdDestination, destination, colorKey);
delete resource;
}
void Screen::draw(GraphicResource *resource, uint32 frameIndex, const Common::Point &source, DrawFlags flags, bool colorKey) {
draw(resource, frameIndex, source, flags, kResourceNone, Common::Point(0, 0), colorKey);
}
void Screen::drawTransparent(GraphicResource *resource, uint32 frameIndex, const Common::Point &source, DrawFlags flags, uint32 transTableNum) {
byte *index = _transTable;
selectTransTable(transTableNum);
draw(resource, frameIndex, source, (DrawFlags)(flags | 0x90000000));
_transTable = index;
}
void Screen::draw(GraphicResource *resource, uint32 frameIndex, const Common::Point &source, DrawFlags flags, ResourceId resourceIdDestination, const Common::Point &destination, bool colorKey) {
GraphicFrame *frame = resource->getFrame(frameIndex);
ResourceEntry *resourceMask = nullptr;
// Compute coordinates
Common::Rect src;
Common::Rect dest;
Common::Rect srcMask;
Common::Rect destMask;
dest.left = source.x + frame->x;
if (flags & kDrawFlagMirrorLeftRight) {
if (_flag == -1) {
if ((resource->getData().flags & 15) >= 2) {
dest.left = source.x + (int16)resource->getData().maxWidth - ((int16)frame->getWidth() + frame->x);
}
} else {
dest.left += (int16)(2 * (_flag - (frame->getHeight() * 2 - frame->x)));
}
}
dest.top = source.y + frame->y;
dest.right = dest.left + (int16)frame->getWidth();
dest.bottom = dest.top + (int16)frame->getHeight();
src.left = 0;
src.top = 0;
src.right = frame->getWidth();
src.bottom = frame->getHeight();
clip(&src, &dest, flags);
bool masked = false;
if (resourceIdDestination) {
masked = true;
// Get the resource to use as a mask
resourceMask = getResource()->get(resourceIdDestination);
// Adjust masked rectangles
srcMask = Common::Rect(0, 0, (int16)resourceMask->getData(4), (int16)resourceMask->getData(0));
destMask = Common::Rect(destination.x,
destination.y,
destination.x + (int16)resourceMask->getData(4),
destination.y + (int16)resourceMask->getData(0));
clip(&srcMask, &destMask, 0);
if (!dest.intersects(destMask))
masked = false;
if (g_debugDrawRects)
_backBuffer.frameRect(destMask, 0x125);
}
// Check src rectangle
if (!src.isValidRect())
return;
// Set the color key (always 0 if set)
_useColorKey = colorKey;
if (masked) {
if (!resourceMask)
error("[Screen::draw] Trying to draw masked with an invalid resource mask");
blitMasked(frame, &src, resourceMask->data + 8, &srcMask, &destMask, (uint16)resourceMask->getData(4), &dest, flags);
} else {
blit(frame, &src, &dest, flags);
}
}
void Screen::draw(const Graphics::Surface &surface, int x, int y) {
_backBuffer.copyRectToSurface(surface, x, y, Common::Rect(0, 0, surface.w, surface.h));
}
//////////////////////////////////////////////////////////////////////////
// Misc
//////////////////////////////////////////////////////////////////////////
void Screen::clear() {
_backBuffer.fillRect(Common::Rect(0, 0, 640, 480), 0);
copyBackBufferToScreen();
}
void Screen::drawWideScreenBars(int16 barSize) const {
if (barSize > 0) {
_vm->_system->fillScreen(Common::Rect(0, 0, 640, barSize), 0);
_vm->_system->fillScreen(Common::Rect(0, 480 - barSize, 640, 480), 0);
}
}
void Screen::fillRect(int16 x, int16 y, int16 width, int16 height, uint32 color) {
_backBuffer.fillRect(Common::Rect(x, y, x + width, y + height), color);
}
void Screen::copyBackBufferToScreen() {
_vm->_system->copyRectToScreen((byte *)_backBuffer.getPixels(), _backBuffer.w, 0, 0, _backBuffer.w, _backBuffer.h);
}
void Screen::clip(Common::Rect *source, Common::Rect *destination, int32 flags) const {
int16 diffLeft = _clipRect.left - destination->left;
if (diffLeft > 0) {
destination->left = _clipRect.left;
if (flags & 2)
source->right -= diffLeft;
else
source->left += diffLeft;
}
int16 diffRight = destination->right - _clipRect.right;
if (diffRight > 0) {
destination->right -= diffRight;
if (flags & 2)
source->left += diffRight;
else
source->right -= diffRight;
}
int16 diffTop = _clipRect.top - destination->top;
if (diffTop > 0) {
destination->top = _clipRect.top;
source->top += diffTop;
}
int16 diffBottom = destination->bottom - _clipRect.bottom;
if (diffBottom > 0) {
source->bottom -= diffBottom;
destination->bottom -= diffBottom;
}
}
//////////////////////////////////////////////////////////////////////////
// Palette
//////////////////////////////////////////////////////////////////////////
byte *Screen::getPaletteData(ResourceId id) {
ResourceEntry *resource = getResource()->get(id);
// Check that resource is a valid palette
byte flag = *(resource->data + 5);
if (!(flag & 32))
error("[Screen::getPaletteData] Invalid palette resource id %d (0x%X) with flag %d", id, id, flag);
return (resource->data + resource->getData(12));
}
void Screen::loadGrayPalette() {
// Get the current action palette
ResourceId paletteId = getWorld()->actions[getScene()->getActor()->getActionIndex3()]->paletteResourceId;
if (!paletteId)
paletteId = getWorld()->currentPaletteId;
// Get the data
byte *paletteData = getPaletteData(paletteId);
paletteData += 4;
// Store grayscale data into our global palette
for (uint32 j = 3; j < ARRAYSIZE(_currentPalette) - 3; j += 3) {
uint32 gray = 4 * (paletteData[j] + paletteData[j + 1] + paletteData[j + 2]) / 3;
_currentPalette[j] = _currentPalette[j + 1] = _currentPalette[j + 2] = (byte)gray;
}
}
void Screen::setPalette(ResourceId id) {
byte *data = getPaletteData(id);
setupPalette(data + 4, data[2], READ_LE_UINT16(data));
}
void Screen::setMainPalette(const byte *data) {
memcpy(&_mainPalette, data, sizeof(_mainPalette));
}
void Screen::setupPalette(byte *buffer, int start, int count) {
// Check parameters
if (start < 0 || start > 256)
error("[Screen::setupPalette] Invalid start parameter (was: %d, valid: [0 ; 255])", start);
if ((count + start) > 256)
error("[Screen::setupPalette] Parameters go past the palette buffer (start: %d, count: %d with sum > 256)", start, count);
// TODO: Update transparent palette if needed
// Setup our main palette
if (count > 0) {
byte *palette = (byte *)_mainPalette;
palette += start;
for (int32 i = 0; i < count; i++) {
palette[0] = (byte)(buffer[0] * 4);
palette[1] = (byte)(buffer[1] * 4);
palette[2] = (byte)(buffer[2] * 4);
buffer += 3;
palette += 3;
}
}
// Change the system palette
_vm->_system->getPaletteManager()->setPalette(_mainPalette, 0, 256);
}
void Screen::updatePalette() {
// FIXME: This is used to replace all the inline code to setup the palette before calls to setupPalette/paletteFade
// See if all that code can really be factorized into a single function or not
debugC(kDebugLevelScene, "[Screen::updatePalette] Not implemented!");
}
void Screen::updatePalette(int32 param) {
if (param >= 21) {
for (uint32 j = 3; j < ARRAYSIZE(_mainPalette) - 3; j += 3) {
_mainPalette[j] = _currentPalette[j];
_mainPalette[j + 1] = _currentPalette[j + 1];
_mainPalette[j + 2] = _currentPalette[j + 2];
}
setupPalette(nullptr, 0, 0);
paletteFade(0, 25, 10);
} else {
// Get the current action palette
ResourceId paletteId = getWorld()->actions[getScene()->getActor()->getActionIndex3()]->paletteResourceId;
if (!paletteId)
paletteId = getWorld()->currentPaletteId;
// Get the data
byte *paletteData = getPaletteData(paletteId);
paletteData += 4;
float fParam = param / 20.0;
for (uint32 j = 3; j < ARRAYSIZE(_mainPalette) - 3; j += 3) {
_mainPalette[j] = (byte)((1.0 - fParam) * 4 * paletteData[j] + fParam * _currentPalette[j]);
_mainPalette[j + 1] = (byte)((1.0 - fParam) * 4 * paletteData[j + 1] + fParam * _currentPalette[j + 1]);
_mainPalette[j + 2] = (byte)((1.0 - fParam) * 4 * paletteData[j + 2] + fParam * _currentPalette[j + 2]);
}
setupPalette(nullptr, 0, 0);
}
}
//////////////////////////////////////////////////////////////////////////
// Palette fading
//////////////////////////////////////////////////////////////////////////
void Screen::queuePaletteFade(ResourceId resourceId, int32 ticksWait, int32 delta) {
if (_isFading && !_fadeQueue.empty() && _fadeQueue.front().resourceId == resourceId)
return;
if (ticksWait < 0 || delta <= 0)
return;
FadeParameters fadeParams = {resourceId, ticksWait, delta, _vm->getTick(), 1};
_fadeQueue.push(fadeParams);
}
void Screen::stopPaletteFade(char red, char green, char blue) {
// Setup main palette
for (uint i = 3; i < ARRAYSIZE(_mainPalette) - 3; i += 3) {
_mainPalette[i] = (byte)red;
_mainPalette[i + 1] = (byte)green;
_mainPalette[i + 2] = (byte)blue;
}
stopQueuedPaletteFade();
setupPalette(nullptr, 0, 0);
}
void Screen::stopPaletteFadeAndSet(ResourceId id, int32 ticksWait, int32 delta) {
stopQueuedPaletteFade();
initQueuedPaletteFade(id, delta);
for (int i = 1; i < delta + 1; i++) {
runQueuedPaletteFade(id, delta, i);
g_system->delayMillis((uint32)ticksWait);
g_system->updateScreen();
}
}
void Screen::paletteFade(uint32 start, int32 ticksWait, int32 delta) {
if (start > 255 || ticksWait < 0 || delta <= 0)
return;
byte palette[PALETTE_SIZE];
memcpy(&palette, &_mainPalette, sizeof(palette));
// Prepare for palette fading loop
int32 colorDelta = delta + 1;
byte red = palette[3 * start];
byte green = palette[3 * start + 1];
byte blue = palette[3 * start + 2];
for (int32 i = 1; i < colorDelta; i++) {
for (uint32 j = 3; j < ARRAYSIZE(_mainPalette) - 3; j += 3) {
_mainPalette[j] = (byte)(palette[j] + i * (red - palette[j]) / colorDelta);
_mainPalette[j + 1] = (byte)(palette[j + 1] + i * (green - palette[j + 1]) / colorDelta);
_mainPalette[j + 2] = (byte)(palette[j + 2] + i * (blue - palette[j + 2]) / colorDelta);
}
setupPalette(nullptr, 0, 0);
g_system->delayMillis((uint32)ticksWait);
// Poll events (this ensure we don't freeze the screen)
Common::Event ev;
do {
} while (_vm->getEventManager()->pollEvent(ev));
// Refresh the screen
g_system->updateScreen();
}
}
void Screen::processPaletteFadeQueue() {
if (_fadeQueue.empty())
return;
FadeParameters *current = &_fadeQueue.front();
if (_vm->getTick() > current->nextTick) {
if (current->step > current->delta) {
_isFading = false;
(void)_fadeQueue.pop();
if (_fadeQueue.empty()) {
stopQueuedPaletteFade();
return;
}
current = &_fadeQueue.front();
initQueuedPaletteFade(current->resourceId, current->delta);
} else {
if (current->step == 1)
initQueuedPaletteFade(current->resourceId, current->delta);
current->nextTick += current->ticksWait;
}
runQueuedPaletteFade(current->resourceId, current->delta, current->step++);
}
}
void Screen::initQueuedPaletteFade(ResourceId id, int32 delta) {
// Reset flag
_fadeStop = false;
// Start fading
_isFading = true;
byte *data = getPaletteData(id);
// Setup our palette
memcpy(_fromPalette, _mainPalette, sizeof(_fromPalette));
memcpy(_toPalette, _mainPalette, sizeof(_toPalette));
// Adjust palette using the target palette data
int16 count = READ_LE_UINT16(data);
byte start = data[2];
if (count > 0) {
byte *pData = data + 4;
for (int16 i = 0; i < count; i++) {
_toPalette[i + start] = (byte)(4 * pData[0]);
_toPalette[i + start + 1] = (byte)(4 * pData[1]);
_toPalette[i + start + 2] = (byte)(4 * pData[2]);
pData += 3;
}
}
// Adjust gamma
setPaletteGamma(data, _toPalette);
}
void Screen::runQueuedPaletteFade(ResourceId id, int32 delta, int i) {
if (_fadeStop)
return;
int32 colorDelta = delta + 1;
for (uint32 j = 3; j < ARRAYSIZE(_mainPalette) - 3; j += 3) {
_mainPalette[j] = (byte)(_fromPalette[j] + i * (_toPalette[j] - _fromPalette[j]) / colorDelta);
_mainPalette[j + 1] = (byte)(_fromPalette[j + 1] + i * (_toPalette[j + 1] - _fromPalette[j + 1]) / colorDelta);
_mainPalette[j + 2] = (byte)(_fromPalette[j + 2] + i * (_toPalette[j + 2] - _fromPalette[j + 2]) / colorDelta);
}
setupPalette(nullptr, 0, 0);
}
void Screen::stopQueuedPaletteFade() {
if (!_isFading)
return;
// Signal timer to exit its main loop
_fadeStop = true;
}
//////////////////////////////////////////////////////////////////////////
// Gamma
//////////////////////////////////////////////////////////////////////////
void Screen::setPaletteGamma(ResourceId id) {
setPaletteGamma(getPaletteData(id));
}
void Screen::setPaletteGamma(byte *data, byte *target) {
if (target == nullptr)
target = (byte *)&_mainPalette;
// Skip first entry
data += 4;
for (int32 i = 1; i < 256; i++) {
byte color = 0;
if (data[0] > 0)
color = data[0];
if (data[1] > color)
color = data[1];
if (data[2] > color)
color = data[2];
int gamma = color + (Config.gammaLevel * (63 - color) + 31) / 63;
if (gamma && color != 0) {
if (data[0])
target[0] = (byte)(4 * ((color >> 1) + data[0] * gamma) / color);
if (data[1])
target[1] = (byte)(4 * ((color >> 1) + data[1] * gamma) / color);
if (data[2])
target[2] = (byte)(4 * ((color >> 1) + data[2] * gamma) / color);
}
// Advance palette data
target += 3;
data += 3;
}
}
void Screen::setGammaLevel(ResourceId id) {
if (!Config.gammaLevel)
return;
if (!id)
error("[Screen::setGammaLevel] Resource Id is invalid");
setPaletteGamma(getPaletteData(id));
setupPalette(nullptr, 0, 0);
}
//////////////////////////////////////////////////////////////////////////
// Transparency tables
//////////////////////////////////////////////////////////////////////////
void Screen::setupTransTable(ResourceId resourceId) {
if (resourceId)
setupTransTables(1, resourceId);
else
setupTransTables(0);
}
void Screen::setupTransTables(uint32 count, ...) {
if (!count) {
clearTransTables();
return;
}
// Load tables
va_list va;
va_start(va, count);
if (_transTableCount != count)
clearTransTables();
_transTableCount = count;
if (!_transTableBuffer) {
_transTableBuffer = (byte *)malloc(count * TRANSPARENCY_TABLE_SIZE);
if (!_transTableBuffer)
error("[Screen::setupTransTables] Cannot allocate memory for transparency table buffer");
_transTable = _transTableBuffer;
}
uint32 index = 0;
for (uint32 i = 0; i < _transTableCount; i++) {
ResourceId id = va_arg(va, ResourceId);
memcpy(&_transTableBuffer[index], getResource()->get(id)->data, TRANSPARENCY_TABLE_SIZE);
index += TRANSPARENCY_TABLE_SIZE;
}
va_end(va);
}
void Screen::clearTransTables() {
free(_transTableBuffer);
_transTableBuffer = nullptr;
_transTable = nullptr;
_transTableCount = 0;
}
void Screen::selectTransTable(uint32 index) {
if (!_transTableBuffer)
error("[Screen::selectTransTable] Transparency table buffer not initialized");
if (index >= _transTableCount)
return;
_transTable = &_transTableBuffer[TRANSPARENCY_TABLE_SIZE * index];
}
//////////////////////////////////////////////////////////////////////////
// Graphic queue
//////////////////////////////////////////////////////////////////////////
void Screen::addGraphicToQueue(ResourceId resourceId, uint32 frameIndex, const Common::Point &point, DrawFlags flags, int32 transTableNum, int32 priority) {
GraphicQueueItem item;
item.priority = priority;
item.type = kGraphicItemNormal;
item.source = point;
item.resourceId = resourceId;
item.frameIndex = frameIndex;
item.flags = flags;
item.transTableNum = transTableNum;
_queueItems.push_back(item);
}
void Screen::addGraphicToQueue(ResourceId resourceId, uint32 frameIndex, const int16 (*pointPtr)[2], DrawFlags flags, int32 transTableNum, int32 priority) {
addGraphicToQueue(resourceId, frameIndex, Common::Point((*pointPtr)[0], (*pointPtr)[1]), flags, transTableNum, priority);
}
void Screen::addGraphicToQueueMasked(ResourceId resourceId, uint32 frameIndex, const Common::Point &source, int32 resourceIdDestination, const Common::Point &destination, DrawFlags flags, int32 priority) {
GraphicQueueItem item;
item.priority = priority;
item.type = kGraphicItemMasked;
item.source = source;
item.resourceId = resourceId;
item.frameIndex = frameIndex;
item.flags = flags;
item.resourceIdDestination = resourceIdDestination;
item.destination = destination;
_queueItems.push_back(item);
}
void Screen::addGraphicToQueueCrossfade(ResourceId resourceId, uint32 frameIndex, const Common::Point &point, int32 objectResourceId, const Common::Point &destination, uint32 transTableNum) {
// Save current transparency index
byte *transparencyIndex = _transTable;
selectTransTable(transTableNum);
// Get graphic frames
GraphicResource *resource = new GraphicResource(_vm, resourceId);
GraphicFrame *frame = resource->getFrame(frameIndex);
GraphicResource *resourceObject = new GraphicResource(_vm, objectResourceId);
GraphicFrame *frameObject = resourceObject->getFrame(0);
// Compute rectangles
Common::Rect src(0, 0, frame->getWidth(), frame->getHeight());
Common::Rect dst = src;
dst.translate(point.x + frame->x, point.y + frame->y);
clip(&src, &dst, 0);
if (src.isValidRect()) {
// Set the color key (always 0)
_useColorKey = true;
blitCrossfade((byte *)_backBuffer.getPixels() + dst.top * _backBuffer.pitch + dst.left,
(byte *)frame->surface.getPixels() + src.top * frame->surface.pitch + src.left,
(byte *)frameObject->surface.getPixels() + (destination.y + dst.top) * frameObject->surface.pitch + (dst.left + destination.x),
dst.height(),
dst.width(),
(uint16)(frame->surface.pitch - dst.width()),
(uint16)(_backBuffer.pitch - dst.width()),
(uint16)(frameObject->surface.pitch - dst.width()));
}
// Restore transparency table
_transTable = transparencyIndex;
delete resource;
delete resourceObject;
}
void Screen::addGraphicToQueue(GraphicQueueItem const &item) {
_queueItems.push_back(item);
}
bool Screen::graphicQueueItemComparator(const GraphicQueueItem &item1, const GraphicQueueItem &item2) {
return item1.priority > item2.priority;
}
void Screen::drawGraphicsInQueue() {
// Sort by priority first
Common::sort(_queueItems.begin(), _queueItems.end(), &Screen::graphicQueueItemComparator);
for (const auto &item : _queueItems) {
if (item.type == kGraphicItemNormal) {
if (item.transTableNum <= 0 || Config.performance <= 1)
draw(item.resourceId, item.frameIndex, item.source, item.flags);
else
drawTransparent(item.resourceId, item.frameIndex, item.source, item.flags, (uint32)(item.transTableNum - 1));
} else if (item.type == kGraphicItemMasked) {
draw(item.resourceId, item.frameIndex, item.source, item.flags, item.resourceIdDestination, item.destination);
}
}
}
void Screen::clearGraphicsInQueue() {
_queueItems.clear();
}
void Screen::deleteGraphicFromQueue(ResourceId resourceId) {
for (uint32 i = 0; i < _queueItems.size(); i++) {
if (_queueItems[i].resourceId == resourceId) {
_queueItems.remove_at(i);
break;
}
}
}
//////////////////////////////////////////////////////////////////////////
// Graphic Data
//////////////////////////////////////////////////////////////////////////
void Screen::blit(GraphicFrame *frame, Common::Rect *source, Common::Rect *destination, int32 flags) {
if (!_transTable)
error("[Screen::blit] Transparency table buffer not initialized");
if ((uint32)flags & 0x80000000) {
// Used in the menu (and more?)
int32 flagSet = flags & 0x7FFFFFFF;
bool hasTransTableIndex = false;
if (flags & 0x10000000) {
flagSet = flags & 0x6FFFFFFF;
hasTransTableIndex = (_transTable ? true : false);
}
bool isMirrored = (flagSet == kDrawFlagMirrorLeftRight);
if (hasTransTableIndex) {
if (isMirrored) {
blitTranstableMirrored((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destination->left,
(byte *)frame->surface.getPixels() + source->top * frame->surface.pitch + source->right - 1,
destination->height(),
destination->width(),
(uint16)destination->width() + frame->surface.pitch,
_backBuffer.pitch - (uint16)destination->width());
} else {
blitTranstable((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destination->left,
(byte *)frame->surface.getPixels() + source->top * frame->surface.pitch + source->left,
destination->height(),
destination->width(),
frame->surface.pitch - (uint16)destination->width(),
_backBuffer.pitch - (uint16)destination->width());
}
} else if (flagSet) {
if (isMirrored) {
if (_useColorKey) {
blitMirroredColorKey((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destination->left,
(byte *)frame->surface.getPixels() + source->top * frame->surface.pitch + source->right,
destination->height(),
destination->width(),
frame->surface.pitch + (uint16)destination->width(),
_backBuffer.pitch - (uint16)destination->width());
} else {
blitMirrored((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destination->left,
(byte *)frame->surface.getPixels() + source->top * frame->surface.pitch + source->right,
destination->height(),
destination->width(),
frame->surface.pitch + (uint16)destination->width(),
_backBuffer.pitch - (uint16)destination->width());
}
}
} else {
if (_useColorKey) {
blitRawColorKey((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destination->left,
(byte *)frame->surface.getPixels() + source->top * frame->surface.pitch + source->left,
destination->height(),
destination->width(),
frame->surface.pitch - (uint16)destination->width(),
_backBuffer.pitch - (uint16)destination->width());
} else {
blitRaw((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destination->left,
(byte *)frame->surface.getPixels() + source->top * frame->surface.pitch + source->left,
destination->height(),
destination->width(),
frame->surface.pitch - (uint16)destination->width(),
_backBuffer.pitch - (uint16)destination->width());
}
}
} else if (flags) {
blt(destination, frame, source, flags);
} else {
bltFast(destination->left, destination->top, frame, source);
}
if (g_debugDrawRects)
_backBuffer.frameRect(*destination, 0x220);
}
void Screen::blitTranstable(byte *dstBuffer, byte *srcBuffer, int16 height, int16 width, uint16 srcPitch, uint16 dstPitch) const {
if (!_transTable)
error("[Screen::blitTranstable] Transparency table buffer not initialized");
while (height--) {
for (int16 i = width; i; --i) {
if (*srcBuffer)
*dstBuffer = _transTable[(*srcBuffer << 8) + *dstBuffer];
dstBuffer++;
srcBuffer++;
}
dstBuffer += dstPitch;
srcBuffer += srcPitch;
}
}
void Screen::blitTranstableMirrored(byte *dstBuffer, byte *srcBuffer, int16 height, int16 width, uint16 srcPitch, uint16 dstPitch) const {
if (!_transTable)
error("[Screen::blitTranstableMirrored] Transparency table buffer not initialized");
while (height--) {
for (int16 i = width; i; --i) {
if (*srcBuffer)
*dstBuffer = _transTable[(*srcBuffer << 8) + *dstBuffer];
dstBuffer++;
srcBuffer--;
}
dstBuffer += dstPitch;
srcBuffer += srcPitch;
}
}
void Screen::blitCrossfade(byte *dstBuffer, byte *srcBuffer, byte *objectBuffer, int16 height, int16 width, uint16 srcPitch, uint16 dstPitch, uint16 objectPitch) const {
if (!_transTable)
error("[Screen::blitCrossfade] Transparency table buffer not initialized");
while (height--) {
for (int16 i = width; i; --i) {
if (*srcBuffer)
*dstBuffer = _transTable[(*srcBuffer << 8) + *objectBuffer];
dstBuffer++;
srcBuffer++;
objectBuffer++;
}
dstBuffer += dstPitch;
srcBuffer += srcPitch;
objectBuffer += objectPitch;
}
}
void Screen::blitMirrored(byte *dstBuffer, byte *srcBuffer, int16 height, int16 width, uint16 srcPitch, uint16 dstPitch) const {
while (height--) {
for (int16 i = width; i; --i) {
*dstBuffer = *srcBuffer;
dstBuffer++;
srcBuffer--;
}
dstBuffer += dstPitch;
srcBuffer += srcPitch;
}
}
void Screen::blitMirroredColorKey(byte *dstBuffer, byte *srcBuffer, int16 height, int16 width, uint16 srcPitch, uint16 dstPitch) const {
while (height--) {
for (int16 i = width; i; --i) {
if (*srcBuffer != 0)
*dstBuffer = *srcBuffer;
dstBuffer++;
srcBuffer--;
}
dstBuffer += dstPitch;
srcBuffer += srcPitch;
}
}
void Screen::blitRaw(byte *dstBuffer, byte *srcBuffer, int16 height, int16 width, uint16 srcPitch, uint16 dstPitch) const {
while (height--) {
memcpy(dstBuffer, srcBuffer, (uint16)width);
dstBuffer += dstPitch;
srcBuffer += srcPitch;
}
}
void Screen::blitRawColorKey(byte *dstBuffer, byte *srcBuffer, int16 height, int16 width, uint16 srcPitch, uint16 dstPitch) const {
while (height--) {
for (int16 i = width; i; --i) {
if (*srcBuffer != 0)
*dstBuffer = *srcBuffer;
dstBuffer++;
srcBuffer++;
}
dstBuffer += dstPitch;
srcBuffer += srcPitch;
}
}
void Screen::blitMasked(GraphicFrame *frame, Common::Rect *source, byte *maskData, Common::Rect *sourceMask, Common::Rect *destMask, uint16 maskWidth, Common::Rect *destination, int32 flags) {
byte *frameBuffer = (byte *)frame->surface.getPixels();
byte *mirroredBuffer = nullptr;
int16 frameRight = frame->surface.pitch;
uint16 maskHeight = (uint16)sourceMask->height(); // for debugging only
byte nSkippedBits = ABS(sourceMask->left) % 8;
// Prepare temporary source buffer if needed
if (flags & kDrawFlagMirrorLeftRight) {
mirroredBuffer = (byte *)malloc((size_t)(source->right * source->bottom));
if (!mirroredBuffer)
error("[Screen::blitMasked] Cannot allocate buffer for mirrored surface");
blitMirrored(mirroredBuffer,
frameBuffer + source->right - 1,
source->bottom,
source->right,
(uint16)(source->right + frame->surface.pitch),
0);
frameBuffer = mirroredBuffer;
frameRight = source->right;
source->right -= source->left;
source->left = 0;
}
// Setup buffers and rectangles
byte *frameBufferPtr = frameBuffer + source->top * frameRight + source->left;
byte *maskBufferPtr = maskData + sourceMask->top * (maskWidth / 8) + sourceMask->left / 8;
// Check if we need to draw masked
if ((destMask->left + sourceMask->width()) < destination->left
|| (destination->left + source->width()) < destMask->left
|| (destMask->top + sourceMask->height()) < destination->top
|| (destination->top + source->height()) < destMask->top) {
blitRawColorKey((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destination->left,
frameBufferPtr,
source->height(),
source->width(),
(uint16)(frameRight - source->width()),
(uint16)(_backBuffer.pitch - source->width()));
// cleanup
free(mirroredBuffer);
// Draw debug rects
if (g_debugDrawRects)
_backBuffer.frameRect(*destMask, 0x220);
return;
}
if (destination->left > destMask->left) {
nSkippedBits += ABS(destination->left - destMask->left) % 8;
maskBufferPtr += (destination->left - destMask->left) / 8 + nSkippedBits / 8;
nSkippedBits %= 8;
sourceMask->setWidth(sourceMask->width() + destMask->left - destination->left);
destMask->left = destination->left;
}
if (destination->top > destMask->top) {
maskBufferPtr += (destination->top - destMask->top) * maskWidth / 8;
sourceMask->setHeight(sourceMask->height() + destMask->top - destination->top);
destMask->top = destination->top;
}
//////////////////////////////////////////////////////////////////////////
// Left part
if (destination->left < destMask->left) {
blitRawColorKey((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destination->left,
frameBufferPtr,
source->height(),
destMask->left - destination->left,
(uint16)(frameRight + destination->left - destMask->left),
(uint16)(_backBuffer.pitch + destination->left - destMask->left));
if (g_debugDrawRects)
_backBuffer.frameRect(Common::Rect(destination->left, destination->top, destMask->left, destination->top + source->height()), 0x10);
frameBufferPtr += destMask->left - destination->left;
source->setWidth(source->width() + destination->left - destMask->left);
destination->left = destMask->left;
}
//////////////////////////////////////////////////////////////////////////
// Right part
if ((source->width() + destination->left) > (destMask->left + sourceMask->width())) {
blitRawColorKey((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destMask->left + sourceMask->width(),
frameBufferPtr + destMask->left + sourceMask->width() - destination->left,
source->height(),
source->width() + destination->left - (destMask->left + sourceMask->width()),
(uint16)(frameRight + destMask->left + sourceMask->width() - (destination->left + source->width())),
(uint16)(_backBuffer.pitch + destMask->left + sourceMask->width() - (destination->left + source->width())));
if (g_debugDrawRects)
_backBuffer.frameRect(Common::Rect(destMask->left, destination->top, destMask->left + source->width(), destination->top + source->height()), 0x36);
source->setWidth(destMask->left + sourceMask->width() - destination->left);
}
//////////////////////////////////////////////////////////////////////////
// Top part
if (destination->top < destMask->top) {
blitRawColorKey((byte *)_backBuffer.getPixels() + destination->top * _backBuffer.pitch + destination->left,
frameBufferPtr,
destMask->top - destination->top,
source->width(),
(uint16)(frameRight - source->width()),
(uint16)(_backBuffer.pitch - source->width()));
if (g_debugDrawRects)
_backBuffer.frameRect(Common::Rect(destination->left, destination->top, destination->left + source->width(), destMask->top), 0x23);
frameBufferPtr += (destMask->top - destination->top) * frameRight;
source->setHeight(source->height() + destination->top - destMask->top);
destination->top = destMask->top;
}
//////////////////////////////////////////////////////////////////////////
// Bottom part
if ((source->height() + destination->top) > (destMask->top + sourceMask->height())) {
blitRawColorKey((byte *)_backBuffer.getPixels() + (destMask->top + sourceMask->height()) * _backBuffer.pitch + destination->left,
frameBufferPtr + (destMask->top + sourceMask->height() - destination->top) * frameRight,
destination->top + source->height() - (sourceMask->height() + destMask->top),
source->width(),
(uint16)(frameRight - source->width()),
(uint16)(_backBuffer.pitch - source->width()));
source->setHeight(destMask->top + sourceMask->height() - destination->top);
}
//////////////////////////////////////////////////////////////////////////
// Masked part
bltMasked(frameBufferPtr,
maskBufferPtr,
source->height(),
source->width(),
(uint16)(frameRight - source->width()),
(uint16)(maskWidth - (nSkippedBits + source->width())) / 8,
nSkippedBits,
(byte *)_backBuffer.getPixels() + _backBuffer.pitch * destination->top + destination->left,
(uint16)(_backBuffer.pitch - source->width()));
// Draw debug rects
if (g_debugDrawRects) {
_backBuffer.frameRect(*destination, 0x128);
drawZoomedMask(maskData, maskHeight / 8, maskWidth / 8, maskWidth);
}
// Cleanup
free(mirroredBuffer);
}
// DEBUG: Draw the mask (zoomed)
void Screen::drawZoomedMask(byte *mask, uint16 height, uint16 width, uint16 maskPitch) {
uint16 zoom = 7;
byte *dstBuffer = (byte *)_backBuffer.getPixels();
uint16 dstPitch = (uint16)(_backBuffer.pitch - (width * zoom));
uint16 srcPitch = maskPitch;
byte *srcBuffer = mask;
height *= zoom;
while (height--) {
for (int16 i = 0; i < width; i++) {
for (int j = 0; j < zoom; j++) {
*dstBuffer = *srcBuffer;
dstBuffer++;
}
srcBuffer++;
}
dstBuffer += dstPitch;
srcBuffer += (height % zoom) ? -width : srcPitch;
}
}
void Screen::bltMasked(byte *srcBuffer, byte *maskBuffer, int16 height, int16 width, uint16 srcPitch, uint16 maskPitch, byte nSkippedBits, byte *dstBuffer, uint16 dstPitch) const {
if (nSkippedBits > 7)
error("[Screen::bltMasked] Invalid number of skipped bits (was: %d, max: 7)", nSkippedBits);
while (height--) {
// Calculate current run length
int run = 7 - nSkippedBits;
uint skip = *maskBuffer >> nSkippedBits;
for (int16 i = 0; i < width; i++) {
// Set destination value
if (*srcBuffer && !(skip & 1))
*dstBuffer = *srcBuffer;
// Advance buffers
dstBuffer++;
srcBuffer++;
if (i == width - 1)
break;
// Check run/skip
run--;
if (run < 0) {
++maskBuffer;
run = 7;
skip = *maskBuffer;
} else {
skip >>= 1;
}
}
dstBuffer += dstPitch;
srcBuffer += srcPitch;
maskBuffer += maskPitch + 1;
}
}
void Screen::blt(Common::Rect *dest, GraphicFrame *frame, Common::Rect *source, int32 flags) {
if (_useColorKey) {
copyToBackBufferWithTransparency((byte *)frame->surface.getBasePtr(source->left, source->top),
frame->surface.pitch,
dest->left,
dest->top,
(uint16)source->width(),
(uint16)source->height(),
(bool)(flags & kDrawFlagMirrorLeftRight));
} else {
copyToBackBuffer((byte *)frame->surface.getBasePtr(source->left, source->top),
frame->surface.pitch,
dest->left,
dest->top,
(uint16)source->width(),
(uint16)source->height(),
(bool)(flags & kDrawFlagMirrorLeftRight));
}
}
void Screen::bltFast(int16 dX, int16 dY, GraphicFrame *frame, Common::Rect *source) {
if (!frame->surface.getPixels() || source->width() == 0 || source->height() == 0)
return;
if (_useColorKey) {
_backBuffer.copyRectToSurfaceWithKey(frame->surface, dX, dY, *source, 0x00);
} else {
_backBuffer.copyRectToSurface(frame->surface, dX, dY, *source);
}
}
void Screen::copyToBackBuffer(const byte *buffer, int32 pitch, int16 x, int16 y, uint16 width, uint16 height, bool mirrored) {
if (!buffer || width == 0 || height == 0)
return;
if (!mirrored) {
_backBuffer.copyRectToSurface(buffer, pitch, x, y, width, height);
} else {
error("[Screen::copyToBackBuffer] Mirrored drawing not implemented (no color key)");
}
}
void Screen::copyToBackBufferWithTransparency(byte *buffer, int32 pitch, int16 x, int16 y, uint16 width, uint16 height, bool mirrored) {
byte *dest = (byte *)_backBuffer.getPixels();
int32 left = (x < 0) ? -x : 0;
int32 top = (y < 0) ? -y : 0;
int32 right = (x + width > 640) ? 640 - abs(x) : width;
int32 bottom = (y + height > 480) ? 480 - abs(y) : height;
for (int32 curY = top; curY < bottom; curY++) {
for (int32 curX = left; curX < right; curX++) {
uint32 offset = (uint32)((mirrored ? right - (curX + 1) : curX) + curY * pitch);
if (buffer[offset] != 0)
dest[x + curX + (y + curY) * 640] = buffer[offset];
}
}
}
//////////////////////////////////////////////////////////////////////////
// Debug
//////////////////////////////////////////////////////////////////////////
void Screen::drawLine(const Common::Point &source, const Common::Point &destination, uint32 color) {
_backBuffer.drawLine(source.x, source.y, destination.x, destination.y, color);
}
void Screen::drawLine(const int16 (*srcPtr)[2], const int16 (*dstPtr)[2], uint32 color) {
_backBuffer.drawLine((*srcPtr)[0], (*srcPtr)[1], (*dstPtr)[0], (*dstPtr)[1], color);
}
void Screen::drawRect(const Common::Rect &rect, uint32 color) {
_backBuffer.frameRect(rect, color);
}
void Screen::copyToBackBufferClipped(Graphics::Surface *surface, int16 x, int16 y) {
Common::Rect screenRect(getWorld()->xLeft, getWorld()->yTop, getWorld()->xLeft + 640, getWorld()->yTop + 480);
Common::Rect animRect(x, y, x + (int16)surface->w, y + (int16)surface->h);
animRect.clip(screenRect);
if (!animRect.isEmpty()) {
// Translate animation rectangle
animRect.translate(-(int16)getWorld()->xLeft, -(int16)getWorld()->yTop);
int startX = animRect.right == 640 ? 0 : surface->w - animRect.width();
int startY = animRect.bottom == 480 ? 0 : surface->h - animRect.height();
if (surface->w > 640)
startX = getWorld()->xLeft;
if (surface->h > 480)
startY = getWorld()->yTop;
_vm->screen()->copyToBackBufferWithTransparency(
((byte *)surface->getPixels()) +
startY * surface->pitch +
startX * surface->format.bytesPerPixel,
surface->pitch,
animRect.left,
animRect.top,
(uint16)animRect.width(),
(uint16)animRect.height());
}
}
} // end of namespace Asylum