diff --git a/Core/BaseVideoFilter.cpp b/Core/BaseVideoFilter.cpp index 3e5a0d8..48c5014 100644 --- a/Core/BaseVideoFilter.cpp +++ b/Core/BaseVideoFilter.cpp @@ -10,7 +10,7 @@ BaseVideoFilter::BaseVideoFilter(shared_ptr console) { _console = console; - _overscan = {}; //TODO _console->GetSettings()->GetOverscanDimensions(); + _overscan = _console->GetSettings()->GetOverscan(); } BaseVideoFilter::~BaseVideoFilter() @@ -29,7 +29,11 @@ void BaseVideoFilter::SetBaseFrameInfo(FrameInfo frameInfo) FrameInfo BaseVideoFilter::GetFrameInfo() { - return _baseFrameInfo; + FrameInfo frameInfo = _baseFrameInfo; + OverscanDimensions overscan = GetOverscan(); + frameInfo.Width -= overscan.Left * 2 + overscan.Right * 2; + frameInfo.Height -= overscan.Top * 2 + overscan.Bottom * 2; + return frameInfo; } void BaseVideoFilter::UpdateBufferSize() @@ -64,7 +68,7 @@ bool BaseVideoFilter::IsOddFrame() void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber) { _frameLock.Acquire(); - _overscan = {}; //TODO _console->GetSettings()->GetOverscanDimensions(); + _overscan = _console->GetSettings()->GetOverscan(); _isOddFrame = frameNumber % 2; UpdateBufferSize(); OnBeforeApplyFilter(); diff --git a/Core/BaseVideoFilter.h b/Core/BaseVideoFilter.h index 63c25d9..8f40818 100644 --- a/Core/BaseVideoFilter.h +++ b/Core/BaseVideoFilter.h @@ -13,12 +13,12 @@ private: SimpleLock _frameLock; OverscanDimensions _overscan; bool _isOddFrame; - FrameInfo _baseFrameInfo; void UpdateBufferSize(); protected: shared_ptr _console; + FrameInfo _baseFrameInfo; virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0; virtual void OnBeforeApplyFilter(); diff --git a/Core/DefaultVideoFilter.cpp b/Core/DefaultVideoFilter.cpp index 7be90c0..4ec7542 100644 --- a/Core/DefaultVideoFilter.cpp +++ b/Core/DefaultVideoFilter.cpp @@ -87,26 +87,31 @@ void DefaultVideoFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { uint32_t *out = GetOutputBuffer(); FrameInfo frameInfo = GetFrameInfo(); + OverscanDimensions overscan = GetOverscan(); + + uint32_t xOffset = overscan.Left * 2; + uint32_t yOffset = overscan.Top * 2 * 512; uint8_t scanlineIntensity = (uint8_t)((1.0 - _console->GetSettings()->GetVideoConfig().ScanlineIntensity) * 255); if(scanlineIntensity < 255) { for(uint32_t i = 0; i < frameInfo.Height; i++) { if(i & 0x01) { for(uint32_t j = 0; j < frameInfo.Width; j++) { - *out = ApplyScanlineEffect(_calculatedPalette[ppuOutputBuffer[i * 512 + j]], scanlineIntensity); + *out = ApplyScanlineEffect(_calculatedPalette[ppuOutputBuffer[i * 512 + j + yOffset + xOffset]], scanlineIntensity); out++; } } else { for(uint32_t j = 0; j < frameInfo.Width; j++) { - *out = _calculatedPalette[ppuOutputBuffer[i * 512 + j]]; + *out = _calculatedPalette[ppuOutputBuffer[i * 512 + j + yOffset + xOffset]]; out++; } } } } else { - uint32_t pixelCount = frameInfo.Width * frameInfo.Height; - for(uint32_t i = 0; i < pixelCount; i++) { - out[i] = _calculatedPalette[ppuOutputBuffer[i]]; + for(uint32_t i = 0; i < frameInfo.Height; i++) { + for(uint32_t j = 0; j < frameInfo.Width; j++) { + out[i*frameInfo.Width+j] = _calculatedPalette[ppuOutputBuffer[i * 512 + j + yOffset + xOffset]]; + } } } } diff --git a/Core/EmuSettings.cpp b/Core/EmuSettings.cpp index 4e55f9a..f40246e 100644 --- a/Core/EmuSettings.cpp +++ b/Core/EmuSettings.cpp @@ -151,6 +151,16 @@ vector EmuSettings::GetShortcutSupersets(EmulatorShortcut shortc return _shortcutSupersets[keySetIndex][(uint32_t)shortcut]; } +OverscanDimensions EmuSettings::GetOverscan() +{ + OverscanDimensions overscan; + overscan.Left = _video.OverscanLeft; + overscan.Right = _video.OverscanRight; + overscan.Top = _video.OverscanTop; + overscan.Bottom = _video.OverscanBottom; + return overscan; +} + uint32_t EmuSettings::GetRewindBufferSize() { return _preferences.RewindBufferSize; diff --git a/Core/EmuSettings.h b/Core/EmuSettings.h index 76ed940..107f2b6 100644 --- a/Core/EmuSettings.h +++ b/Core/EmuSettings.h @@ -51,6 +51,7 @@ public: KeyCombination GetShortcutKey(EmulatorShortcut shortcut, int keySetIndex); vector GetShortcutSupersets(EmulatorShortcut shortcut, int keySetIndex); + OverscanDimensions GetOverscan(); uint32_t GetRewindBufferSize(); uint32_t GetEmulationSpeed(); double GetAspectRatio(); diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index c76cde9..df76a68 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -15,7 +15,8 @@ NtscFilter::NtscFilter(shared_ptr console) : BaseVideoFilter(console) FrameInfo NtscFilter::GetFrameInfo() { FrameInfo frameInfo = BaseVideoFilter::GetFrameInfo(); - frameInfo.Width = SNES_NTSC_OUT_WIDTH(frameInfo.Width / 2); + OverscanDimensions overscan = GetOverscan(); + frameInfo.Width = SNES_NTSC_OUT_WIDTH(_baseFrameInfo.Width / 2) - overscan.Left * 2 - overscan.Right * 2; return frameInfo; } @@ -45,22 +46,29 @@ void NtscFilter::OnBeforeApplyFilter() void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { FrameInfo frameInfo = GetFrameInfo(); + OverscanDimensions overscan = GetOverscan(); + uint32_t baseWidth = SNES_NTSC_OUT_WIDTH(256); + uint32_t xOffset = overscan.Left * 2; + uint32_t yOffset = overscan.Top * 2 * baseWidth; + snes_ntsc_blit_hires(&_ntscData, ppuOutputBuffer, 512, IsOddFrame() ? 0 : 1, 512, frameInfo.Height, _ntscBuffer, SNES_NTSC_OUT_WIDTH(256)*4); VideoConfig cfg = _console->GetSettings()->GetVideoConfig(); if(cfg.ScanlineIntensity == 0) { - memcpy(GetOutputBuffer(), _ntscBuffer, frameInfo.Width * frameInfo.Height * sizeof(uint32_t)); + for(uint32_t i = 0; i < frameInfo.Height; i++) { + memcpy(GetOutputBuffer()+i*frameInfo.Width, _ntscBuffer + yOffset + xOffset + i*baseWidth, frameInfo.Width * sizeof(uint32_t)); + } } else { uint8_t intensity = (uint8_t)((1.0 - cfg.ScanlineIntensity) * 255); for(uint32_t i = 0; i < frameInfo.Height; i++) { if(i & 0x01) { - uint32_t *in = _ntscBuffer + i * frameInfo.Width; + uint32_t *in = _ntscBuffer + yOffset + xOffset + i * baseWidth; uint32_t *out = GetOutputBuffer() + i * frameInfo.Width; for(uint32_t j = 0; j < frameInfo.Width; j++) { out[j] = ApplyScanlineEffect(in[j], intensity); } } else { - memcpy(GetOutputBuffer()+i*frameInfo.Width, _ntscBuffer+i*frameInfo.Width, frameInfo.Width * sizeof(uint32_t)); + memcpy(GetOutputBuffer()+i*frameInfo.Width, _ntscBuffer + yOffset + xOffset + i*baseWidth, frameInfo.Width * sizeof(uint32_t)); } } } diff --git a/Core/SettingTypes.h b/Core/SettingTypes.h index cf94af4..b022883 100644 --- a/Core/SettingTypes.h +++ b/Core/SettingTypes.h @@ -86,6 +86,11 @@ struct VideoConfig double NtscSharpness = 0; bool NtscMergeFields = false; + uint32_t OverscanLeft = 0; + uint32_t OverscanRight = 0; + uint32_t OverscanTop = 0; + uint32_t OverscanBottom = 0; + bool FullscreenForceIntegerScale = false; bool UseExclusiveFullscreen = false; int32_t ExclusiveFullscreenRefreshRate = 60; diff --git a/Core/VideoDecoder.cpp b/Core/VideoDecoder.cpp index d9ac3d4..ff02382 100644 --- a/Core/VideoDecoder.cpp +++ b/Core/VideoDecoder.cpp @@ -18,7 +18,10 @@ VideoDecoder::VideoDecoder(shared_ptr console) _console = console; _frameChanged = false; _stopFlag = false; + _baseFrameInfo = { 512, 478 }; + _lastFrameInfo = _baseFrameInfo; UpdateVideoFilter(); + _videoFilter->SetBaseFrameInfo(_baseFrameInfo); } VideoDecoder::~VideoDecoder() @@ -33,22 +36,25 @@ FrameInfo VideoDecoder::GetFrameInfo() ScreenSize VideoDecoder::GetScreenSize(bool ignoreScale) { - ScreenSize size = {}; - if(_videoFilter) { - VideoConfig config = _console->GetSettings()->GetVideoConfig(); - double aspectRatio = _console->GetSettings()->GetAspectRatio(); - double scale = (ignoreScale ? 1 : config.VideoScale); - size.Width = (int32_t)(_baseFrameInfo.Width * scale / 2); - size.Height = (int32_t)(_baseFrameInfo.Height * scale / 2); - size.Scale = scale; - if(aspectRatio != 0.0) { - size.Width = (uint32_t)(_baseFrameInfo.Height * scale * aspectRatio / 2); - } - - /*if(_console->GetSettings()->GetScreenRotation() % 180) { - std::swap(size.Width, size.Height); - }*/ + ScreenSize size; + OverscanDimensions overscan = ignoreScale ? _videoFilter->GetOverscan() : _console->GetSettings()->GetOverscan(); + FrameInfo frameInfo = _videoFilter->GetFrameInfo(); + double aspectRatio = _console->GetSettings()->GetAspectRatio(); + double scale = (ignoreScale ? 1 : _console->GetSettings()->GetVideoConfig().VideoScale); + size.Width = (int32_t)(frameInfo.Width * scale / 2); + size.Height = (int32_t)(frameInfo.Height * scale / 2); + if(aspectRatio != 0.0) { + uint32_t originalHeight = frameInfo.Height + (overscan.Top + overscan.Bottom) * 2; + uint32_t originalWidth = frameInfo.Width + (overscan.Left + overscan.Right) * 2; + size.Width = (uint32_t)(originalHeight * scale * aspectRatio * ((double)frameInfo.Width / originalWidth)) / 2; } + + /* + if(_console->GetSettings()->GetScreenRotation() % 180) { + std::swap(size.Width, size.Height); + }*/ + + size.Scale = scale; return size; } diff --git a/Core/VideoDecoder.h b/Core/VideoDecoder.h index ea7f043..09c065d 100644 --- a/Core/VideoDecoder.h +++ b/Core/VideoDecoder.h @@ -28,9 +28,9 @@ private: atomic _stopFlag; uint32_t _frameCount = 0; - FrameInfo _baseFrameInfo = { 512, 478 }; ScreenSize _previousScreenSize = {}; double _previousScale = 0; + FrameInfo _baseFrameInfo; FrameInfo _lastFrameInfo; VideoFilterType _videoFilterType = VideoFilterType::None; diff --git a/UI/Config/VideoConfig.cs b/UI/Config/VideoConfig.cs index ce28cb5..9988ea0 100644 --- a/UI/Config/VideoConfig.cs +++ b/UI/Config/VideoConfig.cs @@ -33,6 +33,11 @@ namespace Mesen.GUI.Config [MinMax(-1, 1.0)] public double NtscSharpness = 0; [MarshalAs(UnmanagedType.I1)] public bool NtscMergeFields = false; + [MinMax(0, 100)] public UInt32 OverscanLeft = 0; + [MinMax(0, 100)] public UInt32 OverscanRight = 0; + [MinMax(0, 100)] public UInt32 OverscanTop = 0; + [MinMax(0, 100)] public UInt32 OverscanBottom = 0; + [MarshalAs(UnmanagedType.I1)] public bool FullscreenForceIntegerScale = false; [MarshalAs(UnmanagedType.I1)] public bool UseExclusiveFullscreen = false; public Int32 ExclusiveFullscreenRefreshRate = 60; diff --git a/UI/Emulation/DisplayManager.cs b/UI/Emulation/DisplayManager.cs index ab7c6e7..b3db978 100644 --- a/UI/Emulation/DisplayManager.cs +++ b/UI/Emulation/DisplayManager.cs @@ -16,6 +16,7 @@ namespace Mesen.GUI.Emulation private frmMain _frm; private ctrlRenderer _renderer; private Panel _panel; + private bool _resizeForm; public DisplayManager(frmMain frm, ctrlRenderer renderer, Panel panel) { @@ -30,11 +31,11 @@ namespace Mesen.GUI.Emulation SetScaleBasedOnWindowSize(); } - public void UpdateViewerSize(bool resizeForm) + public void UpdateViewerSize() { ScreenSize screenSize = EmuApi.GetScreenSize(false); - if(resizeForm && _frm.WindowState != FormWindowState.Maximized) { + if(_resizeForm && _frm.WindowState != FormWindowState.Maximized) { _frm.Resize -= frmMain_Resize; Size newSize = new Size(screenSize.Width, screenSize.Height); _frm.ClientSize = new Size(newSize.Width, newSize.Height + _panel.Top); @@ -72,11 +73,12 @@ namespace Mesen.GUI.Emulation public void SetScale(double scale, bool resizeForm) { + _resizeForm = resizeForm; ConfigManager.Config.Video.VideoScale = scale; ConfigManager.Config.Video.ApplyConfig(); ConfigManager.ApplyChanges(); - UpdateViewerSize(resizeForm); + UpdateViewerSize(); } public void ToggleFullscreen() diff --git a/UI/Forms/Config/frmVideoConfig.Designer.cs b/UI/Forms/Config/frmVideoConfig.Designer.cs index e2b7abb..bcf1e39 100644 --- a/UI/Forms/Config/frmVideoConfig.Designer.cs +++ b/UI/Forms/Config/frmVideoConfig.Designer.cs @@ -76,6 +76,21 @@ this.mnuPresetSVideo = new System.Windows.Forms.ToolStripMenuItem(); this.mnuPresetRgb = new System.Windows.Forms.ToolStripMenuItem(); this.mnuPresetMonochrome = new System.Windows.Forms.ToolStripMenuItem(); + this.tpgOverscan = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.picOverscan = new System.Windows.Forms.PictureBox(); + this.tableLayoutPanel11 = new System.Windows.Forms.TableLayoutPanel(); + this.nudOverscanTop = new Mesen.GUI.Controls.MesenNumericUpDown(); + this.lblTop = new System.Windows.Forms.Label(); + this.tableLayoutPanel12 = new System.Windows.Forms.TableLayoutPanel(); + this.nudOverscanBottom = new Mesen.GUI.Controls.MesenNumericUpDown(); + this.lblBottom = new System.Windows.Forms.Label(); + this.tableLayoutPanel13 = new System.Windows.Forms.TableLayoutPanel(); + this.nudOverscanRight = new Mesen.GUI.Controls.MesenNumericUpDown(); + this.lblRight = new System.Windows.Forms.Label(); + this.tableLayoutPanel14 = new System.Windows.Forms.TableLayoutPanel(); + this.nudOverscanLeft = new Mesen.GUI.Controls.MesenNumericUpDown(); + this.lblLeft = new System.Windows.Forms.Label(); this.tabMain.SuspendLayout(); this.tpgGeneral.SuspendLayout(); this.tlpMain.SuspendLayout(); @@ -91,6 +106,13 @@ this.grpScanlines.SuspendLayout(); this.tableLayoutPanel8.SuspendLayout(); this.ctxPicturePresets.SuspendLayout(); + this.tpgOverscan.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picOverscan)).BeginInit(); + this.tableLayoutPanel11.SuspendLayout(); + this.tableLayoutPanel12.SuspendLayout(); + this.tableLayoutPanel13.SuspendLayout(); + this.tableLayoutPanel14.SuspendLayout(); this.SuspendLayout(); // // baseConfigPanel @@ -102,6 +124,7 @@ // this.tabMain.Controls.Add(this.tpgGeneral); this.tabMain.Controls.Add(this.tpgPicture); + this.tabMain.Controls.Add(this.tpgOverscan); this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tabMain.Location = new System.Drawing.Point(0, 0); this.tabMain.Name = "tabMain"; @@ -791,6 +814,294 @@ this.mnuPresetMonochrome.Text = "Monochrome"; this.mnuPresetMonochrome.Click += new System.EventHandler(this.mnuPresetMonochrome_Click); // + // tpgOverscan + // + this.tpgOverscan.Controls.Add(this.tableLayoutPanel1); + this.tpgOverscan.Location = new System.Drawing.Point(4, 22); + this.tpgOverscan.Name = "tpgOverscan"; + this.tpgOverscan.Padding = new System.Windows.Forms.Padding(3); + this.tpgOverscan.Size = new System.Drawing.Size(566, 382); + this.tpgOverscan.TabIndex = 6; + this.tpgOverscan.Text = "Overscan"; + this.tpgOverscan.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 262F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.picOverscan, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel11, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel12, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel13, 2, 1); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel14, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 246F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(560, 376); + this.tableLayoutPanel1.TabIndex = 1; + // + // picOverscan + // + this.picOverscan.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.picOverscan.Dock = System.Windows.Forms.DockStyle.Fill; + this.picOverscan.Location = new System.Drawing.Point(152, 68); + this.picOverscan.Name = "picOverscan"; + this.picOverscan.Size = new System.Drawing.Size(256, 240); + this.picOverscan.TabIndex = 1; + this.picOverscan.TabStop = false; + // + // tableLayoutPanel11 + // + this.tableLayoutPanel11.ColumnCount = 1; + this.tableLayoutPanel11.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel11.Controls.Add(this.nudOverscanTop, 0, 1); + this.tableLayoutPanel11.Controls.Add(this.lblTop, 0, 0); + this.tableLayoutPanel11.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel11.Location = new System.Drawing.Point(149, 0); + this.tableLayoutPanel11.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel11.Name = "tableLayoutPanel11"; + this.tableLayoutPanel11.RowCount = 2; + this.tableLayoutPanel11.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel11.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel11.Size = new System.Drawing.Size(262, 65); + this.tableLayoutPanel11.TabIndex = 4; + // + // nudOverscanTop + // + this.nudOverscanTop.Anchor = System.Windows.Forms.AnchorStyles.Bottom; + this.nudOverscanTop.DecimalPlaces = 0; + this.nudOverscanTop.Increment = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudOverscanTop.Location = new System.Drawing.Point(110, 44); + this.nudOverscanTop.Margin = new System.Windows.Forms.Padding(0); + this.nudOverscanTop.Maximum = new decimal(new int[] { + 100, + 0, + 0, + 0}); + this.nudOverscanTop.MaximumSize = new System.Drawing.Size(10000, 20); + this.nudOverscanTop.Minimum = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOverscanTop.MinimumSize = new System.Drawing.Size(0, 21); + this.nudOverscanTop.Name = "nudOverscanTop"; + this.nudOverscanTop.Size = new System.Drawing.Size(41, 21); + this.nudOverscanTop.TabIndex = 2; + this.nudOverscanTop.Value = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOverscanTop.ValueChanged += new System.EventHandler(this.nudOverscan_ValueChanged); + // + // lblTop + // + this.lblTop.Anchor = System.Windows.Forms.AnchorStyles.Bottom; + this.lblTop.AutoSize = true; + this.lblTop.Location = new System.Drawing.Point(118, 31); + this.lblTop.Name = "lblTop"; + this.lblTop.Size = new System.Drawing.Size(26, 13); + this.lblTop.TabIndex = 0; + this.lblTop.Text = "Top"; + this.lblTop.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // tableLayoutPanel12 + // + this.tableLayoutPanel12.ColumnCount = 1; + this.tableLayoutPanel12.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel12.Controls.Add(this.nudOverscanBottom, 0, 1); + this.tableLayoutPanel12.Controls.Add(this.lblBottom, 0, 0); + this.tableLayoutPanel12.Location = new System.Drawing.Point(149, 311); + this.tableLayoutPanel12.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel12.Name = "tableLayoutPanel12"; + this.tableLayoutPanel12.RowCount = 2; + this.tableLayoutPanel12.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel12.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel12.Size = new System.Drawing.Size(262, 58); + this.tableLayoutPanel12.TabIndex = 5; + // + // nudOverscanBottom + // + this.nudOverscanBottom.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.nudOverscanBottom.DecimalPlaces = 0; + this.nudOverscanBottom.Increment = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudOverscanBottom.Location = new System.Drawing.Point(110, 13); + this.nudOverscanBottom.Margin = new System.Windows.Forms.Padding(0); + this.nudOverscanBottom.Maximum = new decimal(new int[] { + 100, + 0, + 0, + 0}); + this.nudOverscanBottom.MaximumSize = new System.Drawing.Size(10000, 20); + this.nudOverscanBottom.Minimum = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOverscanBottom.MinimumSize = new System.Drawing.Size(0, 21); + this.nudOverscanBottom.Name = "nudOverscanBottom"; + this.nudOverscanBottom.Size = new System.Drawing.Size(41, 21); + this.nudOverscanBottom.TabIndex = 2; + this.nudOverscanBottom.Value = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOverscanBottom.ValueChanged += new System.EventHandler(this.nudOverscan_ValueChanged); + // + // lblBottom + // + this.lblBottom.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.lblBottom.AutoSize = true; + this.lblBottom.Location = new System.Drawing.Point(111, 0); + this.lblBottom.Name = "lblBottom"; + this.lblBottom.Size = new System.Drawing.Size(40, 13); + this.lblBottom.TabIndex = 0; + this.lblBottom.Text = "Bottom"; + this.lblBottom.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // tableLayoutPanel13 + // + this.tableLayoutPanel13.ColumnCount = 2; + this.tableLayoutPanel13.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel13.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel13.Controls.Add(this.nudOverscanRight, 0, 2); + this.tableLayoutPanel13.Controls.Add(this.lblRight, 0, 1); + this.tableLayoutPanel13.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel13.Location = new System.Drawing.Point(411, 65); + this.tableLayoutPanel13.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel13.Name = "tableLayoutPanel13"; + this.tableLayoutPanel13.RowCount = 4; + this.tableLayoutPanel13.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel13.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel13.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel13.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel13.Size = new System.Drawing.Size(149, 246); + this.tableLayoutPanel13.TabIndex = 6; + // + // nudOverscanRight + // + this.nudOverscanRight.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.nudOverscanRight.DecimalPlaces = 0; + this.nudOverscanRight.Increment = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudOverscanRight.Location = new System.Drawing.Point(0, 119); + this.nudOverscanRight.Margin = new System.Windows.Forms.Padding(0); + this.nudOverscanRight.Maximum = new decimal(new int[] { + 100, + 0, + 0, + 0}); + this.nudOverscanRight.MaximumSize = new System.Drawing.Size(10000, 20); + this.nudOverscanRight.Minimum = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOverscanRight.MinimumSize = new System.Drawing.Size(0, 21); + this.nudOverscanRight.Name = "nudOverscanRight"; + this.nudOverscanRight.Size = new System.Drawing.Size(41, 21); + this.nudOverscanRight.TabIndex = 1; + this.nudOverscanRight.Value = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOverscanRight.ValueChanged += new System.EventHandler(this.nudOverscan_ValueChanged); + // + // lblRight + // + this.lblRight.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.lblRight.AutoSize = true; + this.lblRight.Location = new System.Drawing.Point(4, 106); + this.lblRight.Name = "lblRight"; + this.lblRight.Size = new System.Drawing.Size(32, 13); + this.lblRight.TabIndex = 0; + this.lblRight.Text = "Right"; + this.lblRight.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // tableLayoutPanel14 + // + this.tableLayoutPanel14.ColumnCount = 2; + this.tableLayoutPanel14.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel14.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel14.Controls.Add(this.nudOverscanLeft, 1, 2); + this.tableLayoutPanel14.Controls.Add(this.lblLeft, 1, 1); + this.tableLayoutPanel14.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel14.Location = new System.Drawing.Point(0, 65); + this.tableLayoutPanel14.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel14.Name = "tableLayoutPanel14"; + this.tableLayoutPanel14.RowCount = 4; + this.tableLayoutPanel14.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel14.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel14.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel14.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel14.Size = new System.Drawing.Size(149, 246); + this.tableLayoutPanel14.TabIndex = 7; + // + // nudOverscanLeft + // + this.nudOverscanLeft.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.nudOverscanLeft.DecimalPlaces = 0; + this.nudOverscanLeft.Increment = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudOverscanLeft.Location = new System.Drawing.Point(108, 119); + this.nudOverscanLeft.Margin = new System.Windows.Forms.Padding(0); + this.nudOverscanLeft.Maximum = new decimal(new int[] { + 100, + 0, + 0, + 0}); + this.nudOverscanLeft.MaximumSize = new System.Drawing.Size(10000, 20); + this.nudOverscanLeft.Minimum = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOverscanLeft.MinimumSize = new System.Drawing.Size(0, 21); + this.nudOverscanLeft.Name = "nudOverscanLeft"; + this.nudOverscanLeft.Size = new System.Drawing.Size(41, 21); + this.nudOverscanLeft.TabIndex = 2; + this.nudOverscanLeft.Value = new decimal(new int[] { + 0, + 0, + 0, + 0}); + this.nudOverscanLeft.ValueChanged += new System.EventHandler(this.nudOverscan_ValueChanged); + // + // lblLeft + // + this.lblLeft.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.lblLeft.AutoSize = true; + this.lblLeft.Location = new System.Drawing.Point(116, 106); + this.lblLeft.Name = "lblLeft"; + this.lblLeft.Size = new System.Drawing.Size(25, 13); + this.lblLeft.TabIndex = 0; + this.lblLeft.Text = "Left"; + this.lblLeft.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // // frmVideoConfig // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -827,6 +1138,17 @@ this.tableLayoutPanel8.ResumeLayout(false); this.tableLayoutPanel8.PerformLayout(); this.ctxPicturePresets.ResumeLayout(false); + this.tpgOverscan.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.picOverscan)).EndInit(); + this.tableLayoutPanel11.ResumeLayout(false); + this.tableLayoutPanel11.PerformLayout(); + this.tableLayoutPanel12.ResumeLayout(false); + this.tableLayoutPanel12.PerformLayout(); + this.tableLayoutPanel13.ResumeLayout(false); + this.tableLayoutPanel13.PerformLayout(); + this.tableLayoutPanel14.ResumeLayout(false); + this.tableLayoutPanel14.PerformLayout(); this.ResumeLayout(false); } @@ -881,5 +1203,20 @@ private System.Windows.Forms.ToolStripMenuItem mnuPresetSVideo; private System.Windows.Forms.ToolStripMenuItem mnuPresetRgb; private System.Windows.Forms.ToolStripMenuItem mnuPresetMonochrome; + private System.Windows.Forms.TabPage tpgOverscan; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.PictureBox picOverscan; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel11; + private Controls.MesenNumericUpDown nudOverscanTop; + private System.Windows.Forms.Label lblTop; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel12; + private Controls.MesenNumericUpDown nudOverscanBottom; + private System.Windows.Forms.Label lblBottom; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel13; + private Controls.MesenNumericUpDown nudOverscanRight; + private System.Windows.Forms.Label lblRight; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel14; + private Controls.MesenNumericUpDown nudOverscanLeft; + private System.Windows.Forms.Label lblLeft; } } \ No newline at end of file diff --git a/UI/Forms/Config/frmVideoConfig.cs b/UI/Forms/Config/frmVideoConfig.cs index ed2439c..133b5ad 100644 --- a/UI/Forms/Config/frmVideoConfig.cs +++ b/UI/Forms/Config/frmVideoConfig.cs @@ -47,6 +47,11 @@ namespace Mesen.GUI.Forms.Config AddBinding(nameof(VideoConfig.NtscResolution), trkResolution); AddBinding(nameof(VideoConfig.NtscSharpness), trkSharpness); AddBinding(nameof(VideoConfig.NtscMergeFields), chkMergeFields); + + AddBinding(nameof(VideoConfig.OverscanLeft), nudOverscanLeft); + AddBinding(nameof(VideoConfig.OverscanRight), nudOverscanRight); + AddBinding(nameof(VideoConfig.OverscanTop), nudOverscanTop); + AddBinding(nameof(VideoConfig.OverscanBottom), nudOverscanBottom); } protected override bool ValidateInput() @@ -123,5 +128,24 @@ namespace Mesen.GUI.Forms.Config { SetNtscPreset(0, -100, 0, 0, 20, 0, 70, -20, -20, -10, 15, false); } + + private void UpdateOverscanImage(PictureBox picture, int top, int bottom, int left, int right) + { + Bitmap overscan = new Bitmap(picture.Width - 2, picture.Height - 2); + + using(Graphics g = Graphics.FromImage(overscan)) { + g.Clear(Color.DarkGray); + + Rectangle fg = new Rectangle(left, top, 256 - left - right, 240 - top - bottom); + g.ScaleTransform((float)overscan.Width / 256, (float)overscan.Height / 240); + g.FillRectangle(Brushes.LightCyan, fg); + } + picture.Image = overscan; + } + + private void nudOverscan_ValueChanged(object sender, EventArgs e) + { + UpdateOverscanImage(picOverscan, (int)nudOverscanTop.Value, (int)nudOverscanBottom.Value, (int)nudOverscanLeft.Value, (int)nudOverscanRight.Value); + } } } diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs index e59caa9..2a95b81 100644 --- a/UI/Forms/frmMain.cs +++ b/UI/Forms/frmMain.cs @@ -57,7 +57,7 @@ namespace Mesen.GUI.Forms ConfigManager.Config.ApplyConfig(); _displayManager = new DisplayManager(this, ctrlRenderer, pnlRenderer); - _displayManager.UpdateViewerSize(false); + _displayManager.UpdateViewerSize(); _shortcuts = new ShortcutHandler(_displayManager); _notifListener = new NotificationListener(); @@ -112,6 +112,12 @@ namespace Mesen.GUI.Forms })); break; + case ConsoleNotificationType.ResolutionChanged: + this.BeginInvoke((Action)(() => { + _displayManager.UpdateViewerSize(); + })); + break; + case ConsoleNotificationType.ExecuteShortcut: this.BeginInvoke((Action)(() => { _shortcuts.ExecuteShortcut((EmulatorShortcut)e.Parameter);