added different mask pattern for scanline emulation

This commit is contained in:
Thomas Jentzsch 2021-11-01 17:55:45 +01:00
parent 18db3456f4
commit 6238b5efbb
17 changed files with 300 additions and 89 deletions

View File

@ -25,6 +25,8 @@
* Added 'Check for Update' button to Help dialog. * Added 'Check for Update' button to Help dialog.
* Added different mask pattern for scanline emulation (TODO: doc)
* Fixed MindLink controller. * Fixed MindLink controller.
* Fixed SaveKey not working with QuadTari. * Fixed SaveKey not working with QuadTari.

View File

@ -23,7 +23,7 @@ class OSystem;
#include <array> #include <array>
/** /**
This class takes care developer settings sets. This class takes care of developer settings sets.
@author Thomas Jentzsch @author Thomas Jentzsch
*/ */
@ -33,7 +33,7 @@ class DevSettingsHandler
enum SettingsSet { enum SettingsSet {
player, player,
developer, developer,
numSettings numSets
}; };
DevSettingsHandler(OSystem& osystem); DevSettingsHandler(OSystem& osystem);
@ -45,40 +45,40 @@ class DevSettingsHandler
protected: protected:
OSystem& myOSystem; OSystem& myOSystem;
// Emulator sets // Emulator sets
std::array<bool, numSettings> myFrameStats; std::array<bool, numSets> myFrameStats;
std::array<bool, numSettings> myDetectedInfo; std::array<bool, numSets> myDetectedInfo;
std::array<bool, numSettings> myExternAccess; std::array<bool, numSets> myExternAccess;
std::array<int, numSettings> myConsole; std::array<int, numSets> myConsole;
std::array<bool, numSettings> myRandomBank; std::array<bool, numSets> myRandomBank;
std::array<bool, numSettings> myRandomizeTIA; std::array<bool, numSets> myRandomizeTIA;
std::array<bool, numSettings> myRandomizeRAM; std::array<bool, numSets> myRandomizeRAM;
std::array<string, numSettings> myRandomizeCPU; std::array<string, numSets> myRandomizeCPU;
std::array<bool, numSettings> myColorLoss; std::array<bool, numSets> myColorLoss;
std::array<bool, numSettings> myTVJitter; std::array<bool, numSets> myTVJitter;
std::array<int, numSettings> myTVJitterRec; std::array<int, numSets> myTVJitterRec;
std::array<bool, numSettings> myDebugColors; std::array<bool, numSets> myDebugColors;
std::array<bool, numSettings> myUndrivenPins; std::array<bool, numSets> myUndrivenPins;
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
std::array<bool, numSettings> myRWPortBreak; std::array<bool, numSets> myRWPortBreak;
std::array<bool, numSettings> myWRPortBreak; std::array<bool, numSets> myWRPortBreak;
#endif #endif
std::array<bool, numSettings> myThumbException; std::array<bool, numSets> myThumbException;
// TIA sets // TIA sets
std::array<string, numSettings> myTIAType; std::array<string, numSets> myTIAType;
std::array<bool, numSettings> myPlInvPhase; std::array<bool, numSets> myPlInvPhase;
std::array<bool, numSettings> myMsInvPhase; std::array<bool, numSets> myMsInvPhase;
std::array<bool, numSettings> myBlInvPhase; std::array<bool, numSets> myBlInvPhase;
std::array<bool, numSettings> myPFBits; std::array<bool, numSets> myPFBits;
std::array<bool, numSettings> myPFColor; std::array<bool, numSets> myPFColor;
std::array<bool, numSettings> myBKColor; std::array<bool, numSets> myBKColor;
std::array<bool, numSettings> myPlSwap; std::array<bool, numSets> myPlSwap;
std::array<bool, numSettings> myBlSwap; std::array<bool, numSets> myBlSwap;
// States sets // States sets
std::array<bool, numSettings> myTimeMachine; std::array<bool, numSets> myTimeMachine;
std::array<int, numSettings> myStateSize; std::array<int, numSets> myStateSize;
std::array<int, numSettings> myUncompressed; std::array<int, numSets> myUncompressed;
std::array<string, numSettings> myStateInterval; std::array<string, numSets> myStateInterval;
std::array<string, numSettings> myStateHorizon; std::array<string, numSets> myStateHorizon;
private: private:
void handleEnableDebugColors(bool enable); void handleEnableDebugColors(bool enable);

View File

@ -644,6 +644,8 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
{ Event::TogglePhosphor, KBDK_P, MOD3 }, { Event::TogglePhosphor, KBDK_P, MOD3 },
{ Event::ScanlinesDecrease, KBDK_5, KBDM_SHIFT | MOD3 }, { Event::ScanlinesDecrease, KBDK_5, KBDM_SHIFT | MOD3 },
{ Event::ScanlinesIncrease, KBDK_5, 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::PreviousPaletteAttribute, KBDK_9, KBDM_SHIFT | MOD3 },
{ Event::NextPaletteAttribute, KBDK_9, MOD3 }, { Event::NextPaletteAttribute, KBDK_9, MOD3 },
{ Event::PaletteAttributeDecrease, KBDK_0, KBDM_SHIFT | MOD3 }, { Event::PaletteAttributeDecrease, KBDK_0, KBDM_SHIFT | MOD3 },

View File

@ -345,6 +345,8 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
{Event::NextAttribute, "NextAttribute"}, {Event::NextAttribute, "NextAttribute"},
{Event::DecreaseAttribute, "DecreaseAttribute"}, {Event::DecreaseAttribute, "DecreaseAttribute"},
{Event::IncreaseAttribute, "IncreaseAttribute"}, {Event::IncreaseAttribute, "IncreaseAttribute"},
{Event::PreviousScanlineMask, "PreviousScanlineMask"},
{Event::NextScanlineMask, "NextScanlineMask"},
{Event::ScanlinesDecrease, "ScanlinesDecrease"}, {Event::ScanlinesDecrease, "ScanlinesDecrease"},
{Event::ScanlinesIncrease, "ScanlinesIncrease"}, {Event::ScanlinesIncrease, "ScanlinesIncrease"},
{Event::PhosphorDecrease, "PhosphorDecrease"}, {Event::PhosphorDecrease, "PhosphorDecrease"},

View File

@ -147,8 +147,14 @@ void QisBlitter::recreateTexturesIfNecessary()
SDL_TextureAccess texAccess = myStaticData == nullptr ? SDL_TEXTUREACCESS_STREAMING : SDL_TEXTUREACCESS_STATIC; SDL_TextureAccess texAccess = myStaticData == nullptr ? SDL_TEXTUREACCESS_STREAMING : SDL_TEXTUREACCESS_STATIC;
myIntermediateRect.w = (myDstRect.w / mySrcRect.w) * mySrcRect.w; if(myDstRect.w > mySrcRect.w)
myIntermediateRect.h = (myDstRect.h / mySrcRect.h) * mySrcRect.h; 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.x = 0;
myIntermediateRect.y = 0; myIntermediateRect.y = 0;

View File

@ -111,6 +111,7 @@ class Event
PreviousVideoMode, NextVideoMode, PreviousVideoMode, NextVideoMode,
PreviousAttribute, NextAttribute, DecreaseAttribute, IncreaseAttribute, PreviousAttribute, NextAttribute, DecreaseAttribute, IncreaseAttribute,
ScanlinesDecrease, ScanlinesIncrease, ScanlinesDecrease, ScanlinesIncrease,
PreviousScanlineMask, NextScanlineMask,
PhosphorDecrease, PhosphorIncrease, TogglePhosphor, ToggleInter, PhosphorDecrease, PhosphorIncrease, TogglePhosphor, ToggleInter,
ToggleDeveloperSet, JitterDecrease, JitterIncrease, ToggleJitter, ToggleDeveloperSet, JitterDecrease, JitterIncrease, ToggleJitter,

View File

@ -713,7 +713,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
case Event::ScanlinesDecrease: case Event::ScanlinesDecrease:
if(pressed) if(pressed)
{ {
myOSystem.frameBuffer().tiaSurface().setScanlineIntensity(-1); myOSystem.frameBuffer().tiaSurface().changeScanlineIntensity(-1);
myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::SCANLINES); myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::SCANLINES);
} }
return; return;
@ -721,11 +721,27 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
case Event::ScanlinesIncrease: case Event::ScanlinesIncrease:
if(pressed) if(pressed)
{ {
myOSystem.frameBuffer().tiaSurface().setScanlineIntensity(+1); myOSystem.frameBuffer().tiaSurface().changeScanlineIntensity(+1);
myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::SCANLINES); myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::SCANLINES);
} }
return; 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: case Event::ToggleInter:
if(pressed && !repeated) if(pressed && !repeated)
{ {
@ -2882,6 +2898,8 @@ EventHandler::EmulActionList EventHandler::ourEmulActionList = { {
{ Event::PhosphorIncrease, "Increase 'phosphor' blend", "" }, { Event::PhosphorIncrease, "Increase 'phosphor' blend", "" },
{ Event::ScanlinesDecrease, "Decrease scanlines", "" }, { Event::ScanlinesDecrease, "Decrease scanlines", "" },
{ Event::ScanlinesIncrease, "Increase 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::PreviousSettingGroup, "Select previous setting group", "" },
{ Event::NextSettingGroup, "Select next 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::PreviousAttribute, Event::NextAttribute, Event::DecreaseAttribute, Event::IncreaseAttribute,
Event::PhosphorDecrease, Event::PhosphorIncrease, Event::TogglePhosphor, Event::PhosphorDecrease, Event::PhosphorIncrease, Event::TogglePhosphor,
Event::ScanlinesDecrease, Event::ScanlinesIncrease, Event::ScanlinesDecrease, Event::ScanlinesIncrease,
Event::PreviousScanlineMask, Event::NextScanlineMask,
Event::ToggleInter, Event::ToggleInter,
}; };

View File

@ -522,7 +522,7 @@ class EventHandler
#else #else
REFRESH_SIZE = 0, REFRESH_SIZE = 0,
#endif #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 MENU_ACTIONLIST_SIZE = 19
; ;

View File

@ -1201,7 +1201,7 @@ void FrameBuffer::switchVideoMode(int direction)
} }
saveCurrentWindowPosition(); saveCurrentWindowPosition();
if(applyVideoMode() == FBInitStatus::Success) if(!direction || applyVideoMode() == FBInitStatus::Success)
{ {
if(fullScreen()) if(fullScreen())
showTextMessage(myActiveVidMode.description); showTextMessage(myActiveVidMode.description);

View File

@ -374,7 +374,8 @@ GlobalKeyHandler::SettingData GlobalKeyHandler::getSettingData(const Setting set
int(NTSCFilter::Adjustables::BLEEDING), _1)}}, int(NTSCFilter::Adjustables::BLEEDING), _1)}},
// Other TV effects adjustables // Other TV effects adjustables
{Setting::PHOSPHOR, {true, std::bind(&Console::changePhosphor, &myOSystem.console(), _1)}}, {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)}}, {Setting::INTERPOLATION, {false, std::bind(&Console::toggleInter, &myOSystem.console(), _1)}},
// *** Input group *** // *** Input group ***
{Setting::DIGITAL_DEADZONE, {true, std::bind(&PhysicalJoystickHandler::changeDigitalDeadZone, &joyHandler(), _1)}}, {Setting::DIGITAL_DEADZONE, {true, std::bind(&PhysicalJoystickHandler::changeDigitalDeadZone, &joyHandler(), _1)}},

View File

@ -70,6 +70,7 @@ class GlobalKeyHandler
// Other TV effects adjustables // Other TV effects adjustables
PHOSPHOR, PHOSPHOR,
SCANLINES, SCANLINES,
SCANLINE_MASK,
INTERPOLATION, INTERPOLATION,
// *** Input group *** // *** Input group ***
DIGITAL_DEADZONE, DIGITAL_DEADZONE,

View File

