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: