scummvm/engines/adl/display.cpp

577 lines
15 KiB
C++
Raw Normal View History

2016-02-26 23:32:06 +01:00
/* 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 "adl/display.h"
#include "common/stream.h"
#include "common/rect.h"
#include "graphics/surface.h"
#include "common/system.h"
#include "common/str.h"
#include "common/events.h"
#include "common/rect.h"
#include "common/array.h"
#include "common/config-manager.h"
2016-02-26 23:32:06 +01:00
#include "engines/engine.h"
#include "engines/util.h"
#include "graphics/palette.h"
2016-02-26 23:32:06 +01:00
namespace Adl {
static const byte font[64][5] = {
2016-02-26 23:32:06 +01:00
{ 0x7c, 0x82, 0xba, 0xb2, 0x9c }, { 0xf8, 0x24, 0x22, 0x24, 0xf8 }, // @A
{ 0xfe, 0x92, 0x92, 0x92, 0x6c }, { 0x7c, 0x82, 0x82, 0x82, 0x44 }, // BC
{ 0xfe, 0x82, 0x82, 0x82, 0x7c }, { 0xfe, 0x92, 0x92, 0x92, 0x82 }, // DE
{ 0xfe, 0x12, 0x12, 0x12, 0x02 }, { 0x7c, 0x82, 0x82, 0xa2, 0xe2 }, // FG
{ 0xfe, 0x10, 0x10, 0x10, 0xfe }, { 0x00, 0x82, 0xfe, 0x82, 0x00 }, // HI
{ 0x40, 0x80, 0x80, 0x80, 0x7e }, { 0xfe, 0x10, 0x28, 0x44, 0x82 }, // JK
{ 0xfe, 0x80, 0x80, 0x80, 0x80 }, { 0xfe, 0x04, 0x18, 0x04, 0xfe }, // LM
{ 0xfe, 0x08, 0x10, 0x20, 0xfe }, { 0x7c, 0x82, 0x82, 0x82, 0x7c }, // NO
{ 0xfe, 0x12, 0x12, 0x12, 0x0c }, { 0x7c, 0x82, 0xa2, 0x42, 0xbc }, // PQ
{ 0xfe, 0x12, 0x32, 0x52, 0x8c }, { 0x4c, 0x92, 0x92, 0x92, 0x64 }, // RS
{ 0x02, 0x02, 0xfe, 0x02, 0x02 }, { 0x7e, 0x80, 0x80, 0x80, 0x7e }, // TU
{ 0x3e, 0x40, 0x80, 0x40, 0x3e }, { 0xfe, 0x40, 0x30, 0x40, 0xfe }, // VW
{ 0xc6, 0x28, 0x10, 0x28, 0xc6 }, { 0x06, 0x08, 0xf0, 0x08, 0x06 }, // XY
{ 0xc2, 0xa2, 0x92, 0x8a, 0x86 }, { 0xfe, 0xfe, 0x82, 0x82, 0x82 }, // Z[
{ 0x04, 0x08, 0x10, 0x20, 0x40 }, { 0x82, 0x82, 0x82, 0xfe, 0xfe }, // \]
{ 0x20, 0x10, 0x08, 0x10, 0x20 }, { 0x80, 0x80, 0x80, 0x80, 0x80 }, // ^_
{ 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0xbe, 0x00, 0x00 }, // !
{ 0x00, 0x0e, 0x00, 0x0e, 0x00 }, { 0x28, 0xfe, 0x28, 0xfe, 0x28 }, // "#
{ 0x48, 0x54, 0xfe, 0x54, 0x24 }, { 0x46, 0x26, 0x10, 0xc8, 0xc4 }, // $%
{ 0x6c, 0x92, 0xac, 0x40, 0xa0 }, { 0x00, 0x00, 0x0e, 0x00, 0x00 }, // &'
{ 0x38, 0x44, 0x82, 0x00, 0x00 }, { 0x00, 0x00, 0x82, 0x44, 0x38 }, // ()
{ 0x44, 0x28, 0xfe, 0x28, 0x44 }, { 0x10, 0x10, 0x7c, 0x10, 0x10 }, // *+
{ 0x00, 0x80, 0x60, 0x00, 0x00 }, { 0x10, 0x10, 0x10, 0x10, 0x10 }, // ,-
{ 0x00, 0x00, 0x80, 0x00, 0x00 }, { 0x40, 0x20, 0x10, 0x08, 0x04 }, // ./
{ 0x7c, 0xa2, 0x92, 0x8a, 0x7c }, { 0x00, 0x84, 0xfe, 0x80, 0x00 }, // 01
{ 0xc4, 0xa2, 0x92, 0x92, 0x8c }, { 0x42, 0x82, 0x92, 0x9a, 0x66 }, // 23
{ 0x30, 0x28, 0x24, 0xfe, 0x20 }, { 0x4e, 0x8a, 0x8a, 0x8a, 0x72 }, // 45
{ 0x78, 0x94, 0x92, 0x92, 0x62 }, { 0x02, 0xe2, 0x12, 0x0a, 0x06 }, // 67
{ 0x6c, 0x92, 0x92, 0x92, 0x6c }, { 0x8c, 0x92, 0x92, 0x52, 0x3c }, // 89
{ 0x00, 0x00, 0x28, 0x00, 0x00 }, { 0x00, 0x80, 0x68, 0x00, 0x00 }, // :;
{ 0x10, 0x28, 0x44, 0x82, 0x00 }, { 0x28, 0x28, 0x28, 0x28, 0x28 }, // <=
{ 0x00, 0x82, 0x44, 0x28, 0x10 }, { 0x04, 0x02, 0xb2, 0x0a, 0x04 } // >?
};
Display::Display() :
_cursorPos(0),
2016-03-01 15:47:34 +01:00
_mode(kModeText),
_showCursor(false) {
initGraphics(560, 384, true);
_monochrome = !ConfMan.getBool("color");
_scanlines = ConfMan.getBool("scanlines");
setPalette(_scanlines, _monochrome);
2016-02-26 23:32:06 +01:00
_frameBuf = new byte[kFrameBufSize];
_frameBufSurface = new Graphics::Surface;
_frameBufSurface->create(kWidth * 2, kHeight * 2, Graphics::PixelFormat::createFormatCLUT8());
_textBuf = new byte[kTextBufSize];
memset(_textBuf, ' ' | 0x80, kTextBufSize);
_textBufSurface = new Graphics::Surface;
_textBufSurface->create(kWidth * 2, kHeight * 2, Graphics::PixelFormat::createFormatCLUT8());
createFont();
}
Display::~Display() {
delete[] _frameBuf;
_frameBufSurface->free();
delete _frameBufSurface;
delete[] _textBuf;
_textBufSurface->free();
delete _textBufSurface;
_font->free();
delete _font;
}
void Display::setPalette(bool scanlines, bool monochrome) {
const byte colorPal[6 * 3] = {
0x00, 0x00, 0x00,
0xff, 0xff, 0xff,
0xc7, 0x34, 0xff,
0x38, 0xcb, 0x00,
0x0d, 0xa1, 0xff,
0xf2, 0x5e, 0x00
};
const byte monoPal[2 * 3] = {
0x00, 0x00, 0x00,
0x00, 0xc0, 0x01
};
if (monochrome) {
g_system->getPaletteManager()->setPalette(monoPal, 0, 2);
if (!scanlines)
g_system->getPaletteManager()->setPalette(monoPal, 6, 2);
} else {
g_system->getPaletteManager()->setPalette(colorPal, 0, 6);
if (!scanlines)
g_system->getPaletteManager()->setPalette(colorPal, 6, 6);
}
}
2016-02-26 23:32:06 +01:00
void Display::loadFrameBuffer(Common::ReadStream &stream) {
stream.read(_frameBuf, kFrameBufSize);
}
void Display::decodeScanlineColor(byte *dst, int pitch, byte *src) {
2016-02-26 23:32:06 +01:00
// TODO: shift secondPal by half a pixel
bool prevOn = false;
for (uint j = 0; j < 39; ++j) {
bool secondPal = src[j] & 0x80;
byte cur = src[j];
byte next = 0;
if (j != 39)
next = src[j + 1];
for (uint k = 0; k < 7; ++k) {
bool curOn = cur & (1 << k);
bool nextOn;
if (k != 6)
nextOn = cur & (1 << (k + 1));
else
nextOn = next & 1;
byte color;
if (curOn == prevOn || curOn == nextOn)
color = curOn ? 1 : 0;
else {
if (secondPal)
color = (curOn == ((j + k) % 2) ? 5 : 4);
else
color = (curOn == ((j + k) % 2) ? 3 : 2);
}
dst[0] = color;
dst[1] = color;
dst[pitch] = color + 6;
dst[pitch + 1] = color + 6;
2016-02-26 23:32:06 +01:00
dst += 2;
prevOn = curOn;
}
}
}
void Display::decodeScanlineMono(byte *dst, int pitch, byte *src) {
// TODO: shift secondPal by half a pixel
for (uint j = 0; j < 39; ++j) {
for (uint k = 0; k < 7; ++k) {
byte color = 0;
if (src[j] & (1 << k))
color = 1;
dst[0] = color;
dst[1] = color;
dst[pitch] = color + 6;
dst[pitch + 1] = color + 6;
dst += 2;
}
}
}
void Display::decodeScanline(byte *dst, int pitch, byte *src) {
if (_monochrome)
decodeScanlineMono(dst, pitch, src);
else
decodeScanlineColor(dst, pitch, src);
}
2016-02-26 23:32:06 +01:00
Display::PixelPos Display::getPixelPos(byte x, byte y) {
PixelPos pixelPos;
// FIXME: check X, Y range
byte offsetL = y & 0xc0;
offsetL |= offsetL >> 2;
byte offsetH = y;
y <<= 2;
offsetH <<= 1;
offsetH |= y >> 7;
y <<= 1;
offsetH <<= 1;
offsetH |= y >> 7;
y <<= 1;
offsetL >>= 1;
offsetL |= y & 0x80;
y <<= 1;
offsetH = offsetH & 0x1f;
pixelPos.rowAddr = (offsetH << 8) | offsetL;
pixelPos.byteOffset = x / 7;
pixelPos.bitMask = 0x80 | (1 << x % 7);
return pixelPos;
}
byte Display::getPixelColor(byte offset, byte color) {
if (offset & 1) {
byte c = color << 1;
if (c >= 0x40 && c < 0xc0)
return color ^ 0x7f;
}
return color;
}
void Display::decodeFrameBuffer() {
byte *src = _frameBuf;
int pitch = _frameBufSurface->pitch;
for (int j = 0; j < 8; ++j) {
for (int i = 0; i < 8; ++i) {
byte *dst = (byte *)_frameBufSurface->getPixels() + pitch * 2 * (i * 8 + j);
decodeScanline(dst, pitch, src);
src += 40;
dst += pitch * 2 * 64;
decodeScanline(dst, pitch, src);
src += 40;
dst += pitch * 2 * 64;
decodeScanline(dst, pitch, src);
src += 48;
dst += pitch * 2 * 64;
}
}
}
void Display::drawPixel(byte x, byte y, byte color) {
PixelPos p = getPixelPos(x, y);
byte c = getPixelColor(p.byteOffset, color);
byte *b = _frameBuf + p.rowAddr + p.byteOffset;
c ^= *b;
c &= p.bitMask;
c ^= *b;
*b = c;
}
void Display::moveX(PixelPos &p, byte &color, bool left) {
if (left) {
byte bit = p.bitMask;
bool b = bit & 1;
bit >>= 1;
if (!b) {
bit ^= 0xc0;
p.bitMask = bit;
return;
}
--p.byteOffset;
if (p.byteOffset & 0x80)
p.byteOffset = 39;
p.bitMask = 0xc0;
} else {
byte bit = p.bitMask;
bit <<= 1;
bit ^= 0x80;
if (bit & 0x80) {
p.bitMask = bit;
return;
}
p.bitMask = 0x81;
++p.byteOffset;
if (p.byteOffset == 40)
p.byteOffset = 0;
}
color = getPixelColor(p.byteOffset, color);
}
void Display::moveY(PixelPos &p, bool down) {
if (!down) {
if (p.rowAddr & 0x1c00)
p.rowAddr -= 0x400;
else if (p.rowAddr & 0x380)
p.rowAddr += 0x1b80;
else {
p.rowAddr += 0x1f58;
if (!(p.rowAddr & 0x80))
p.rowAddr += 0x78; // Wrap around
}
} else {
p.rowAddr += 0x400;
if (p.rowAddr & 0x1c00)
return;
else if ((p.rowAddr & 0x380) != 0x380)
p.rowAddr -= 0x1f80;
else {
p.rowAddr -= 0x2358;
if ((p.rowAddr & 0x78) == 0x78)
p.rowAddr -= 0x78; // Wrap around
}
}
}
void Display::drawNextPixel(Display::PixelPos &p, byte &color, byte bits, byte quadrant) {
if (bits & 4) {
byte b = (_frameBuf[p.rowAddr + p.byteOffset] ^ color) & p.bitMask;
_frameBuf[p.rowAddr + p.byteOffset] ^= b;
}
bits += quadrant;
if (bits & 1)
moveX(p, color, bits & 2);
else
moveY(p, bits & 2);
}
2016-02-28 17:21:09 +01:00
void Display::drawLineArt(const Common::Array<byte> &lineArt, Common::Point p, byte rotation, byte scaling, byte color) {
2016-02-26 23:32:06 +01:00
const byte stepping[] = {
0xff, 0xfe, 0xfa, 0xf4, 0xec, 0xe1, 0xd4, 0xc5,
0xb4, 0xa1, 0x8d, 0x78, 0x61, 0x49, 0x31, 0x18,
0xff
};
PixelPos pos = getPixelPos(p.x, p.y);
byte c = getPixelColor(pos.byteOffset, color);
byte quadrant = rotation >> 4;
rotation &= 0xf;
byte xStep = stepping[rotation];
byte yStep = stepping[(rotation ^ 0xf) + 1] + 1;
2016-02-28 17:21:09 +01:00
for (uint i = 0; i < lineArt.size(); ++i) {
byte b = lineArt[i];
2016-02-26 23:32:06 +01:00
do {
byte xFrac = 0x80;
byte yFrac = 0x80;
for (uint j = 0; j < scaling; ++j) {
if (xFrac + xStep + 1 > 255)
drawNextPixel(pos, c, b, quadrant);
xFrac += xStep + 1;
if (yFrac + yStep > 255)
drawNextPixel(pos, c, b, quadrant + 1);
yFrac += yStep;
}
b >>= 3;
} while (b != 0);
}
}
void Display::drawLine(Common::Point p1, Common::Point p2, byte color) {
PixelPos p = getPixelPos(p1.x, p1.y);
byte c = getPixelColor(p.byteOffset, color);
int16 deltaX = p2.x - p1.x;
byte dir = deltaX >> 8;
if (deltaX < 0)
deltaX = -deltaX;
int16 err = deltaX;
int16 deltaY = p2.y - p1.y - 1;
dir >>= 1;
if (deltaY >= 0) {
deltaY = -deltaY - 2;
dir |= 0x80;
}
int16 steps = deltaY - deltaX;
err += deltaY + 1;
while (1) {
byte *b = _frameBuf + p.rowAddr + p.byteOffset;
byte d = *b;
d ^= c;
d &= p.bitMask;
d ^= *b;
*b = d;
if (++steps == 0)
return;
if (err < 0) {
moveY(p, dir & 0x80);
err += deltaX;
} else {
moveX(p, c, dir & 0x40);
err += deltaY + 1;
}
}
}
void Display::clear(byte color) {
for (uint i = 0; i < kFrameBufSize; ++i)
_frameBuf[i] = getPixelColor(i & 1, color);
}
void Display::updateTextSurface() {
for (uint row = 0; row < 24; ++row)
for (uint col = 0; col < 40; ++col) {
2016-03-01 15:47:34 +01:00
int charPos = row * 40 + col;
2016-02-26 23:32:06 +01:00
char c = _textBuf[row * 40 + col];
2016-03-01 15:47:34 +01:00
if (charPos == _cursorPos && _showCursor)
c = (c & 0x3f) | 0x40;
2016-02-26 23:32:06 +01:00
Common::Rect r(7 * 2, 8 * 2);
r.translate(((c & 0x3f) % 16) * 7 * 2, (c & 0x3f) / 16 * 8 * 2);
if (!(c & 0x80)) {
if (!(c & 0x40) || ((g_system->getMillis() / 270) & 1))
r.translate(0, 4 * 8 * 2);
}
_textBufSurface->copyRectToSurface(*_font, col * 7 * 2, row * 8 * 2, r);
}
}
void Display::drawChar(byte c, int x, int y) {
byte *buf = (byte *)_font->getPixels() + y * _font->pitch + x;
for (uint row = 0; row < 8; ++row) {
if (row & 1) {
buf[_font->pitch] = 6;
buf[_font->pitch + 1] = 6;
buf[_font->pitch + 6 * 2] = 6;
buf[_font->pitch + 6 * 2 + 1] = 6;
}
for (uint col = 1; col < 6; ++col) {
2016-02-26 23:32:06 +01:00
if (font[c][col - 1] & (1 << row)) {
buf[col * 2] = 1;
buf[col * 2 + 1] = 1;
buf[_font->pitch + col * 2] = 1 + 6;
buf[_font->pitch + col * 2 + 1] = 1 + 6;
} else {
buf[_font->pitch + col * 2] = 6;
buf[_font->pitch + col * 2 + 1] = 6;
2016-02-26 23:32:06 +01:00
}
}
2016-02-26 23:32:06 +01:00
buf += 2 * _font->pitch;
}
}
void Display::createFont() {
_font = new Graphics::Surface;
_font->create(16 * 7 * 2, 4 * 8 * 2 * 2, Graphics::PixelFormat::createFormatCLUT8());
for (uint i = 0; i < 4; ++i)
for (uint j = 0; j < 16; ++j)
drawChar(i * 16 + j, j * 7 * 2, i * 8 * 2);
// Create inverted font
byte *buf = (byte *)_font->getPixels();
byte *bufInv = buf + (_font->h / 2) * _font->pitch;
for (uint row = 0; row < _font->h / 2; row += 2) {
for (uint col = 0; col < _font->w; ++col)
bufInv[col] = (buf[col] ? 0 : 1);
buf += _font->pitch;
bufInv += _font->pitch;
for (uint col = 0; col < _font->w; ++col)
bufInv[col] = (buf[col] == 7 ? 6 : 7);
2016-02-26 23:32:06 +01:00
buf += _font->pitch;
bufInv += _font->pitch;
}
}
void Display::updateScreen() {
if (_mode == kModeText) {
g_system->copyRectToScreen(_textBufSurface->getPixels(), _textBufSurface->pitch, 0, 0, _textBufSurface->w, _textBufSurface->h);
} else if (_mode == kModeHires) {
g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h);
} else {
g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h - 4 * 8 * 2);
g_system->copyRectToScreen(_textBufSurface->getBasePtr(0, _textBufSurface->h - 4 * 8 * 2), _textBufSurface->pitch, 0, _textBufSurface->h - 4 * 8 * 2, _textBufSurface->w, 4 * 8 * 2);
}
}
2016-03-01 15:47:34 +01:00
void Display::home() {
memset(_textBuf, APPLECHAR(' '), kTextBufSize);
_cursorPos = 0;
2016-02-26 23:32:06 +01:00
}
2016-03-01 15:47:34 +01:00
void Display::moveCursorForward() {
++_cursorPos;
2016-02-26 23:32:06 +01:00
2016-03-01 15:47:34 +01:00
if (_cursorPos >= kTextBufSize)
scrollUp();
}
2016-02-26 23:32:06 +01:00
2016-03-01 15:47:34 +01:00
void Display::moveCursorBackward() {
--_cursorPos;
2016-02-26 23:32:06 +01:00
2016-03-01 15:47:34 +01:00
if (_cursorPos < 0)
_cursorPos = 0;
2016-02-26 23:32:06 +01:00
}
2016-03-01 15:47:34 +01:00
void Display::moveCursorTo(const Common::Point &pos) {
_cursorPos = pos.y * 40 + pos.x;
2016-02-26 23:32:06 +01:00
2016-03-01 15:47:34 +01:00
if (_cursorPos >= kTextBufSize)
error("Cursor position (%i, %i) out of bounds", pos.x, pos.y);
}
2016-02-26 23:32:06 +01:00
2016-03-01 15:47:34 +01:00
void Display::setCharAtCursor(byte c) {
_textBuf[_cursorPos] = c;
}
2016-02-26 23:32:06 +01:00
2016-03-01 15:47:34 +01:00
void Display::scrollUp() {
memmove(_textBuf, _textBuf + 40, kTextBufSize - 40);
memset(_textBuf + kTextBufSize - 40, ' ' | 0x80, 40);
_cursorPos -= 40;
}
2016-02-26 23:32:06 +01:00
2016-03-01 15:47:34 +01:00
void Display::printString(const Common::String &str) {
Common::String::const_iterator c;
for (c = str.begin(); c != str.end(); ++c) {
byte b = *c;
2016-02-26 23:32:06 +01:00
2016-03-01 15:47:34 +01:00
if (*c == APPLECHAR('\r'))
_cursorPos = (_cursorPos / 40 + 1) * 40;
else if (b < 0x80 || b >= 0xa0) {
setCharAtCursor(b);
++_cursorPos;
2016-02-26 23:32:06 +01:00
}
2016-03-01 15:47:34 +01:00
if (_cursorPos == kTextBufSize)
scrollUp();
2016-02-26 23:32:06 +01:00
}
2016-03-01 15:47:34 +01:00
updateTextSurface();
2016-02-26 23:32:06 +01:00
}
2016-03-01 15:47:34 +01:00
void Display::showCursor(bool enable) {
_showCursor = enable;
2016-02-26 23:32:06 +01:00
}
2016-02-29 16:50:24 +01:00
void Display::setCursorPos(Common::Point pos) {
_cursorPos = pos.y * 40 + pos.x;
}
2016-02-26 23:32:06 +01:00
} // End of namespace Adl