scummvm/engines/mohawk/myst_graphics.cpp
Colin Snover 432fd522d2 ENGINES: Remove default1x scaler flag
This flag is removed for a few reasons:

* Engines universally set this flag to true for widths > 320,
  which made it redundant everywhere;
* This flag functioned primarily as a "force 1x scaler" flag,
  since its behaviour was almost completely undocumented and users
  would need to figure out that they'd need an explicit non-default
  scaler set to get a scaler to operate at widths > 320;
* (Most importantly) engines should not be in the business of
  deciding how the backend may choose to render its virtual screen.
  The choice of rendering behaviour belongs to the user, and the
  backend, in that order.

A nearby future commit restores the default1x scaler behaviour in
the SDL backend code for the moment, but in the future it is my
hope that there will be a better configuration UI to allow users
to specify how they want scaling to work for high resolutions.
2017-10-07 12:30:29 -05:00

794 lines
25 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(), nullptr);
if (_vm->_system->getScreenFormat().bytesPerPixel == 1)
error("Myst ME requires greater than 256 colors to run");
} else {
// Paletted
initGraphics(_viewport.width(), _viewport.height());
clearScreenPalette();
}
_pixelFormat = _vm->_system->getScreenFormat();
// Initialize our buffer
_backBuffer = new Graphics::Surface();
_backBuffer->create(_vm->_system->getWidth(), _vm->_system->getHeight(), _pixelFormat);
}
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 %d", id);
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);
applyImagePatches(id, mhkSurface);
return mhkSurface;
}
void MystGraphics::applyImagePatches(uint16 id, const MohawkSurface *mhkSurface) const {
// In the English ME version of the game, the instructions found on Stoneship
// to open the vault are incorrect. They are:
// Turn every one of [these switches to the] "off" position.
// They should be:
// Turn every one of [these switches to the] "on" position.
//
// Here we stomp over the "off" with an "on".
// The fixed image was provided by dafioram in bug Trac#10115.
if (id == 2019 && _vm->getFeatures() & GF_ME && _vm->getLanguage() == Common::EN_ANY) {
static const byte markerSwitchInstructionsFixPic[] = {
0x1d, 0x1c, 0x19, 0x19, 0x19, 0x19, 0x1c, 0x19, 0x19, 0x17, 0x19, 0x19, 0x19, 0x19, 0x19,
0x1e, 0x1e, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
0x1c, 0x19, 0x19, 0x19, 0x1c, 0x19, 0x19, 0x19, 0x1c, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
0x1d, 0x1e, 0x16, 0x0d, 0x0e, 0x12, 0x19, 0x19, 0x17, 0x10, 0x06, 0x05, 0x19, 0x19, 0x19,
0x1e, 0x1e, 0x10, 0x13, 0x1c, 0x11, 0x0d, 0x19, 0x12, 0x09, 0x16, 0x04, 0x18, 0x18, 0x19,
0x1e, 0x1a, 0x03, 0x1b, 0x1c, 0x17, 0x02, 0x15, 0x13, 0x00, 0x19, 0x06, 0x18, 0x19, 0x18,
0x1e, 0x1e, 0x01, 0x1b, 0x1c, 0x1b, 0x02, 0x15, 0x13, 0x00, 0x19, 0x07, 0x0a, 0x19, 0x18,
0x1e, 0x1c, 0x0c, 0x0e, 0x14, 0x0c, 0x0c, 0x19, 0x0b, 0x00, 0x19, 0x00, 0x08, 0x19, 0x19,
0x1e, 0x1c, 0x19, 0x14, 0x0f, 0x0f, 0x14, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x17,
0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
0x1c, 0x1c, 0x1e, 0x19, 0x19, 0x19, 0x17, 0x19, 0x19, 0x17, 0x19, 0x19, 0x19, 0x19, 0x19
};
static const byte markerSwitchInstructionsFixPal[] = {
0x00, 0x00, 0x00,
0x10, 0x08, 0x08,
0x18, 0x10, 0x10,
0x28, 0x10, 0x08,
0x20, 0x18, 0x18,
0x28, 0x20, 0x20,
0x38, 0x20, 0x10,
0x30, 0x28, 0x20,
0x38, 0x30, 0x28,
0x40, 0x38, 0x28,
0x48, 0x38, 0x28,
0x48, 0x40, 0x30,
0x50, 0x48, 0x38,
0x50, 0x48, 0x40,
0x60, 0x50, 0x38,
0x68, 0x58, 0x40,
0x68, 0x58, 0x48,
0x70, 0x60, 0x50,
0x78, 0x68, 0x50,
0x80, 0x70, 0x50,
0x80, 0x78, 0x60,
0x88, 0x80, 0x60,
0x98, 0x90, 0x70,
0xb0, 0xa0, 0x78,
0xb8, 0xa8, 0x8d,
0xb8, 0xa8, 0x90,
0xb8, 0xb0, 0x88,
0xc0, 0xb8, 0x90,
0xd8, 0xcc, 0x98,
0xd0, 0xe0, 0xc8,
0xf0, 0xe4, 0xc8
};
Graphics::Surface fixSurf;
fixSurf.create(15, 11, Graphics::PixelFormat::createFormatCLUT8());
fixSurf.copyRectToSurface(markerSwitchInstructionsFixPic, fixSurf.w, 0, 0, fixSurf.w, fixSurf.h);
fixSurf.convertToInPlace(_pixelFormat, markerSwitchInstructionsFixPal);
mhkSurface->getSurface()->copyRectToSurface(fixSurf, 171, 208, Common::Rect(fixSurf.w, fixSurf.h));
fixSurf.free();
}
}
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);
_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);
_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) {
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;
copyBackBufferToScreen(area);
_vm->wait(delay);
}
if (area.right < rect.right) {
area.left = area.right;
area.right = rect.right;
copyBackBufferToScreen(area);
}
}
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;
copyBackBufferToScreen(area);
_vm->wait(delay);
}
if (area.left > rect.left) {
area.right = area.left;
area.left = rect.left;
copyBackBufferToScreen(area);
}
}
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++) {
transitionDissolve(rect, step);
_vm->doFrame();
}
}
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;
copyBackBufferToScreen(area);
_vm->wait(delay);
}
if (area.bottom < rect.bottom) {
area.top = area.bottom;
area.bottom = rect.bottom;
copyBackBufferToScreen(area);
}
}
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;
copyBackBufferToScreen(area);
_vm->wait(delay);
}
if (area.top > rect.top) {
area.bottom = area.top;
area.top = rect.top;
copyBackBufferToScreen(area);
}
}
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);
break;
default:
error("Unknown transition %d", type);
}
}
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();
}
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->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
_vm->wait(delay);
}
if (dstRect.right != rect.right) {
copyBackBufferToScreen(rect);
}
}
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->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
_vm->wait(delay);
}
if (dstRect.left != rect.left) {
copyBackBufferToScreen(rect);
}
}
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->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
_vm->wait(delay);
}
if (dstRect.bottom < rect.bottom) {
copyBackBufferToScreen(rect);
}
}
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->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
_vm->wait(delay);
}
if (dstRect.top > rect.top) {
copyBackBufferToScreen(rect);
}
}
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;
_vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
_vm->doFrame();
}
copyBackBufferToScreen(rect);
}
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;
_vm->_system->copyRectToScreen(_backBuffer->getBasePtr(dstRect.left, dstRect.top),
_backBuffer->pitch, srcRect.left, srcRect.top, srcRect.width(), srcRect.height());
_vm->doFrame();
}
copyBackBufferToScreen(rect);
}
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::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->doFrame();
}
}
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->doFrame();
}
// Set the full palette
_vm->_system->getPaletteManager()->setPalette(_palette, 0, 256);
}
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