@ -21,6 +21,7 @@
#include "Version.hxx" #include "Version.hxx"
#include "Logger.hxx" #include "Logger.hxx"
#include "AudioSettings.hxx" #include "AudioSettings.hxx"
#include "TIASurface.hxx"
#include "PaletteHandler.hxx" #include "PaletteHandler.hxx"
#include "Joystick.hxx" #include "Joystick.hxx"
#include "Paddles.hxx" #include "Paddles.hxx"
@ -80,6 +81,7 @@ Settings::Settings()
setPermanent("tv.phosphor", "byrom"); setPermanent("tv.phosphor", "byrom");
setPermanent("tv.phosblend", "50"); setPermanent("tv.phosblend", "50");
setPermanent("tv.scanlines", "0"); setPermanent("tv.scanlines", "0");
setPermanent("tv.scanmask", TIASurface::SETTING_STANDARD);
// TV options when using 'custom' mode // TV options when using 'custom' mode
setPermanent("tv.sharpness", "0.0"); setPermanent("tv.sharpness", "0.0");
setPermanent("tv.resolution", "0.0"); setPermanent("tv.resolution", "0.0");
@ -315,6 +317,13 @@ void Settings::validate()
i = getInt("tv.phosblend"); i = getInt("tv.phosblend");
if(i < 0 || i > 100) setValue("tv.phosblend", "50"); 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"); i = getInt("tv.filter");
if(i < 0 || i > 5) setValue("tv.filter", "0"); 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.phosblend <0-100> Set default blend level in phosphor mode\n"
<< " -tv.scanlines <0-100> Set scanline intensity to percentage\n" << " -tv.scanlines <0-100> Set scanline intensity to percentage\n"
<< " (0 disables completely)\n" << " (0 disables completely)\n"
<< " -tv.scanmask <standard| Use the specified scanline mask\n"
<< " thin|pixel|\n"
<< " mame>\n"
<< " -tv.sharpness <-1.0 - 1.0> Set TV effects custom sharpness\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.resolution <-1.0 - 1.0> Set TV effects custom resolution\n"
<< " -tv.artifacts <-1.0 - 1.0> Set TV effects custom artifacts\n" << " -tv.artifacts <-1.0 - 1.0> Set TV effects custom artifacts\n"

View File

@ -61,16 +61,6 @@ TIASurface::TIASurface(OSystem& system)
: interpolationModeFromSettings(myOSystem.settings()) : interpolationModeFromSettings(myOSystem.settings())
); );
// Generate scanline data, and a pre-defined scanline surface
constexpr uInt32 scanHeight = TIAConstants::frameBufferHeight * 2;
std::array<uInt32, scanHeight> 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 // Base TIA surface for use in taking snapshots in 1x mode
myBaseTiaSurface = myFB.allocateSurface(TIAConstants::frameBufferWidth*2, myBaseTiaSurface = myFB.allocateSurface(TIAConstants::frameBufferWidth*2,
TIAConstants::frameBufferHeight); TIAConstants::frameBufferHeight);
@ -108,8 +98,6 @@ void TIASurface::initialize(const Console& console,
myTiaSurface->setDstPos(mode.imageR.x(), mode.imageR.y()); myTiaSurface->setDstPos(mode.imageR.x(), mode.imageR.y());
myTiaSurface->setDstSize(mode.imageR.w(), mode.imageR.h()); 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(); myPaletteHandler->setPalette();
@ -129,6 +117,7 @@ void TIASurface::initialize(const Console& console,
} }
enablePhosphor(enable, p_blend); enablePhosphor(enable, p_blend);
createScanlineSurface();
setNTSC(NTSCFilter::Preset(myOSystem.settings().getInt("tv.filter")), false); setNTSC(NTSCFilter::Preset(myOSystem.settings().getInt("tv.filter")), false);
#if 0 #if 0
@ -260,14 +249,20 @@ void TIASurface::changeCurrentNTSCAdjustable(int direction)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIASurface::setScanlineIntensity(int direction) void TIASurface::changeScanlineIntensity(int direction)
{ {
ostringstream buf; FBSurface::Attributes& attr = mySLineSurface->attributes();
uInt32 intensity = enableScanlines(direction * 2);
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); myOSystem.settings().setValue("tv.scanlines", intensity);
enableNTSC(ntscEnabled()); enableNTSC(ntscEnabled());
ostringstream buf;
if(intensity) if(intensity)
buf << intensity << "%"; buf << intensity << "%";
else 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; for(auto& mask : Masks)
attr.blendalpha = BSPF::clamp(Int32(attr.blendalpha), 0, 100); {
mySLineSurface->applyAttributes(); 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 <PatternSize, int(ScanlineMask::NumMasks)> Sizes = {{
PatternSize(1, 2, 1),
PatternSize(1, 3, 1),
PatternSize(3, 3, 2),
PatternSize(3, 4, 3)
}};
std::array<std::vector<std::vector<uInt32>>, 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::vector<uInt32>data(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) void TIASurface::enableNTSC(bool enable)
{ {
@ -311,11 +445,11 @@ void TIASurface::enableNTSC(bool enable)
myTiaSurface->invalidate(); 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; myScanlinesEnabled = myOSystem.settings().getInt("tv.scanlines") > 0;
FBSurface::Attributes& sl_attr = mySLineSurface->attributes(); FBSurface::Attributes& sl_attr = mySLineSurface->attributes();
sl_attr.blending = myScanlinesEnabled; sl_attr.blending = myScanlinesEnabled;
sl_attr.blendalpha = myOSystem.settings().getInt("tv.scanlines"); sl_attr.blendalpha = myOSystem.settings().getInt("tv.scanlines");
mySLineSurface->applyAttributes(); mySLineSurface->applyAttributes();

View File

@ -45,6 +45,12 @@ class PaletteHandler;
class TIASurface class TIASurface
{ {
public: 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 Creates a new TIASurface object
*/ */
@ -138,15 +144,14 @@ class TIASurface
@param direction +1 indicates increase, -1 indicates decrease. @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' @param direction +1 next mask, -1 mask.
@return New current intensity
*/ */
uInt32 enableScanlines(int change); void cycleScanlineMask(int direction = +1);
/** /**
Enable/disable/query phosphor effect. Enable/disable/query phosphor effect.
@ -154,6 +159,11 @@ class TIASurface
void enablePhosphor(bool enable, int blend = -1); void enablePhosphor(bool enable, int blend = -1);
bool phosphorEnabled() const { return myPhosphorHandler.phosphorEnabled(); } bool phosphorEnabled() const { return myPhosphorHandler.phosphorEnabled(); }
/**
Creates a scanline surface for the current TIA resolution
*/
void createScanlineSurface();
/** /**
Enable/disable/query NTSC filtering effects. Enable/disable/query NTSC filtering effects.
*/ */
@ -184,15 +194,14 @@ class TIASurface
void updateSurfaceSettings(); void updateSurfaceSettings();
private: private:
/** enum class ScanlineMask {
Average current calculated buffer's pixel with previous calculated buffer's pixel (50:50). Standard,
*/ Thin,
uInt32 averageBuffers(uInt32 bufOfs); Pixels,
Mame,
NumMasks
};
// Is plain video mode enabled?
bool correctAspect() const;
private:
// Enumeration created such that phosphor off/on is in LSB, // Enumeration created such that phosphor off/on is in LSB,
// and Blargg off/on is in MSB // and Blargg off/on is in MSB
enum class Filter: uInt8 { enum class Filter: uInt8 {
@ -201,6 +210,19 @@ class TIASurface
BlarggNormal = 0x10, BlarggNormal = 0x10,
BlarggPhosphor = 0x11 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}; Filter myFilter{Filter::Normal};
private: private:

View File

@ -29,7 +29,6 @@ class RadioButtonWidget;
class SliderWidget; class SliderWidget;
class StaticTextWidget; class StaticTextWidget;
class ColorWidget; class ColorWidget;
class DevSettingsHandler;
namespace GUI { namespace GUI {
class Font; class Font;

View File

@ -331,7 +331,7 @@ void VideoAudioDialog::addTVEffectsTab()
int xpos = HBORDER, int xpos = HBORDER,
ypos = VBORDER; ypos = VBORDER;
const int lwidth = _font.getStringWidth("Saturation "); const int lwidth = _font.getStringWidth("Saturation ");
const int pwidth = _font.getStringWidth("Bad adjust "); int pwidth = _font.getStringWidth("Bad adjust ");
WidgetArray wid; WidgetArray wid;
VariantList items; VariantList items;
const int tabID = myTab->addTab(" TV Effects ", TabWidget::AUTO_WIDTH); const int tabID = myTab->addTab(" TV Effects ", TabWidget::AUTO_WIDTH);
@ -380,6 +380,17 @@ void VideoAudioDialog::addTVEffectsTab()
xpos += INDENT; xpos += INDENT;
CREATE_CUSTOM_SLIDERS(ScanIntense, "Intensity", kScanlinesChanged) 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 // Create buttons in 2nd column
xpos = _w - HBORDER - 2 * 2 - buttonWidth; xpos = _w - HBORDER - 2 * 2 - buttonWidth;
ypos = VBORDER - VGAP / 2; ypos = VBORDER - VGAP / 2;
@ -633,15 +644,14 @@ void VideoAudioDialog::loadConfig()
// TV Custom adjustables // TV Custom adjustables
loadTVAdjustables(NTSCFilter::Preset::CUSTOM); loadTVAdjustables(NTSCFilter::Preset::CUSTOM);
// TV phosphor mode // TV phosphor mode & blend
myTVPhosphor->setState(settings.getString("tv.phosphor") == "always"); myTVPhosphor->setState(settings.getString("tv.phosphor") == "always");
// TV phosphor blend
myTVPhosLevel->setValue(settings.getInt("tv.phosblend")); myTVPhosLevel->setValue(settings.getInt("tv.phosblend"));
handlePhosphorChange(); handlePhosphorChange();
// TV scanline intensity and interpolation // TV scanline intensity & mask
myTVScanIntense->setValue(settings.getInt("tv.scanlines")); myTVScanIntense->setValue(settings.getInt("tv.scanlines"));
myTVScanMask->setSelected(settings.getString("tv.scanmask"), TIASurface::SETTING_STANDARD);
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// Audio tab // Audio tab
@ -754,15 +764,15 @@ void VideoAudioDialog::saveConfig()
instance().frameBuffer().tiaSurface().ntsc().saveConfig(settings); instance().frameBuffer().tiaSurface().ntsc().saveConfig(settings);
// TV phosphor mode // TV phosphor mode & blend
settings.setValue("tv.phosphor", settings.setValue("tv.phosphor",
myTVPhosphor->getState() ? "always" : "byrom"); myTVPhosphor->getState() ? "always" : "byrom");
// TV phosphor blend
settings.setValue("tv.phosblend", myTVPhosLevel->getValueLabel() == "Off" settings.setValue("tv.phosblend", myTVPhosLevel->getValueLabel() == "Off"
? "0" : myTVPhosLevel->getValueLabel()); ? "0" : myTVPhosLevel->getValueLabel());
// TV scanline intensity // TV scanline intensity & mask
settings.setValue("tv.scanlines", myTVScanIntense->getValueLabel()); settings.setValue("tv.scanlines", myTVScanIntense->getValueLabel());
settings.setValue("tv.scanmask", myTVScanMask->getSelectedTag());
if(instance().hasConsole()) if(instance().hasConsole())
{ {
@ -879,14 +889,13 @@ void VideoAudioDialog::setDefaults()
{ {
myTVMode->setSelected("0", "0"); myTVMode->setSelected("0", "0");
// TV phosphor mode // TV phosphor mode & blend
myTVPhosphor->setState(false); myTVPhosphor->setState(false);
// TV phosphor blend
myTVPhosLevel->setValue(50); myTVPhosLevel->setValue(50);
// TV scanline intensity and interpolation // TV scanline intensity & mask
myTVScanIntense->setValue(25); myTVScanIntense->setValue(25);
myTVScanMask->setSelected(TIASurface::SETTING_STANDARD);
// Make sure that mutually-exclusive items are not enabled at the same time // Make sure that mutually-exclusive items are not enabled at the same time
handleTVModeChange(NTSCFilter::Preset::OFF); handleTVModeChange(NTSCFilter::Preset::OFF);

View File

@ -95,6 +95,7 @@ class VideoAudioDialog : public Dialog
// TV scanline intensity and interpolation // TV scanline intensity and interpolation
StaticTextWidget* myTVScanLabel{nullptr}; StaticTextWidget* myTVScanLabel{nullptr};
SliderWidget* myTVScanIntense{nullptr}; SliderWidget* myTVScanIntense{nullptr};
PopUpWidget* myTVScanMask{nullptr};
// TV effects adjustables presets (custom mode) // TV effects adjustables presets (custom mode)
ButtonWidget* myCloneComposite{nullptr}; ButtonWidget* myCloneComposite{nullptr};