Files
scummvm/engines/hopkins/graphics.cpp
T
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

1930 lines
51 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 "hopkins/graphics.h"
#include "hopkins/files.h"
#include "hopkins/globals.h"
#include "hopkins/hopkins.h"
#include "common/system.h"
#include "graphics/palette.h"
#include "image/pcx.h"
#include "common/file.h"
#include "common/rect.h"
#include "engines/util.h"
namespace Hopkins {
GraphicsManager::GraphicsManager(HopkinsEngine *vm) {
_vm = vm;
_lockCounter = 0;
_initGraphicsFl = false;
_screenWidth = _screenHeight = 0;
_screenLineSize = 0;
_palettePixels = NULL;
_lineNbr = 0;
_videoPtr = NULL;
_scrollOffset = 0;
_scrollPosX = 0;
_largeScreenFl = false;
_oldScrollPosX = 0;
_backBuffer = NULL;
_frontBuffer = NULL;
_screenBuffer = NULL;
_backupScreen = NULL;
_showDirtyRects = false;
_lineNbr2 = 0;
_enlargedX = _enlargedY = 0;
_enlargedXFl = _enlargedYFl = false;
_fadeDefaultSpeed = 15;
_fadingFl = false;
_skipVideoLockFl = false;
_scrollStatus = 0;
_minX = 0;
_minY = 20;
_maxX = SCREEN_WIDTH * 2;
_maxY = SCREEN_HEIGHT - 20;
_posXClipped = _posYClipped = 0;
_clipX1 = _clipY1 = 0;
_clipFl = false;
_reduceX = _reducedY = 0;
_zoomOutFactor = 0;
_width = 0;
_specialWidth = 0;
_showZones = false;
_showLines = false;
Common::fill(&_paletteBuffer[0], &_paletteBuffer[PALETTE_SIZE * 2], 0);
Common::fill(&_colorTable[0], &_colorTable[PALETTE_EXT_BLOCK_SIZE], 0);
Common::fill(&_palette[0], &_palette[PALETTE_EXT_BLOCK_SIZE], 0);
Common::fill(&_oldPalette[0], &_oldPalette[PALETTE_EXT_BLOCK_SIZE], 0);
if (_vm->getIsDemo()) {
if (_vm->getPlatform() == Common::kPlatformLinux)
// CHECKME: Should be false?
_manualScroll = true;
else
_manualScroll = false;
_scrollSpeed = 16;
} else {
_manualScroll = false;
_scrollSpeed = 32;
}
_noFadingFl = false;
}
GraphicsManager::~GraphicsManager() {
_vm->_globals->freeMemory(_backBuffer);
_vm->_globals->freeMemory(_frontBuffer);
_vm->_globals->freeMemory(_screenBuffer);
_vm->_globals->freeMemory(_backupScreen);
}
void GraphicsManager::setGraphicalMode(int width, int height) {
if (!_initGraphicsFl) {
Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
initGraphics(width, height, &pixelFormat16);
// Init surfaces
_backBuffer = _vm->_globals->allocMemory(SCREEN_WIDTH * 2 * SCREEN_HEIGHT);
_frontBuffer = _vm->_globals->allocMemory(SCREEN_WIDTH * 2 * SCREEN_HEIGHT);
_screenBuffer = _vm->_globals->allocMemory(SCREEN_WIDTH * 2 * SCREEN_HEIGHT);
_videoPtr = NULL;
_screenWidth = width;
_screenHeight = height;
_screenLineSize = SCREEN_WIDTH * 2;
_palettePixels = _paletteBuffer;
_lineNbr = width;
_initGraphicsFl = true;
} else {
error("setGraphicalMode called multiple times");
}
}
/**
* (try to) Lock Screen
*/
void GraphicsManager::lockScreen() {
if (!_skipVideoLockFl) {
if (_lockCounter++ == 0) {
_videoPtr = _screenBuffer;
_screenLineSize = SCREEN_WIDTH * 2;
}
}
}
/**
* (try to) Unlock Screen
*/
void GraphicsManager::unlockScreen() {
assert(_videoPtr);
if (--_lockCounter == 0) {
_videoPtr = NULL;
}
}
/**
* Clear Screen
*/
void GraphicsManager::clearScreen() {
lockScreen();
assert(_videoPtr);
Common::fill(_screenBuffer, _screenBuffer + _screenLineSize * _screenHeight, 0);
addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
unlockScreen();
}
void GraphicsManager::clearVesaScreen() {
Common::fill(_backBuffer, _backBuffer + _screenLineSize * _screenHeight, 0);
Common::fill(_frontBuffer, _frontBuffer + _screenLineSize * _screenHeight, 0);
addDirtyRect(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
}
/**
* Load Image
*/
void GraphicsManager::loadImage(const Common::String &file) {
Common::String filename = Common::String::format("%s.PCX", file.c_str());
loadScreen(filename);
initColorTable(165, 170, _palette);
}
/**
* Load VGA Image
*/
void GraphicsManager::loadVgaImage(const Common::String &file) {
setScreenWidth(SCREEN_WIDTH);
clearScreen();
loadPCX320(_backBuffer, file, _palette);
memcpy(_frontBuffer, _backBuffer, 64000);
setScreenWidth(320);
_maxX = 320;
copy16bFromSurfaceScaleX2(_frontBuffer);
addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
fadeInBreakout();
}
/**
* Load Screen
*/
void GraphicsManager::loadScreen(const Common::String &file) {
Common::File f;
assert(!_videoPtr);
bool flag = true;
bool fileFoundFl = false;
_vm->_fileIO->searchCat(file, RES_PIC, fileFoundFl);
if (!fileFoundFl) {
if (!f.open(file))
error("loadScreen - %s", file.c_str());
f.seek(0, SEEK_END);
f.close();
flag = false;
}
scrollScreen(0);
loadPCX640(_backBuffer, file, _palette, flag);
_scrollPosX = 0;
_oldScrollPosX = 0;
clearPalette();
if (!_largeScreenFl) {
setScreenWidth(SCREEN_WIDTH);
_maxX = SCREEN_WIDTH;
clearScreen();
display8BitRect(_backBuffer, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
} else {
setScreenWidth(SCREEN_WIDTH * 2);
_maxX = SCREEN_WIDTH * 2;
clearScreen();
if (_manualScroll)
display8BitRect(_backBuffer, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
}
memcpy(_frontBuffer, _backBuffer, SCREEN_WIDTH * 2 * SCREEN_HEIGHT);
}
void GraphicsManager::initColorTable(int minIndex, int maxIndex, byte *palette) {
for (int idx = 0; idx < 256; ++idx)
_colorTable[idx] = idx;
translateSurface(_colorTable, palette, 256, minIndex, maxIndex);
for (int idx = 0; idx < 256; ++idx) {
byte v = _colorTable[idx];
if (v > 27 || !v)
_colorTable[idx] = 0;
}
_colorTable[0] = 1;
}
/**
* Scroll Screen
*/
void GraphicsManager::scrollScreen(int amount) {
int result = CLIP(amount, 0, SCREEN_WIDTH);
_vm->_events->_startPos.x = result;
_scrollOffset = result;
_scrollPosX = result;
}
void GraphicsManager::translateSurface(byte *destP, const byte *srcP, int count, int minThreshold, int maxThreshold) {
byte *destPosP = destP;
for (int idx = 0; idx < count; ++idx) {
int palIndex = *destPosP;
int srcOffset = 3 * palIndex;
int col1 = srcP[srcOffset] + srcP[srcOffset + 1] + srcP[srcOffset + 2];
for (int idx2 = 0; idx2 < 38; ++idx2) {
srcOffset = 3 * idx2;
int col2 = srcP[srcOffset] + srcP[srcOffset + 1] + srcP[srcOffset + 2];
col2 += minThreshold;
if (col2 < col1)
continue;
col2 -= maxThreshold;
if (col2 > col1)
continue;
*destPosP = (idx2 == 0) ? 1 : idx2;
break;
}
destPosP++;
}
}
void GraphicsManager::fillSurface(byte *surface, byte *col, int size) {
byte dataVal;
byte *dataP = surface;
for (int count = size - 1; count; count--){
dataVal = *dataP;
*dataP = col[dataVal];
dataP++;
}
}
void GraphicsManager::loadPCX640(byte *surface, const Common::String &file, byte *palette, bool typeFlag) {
Common::File f;
Image::PCXDecoder pcxDecoder;
// Clear the passed surface
memset(surface, 0, SCREEN_WIDTH * 2 * SCREEN_HEIGHT);
if (typeFlag) {
// Load PCX from within the PIC resource
if (!f.open("PIC.RES"))
error("Error opening PIC.RES.");
f.seek(_vm->_fileIO->_catalogPos);
} else {
// Load stand alone PCX file
if (!f.open(file))
error("Error opening PCX %s.", file.c_str());
}
// Decode the PCX
if (!pcxDecoder.loadStream(f))
error("Error decoding PCX %s", file.c_str());
const Graphics::Surface *s = pcxDecoder.getSurface();
// Copy out the dimensions and pixels of the decoded surface
_largeScreenFl = s->w > SCREEN_WIDTH;
Common::copy((const byte *)s->getPixels(), (const byte *)s->getBasePtr(0, s->h), surface);
// Copy out the palette
const byte *palSrc = pcxDecoder.getPalette();
Common::copy((const byte *)palSrc, (const byte *)palSrc + PALETTE_BLOCK_SIZE, palette);
f.close();
}
void GraphicsManager::loadPCX320(byte *surface, const Common::String &file, byte *palette) {
Common::File f;
if (!f.open(file))
error("File not found - %s", file.c_str());
size_t filesize = f.size();
f.read(surface, 128);
int imageSize = filesize - 896;
byte *ptr = _vm->_globals->allocMemory(65024);
size_t curBufSize;
int imageNumb;
int imageDataSize;
if (imageSize >= 64000) {
imageNumb = imageSize / 64000 + 1;
imageDataSize = abs(64000 * (imageSize / 64000) - imageSize);
f.read(ptr, 64000);
curBufSize = 64000;
} else {
imageNumb = 1;
imageDataSize = imageSize;
f.read(ptr, imageSize);
curBufSize = imageSize;
}
imageNumb--;
size_t curByteIdx = 0;
for (int i = 0; i < 64000; i++) {
if (curByteIdx == curBufSize) {
curByteIdx = 0;
--imageNumb;
curBufSize = 64000;
if (!imageNumb)
curBufSize = imageDataSize;
f.read(ptr, curBufSize);
}
byte curByte = ptr[curByteIdx++];
if (curByte > 192) {
int repeatCount = curByte - 192;
if (curByteIdx == curBufSize) {
curByteIdx = 0;
--imageNumb;
curBufSize = 64000;
if (imageNumb == 1)
curBufSize = imageDataSize;
f.read(ptr, curBufSize);
}
curByte = ptr[curByteIdx++];
for (; repeatCount; repeatCount--)
surface[i++] = curByte;
--i;
} else {
surface[i] = curByte;
}
}
f.seek(filesize - 768);
f.read(palette, 768);
f.close();
_vm->_globals->freeMemory(ptr);
}
// Clear Palette
void GraphicsManager::clearPalette() {
// As weird as it sounds, this is what the original Linux executable does,
// and not a full array clear.
_paletteBuffer[0] = 0;
}
void GraphicsManager::setScreenWidth(int pitch) {
_lineNbr = _lineNbr2 = pitch;
}
/**
* Copies data from a 8-bit palette surface into the 16-bit screen
*/
void GraphicsManager::display8BitRect(const byte *surface, int xs, int ys, int width, int height, int destX, int destY) {
lockScreen();
assert(_videoPtr);
const byte *srcP = xs + _lineNbr2 * ys + surface;
byte *destP = (byte *)_videoPtr + destX * 2 + _screenLineSize * destY;
for (int yp = 0; yp < height; ++yp) {
// Copy over the line, using the source pixels as lookups into the pixels palette
const byte *lineSrcP = srcP;
byte *lineDestP = destP;
for (int xp = 0; xp < width; ++xp) {
lineDestP[0] = _palettePixels[lineSrcP[0] * 2];
lineDestP[1] = _palettePixels[(lineSrcP[0] * 2) + 1];
lineDestP += 2;
lineSrcP++;
}
// Move to the start of the next line
srcP += _lineNbr2;
destP += _screenLineSize;
}
unlockScreen();
addRefreshRect(destX, destY, destX + width, destY + height);
}
void GraphicsManager::displayScaled8BitRect(const byte *surface, int xp, int yp, int width, int height, int destX, int destY) {
const byte *palette;
byte *loopDestP;
const byte *loopSrcP;
int yCtr;
assert(_videoPtr);
const byte *srcP = surface + xp + 320 * yp;
byte *destP = (byte *)_videoPtr + 30 * _screenLineSize + destX + destX + destX + destX + _screenLineSize * 2 * destY;
int yCount = height;
int xCount = width;
do {
yCtr = yCount;
int xCtr = xCount;
loopSrcP = srcP;
loopDestP = destP;
int savedXCount = xCount;
palette = _palettePixels;
do {
destP[0] = destP[2] = destP[_screenLineSize] = destP[_screenLineSize + 2] = palette[2 * srcP[0]];
destP[1] = destP[3] = destP[_screenLineSize + 1] = destP[_screenLineSize + 3] = palette[(2 * srcP[0]) + 1];
++srcP;
destP += 4;
--xCtr;
} while (xCtr);
xCount = savedXCount;
destP = loopDestP + _screenLineSize * 2;
srcP = loopSrcP + 320;
yCount = yCtr - 1;
} while (yCtr != 1);
addRefreshRect(destX, destY, destX + width, destY + width);
}
/**
* Fade in. the step number is determine by parameter.
*/
void GraphicsManager::fadeIn(const byte *palette, int step, const byte *surface) {
byte palData2[PALETTE_BLOCK_SIZE];
int fadeStep;
if (step > 1)
fadeStep = step;
else
fadeStep = 2;
// Initialize temporary palette
Common::fill(&palData2[0], &palData2[PALETTE_BLOCK_SIZE], 0);
// Set current palette to black
setPaletteVGA256(palData2);
// Loop through fading in the palette
for (int fadeIndex = 0; fadeIndex < fadeStep; ++fadeIndex) {
for (int palOffset = 0; palOffset < PALETTE_BLOCK_SIZE; palOffset += 3) {
palData2[palOffset + 0] = fadeIndex * palette[palOffset + 0] / (fadeStep - 1);
palData2[palOffset + 1] = fadeIndex * palette[palOffset + 1] / (fadeStep - 1);
palData2[palOffset + 2] = fadeIndex * palette[palOffset + 2] / (fadeStep - 1);
}
// Set the transition palette and refresh the screen
setPaletteVGA256(palData2);
display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
updateScreen();
// Added a delay in order to see the fading
_vm->_events->delay(20);
}
// Set the final palette
setPaletteVGA256(palette);
// Refresh the screen
display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
updateScreen();
}
/**
* Fade out. the step number is determine by parameter.
*/
void GraphicsManager::fadeOut(const byte *palette, int step, const byte *surface) {
byte palData[PALETTE_BLOCK_SIZE];
if ((step > 1) && (palette) && (!_vm->_events->_escKeyFl)) {
int fadeStep = step;
for (int fadeIndex = 0; fadeIndex < fadeStep; fadeIndex++) {
for (int palOffset = 0; palOffset < PALETTE_BLOCK_SIZE; palOffset += 3) {
palData[palOffset + 0] = (fadeStep - fadeIndex - 1) * palette[palOffset + 0] / (fadeStep - 1);
palData[palOffset + 1] = (fadeStep - fadeIndex - 1) * palette[palOffset + 1] / (fadeStep - 1);
palData[palOffset + 2] = (fadeStep - fadeIndex - 1) * palette[palOffset + 2] / (fadeStep - 1);
}
setPaletteVGA256(palData);
display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
updateScreen();
_vm->_events->delay(20);
}
}
// No initial palette, or end of fading
for (int i = 0; i < PALETTE_BLOCK_SIZE; i++)
palData[i] = 0;
setPaletteVGA256(palData);
display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
updateScreen();
}
/**
* Short fade in. The step number is 1, the default step number is also set to 1.
*/
void GraphicsManager::fadeInShort() {
_fadeDefaultSpeed = 1;
fadeIn(_palette, 1, (const byte *)_frontBuffer);
}
/**
* Short fade out. The step number is 1, the default step number is also set to 1.
*/
void GraphicsManager::fadeOutShort() {
_fadeDefaultSpeed = 1;
fadeOut(_palette, 1, (const byte *)_frontBuffer);
}
/**
* Long fade in. The step number is 20, the default step number is also set to 15.
*/
void GraphicsManager::fadeInLong() {
_fadeDefaultSpeed = 15;
fadeIn(_palette, 20, (const byte *)_frontBuffer);
}
/**
* Long fade out. The step number is 20, the default step number is also set to 15.
*/
void GraphicsManager::fadeOutLong() {
_fadeDefaultSpeed = 15;
fadeOut(_palette, 20, (const byte *)_frontBuffer);
}
/**
* Fade in. The step number used is the default step number.
*/
void GraphicsManager::fadeInDefaultLength(const byte *surface) {
assert(surface);
fadeIn(_palette, _fadeDefaultSpeed, surface);
}
/**
* Fade out. The step number used is the default step number.
*/
void GraphicsManager::fadeOutDefaultLength(const byte *surface) {
assert(surface);
fadeOut(_palette, _fadeDefaultSpeed, surface);
}
/**
* Fade in used by for the breakout mini-game
*/
void GraphicsManager::fadeInBreakout() {
setPaletteVGA256(_palette);
copy16bFromSurfaceScaleX2(_frontBuffer);
updateScreen();
}
/**
* Fade out used by for the breakout mini-game
*/
void GraphicsManager::fadeOutBreakout() {
byte palette[PALETTE_EXT_BLOCK_SIZE];
memset(palette, 0, PALETTE_EXT_BLOCK_SIZE);
setPaletteVGA256(palette);
copy16bFromSurfaceScaleX2(_frontBuffer);
updateScreen();
}
void GraphicsManager::setPaletteVGA256(const byte *palette) {
changePalette(palette);
}
void GraphicsManager::setPaletteVGA256WithRefresh(const byte *palette, const byte *surface) {
changePalette(palette);
display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
updateScreen();
}
void GraphicsManager::setColorPercentage(int palIndex, int r, int g, int b) {
int palOffset = 3 * palIndex;
_palette[palOffset] = 255 * r / 100;
_palette[palOffset + 1] = 255 * g / 100;
_palette[palOffset + 2] = 255 * b / 100;
}
void GraphicsManager::setColorPercentage2(int palIndex, int r, int g, int b) {
int rv = 255 * r / 100;
int gv = 255 * g / 100;
int bv = 255 * b / 100;
int palOffset = 3 * palIndex;
_palette[palOffset] = rv;
_palette[palOffset + 1] = gv;
_palette[palOffset + 2] = bv;
WRITE_UINT16(&_paletteBuffer[2 * palIndex], mapRGB(rv, gv, bv));
}
void GraphicsManager::changePalette(const byte *palette) {
const byte *srcP = &palette[0];
for (int idx = 0; idx < PALETTE_SIZE; ++idx, srcP += 3) {
WRITE_UINT16(&_paletteBuffer[2 * idx], mapRGB(srcP[0], srcP[1], srcP[2]));
}
}
uint16 GraphicsManager::mapRGB(byte r, byte g, byte b) {
Graphics::PixelFormat format = g_system->getScreenFormat();
return (r >> format.rLoss) << format.rShift
| (g >> format.gLoss) << format.gShift
| (b >> format.bLoss) << format.bShift;
}
void GraphicsManager::updateScreen() {
// Display any aras of the screen that need refreshing
displayDirtyRects();
displayRefreshRects();
// Extra checks for debug information
if (_showZones)
displayZones();
if (_showLines)
displayLines();
// Update the screen
g_system->updateScreen();
}
void GraphicsManager::copyWinscanVbe3(const byte *srcData, byte *destSurface) {
byte srcByte;
byte destLen1;
byte *destSlice1P;
byte destLen2;
byte *destSlice2P;
int rleValue = 0;
int destOffset = 0;
const byte *srcP = srcData;
for (;;) {
srcByte = srcP[0];
if (srcByte == kByteStop)
return;
if (srcByte == 211) {
destLen1 = srcP[1];
rleValue = srcP[2];
destSlice1P = destOffset + destSurface;
destOffset += destLen1;
memset(destSlice1P, rleValue, destLen1);
srcP += 3;
} else if (srcByte < 222) {
if (srcByte > 211) {
destLen2 = (byte)(srcP[0] + 45);
rleValue = srcP[1];
destSlice2P = destOffset + destSurface;
destOffset += destLen2;
memset(destSlice2P, rleValue, destLen2);
srcP += 2;
} else {
destSurface[destOffset] = srcByte;
++srcP;
++destOffset;
}
} else if (srcByte < kSetOffset) {
destOffset += (byte)(srcP[0] + 35);
srcP++;
} else if (srcByte == k8bVal) {
destOffset += srcP[1];
srcP += 2;
} else if (srcByte == k16bVal) {
destOffset += READ_LE_UINT16(srcP + 1);
srcP += 3;
} else {
destOffset += READ_LE_UINT32(srcP + 1);
srcP += 5;
}
}
}
void GraphicsManager::copyVideoVbe16(const byte *srcData) {
const byte *srcP = srcData;
int destOffset = 0;
lockScreen();
assert(_videoPtr);
for (;;) {
byte srcByte = srcP[0];
if (srcByte >= 222) {
if (srcByte == kByteStop)
break;
if (srcByte < kSetOffset) {
destOffset += srcByte - 221;
srcByte = *++srcP;
} else if (srcByte == k8bVal) {
destOffset += srcP[1];
srcByte = srcP[2];
srcP += 2;
} else if (srcByte == k16bVal) {
destOffset += READ_LE_UINT16(srcP + 1);
srcByte = srcP[3];
srcP += 3;
} else {
destOffset += READ_LE_UINT32(srcP + 1);
srcByte = srcP[5];
srcP += 5;
}
}
if (destOffset > SCREEN_WIDTH * SCREEN_HEIGHT) {
warning("HACK: Stopping anim, out of bounds - 0x%x %d", srcByte, destOffset);
break;
}
if (srcByte > 210) {
if (srcByte == 211) {
int pixelCount = srcP[1];
int pixelIndex = srcP[2];
byte *destP = (byte *)_videoPtr + destOffset * 2;
destOffset += pixelCount;
while (pixelCount--) {
destP[0] = _palettePixels[2 * pixelIndex];
destP[1] = _palettePixels[(2 * pixelIndex) + 1];
destP += 2;
}
srcP += 3;
} else {
int pixelCount = srcByte - 211;
int pixelIndex = srcP[1];
byte *destP = (byte *)_videoPtr + destOffset * 2;
destOffset += pixelCount;
while (pixelCount--) {
destP[0] = _palettePixels[2 * pixelIndex];
destP[1] = _palettePixels[(2 * pixelIndex) + 1];
destP += 2;
}
srcP += 2;
}
} else {
byte *destP = (byte *)_videoPtr + destOffset * 2;
destP[0] = _palettePixels[2 * srcByte];
destP[1] = _palettePixels[(2 * srcByte) + 1];
++srcP;
++destOffset;
}
}
unlockScreen();
}
void GraphicsManager::copyVideoVbe16a(const byte *srcData) {
byte srcByte;
int destOffset = 0;
const byte *srcP = srcData;
lockScreen();
for (;;) {
srcByte = srcP[0];
if (srcByte == kByteStop)
break;
if (srcP[0] > kByteStop) {
if (srcByte == k8bVal) {
destOffset += srcP[1];
srcByte = srcP[2];
srcP += 2;
} else if (srcByte == k16bVal) {
destOffset += READ_LE_UINT16(srcP + 1);
srcByte = srcP[3];
srcP += 3;
} else {
destOffset += READ_LE_UINT32(srcP + 1);
srcByte = srcP[5];
srcP += 5;
}
}
WRITE_LE_UINT16((byte *)_videoPtr + destOffset * 2, READ_LE_UINT16(_palettePixels + 2 * srcByte));
++srcP;
++destOffset;
}
unlockScreen();
}
void GraphicsManager::copySurfaceRect(const byte *srcSurface, byte *destSurface, int xs, int ys, int width, int height) {
const byte *srcP;
byte *destP;
int rowCount;
int rowCount2;
// TODO: This code in the original is potentially dangerous, as it doesn't clip the area to within
// the screen, and so thus can read areas outside of the allocated surface buffer
srcP = xs + _lineNbr2 * ys + srcSurface;
destP = destSurface;
rowCount = height;
do {
rowCount2 = rowCount;
if (width & 1) {
memcpy(destP, srcP, width);
srcP += width;
destP += width;
} else if (width & 2) {
for (int i = width >> 1; i; --i) {
destP[0] = srcP[0];
destP[1] = srcP[1];
srcP += 2;
destP += 2;
}
} else {
memcpy(destP, srcP, 4 * (width >> 2));
srcP += 4 * (width >> 2);
destP += 4 * (width >> 2);
}
srcP = _lineNbr2 + srcP - width;
rowCount = rowCount2 - 1;
} while (rowCount2 != 1);
}
/**
* Draws a sprite onto the screen
* @param surface Destination surface
* @param spriteData The raw data for a sprite set
* @param xp X co-ordinate. For some reason, starts from 300 = first column
* @param yp Y co-ordinate. FOr some reason, starts from 300 = top row
* @param spriteIndex Index of the sprite to draw
*/
void GraphicsManager::drawVesaSprite(byte *surface, const byte *spriteData, int xp, int yp, int spriteIndex) {
// Get a pointer to the start of the desired sprite
const byte *spriteP = spriteData + 3;
for (int i = spriteIndex; i; --i)
spriteP += READ_LE_UINT32(spriteP) + 16;
_posXClipped = 0;
_posYClipped = 0;
_clipFl = false;
spriteP += 4;
int width = READ_LE_UINT16(spriteP);
spriteP += 2;
int height = READ_LE_UINT16(spriteP);
// Clip X
_clipX1 = width;
if ((xp + width) <= _minX + 300)
return;
if (xp < _minX + 300) {
_posXClipped = _minX + 300 - xp;
_clipFl = true;
}
// Clip Y
_clipY1 = height;
if (yp <= 0)
return;
if (yp < _minY + 300) {
_posYClipped = _minY + 300 - yp;
_clipFl = true;
}
// Clip X1
if (xp >= _maxX + 300)
return;
if (xp + width > _maxX + 300) {
int xAmount = width + 10 - (xp + width - (_maxX + 300));
if (xAmount <= 10)
return;
_clipX1 = xAmount - 10;
_clipFl = true;
}
// Clip Y1
if (yp >= _maxY + 300)
return;
if (yp + height > _maxY + 300) {
int yAmount = height + 10 - (yp + height - (_maxY + 300));
if (yAmount <= 10)
return;
// _clipY1 is always positive thanks to the previous check
_clipY1 = yAmount - 10;
_clipFl = true;
}
// Sprite display
// Set up source
spriteP += 6;
int srcOffset = READ_LE_UINT16(spriteP);
spriteP += 4;
const byte *srcP = spriteP;
spriteP += srcOffset;
// Set up surface destination
byte *destP = surface + (yp - 300) * _lineNbr2 + (xp - 300);
// Handling for clipped versus non-clipped
if (_clipFl) {
// Clipped version
for (int yc = 0; yc < _clipY1; ++yc, destP += _lineNbr2) {
byte *tempDestP = destP;
byte byteVal;
int xc = 0;
while ((byteVal = *srcP) != 253) {
++srcP;
width = READ_LE_UINT16(srcP);
srcP += 2;
if (byteVal == 254) {
// Copy pixel range
for (int xv = 0; xv < width; ++xv, ++xc, ++spriteP, ++tempDestP) {
if (_posYClipped == 0 && xc >= _posXClipped && xc < _clipX1)
*tempDestP = *spriteP;
}
} else {
// Skip over bytes
tempDestP += width;
xc += width;
}
}
if (_posYClipped > 0)
--_posYClipped;
srcP += 3;
}
} else {
// Non-clipped
for (int yc = 0; yc < height; ++yc, destP += _lineNbr2) {
byte *tempDestP = destP;
byte byteVal;
while ((byteVal = *srcP) != 253) {
++srcP;
width = READ_LE_UINT16(srcP);
srcP += 2;
if (byteVal == 254) {
// Copy pixel range
Common::copy(spriteP, spriteP + width, tempDestP);
spriteP += width;
}
tempDestP += width;
}
// Skip over control byte and width
srcP += 3;
}
}
}
void GraphicsManager::endDisplayBob() {
for (int idx = 1; idx <= 20; ++idx) {
if (_vm->_animMan->_animBqe[idx]._enabledFl)
_vm->_objectsMan->hideBob(idx);
}
_vm->_events->refreshScreenAndEvents();
_vm->_events->refreshScreenAndEvents();
for (int idx = 1; idx <= 20; ++idx) {
if (_vm->_animMan->_animBqe[idx]._enabledFl)
_vm->_objectsMan->resetBob(idx);
}
for (int idx = 1; idx < 36; ++idx) {
_vm->_objectsMan->_lockedAnims[idx]._enableFl = false;
}
for (int idx = 1; idx <= 20; ++idx) {
_vm->_animMan->_animBqe[idx]._enabledFl = false;
}
}
void GraphicsManager::displayAllBob() {
for (int idx = 1; idx <= 20; ++idx) {
if (_vm->_animMan->_animBqe[idx]._enabledFl)
_vm->_objectsMan->displayBob(idx);
}
}
void GraphicsManager::resetDirtyRects() {
_dirtyRects.clear();
}
void GraphicsManager::resetRefreshRects() {
_refreshRects.clear();
}
// Add a game area dirty rectangle
void GraphicsManager::addDirtyRect(int x1, int y1, int x2, int y2) {
x1 = CLIP(x1, _minX, _maxX);
y1 = CLIP(y1, _minY, _maxY);
x2 = CLIP(x2, _minX, _maxX);
y2 = CLIP(y2, _minY, _maxY);
if ((x2 > x1) && (y2 > y1))
addRectToArray(_dirtyRects, Common::Rect(x1, y1, x2, y2));
}
// Add a refresh rect
void GraphicsManager::addRefreshRect(int x1, int y1, int x2, int y2) {
x1 = MAX(x1, 0);
y1 = MAX(y1, 0);
x2 = MIN(x2, SCREEN_WIDTH);
y2 = MIN(y2, SCREEN_HEIGHT);
if ((x2 > x1) && (y2 > y1))
addRectToArray(_refreshRects, Common::Rect(x1, y1, x2, y2));
}
void GraphicsManager::addRectToArray(Common::Array<Common::Rect> &rects, const Common::Rect &newRect) {
// Scan for an intersection with existing rects
uint rectIndex;
for (rectIndex = 0; rectIndex < rects.size(); ++rectIndex) {
Common::Rect &r = rects[rectIndex];
if (r.intersects(newRect)) {
// Rect either intersects or is completely inside existing one, so extend existing one as necessary
r.extend(newRect);
break;
}
}
if (rectIndex == rects.size()) {
// Rect not intersecting any existing one, so add it in
assert(rects.size() < DIRTY_RECTS_SIZE);
rects.push_back(newRect);
}
// Take care of merging the existing rect list. This is done as a separate check even if
// a previous extending above has been done, since the merging of the new rect above may
// result in further rects now able to be merged
for (int srcIndex = rects.size() - 1; srcIndex > 0; --srcIndex) {
const Common::Rect &srcRect = rects[srcIndex];
// Loop through all the other rects to see if it intersects them
for (int destIndex = srcIndex - 1; destIndex >= 0; --destIndex) {
if (rects[destIndex].intersects(srcRect)) {
// Found an intersection, so extend the found one, and delete the original
rects[destIndex].extend(srcRect);
rects.remove_at(srcIndex);
break;
}
}
}
}
// Draw any game dirty rects onto the screen intermediate surface
void GraphicsManager::displayDirtyRects() {
if (_dirtyRects.size() == 0)
return;
lockScreen();
// Refresh the entire screen
for (uint idx = 0; idx < _dirtyRects.size(); ++idx) {
Common::Rect &r = _dirtyRects[idx];
Common::Rect dstRect;
if (_vm->_events->_breakoutFl) {
displayScaled8BitRect(_frontBuffer, r.left, r.top, r.right - r.left, r.bottom - r.top, r.left, r.top);
dstRect.left = r.left * 2;
dstRect.top = r.top * 2 + 30;
dstRect.setWidth((r.right - r.left) * 2);
dstRect.setHeight((r.bottom - r.top) * 2);
} else if (r.right > _vm->_events->_startPos.x && r.left < _vm->_events->_startPos.x + SCREEN_WIDTH) {
r.left = MAX<int16>(r.left, _vm->_events->_startPos.x);
r.right = MIN<int16>(r.right, (int16)_vm->_events->_startPos.x + SCREEN_WIDTH);
display8BitRect(_frontBuffer, r.left, r.top, r.right - r.left, r.bottom - r.top, r.left - _vm->_events->_startPos.x, r.top);
dstRect.left = r.left - _vm->_events->_startPos.x;
dstRect.top = r.top;
dstRect.setWidth(r.right - r.left);
dstRect.setHeight(r.bottom - r.top);
}
// If it's a valid rect, then add it to the list of areas to refresh on the screen
if (dstRect.isValidRect() && dstRect.width() > 0 && dstRect.height() > 0)
addRectToArray(_refreshRects, dstRect);
}
unlockScreen();
resetDirtyRects();
}
void GraphicsManager::displayRefreshRects() {
Graphics::Surface *screenSurface = NULL;
if (_showDirtyRects) {
screenSurface = g_system->lockScreen();
g_system->copyRectToScreen(_screenBuffer, _screenLineSize, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
}
// Loop through copying over any specified rects to the screen
for (uint idx = 0; idx < _refreshRects.size(); ++idx) {
const Common::Rect &r = _refreshRects[idx];
byte *srcP = _screenBuffer + _screenLineSize * r.top + (r.left * 2);
g_system->copyRectToScreen(srcP, _screenLineSize, r.left, r.top, r.width(), r.height());
if (_showDirtyRects)
screenSurface->frameRect(r, 0xffffff);
}
if (_showDirtyRects)
g_system->unlockScreen();
resetRefreshRects();
}
/**
* Display any zones for the current room
*/
void GraphicsManager::displayZones() {
Graphics::Surface *screenSurface = g_system->lockScreen();
for (int bobZoneId = 0; bobZoneId <= 48; bobZoneId++) {
int bobId = _vm->_linesMan->_bobZone[bobZoneId];
if (bobId) {
// Get the rectangle for the zone
Common::Rect r(_vm->_objectsMan->_bob[bobId]._oldX, _vm->_objectsMan->_bob[bobId]._oldY,
_vm->_objectsMan->_bob[bobId]._oldX + _vm->_objectsMan->_bob[bobId]._oldWidth,
_vm->_objectsMan->_bob[bobId]._oldY + _vm->_objectsMan->_bob[bobId]._oldHeight);
displayDebugRect(screenSurface, r, 0xff0000);
}
}
for (int squareZoneId = 0; squareZoneId <= 99; squareZoneId++) {
if (_vm->_linesMan->_zone[squareZoneId]._enabledFl && _vm->_linesMan->_squareZone[squareZoneId]._enabledFl) {
Common::Rect r(_vm->_linesMan->_squareZone[squareZoneId]._left, _vm->_linesMan->_squareZone[squareZoneId]._top,
_vm->_linesMan->_squareZone[squareZoneId]._right, _vm->_linesMan->_squareZone[squareZoneId]._bottom);
displayDebugRect(screenSurface, r, 0x00ff00);
}
}
g_system->unlockScreen();
}
/**
* Display any zones for the current room
*/
void GraphicsManager::displayLines() {
Graphics::Surface *screenSurface = g_system->lockScreen();
for (int lineIndex = 0; lineIndex < _vm->_linesMan->_linesNumb; lineIndex++) {
int i = 0;
do {
int x = _vm->_linesMan->_lineItem[lineIndex]._lineData[i] - _scrollPosX;
int y = _vm->_linesMan->_lineItem[lineIndex]._lineData[i+1];
if (x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT) {
WRITE_UINT16(screenSurface->getBasePtr(x, y), 0xffff);
}
i += 2;
}
while(_vm->_linesMan->_lineItem[lineIndex]._lineData[i] != -1);
}
g_system->unlockScreen();
}
void GraphicsManager::displayDebugRect(Graphics::Surface *surface, const Common::Rect &srcRect, uint32 color) {
Common::Rect r = srcRect;
// Move for scrolling offset and adjust to crop on-screen
r.translate(-_scrollPosX, 0);
r.left = MAX(r.left, (int16)0);
r.top = MAX(r.top, (int16)0);
r.right = MIN(r.right, (int16)SCREEN_WIDTH);
r.bottom = MIN(r.bottom, (int16)SCREEN_HEIGHT);
// If there's an on-screen portion, display it
if (r.isValidRect())
surface->frameRect(r, color);
}
/**
* Fast Display of either a compressed or vesa sprite
*/
void GraphicsManager::fastDisplay(const byte *spriteData, int xp, int yp, int spriteIndex, bool addSegment) {
int width = _vm->_objectsMan->getWidth(spriteData, spriteIndex);
int height = _vm->_objectsMan->getHeight(spriteData, spriteIndex);
if (*spriteData == 78) {
drawCompressedSprite(_backBuffer, spriteData, xp + 300, yp + 300, spriteIndex, 0, 0, false);
drawCompressedSprite(_frontBuffer, spriteData, xp + 300, yp + 300, spriteIndex, 0, 0, false);
} else {
drawVesaSprite(_frontBuffer, spriteData, xp + 300, yp + 300, spriteIndex);
drawVesaSprite(_backBuffer, spriteData, xp + 300, yp + 300, spriteIndex);
}
if (addSegment)
addDirtyRect(xp, yp, xp + width, yp + height);
}
void GraphicsManager::fastDisplay2(const byte *objectData, int xp, int yp, int idx, bool addSegment) {
int width = _vm->_objectsMan->getWidth(objectData, idx);
int height = _vm->_objectsMan->getHeight(objectData, idx);
if (*objectData == 78) {
drawCompressedSprite(_backBuffer, objectData, xp + 300, yp + 300, idx, 0, 0, false);
drawCompressedSprite(_frontBuffer, objectData, xp + 300, yp + 300, idx, 0, 0, false);
} else {
drawVesaSprite(_frontBuffer, objectData, xp + 300, yp + 300, idx);
drawVesaSprite(_backBuffer, objectData, xp + 300, yp + 300, idx);
}
if (addSegment)
addDirtyRect(xp, yp, xp + width, yp + height);
}
/**
* Copy from surface to video buffer, scale 2x.
*/
void GraphicsManager::copy16bFromSurfaceScaleX2(const byte *surface) {
byte *palPtr;
int curPixel;
lockScreen();
assert(_videoPtr);
const byte *curSurface = surface;
byte *destPtr = 30 * _screenLineSize + (byte *)_videoPtr;
for (int y = 200; y; y--) {
byte *oldDestPtr = destPtr;
for (int x = 320; x; x--) {
curPixel = 2 * *curSurface;
palPtr = _palettePixels + curPixel;
destPtr[0] = destPtr[2] = destPtr[_screenLineSize] = destPtr[_screenLineSize + 2] = palPtr[0];
destPtr[1] = destPtr[3] = destPtr[_screenLineSize + 1] = destPtr[_screenLineSize + 3] = palPtr[1];
++curSurface;
destPtr += 4;
}
destPtr = _screenLineSize * 2 + oldDestPtr;
}
unlockScreen();
}
void GraphicsManager::restoreSurfaceRect(byte *destSurface, const byte *src, int xp, int yp, int width, int height) {
int yCtr;
byte *destP = xp + _lineNbr2 * yp + destSurface;
int yNext = height;
const byte *srcP = src;
do {
yCtr = yNext;
if (width & 1) {
memcpy(destP, srcP, width);
srcP += width;
destP += width;
} else if (width & 2) {
for (int i = width >> 1; i; --i) {
destP[0] = srcP[0];
destP[1] = srcP[1];
srcP += 2;
destP += 2;
}
} else {
memcpy(destP, srcP, 4 * (width >> 2));
srcP += 4 * (width >> 2);
destP += 4 * (width >> 2);
}
destP = _lineNbr2 + destP - width;
yNext = yCtr - 1;
} while (yCtr != 1);
}
/**
* Compute the value of a parameter plus a given percentage
*/
int GraphicsManager::zoomIn(int val, int percentage) {
if (val)
val += percentage * (long int)val / 100;
return val;
}
/**
* Compute the value of a parameter minus a given percentage
*/
int GraphicsManager::zoomOut(int val, int percentage) {
if (val)
val -= percentage * (long int)val / 100;
return val;
}
// Display 'Perfect?'
void GraphicsManager::drawCompressedSprite(byte *surface, const byte *srcData, int xp300, int yp300, int frameIndex, int zoom1, int zoom2, bool flipFl) {
const byte *spriteStartP = srcData + 3;
for (int i = frameIndex; i; --i)
spriteStartP += READ_LE_UINT32(spriteStartP) + 16;
const byte *spriteSizeP = spriteStartP + 4;
int spriteWidth = READ_LE_INT16(spriteSizeP);
spriteSizeP += 2;
int spriteHeight2 = READ_LE_INT16(spriteSizeP);
int spriteHeight1 = spriteHeight2;
const byte *spritePixelsP = spriteSizeP + 10;
_posXClipped = 0;
_posYClipped = 0;
_clipX1 = 0;
_clipY1 = 0;
if ((xp300 <= _minX) || (yp300 <= _minY) || (xp300 >= _maxX + 300) || (yp300 >= _maxY + 300))
return;
// Clipped values are greater or equal to zero, thanks to the previous test
_clipX1 = _maxX + 300 - xp300;
_clipY1 = _maxY + 300 - yp300;
// _minX is never negative, and should be always 0
// The previous check insures that xp300 it's always greater to it
// After this check, posXClipped is always positive
if (xp300 < _minX + 300)
_posXClipped = _minX + 300 - xp300;
// Ditto.
if (yp300 < _minY + 300)
_posYClipped = _minY + 300 - yp300;
byte *dest1P = xp300 + _lineNbr2 * (yp300 - 300) - 300 + surface;
if (zoom2) {
_enlargedX = 0;
_enlargedY = 0;
_enlargedYFl = false;
_enlargedXFl = false;
_width = spriteWidth;
int zoomedWidth = zoomIn(spriteWidth, zoom2);
int zoomedHeight = zoomIn(spriteHeight1, zoom2);
if (flipFl) {
byte *clippedDestP = zoomedWidth + dest1P;
if (_posYClipped) {
if (_posYClipped < 0 || _posYClipped >= zoomedHeight)
return;
int hiddenHeight = 0;
while (zoomIn(++hiddenHeight, zoom2) < _posYClipped)
;
spritePixelsP += _width * hiddenHeight;
clippedDestP += _lineNbr2 * _posYClipped;
zoomedHeight -= _posYClipped;
}
if (zoomedHeight > _clipY1)
zoomedHeight = _clipY1;
if (_posXClipped) {
if (_posXClipped >= zoomedWidth)
return;
zoomedWidth -= _posXClipped;
}
if (zoomedWidth > _clipX1) {
int clippedZoomedWidth = zoomedWidth - _clipX1;
clippedDestP -= clippedZoomedWidth;
int closestWidth = 0;
while (zoomIn(++closestWidth, zoom2) < clippedZoomedWidth)
;
spritePixelsP += closestWidth;
zoomedWidth = _clipX1;
}
int curHeight;
do {
for (;;) {
curHeight = zoomedHeight;
byte *oldDestP = clippedDestP;
const byte *oldSpritePixelsP = spritePixelsP;
_enlargedXFl = false;
_enlargedX = 0;
for (int i = zoomedWidth; i; _enlargedXFl = false, i--) {
for (;;) {
if (*spritePixelsP)
*clippedDestP = *spritePixelsP;
--clippedDestP;
++spritePixelsP;
if (!_enlargedXFl)
_enlargedX += zoom2;
if (_enlargedX >= 0 && _enlargedX < 100)
break;
_enlargedX -= 100;
--spritePixelsP;
_enlargedXFl = true;
--i;
if (!i)
break;
}
}
spritePixelsP = _width + oldSpritePixelsP;
clippedDestP = _lineNbr2 + oldDestP;
if (!_enlargedYFl)
_enlargedY += zoom2;
if (_enlargedY >= 0 && _enlargedY < 100)
break;
_enlargedY -= 100;
spritePixelsP = oldSpritePixelsP;
_enlargedYFl = true;
zoomedHeight = curHeight - 1;
if (curHeight == 1)
return;
}
_enlargedYFl = false;
zoomedHeight = curHeight - 1;
} while (curHeight != 1);
} else {
if (_posYClipped) {
if (_posYClipped >= zoomedHeight)
return;
int closerHeight = 0;
while (zoomIn(++closerHeight, zoom2) < _posYClipped)
;
spritePixelsP += _width * closerHeight;
dest1P += _lineNbr2 * _posYClipped;
zoomedHeight -= _posYClipped;
}
if (zoomedHeight > _clipY1)
zoomedHeight = _clipY1;
if (_posXClipped) {
if (_posXClipped >= zoomedWidth)
return;
int closerWidth = 0;
while (zoomIn(++closerWidth, zoom2) < _posXClipped)
;
spritePixelsP += closerWidth;
dest1P += _posXClipped;
zoomedWidth = zoomedWidth - _posXClipped;
}
if (zoomedWidth > _clipX1)
zoomedWidth = _clipX1;
int curHeight;
do {
for (;;) {
curHeight = zoomedHeight;
byte *oldDest1P = dest1P;
const byte *oldSpritePixelsP = spritePixelsP;
_enlargedXFl = false;
_enlargedX = 0;
for (int i = zoomedWidth; i; _enlargedXFl = false, i--) {
for (;;) {
if (*spritePixelsP)
*dest1P = *spritePixelsP;
++dest1P;
++spritePixelsP;
if (!_enlargedXFl)
_enlargedX += zoom2;
if (_enlargedX >= 0 && _enlargedX < 100)
break;
_enlargedX -= 100;
--spritePixelsP;
_enlargedXFl = true;
--i;
if (!i)
break;
}
}
spritePixelsP = _width + oldSpritePixelsP;
dest1P = _lineNbr2 + oldDest1P;
if (!_enlargedYFl)
_enlargedY += zoom2;
if (_enlargedY >= 0 && _enlargedY < 100)
break;
_enlargedY -= 100;
spritePixelsP = oldSpritePixelsP;
_enlargedYFl = true;
zoomedHeight = curHeight - 1;
if (curHeight == 1)
return;
}
_enlargedYFl = false;
zoomedHeight = curHeight - 1;
} while (curHeight != 1);
}
} else if (zoom1) {
_reduceX = 0;
_reducedY = 0;
_width = spriteWidth;
_zoomOutFactor = zoom1;
if (zoom1 < 100) {
int zoomedSpriteWidth = zoomOut(spriteWidth, _zoomOutFactor);
if (flipFl) {
byte *curDestP = zoomedSpriteWidth + dest1P;
do {
byte *oldDestP = curDestP;
_reducedY += _zoomOutFactor;
if (_reducedY >= 0 && _reducedY < 100) {
_reduceX = 0;
int curWidth = zoomedSpriteWidth;
for (int i = _width; i; i--) {
_reduceX += _zoomOutFactor;
if (_reduceX >= 0 && _reduceX < 100) {
if (curWidth >= _posXClipped && curWidth < _clipX1 && *spritePixelsP)
*curDestP = *spritePixelsP;
--curDestP;
++spritePixelsP;
--curWidth;
} else {
_reduceX -= 100;
++spritePixelsP;
}
}
curDestP = _lineNbr2 + oldDestP;
} else {
_reducedY -= 100;
spritePixelsP += _width;
}
--spriteHeight2;
} while (spriteHeight2);
} else {
do {
int oldSpriteHeight = spriteHeight2;
byte *oldDest1P = dest1P;
_reducedY += _zoomOutFactor;
if (_reducedY >= 0 && _reducedY < 100) {
_reduceX = 0;
int curX = 0;
for (int i = _width; i; i--) {
_reduceX += _zoomOutFactor;
if (_reduceX >= 0 && _reduceX < 100) {
if (curX >= _posXClipped && curX < _clipX1 && *spritePixelsP)
*dest1P = *spritePixelsP;
++dest1P;
++spritePixelsP;
++curX;
} else {
_reduceX -= 100;
++spritePixelsP;
}
}
spriteHeight2 = oldSpriteHeight;
dest1P = _lineNbr2 + oldDest1P;
} else {
_reducedY -= 100;
spritePixelsP += _width;
}
--spriteHeight2;
} while (spriteHeight2);
}
}
} else {
_width = spriteWidth;
if (flipFl) {
byte *dest2P = spriteWidth + dest1P;
_specialWidth = spriteWidth;
if (_posYClipped) {
if (_posYClipped >= spriteHeight1 || spriteHeight1 < 0)
return;
spritePixelsP += spriteWidth * _posYClipped;
dest2P += _lineNbr2 * _posYClipped;
spriteHeight1 -= _posYClipped;
}
if (spriteHeight1 > _clipY1)
spriteHeight1 = _clipY1;
if (_posXClipped >= spriteWidth)
return;
spriteWidth -= _posXClipped;
if (spriteWidth > _clipX1) {
int clippedWidth = spriteWidth - _clipX1;
spritePixelsP += clippedWidth;
dest2P -= clippedWidth;
spriteWidth = _clipX1;
}
int yCtr2;
do {
yCtr2 = spriteHeight1;
byte *destCopy2P = dest2P;
const byte *spritePixelsCopy2P = spritePixelsP;
for (int xCtr2 = spriteWidth; xCtr2; xCtr2--) {
if (*spritePixelsP)
*dest2P = *spritePixelsP;
++spritePixelsP;
--dest2P;
}
spritePixelsP = _specialWidth + spritePixelsCopy2P;
dest2P = _lineNbr2 + destCopy2P;
spriteHeight1 = yCtr2 - 1;
} while (yCtr2 != 1);
} else {
_specialWidth = spriteWidth;
if (_posYClipped) {
if (_posYClipped >= spriteHeight1 || spriteHeight1 < 0)
return;
spritePixelsP += spriteWidth * _posYClipped;
dest1P += _lineNbr2 * _posYClipped;
spriteHeight1 -= _posYClipped;
}
if (spriteHeight1 > _clipY1)
spriteHeight1 = _clipY1;
if (_posXClipped) {
if (_posXClipped >= spriteWidth)
return;
spritePixelsP += _posXClipped;
dest1P += _posXClipped;
spriteWidth -= _posXClipped;
}
if (spriteWidth > _clipX1)
spriteWidth = _clipX1;
int yCtr1;
do {
yCtr1 = spriteHeight1;
byte *dest1CopyP = dest1P;
const byte *spritePixelsCopyP = spritePixelsP;
for (int xCtr1 = spriteWidth; xCtr1; xCtr1--) {
if (*spritePixelsP)
*dest1P = *spritePixelsP;
++dest1P;
++spritePixelsP;
}
spritePixelsP = _specialWidth + spritePixelsCopyP;
dest1P = _lineNbr2 + dest1CopyP;
spriteHeight1 = yCtr1 - 1;
} while (yCtr1 != 1);
}
}
}
void GraphicsManager::copySurface(const byte *surface, int x1, int y1, int width, int height, byte *destSurface, int destX, int destY) {
int left = x1;
int top = y1;
int croppedWidth = width;
int croppedHeight = height;
if (x1 < _minX) {
croppedWidth = width - (_minX - x1);
left = _minX;
}
if (y1 < _minY) {
croppedHeight = height - (_minY - y1);
top = _minY;
}
if (top + croppedHeight > _maxY)
croppedHeight = _maxY - top;
if (left + croppedWidth > _maxX)
croppedWidth = _maxX - left;
if (croppedWidth > 0 && croppedHeight > 0) {
int height2 = croppedHeight;
copyRect(surface, left, top, croppedWidth, croppedHeight, destSurface, destX, destY);
addDirtyRect(left, top, left + croppedWidth, top + height2);
}
}
void GraphicsManager::copyRect(const byte *srcSurface, int x1, int y1, uint16 width, int height, byte *destSurface, int destX, int destY) {
const byte *srcP = x1 + _lineNbr2 * y1 + srcSurface;
byte *destP = destX + _lineNbr2 * destY + destSurface;
int yp = height;
int yCurrent;
do {
yCurrent = yp;
memcpy(destP, srcP, 4 * (width >> 2));
const byte *src2P = (srcP + 4 * (width >> 2));
byte *dest2P = (destP + 4 * (width >> 2));
int pitch = width - 4 * (width >> 2);
memcpy(dest2P, src2P, pitch);
destP = (dest2P + pitch + _lineNbr2 - width);
srcP = (src2P + pitch + _lineNbr2 - width);
yp = yCurrent - 1;
} while (yCurrent != 1);
}
// Display Font
void GraphicsManager::displayFont(byte *surface, const byte *spriteData, int xp, int yp, int characterIndex, int color) {
const byte *spriteDataP = spriteData + 3;
for (int i = characterIndex; i; --i)
spriteDataP += READ_LE_UINT32(spriteDataP) + 16;
int spriteWidth = 0;
int spriteHeight = 0;
const byte *spriteSizeP = spriteDataP + 4;
spriteWidth = READ_LE_INT16(spriteSizeP);
spriteSizeP += 2;
spriteHeight = READ_LE_INT16(spriteSizeP);
const byte *spritePixelsP = spriteSizeP + 10;
byte *destP = surface + xp + _lineNbr2 * yp;
_width = spriteWidth;
int yCtr;
do {
yCtr = spriteHeight;
byte *destLineP = destP;
for (int xCtr = spriteWidth; xCtr; xCtr--) {
byte destByte = *spritePixelsP;
if (*spritePixelsP) {
if (destByte == 252)
destByte = color;
*destP = destByte;
}
++destP;
++spritePixelsP;
}
destP = _lineNbr2 + destLineP;
spriteHeight = yCtr - 1;
} while (yCtr != 1);
}
void GraphicsManager::initScreen(const Common::String &file, int mode, bool initializeScreen) {
Common::String filename = file + ".ini";
bool fileFoundFl = false;
byte *ptr = _vm->_fileIO->searchCat(filename, RES_INI, fileFoundFl);
if (!fileFoundFl) {
ptr = _vm->_fileIO->loadFile(filename);
}
if (!mode) {
filename = file + ".spr";
_vm->_globals->_levelSpriteBuf = _vm->_globals->freeMemory(_vm->_globals->_levelSpriteBuf);
if (initializeScreen) {
fileFoundFl = false;
_vm->_globals->_levelSpriteBuf = _vm->_fileIO->searchCat(filename, RES_SLI, fileFoundFl);
if (!fileFoundFl) {
_vm->_globals->_levelSpriteBuf = _vm->_fileIO->loadFile(filename);
} else {
_vm->_globals->_levelSpriteBuf = _vm->_fileIO->loadFile("RES_SLI.RES");
}
}
}
if (READ_BE_UINT24(ptr) != MKTAG24('I', 'N', 'I')) {
error("Invalid INI File %s", file.c_str());
} else {
bool doneFlag = false;
int dataOffset = 1;
do {
int dataVal1 = _vm->_script->handleOpcode(ptr + 20 * dataOffset);
if (dataVal1 == -1 || _vm->shouldQuit())
return;
if (dataVal1 == 2)
dataOffset = _vm->_script->handleGoto((ptr + 20 * dataOffset));
if (dataVal1 == 3)
dataOffset = _vm->_script->handleIf(ptr, dataOffset);
if (dataOffset == -1)
error("Error, defective IFF");
if (dataVal1 == 1 || dataVal1 == 4)
++dataOffset;
if (!dataVal1 || dataVal1 == 5)
doneFlag = true;
} while (!doneFlag);
}
_vm->_globals->freeMemory(ptr);
_vm->_globals->_answerBuffer = _vm->_globals->freeMemory(_vm->_globals->_answerBuffer);
filename = file + ".rep";
fileFoundFl = false;
byte *dataP = _vm->_fileIO->searchCat(filename, RES_REP, fileFoundFl);
if (!fileFoundFl)
dataP = _vm->_fileIO->loadFile(filename);
_vm->_globals->_answerBuffer = dataP;
_vm->_objectsMan->_forceZoneFl = true;
_vm->_objectsMan->_changeVerbFl = false;
}
void GraphicsManager::displayScreen(bool initPalette) {
if (initPalette)
initColorTable(50, 65, _palette);
if (_lineNbr == SCREEN_WIDTH)
fillSurface(_frontBuffer, _colorTable, SCREEN_WIDTH * SCREEN_HEIGHT);
else if (_lineNbr == (SCREEN_WIDTH * 2))
fillSurface(_frontBuffer, _colorTable, SCREEN_WIDTH * SCREEN_HEIGHT * 2);
display8BitRect(_frontBuffer, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
memcpy(_backBuffer, _frontBuffer, 614399);
updateScreen();
}
void GraphicsManager::copyWinscanVbe(const byte *src, byte *dest) {
int destOffset = 0;
const byte *srcPtr = src;
for (;;) {
byte byteVal = *srcPtr;
if (byteVal == kByteStop)
return;
if (*srcPtr > kByteStop) {
if (byteVal == k8bVal) {
destOffset += srcPtr[1];
byteVal = srcPtr[2];
srcPtr += 2;
} else if (byteVal == k16bVal) {
destOffset += READ_LE_UINT16(srcPtr + 1);
byteVal = srcPtr[3];
srcPtr += 3;
} else {
destOffset += READ_LE_UINT32(srcPtr + 1);
byteVal = srcPtr[5];
srcPtr += 5;
}
}
dest[destOffset] = byteVal;
++srcPtr;
++destOffset;
}
}
// Reduce Screen
void GraphicsManager::reduceScreenPart(const byte *srcSurface, byte *destSurface, int xp, int yp, int width, int height, int zoom) {
const byte *srcP = xp + _lineNbr2 * yp + srcSurface;
byte *destP = destSurface;
_zoomOutFactor = zoom;
_width = width;
_reduceX = 0;
_reducedY = 0;
if (zoom < 100) {
for (int yCtr = 0; yCtr < height; ++yCtr, srcP += _lineNbr2) {
_reducedY += _zoomOutFactor;
if (_reducedY < 100) {
_reduceX = 0;
const byte *lineSrcP = srcP;
for (int xCtr = 0; xCtr < _width; ++xCtr) {
_reduceX += _zoomOutFactor;
if (_reduceX < 100) {
*destP++ = *lineSrcP++;
} else {
_reduceX -= 100;
++lineSrcP;
}
}
} else {
_reducedY -= 100;
}
}
}
}
/**
* Draw horizontal line
*/
void GraphicsManager::drawHorizontalLine(byte *surface, int xp, int yp, uint16 width, byte col) {
memset(surface + xp + _lineNbr2 * yp, col, width);
}
/**
* Draw vertical line
*/
void GraphicsManager::drawVerticalLine(byte *surface, int xp, int yp, int height, byte col) {
byte *destP = surface + xp + _lineNbr2 * yp;
for (int yCtr = height; yCtr; yCtr--) {
*destP = col;
destP += _lineNbr2;
}
}
/**
* Backup the current screen
*/
void GraphicsManager::backupScreen() {
// Allocate a new data block for the screen, if necessary
if (_vm->_graphicsMan->_backupScreen == NULL)
_vm->_graphicsMan->_backupScreen = _vm->_globals->allocMemory(SCREEN_WIDTH * 2 * SCREEN_HEIGHT);
// Backup the screen
Common::copy(_vm->_graphicsMan->_backBuffer, _vm->_graphicsMan->_backBuffer +
SCREEN_WIDTH * 2 * SCREEN_HEIGHT, _vm->_graphicsMan->_backupScreen);
}
/**
* Restore a previously backed up screen
*/
void GraphicsManager::restoreScreen() {
assert(_vm->_graphicsMan->_backupScreen);
// Restore the screen and free the buffer
Common::copy(_vm->_graphicsMan->_backupScreen, _vm->_graphicsMan->_backupScreen +
SCREEN_WIDTH * 2 * SCREEN_HEIGHT, _vm->_graphicsMan->_backBuffer);
_vm->_globals->freeMemory(_vm->_graphicsMan->_backupScreen);
_backupScreen = NULL;
}
} // End of namespace Hopkins