From cd4c54400e7c42acbd1cff855925d1ff974f7297 Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Thu, 26 Dec 2019 17:28:55 -0330 Subject: [PATCH] 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. --- src/common/tv_filters/AtariNTSC.cxx | 56 ++++++++----- src/common/tv_filters/AtariNTSC.hxx | 39 +++++---- src/common/tv_filters/NTSCFilter.cxx | 2 +- src/common/tv_filters/NTSCFilter.hxx | 24 ++---- src/emucore/Console.cxx | 32 +++---- src/emucore/Console.hxx | 19 +++-- src/emucore/FBSurface.cxx | 18 ++-- src/emucore/FBSurface.hxx | 11 ++- src/emucore/FrameBuffer.cxx | 84 +++++++++++-------- src/emucore/FrameBuffer.hxx | 33 ++++---- src/emucore/FrameBufferConstants.hxx | 8 ++ src/emucore/TIASurface.cxx | 46 +++++----- src/emucore/TIASurface.hxx | 6 +- .../frame-manager/AbstractFrameManager.hxx | 2 +- 14 files changed, 202 insertions(+), 178 deletions(-) diff --git a/src/common/tv_filters/AtariNTSC.cxx b/src/common/tv_filters/AtariNTSC.cxx index 9fa36eaf9..1869067ec 100644 --- a/src/common/tv_filters/AtariNTSC.cxx +++ b/src/common/tv_filters/AtariNTSC.cxx @@ -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(c >> 16), + gc = static_cast(c >> 8), + bc = static_cast(c), + rp = static_cast(p >> 16), + gp = static_cast(p >> 8), + bp = static_cast(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]; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/tv_filters/AtariNTSC.hxx b/src/common/tv_filters/AtariNTSC.hxx index c2a126c22..184afdc86 100644 --- a/src/common/tv_filters/AtariNTSC.hxx +++ b/src/common/tv_filters/AtariNTSC.hxx @@ -43,15 +43,14 @@ #include #include +#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 myRGBPalette; + std::array, palette_size> myColorTable; + PhosphorLUT myPhosphorLUT; // Rendering threads unique_ptr 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. diff --git a/src/common/tv_filters/NTSCFilter.cxx b/src/common/tv_filters/NTSCFilter.cxx index ced7741d2..571a2e609 100644 --- a/src/common/tv_filters/NTSCFilter.cxx +++ b/src/common/tv_filters/NTSCFilter.cxx @@ -61,7 +61,7 @@ string NTSCFilter::setPreset(Preset preset) default: return msg; } - myNTSC.initialize(mySetup, myTIAPalette); + myNTSC.initialize(mySetup); return msg; } diff --git a/src/common/tv_filters/NTSCFilter.hxx b/src/common/tv_filters/NTSCFilter.hxx index 3530b877b..0b98c75f0 100644 --- a/src/common/tv_filters/NTSCFilter.hxx +++ b/src/common/tv_filters/NTSCFilter.hxx @@ -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; diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index 2caa18d51..fca244bde 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -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 diff --git a/src/emucore/Console.hxx b/src/emucore/Console.hxx index 6c3c86462..5a4eaeb67 100644 --- a/src/emucore/Console.hxx +++ b/src/emucore/Console.hxx @@ -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 diff --git a/src/emucore/FBSurface.cxx b/src/emucore/FBSurface.cxx index f9f57cb7e..f89c83d4d 100644 --- a/src/emucore/FBSurface.cxx +++ b/src/emucore/FBSurface.cxx @@ -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(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 }; diff --git a/src/emucore/FBSurface.hxx b/src/emucore/FBSurface.hxx index d3eb5d35d..625977b66 100644 --- a/src/emucore/FBSurface.hxx +++ b/src/emucore/FBSurface.hxx @@ -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 diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index 2a7a53e08..d426c0abe 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -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 diff --git a/src/emucore/FrameBuffer.hxx b/src/emucore/FrameBuffer.hxx index 7e7885457..67235b9b6 100644 --- a/src/emucore/FrameBuffer.hxx +++ b/src/emucore/FrameBuffer.hxx @@ -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> 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 diff --git a/src/emucore/FrameBufferConstants.hxx b/src/emucore/FrameBufferConstants.hxx index 22e5e5d84..cccb230b0 100644 --- a/src/emucore/FrameBufferConstants.hxx +++ b/src/emucore/FrameBufferConstants.hxx @@ -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; +using UIPaletteArray = std::array; +using FullPaletteArray = std::array; + +// Lookup table for phosphor mode, for generating corresponding palette +using PhosphorLUT = std::array, kColor>; + // Text alignment modes for drawString() enum class TextAlign { Left, diff --git a/src/emucore/TIASurface.cxx b/src/emucore/TIASurface.cxx index bb7a7c0b1..7b1ef3921 100644 --- a/src/emucore/TIASurface.cxx +++ b/src/emucore/TIASurface.cxx @@ -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(c >> 16), + gc = static_cast(c >> 8), + bc = static_cast(c), + rp = static_cast(p >> 16), + gp = static_cast(p >> 8), + bp = static_cast(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(c >> 16), + gc = static_cast(c >> 8), + bc = static_cast(c), + rp = static_cast(p >> 16), + gp = static_cast(p >> 8), + bp = static_cast(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; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/TIASurface.hxx b/src/emucore/TIASurface.hxx index 744e30f9e..1fd326cce 100644 --- a/src/emucore/TIASurface.hxx +++ b/src/emucore/TIASurface.hxx @@ -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; diff --git a/src/emucore/tia/frame-manager/AbstractFrameManager.hxx b/src/emucore/tia/frame-manager/AbstractFrameManager.hxx index e37aa5488..9804f0603 100644 --- a/src/emucore/tia/frame-manager/AbstractFrameManager.hxx +++ b/src/emucore/tia/frame-manager/AbstractFrameManager.hxx @@ -28,7 +28,7 @@ class AbstractFrameManager : public Serializable { public: - using callback = std::function; + using callback = std::function; public: