diff --git a/docs/graphics/options_video_tv.png b/docs/graphics/options_video_tv.png
index 91e361c64..062db25da 100644
Binary files a/docs/graphics/options_video_tv.png and b/docs/graphics/options_video_tv.png differ
diff --git a/docs/index.html b/docs/index.html
index 3ea94ea6d..c9ff8a32d 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -1553,6 +1553,16 @@
Alt + 5 |
Cmd + 5 |
+
+ Switch to previous scanline mask |
+ Shift-Alt + 6 |
+ Shift-Cmd + 6 |
+
+
+ Switch to next scanline mask |
+ Alt + 6 |
+ Cmd + 6 |
+
These settings can also be changed using Global Keys
@@ -2964,6 +2974,12 @@
Note: No scanlines in 1x mode snapshots |
+
+ -tv.scanmask <standard|thin|pixel|aperture|mame> |
+ Set the scanline mask.
+ Note: All masks (except 'standard') work better at higher zoom levels. |
+
+
-cheat <code> |
Use the specified cheatcode (see Cheat section for description). |
@@ -3782,11 +3798,13 @@
(needs to be manually adjusted for your particular hardware)-tv.phosblend |
Scanline intensity | Sets scanline black-level intensity.
Note: No scanlines in 1x mode snapshots | -tv.scanlines |
+ (Scanline) Mask | Sets the scanline mask.
+ Note: All masks (except 'standard') work better at higher zoom levels | -tv.scanmask |
Clone RGB | Copy 'RGB' attributes to 'Custom' TV mode sliders | |
Clone S-Video | Copy 'S-Video' attributes to 'Custom' TV mode sliders | |
Clone Composite | Copy 'Composite' attributes to 'Custom' TV mode sliders | |
Clone Bad adjust | Copy 'Bad Adjust' attributes to 'Custom' TV mode sliders | |
- Revert | Revert attribute sliders to saved 'Custom' TV mode settings | |
+ Revert | Revert attribute sliders to previously saved 'Custom' TV mode settings | |
diff --git a/src/common/FBSurfaceSDL2.cxx b/src/common/FBSurfaceSDL2.cxx
index a2b8d0166..b1588db73 100644
--- a/src/common/FBSurfaceSDL2.cxx
+++ b/src/common/FBSurfaceSDL2.cxx
@@ -278,6 +278,10 @@ void FBSurfaceSDL2::applyAttributes()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceSDL2::setScalingInterpolation(ScalingInterpolation interpolation)
{
+ if (interpolation == ScalingInterpolation::sharp
+ && mySrcGUIR.h() >= myDstGUIR.h())
+ interpolation = ScalingInterpolation::blur;
+
if (interpolation == myInterpolationMode) return;
myInterpolationMode = interpolation;
diff --git a/src/common/sdl_blitter/QisBlitter.cxx b/src/common/sdl_blitter/QisBlitter.cxx
index cf9ea16e9..a6633364b 100644
--- a/src/common/sdl_blitter/QisBlitter.cxx
+++ b/src/common/sdl_blitter/QisBlitter.cxx
@@ -147,14 +147,8 @@ void QisBlitter::recreateTexturesIfNecessary()
SDL_TextureAccess texAccess = myStaticData == nullptr ? SDL_TEXTUREACCESS_STREAMING : SDL_TEXTUREACCESS_STATIC;
- 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.w = (myDstRect.w / mySrcRect.w) * mySrcRect.w;
+ myIntermediateRect.h = (myDstRect.h / mySrcRect.h) * mySrcRect.h;
myIntermediateRect.x = 0;
myIntermediateRect.y = 0;
diff --git a/src/emucore/GlobalKeyHandler.cxx b/src/emucore/GlobalKeyHandler.cxx
index 7fdd87648..8815b6189 100644
--- a/src/emucore/GlobalKeyHandler.cxx
+++ b/src/emucore/GlobalKeyHandler.cxx
@@ -197,6 +197,8 @@ bool GlobalKeyHandler::skipAVSetting() const
myOSystem.settings().getString("palette") == PaletteHandler::SETTING_CUSTOM;
const bool isCustomFilter =
myOSystem.settings().getInt("tv.filter") == int(NTSCFilter::Preset::CUSTOM);
+ const bool hasScanlines =
+ myOSystem.settings().getInt("tv.scanlines") > 0;
const bool isSoftwareRenderer =
myOSystem.settings().getString("video") == "software";
@@ -210,6 +212,7 @@ bool GlobalKeyHandler::skipAVSetting() const
|| (mySetting >= Setting::NTSC_SHARPNESS
&& mySetting <= Setting::NTSC_BLEEDING
&& !isCustomFilter)
+ || (mySetting == Setting::SCANLINE_MASK && !hasScanlines)
|| (mySetting == Setting::INTERPOLATION && isSoftwareRenderer);
}
@@ -375,7 +378,7 @@ GlobalKeyHandler::SettingData GlobalKeyHandler::getSettingData(const Setting set
// Other TV effects adjustables
{Setting::PHOSPHOR, {true, std::bind(&Console::changePhosphor, &myOSystem.console(), _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::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/Settings.cxx b/src/emucore/Settings.cxx
index d77d40261..53f91359c 100644
--- a/src/emucore/Settings.cxx
+++ b/src/emucore/Settings.cxx
@@ -319,9 +319,10 @@ void Settings::validate()
s = getString("tv.scanmask");
if(s != TIASurface::SETTING_STANDARD
- && s != TIASurface::SETTING_THIN
- && s != TIASurface::SETTING_PIXELS
- && s != TIASurface::SETTING_MAME)
+ && s != TIASurface::SETTING_THIN
+ && s != TIASurface::SETTING_PIXELS
+ && s != TIASurface::SETTING_APERTURE
+ && s != TIASurface::SETTING_MAME)
setValue("tv.scanmask", TIASurface::SETTING_STANDARD);
i = getInt("tv.filter");
diff --git a/src/emucore/TIASurface.cxx b/src/emucore/TIASurface.cxx
index 102355e1c..c95be538b 100644
--- a/src/emucore/TIASurface.cxx
+++ b/src/emucore/TIASurface.cxx
@@ -277,6 +277,7 @@ TIASurface::ScanlineMask TIASurface::scanlineMaskType(int direction)
SETTING_STANDARD,
SETTING_THIN,
SETTING_PIXELS,
+ SETTING_APERTURE,
SETTING_MAME
};
int i = 0;
@@ -302,10 +303,11 @@ TIASurface::ScanlineMask TIASurface::scanlineMaskType(int direction)
void TIASurface::cycleScanlineMask(int direction)
{
const string Names[int(ScanlineMask::NumMasks)] = {
- "'Standard'",
- "'Thin lines'",
- "'Pixelated'",
- "'MAME'"
+ "Standard",
+ "Thin lines",
+ "Pixelated",
+ "Aperture Grille",
+ "MAME"
};
int i = int(scanlineMaskType(direction));
@@ -314,7 +316,7 @@ void TIASurface::cycleScanlineMask(int direction)
ostringstream msg;
- msg << "Scanline pattern " << Names[i];
+ msg << "Scanline pattern '" << Names[i] << "'";
myOSystem.frameBuffer().showTextMessage(msg.str());
}
@@ -331,6 +333,10 @@ void TIASurface::enablePhosphor(bool enable, int blend)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIASurface::createScanlineSurface()
{
+ // Idea: Emulate
+ // - Dot-Trio Shadow-Mask
+ // - Slot-Mask (NEC 'CromaClear')
+ // - Aperture-Grille (Sony 'Trinitron')
struct PatternSize
{
uInt16 width{1};
@@ -345,15 +351,17 @@ void TIASurface::createScanlineSurface()
{}
};
std::array Sizes = {{
- PatternSize(1, 2, 1),
- PatternSize(1, 3, 1),
- PatternSize(3, 3, 2),
- PatternSize(3, 4, 3)
+ PatternSize(1, 2, 1), // standard
+ PatternSize(1, 3, 1), // thin
+ PatternSize(3, 3, 2), // pixel
+ PatternSize(6, 3, 1), // aperture
+ PatternSize(3, 4, 3), // mame
}};
+ // Note: With RGB = 0,0,0, the average alpha should be ~0x55 (85)
std::array>, int(ScanlineMask::NumMasks)> Pattern = {{
{ // standard
{ 0x00000000 },
- { 0xff000000 },
+ { 0xaa000000 }, // 0xff decreased to 0xaa (67%) to match overall brightness of other pattern
}, { // thin lines
{ 0x00000000 },
{ 0x00000000 },
@@ -367,12 +375,26 @@ void TIASurface::createScanlineSurface()
//{ 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 },
+ //{ 0x08000000, 0x02000000, 0x80000000 },
+ //{ 0x08000000, 0x80000000, 0x40000000 },
+ //{ 0xff000000, 0xff000000, 0xff000000 },
+ //{ 0x80000000, 0x04000000, 0x04000000 },
+ //{ 0x04000000, 0x80000000, 0x20000000 },
+ //{ 0xff000000, 0xff000000, 0xff000000 },
+ // brightened
+ { 0x06000000, 0x01000000, 0x5a000000 },
+ { 0x06000000, 0x5a000000, 0x3d000000 },
+ { 0xb4000000, 0xb4000000, 0xb4000000 },
+ { 0x5a000000, 0x03000000, 0x03000000 },
+ { 0x03000000, 0x5a000000, 0x17000000 },
+ { 0xb4000000, 0xb4000000, 0xb4000000 },
+ }, { // aperture (doubled & darkened, alpha */ 1.75)
+ //{ 0x2cf31d00, 0x1500ffce, 0x1c1200a4 },
+ //{ 0x557e0f00, 0x40005044, 0x45070067 },
+ //{ 0x800c0200, 0x89000606, 0x8d02000d },
+ { 0x19f31d00, 0x0c00ffce, 0x101200a4, 0x19f31d00, 0x0c00ffce, 0x101200a4 },
+ { 0x317e0f00, 0x25005044, 0x37070067, 0x317e0f00, 0x25005044, 0x37070067 },
+ { 0xe00c0200, 0xf0000606, 0xf702000d, 0xe00c0200, 0xf0000606, 0xf702000d },
}, { // mame
// original tile data from https://wiki.arcadeotaku.com/w/MAME_CRT_Simulation
//{ 0xffb4b4b4, 0xffa5a5a5, 0xffc3c3c3 },
@@ -427,6 +449,7 @@ void TIASurface::createScanlineSurface()
mySLineSurface->setSrcSize(mySLineSurface->width(), height);
mySLineSurface->setDstRect(myTiaSurface->dstRect());
+ updateSurfaceSettings();
enableNTSC(ntscEnabled());
}
@@ -664,12 +687,13 @@ void TIASurface::renderForSnapshot()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIASurface::updateSurfaceSettings()
{
- myTiaSurface->setScalingInterpolation(
- interpolationModeFromSettings(myOSystem.settings())
- );
- mySLineSurface->setScalingInterpolation(
- interpolationModeFromSettings(myOSystem.settings())
- );
+ if(myTiaSurface != nullptr)
+ myTiaSurface->setScalingInterpolation(
+ interpolationModeFromSettings(myOSystem.settings()));
+
+ if(mySLineSurface != nullptr)
+ mySLineSurface->setScalingInterpolation(
+ interpolationModeFromSettings(myOSystem.settings()));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/emucore/TIASurface.hxx b/src/emucore/TIASurface.hxx
index 09d914975..c9f3772ef 100644
--- a/src/emucore/TIASurface.hxx
+++ b/src/emucore/TIASurface.hxx
@@ -49,6 +49,7 @@ class TIASurface
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_APERTURE = "aperture";
static constexpr const char* SETTING_MAME = "mame";
/**
@@ -198,6 +199,7 @@ class TIASurface
Standard,
Thin,
Pixels,
+ Aperture,
Mame,
NumMasks
};
diff --git a/src/gui/VideoAudioDialog.cxx b/src/gui/VideoAudioDialog.cxx
index 0fdaa262e..1a8b984e7 100644
--- a/src/gui/VideoAudioDialog.cxx
+++ b/src/gui/VideoAudioDialog.cxx
@@ -384,10 +384,12 @@ void VideoAudioDialog::addTVEffectsTab()
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, "Aperture Gr.", TIASurface::SETTING_APERTURE);
VarList::push_back(items, "MAME", TIASurface::SETTING_MAME);
- pwidth = _font.getStringWidth("Thin lines");
- myTVScanMask = new PopUpWidget(myTab, _font, myTVScanIntense->getRight() + fontWidth * 2,
+ xpos = myTVScanIntense->getRight() + fontWidth * 2;
+ pwidth = _w - HBORDER - xpos - fontWidth * 5 - PopUpWidget::dropDownWidth(_font) - 2 * 2;
+ myTVScanMask = new PopUpWidget(myTab, _font, xpos,
myTVScanIntense->getTop() + 1, pwidth, lineHeight, items, "Mask ");
wid.push_back(myTVScanMask);
@@ -894,7 +896,7 @@ void VideoAudioDialog::setDefaults()
myTVPhosLevel->setValue(50);
// TV scanline intensity & mask
- myTVScanIntense->setValue(25);
+ myTVScanIntense->setValue(0);
myTVScanMask->setSelected(TIASurface::SETTING_STANDARD);
// Make sure that mutually-exclusive items are not enabled at the same time
@@ -1155,9 +1157,13 @@ void VideoAudioDialog::handleCommand(CommandSender* sender, int cmd,
{
myTVScanIntense->setValueLabel("Off");
myTVScanIntense->setValueUnit("");
+ myTVScanMask->setEnabled(false);
}
else
+ {
myTVScanIntense->setValueUnit("%");
+ myTVScanMask->setEnabled(true);
+ }
break;
case kPhosphorChanged: