scummvm/engines/adl/display_a2.cpp

666 lines
21 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
// Based on AppleWin's code for NTSC emulation and its RGB Monitor palette
// Copyright (C) 2010-2011, William S Simms
// Copyright (C) 2014-2016, Michael Pohoreski, Tom Charlesworth
// Licensed under GPLv2+
#include "common/stream.h"
#include "common/rect.h"
#include "common/system.h"
#include "common/str.h"
#include "common/config-manager.h"
#include "common/math.h"
#include "common/memstream.h"
#include "graphics/surface.h"
#include "graphics/thumbnail.h"
#include "engines/util.h"
#include "adl/display_a2.h"
#include "adl/adl.h"
namespace Adl {
#define NTSC_REMOVE_BLACK_GHOSTING
// #define NTSC_REMOVE_WHITE_RINGING
// Uppercase-only Apple II font (manually created).
const byte Display_A2::_font[64][8] = {
{ 0x00, 0x1c, 0x22, 0x2a, 0x3a, 0x1a, 0x02, 0x3c }, { 0x00, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x22, 0x22 }, // @A
{ 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x22, 0x22, 0x1e }, { 0x00, 0x1c, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1c }, // BC
{ 0x00, 0x1e, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1e }, { 0x00, 0x3e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x3e }, // DE
{ 0x00, 0x3e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x02 }, { 0x00, 0x3c, 0x02, 0x02, 0x02, 0x32, 0x22, 0x3c }, // FG
{ 0x00, 0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22 }, { 0x00, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c }, // HI
{ 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x1c }, { 0x00, 0x22, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x22 }, // JK
{ 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3e }, { 0x00, 0x22, 0x36, 0x2a, 0x2a, 0x22, 0x22, 0x22 }, // LM
{ 0x00, 0x22, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x22 }, { 0x00, 0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c }, // NO
{ 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x02 }, { 0x00, 0x1c, 0x22, 0x22, 0x22, 0x2a, 0x12, 0x2c }, // PQ
{ 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x0a, 0x12, 0x22 }, { 0x00, 0x1c, 0x22, 0x02, 0x1c, 0x20, 0x22, 0x1c }, // RS
{ 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, { 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c }, // TU
{ 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08 }, { 0x00, 0x22, 0x22, 0x22, 0x2a, 0x2a, 0x36, 0x22 }, // VW
{ 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22 }, { 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08 }, // XY
{ 0x00, 0x3e, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3e }, { 0x00, 0x3e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3e }, // Z[
{ 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00 }, { 0x00, 0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e }, // \]
{ 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e }, // ^_
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08 }, // !
{ 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x14, 0x14, 0x3e, 0x14, 0x3e, 0x14, 0x14 }, // "#
{ 0x00, 0x08, 0x3c, 0x0a, 0x1c, 0x28, 0x1e, 0x08 }, { 0x00, 0x06, 0x26, 0x10, 0x08, 0x04, 0x32, 0x30 }, // $%
{ 0x00, 0x04, 0x0a, 0x0a, 0x04, 0x2a, 0x12, 0x2c }, { 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00 }, // &'
{ 0x00, 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08 }, { 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08 }, // ()
{ 0x00, 0x08, 0x2a, 0x1c, 0x08, 0x1c, 0x2a, 0x08 }, { 0x00, 0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00 }, // *+
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x04 }, { 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00 }, // ,-
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 }, { 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00 }, // ./
{ 0x00, 0x1c, 0x22, 0x32, 0x2a, 0x26, 0x22, 0x1c }, { 0x00, 0x08, 0x0c, 0x08, 0x08, 0x08, 0x08, 0x1c }, // 01
{ 0x00, 0x1c, 0x22, 0x20, 0x18, 0x04, 0x02, 0x3e }, { 0x00, 0x3e, 0x20, 0x10, 0x18, 0x20, 0x22, 0x1c }, // 23
{ 0x00, 0x10, 0x18, 0x14, 0x12, 0x3e, 0x10, 0x10 }, { 0x00, 0x3e, 0x02, 0x1e, 0x20, 0x20, 0x22, 0x1c }, // 45
{ 0x00, 0x38, 0x04, 0x02, 0x1e, 0x22, 0x22, 0x1c }, { 0x00, 0x3e, 0x20, 0x10, 0x08, 0x04, 0x04, 0x04 }, // 67
{ 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c }, { 0x00, 0x1c, 0x22, 0x22, 0x3c, 0x20, 0x10, 0x0e }, // 89
{ 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x04 }, // :;
{ 0x00, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10 }, { 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00 }, // <=
{ 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04 }, { 0x00, 0x1c, 0x22, 0x10, 0x08, 0x08, 0x00, 0x08 } // >?
};
struct LineDoubleBright {
static uint8 blend(uint8 c1, uint8 c2) {
return c1;
}
};
struct LineDoubleDim {
static uint8 blend(uint8 c1, uint8 c2) {
return (c1 >> 1) + (c1 >> 2);
}
};
struct BlendBright {
static uint8 blend(uint8 c1, uint8 c2) {
return (c1 + c2) >> 1;
}
};
struct BlendDim {
static uint8 blend(uint8 c1, uint8 c2) {
// AppleWin does c1 >>= 2; return (c1 < c2 ? c2 - c1 : 0);
// I think the following looks a lot better:
return ((c1 + c2) >> 2) + ((c1 + c2) >> 3);
}
};
static const uint kColorPhases = 4;
// All PixelWriters have been adjusted to have 3 pixels of "pre-render" that
// will be cut off when blitting to the screen
static const uint kPreRender = 3;
template<typename ColorType, typename T>
class PixelWriter {
public:
PixelWriter() : _ptr(nullptr), _format(g_system->getScreenFormat()), _phase(0), _window(0) { }
void setupWrite(ColorType *dest) {
_ptr = dest;
_phase = 3;
_window = 0;
}
void writePixels(uint bits) {
for (uint b = 0; b < 14; ++b) {
_window <<= 1;
_window |= bits & 1;
bits >>= 1;
*_ptr++ = static_cast<T *>(this)->getColor();
_phase = (_phase + 1) & 3;
}
}
protected:
ColorType *_ptr;
Graphics::PixelFormat _format;
uint _phase;
uint _window;
};
template<typename ColorType>
class PixelWriterColor : public PixelWriter<ColorType, PixelWriterColor<ColorType> > {
public:
static const uint kColors = 16;
typedef LineDoubleBright BlendRegular;
typedef LineDoubleDim BlendScanlines;
PixelWriterColor() {
const byte palette[kColors][3] = {
{ 0x00, 0x00, 0x00 }, { 0x9d, 0x09, 0x66 }, { 0x2a, 0x2a, 0xe5 }, { 0xc7, 0x34, 0xff },
{ 0x00, 0x80, 0x00 }, { 0x80, 0x80, 0x80 }, { 0x0d, 0xa1, 0xff }, { 0xaa, 0xaa, 0xff },
{ 0x55, 0x55, 0x00 }, { 0xf2, 0x5e, 0x00 }, { 0xc0, 0xc0, 0xc0 }, { 0xff, 0x89, 0xe5 },
{ 0x38, 0xcb, 0x00 }, { 0xd5, 0xd5, 0x1a }, { 0x62, 0xf6, 0x99 }, { 0xff, 0xff, 0xff }
};
for (uint pattern = 0; pattern < kColors; ++pattern) {
uint color = ((pattern & 1) << 3) | ((pattern & 2) << 1) | ((pattern & 4) >> 1) | ((pattern & 8) >> 3);
for (uint phase = 0; phase < kColorPhases; ++phase) {
_colors[phase][pattern] = this->_format.RGBToColor(palette[color][0], palette[color][1], palette[color][2]);
color = ((color & 8) >> 3) | ((color << 1) & 0x0f);
}
}
}
// >> 2 to synchronize rendering output with NTSC
ColorType getColor() { return _colors[this->_phase][(this->_window >> 2) & (kColors - 1)]; }
private:
ColorType _colors[kColorPhases][kColors];
};
template<typename ColorType, uint8 R, uint8 G, uint8 B>
class PixelWriterMono : public PixelWriter<ColorType, PixelWriterMono<ColorType, R, G, B> > {
public:
static const uint kColors = 2;
typedef LineDoubleBright BlendRegular;
typedef LineDoubleDim BlendScanlines;
PixelWriterMono() {
_colors[0] = this->_format.RGBToColor(0, 0, 0);
_colors[1] = this->_format.RGBToColor(R, G, B);
}
ColorType getColor() { return _colors[(this->_window >> 3) & (kColors - 1)]; }
private:
ColorType _colors[kColors];
};
static double filterChroma(double z) {
static double x[3] = {0, 0, 0};
static double y[3] = {0, 0, 0};
x[0] = x[1];
x[1] = x[2];
x[2] = z / 7.438011255;
y[0] = y[1];
y[1] = y[2];
y[2] = -x[0] + x[2] + (-0.7318893645 * y[0]) + (1.2336442711 * y[1]);
return y[2];
}
static double filterLuma(double z) {
static double x[3] = {0, 0, 0};
static double y[3] = {0, 0, 0};
x[0] = x[1];
x[1] = x[2];
x[2] = z / 13.71331570;
y[0] = y[1];
y[1] = y[2];
y[2] = x[0] + x[2] + (2.f * x[1]) + (-0.3961075449 * y[0]) + (1.1044202472 * y[1]);
return y[2];
}
static double filterSignal(double z) {
static double x[3] = {0, 0, 0};
static double y[3] = {0, 0, 0};
x[0] = x[1];
x[1] = x[2];
x[2] = z / 7.614490548;
y[0] = y[1];
y[1] = y[2];
y[2] = x[0] + x[2] + (2.0 * x[1]) + (-0.2718798058 * y[0]) + (0.7465656072 * y[1]);
return y[2];
}
template<typename ColorType>
class PixelWriterColorNTSC : public PixelWriter<ColorType, PixelWriterColorNTSC<ColorType> > {
public:
static const uint kColors = 4096;
typedef BlendBright BlendRegular;
typedef BlendDim BlendScanlines;
PixelWriterColorNTSC() {
for (uint phase = 0; phase < kColorPhases; ++phase) {
double phi = Common::deg2rad(phase * 90.0 + 45.0);
for (uint s = 0; s < kColors; ++s) {
uint t = s;
double y;
double i = 0.0;
double q = 0.0;
for (uint n = 0; n < 12; ++n) {
double z = (double)(0 != (t & 0x800));
t = t << 1;
for (uint k = 0; k < 2; k++ ) {
const double zz = filterSignal(z);
double c = filterChroma(zz);
y = filterLuma(zz - c);
c = c * 2.0;
i = i + (c * cos(phi) - i) / 8.0;
q = q + (c * sin(phi) - q) / 8.0;
phi += Common::deg2rad(45.0);
}
}
// YIQ to RGB
const double r64 = y + (0.956 * i) + (0.621 * q);
const double g64 = y + (-0.272 * i) + (-0.647 * q);
const double b64 = y + (-1.105 * i) + (1.702 * q);
uint8 r = CLIP<double>(r64, 0.0, 1.0) * 255;
uint8 g = CLIP<double>(g64, 0.0, 1.0) * 255;
uint8 b = CLIP<double>(b64, 0.0, 1.0) * 255;
#ifdef NTSC_REMOVE_WHITE_RINGING
if ((s & 0xf) == 15) {
// white
r = 255;
g = 255;
b = 255;
}
#endif
#ifdef NTSC_REMOVE_BLACK_GHOSTING
if ((s & 0xf) == 0) {
// Black
r = 0;
g = 0;
b = 0;
}
#endif
_colors[phase][s] = this->_format.RGBToColor(r, g, b);
}
}
}
ColorType getColor() { return _colors[this->_phase][(this->_window >> 1) & (kColors - 1)]; }
private:
ColorType _colors[kColorPhases][kColors];
};
template<typename ColorType>
class PixelWriterMonoNTSC : public PixelWriter<ColorType, PixelWriterMonoNTSC<ColorType> > {
public:
static const uint kColors = 4096;
typedef BlendBright BlendRegular;
typedef BlendDim BlendScanlines;
PixelWriterMonoNTSC() {
for (uint s = 0; s < kColors; ++s) {
uint t = s;
double y;
for (uint n = 0; n < 12; ++n) {
double z = (double)(0 != (t & 0x800));
t = t << 1;
for (uint k = 0; k < 2; k++ ) {
const double zz = filterSignal(z);
double c = filterChroma(zz);
y = filterLuma(zz - c);
}
}
const uint8 brightness = CLIP<double>(y, 0.0, 1.0) * 255;
_colors[s] = this->_format.RGBToColor(brightness, brightness, brightness);
}
}
ColorType getColor() { return _colors[(this->_window >> 1) & (kColors - 1)]; }
private:
ColorType _colors[kColors];
};
template<typename ColorType, typename GfxWriter, typename TextWriter>
class DisplayImpl_A2 : public Display_A2 {
public:
DisplayImpl_A2();
~DisplayImpl_A2() override;
void renderText() override;
void renderGraphics() override;
private:
enum {
kRenderBufWidth = (kGfxPitch + 1) * 14, // one extra chunk to account for pre-render
kRenderBufHeight = (kGfxHeight * 2) + 1 // one extra line to simplify scanline mixing
};
template<typename BlendFunc>
void blendScanlines(uint yStart, uint yEnd);
template<typename Reader, typename Writer>
void render(Writer &writer);
ColorType *_renderBuf;
uint16 _doublePixelMasks[128];
GfxWriter _writerColor;
TextWriter _writerMono;
};
template<typename ColorType, typename GfxWriter, typename TextWriter>
DisplayImpl_A2<ColorType, GfxWriter, TextWriter>::DisplayImpl_A2() : _doublePixelMasks() {
_renderBuf = new ColorType[kRenderBufHeight * kRenderBufWidth]();
for (uint8 val = 0; val < ARRAYSIZE(_doublePixelMasks); ++val)
for (uint8 mask = 0; mask < 7; mask++)
if (val & (1 << mask))
_doublePixelMasks[val] |= 3 << (mask * 2);
}
template<typename ColorType, typename GfxWriter, typename TextWriter>
DisplayImpl_A2<ColorType, GfxWriter, TextWriter>::~DisplayImpl_A2() {
delete[] _renderBuf;
}
template<typename ColorType, typename GfxWriter, typename TextWriter>
template<typename Reader, typename Writer>
void DisplayImpl_A2<ColorType, GfxWriter, TextWriter>::render(Writer &writer) {
uint startY = Reader::getStartY(this);
const uint endY = Reader::getEndY(this);
ColorType *ptr = _renderBuf + startY * kRenderBufWidth * 2;
for (uint y = startY; y < endY; ++y) {
uint16 lastBit = 0;
writer.setupWrite(ptr);
for (uint x = 0; x < kGfxPitch; ++x) {
const uint8 m = Reader::getBits(this, y, x);
uint16 bits = _doublePixelMasks[m & 0x7F];
if (m & 0x80)
bits = (bits << 1) | lastBit;
lastBit = (bits >> 13) & 1;
writer.writePixels(bits);
}
// Because of the pre-render, we need to feed
// in some more bits to get the full picture
writer.writePixels(0);
// The odd lines will be filled in later, so skip a line
ptr += 2 * kRenderBufWidth;
}
if (_enableScanlines)
blendScanlines<typename Writer::BlendScanlines>(startY, endY);
else
blendScanlines<typename Writer::BlendRegular>(startY, endY);
// For the NTSC modes we need to redo the scanline that blends with our first line
if (GfxWriter::kColors == 4096 && startY > 0) {
--startY;
if (_enableScanlines)
blendScanlines<typename GfxWriter::BlendScanlines>(startY, startY + 1);
else
blendScanlines<typename GfxWriter::BlendRegular>(startY, startY + 1);
}
g_system->copyRectToScreen(_renderBuf + startY * 2 * kRenderBufWidth + kPreRender, kRenderBufWidth * sizeof(ColorType), 0, startY * 2, kGfxWidth * 2, (endY - startY) * 2);
g_system->updateScreen();
}
template<typename ColorType, typename GfxWriter, typename TextWriter>
template<typename BlendType>
void DisplayImpl_A2<ColorType, GfxWriter, TextWriter>::blendScanlines(uint yStart, uint yEnd) {
const Graphics::PixelFormat rgbFormat = g_system->getScreenFormat();
// Note: this reads line yEnd * 2 of _renderBuf!
for (uint y = yStart; y < yEnd; ++y) {
ColorType *buf = &_renderBuf[y * 2 * kRenderBufWidth];
for (uint x = 0; x < kRenderBufWidth; ++x) {
const ColorType color1 = buf[x];
const ColorType color2 = buf[2 * kRenderBufWidth + x];
uint8 r1, g1, b1, r2, g2, b2;
rgbFormat.colorToRGB(color1, r1, g1, b1);
rgbFormat.colorToRGB(color2, r2, g2, b2);
const uint8 r3 = BlendType::blend(r1, r2);
const uint8 g3 = BlendType::blend(g1, g2);
const uint8 b3 = BlendType::blend(b1, b2);
buf[kRenderBufWidth + x] = rgbFormat.RGBToColor(r3, g3, b3);
}
}
}
template<typename ColorType, typename GfxWriter, typename TextWriter>
void DisplayImpl_A2<ColorType, GfxWriter, TextWriter>::renderText() {
if (_mode == kModeGraphics)
return;
_blink = (g_system->getMillis() / 270) & 1;
if (_mode == kModeMixed && _enableColor && !_enableMonoText)
render<TextReader>(_writerColor);
else
render<TextReader>(_writerMono);
}
template<typename ColorType, typename GfxWriter, typename TextWriter>
void DisplayImpl_A2<ColorType, GfxWriter, TextWriter>::renderGraphics() {
if (_mode == kModeText)
return;
render<GfxReader>(_writerColor);
}
Display_A2::Display_A2() :
_frameBuf(nullptr),
_showCursor(false),
_enableColor(false),
_enableScanlines(false),
_enableMonoText(false),
_blink(false) { }
Display_A2::~Display_A2() {
delete[] _frameBuf;
}
void Display_A2::init() {
createTextBuffer(Display_A2::kTextWidth, Display_A2::kTextHeight);
_frameBuf = new byte[Display_A2::kGfxSize]();
_enableColor = ConfMan.getBool("color");
_enableScanlines = ConfMan.getBool("scanlines");
_enableMonoText = ConfMan.getBool("monotext");
}
void Display_A2::loadFrameBuffer(Common::ReadStream &stream, byte *dst) const {
for (uint j = 0; j < 8; ++j) {
for (uint i = 0; i < 8; ++i) {
stream.read(dst, Display_A2::kGfxPitch);
dst += Display_A2::kGfxPitch * 64;
stream.read(dst, Display_A2::kGfxPitch);
dst += Display_A2::kGfxPitch * 64;
stream.read(dst, Display_A2::kGfxPitch);
stream.readUint32LE();
stream.readUint32LE();
dst -= Display_A2::kGfxPitch * 120;
}
dst -= Display_A2::kGfxPitch * 63;
}
if (stream.eos() || stream.err())
error("Failed to read frame buffer");
}
void Display_A2::loadFrameBuffer(Common::ReadStream &stream) {
loadFrameBuffer(stream, _frameBuf);
}
void Display_A2::putPixel(const Common::Point &p, byte color) {
const byte offset = p.x / 7;
byte mask = 0x80 | (1 << (p.x % 7));
// Since white and black are in both palettes, we leave
// the palette bit alone
if ((color & 0x7f) == 0x7f || (color & 0x7f) == 0)
mask &= 0x7f;
// Adjust colors starting with bits '01' or '10' for
// odd offsets
if (offset & 1) {
byte c = color << 1;
if (c >= 0x40 && c < 0xc0)
color ^= 0x7f;
}
writeFrameBuffer(p, color, mask);
}
void Display_A2::setPixelByte(const Common::Point &p, byte color) {
assert(p.x >= 0 && p.x < Display_A2::kGfxWidth && p.y >= 0 && p.y < Display_A2::kGfxHeight);
_frameBuf[p.y * Display_A2::kGfxPitch + p.x / 7] = color;
}
void Display_A2::setPixelBit(const Common::Point &p, byte color) {
writeFrameBuffer(p, color, 1 << (p.x % 7));
}
void Display_A2::setPixelPalette(const Common::Point &p, byte color) {
writeFrameBuffer(p, color, 0x80);
}
byte Display_A2::getPixelByte(const Common::Point &p) const {
assert(p.x >= 0 && p.x < Display_A2::kGfxWidth && p.y >= 0 && p.y < Display_A2::kGfxHeight);
return _frameBuf[p.y * Display_A2::kGfxPitch + p.x / 7];
}
bool Display_A2::getPixelBit(const Common::Point &p) const {
assert(p.x >= 0 && p.x < Display_A2::kGfxWidth && p.y >= 0 && p.y < Display_A2::kGfxHeight);
const byte *b = _frameBuf + p.y * Display_A2::kGfxPitch + p.x / 7;
return *b & (1 << (p.x % 7));
}
void Display_A2::clear(byte color) {
byte val = 0;
const byte c = color << 1;
if (c >= 0x40 && c < 0xc0)
val = 0x7f;
for (uint i = 0; i < Display_A2::kGfxSize; ++i) {
_frameBuf[i] = color;
color ^= val;
}
}
// FIXME: This does not currently update the surfaces
void Display_A2::printChar(char c) {
if (c == Display_A2::asciiToNative('\r'))
_cursorPos = (_cursorPos / Display_A2::kTextWidth + 1) * Display_A2::kTextWidth;
else if (c == Display_A2::asciiToNative('\a')) {
renderText();
static_cast<AdlEngine *>(g_engine)->bell();
} else if ((byte)c < 0x80 || (byte)c >= 0xa0) {
setCharAtCursor(c);
++_cursorPos;
}
if (_cursorPos == Display_A2::kTextWidth * Display_A2::kTextHeight)
scrollUp();
}
void Display_A2::showCursor(bool enable) {
_showCursor = enable;
}
void Display_A2::writeFrameBuffer(const Common::Point &p, byte color, byte mask) {
assert(p.x >= 0 && p.x < Display_A2::kGfxWidth && p.y >= 0 && p.y < Display_A2::kGfxHeight);
byte *b = _frameBuf + p.y * Display_A2::kGfxPitch + p.x / 7;
color ^= *b;
color &= mask;
*b ^= color;
}
template<typename ColorType>
static Display_A2 *Display_A2_create_helper() {
const bool ntsc = ConfMan.getBool("ntsc");
const bool color = ConfMan.getBool("color");
const bool monotext = ConfMan.getBool("monotext");
typedef PixelWriterMono<ColorType, 0xff, 0xff, 0xff> PixelWriterMonoWhite;
typedef PixelWriterMono<ColorType, 0x00, 0xc0, 0x00> PixelWriterMonoGreen;
if (ntsc) {
if (color) {
if (monotext)
return new DisplayImpl_A2<ColorType, PixelWriterColorNTSC<ColorType>, PixelWriterMonoWhite>;
else
return new DisplayImpl_A2<ColorType, PixelWriterColorNTSC<ColorType>, PixelWriterMonoNTSC<ColorType> >;
} else {
if (monotext)
return new DisplayImpl_A2<ColorType, PixelWriterMonoNTSC<ColorType>, PixelWriterMonoWhite>;
else
return new DisplayImpl_A2<ColorType, PixelWriterMonoNTSC<ColorType>, PixelWriterMonoNTSC<ColorType> >;
}
}
if (color)
return new DisplayImpl_A2<ColorType, PixelWriterColor<ColorType>, PixelWriterMonoWhite>;
else
return new DisplayImpl_A2<ColorType, PixelWriterMonoGreen, PixelWriterMonoGreen>;
}
Display_A2 *Display_A2_create() {
initGraphics(Display_A2::kGfxWidth * 2, Display_A2::kGfxHeight * 2, nullptr);
debugN(1, "Initialized graphics with format: %s", g_system->getScreenFormat().toString().c_str());
const uint bpp = g_system->getScreenFormat().bytesPerPixel;
if (bpp == 4)
return Display_A2_create_helper<uint32>();
else if (bpp == 2)
return Display_A2_create_helper<uint16>();
else
return nullptr;
}
} // End of namespace Adl