aboutsummaryrefslogtreecommitdiff
path: root/engines/adl
diff options
context:
space:
mode:
authorWalter van Niftrik2019-08-01 00:32:11 +0200
committerWalter van Niftrik2019-08-12 02:18:34 +0200
commit32824ea83f05ee28be5816c30ff14ba57b84ade1 (patch)
tree746f6f3329c32e7587a177426d9c7e20647105d6 /engines/adl
parentd5c7e9d2073c54ed284e1aa4e532f36bf68c3818 (diff)
downloadscummvm-rg350-32824ea83f05ee28be5816c30ff14ba57b84ade1.tar.gz
scummvm-rg350-32824ea83f05ee28be5816c30ff14ba57b84ade1.tar.bz2
scummvm-rg350-32824ea83f05ee28be5816c30ff14ba57b84ade1.zip
ADL: Improve color accuracy
This adds two new display modes to replace the old one. One is a 16-color mode and the other does TV "emulation" based on code in AppleWin. Both of these modes should deliver more accurate colors, including NTSC artifact colors.
Diffstat (limited to 'engines/adl')
-rw-r--r--engines/adl/configure.engine2
-rw-r--r--engines/adl/detection.cpp59
-rw-r--r--engines/adl/display_a2.cpp720
-rw-r--r--engines/adl/display_a2.h56
4 files changed, 521 insertions, 316 deletions
diff --git a/engines/adl/configure.engine b/engines/adl/configure.engine
index 8abee75533..c138b64943 100644
--- a/engines/adl/configure.engine
+++ b/engines/adl/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine adl "ADL" yes "" "" "highres"
+add_engine adl "ADL" yes "" "" "16bit highres"
diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp
index 52807cab09..45cd73c509 100644
--- a/engines/adl/detection.cpp
+++ b/engines/adl/detection.cpp
@@ -41,13 +41,25 @@ namespace Adl {
#define GAMEOPTION_COLOR_DEFAULT_OFF GUIO_GAMEOPTIONS1
#define GAMEOPTION_SCANLINES GUIO_GAMEOPTIONS2
#define GAMEOPTION_COLOR_DEFAULT_ON GUIO_GAMEOPTIONS3
+#define GAMEOPTION_NTSC GUIO_GAMEOPTIONS4
+#define GAMEOPTION_MONO_TEXT GUIO_GAMEOPTIONS5
static const ADExtraGuiOptionsMap optionsList[] = {
{
+ GAMEOPTION_NTSC,
+ {
+ _s("TV emulation"),
+ _s("Emulate composite output to an NTSC TV"),
+ "ntsc",
+ true
+ }
+ },
+
+ {
GAMEOPTION_COLOR_DEFAULT_OFF,
{
- _s("Color mode"),
- _s("Use color graphics"),
+ _s("Color graphics"),
+ _s("Use color graphics instead of monochrome"),
"color",
false
}
@@ -56,8 +68,8 @@ static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_COLOR_DEFAULT_ON,
{
- _s("Color mode"),
- _s("Use color graphics"),
+ _s("Color graphics"),
+ _s("Use color graphics instead of monochrome"),
"color",
true
}
@@ -66,16 +78,29 @@ static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_SCANLINES,
{
- _s("Scanlines"),
_s("Show scanlines"),
+ _s("Darken every other scanline to mimic the look of a CRT"),
"scanlines",
false
}
},
+ {
+ GAMEOPTION_MONO_TEXT,
+ {
+ _s("Always use sharp monochrome text"),
+ _s("Do not emulate NTSC artifacts for text"),
+ "monotext",
+ true
+ }
+ },
+
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
+#define DEFAULT_OPTIONS GUIO4(GAMEOPTION_NTSC, GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_MONO_TEXT, GAMEOPTION_SCANLINES)
+#define MH_OPTIONS GUIO4(GAMEOPTION_NTSC, GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_MONO_TEXT, GAMEOPTION_SCANLINES)
+
static const PlainGameDescriptor adlGames[] = {
{ "hires0", "Hi-Res Adventure #0: Mission Asteroid" },
{ "hires1", "Hi-Res Adventure #1: Mystery House" },
@@ -105,7 +130,7 @@ static const AdlGameDescription gameFileDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
+ MH_OPTIONS
},
GAME_TYPE_HIRES1,
GAME_VER_HR1_SIMI
@@ -121,7 +146,7 @@ static const AdlGameDescription gameFileDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
+ MH_OPTIONS
},
GAME_TYPE_HIRES1,
GAME_VER_HR1_COARSE
@@ -138,7 +163,7 @@ static const AdlGameDescription gameFileDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
+ MH_OPTIONS
},
GAME_TYPE_HIRES1,
GAME_VER_HR1_PD
@@ -157,7 +182,7 @@ static const AdlGameDescription gameDiskDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
+ MH_OPTIONS
},
GAME_TYPE_HIRES1,
GAME_VER_HR1_COARSE
@@ -172,7 +197,7 @@ static const AdlGameDescription gameDiskDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
+ MH_OPTIONS
},
GAME_TYPE_HIRES1,
GAME_VER_HR1_PD
@@ -187,7 +212,7 @@ static const AdlGameDescription gameDiskDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
+ DEFAULT_OPTIONS
},
GAME_TYPE_HIRES2,
GAME_VER_NONE
@@ -202,7 +227,7 @@ static const AdlGameDescription gameDiskDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
+ DEFAULT_OPTIONS
},
GAME_TYPE_HIRES0,
GAME_VER_NONE
@@ -217,7 +242,7 @@ static const AdlGameDescription gameDiskDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
+ DEFAULT_OPTIONS
},
GAME_TYPE_HIRES3,
GAME_VER_NONE
@@ -233,7 +258,7 @@ static const AdlGameDescription gameDiskDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
+ DEFAULT_OPTIONS
},
GAME_TYPE_HIRES4,
GAME_VER_NONE
@@ -277,7 +302,7 @@ static const AdlGameDescription gameDiskDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
+ DEFAULT_OPTIONS
},
GAME_TYPE_HIRES5,
GAME_VER_NONE
@@ -295,7 +320,7 @@ static const AdlGameDescription gameDiskDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
+ DEFAULT_OPTIONS
},
GAME_TYPE_HIRES6,
GAME_VER_NONE
@@ -313,7 +338,7 @@ static const AdlGameDescription gameDiskDescriptions[] = {
Common::EN_ANY,
Common::kPlatformApple2,
ADGF_NO_FLAGS,
- GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
+ DEFAULT_OPTIONS
},
GAME_TYPE_HIRES6,
GAME_VER_NONE
diff --git a/engines/adl/display_a2.cpp b/engines/adl/display_a2.cpp
index e738e77154..0471b66146 100644
--- a/engines/adl/display_a2.cpp
+++ b/engines/adl/display_a2.cpp
@@ -20,11 +20,18 @@
*
*/
+// 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/palette.h"
@@ -37,376 +44,457 @@
namespace Adl {
-#define COLOR_PALETTE_ENTRIES 8
-static const byte colorPalette[COLOR_PALETTE_ENTRIES * 3] = {
- 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff,
- 0xc7, 0x34, 0xff,
- 0x38, 0xcb, 0x00,
- 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff,
- 0x0d, 0xa1, 0xff,
- 0xf2, 0x5e, 0x00
-};
+#define NTSC_REMOVE_BLACK_GHOSTING
+// #define NTSC_REMOVE_WHITE_RINGING
-// Opacity of the optional scanlines (percentage)
-#define SCANLINE_OPACITY 75
+// 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 } // >?
+};
-// Corresponding color in second palette
-#define PAL2(X) ((X) | 0x04)
+struct LineDoubleBright {
+ static uint8 blend(uint8 c1, uint8 c2) {
+ return c1;
+ }
+};
-// Alternate color for odd pixel rows (for scanlines)
-#define ALTCOL(X) ((X) | 0x08)
+struct LineDoubleDim {
+ static uint8 blend(uint8 c1, uint8 c2) {
+ return (c1 >> 1) + (c1 >> 2);
+ }
+};
-// Green monochrome palette
-#define MONO_PALETTE_ENTRIES 2
-static const byte monoPalette[MONO_PALETTE_ENTRIES * 3] = {
- 0x00, 0x00, 0x00,
- 0x00, 0xc0, 0x01
+struct BlendBright {
+ static uint8 blend(uint8 c1, uint8 c2) {
+ return (c1 + c2) >> 1;
+ }
};
-// Uppercase-only Apple II font (manually created).
-static const byte font[64][5] = {
- { 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 } // >?
+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;
-static byte processColorBits(uint16 &bits, bool &odd, bool secondPal) {
- byte color = 0;
-
- switch (bits & 0x7) {
- case 0x3: // 011 (white)
- case 0x6: // 110
- case 0x7: // 111
- color = 1;
- break;
- case 0x2: // 010 (color)
- color = 2 + odd;
- break;
- case 0x5: // 101 (color)
- color = 2 + !odd;
+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;
}
- if (secondPal)
- color = PAL2(color);
+ 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;
+ }
+ }
- odd = !odd;
- bits >>= 1;
+protected:
+ ColorType *_ptr;
+ Graphics::PixelFormat _format;
+ uint _phase;
+ uint _window;
+};
- return color;
-}
+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);
+ }
+ }
+ }
-static void renderPixelRowColor(byte *dst, byte *src) {
- uint16 bits = (src[0] & 0x7f) << 1;
- byte pal = src[0] >> 7;
+ // >> 2 to synchronize rendering output with NTSC
+ ColorType getColor() { return _colors[this->_phase][(this->_window >> 2) & (kColors - 1)]; }
- if (pal != 0)
- *dst++ = 0;
+private:
+ ColorType _colors[kColorPhases][kColors];
+};
- bool odd = false;
+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;
- for (uint i = 0; i < Display_A2::kGfxPitch; ++i) {
- if (i != Display_A2::kGfxPitch - 1) {
- bits |= (src[i + 1] & 0x7f) << 8;
- pal |= (src[i + 1] >> 7) << 1;
- }
+ typedef LineDoubleBright BlendRegular;
+ typedef LineDoubleDim BlendScanlines;
- // For the first 6 bits in the block we draw two pixels
- for (uint j = 0; j < 6; ++j) {
- byte color = processColorBits(bits, odd, pal & 1);
- *dst++ = color;
- *dst++ = color;
- }
+ PixelWriterMono() {
+ _colors[0] = this->_format.RGBToColor(0, 0, 0);
+ _colors[1] = this->_format.RGBToColor(R, G, B);
+ }
- // Last bit of the block, draw one, two or three pixels
- byte color = processColorBits(bits, odd, pal & 1);
-
- // Draw the first pixel
- *dst++ = color;
-
- switch (pal) {
- case 0x0:
- case 0x3:
- // If palette stays the same, draw a second pixel
- *dst++ = color;
- break;
- case 0x2:
- // If we're moving from first to second palette,
- // draw a second pixel, and a third in the second
- // palette.
- *dst++ = color;
- *dst++ = PAL2(color);
- }
+ ColorType getColor() { return _colors[(this->_window >> 3) & (kColors - 1)]; }
- pal >>= 1;
- }
-}
+private:
+ ColorType _colors[kColors];
+};
-static void renderPixelRowMono(byte *dst, byte *src) {
- byte pal = src[0] >> 7;
+static double filterChroma(double z) {
+ static double x[3] = {0, 0, 0};
+ static double y[3] = {0, 0, 0};
- if (pal != 0)
- *dst++ = 0;
+ x[0] = x[1];
+ x[1] = x[2];
+ x[2] = z / 7.438011255;
- for (uint i = 0; i < Display_A2::kGfxPitch; ++i) {
- if (i != Display_A2::kGfxPitch - 1)
- pal |= (src[i + 1] >> 7) << 1;
+ y[0] = y[1];
+ y[1] = y[2];
+ y[2] = -x[0] + x[2] + (-0.7318893645 * y[0]) + (1.2336442711 * y[1]);
- for (uint j = 0; j < 6; ++j) {
- bool color = src[i] & (1 << j);
- *dst++ = color;
- *dst++ = color;
- }
+ return y[2];
+}
- bool color = src[i] & (1 << 6);
+static double filterLuma(double z) {
+ static double x[3] = {0, 0, 0};
+ static double y[3] = {0, 0, 0};
- *dst++ = color;
+ x[0] = x[1];
+ x[1] = x[2];
+ x[2] = z / 13.71331570;
- switch (pal) {
- case 0x0:
- case 0x3:
- *dst++ = color;
- break;
- case 0x2:
- *dst++ = color;
- *dst++ = color;
- }
+ 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]);
- pal >>= 1;
- }
+ return y[2];
}
-static void copyEvenSurfaceRows(Graphics::Surface &surf) {
- byte *src = (byte *)surf.getPixels();
+static double filterSignal(double z) {
+ static double x[3] = {0, 0, 0};
+ static double y[3] = {0, 0, 0};
- for (uint y = 0; y < surf.h / 2u; ++y) {
- byte *dst = src + surf.pitch;
- for (uint x = 0; x < surf.w; ++x)
- dst[x] = ALTCOL(src[x]);
- src += surf.pitch * 2;
- }
+ 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];
}
-class Display_A2_Monitor : public Display_A2 {
+template<typename ColorType>
+class PixelWriterColorNTSC : public PixelWriter<ColorType, PixelWriterColorNTSC<ColorType> > {
public:
- Display_A2_Monitor();
- ~Display_A2_Monitor();
-
- enum {
- kSplitHeight = 64
- };
+ 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(r64, 0.0, 1.0) * 255;
+ uint8 g = CLIP(g64, 0.0, 1.0) * 255;
+ uint8 b = CLIP(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);
+ }
+ }
+ }
- void init() override;
- void renderText() override;
- void renderGraphics() override;
+ ColorType getColor() { return _colors[this->_phase][(this->_window >> 1) & (kColors - 1)]; }
private:
- void updateTextSurface();
- void updateGfxSurface();
- void drawChar(byte c, int x, int y);
- void createFont();
- void showScanlines(bool enable);
- void createSurfaces(uint gfxWidth, uint gfxHeight);
-
- Graphics::Surface *_textSurface;
- Graphics::Surface *_gfxSurface;
- Graphics::Surface *_font;
- bool _scanlines;
- bool _monochrome;
+ ColorType _colors[kColorPhases][kColors];
};
-Display_A2_Monitor::Display_A2_Monitor() :
- _textSurface(nullptr),
- _gfxSurface(nullptr),
- _font(nullptr),
- _scanlines(false),
- _monochrome(false) { }
-
-Display_A2_Monitor::~Display_A2_Monitor() {
- if (_font) {
- _font->free();
- delete _font;
- }
+template<typename ColorType>
+class PixelWriterMonoNTSC : public PixelWriter<ColorType, PixelWriterMonoNTSC<ColorType> > {
+public:
+ static const uint kColors = 4096;
- _textSurface->free();
- delete _textSurface;
+ typedef BlendBright BlendRegular;
+ typedef BlendDim BlendScanlines;
- _gfxSurface->free();
- delete _gfxSurface;
-}
+ PixelWriterMonoNTSC() {
+ for (uint s = 0; s < kColors; ++s) {
+ uint t = s;
+ double y;
-void Display_A2_Monitor::createSurfaces(uint gfxWidth, uint gfxHeight) {
- _gfxSurface = new Graphics::Surface;
- _gfxSurface->create(gfxWidth, gfxHeight, Graphics::PixelFormat::createFormatCLUT8());
- _textSurface = new Graphics::Surface;
- _textSurface->create(gfxWidth, gfxHeight, Graphics::PixelFormat::createFormatCLUT8());
-}
+ for (uint n = 0; n < 12; ++n) {
+ double z = (double)(0 != (t & 0x800));
+ t = t << 1;
-void Display_A2_Monitor::init() {
- Display_A2::init();
+ for (uint k = 0; k < 2; k++ ) {
+ const double zz = filterSignal(z);
+ double c = filterChroma(zz);
+ y = filterLuma(zz - c);
+ }
+ }
- // We need 2x scaling to properly render the half-pixel shift
- // of the second palette
- createSurfaces(Display_A2::kGfxWidth * 2, Display_A2::kGfxHeight * 2);
+ const uint8 brightness = CLIP(y, 0.0, 1.0) * 255;
+ _colors[s] = this->_format.RGBToColor(brightness, brightness, brightness);
+ }
+ }
- _monochrome = !ConfMan.getBool("color");
- _scanlines = ConfMan.getBool("scanlines");
+ ColorType getColor() { return _colors[(this->_window >> 1) & (kColors - 1)]; }
- if (_monochrome)
- g_system->getPaletteManager()->setPalette(monoPalette, 0, MONO_PALETTE_ENTRIES);
- else
- g_system->getPaletteManager()->setPalette(colorPalette, 0, COLOR_PALETTE_ENTRIES);
+private:
+ ColorType _colors[kColors];
+};
- createFont();
+template<typename ColorType, typename GfxWriter, typename TextWriter>
+class DisplayImpl_A2 : public Display_A2 {
+public:
+ DisplayImpl_A2();
+ ~DisplayImpl_A2();
- showScanlines(_scanlines);
-}
+ void renderText() override;
+ void renderGraphics() override;
-void Display_A2_Monitor::updateGfxSurface() {
- byte *src = _frameBuf;
- byte *dst = (byte *)_gfxSurface->getPixels();
+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
+ };
- for (uint i = 0; i < Display_A2::kGfxHeight; ++i) {
- if (_monochrome)
- renderPixelRowMono(dst, src);
- else
- renderPixelRowColor(dst, src);
- src += Display_A2::kGfxPitch;
- dst += _gfxSurface->pitch * 2;
- }
+ template<typename BlendFunc>
+ void blendScanlines(uint yStart, uint yEnd);
- copyEvenSurfaceRows(*_gfxSurface);
-}
+ template<typename Reader, typename Writer>
+ void render(Writer &writer);
-void Display_A2_Monitor::updateTextSurface() {
- for (uint row = 0; row < 24; ++row)
- for (uint col = 0; col < Display_A2::kTextWidth; ++col) {
- uint charPos = row * Display_A2::kTextWidth + col;
- char c = _textBuf[row * Display_A2::kTextWidth + col];
+ ColorType *_renderBuf;
+ uint16 _doublePixelMasks[128];
- if (charPos == _cursorPos && _showCursor)
- c = (c & 0x3f) | 0x40;
+ GfxWriter _writerColor;
+ TextWriter _writerMono;
+};
- Common::Rect r(7 * 2, 8 * 2);
- r.translate(((c & 0x3f) % 16) * 7 * 2, (c & 0x3f) / 16 * 8 * 2);
+template<typename ColorType, typename GfxWriter, typename TextWriter>
+DisplayImpl_A2<ColorType, GfxWriter, TextWriter>::DisplayImpl_A2() : _doublePixelMasks() {
+ _renderBuf = new ColorType[kRenderBufHeight * kRenderBufWidth]();
- if (!(c & 0x80)) {
- if (!(c & 0x40) || ((g_system->getMillis() / 270) & 1))
- r.translate(0, 4 * 8 * 2);
- }
+ for (uint8 val = 0; val < ARRAYSIZE(_doublePixelMasks); ++val)
+ for (uint8 mask = 0; mask < 7; mask++)
+ if (val & (1 << mask))
+ _doublePixelMasks[val] |= 3 << (mask * 2);
+}
- _textSurface->copyRectToSurface(*_font, col * 7 * 2, row * 8 * 2, r);
- }
+template<typename ColorType, typename GfxWriter, typename TextWriter>
+DisplayImpl_A2<ColorType, GfxWriter, TextWriter>::~DisplayImpl_A2() {
+ delete[] _renderBuf;
}
-void Display_A2_Monitor::renderText() {
- updateTextSurface();
+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);
- if (_mode == Display::kModeText)
- g_system->copyRectToScreen(_textSurface->getPixels(), _textSurface->pitch, 0, 0, _textSurface->w, _textSurface->h);
- else if (_mode == Display::kModeMixed)
- g_system->copyRectToScreen(_textSurface->getBasePtr(0, _textSurface->h - kSplitHeight), _textSurface->pitch, 0, _textSurface->h - kSplitHeight, _textSurface->w, kSplitHeight);
+ ColorType *ptr = _renderBuf + startY * kRenderBufWidth * 2;
- g_system->updateScreen();
-}
+ for (uint y = startY; y < endY; ++y) {
+ uint16 lastBit = 0;
-void Display_A2_Monitor::renderGraphics() {
- updateGfxSurface();
+ writer.setupWrite(ptr);
- if (_mode == kModeGraphics)
- g_system->copyRectToScreen(_gfxSurface->getPixels(), _gfxSurface->pitch, 0, 0, _gfxSurface->w, _gfxSurface->h);
- else if (_mode == kModeMixed)
- g_system->copyRectToScreen(_gfxSurface->getPixels(), _gfxSurface->pitch, 0, 0, _gfxSurface->w, _gfxSurface->h - kSplitHeight);
+ for (uint x = 0; x < kGfxPitch; ++x) {
+ const uint8 m = Reader::getBits(this, y, x);
- g_system->updateScreen();
-}
+ uint16 bits = _doublePixelMasks[m & 0x7F];
-void Display_A2_Monitor::drawChar(byte c, int x, int y) {
- byte *buf = (byte *)_font->getPixels() + y * _font->pitch + x;
+ if (m & 0x80)
+ bits = (bits << 1) | lastBit;
- for (uint row = 0; row < 8; ++row) {
- for (uint col = 1; col < 6; ++col) {
- if (font[c][col - 1] & (1 << row)) {
- buf[col * 2] = 1;
- buf[col * 2 + 1] = 1;
- }
+ lastBit = (bits >> 13) & 1;
+
+ writer.writePixels(bits);
}
- buf += 2 * _font->pitch;
+ // 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;
}
-}
-void Display_A2_Monitor::createFont() {
- _font = new Graphics::Surface;
- _font->create(16 * 7 * 2, 4 * 8 * 2 * 2, Graphics::PixelFormat::createFormatCLUT8());
+ if (_enableScanlines)
+ blendScanlines<typename Writer::BlendScanlines>(startY, endY);
+ else
+ blendScanlines<typename Writer::BlendRegular>(startY, endY);
- for (uint i = 0; i < 4; ++i)
- for (uint j = 0; j < 16; ++j)
- drawChar(i * 16 + j, j * 7 * 2, i * 8 * 2);
+ // 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);
+ }
- // Create inverted font
- byte *buf = (byte *)_font->getPixels();
- byte *bufInv = buf + (_font->h / 2) * _font->pitch;
+ g_system->copyRectToScreen(_renderBuf + startY * 2 * kRenderBufWidth + kPreRender, kRenderBufWidth * sizeof(ColorType), 0, startY * 2, kGfxWidth * 2, (endY - startY) * 2);
+ g_system->updateScreen();
+}
- for (uint row = 0; row < _font->h / 2u; row += 2) {
- for (uint col = 0; col < _font->w; ++col)
- bufInv[col] = (buf[col] ? 0 : 1);
+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();
- buf += _font->pitch * 2;
- bufInv += _font->pitch * 2;
- }
+ // 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];
- copyEvenSurfaceRows(*_font);
-}
+ uint8 r1, g1, b1, r2, g2, b2;
-void Display_A2_Monitor::showScanlines(bool enable) {
- byte pal[COLOR_PALETTE_ENTRIES * 3];
+ rgbFormat.colorToRGB(color1, r1, g1, b1);
+ rgbFormat.colorToRGB(color2, r2, g2, b2);
- g_system->getPaletteManager()->grabPalette(pal, 0, COLOR_PALETTE_ENTRIES);
+ const uint8 r3 = BlendType::blend(r1, r2);
+ const uint8 g3 = BlendType::blend(g1, g2);
+ const uint8 b3 = BlendType::blend(b1, b2);
- if (enable) {
- for (uint i = 0; i < ARRAYSIZE(pal); ++i)
- pal[i] = pal[i] * (100 - SCANLINE_OPACITY) / 100;
+ 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;
- g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES);
+ _blink = (g_system->getMillis() / 270) & 1;
+
+ if (_mode == kModeMixed && _enableColor && !_enableMonoText)
+ render<TextReader>(_writerColor);
+ else
+ render<TextReader>(_writerMono);
}
-Display_A2::Display_A2() : _frameBuf(nullptr), _showCursor(false) {
- initGraphics(Display_A2::kGfxWidth * 2, Display_A2::kGfxHeight * 2);
+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;
}
@@ -414,11 +502,14 @@ Display_A2::~Display_A2() {
void Display_A2::init() {
createTextBuffer(Display_A2::kTextWidth, Display_A2::kTextHeight);
- _frameBuf = new byte[Display_A2::kGfxSize];
- memset(_frameBuf, 0, Display_A2::kGfxSize);
+ _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) {
+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);
@@ -442,7 +533,7 @@ void Display_A2::loadFrameBuffer(Common::ReadStream &stream) {
}
void Display_A2::putPixel(const Common::Point &p, byte color) {
- byte offset = p.x / 7;
+ const byte offset = p.x / 7;
byte mask = 0x80 | (1 << (p.x % 7));
// Since white and black are in both palettes, we leave
@@ -484,14 +575,14 @@ byte Display_A2::getPixelByte(const Common::Point &p) const {
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);
- byte *b = _frameBuf + p.y * Display_A2::kGfxPitch + p.x / 7;
+ 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;
- byte c = color << 1;
+ const byte c = color << 1;
if (c >= 0x40 && c < 0xc0)
val = 0x7f;
@@ -530,6 +621,47 @@ void Display_A2::writeFrameBuffer(const Common::Point &p, byte color, byte mask)
*b ^= color;
}
-Display_A2 *Display_A2_create() { return new Display_A2_Monitor(); }
+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, new Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
+ debugN(1, "Initialized graphics with format: %s\n", 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
+ error("Graphics format uses %d bytes per pixel", bpp);
+}
} // End of namespace Adl
diff --git a/engines/adl/display_a2.h b/engines/adl/display_a2.h
index 4e84facd58..8de3a775c7 100644
--- a/engines/adl/display_a2.h
+++ b/engines/adl/display_a2.h
@@ -36,9 +36,10 @@ public:
kGfxWidth = 280,
kGfxHeight = 192,
kGfxPitch = kGfxWidth / 7,
- kGfxSize = kGfxWidth * kGfxHeight,
+ kGfxSize = kGfxPitch * kGfxHeight,
kTextWidth = 40,
- kTextHeight = 24
+ kTextHeight = 24,
+ kSplitHeight = 32
};
void init() override;
@@ -47,7 +48,7 @@ public:
uint getGfxWidth() const { return kGfxWidth; }
uint getGfxHeight() const { return kGfxHeight; }
uint getGfxPitch() const { return kGfxPitch; }
- void loadFrameBuffer(Common::ReadStream &stream, byte *dst);
+ void loadFrameBuffer(Common::ReadStream &stream, byte *dst) const ;
void loadFrameBuffer(Common::ReadStream &stream);
void putPixel(const Common::Point &p, byte color);
void setPixelByte(const Common::Point &p, byte color);
@@ -63,13 +64,60 @@ public:
void showCursor(bool enable) override;
protected:
+ class TextReader {
+ public:
+ static uint16 getBits(const Display_A2 *display, uint y, uint x) {
+ const uint charPos = (y >> 3) * kTextWidth + x;
+ byte m = display->_textBuf[charPos];
+
+ if (display->_showCursor && charPos == display->_cursorPos)
+ m = (m & 0x3f) | 0x40;
+
+ byte b = _font[m & 0x3f][y % 8];
+
+ if (!(m & 0x80) && (!(m & 0x40) || display->_blink))
+ b = ~b;
+
+ return b & 0x7f;
+ }
+
+ static uint8 getStartY(const Display_A2 *display) {
+ if (display->_mode == kModeText)
+ return 0;
+ else
+ return kGfxHeight - kSplitHeight;
+ }
+
+ static uint8 getEndY(const Display_A2 *display) { return kGfxHeight; }
+ };
+
+ class GfxReader {
+ public:
+ static uint16 getBits(const Display_A2 *display, uint y, uint x) {
+ return display->_frameBuf[y * kGfxPitch + x];
+ }
+
+ static uint8 getStartY(const Display_A2 *display) { return 0; }
+
+ static uint8 getEndY(const Display_A2 *display) {
+ if (display->_mode == kModeGraphics)
+ return kGfxHeight;
+ else
+ return kGfxHeight - kSplitHeight;
+ }
+ };
+
byte *_frameBuf;
bool _showCursor;
+ bool _enableColor;
+ bool _enableScanlines;
+ bool _enableMonoText;
+ bool _blink;
private:
void writeFrameBuffer(const Common::Point &p, byte color, byte mask);
- virtual void showScanlines(bool enable) { };
+ static const byte _font[64][8];
};
Display_A2 *Display_A2_create();