diff --git a/Changes.txt b/Changes.txt index 16364a58a..2fd89745e 100644 --- a/Changes.txt +++ b/Changes.txt @@ -25,6 +25,8 @@ * Added 'Check for Update' button to Help dialog. + * Added different mask pattern for scanline emulation (TODO: doc) + * Fixed MindLink controller. * Fixed SaveKey not working with QuadTari. diff --git a/src/common/DevSettingsHandler.hxx b/src/common/DevSettingsHandler.hxx index 8cd3e2c9f..1d2ba73aa 100644 --- a/src/common/DevSettingsHandler.hxx +++ b/src/common/DevSettingsHandler.hxx @@ -23,7 +23,7 @@ class OSystem; #include /** - This class takes care developer settings sets. + This class takes care of developer settings sets. @author Thomas Jentzsch */ @@ -33,7 +33,7 @@ class DevSettingsHandler enum SettingsSet { player, developer, - numSettings + numSets }; DevSettingsHandler(OSystem& osystem); @@ -45,40 +45,40 @@ class DevSettingsHandler protected: OSystem& myOSystem; // Emulator sets - std::array myFrameStats; - std::array myDetectedInfo; - std::array myExternAccess; - std::array myConsole; - std::array myRandomBank; - std::array myRandomizeTIA; - std::array myRandomizeRAM; - std::array myRandomizeCPU; - std::array myColorLoss; - std::array myTVJitter; - std::array myTVJitterRec; - std::array myDebugColors; - std::array myUndrivenPins; + std::array myFrameStats; + std::array myDetectedInfo; + std::array myExternAccess; + std::array myConsole; + std::array myRandomBank; + std::array myRandomizeTIA; + std::array myRandomizeRAM; + std::array myRandomizeCPU; + std::array myColorLoss; + std::array myTVJitter; + std::array myTVJitterRec; + std::array myDebugColors; + std::array myUndrivenPins; #ifdef DEBUGGER_SUPPORT - std::array myRWPortBreak; - std::array myWRPortBreak; + std::array myRWPortBreak; + std::array myWRPortBreak; #endif - std::array myThumbException; + std::array myThumbException; // TIA sets - std::array myTIAType; - std::array myPlInvPhase; - std::array myMsInvPhase; - std::array myBlInvPhase; - std::array myPFBits; - std::array myPFColor; - std::array myBKColor; - std::array myPlSwap; - std::array myBlSwap; + std::array myTIAType; + std::array myPlInvPhase; + std::array myMsInvPhase; + std::array myBlInvPhase; + std::array myPFBits; + std::array myPFColor; + std::array myBKColor; + std::array myPlSwap; + std::array myBlSwap; // States sets - std::array myTimeMachine; - std::array myStateSize; - std::array myUncompressed; - std::array myStateInterval; - std::array myStateHorizon; + std::array myTimeMachine; + std::array myStateSize; + std::array myUncompressed; + std::array myStateInterval; + std::array myStateHorizon; private: void handleEnableDebugColors(bool enable); diff --git a/src/common/PKeyboardHandler.cxx b/src/common/PKeyboardHandler.cxx index 17f70a5e8..33dc9e5b4 100644 --- a/src/common/PKeyboardHandler.cxx +++ b/src/common/PKeyboardHandler.cxx @@ -644,6 +644,8 @@ PhysicalKeyboardHandler::DefaultCommonMapping = { { Event::TogglePhosphor, KBDK_P, MOD3 }, { Event::ScanlinesDecrease, KBDK_5, KBDM_SHIFT | MOD3 }, { Event::ScanlinesIncrease, KBDK_5, MOD3 }, + { Event::PreviousScanlineMask, KBDK_6, KBDM_SHIFT | MOD3 }, + { Event::NextScanlineMask, KBDK_6, MOD3 }, { Event::PreviousPaletteAttribute, KBDK_9, KBDM_SHIFT | MOD3 }, { Event::NextPaletteAttribute, KBDK_9, MOD3 }, { Event::PaletteAttributeDecrease, KBDK_0, KBDM_SHIFT | MOD3 }, diff --git a/src/common/jsonDefinitions.hxx b/src/common/jsonDefinitions.hxx index cfea0b31f..26ab8359d 100644 --- a/src/common/jsonDefinitions.hxx +++ b/src/common/jsonDefinitions.hxx @@ -345,6 +345,8 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, { {Event::NextAttribute, "NextAttribute"}, {Event::DecreaseAttribute, "DecreaseAttribute"}, {Event::IncreaseAttribute, "IncreaseAttribute"}, + {Event::PreviousScanlineMask, "PreviousScanlineMask"}, + {Event::NextScanlineMask, "NextScanlineMask"}, {Event::ScanlinesDecrease, "ScanlinesDecrease"}, {Event::ScanlinesIncrease, "ScanlinesIncrease"}, {Event::PhosphorDecrease, "PhosphorDecrease"}, diff --git a/src/common/sdl_blitter/QisBlitter.cxx b/src/common/sdl_blitter/QisBlitter.cxx index a6633364b..cf9ea16e9 100644 --- a/src/common/sdl_blitter/QisBlitter.cxx +++ b/src/common/sdl_blitter/QisBlitter.cxx @@ -147,8 +147,14 @@ void QisBlitter::recreateTexturesIfNecessary() SDL_TextureAccess texAccess = myStaticData == nullptr ? SDL_TEXTUREACCESS_STREAMING : SDL_TEXTUREACCESS_STATIC; - myIntermediateRect.w = (myDstRect.w / mySrcRect.w) * mySrcRect.w; - myIntermediateRect.h = (myDstRect.h / mySrcRect.h) * mySrcRect.h; + if(myDstRect.w > mySrcRect.w) + myIntermediateRect.w = (myDstRect.w / mySrcRect.w) * mySrcRect.w; + else + myIntermediateRect.w = mySrcRect.w; + if(myDstRect.h > mySrcRect.h) + myIntermediateRect.h = (myDstRect.h / mySrcRect.h) * mySrcRect.h; + else + myIntermediateRect.h = mySrcRect.h; myIntermediateRect.x = 0; myIntermediateRect.y = 0; diff --git a/src/emucore/Event.hxx b/src/emucore/Event.hxx index ac1400393..17c652e29 100644 --- a/src/emucore/Event.hxx +++ b/src/emucore/Event.hxx @@ -111,6 +111,7 @@ class Event PreviousVideoMode, NextVideoMode, PreviousAttribute, NextAttribute, DecreaseAttribute, IncreaseAttribute, ScanlinesDecrease, ScanlinesIncrease, + PreviousScanlineMask, NextScanlineMask, PhosphorDecrease, PhosphorIncrease, TogglePhosphor, ToggleInter, ToggleDeveloperSet, JitterDecrease, JitterIncrease, ToggleJitter, diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index ba3ddbace..f3e1567aa 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -713,7 +713,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) case Event::ScanlinesDecrease: if(pressed) { - myOSystem.frameBuffer().tiaSurface().setScanlineIntensity(-1); + myOSystem.frameBuffer().tiaSurface().changeScanlineIntensity(-1); myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::SCANLINES); } return; @@ -721,11 +721,27 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) case Event::ScanlinesIncrease: if(pressed) { - myOSystem.frameBuffer().tiaSurface().setScanlineIntensity(+1); + myOSystem.frameBuffer().tiaSurface().changeScanlineIntensity(+1); myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::SCANLINES); } return; + case Event::PreviousScanlineMask: + if(pressed && !repeated) + { + myOSystem.frameBuffer().tiaSurface().cycleScanlineMask(-1); + myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::SCANLINE_MASK); + } + return; + + case Event::NextScanlineMask: + if(pressed && !repeated) + { + myOSystem.frameBuffer().tiaSurface().cycleScanlineMask(+1); + myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::SCANLINE_MASK); + } + return; + case Event::ToggleInter: if(pressed && !repeated) { @@ -2882,6 +2898,8 @@ EventHandler::EmulActionList EventHandler::ourEmulActionList = { { { Event::PhosphorIncrease, "Increase 'phosphor' blend", "" }, { Event::ScanlinesDecrease, "Decrease scanlines", "" }, { Event::ScanlinesIncrease, "Increase scanlines", "" }, + { Event::PreviousScanlineMask, "Switch to previous scanline mask", "" }, + { Event::NextScanlineMask, "Switch to next scanline mask", "" }, { Event::PreviousSettingGroup, "Select previous setting group", "" }, { Event::NextSettingGroup, "Select next setting group", "" }, @@ -3050,6 +3068,7 @@ const Event::EventSet EventHandler::AudioVideoEvents = { Event::PreviousAttribute, Event::NextAttribute, Event::DecreaseAttribute, Event::IncreaseAttribute, Event::PhosphorDecrease, Event::PhosphorIncrease, Event::TogglePhosphor, Event::ScanlinesDecrease, Event::ScanlinesIncrease, + Event::PreviousScanlineMask, Event::NextScanlineMask, Event::ToggleInter, }; diff --git a/src/emucore/EventHandler.hxx b/src/emucore/EventHandler.hxx index ab229a1bb..5a668629c 100644 --- a/src/emucore/EventHandler.hxx +++ b/src/emucore/EventHandler.hxx @@ -522,7 +522,7 @@ class EventHandler #else REFRESH_SIZE = 0, #endif - EMUL_ACTIONLIST_SIZE = 219 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE, + EMUL_ACTIONLIST_SIZE = 221 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE, MENU_ACTIONLIST_SIZE = 19 ; diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index 68af5a9b8..3136316bc 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -1201,7 +1201,7 @@ void FrameBuffer::switchVideoMode(int direction) } saveCurrentWindowPosition(); - if(applyVideoMode() == FBInitStatus::Success) + if(!direction || applyVideoMode() == FBInitStatus::Success) { if(fullScreen()) showTextMessage(myActiveVidMode.description); diff --git a/src/emucore/GlobalKeyHandler.cxx b/src/emucore/GlobalKeyHandler.cxx index c7ae47b16..7fdd87648 100644 --- a/src/emucore/GlobalKeyHandler.cxx +++ b/src/emucore/GlobalKeyHandler.cxx @@ -374,7 +374,8 @@ GlobalKeyHandler::SettingData GlobalKeyHandler::getSettingData(const Setting set int(NTSCFilter::Adjustables::BLEEDING), _1)}}, // Other TV effects adjustables {Setting::PHOSPHOR, {true, std::bind(&Console::changePhosphor, &myOSystem.console(), _1)}}, - {Setting::SCANLINES, {true, std::bind(&TIASurface::setScanlineIntensity, &myOSystem.frameBuffer().tiaSurface(), _1)}}, + {Setting::SCANLINES, {true, std::bind(&TIASurface::changeScanlineIntensity, &myOSystem.frameBuffer().tiaSurface(), _1)}}, + {Setting::SCANLINE_MASK, {false, std::bind(&TIASurface::cycleScanlineMask, &myOSystem.frameBuffer().tiaSurface(), _1)}}, {Setting::INTERPOLATION, {false, std::bind(&Console::toggleInter, &myOSystem.console(), _1)}}, // *** Input group *** {Setting::DIGITAL_DEADZONE, {true, std::bind(&PhysicalJoystickHandler::changeDigitalDeadZone, &joyHandler(), _1)}}, diff --git a/src/emucore/GlobalKeyHandler.hxx b/src/emucore/GlobalKeyHandler.hxx index ec9417f64..137bc75ed 100644 --- a/src/emucore/GlobalKeyHandler.hxx +++ b/src/emucore/GlobalKeyHandler.hxx @@ -70,6 +70,7 @@ class GlobalKeyHandler // Other TV effects adjustables PHOSPHOR, SCANLINES, + SCANLINE_MASK, INTERPOLATION, // *** Input group *** DIGITAL_DEADZONE, diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index d374e519f..d77d40261 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -21,6 +21,7 @@ #include "Version.hxx" #include "Logger.hxx" #include "AudioSettings.hxx" +#include "TIASurface.hxx" #include "PaletteHandler.hxx" #include "Joystick.hxx" #include "Paddles.hxx" @@ -80,6 +81,7 @@ Settings::Settings() setPermanent("tv.phosphor", "byrom"); setPermanent("tv.phosblend", "50"); setPermanent("tv.scanlines", "0"); + setPermanent("tv.scanmask", TIASurface::SETTING_STANDARD); // TV options when using 'custom' mode setPermanent("tv.sharpness", "0.0"); setPermanent("tv.resolution", "0.0"); @@ -315,6 +317,13 @@ void Settings::validate() i = getInt("tv.phosblend"); if(i < 0 || i > 100) setValue("tv.phosblend", "50"); + s = getString("tv.scanmask"); + if(s != TIASurface::SETTING_STANDARD + && s != TIASurface::SETTING_THIN + && s != TIASurface::SETTING_PIXELS + && s != TIASurface::SETTING_MAME) + setValue("tv.scanmask", TIASurface::SETTING_STANDARD); + i = getInt("tv.filter"); if(i < 0 || i > 5) setValue("tv.filter", "0"); @@ -506,6 +515,9 @@ void Settings::usage() const << " -tv.phosblend <0-100> Set default blend level in phosphor mode\n" << " -tv.scanlines <0-100> Set scanline intensity to percentage\n" << " (0 disables completely)\n" + << " -tv.scanmask \n" << " -tv.sharpness <-1.0 - 1.0> Set TV effects custom sharpness\n" << " -tv.resolution <-1.0 - 1.0> Set TV effects custom resolution\n" << " -tv.artifacts <-1.0 - 1.0> Set TV effects custom artifacts\n" diff --git a/src/emucore/TIASurface.cxx b/src/emucore/TIASurface.cxx index 65ab16997..102355e1c 100644 --- a/src/emucore/TIASurface.cxx +++ b/src/emucore/TIASurface.cxx @@ -61,16 +61,6 @@ TIASurface::TIASurface(OSystem& system) : interpolationModeFromSettings(myOSystem.settings()) ); - // Generate scanline data, and a pre-defined scanline surface - constexpr uInt32 scanHeight = TIAConstants::frameBufferHeight * 2; - std::array scanData; - for(uInt32 i = 0; i < scanHeight; i += 2) - { - scanData[i] = 0x00000000; - scanData[i+1] = 0xff000000; - } - mySLineSurface = myFB.allocateSurface(1, scanHeight, interpolationModeFromSettings(myOSystem.settings()), scanData.data()); - // Base TIA surface for use in taking snapshots in 1x mode myBaseTiaSurface = myFB.allocateSurface(TIAConstants::frameBufferWidth*2, TIAConstants::frameBufferHeight); @@ -108,8 +98,6 @@ void TIASurface::initialize(const Console& console, myTiaSurface->setDstPos(mode.imageR.x(), mode.imageR.y()); myTiaSurface->setDstSize(mode.imageR.w(), mode.imageR.h()); - mySLineSurface->setDstPos(mode.imageR.x(), mode.imageR.y()); - mySLineSurface->setDstSize(mode.imageR.w(), mode.imageR.h()); myPaletteHandler->setPalette(); @@ -129,6 +117,7 @@ void TIASurface::initialize(const Console& console, } enablePhosphor(enable, p_blend); + createScanlineSurface(); setNTSC(NTSCFilter::Preset(myOSystem.settings().getInt("tv.filter")), false); #if 0 @@ -260,14 +249,20 @@ void TIASurface::changeCurrentNTSCAdjustable(int direction) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TIASurface::setScanlineIntensity(int direction) +void TIASurface::changeScanlineIntensity(int direction) { - ostringstream buf; - uInt32 intensity = enableScanlines(direction * 2); + FBSurface::Attributes& attr = mySLineSurface->attributes(); + + attr.blendalpha += direction * 2; + attr.blendalpha = BSPF::clamp(Int32(attr.blendalpha), 0, 100); + mySLineSurface->applyAttributes(); + + uInt32 intensity = attr.blendalpha; myOSystem.settings().setValue("tv.scanlines", intensity); enableNTSC(ntscEnabled()); + ostringstream buf; if(intensity) buf << intensity << "%"; else @@ -276,15 +271,51 @@ void TIASurface::setScanlineIntensity(int direction) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 TIASurface::enableScanlines(int change) +TIASurface::ScanlineMask TIASurface::scanlineMaskType(int direction) { - FBSurface::Attributes& attr = mySLineSurface->attributes(); + const string Masks[int(ScanlineMask::NumMasks)] = { + SETTING_STANDARD, + SETTING_THIN, + SETTING_PIXELS, + SETTING_MAME + }; + int i = 0; + const string& name = myOSystem.settings().getString("tv.scanmask"); - attr.blendalpha += change; - attr.blendalpha = BSPF::clamp(Int32(attr.blendalpha), 0, 100); - mySLineSurface->applyAttributes(); + for(auto& mask : Masks) + { + if(mask == name) + { + if(direction) + { + i = BSPF::clampw(i + direction, 0, int(ScanlineMask::NumMasks) - 1); + myOSystem.settings().setValue("tv.scanmask", Masks[i]); + } + return ScanlineMask(i); + } + ++i; + } + return ScanlineMask::Standard; +} - return attr.blendalpha; +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIASurface::cycleScanlineMask(int direction) +{ + const string Names[int(ScanlineMask::NumMasks)] = { + "'Standard'", + "'Thin lines'", + "'Pixelated'", + "'MAME'" + }; + int i = int(scanlineMaskType(direction)); + + if(direction) + createScanlineSurface(); + + ostringstream msg; + + msg << "Scanline pattern " << Names[i]; + myOSystem.frameBuffer().showTextMessage(msg.str()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -297,6 +328,109 @@ void TIASurface::enablePhosphor(bool enable, int blend) } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIASurface::createScanlineSurface() +{ + struct PatternSize + { + uInt16 width{1}; + uInt16 height{1}; + uInt16 hRepeats{1}; + + explicit PatternSize(uInt16 c_width, uInt16 c_height, uInt16 c_yRepeats) + : width(c_width), height(c_height), hRepeats(c_yRepeats) + {} + PatternSize(const PatternSize& size) + : width(size.width), height(size.height), hRepeats(size.hRepeats) + {} + }; + std::array Sizes = {{ + PatternSize(1, 2, 1), + PatternSize(1, 3, 1), + PatternSize(3, 3, 2), + PatternSize(3, 4, 3) + }}; + std::array>, int(ScanlineMask::NumMasks)> Pattern = {{ + { // standard + { 0x00000000 }, + { 0xff000000 }, + }, { // thin lines + { 0x00000000 }, + { 0x00000000 }, + { 0xff000000 }, + }, { // pixelated + // orignal data from https://forum.arcadeotaku.com/posting.php?mode=quote&f=10&p=134359 + //{ 0x08ffffff, 0x02ffffff, 0x80e7e7e7 }, + //{ 0x08ffffff, 0x80e7e7e7, 0x40ffffff }, + //{ 0xff282828, 0xff282828, 0xff282828 }, + //{ 0x80e7e7e7, 0x04ffffff, 0x04ffffff }, + //{ 0x04ffffff, 0x80e7e7e7, 0x20ffffff }, + //{ 0xff282828, 0xff282828, 0xff282828 }, + // same but using RGB = 0,0,0 + { 0x08000000, 0x02000000, 0x80000000 }, + { 0x08000000, 0x80000000, 0x40000000 }, + { 0xff000000, 0xff000000, 0xff000000 }, + { 0x80000000, 0x04000000, 0x04000000 }, + { 0x04000000, 0x80000000, 0x20000000 }, + { 0xff000000, 0xff000000, 0xff000000 }, + }, { // mame + // original tile data from https://wiki.arcadeotaku.com/w/MAME_CRT_Simulation + //{ 0xffb4b4b4, 0xffa5a5a5, 0xffc3c3c3 }, + //{ 0xffffffff, 0xfff0f0f0, 0xfff0f0f0 }, + //{ 0xfff0f0f0, 0xffffffff, 0xffe1e1e1 }, + //{ 0xff000000, 0xff000000, 0xff000000 }, + //{ 0xffa5a5a5, 0xffc3c3c3, 0xffb4b4b4 }, + //{ 0xfff0f0f0, 0xfff0f0f0, 0xffffffff }, + //{ 0xffffffff, 0xffe1e1e1, 0xfff0f0f0 }, + //{ 0xff000000, 0xff000000, 0xff000000 }, + //{ 0xffc3c3c3, 0xffb4b4b4, 0xffa5a5a5 }, + //{ 0xfff0f0f0, 0xffffffff, 0xfff0f0f0 }, + //{ 0xffe1e1e1, 0xfff0f0f0, 0xffffffff }, + //{ 0xff000000, 0xff000000, 0xff000000 }, + // MAME tile RGB values inverted into alpha channel + { 0x4b000000, 0x5a000000, 0x3c000000 }, + { 0x00000000, 0x0f000000, 0x0f000000 }, + { 0x0f000000, 0x00000000, 0x1e000000 }, + { 0xff000000, 0xff000000, 0xff000000 }, + { 0x5a000000, 0x3c000000, 0x4b000000 }, + { 0x0f000000, 0x0f000000, 0x00000000 }, + { 0x00000000, 0x1e000000, 0x0f000000 }, + { 0xff000000, 0xff000000, 0xff000000 }, + { 0x3c000000, 0x4b000000, 0x5a000000 }, + { 0x0f000000, 0x00000000, 0x0f000000 }, + { 0x1e000000, 0x0f000000, 0x00000000 }, + { 0xff000000, 0xff000000, 0xff000000 }, + } + }}; + + const int mask = int(scanlineMaskType()); + const PatternSize size(Sizes[mask]); + uInt32 width{1}, height{1}; + + // Single width pattern need no x-repeats + if(size.width > 1) + width = TIAConstants::frameBufferWidth * size.width; + else + width = 1; + // TODO: use alternative mask pattern if destination is scaled smaller than mask height + height = myTIA->height() * size.height; // hRepeats are not used here + + // Copy repeated pattern into surface data + std::vectordata(width * height); + + for(uInt32 i = 0; i < width * height; ++i) + data[i] = Pattern[mask][(i / width) % (size.height * size.hRepeats)][i % size.width]; + + myFB.deallocateSurface(mySLineSurface); + mySLineSurface = myFB.allocateSurface(width, height, + interpolationModeFromSettings(myOSystem.settings()), data.data()); + + mySLineSurface->setSrcSize(mySLineSurface->width(), height); + mySLineSurface->setDstRect(myTiaSurface->dstRect()); + + enableNTSC(ntscEnabled()); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TIASurface::enableNTSC(bool enable) { @@ -311,11 +445,11 @@ void TIASurface::enableNTSC(bool enable) myTiaSurface->invalidate(); } - mySLineSurface->setSrcSize(1, 2 * myTIA->height()); - + // Generate a scanline surface from current scanline pattern + // Apply current blend to scan line surface myScanlinesEnabled = myOSystem.settings().getInt("tv.scanlines") > 0; FBSurface::Attributes& sl_attr = mySLineSurface->attributes(); - sl_attr.blending = myScanlinesEnabled; + sl_attr.blending = myScanlinesEnabled; sl_attr.blendalpha = myOSystem.settings().getInt("tv.scanlines"); mySLineSurface->applyAttributes(); diff --git a/src/emucore/TIASurface.hxx b/src/emucore/TIASurface.hxx index 9cb73c5ba..09d914975 100644 --- a/src/emucore/TIASurface.hxx +++ b/src/emucore/TIASurface.hxx @@ -45,6 +45,12 @@ class PaletteHandler; class TIASurface { public: + // Setting names of palette types + static constexpr const char* SETTING_STANDARD = "standard"; + static constexpr const char* SETTING_THIN = "thin"; + static constexpr const char* SETTING_PIXELS = "pixels"; + static constexpr const char* SETTING_MAME = "mame"; + /** Creates a new TIASurface object */ @@ -138,15 +144,14 @@ class TIASurface @param direction +1 indicates increase, -1 indicates decrease. */ - void setScanlineIntensity(int direction = +1); + void changeScanlineIntensity(int direction = +1); /** - Change scanline intensity and interpolation. + Cycle through available scanline masks. - @param change change current intensity by 'change' - @return New current intensity + @param direction +1 next mask, -1 mask. */ - uInt32 enableScanlines(int change); + void cycleScanlineMask(int direction = +1); /** Enable/disable/query phosphor effect. @@ -154,6 +159,11 @@ class TIASurface void enablePhosphor(bool enable, int blend = -1); bool phosphorEnabled() const { return myPhosphorHandler.phosphorEnabled(); } + /** + Creates a scanline surface for the current TIA resolution + */ + void createScanlineSurface(); + /** Enable/disable/query NTSC filtering effects. */ @@ -184,15 +194,14 @@ class TIASurface void updateSurfaceSettings(); private: - /** - Average current calculated buffer's pixel with previous calculated buffer's pixel (50:50). - */ - uInt32 averageBuffers(uInt32 bufOfs); + enum class ScanlineMask { + Standard, + Thin, + Pixels, + Mame, + NumMasks + }; - // Is plain video mode enabled? - bool correctAspect() const; - - private: // Enumeration created such that phosphor off/on is in LSB, // and Blargg off/on is in MSB enum class Filter: uInt8 { @@ -201,6 +210,19 @@ class TIASurface BlarggNormal = 0x10, BlarggPhosphor = 0x11 }; + + private: + /** + Average current calculated buffer's pixel with previous calculated buffer's pixel (50:50). + */ + uInt32 averageBuffers(uInt32 bufOfs); + + // Is plain video mode enabled? + bool correctAspect() const; + + // Convert scanline mask setting name into type + ScanlineMask scanlineMaskType(int direction = 0); + Filter myFilter{Filter::Normal}; private: diff --git a/src/gui/DeveloperDialog.hxx b/src/gui/DeveloperDialog.hxx index c19a5b7eb..5779364f2 100644 --- a/src/gui/DeveloperDialog.hxx +++ b/src/gui/DeveloperDialog.hxx @@ -29,7 +29,6 @@ class RadioButtonWidget; class SliderWidget; class StaticTextWidget; class ColorWidget; -class DevSettingsHandler; namespace GUI { class Font; diff --git a/src/gui/VideoAudioDialog.cxx b/src/gui/VideoAudioDialog.cxx index f6f540b61..0fdaa262e 100644 --- a/src/gui/VideoAudioDialog.cxx +++ b/src/gui/VideoAudioDialog.cxx @@ -331,7 +331,7 @@ void VideoAudioDialog::addTVEffectsTab() int xpos = HBORDER, ypos = VBORDER; const int lwidth = _font.getStringWidth("Saturation "); - const int pwidth = _font.getStringWidth("Bad adjust "); + int pwidth = _font.getStringWidth("Bad adjust "); WidgetArray wid; VariantList items; const int tabID = myTab->addTab(" TV Effects ", TabWidget::AUTO_WIDTH); @@ -380,6 +380,17 @@ void VideoAudioDialog::addTVEffectsTab() xpos += INDENT; CREATE_CUSTOM_SLIDERS(ScanIntense, "Intensity", kScanlinesChanged) + items.clear(); + VarList::push_back(items, "Standard", TIASurface::SETTING_STANDARD); + VarList::push_back(items, "Thin lines", TIASurface::SETTING_THIN); + VarList::push_back(items, "Pixelated", TIASurface::SETTING_PIXELS); + VarList::push_back(items, "MAME", TIASurface::SETTING_MAME); + + pwidth = _font.getStringWidth("Thin lines"); + myTVScanMask = new PopUpWidget(myTab, _font, myTVScanIntense->getRight() + fontWidth * 2, + myTVScanIntense->getTop() + 1, pwidth, lineHeight, items, "Mask "); + wid.push_back(myTVScanMask); + // Create buttons in 2nd column xpos = _w - HBORDER - 2 * 2 - buttonWidth; ypos = VBORDER - VGAP / 2; @@ -633,15 +644,14 @@ void VideoAudioDialog::loadConfig() // TV Custom adjustables loadTVAdjustables(NTSCFilter::Preset::CUSTOM); - // TV phosphor mode + // TV phosphor mode & blend myTVPhosphor->setState(settings.getString("tv.phosphor") == "always"); - - // TV phosphor blend myTVPhosLevel->setValue(settings.getInt("tv.phosblend")); handlePhosphorChange(); - // TV scanline intensity and interpolation + // TV scanline intensity & mask myTVScanIntense->setValue(settings.getInt("tv.scanlines")); + myTVScanMask->setSelected(settings.getString("tv.scanmask"), TIASurface::SETTING_STANDARD); ///////////////////////////////////////////////////////////////////////////// // Audio tab @@ -754,15 +764,15 @@ void VideoAudioDialog::saveConfig() instance().frameBuffer().tiaSurface().ntsc().saveConfig(settings); - // TV phosphor mode + // TV phosphor mode & blend settings.setValue("tv.phosphor", myTVPhosphor->getState() ? "always" : "byrom"); - // TV phosphor blend settings.setValue("tv.phosblend", myTVPhosLevel->getValueLabel() == "Off" ? "0" : myTVPhosLevel->getValueLabel()); - // TV scanline intensity + // TV scanline intensity & mask settings.setValue("tv.scanlines", myTVScanIntense->getValueLabel()); + settings.setValue("tv.scanmask", myTVScanMask->getSelectedTag()); if(instance().hasConsole()) { @@ -879,14 +889,13 @@ void VideoAudioDialog::setDefaults() { myTVMode->setSelected("0", "0"); - // TV phosphor mode + // TV phosphor mode & blend myTVPhosphor->setState(false); - - // TV phosphor blend myTVPhosLevel->setValue(50); - // TV scanline intensity and interpolation + // TV scanline intensity & mask myTVScanIntense->setValue(25); + myTVScanMask->setSelected(TIASurface::SETTING_STANDARD); // Make sure that mutually-exclusive items are not enabled at the same time handleTVModeChange(NTSCFilter::Preset::OFF); diff --git a/src/gui/VideoAudioDialog.hxx b/src/gui/VideoAudioDialog.hxx index 66990a2d3..6622430c5 100644 --- a/src/gui/VideoAudioDialog.hxx +++ b/src/gui/VideoAudioDialog.hxx @@ -95,6 +95,7 @@ class VideoAudioDialog : public Dialog // TV scanline intensity and interpolation StaticTextWidget* myTVScanLabel{nullptr}; SliderWidget* myTVScanIntense{nullptr}; + PopUpWidget* myTVScanMask{nullptr}; // TV effects adjustables presets (custom mode) ButtonWidget* myCloneComposite{nullptr};