mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-17 15:18:11 +00:00
3391c726cf
The Spanish version of Myst has bitmaps that use palette indices in the system reserved range. Affected pixels previously used colors from the Windows system palette instead of the bitmap's own palette, resulting in visual glitches. Bitmaps are now remapped to the screen palette which is made of the Windows reserved palette and part of the bitmap palette. The original engine used GDI's StretchDIBits with DIB_RGB_COLORS to achieve the same result. Fixes #7153.
803 lines
24 KiB
C++
803 lines
24 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "mohawk/myst.h"
|
|
#include "mohawk/myst_graphics.h"
|
|
#include "mohawk/resource.h"
|
|
|
|
#include "common/substream.h"
|
|
#include "common/system.h"
|
|
#include "common/textconsole.h"
|
|
#include "engines/util.h"
|
|
#include "graphics/palette.h"
|
|
#include "image/pict.h"
|
|
|
|
namespace Mohawk {
|
|
|
|
MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
|
|
_bmpDecoder = new MystBitmap();
|
|
|
|
_viewport = Common::Rect(544, 332);
|
|
|
|
if (_vm->getFeatures() & GF_ME) {
|
|
// High color
|
|
initGraphics(_viewport.width(), _viewport.height(), true, nullptr);
|
|
|
|
if (_vm->_system->getScreenFormat().bytesPerPixel == 1)
|
|
error("Myst ME requires greater than 256 colors to run");
|
|
} else {
|
|
// Paletted
|
|
initGraphics(_viewport.width(), _viewport.height(), true);
|
|
clearScreenPalette();
|
|
}
|
|
|
|
_pixelFormat = _vm->_system->getScreenFormat();
|
|
|
|
// Initialize our buffer
|
|
_backBuffer = new Graphics::Surface();
|
|
_backBuffer->create(_vm->_system->getWidth(), _vm->_system->getHeight(), _pixelFormat);
|
|
|
|
_nextAllowedDrawTime = _vm->_system->getMillis();
|
|
_enableDrawingTimeSimulation = 0;
|
|
}
|
|
|
|
MystGraphics::~MystGraphics() {
|
|
delete _bmpDecoder;
|
|
|
|
_backBuffer->free();
|
|
delete _backBuffer;
|
|
}
|
|
|
|
MohawkSurface *MystGraphics::decodeImage(uint16 id) {
|
|
// We need to grab the image from the current stack archive, however, we don't know
|
|
// if it's a PICT or WDIB resource. If it's Myst ME it's most likely a PICT, and if it's
|
|
// original it's definitely a WDIB. However, Myst ME throws us another curve ball in
|
|
// that PICT resources can contain WDIB's instead of PICT's.
|
|
Common::SeekableReadStream *dataStream = nullptr;
|
|
|
|
if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) {
|
|
// The PICT resource exists. However, it could still contain a MystBitmap
|
|
// instead of a PICT image...
|
|
dataStream = _vm->getResource(ID_PICT, id);
|
|
} else {
|
|
// No PICT, so the WDIB must exist. Let's go grab it.
|
|
dataStream = _vm->getResource(ID_WDIB, id);
|
|
}
|
|
|
|
bool isPict = false;
|
|
|
|
if ((_vm->getFeatures() & GF_ME) && dataStream->size() > 512 + 10 + 4) {
|
|
// Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap
|
|
// would be compressed, there's no way to detect for the BM without a hack.
|
|
// So, we search for the PICT version opcode for detection.
|
|
dataStream->seek(512 + 10); // 512 byte pict header
|
|
isPict = (dataStream->readUint32BE() == 0x001102FF);
|
|
dataStream->seek(0);
|
|
}
|
|
|
|
MohawkSurface *mhkSurface = nullptr;
|
|
|
|
if (isPict) {
|
|
Image::PICTDecoder pict;
|
|
|
|
if (!pict.loadStream(*dataStream))
|
|
error("Could not decode Myst ME PICT");
|
|
|
|
delete dataStream;
|
|
|
|
mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat));
|
|
} else {
|
|
mhkSurface = _bmpDecoder->decodeImage(dataStream);
|
|
|
|
if (_vm->getFeatures() & GF_ME) {
|
|
mhkSurface->convertToTrueColor();
|
|
} else {
|
|
remapSurfaceToSystemPalette(mhkSurface);
|
|
}
|
|
}
|
|
|
|
assert(mhkSurface);
|
|
return mhkSurface;
|
|
}
|
|
|
|
void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest) {
|
|
Graphics::Surface *surface = findImage(image)->getSurface();
|
|
|
|
// Make sure the image is bottom aligned in the dest rect
|
|
dest.top = dest.bottom - MIN<int>(surface->h, dest.height());
|
|
|
|
// Convert from bitmap coordinates to surface coordinates
|
|
uint16 top = surface->h - (src.top + MIN<int>(surface->h, dest.height()));
|
|
|
|
// Do not draw the top pixels if the image is too tall
|
|
if (dest.height() > _viewport.height())
|
|
top += dest.height() - _viewport.height();
|
|
|
|
// Clip the destination rect to the screen
|
|
if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight())
|
|
dest.debugPrint(4, "Clipping destination rect to the screen");
|
|
dest.right = CLIP<int>(dest.right, 0, _vm->_system->getWidth());
|
|
dest.bottom = CLIP<int>(dest.bottom, 0, _vm->_system->getHeight());
|
|
|
|
uint16 width = MIN<int>(surface->w, dest.width());
|
|
uint16 height = MIN<int>(surface->h, dest.height());
|
|
|
|
// Clamp Width and Height to within src surface dimensions
|
|
if (src.left + width > surface->w)
|
|
width = surface->w - src.left;
|
|
if (src.top + height > surface->h)
|
|
height = surface->h - src.top;
|
|
|
|
debug(3, "MystGraphics::copyImageSectionToScreen()");
|
|
debug(3, "\tImage: %d", image);
|
|
debug(3, "\tsrc.left: %d", src.left);
|
|
debug(3, "\tsrc.top: %d", src.top);
|
|
debug(3, "\tdest.left: %d", dest.left);
|
|
debug(3, "\tdest.top: %d", dest.top);
|
|
debug(3, "\twidth: %d", width);
|
|
debug(3, "\theight: %d", height);
|
|
|
|
simulatePreviousDrawDelay(dest);
|
|
|
|
_vm->_system->copyRectToScreen(surface->getBasePtr(src.left, top), surface->pitch, dest.left, dest.top, width, height);
|
|
}
|
|
|
|
void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest) {
|
|
MohawkSurface *mhkSurface = findImage(image);
|
|
Graphics::Surface *surface = mhkSurface->getSurface();
|
|
|
|
// Make sure the image is bottom aligned in the dest rect
|
|
dest.top = dest.bottom - MIN<int>(surface->h, dest.height());
|
|
|
|
// Convert from bitmap coordinates to surface coordinates
|
|
uint16 top = surface->h - (src.top + MIN<int>(surface->h, dest.height()));
|
|
|
|
// Do not draw the top pixels if the image is too tall
|
|
if (dest.height() > _viewport.height()) {
|
|
top += dest.height() - _viewport.height();
|
|
}
|
|
|
|
// Clip the destination rect to the screen
|
|
if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight())
|
|
dest.debugPrint(4, "Clipping destination rect to the screen");
|
|
dest.right = CLIP<int>(dest.right, 0, _vm->_system->getWidth());
|
|
dest.bottom = CLIP<int>(dest.bottom, 0, _vm->_system->getHeight());
|
|
|
|
uint16 width = MIN<int>(surface->w, dest.width());
|
|
uint16 height = MIN<int>(surface->h, dest.height());
|
|
|
|
// Clamp Width and Height to within src surface dimensions
|
|
if (src.left + width > surface->w)
|
|
width = surface->w - src.left;
|
|
if (src.top + height > surface->h)
|
|
height = surface->h - src.top;
|
|
|
|
debug(3, "MystGraphics::copyImageSectionToBackBuffer()");
|
|
debug(3, "\tImage: %d", image);
|
|
debug(3, "\tsrc.left: %d", src.left);
|
|
debug(3, "\tsrc.top: %d", src.top);
|
|
debug(3, "\tdest.left: %d", dest.left);
|
|
debug(3, "\tdest.top: %d", dest.top);
|
|
debug(3, "\twidth: %d", width);
|
|
debug(3, "\theight: %d", height);
|
|
|
|
for (uint16 i = 0; i < height; i++)
|
|
memcpy(_backBuffer->getBasePtr(dest.left, i + dest.top), surface->getBasePtr(src.left, top + i), width * surface->format.bytesPerPixel);
|
|
|
|
if (!(_vm->getFeatures() & GF_ME)) {
|
|
// Make sure the palette is set
|
|
assert(mhkSurface->getPalette());
|
|
memcpy(_palette, mhkSurface->getPalette(), 256 * 3);
|
|
setPaletteToScreen();
|
|
}
|
|
}
|
|
|
|
void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) {
|
|
copyImageSectionToScreen(image, Common::Rect(544, 333), dest);
|
|
}
|
|
|
|
void MystGraphics::copyImageToBackBuffer(uint16 image, Common::Rect dest) {
|
|
copyImageSectionToBackBuffer(image, Common::Rect(544, 333), dest);
|
|
}
|
|
|
|
void MystGraphics::copyBackBufferToScreen(Common::Rect r) {
|
|
r.clip(_viewport);
|
|
|
|
simulatePreviousDrawDelay(r);
|
|
|
|
_vm->_system->copyRectToScreen(_backBuffer->getBasePtr(r.left, r.top), _backBuffer->pitch, r.left, r.top, r.width(), r.height());
|
|
}
|
|
|
|
void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16 steps, uint16 delay) {
|
|
|
|
// Transitions are barely visible without adding delays between the draw calls
|
|
enableDrawingTimeSimulation(true);
|
|
|
|
switch (type) {
|
|
case kTransitionLeftToRight: {
|
|
debugC(kDebugView, "Left to Right");
|
|
|
|
uint16 step = (rect.right - rect.left) / steps;
|
|
Common::Rect area = rect;
|
|
for (uint i = 0; i < steps; i++) {
|
|
area.left = rect.left + step * i;
|
|
area.right = area.left + step;
|
|
|
|
_vm->_system->delayMillis(delay);
|
|
|
|
copyBackBufferToScreen(area);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
if (area.right < rect.right) {
|
|
area.left = area.right;
|
|
area.right = rect.right;
|
|
|
|
copyBackBufferToScreen(area);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
break;
|
|
case kTransitionRightToLeft: {
|
|
debugC(kDebugView, "Right to Left");
|
|
|
|
uint16 step = (rect.right - rect.left) / steps;
|
|
Common::Rect area = rect;
|
|
for (uint i = 0; i < steps; i++) {
|
|
area.right = rect.right - step * i;
|
|
area.left = area.right - step;
|
|
|
|
_vm->_system->delayMillis(delay);
|
|
|
|
copyBackBufferToScreen(area);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
if (area.left > rect.left) {
|
|
area.right = area.left;
|
|
area.left = rect.left;
|
|
|
|
copyBackBufferToScreen(area);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
break;
|
|
case kTransitionSlideToLeft:
|
|
debugC(kDebugView, "Slide to left");
|
|
transitionSlideToLeft(rect, steps, delay);
|
|
break;
|
|
case kTransitionSlideToRight:
|
|
debugC(kDebugView, "Slide to right");
|
|
transitionSlideToRight(rect, steps, delay);
|
|
break;
|
|
case kTransitionDissolve: {
|
|
debugC(kDebugView, "Dissolve");
|
|
|
|
for (int16 step = 0; step < 8; step++) {
|
|
// Only one eighth of the rect pixels are updated by a draw step,
|
|
// delay by one eighth of the regular time
|
|
simulatePreviousDrawDelay(Common::Rect(rect.width() / 8, rect.height()));
|
|
|
|
transitionDissolve(rect, step);
|
|
}
|
|
}
|
|
break;
|
|
case kTransitionTopToBottom: {
|
|
debugC(kDebugView, "Top to Bottom");
|
|
|
|
uint16 step = (rect.bottom - rect.top) / steps;
|
|
Common::Rect area = rect;
|
|
for (uint i = 0; i < steps; i++) {
|
|
area.top = rect.top + step * i;
|
|
area.bottom = area.top + step;
|
|
|
|
_vm->_system->delayMillis(delay);
|
|
|
|
copyBackBufferToScreen(area);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
if (area.bottom < rect.bottom) {
|
|
area.top = area.bottom;
|
|
area.bottom = rect.bottom;
|
|
|
|
copyBackBufferToScreen(area);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
break;
|
|
case kTransitionBottomToTop: {
|
|
debugC(kDebugView, "Bottom to Top");
|
|
|
|
uint16 step = (rect.bottom - rect.top) / steps;
|
|
Common::Rect area = rect;
|
|
for (uint i = 0; i < steps; i++) {
|
|
area.bottom = rect.bottom - step * i;
|
|
area.top = area.bottom - step;
|
|
|
|
_vm->_system->delayMillis(delay);
|
|
|
|
copyBackBufferToScreen(area);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
if (area.top > rect.top) {
|
|
area.bottom = area.top;
|
|
area.top = rect.top;
|
|
|
|
copyBackBufferToScreen(area);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
break;
|
|
case kTransitionSlideToTop:
|
|
debugC(kDebugView, "Slide to top");
|
|
transitionSlideToTop(rect, steps, delay);
|
|
break;
|
|
case kTransitionSlideToBottom:
|
|
debugC(kDebugView, "Slide to bottom");
|
|
transitionSlideToBottom(rect, steps, delay);
|
|
break;
|
|
case kTransitionPartToRight: {
|
|
debugC(kDebugView, "Partial left to right");
|
|
|
|
transitionPartialToRight(rect, 75, 3);
|
|
}
|
|
break;
|
|
case kTransitionPartToLeft: {
|
|
debugC(kDebugView, "Partial right to left");
|
|
|
|
transitionPartialToLeft(rect, 75, 3);
|
|
}
|
|
break;
|
|
case kTransitionCopy:
|
|
copyBackBufferToScreen(rect);
|
|
_vm->_system->updateScreen();
|
|
break;
|
|
default:
|
|
error("Unknown transition %d", type);
|
|
}
|
|
|
|
enableDrawingTimeSimulation(false);
|
|
}
|
|
|
|
void MystGraphics::transitionDissolve(Common::Rect rect, uint step) {
|
|
static const bool pattern[][4][4] = {
|
|
{
|
|
{ true, false, false, false },
|
|
{ false, false, false, false },
|
|
{ false, false, true, false },
|
|
{ false, false, false, false }
|
|
},
|
|
{
|
|
{ false, false, true, false },
|
|
{ false, false, false, false },
|
|
{ true, false, false, false },
|
|
{ false, false, false, false }
|
|
},
|
|
{
|
|
{ false, false, false, false },
|
|
{ false, true, false, false },
|
|
{ false, false, false, false },
|
|
{ false, false, false, true }
|
|
},
|
|
{
|
|
{ false, false, false, false },
|
|
{ false, false, false, true },
|
|
{ false, false, false, false },
|
|
{ false, true, false, false }
|
|
},
|
|
{
|
|
{ false, false, false, false },
|
|
{ false, false, true, false },
|
|
{ false, true, false, false },
|
|
{ false, false, false, false }
|
|
},
|
|
{
|
|
{ false, true, false, false },
|
|
{ false, false, false, false },
|
|
{ false, false, false, false },
|
|
{ false, false, true, false }
|
|
},
|
|
{
|
|
{ false, false, false, false },
|
|
{ true, false, false, false },
|
|
{ false, false, false, true },
|
|
{ false, false, false, false }
|
|
},
|
|
{
|
|
{ false, false, false, true },
|
|
{ false, false, false, false },
|
|
{ false, false, false, false },
|
|
{ true, false, false, false }
|
|
}
|
|
};
|
|
|
|
rect.clip(_viewport);
|
|
|
|
Graphics::Surface *screen = _vm->_system->lockScreen();
|
|
|
|
for (uint16 y = rect.top; y < rect.bottom; y++) {
|
|
const bool *linePattern = pattern[step][y % 4];
|
|
|
|
if (!linePattern[0] && !linePattern[1] && !linePattern[2] && !linePattern[3])
|
|
continue;
|
|
|
|
for (uint16 x = rect.left; x < rect.right; x++) {
|
|
if (linePattern[x % 4]) {
|
|
switch (_pixelFormat.bytesPerPixel) {
|
|
case 1:
|
|
*((byte *)screen->getBasePtr(x, y)) = *((const byte *)_backBuffer->getBasePtr(x, y));
|
|
break;
|
|
case 2:
|
|
*((uint16 *)screen->getBasePtr(x, y)) = *((const uint16 *)_backBuffer->getBasePtr(x, y));
|
|
break;
|
|
case 4:
|
|
*((uint32 *)screen->getBasePtr(x, y)) = *((const uint32 *)_backBuffer->getBasePtr(x, y));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_vm->_system->unlockScreen();
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
void MystGraphics::transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay) {
|
|
rect.clip(_viewport);
|
|
|
|
uint32 stepWidth = (rect.right - rect.left) / steps;
|
|
Common::Rect srcRect = Common::Rect(rect.right, rect.top, rect.right, rect.bottom);
|
|
Common::Rect dstRect = Common::Rect(rect.left, rect.top, rect.left, rect.bottom);
|
|
|
|
for (uint step = 1; step <= steps; step++) {
|
|
dstRect.right = dstRect.left + step * stepWidth;
|
|
srcRect.left = srcRect.right - step * stepWidth;
|
|
|
|
_vm->_system->delayMillis(delay);
|
|
|
|
simulatePreviousDrawDelay(dstRect);
|
|
_vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
|
|
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
if (dstRect.right != rect.right) {
|
|
copyBackBufferToScreen(rect);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
|
|
void MystGraphics::transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay) {
|
|
rect.clip(_viewport);
|
|
|
|
uint32 stepWidth = (rect.right - rect.left) / steps;
|
|
Common::Rect srcRect = Common::Rect(rect.left, rect.top, rect.left, rect.bottom);
|
|
Common::Rect dstRect = Common::Rect(rect.right, rect.top, rect.right, rect.bottom);
|
|
|
|
for (uint step = 1; step <= steps; step++) {
|
|
dstRect.left = dstRect.right - step * stepWidth;
|
|
srcRect.right = srcRect.left + step * stepWidth;
|
|
|
|
_vm->_system->delayMillis(delay);
|
|
|
|
simulatePreviousDrawDelay(dstRect);
|
|
_vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
|
|
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
if (dstRect.left != rect.left) {
|
|
copyBackBufferToScreen(rect);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
|
|
void MystGraphics::transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay) {
|
|
rect.clip(_viewport);
|
|
|
|
uint32 stepWidth = (rect.bottom - rect.top) / steps;
|
|
Common::Rect srcRect = Common::Rect(rect.left, rect.bottom, rect.right, rect.bottom);
|
|
Common::Rect dstRect = Common::Rect(rect.left, rect.top, rect.right, rect.top);
|
|
|
|
for (uint step = 1; step <= steps; step++) {
|
|
dstRect.bottom = dstRect.top + step * stepWidth;
|
|
srcRect.top = srcRect.bottom - step * stepWidth;
|
|
|
|
_vm->_system->delayMillis(delay);
|
|
|
|
simulatePreviousDrawDelay(dstRect);
|
|
_vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
|
|
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
|
|
if (dstRect.bottom < rect.bottom) {
|
|
copyBackBufferToScreen(rect);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
|
|
void MystGraphics::transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay) {
|
|
rect.clip(_viewport);
|
|
|
|
uint32 stepWidth = (rect.bottom - rect.top) / steps;
|
|
Common::Rect srcRect = Common::Rect(rect.left, rect.top, rect.right, rect.top);
|
|
Common::Rect dstRect = Common::Rect(rect.left, rect.bottom, rect.right, rect.bottom);
|
|
|
|
for (uint step = 1; step <= steps; step++) {
|
|
dstRect.top = dstRect.bottom - step * stepWidth;
|
|
srcRect.bottom = srcRect.top + step * stepWidth;
|
|
|
|
_vm->_system->delayMillis(delay);
|
|
|
|
simulatePreviousDrawDelay(dstRect);
|
|
_vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
|
|
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
|
|
if (dstRect.top > rect.top) {
|
|
copyBackBufferToScreen(rect);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
|
|
void MystGraphics::transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps) {
|
|
rect.clip(_viewport);
|
|
|
|
uint32 stepWidth = width / steps;
|
|
Common::Rect srcRect = Common::Rect(rect.right, rect.top, rect.right, rect.bottom);
|
|
Common::Rect dstRect = Common::Rect(rect.left, rect.top, rect.left, rect.bottom);
|
|
|
|
for (uint step = 1; step <= steps; step++) {
|
|
dstRect.right = dstRect.left + step * stepWidth;
|
|
srcRect.left = srcRect.right - step * stepWidth;
|
|
|
|
simulatePreviousDrawDelay(dstRect);
|
|
_vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
|
|
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
copyBackBufferToScreen(rect);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
void MystGraphics::transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps) {
|
|
rect.clip(_viewport);
|
|
|
|
uint32 stepWidth = width / steps;
|
|
Common::Rect srcRect = Common::Rect(rect.left, rect.top, rect.left, rect.bottom);
|
|
Common::Rect dstRect = Common::Rect(rect.right, rect.top, rect.right, rect.bottom);
|
|
|
|
for (uint step = 1; step <= steps; step++) {
|
|
dstRect.left = dstRect.right - step * stepWidth;
|
|
srcRect.right = srcRect.left + step * stepWidth;
|
|
|
|
simulatePreviousDrawDelay(dstRect);
|
|
_vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
|
|
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
copyBackBufferToScreen(rect);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
void MystGraphics::drawRect(Common::Rect rect, RectState state) {
|
|
rect.clip(_viewport);
|
|
|
|
// Useful with debugging. Shows where hotspots are on the screen and whether or not they're active.
|
|
if (!rect.isValidRect() || rect.width() == 0 || rect.height() == 0)
|
|
return;
|
|
|
|
Graphics::Surface *screen = _vm->_system->lockScreen();
|
|
|
|
if (state == kRectEnabled)
|
|
screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 255, 0) : 250);
|
|
else if (state == kRectUnreachable)
|
|
screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 0, 255) : 252);
|
|
else
|
|
screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(255, 0, 0) : 249);
|
|
|
|
_vm->_system->unlockScreen();
|
|
}
|
|
|
|
void MystGraphics::drawLine(const Common::Point &p1, const Common::Point &p2, uint32 color) {
|
|
_backBuffer->drawLine(p1.x, p1.y, p2.x, p2.y, color);
|
|
}
|
|
|
|
void MystGraphics::enableDrawingTimeSimulation(bool enable) {
|
|
if (enable)
|
|
_enableDrawingTimeSimulation++;
|
|
else
|
|
_enableDrawingTimeSimulation--;
|
|
|
|
if (_enableDrawingTimeSimulation < 0)
|
|
_enableDrawingTimeSimulation = 0;
|
|
}
|
|
|
|
void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) {
|
|
uint32 time = 0;
|
|
|
|
if (_enableDrawingTimeSimulation) {
|
|
time = _vm->_system->getMillis();
|
|
|
|
// Do not draw anything new too quickly after the previous draw call
|
|
// so that images stay at least a little while on screen
|
|
// This is enabled only for scripted draw calls
|
|
if (time < _nextAllowedDrawTime) {
|
|
debugC(kDebugView, "Delaying draw call by %d ms", _nextAllowedDrawTime - time);
|
|
_vm->_system->delayMillis(_nextAllowedDrawTime - time);
|
|
}
|
|
}
|
|
|
|
// Next draw call allowed at DELAY + AERA * COEFF milliseconds from now
|
|
time = _vm->_system->getMillis();
|
|
_nextAllowedDrawTime = time + _constantDrawDelay + dest.height() * dest.width() / _proportionalDrawDelay;
|
|
}
|
|
|
|
void MystGraphics::fadeToBlack() {
|
|
// This is only for the demo
|
|
assert(!(_vm->getFeatures() & GF_ME));
|
|
|
|
// Linear fade in 64 steps
|
|
for (int i = 63; i >= 0; i--) {
|
|
byte palette[256 * 3];
|
|
byte *src = _palette;
|
|
byte *dst = palette;
|
|
|
|
for (uint j = 0; j < sizeof(palette); j++)
|
|
*dst++ = *src++ * i / 64;
|
|
|
|
_vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
}
|
|
|
|
void MystGraphics::fadeFromBlack() {
|
|
// This is only for the demo
|
|
assert(!(_vm->getFeatures() & GF_ME));
|
|
|
|
copyBackBufferToScreen(_viewport);
|
|
|
|
// Linear fade in 64 steps
|
|
for (int i = 0; i < 64; i++) {
|
|
byte palette[256 * 3];
|
|
byte *src = _palette;
|
|
byte *dst = palette;
|
|
|
|
for (uint j = 0; j < sizeof(palette); j++)
|
|
*dst++ = *src++ * i / 64;
|
|
|
|
_vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
// Set the full palette
|
|
_vm->_system->getPaletteManager()->setPalette(_palette, 0, 256);
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
void MystGraphics::clearScreenPalette() {
|
|
// Set the palette to all black
|
|
byte palette[256 * 3];
|
|
memset(palette, 0, sizeof(palette));
|
|
_vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
|
|
}
|
|
|
|
void MystGraphics::remapSurfaceToSystemPalette(MohawkSurface *mhkSurface) {
|
|
// Entries [0, 9] of the palette
|
|
static const byte lowPalette[] = {
|
|
0x00, 0x00, 0x00,
|
|
0x80, 0x00, 0x00,
|
|
0x00, 0x80, 0x00,
|
|
0x80, 0x80, 0x00,
|
|
0x00, 0x00, 0x80,
|
|
0x80, 0x00, 0x80,
|
|
0x00, 0x80, 0x80,
|
|
0xC0, 0xC0, 0xC0,
|
|
0xC0, 0xDC, 0xC0,
|
|
0xA6, 0xCA, 0xF0
|
|
};
|
|
|
|
// Entries [246, 255] of the palette
|
|
static const byte highPalette[] = {
|
|
0xFF, 0xFB, 0xF0,
|
|
0xA0, 0xA0, 0xA4,
|
|
0x80, 0x80, 0x80,
|
|
0xFF, 0x00, 0x00,
|
|
0x00, 0xFF, 0x00,
|
|
0xFF, 0xFF, 0x00,
|
|
0x00, 0x00, 0xFF,
|
|
0xFF, 0x00, 0xFF,
|
|
0x00, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF
|
|
};
|
|
|
|
byte *originalPalette = mhkSurface->getPalette();
|
|
|
|
// The target palette is made of the Windows reserved palette, and colors 10 to 245
|
|
// of the bitmap palette. Entries 0 to 9 and 246 to 255 of the bitmap palette are
|
|
// discarded.
|
|
byte targetPalette[256 * 3];
|
|
memcpy(targetPalette, lowPalette, sizeof(lowPalette));
|
|
memcpy(targetPalette + sizeof(lowPalette), originalPalette + sizeof(lowPalette), sizeof(_palette) - sizeof(lowPalette) - sizeof(highPalette));
|
|
memcpy(targetPalette + sizeof(_palette) - sizeof(highPalette), highPalette, sizeof(highPalette));
|
|
|
|
// Remap the discarded entries from the bitmap palette using the target palette.
|
|
byte lowColorMap[ARRAYSIZE(lowPalette) / 3];
|
|
byte highColorMap[ARRAYSIZE(highPalette) / 3];
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(lowColorMap); i++) {
|
|
uint colorIndex = 3 * i;
|
|
byte red = originalPalette[colorIndex + 0];
|
|
byte green = originalPalette[colorIndex + 1];
|
|
byte blue = originalPalette[colorIndex + 2];
|
|
|
|
lowColorMap[i] = getColorIndex(targetPalette, red, green, blue);
|
|
}
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(highColorMap); i++) {
|
|
uint colorIndex = 3 * (i + 246);
|
|
byte red = originalPalette[colorIndex + 0];
|
|
byte green = originalPalette[colorIndex + 1];
|
|
byte blue = originalPalette[colorIndex + 2];
|
|
|
|
highColorMap[i] = getColorIndex(targetPalette, red, green, blue);
|
|
}
|
|
|
|
// Replace the original palette with the target palette
|
|
memcpy(originalPalette, targetPalette, sizeof(targetPalette));
|
|
|
|
// Remap the pixel data to the target palette
|
|
Graphics::Surface *surface = mhkSurface->getSurface();
|
|
byte *pixels = (byte *) surface->getPixels();
|
|
|
|
for (int i = 0; i < surface->w * surface->h; i++) {
|
|
if (pixels[i] < ARRAYSIZE(lowColorMap)) {
|
|
pixels[i] = lowColorMap[pixels[i]];
|
|
} else if (pixels[i] >= 246) {
|
|
pixels[i] = highColorMap[pixels[i] - 246];
|
|
}
|
|
}
|
|
}
|
|
|
|
byte MystGraphics::getColorIndex(const byte *palette, byte red, byte green, byte blue) {
|
|
for (uint i = 0; i < 256; i++) {
|
|
if (palette[(3 * i) + 0] == red && palette[(3 * i) + 1] == green && palette[(3 * i) + 2] == blue) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// GDI actually chooses the nearest color if no exact match is found,
|
|
// but this should not happen in Myst
|
|
debug(1, "Color (%d, %d, %d) not in target palette", red, green, blue);
|
|
return 0;
|
|
}
|
|
|
|
void MystGraphics::setPaletteToScreen() {
|
|
_vm->_system->getPaletteManager()->setPalette(_palette, 0, 256);
|
|
}
|
|
|
|
} // End of namespace Mohawk
|