Huge refactoring of the palette-related code.

This has been developed ad-hoc over the years, with different subsystems (TIA, UI, phosphor, Blargg, etc).  This is an attempt to consolidate the code, and also move to C++-style arrays.
Still TODO is look into refactoring phosphor stuff out of TIASurface and AtariNTSC classes, since the code is exactly the same, and doesn't really belong in either.
This is a major change, so some testing is definitely required.
This commit is contained in:
Stephen Anthony 2019-12-26 17:28:55 -03:30
parent df4748417d
commit b276a1e6a7
14 changed files with 202 additions and 178 deletions

View File

@ -31,28 +31,47 @@
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AtariNTSC::initialize(const Setup& setup, const uInt8* palette)
void AtariNTSC::initialize(const Setup& setup)
{
init(myImpl, setup);
initializePalette(palette);
generateKernels();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AtariNTSC::initializePalette(const uInt8* palette)
void AtariNTSC::setPalette(const PaletteArray& palette)
{
// Palette stores R/G/B data for 'palette_size' entries
for ( uInt32 entry = 0; entry < palette_size; ++entry )
uInt8* ptr = myRGBPalette.data();
for(size_t i = 0; i < palette.size(); ++i)
{
float r = myImpl.to_float [*palette++],
g = myImpl.to_float [*palette++],
b = myImpl.to_float [*palette++];
*ptr++ = (palette[i] >> 16) & 0xff;
*ptr++ = (palette[i] >> 8) & 0xff;
*ptr++ = palette[i] & 0xff;
}
generateKernels();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AtariNTSC::setPhosphorTable(const PhosphorLUT& table)
{
myPhosphorLUT = table;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AtariNTSC::generateKernels()
{
const uInt8* ptr = myRGBPalette.data();
for(size_t entry = 0; entry < myRGBPalette.size() / 3; ++entry)
{
float r = myImpl.to_float[*ptr++],
g = myImpl.to_float[*ptr++],
b = myImpl.to_float[*ptr++];
float y, i, q; RGB_TO_YIQ( r, g, b, y, i, q );
// Generate kernel
int ir, ig, ib; YIQ_TO_RGB( y, i, q, myImpl.to_rgb.data(), ir, ig, ib );
uInt32 rgb = PACK_RGB( ir, ig, ib );
uInt32* kernel = myColorTable[entry];
uInt32* kernel = myColorTable[entry].data();
genKernel(myImpl, y, i, q, kernel);
for ( uInt32 c = 0; c < rgb_kernel_size / 2; ++c )
@ -306,18 +325,15 @@ void AtariNTSC::renderWithPhosphorThread(const uInt8* atari_in, const uInt32 in_
inline uInt32 AtariNTSC::getRGBPhosphor(const uInt32 c, const uInt32 p) const
{
// Mix current calculated frame with previous displayed frame
const uInt8 rc = uInt8(c >> 16);
const uInt8 gc = uInt8(c >> 8);
const uInt8 bc = uInt8(c);
const uInt8 rp = uInt8(p >> 16);
const uInt8 gp = uInt8(p >> 8);
const uInt8 bp = uInt8(p);
const uInt8 rc = static_cast<uInt8>(c >> 16),
gc = static_cast<uInt8>(c >> 8),
bc = static_cast<uInt8>(c),
rp = static_cast<uInt8>(p >> 16),
gp = static_cast<uInt8>(p >> 8),
bp = static_cast<uInt8>(p);
const uInt8 rn = myPhosphorPalette[rc][rp];
const uInt8 gn = myPhosphorPalette[gc][gp];
const uInt8 bn = myPhosphorPalette[bc][bp];
return (rn << 16) | (gn << 8) | bn;
return (myPhosphorLUT[rc][rp] << 16) | (myPhosphorLUT[gc][gp] << 8) |
myPhosphorLUT[bc][bp];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -43,15 +43,14 @@
#include <cmath>
#include <thread>
#include "FrameBufferConstants.hxx"
#include "bspf.hxx"
class AtariNTSC
{
public:
static constexpr uInt32 palette_size = 256, entry_size = 2 * 14;
// By default, threading is turned off
AtariNTSC() { enableThreading(false); }
// By default, threading is turned off and palette is blank
AtariNTSC() { enableThreading(false); myRGBPalette.fill(0); }
// Image parameters, ranging from -1.0 to 1.0. Actual internal values shown
// in parenthesis and should remain fairly stable in future versions.
@ -78,18 +77,18 @@ class AtariNTSC
static const Setup TV_RGB; // crisp image
static const Setup TV_Bad; // badly adjusted TV
// Initializes and adjusts parameters.
void initialize(const Setup& setup, const uInt8* palette);
void initializePalette(const uInt8* palette);
// Initializes and adjusts parameters
// Note that this must be called before setting a palette
void initialize(const Setup& setup);
// Set palette for normal Blarrg mode
void setPalette(const PaletteArray& palette);
// Set phosphor table, for use in calculating phosphor palette
void setPhosphorTable(const PhosphorLUT& table);
// Set up threading
void enableThreading(bool enable);
// Set phosphor palette, for use in Blargg + phosphor mode
void setPhosphorPalette(uInt8 palette[256][256]) {
memcpy(myPhosphorPalette, palette, 256 * 256);
}
// Filters one or more rows of pixels. Input pixels are 8-bit Atari
// palette colors.
// In_row_width is the number of pixels to get to the next input row.
@ -112,6 +111,9 @@ class AtariNTSC
}
private:
// Generate kernels from raw RGB palette
void generateKernels();
// Threaded rendering
void renderThread(const uInt8* atari_in, const uInt32 in_width,
const uInt32 in_height, const uInt32 numThreads, const uInt32 threadNum, void* rgb_out, const uInt32 out_pitch);
@ -134,6 +136,8 @@ class AtariNTSC
PIXEL_out_chunk = 7, // number of output pixels generated per chunk
NTSC_black = 0, // palette index for black
palette_size = 256,
entry_size = 2 * 14,
alignment_count = 2,
burst_count = 1,
rescale_in = 8,
@ -166,8 +170,9 @@ class AtariNTSC
luma_cutoff = 0.20F
;
uInt32 myColorTable[palette_size][entry_size];
uInt8 myPhosphorPalette[256][256];
std::array<uInt8, palette_size*3> myRGBPalette;
std::array<std::array<uInt32, entry_size>, palette_size> myColorTable;
PhosphorLUT myPhosphorLUT;
// Rendering threads
unique_ptr<std::thread[]> myThreads;
@ -211,9 +216,9 @@ class AtariNTSC
// off a bit. Use atari_ntsc_black for unused pixels.
#define ATARI_NTSC_BEGIN_ROW( pixel0, pixel1 ) \
unsigned const atari_ntsc_pixel0_ = (pixel0);\
uInt32 const* kernel0 = myColorTable[atari_ntsc_pixel0_];\
uInt32 const* kernel0 = myColorTable[atari_ntsc_pixel0_].data();\
unsigned const atari_ntsc_pixel1_ = (pixel1);\
uInt32 const* kernel1 = myColorTable[atari_ntsc_pixel1_];\
uInt32 const* kernel1 = myColorTable[atari_ntsc_pixel1_].data();\
uInt32 const* kernelx0;\
uInt32 const* kernelx1 = kernel0
@ -221,7 +226,7 @@ class AtariNTSC
#define ATARI_NTSC_COLOR_IN( index, color ) {\
uintptr_t color_;\
kernelx##index = kernel##index;\
kernel##index = (color_ = (color), myColorTable[color_]);\
kernel##index = (color_ = (color), myColorTable[color_].data());\
}
// Generates output in the specified 32-bit format.

View File

@ -61,7 +61,7 @@ string NTSCFilter::setPreset(Preset preset)
default:
return msg;
}
myNTSC.initialize(mySetup, myTIAPalette);
myNTSC.initialize(mySetup);
return msg;
}

View File

@ -23,6 +23,7 @@ class Settings;
#include "bspf.hxx"
#include "AtariNTSC.hxx"
#include "FrameBufferConstants.hxx"
/**
This class is based on the Blargg NTSC filter code from Atari800,
@ -59,21 +60,11 @@ class NTSCFilter
uses this as a baseline for calculating its own internal palette
in YIQ format.
*/
inline void setTIAPalette(const uInt32* palette) {
uInt8* ptr = myTIAPalette;
// Set palette for normal fill
for(uInt32 i = 0; i < AtariNTSC::palette_size; ++i)
{
*ptr++ = (palette[i] >> 16) & 0xff;
*ptr++ = (palette[i] >> 8) & 0xff;
*ptr++ = palette[i] & 0xff;
}
myNTSC.initializePalette(myTIAPalette);
void setPalette(const PaletteArray& palette) {
myNTSC.setPalette(palette);
}
inline void setPhosphorPalette(uInt8 palette[256][256]) {
myNTSC.setPhosphorPalette(palette);
void setPhosphorTable(const PhosphorLUT& table) {
myNTSC.setPhosphorTable(table);
}
// The following are meant to be used strictly for toggling from the GUI
@ -146,11 +137,6 @@ class NTSCFilter
// Current preset in use
Preset myPreset;
// The base 2600 palette contains 128 normal colours
// and 128 black&white colours (PAL colour loss)
// Each colour is represented by 3 bytes, in R,G,B order
uInt8 myTIAPalette[AtariNTSC::palette_size * 3];
struct AdjustableTag {
const char* const type;
float* value;

View File

@ -469,10 +469,10 @@ void Console::setPalette(const string& type)
{
// Look at all the palettes, since we don't know which one is
// currently active
static uInt32* palettes[3][3] = {
{ &ourNTSCPalette[0], &ourPALPalette[0], &ourSECAMPalette[0] },
{ &ourNTSCPaletteZ26[0], &ourPALPaletteZ26[0], &ourSECAMPaletteZ26[0] },
{ &ourUserNTSCPalette[0], &ourUserPALPalette[0], &ourUserSECAMPalette[0] }
static constexpr PaletteArray* palettes[3][3] = {
{ &ourNTSCPalette, &ourPALPalette, &ourSECAMPalette },
{ &ourNTSCPaletteZ26, &ourPALPaletteZ26, &ourSECAMPaletteZ26 },
{ &ourUserNTSCPalette, &ourUserPALPalette, &ourUserSECAMPalette }
};
// See which format we should be using
@ -485,12 +485,12 @@ void Console::setPalette(const string& type)
paletteNum = 2;
// Now consider the current display format
const uInt32* palette =
const PaletteArray* palette =
(myDisplayFormat.compare(0, 3, "PAL") == 0) ? palettes[paletteNum][1] :
(myDisplayFormat.compare(0, 5, "SECAM") == 0) ? palettes[paletteNum][2] :
palettes[paletteNum][0];
myOSystem.frameBuffer().setPalette(palette);
myOSystem.frameBuffer().setTIAPalette(*palette);
if(myTIA->usingFixedColors())
myTIA->enableFixedColors(true);
@ -929,7 +929,7 @@ void Console::loadUserPalette()
secam[(i<<1)] = pixel;
secam[(i<<1)+1] = 0;
}
uInt32* ptr = ourUserSECAMPalette;
uInt32* ptr = ourUserSECAMPalette.data();
for(int i = 0; i < 16; ++i)
{
uInt32* s = secam;
@ -1049,7 +1049,7 @@ void Console::stateChanged(EventHandlerState state)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Console::ourNTSCPalette[256] = {
PaletteArray Console::ourNTSCPalette = {
0x000000, 0, 0x4a4a4a, 0, 0x6f6f6f, 0, 0x8e8e8e, 0,
0xaaaaaa, 0, 0xc0c0c0, 0, 0xd6d6d6, 0, 0xececec, 0,
0x484800, 0, 0x69690f, 0, 0x86861d, 0, 0xa2a22a, 0,
@ -1085,7 +1085,7 @@ uInt32 Console::ourNTSCPalette[256] = {
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Console::ourPALPalette[256] = {
PaletteArray Console::ourPALPalette = {
0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 180 0
0x6c6c6c, 0, 0x909090, 0, 0xb4b4b4, 0, 0xd8d8d8, 0, // was 0x111111..0xcccccc
0x000000, 0, 0x121212, 0, 0x242424, 0, 0x484848, 0, // 198 1
@ -1121,7 +1121,7 @@ uInt32 Console::ourPALPalette[256] = {
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Console::ourSECAMPalette[256] = {
PaletteArray Console::ourSECAMPalette = {
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff50ff, 0,
@ -1157,7 +1157,7 @@ uInt32 Console::ourSECAMPalette[256] = {
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Console::ourNTSCPaletteZ26[256] = {
PaletteArray Console::ourNTSCPaletteZ26 = {
0x000000, 0, 0x505050, 0, 0x646464, 0, 0x787878, 0,
0x8c8c8c, 0, 0xa0a0a0, 0, 0xb4b4b4, 0, 0xc8c8c8, 0,
0x445400, 0, 0x586800, 0, 0x6c7c00, 0, 0x809000, 0,
@ -1193,7 +1193,7 @@ uInt32 Console::ourNTSCPaletteZ26[256] = {
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Console::ourPALPaletteZ26[256] = {
PaletteArray Console::ourPALPaletteZ26 = {
0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
0x888888, 0, 0x9c9c9c, 0, 0xb0b0b0, 0, 0xc4c4c4, 0,
0x000000, 0, 0x4c4c4c, 0, 0x606060, 0, 0x747474, 0,
@ -1229,7 +1229,7 @@ uInt32 Console::ourPALPaletteZ26[256] = {
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Console::ourSECAMPaletteZ26[256] = {
PaletteArray Console::ourSECAMPaletteZ26 = {
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
0x7fff00, 0, 0x7fffff, 0, 0xffff3f, 0, 0xffffff, 0,
0x000000, 0, 0x2121ff, 0, 0xf03c79, 0, 0xff3cff, 0,
@ -1265,10 +1265,10 @@ uInt32 Console::ourSECAMPaletteZ26[256] = {
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Console::ourUserNTSCPalette[256] = { 0 }; // filled from external file
PaletteArray Console::ourUserNTSCPalette = { 0 }; // filled from external file
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Console::ourUserPALPalette[256] = { 0 }; // filled from external file
PaletteArray Console::ourUserPALPalette = { 0 }; // filled from external file
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Console::ourUserSECAMPalette[256] = { 0 }; // filled from external file
PaletteArray Console::ourUserSECAMPalette = { 0 }; // filled from external file

View File

@ -36,6 +36,7 @@ class AudioSettings;
#include "Props.hxx"
#include "TIAConstants.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferConstants.hxx"
#include "Serializable.hxx"
#include "EventHandlerConstants.hxx"
#include "NTSCFilter.hxx"
@ -422,19 +423,19 @@ class Console : public Serializable, public ConsoleIO
AudioSettings& myAudioSettings;
// Table of RGB values for NTSC, PAL and SECAM
static uInt32 ourNTSCPalette[256];
static uInt32 ourPALPalette[256];
static uInt32 ourSECAMPalette[256];
static PaletteArray ourNTSCPalette;
static PaletteArray ourPALPalette;
static PaletteArray ourSECAMPalette;
// Table of RGB values for NTSC, PAL and SECAM - Z26 version
static uInt32 ourNTSCPaletteZ26[256];
static uInt32 ourPALPaletteZ26[256];
static uInt32 ourSECAMPaletteZ26[256];
static PaletteArray ourNTSCPaletteZ26;
static PaletteArray ourPALPaletteZ26;
static PaletteArray ourSECAMPaletteZ26;
// Table of RGB values for NTSC, PAL and SECAM - user-defined
static uInt32 ourUserNTSCPalette[256];
static uInt32 ourUserPALPalette[256];
static uInt32 ourUserSECAMPalette[256];
static PaletteArray ourUserNTSCPalette;
static PaletteArray ourUserPALPalette;
static PaletteArray ourUserSECAMPalette;
private:
// Following constructors and assignment operators not supported

View File

@ -67,7 +67,7 @@ void FBSurface::pixel(uInt32 x, uInt32 y, ColorId color)
// Note: checkbounds() must be done in calling method
uInt32* buffer = myPixels + y * myPitch + x;
*buffer = uInt32(myPalette[color]);
*buffer = myPalette[color];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -140,7 +140,7 @@ void FBSurface::hLine(uInt32 x, uInt32 y, uInt32 x2, ColorId color)
uInt32* buffer = myPixels + y * myPitch + x;
while(x++ <= x2)
*buffer++ = uInt32(myPalette[color]);
*buffer++ = myPalette[color];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -152,7 +152,7 @@ void FBSurface::vLine(uInt32 x, uInt32 y, uInt32 y2, ColorId color)
uInt32* buffer = static_cast<uInt32*>(myPixels + y * myPitch + x);
while(y++ <= y2)
{
*buffer = uInt32(myPalette[color]);
*buffer = myPalette[color];
buffer += myPitch;
}
}
@ -219,7 +219,7 @@ void FBSurface::drawChar(const GUI::Font& font, uInt8 chr,
for(int x = 0; x < bbw; x++, mask >>= 1)
if(ptr & mask)
buffer[x] = uInt32(myPalette[color]);
buffer[x] = myPalette[color];
buffer += myPitch;
}
@ -247,7 +247,7 @@ void FBSurface::drawBitmap(const uInt32* bitmap, uInt32 tx, uInt32 ty,
uInt32 mask = 1 << (w - 1);
for(uInt32 x = 0; x < w; ++x, mask >>= 1)
if(bitmap[y] & mask)
buffer[x] = uInt32(myPalette[color]);
buffer[x] = myPalette[color];
buffer += myPitch;
}
@ -449,10 +449,4 @@ bool FBSurface::checkBounds(const uInt32 x, const uInt32 y) const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt32* FBSurface::myPalette = nullptr;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool operator==(const FBSurface::Attributes& a1, const FBSurface::Attributes& a2)
{
return a1.blendalpha == a2.blendalpha && a1.blending == a2.blending;
}
FullPaletteArray FBSurface::myPalette = { 0 };

View File

@ -329,6 +329,10 @@ class FBSurface
struct Attributes {
bool blending; // Blending is enabled
uInt32 blendalpha; // Alpha to use in blending mode (0-100%)
bool operator==(const Attributes& other) const {
return blendalpha == other.blendalpha && blending == other.blending;
}
};
/**
@ -347,7 +351,7 @@ class FBSurface
*/
virtual void applyAttributes() = 0;
static void setPalette(const uInt32* palette) { myPalette = palette; }
static void setPalette(const FullPaletteArray& palette) { myPalette = palette; }
protected:
/**
@ -370,12 +374,13 @@ class FBSurface
bool isWhiteSpace(const char s) const;
protected:
static const uInt32* myPalette;
uInt32* myPixels;
uInt32 myPitch;
Attributes myAttributes;
static FullPaletteArray myPalette;
private:
// Following constructors and assignment operators not supported
FBSurface(const FBSurface&) = delete;
@ -384,6 +389,4 @@ class FBSurface
FBSurface& operator=(FBSurface&&) = delete;
};
bool operator==(const FBSurface::Attributes& a1, const FBSurface::Attributes& a2);
#endif

View File

@ -153,27 +153,6 @@ bool FrameBuffer::initialize()
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setUIPalette()
{
// Set palette for GUI (upper area of array)
int palID = 0;
if(myOSystem.settings().getString("uipalette") == "classic")
palID = 1;
else if(myOSystem.settings().getString("uipalette") == "light")
palID = 2;
for(uInt32 i = 0, j = 256; i < kNumColors - 256; ++i, ++j)
{
uInt8 r = (ourGUIColors[palID][i] >> 16) & 0xff;
uInt8 g = (ourGUIColors[palID][i] >> 8) & 0xff;
uInt8 b = ourGUIColors[palID][i] & 0xff;
myPalette[j] = mapRGB(r, g, b);
}
FBSurface::setPalette(myPalette);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBInitStatus FrameBuffer::createDisplay(const string& title,
uInt32 width, uInt32 height,
@ -481,8 +460,8 @@ void FrameBuffer::drawFrameStats(float framesPerSecond)
myStatsMsg.surface->invalidate();
// draw scanlines
ColorId color = myOSystem.console().tia().frameBufferScanlinesLastFrame() != myLastScanlines ?
kDbgColorRed : myStatsMsg.color;
ColorId color = myOSystem.console().tia().frameBufferScanlinesLastFrame() !=
myLastScanlines ? kDbgColorRed : myStatsMsg.color;
ss
<< myOSystem.console().tia().frameBufferScanlinesLastFrame()
@ -504,7 +483,7 @@ void FrameBuffer::drawFrameStats(float framesPerSecond)
<< "% speed";
myStatsMsg.surface->drawString(f, ss.str(), xPos, yPos,
myStatsMsg.w, myStatsMsg.color, TextAlign::Left, 0, true, kBGColor);
myStatsMsg.w, myStatsMsg.color, TextAlign::Left, 0, true, kBGColor);
yPos += dy;
ss.str("");
@ -682,20 +661,48 @@ void FrameBuffer::resetSurfaces()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setPalette(const uInt32* raw_palette)
void FrameBuffer::setTIAPalette(const PaletteArray& rgb_palette)
{
// Set palette for normal fill
// Create a TIA palette from the raw RGB data
PaletteArray tia_palette;
for(int i = 0; i < 256; ++i)
{
uInt8 r = (raw_palette[i] >> 16) & 0xff;
uInt8 g = (raw_palette[i] >> 8) & 0xff;
uInt8 b = raw_palette[i] & 0xff;
uInt8 r = (rgb_palette[i] >> 16) & 0xff;
uInt8 g = (rgb_palette[i] >> 8) & 0xff;
uInt8 b = rgb_palette[i] & 0xff;
myPalette[i] = mapRGB(r, g, b);
tia_palette[i] = mapRGB(r, g, b);
}
// Remember the TIA palette; place it at the beginning of the full palette
std::copy_n(tia_palette.begin(), tia_palette.size(), myFullPalette.begin());
// Let the TIA surface know about the new palette
myTIASurface->setPalette(myPalette, raw_palette);
myTIASurface->setPalette(tia_palette, rgb_palette);
// Since the UI palette shares the TIA palette, we need to update it too
setUIPalette();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setUIPalette()
{
// Set palette for UI (upper area of full palette)
const UIPaletteArray& ui_palette =
(myOSystem.settings().getString("uipalette") == "classic") ? ourClassicUIPalette :
(myOSystem.settings().getString("uipalette") == "light") ? ourLightUIPalette :
ourStandardUIPalette;
for(size_t i = 0, j = myFullPalette.size() - ui_palette.size();
i < ui_palette.size(); ++i, ++j)
{
const uInt8 r = (ui_palette[i] >> 16) & 0xff,
g = (ui_palette[i] >> 8) & 0xff,
b = ui_palette[i] & 0xff;
myFullPalette[j] = mapRGB(r, g, b);
}
FBSurface::setPalette(myFullPalette);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1235,8 +1242,7 @@ void FrameBuffer::VideoModeList::setByStretch(FrameBuffer::VideoMode::Stretch st
kColorTitleBarLo Disabled title bar color
kColorTitleTextLo Disabled title text color
*/
uInt32 FrameBuffer::ourGUIColors[3][kNumColors-256] = {
// Standard
UIPaletteArray FrameBuffer::ourStandardUIPalette = {
{ 0x686868, 0x000000, 0xa38c61, 0xdccfa5, 0x404040, // base
0x000000, 0xac3410, 0x9f0000, 0xf0f0cf, // text
0xc9af7c, 0xf0f0cf, 0xd55941, 0xc80000, // UI elements
@ -1246,8 +1252,10 @@ uInt32 FrameBuffer::ourGUIColors[3][kNumColors-256] = {
0xc80000, 0xffff80, 0xc8c8ff, 0xc80000, // debugger
0xac3410, 0xd55941, 0xdccfa5, 0xf0f0cf, 0xa38c61, // slider
0xffffff, 0xac3410, 0xf0f0cf, 0x686868, 0xdccfa5 // other
},
// Classic
}
};
UIPaletteArray FrameBuffer::ourClassicUIPalette = {
{ 0x686868, 0x000000, 0x404040, 0x404040, 0x404040, // base
0x20a020, 0x00ff00, 0xc80000, 0x000000, // text
0x000000, 0x000000, 0x00ff00, 0xc80000, // UI elements
@ -1257,8 +1265,10 @@ uInt32 FrameBuffer::ourGUIColors[3][kNumColors-256] = {
0xc80000, 0x00ff00, 0xc8c8ff, 0xc80000, // debugger
0x20a020, 0x00ff00, 0x404040, 0x686868, 0x404040, // slider
0x00ff00, 0x20a020, 0x000000, 0x686868, 0x404040 // other
},
// Light
}
};
UIPaletteArray FrameBuffer::ourLightUIPalette = {
{ 0x808080, 0x000000, 0xc0c0c0, 0xe1e1e1, 0x333333, // base
0x000000, 0xBDDEF9, 0x0078d7, 0x000000, // text
0xf0f0f0, 0xffffff, 0x0078d7, 0x0f0f0f, // UI elements

View File

@ -102,11 +102,6 @@ class FrameBuffer
*/
bool initialize();
/**
Set palette for user interface
*/
void setUIPalette();
/**
(Re)creates the framebuffer display. This must be called before any
calls are made to derived methods.
@ -184,6 +179,19 @@ class FrameBuffer
const uInt32* data = nullptr
);
/**
Set up the TIA/emulation palette. Due to the way the palette is stored,
a call to this method implicitly calls setUIPalette() too.
@param rgb_palette The array of colors in R/G/B format
*/
void setTIAPalette(const PaletteArray& rgb_palette);
/**
Set palette for user interface.
*/
void setUIPalette();
/**
Returns the current dimensions of the framebuffer image.
Note that this will take into account the current scaling (if any)
@ -273,13 +281,6 @@ class FrameBuffer
*/
void toggleGrabMouse();
/**
Set up the TIA/emulation palette for a screen of any depth > 8.
@param raw_palette The array of colors in R/G/B format
*/
void setPalette(const uInt32* raw_palette);
/**
Informs the Framebuffer of a change in EventHandler state.
*/
@ -456,9 +457,6 @@ z
// The parent system for the framebuffer
OSystem& myOSystem;
// Color palette for TIA and UI modes
uInt32 myPalette[256+kNumColors];
private:
/**
Draw pending messages.
@ -625,8 +623,9 @@ z
// Holds a reference to all the surfaces that have been created
vector<shared_ptr<FBSurface>> mySurfaceList;
// Holds UI palette data (standard and classic colours)
static uInt32 ourGUIColors[3][kNumColors-256];
FullPaletteArray myFullPalette;
// Holds UI palette data (for each variation)
static UIPaletteArray ourStandardUIPalette, ourClassicUIPalette, ourLightUIPalette;
private:
// Following constructors and assignment operators not supported

View File

@ -102,6 +102,14 @@ static constexpr ColorId
kNone = 0 // placeholder to represent default/no color
;
// Palette for normal TIA, UI and both combined
using PaletteArray = std::array<uInt32, kColor>;
using UIPaletteArray = std::array<uInt32, kNumColors-kColor>;
using FullPaletteArray = std::array<uInt32, kNumColors>;
// Lookup table for phosphor mode, for generating corresponding palette
using PhosphorLUT = std::array<std::array<uInt8, kColor>, kColor>;
// Text alignment modes for drawString()
enum class TextAlign {
Left,

View File

@ -43,7 +43,6 @@ TIASurface::TIASurface(OSystem& system)
myUsePhosphor(false),
myPhosphorPercent(0.60f),
myScanlinesEnabled(false),
myPalette(nullptr),
mySaveSnapFlag(false)
{
// Load NTSC filter settings
@ -119,13 +118,14 @@ cerr << "SLine:\n"
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIASurface::setPalette(const uInt32* tia_palette, const uInt32* rgb_palette)
void TIASurface::setPalette(const PaletteArray& tia_palette,
const PaletteArray& rgb_palette)
{
myPalette = tia_palette;
// The NTSC filtering needs access to the raw RGB data, since it calculates
// its own internal palette
myNTSCFilter.setTIAPalette(rgb_palette);
myNTSCFilter.setPalette(rgb_palette);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -226,29 +226,27 @@ void TIASurface::enablePhosphor(bool enable, int blend)
// Precalculate the average colors for the 'phosphor' effect
if(myUsePhosphor)
{
for(int c = 255; c >= 0; c--)
for(int p = 255; p >= 0; p--)
myPhosphorPalette[c][p] = getPhosphor(uInt8(c), uInt8(p));
for(int c = 255; c >= 0; --c)
for(int p = 255; p >= 0; --p)
myPhosphorLUT[c][p] = getPhosphor(uInt8(c), uInt8(p));
myNTSCFilter.setPhosphorPalette(myPhosphorPalette);
myNTSCFilter.setPhosphorTable(myPhosphorLUT);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline uInt32 TIASurface::getRGBPhosphor(const uInt32 c, const uInt32 p) const
{
#define TO_RGB(color, red, green, blue) \
const uInt8 red = color >> 16; const uInt8 green = color >> 8; const uInt8 blue = color;
TO_RGB(c, rc, gc, bc)
TO_RGB(p, rp, gp, bp)
// Mix current calculated frame with previous displayed frame
const uInt8 rn = myPhosphorPalette[rc][rp];
const uInt8 gn = myPhosphorPalette[gc][gp];
const uInt8 bn = myPhosphorPalette[bc][bp];
const uInt8 rc = static_cast<uInt8>(c >> 16),
gc = static_cast<uInt8>(c >> 8),
bc = static_cast<uInt8>(c),
rp = static_cast<uInt8>(p >> 16),
gp = static_cast<uInt8>(p >> 8),
bp = static_cast<uInt8>(p);
return (rn << 16) | (gn << 8) | bn;
return (myPhosphorLUT[rc][rp] << 16) | (myPhosphorLUT[gc][gp] << 8) |
myPhosphorLUT[bc][bp];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -302,12 +300,16 @@ string TIASurface::effectsInfo() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline uInt32 TIASurface::averageBuffers(uInt32 bufOfs)
{
uInt32 c = myRGBFramebuffer[bufOfs];
uInt32 p = myPrevRGBFramebuffer[bufOfs];
const uInt32 c = myRGBFramebuffer[bufOfs];
const uInt32 p = myPrevRGBFramebuffer[bufOfs];
// Split into RGB values
TO_RGB(c, rc, gc, bc)
TO_RGB(p, rp, gp, bp)
const uInt8 rc = static_cast<uInt8>(c >> 16),
gc = static_cast<uInt8>(c >> 8),
bc = static_cast<uInt8>(c),
rp = static_cast<uInt8>(p >> 16),
gp = static_cast<uInt8>(p >> 8),
bp = static_cast<uInt8>(p);
// Mix current calculated buffer with previous calculated buffer (50:50)
const uInt8 rn = (rc + rp) / 2;
@ -315,7 +317,7 @@ inline uInt32 TIASurface::averageBuffers(uInt32 bufOfs)
const uInt8 bn = (bc + bp) / 2;
// return averaged value
return (rn << 16) | (gn << 8) | bn;
return (rn << 16) | (gn << 8) | bn;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -64,7 +64,7 @@ class TIASurface
@param rgb_palette The RGB components of the palette, needed for
calculating a phosphor palette
*/
void setPalette(const uInt32* tia_palette, const uInt32* rgb_palette);
void setPalette(const PaletteArray& tia_palette, const PaletteArray& rgb_palette);
/**
Get the TIA base surface for use in saving to a PNG image.
@ -202,14 +202,14 @@ class TIASurface
float myPhosphorPercent;
// Precalculated averaged phosphor colors
uInt8 myPhosphorPalette[256][256];
PhosphorLUT myPhosphorLUT;
/////////////////////////////////////////////////////////////
// Use scanlines in TIA rendering mode
bool myScanlinesEnabled;
// Palette for normal TIA rendering mode
const uInt32* myPalette;
PaletteArray myPalette;
// Flag for saving a snapshot
bool mySaveSnapFlag;

View File

@ -28,7 +28,7 @@ class AbstractFrameManager : public Serializable
{
public:
using callback = std::function<void()>;
using callback = std::function<void()>;
public: