diff --git a/Core/BaseVideoFilter.cpp b/Core/BaseVideoFilter.cpp index 2ae5fd7..8230233 100644 --- a/Core/BaseVideoFilter.cpp +++ b/Core/BaseVideoFilter.cpp @@ -76,6 +76,15 @@ uint32_t* BaseVideoFilter::GetOutputBuffer() return _outputBuffer; } +uint32_t BaseVideoFilter::ApplyScanlineEffect(uint32_t argb, uint8_t scanlineIntensity) +{ + uint8_t r = ((argb & 0xFF0000) >> 16) * scanlineIntensity / 255; + uint8_t g = ((argb & 0xFF00) >> 8) * scanlineIntensity / 255; + uint8_t b = (argb & 0xFF) * scanlineIntensity / 255; + + return 0xFF000000 | (r << 16) | (g << 8) | b; +} + void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream) { uint32_t* pngBuffer; diff --git a/Core/BaseVideoFilter.h b/Core/BaseVideoFilter.h index c5fac71..63c25d9 100644 --- a/Core/BaseVideoFilter.h +++ b/Core/BaseVideoFilter.h @@ -23,6 +23,7 @@ protected: virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0; virtual void OnBeforeApplyFilter(); bool IsOddFrame(); + uint32_t ApplyScanlineEffect(uint32_t argb, uint8_t scanlineIntensity); public: BaseVideoFilter(shared_ptr console); diff --git a/Core/Console.cpp b/Core/Console.cpp index 54648c5..45d8a2a 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -20,6 +20,7 @@ #include "MessageManager.h" #include "KeyManager.h" #include "EventType.h" +#include "EmuSettings.h" #include "../Utilities/Timer.h" #include "../Utilities/VirtualFile.h" #include "../Utilities/PlatformUtilities.h" @@ -31,6 +32,7 @@ Console::~Console() void Console::Initialize() { + _settings.reset(new EmuSettings()); _notificationManager.reset(new NotificationManager()); _videoDecoder.reset(new VideoDecoder(shared_from_this())); _videoRenderer.reset(new VideoRenderer(shared_from_this())); @@ -176,6 +178,11 @@ shared_ptr Console::GetNotificationManager() return _notificationManager; } +shared_ptr Console::GetSettings() +{ + return _settings; +} + shared_ptr Console::GetDebugHud() { return _debugHud; diff --git a/Core/Console.h b/Core/Console.h index 4578490..d0e86b0 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -17,6 +17,7 @@ class SoundMixer; class VideoRenderer; class VideoDecoder; class NotificationManager; +class EmuSettings; enum class MemoryOperationType; enum class SnesMemoryType; enum class EventType; @@ -40,6 +41,7 @@ private: shared_ptr _videoRenderer; shared_ptr _videoDecoder; shared_ptr _debugHud; + shared_ptr _settings; thread::id _emulationThreadId; @@ -62,6 +64,7 @@ public: shared_ptr GetVideoRenderer(); shared_ptr GetVideoDecoder(); shared_ptr GetNotificationManager(); + shared_ptr GetSettings(); shared_ptr GetDebugHud(); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 9908431..68a36ce 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -61,9 +61,11 @@ + + @@ -101,6 +103,7 @@ + @@ -133,6 +136,7 @@ + @@ -141,8 +145,10 @@ + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 4b8e4cf..034d43d 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -209,6 +209,15 @@ Debugger + + Misc + + + Video + + + Video + @@ -326,6 +335,15 @@ Debugger + + Misc + + + Video + + + Video + diff --git a/Core/DefaultVideoFilter.cpp b/Core/DefaultVideoFilter.cpp index f6dcbc1..7be90c0 100644 --- a/Core/DefaultVideoFilter.cpp +++ b/Core/DefaultVideoFilter.cpp @@ -5,17 +5,16 @@ #include #include "DebugHud.h" #include "Console.h" +#include "EmuSettings.h" +#include "SettingTypes.h" DefaultVideoFilter::DefaultVideoFilter(shared_ptr console) : BaseVideoFilter(console) { - InitConversionMatrix(_pictureSettings.Hue, _pictureSettings.Saturation); + InitLookupTable(); } void DefaultVideoFilter::InitConversionMatrix(double hueShift, double saturationShift) { - _pictureSettings.Hue = hueShift; - _pictureSettings.Saturation = saturationShift; - double hue = hueShift * M_PI; double sat = saturationShift + 1; @@ -34,60 +33,40 @@ void DefaultVideoFilter::InitConversionMatrix(double hueShift, double saturation } } -void DefaultVideoFilter::OnBeforeApplyFilter() +void DefaultVideoFilter::InitLookupTable() { - /*PictureSettings currentSettings = _console->GetSettings()->GetPictureSettings(); - if(_pictureSettings.Hue != currentSettings.Hue || _pictureSettings.Saturation != currentSettings.Saturation) { - InitConversionMatrix(currentSettings.Hue, currentSettings.Saturation); + VideoConfig config = _console->GetSettings()->GetVideoConfig(); + + InitConversionMatrix(config.Hue, config.Saturation); + + double y, i, q; + for(int rgb555 = 0; rgb555 < 0x8000; rgb555++) { + double redChannel = To8Bit(rgb555 & 0x1F) / 255.0; + double greenChannel = To8Bit((rgb555 >> 5) & 0x1F) / 255.0; + double blueChannel = To8Bit(rgb555 >> 10) / 255.0; + + //Apply brightness, contrast, hue & saturation + RgbToYiq(redChannel, greenChannel, blueChannel, y, i, q); + y *= config.Contrast * 0.5f + 1; + y += config.Brightness * 0.5f; + YiqToRgb(y, i, q, redChannel, greenChannel, blueChannel); + + int r = std::min(255, (int)(redChannel * 255)); + int g = std::min(255, (int)(greenChannel * 255)); + int b = std::min(255, (int)(blueChannel * 255)); + + _calculatedPalette[rgb555] = 0xFF000000 | (r << 16) | (g << 8) | b; } - _pictureSettings = currentSettings; - _needToProcess = _pictureSettings.Hue != 0 || _pictureSettings.Saturation != 0 || _pictureSettings.Brightness || _pictureSettings.Contrast; - if(_needToProcess) { - double y, i, q; - uint32_t* originalPalette = _console->GetSettings()->GetRgbPalette(); - - for(int pal = 0; pal < 512; pal++) { - uint32_t pixelOutput = originalPalette[pal]; - double redChannel = ((pixelOutput & 0xFF0000) >> 16) / 255.0; - double greenChannel = ((pixelOutput & 0xFF00) >> 8) / 255.0; - double blueChannel = (pixelOutput & 0xFF) / 255.0; - - //Apply brightness, contrast, hue & saturation - RgbToYiq(redChannel, greenChannel, blueChannel, y, i, q); - y *= _pictureSettings.Contrast * 0.5f + 1; - y += _pictureSettings.Brightness * 0.5f; - YiqToRgb(y, i, q, redChannel, greenChannel, blueChannel); - - int r = std::min(255, (int)(redChannel * 255)); - int g = std::min(255, (int)(greenChannel * 255)); - int b = std::min(255, (int)(blueChannel * 255)); - - _calculatedPalette[pal] = 0xFF000000 | (r << 16) | (g << 8) | b; - } - } else { - memcpy(_calculatedPalette, _console->GetSettings()->GetRgbPalette(), sizeof(_calculatedPalette)); - }*/ + _videoConfig = config; } -void DefaultVideoFilter::DecodePpuBuffer(uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, bool displayScanlines) +void DefaultVideoFilter::OnBeforeApplyFilter() { - /*uint32_t* out = outputBuffer; - OverscanDimensions overscan = GetOverscan(); - uint8_t scanlineIntensity = (uint8_t)((1.0 - _console->GetSettings()->GetPictureSettings().ScanlineIntensity) * 255); - for(uint32_t i = overscan.Top, iMax = 240 - overscan.Bottom; i < iMax; i++) { - if(displayScanlines && (i + overscan.Top) % 2 == 0) { - for(uint32_t j = overscan.Left, jMax = 256 - overscan.Right; j < jMax; j++) { - *out = ApplyScanlineEffect(ppuOutputBuffer[i * 256 + j], scanlineIntensity); - out++; - } - } else { - for(uint32_t j = overscan.Left, jMax = 256 - overscan.Right; j < jMax; j++) { - *out = _calculatedPalette[ppuOutputBuffer[i * 256 + j]]; - out++; - } - } - }*/ + VideoConfig config = _console->GetSettings()->GetVideoConfig(); + if(_videoConfig.Hue != config.Hue || _videoConfig.Saturation != config.Saturation || _videoConfig.Contrast != config.Contrast || _videoConfig.Brightness != config.Brightness) { + InitLookupTable(); + } } uint8_t DefaultVideoFilter::To8Bit(uint8_t color) @@ -107,10 +86,28 @@ uint32_t DefaultVideoFilter::ToArgb(uint16_t rgb555) void DefaultVideoFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { uint32_t *out = GetOutputBuffer(); - uint32_t pixelCount = GetFrameInfo().Width * GetFrameInfo().Height; - - for(uint32_t i = 0; i < pixelCount; i++) { - out[i] = ToArgb(ppuOutputBuffer[i]); + FrameInfo frameInfo = GetFrameInfo(); + + 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++; + } + } else { + for(uint32_t j = 0; j < frameInfo.Width; j++) { + *out = _calculatedPalette[ppuOutputBuffer[i * 512 + j]]; + out++; + } + } + } + } else { + uint32_t pixelCount = frameInfo.Width * frameInfo.Height; + for(uint32_t i = 0; i < pixelCount; i++) { + out[i] = _calculatedPalette[ppuOutputBuffer[i]]; + } } } @@ -126,15 +123,4 @@ void DefaultVideoFilter::YiqToRgb(double y, double i, double q, double &r, doubl r = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[0] * i + _yiqToRgbMatrix[1] * q))); g = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[2] * i + _yiqToRgbMatrix[3] * q))); b = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[4] * i + _yiqToRgbMatrix[5] * q))); -} - -uint32_t DefaultVideoFilter::ApplyScanlineEffect(uint16_t ppuPixel, uint8_t scanlineIntensity) -{ - uint32_t pixelOutput = _calculatedPalette[ppuPixel]; - - uint8_t r = ((pixelOutput & 0xFF0000) >> 16) * scanlineIntensity / 255; - uint8_t g = ((pixelOutput & 0xFF00) >> 8) * scanlineIntensity / 255; - uint8_t b = (pixelOutput & 0xFF) * scanlineIntensity / 255; - - return 0xFF000000 | (r << 16) | (g << 8) | b; } \ No newline at end of file diff --git a/Core/DefaultVideoFilter.h b/Core/DefaultVideoFilter.h index 5c4b8b9..9de20ab 100644 --- a/Core/DefaultVideoFilter.h +++ b/Core/DefaultVideoFilter.h @@ -2,24 +2,24 @@ #include "stdafx.h" #include "BaseVideoFilter.h" +#include "SettingTypes.h" class DefaultVideoFilter : public BaseVideoFilter { private: + uint32_t _calculatedPalette[0x8000]; double _yiqToRgbMatrix[6]; - uint32_t _calculatedPalette[512]; - PictureSettings _pictureSettings; + VideoConfig _videoConfig; bool _needToProcess = false; void InitConversionMatrix(double hueShift, double saturationShift); + void InitLookupTable(); void RgbToYiq(double r, double g, double b, double &y, double &i, double &q); void YiqToRgb(double y, double i, double q, double &r, double &g, double &b); __forceinline static uint8_t To8Bit(uint8_t color); protected: - void DecodePpuBuffer(uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, bool displayScanlines); - uint32_t ApplyScanlineEffect(uint16_t ppuPixel, uint8_t scanlineIntensity); void OnBeforeApplyFilter(); public: diff --git a/Core/EmuSettings.cpp b/Core/EmuSettings.cpp new file mode 100644 index 0000000..b984938 --- /dev/null +++ b/Core/EmuSettings.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "EmuSettings.h" + +void EmuSettings::SetVideoConfig(VideoConfig config) +{ + _video = config; +} + +VideoConfig EmuSettings::GetVideoConfig() +{ + return _video; +} + +double EmuSettings::GetAspectRatio() +{ + switch(_video.AspectRatio) { + case VideoAspectRatio::NoStretching: return 0.0; + + case VideoAspectRatio::Auto: + { + bool isPal = false; + return isPal ? (9440000.0 / 6384411.0) : (128.0 / 105.0); + } + + case VideoAspectRatio::NTSC: return 128.0 / 105.0; + case VideoAspectRatio::PAL: return 9440000.0 / 6384411.0; + case VideoAspectRatio::Standard: return 4.0 / 3.0; + case VideoAspectRatio::Widescreen: return 16.0 / 9.0; + case VideoAspectRatio::Custom: return _video.CustomAspectRatio; + } + return 0.0; +} \ No newline at end of file diff --git a/Core/EmuSettings.h b/Core/EmuSettings.h new file mode 100644 index 0000000..3dba276 --- /dev/null +++ b/Core/EmuSettings.h @@ -0,0 +1,14 @@ +#pragma once +#include "stdafx.h" +#include "SettingTypes.h" + +class EmuSettings +{ +private: + VideoConfig _video; + +public: + void SetVideoConfig(VideoConfig config); + VideoConfig GetVideoConfig(); + double GetAspectRatio(); +}; \ No newline at end of file diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp new file mode 100644 index 0000000..c76cde9 --- /dev/null +++ b/Core/NtscFilter.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "NtscFilter.h" +#include "EmuSettings.h" +#include "SettingTypes.h" +#include "Console.h" + +NtscFilter::NtscFilter(shared_ptr console) : BaseVideoFilter(console) +{ + memset(&_ntscData, 0, sizeof(_ntscData)); + _ntscSetup = { }; + snes_ntsc_init(&_ntscData, &_ntscSetup); + _ntscBuffer = new uint32_t[SNES_NTSC_OUT_WIDTH(256) * 480]; +} + +FrameInfo NtscFilter::GetFrameInfo() +{ + FrameInfo frameInfo = BaseVideoFilter::GetFrameInfo(); + frameInfo.Width = SNES_NTSC_OUT_WIDTH(frameInfo.Width / 2); + return frameInfo; +} + +void NtscFilter::OnBeforeApplyFilter() +{ + VideoConfig cfg = _console->GetSettings()->GetVideoConfig(); + + if(_ntscSetup.hue != cfg.Hue / 100.0 || _ntscSetup.saturation != cfg.Saturation / 100.0 || _ntscSetup.brightness != cfg.Brightness / 100.0 || _ntscSetup.contrast != cfg.Contrast / 100.0 || + _ntscSetup.artifacts != cfg.NtscArtifacts || _ntscSetup.bleed != cfg.NtscBleed || _ntscSetup.fringing != cfg.NtscFringing || _ntscSetup.gamma != cfg.NtscGamma || + (_ntscSetup.merge_fields == 1) != cfg.NtscMergeFields || _ntscSetup.resolution != cfg.NtscResolution || _ntscSetup.sharpness != cfg.NtscSharpness) { + _ntscSetup.hue = cfg.Hue; + _ntscSetup.saturation = cfg.Saturation; + _ntscSetup.brightness = cfg.Brightness; + _ntscSetup.contrast = cfg.Contrast; + + _ntscSetup.artifacts = cfg.NtscArtifacts; + _ntscSetup.bleed = cfg.NtscBleed; + _ntscSetup.fringing = cfg.NtscFringing; + _ntscSetup.gamma = cfg.NtscGamma; + _ntscSetup.merge_fields = (int)cfg.NtscMergeFields; + _ntscSetup.resolution = cfg.NtscResolution; + _ntscSetup.sharpness = cfg.NtscSharpness; + snes_ntsc_init(&_ntscData, &_ntscSetup); + } +} + +void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) +{ + FrameInfo frameInfo = GetFrameInfo(); + 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)); + } 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 *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)); + } + } + } +} + +NtscFilter::~NtscFilter() +{ + delete[] _ntscBuffer; +} \ No newline at end of file diff --git a/Core/NtscFilter.h b/Core/NtscFilter.h new file mode 100644 index 0000000..444bf4f --- /dev/null +++ b/Core/NtscFilter.h @@ -0,0 +1,24 @@ +#pragma once +#include "stdafx.h" +#include "BaseVideoFilter.h" +#include "../Utilities/snes_ntsc.h" + +class Console; + +class NtscFilter : public BaseVideoFilter +{ +private: + snes_ntsc_setup_t _ntscSetup; + snes_ntsc_t _ntscData; + uint32_t* _ntscBuffer; + +protected: + void OnBeforeApplyFilter(); + +public: + NtscFilter(shared_ptr console); + virtual ~NtscFilter(); + + virtual void ApplyFilter(uint16_t *ppuOutputBuffer); + virtual FrameInfo GetFrameInfo(); +}; \ No newline at end of file diff --git a/Core/ScaleFilter.cpp b/Core/ScaleFilter.cpp new file mode 100644 index 0000000..973e1fd --- /dev/null +++ b/Core/ScaleFilter.cpp @@ -0,0 +1,141 @@ +#include "stdafx.h" +#include "ScaleFilter.h" +#include "../Utilities/xBRZ/xbrz.h" +#include "../Utilities/HQX/hqx.h" +#include "../Utilities/Scale2x/scalebit.h" +#include "../Utilities/KreedSaiEagle/SaiEagle.h" + +bool ScaleFilter::_hqxInitDone = false; + +ScaleFilter::ScaleFilter(ScaleFilterType scaleFilterType, uint32_t scale) +{ + _scaleFilterType = scaleFilterType; + _filterScale = scale; + + if(!_hqxInitDone && _scaleFilterType == ScaleFilterType::HQX) { + hqxInit(); + _hqxInitDone = true; + } +} + +ScaleFilter::~ScaleFilter() +{ + if(_outputBuffer) { + delete[] _outputBuffer; + } +} + +uint32_t ScaleFilter::GetScale() +{ + return _filterScale; +} + +void ScaleFilter::ApplyPrescaleFilter(uint32_t *inputArgbBuffer) +{ + uint32_t* outputBuffer = _outputBuffer; + + for(uint32_t y = 0; y < _height; y++) { + for(uint32_t x = 0; x < _width; x++) { + for(uint32_t i = 0; i < _filterScale; i++) { + *(outputBuffer++) = *inputArgbBuffer; + } + inputArgbBuffer++; + } + for(uint32_t i = 1; i < _filterScale; i++) { + memcpy(outputBuffer, outputBuffer - _width*_filterScale, _width*_filterScale *4); + outputBuffer += _width*_filterScale; + } + } +} + +void ScaleFilter::UpdateOutputBuffer(uint32_t width, uint32_t height) +{ + if(!_outputBuffer || width != _width || height != _height) { + if(_outputBuffer) { + delete[] _outputBuffer; + } + + _width = width; + _height = height; + _outputBuffer = new uint32_t[_width*_height*_filterScale*_filterScale]; + } +} + +uint32_t* ScaleFilter::ApplyFilter(uint32_t *inputArgbBuffer, uint32_t width, uint32_t height, double scanlineIntensity) +{ + UpdateOutputBuffer(width, height); + + if(_scaleFilterType == ScaleFilterType::xBRZ) { + xbrz::scale(_filterScale, inputArgbBuffer, _outputBuffer, width, height, xbrz::ColorFormat::ARGB); + } else if(_scaleFilterType == ScaleFilterType::HQX) { + hqx(_filterScale, inputArgbBuffer, _outputBuffer, width, height); + } else if(_scaleFilterType == ScaleFilterType::Scale2x) { + scale(_filterScale, _outputBuffer, width*sizeof(uint32_t)*_filterScale, inputArgbBuffer, width*sizeof(uint32_t), 4, width, height); + } else if(_scaleFilterType == ScaleFilterType::_2xSai) { + twoxsai_generic_xrgb8888(width, height, inputArgbBuffer, width, _outputBuffer, width * _filterScale); + } else if(_scaleFilterType == ScaleFilterType::Super2xSai) { + supertwoxsai_generic_xrgb8888(width, height, inputArgbBuffer, width, _outputBuffer, width * _filterScale); + } else if(_scaleFilterType == ScaleFilterType::SuperEagle) { + supereagle_generic_xrgb8888(width, height, inputArgbBuffer, width, _outputBuffer, width * _filterScale); + } else if(_scaleFilterType == ScaleFilterType::Prescale) { + ApplyPrescaleFilter(inputArgbBuffer); + } + + scanlineIntensity = 1.0 - scanlineIntensity; + + if(scanlineIntensity < 1.0) { + for(int y = 1, yMax = height * _filterScale; y < yMax; y += 2) { + for(int x = 0, xMax = width * _filterScale; x < xMax; x++) { + uint32_t &color = _outputBuffer[y*xMax + x]; + uint8_t r = (color >> 16) & 0xFF, g = (color >> 8) & 0xFF, b = color & 0xFF; + r = (uint8_t)(r * scanlineIntensity); + g = (uint8_t)(g * scanlineIntensity); + b = (uint8_t)(b * scanlineIntensity); + color = 0xFF000000 | (r << 16) | (g << 8) | b; + } + } + } + + return _outputBuffer; +} + +shared_ptr ScaleFilter::GetScaleFilter(VideoFilterType filter) +{ + shared_ptr scaleFilter; + switch(filter) { + case VideoFilterType::NTSC: + case VideoFilterType::None: + break; + + case VideoFilterType::xBRZ2x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 2)); break; + case VideoFilterType::xBRZ3x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 3)); break; + case VideoFilterType::xBRZ4x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 4)); break; + case VideoFilterType::xBRZ5x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 5)); break; + case VideoFilterType::xBRZ6x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::xBRZ, 6)); break; + case VideoFilterType::HQ2x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::HQX, 2)); break; + case VideoFilterType::HQ3x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::HQX, 3)); break; + case VideoFilterType::HQ4x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::HQX, 4)); break; + case VideoFilterType::Scale2x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Scale2x, 2)); break; + case VideoFilterType::Scale3x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Scale2x, 3)); break; + case VideoFilterType::Scale4x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Scale2x, 4)); break; + case VideoFilterType::_2xSai: scaleFilter.reset(new ScaleFilter(ScaleFilterType::_2xSai, 2)); break; + case VideoFilterType::Super2xSai: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Super2xSai, 2)); break; + case VideoFilterType::SuperEagle: scaleFilter.reset(new ScaleFilter(ScaleFilterType::SuperEagle, 2)); break; + + case VideoFilterType::Prescale2x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 2)); break; + case VideoFilterType::Prescale3x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 3)); break; + case VideoFilterType::Prescale4x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 4)); break; + case VideoFilterType::Prescale6x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 6)); break; + case VideoFilterType::Prescale8x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 8)); break; + case VideoFilterType::Prescale10x: scaleFilter.reset(new ScaleFilter(ScaleFilterType::Prescale, 10)); break; + } + return scaleFilter; +} + +FrameInfo ScaleFilter::GetFrameInfo(FrameInfo baseFrameInfo) +{ + FrameInfo info = baseFrameInfo; + info.Height *= this->GetScale(); + info.Width *= this->GetScale(); + return info; +} \ No newline at end of file diff --git a/Core/ScaleFilter.h b/Core/ScaleFilter.h new file mode 100644 index 0000000..d53de67 --- /dev/null +++ b/Core/ScaleFilter.h @@ -0,0 +1,28 @@ +#pragma once + +#include "stdafx.h" +#include "DefaultVideoFilter.h" + +class ScaleFilter +{ +private: + static bool _hqxInitDone; + uint32_t _filterScale; + ScaleFilterType _scaleFilterType; + uint32_t *_outputBuffer = nullptr; + uint32_t _width = 0; + uint32_t _height = 0; + + void ApplyPrescaleFilter(uint32_t *inputArgbBuffer); + void UpdateOutputBuffer(uint32_t width, uint32_t height); + +public: + ScaleFilter(ScaleFilterType scaleFilterType, uint32_t scale); + ~ScaleFilter(); + + uint32_t GetScale(); + uint32_t* ApplyFilter(uint32_t *inputArgbBuffer, uint32_t width, uint32_t height, double scanlineIntensity); + FrameInfo GetFrameInfo(FrameInfo baseFrameInfo); + + static shared_ptr GetScaleFilter(VideoFilterType filter); +}; \ No newline at end of file diff --git a/Core/SettingTypes.h b/Core/SettingTypes.h index 8eedb74..1076239 100644 --- a/Core/SettingTypes.h +++ b/Core/SettingTypes.h @@ -15,32 +15,27 @@ enum class ScaleFilterType enum class VideoFilterType { None = 0, - NTSC = 1, - BisqwitNtscQuarterRes = 2, - BisqwitNtscHalfRes = 3, - BisqwitNtsc = 4, - xBRZ2x = 5, - xBRZ3x = 6, - xBRZ4x = 7, - xBRZ5x = 8, - xBRZ6x = 9, - HQ2x = 10, - HQ3x = 11, - HQ4x = 12, - Scale2x = 13, - Scale3x = 14, - Scale4x = 15, - _2xSai = 16, - Super2xSai = 17, - SuperEagle = 18, - Prescale2x = 19, - Prescale3x = 20, - Prescale4x = 21, - Prescale6x = 22, - Prescale8x = 23, - Prescale10x = 24, - Raw = 25, - HdPack = 999 + NTSC, + xBRZ2x, + xBRZ3x, + xBRZ4x, + xBRZ5x, + xBRZ6x, + HQ2x, + HQ3x, + HQ4x, + Scale2x, + Scale3x, + Scale4x, + _2xSai, + Super2xSai, + SuperEagle, + Prescale2x, + Prescale3x, + Prescale4x, + Prescale6x, + Prescale8x, + Prescale10x }; enum class VideoResizeFilter @@ -60,6 +55,35 @@ enum class VideoAspectRatio Custom = 6 }; +struct VideoConfig +{ + double VideoScale = 2; + double CustomAspectRatio = 1.0; + VideoFilterType VideoFilter = VideoFilterType::NTSC; + VideoAspectRatio AspectRatio = VideoAspectRatio::NoStretching; + bool UseBilinearInterpolation = false; + bool VerticalSync = false; + bool IntegerFpsMode = false; + + double Brightness = 0; + double Contrast = 0; + double Hue = 0; + double Saturation = 0; + double ScanlineIntensity = 0; + + double NtscArtifacts = 0; + double NtscBleed = 0; + double NtscFringing = 0; + double NtscGamma = 0; + double NtscResolution = 0; + double NtscSharpness = 0; + bool NtscMergeFields = false; + + bool FullscreenForceIntegerScale = false; + bool UseExclusiveFullscreen = false; + int32_t ExclusiveFullscreenRefreshRate = 60; +}; + struct OverscanDimensions { uint32_t Left = 0; @@ -68,32 +92,6 @@ struct OverscanDimensions uint32_t Bottom = 0; }; -struct PictureSettings -{ - double Brightness = 0; - double Contrast = 0; - double Saturation = 0; - double Hue = 0; - double ScanlineIntensity = 0; -}; - -struct NtscFilterSettings -{ - double Sharpness = 0; - double Gamma = 0; - double Resolution = 0; - double Artifacts = 0; - double Fringing = 0; - double Bleed = 0; - bool MergeFields = false; - bool VerticalBlend = false; - bool KeepVerticalResolution = false; - - double YFilterLength = 0; - double IFilterLength = 0; - double QFilterLength = 0; -}; - struct FrameInfo { uint32_t Width; diff --git a/Core/VideoDecoder.cpp b/Core/VideoDecoder.cpp index bc03383..bc2dc3c 100644 --- a/Core/VideoDecoder.cpp +++ b/Core/VideoDecoder.cpp @@ -3,7 +3,12 @@ #include "VideoDecoder.h" #include "VideoRenderer.h" #include "DefaultVideoFilter.h" +#include "NotificationManager.h" #include "Console.h" +#include "EmuSettings.h" +#include "SettingTypes.h" +#include "NtscFilter.h" +#include "ScaleFilter.h" #include "Ppu.h" #include "DebugHud.h" @@ -25,64 +30,40 @@ FrameInfo VideoDecoder::GetFrameInfo() return _lastFrameInfo; } -void VideoDecoder::GetScreenSize(ScreenSize &size, bool ignoreScale) +ScreenSize VideoDecoder::GetScreenSize(bool ignoreScale) { + ScreenSize size = {}; if(_videoFilter) { - /*OverscanDimensions overscan = ignoreScale ? _videoFilter->GetOverscan() : _console->GetSettings()->GetOverscanDimensions(); - FrameInfo frameInfo{ overscan.GetScreenWidth(), overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 }; - double aspectRatio = _console->GetSettings()->GetAspectRatio(_console); - double scale = (ignoreScale ? 1 : _console->GetSettings()->GetVideoScale()); - size.Width = (int32_t)(frameInfo.Width * scale); - size.Height = (int32_t)(frameInfo.Height * scale); + 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)(frameInfo.OriginalHeight * scale * aspectRatio * ((double)frameInfo.Width / frameInfo.OriginalWidth)); + size.Width = (uint32_t)(_baseFrameInfo.Height * scale * aspectRatio / 2); } - if(_console->GetSettings()->GetScreenRotation() % 180) { + /*if(_console->GetSettings()->GetScreenRotation() % 180) { std::swap(size.Width, size.Height); - } - - size.Scale = scale;*/ - - size.Scale = 2; - if(ignoreScale) { - size.Width = _baseFrameInfo.Width; - size.Height = _baseFrameInfo.Height; - } else { - if(_baseFrameInfo.Width == 256) { - size.Width = (int32_t)(_baseFrameInfo.Width * size.Scale); - size.Height = (int32_t)(_baseFrameInfo.Height * size.Scale); - } else { - size.Width = (int32_t)_baseFrameInfo.Width; - size.Height = (int32_t)_baseFrameInfo.Height; - if(_baseFrameInfo.Height <= 240) { - size.Height *= 2; - } - } - } + }*/ } + return size; } void VideoDecoder::UpdateVideoFilter() { - VideoFilterType newFilter = VideoFilterType::None; + VideoFilterType newFilter = _console->GetSettings()->GetVideoConfig().VideoFilter; if(_videoFilterType != newFilter || _videoFilter == nullptr) { _videoFilterType = newFilter; _videoFilter.reset(new DefaultVideoFilter(_console)); - //_scaleFilter.reset(); + _scaleFilter.reset(); switch(_videoFilterType) { case VideoFilterType::None: break; - - //TODO - /* case VideoFilterType::NTSC: _videoFilter.reset(new NtscFilter(_console)); break; - case VideoFilterType::BisqwitNtsc: _videoFilter.reset(new BisqwitNtscFilter(_console, 1)); break; - case VideoFilterType::BisqwitNtscHalfRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 2)); break; - case VideoFilterType::BisqwitNtscQuarterRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 4)); break; - case VideoFilterType::Raw: _videoFilter.reset(new RawVideoFilter(_console)); break; - default: _scaleFilter = ScaleFilter::GetScaleFilter(_videoFilterType); break;*/ + default: _scaleFilter = ScaleFilter::GetScaleFilter(_videoFilterType); break; } } @@ -112,23 +93,24 @@ void VideoDecoder::DecodeFrame(bool synchronous) if(_rotateFilter) { outputBuffer = _rotateFilter->ApplyFilter(outputBuffer, frameInfo.Width, frameInfo.Height); frameInfo = _rotateFilter->GetFrameInfo(frameInfo); - } + }*/ if(_scaleFilter) { - outputBuffer = _scaleFilter->ApplyFilter(outputBuffer, frameInfo.Width, frameInfo.Height, _console->GetSettings()->GetPictureSettings().ScanlineIntensity); + outputBuffer = _scaleFilter->ApplyFilter(outputBuffer, frameInfo.Width, frameInfo.Height, _console->GetSettings()->GetVideoConfig().ScanlineIntensity); frameInfo = _scaleFilter->GetFrameInfo(frameInfo); } + /* if(_hud) { _hud->DrawHud(_console, outputBuffer, frameInfo, _videoFilter->GetOverscan()); }*/ - ScreenSize screenSize; - GetScreenSize(screenSize, true); - /*if(_previousScale != _console->GetSettings()->GetVideoScale() || screenSize.Height != _previousScreenSize.Height || screenSize.Width != _previousScreenSize.Width) { + ScreenSize screenSize = GetScreenSize(true); + VideoConfig config = _console->GetSettings()->GetVideoConfig(); + if(_previousScale != config.VideoScale || screenSize.Height != _previousScreenSize.Height || screenSize.Width != _previousScreenSize.Width) { _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ResolutionChanged); - }*/ - _previousScale = 1; // _console->GetSettings()->GetVideoScale(); + } + _previousScale = config.VideoScale; _previousScreenSize = screenSize; _lastFrameInfo = frameInfo; diff --git a/Core/VideoDecoder.h b/Core/VideoDecoder.h index d8d5a73..f5c5162 100644 --- a/Core/VideoDecoder.h +++ b/Core/VideoDecoder.h @@ -5,7 +5,7 @@ #include "SettingTypes.h" class BaseVideoFilter; -//class ScaleFilter; +class ScaleFilter; //class RotateFilter; class IRenderingDevice; //class VideoHud; @@ -35,7 +35,7 @@ private: VideoFilterType _videoFilterType = VideoFilterType::None; unique_ptr _videoFilter; - //shared_ptr _scaleFilter; + shared_ptr _scaleFilter; //shared_ptr _rotateFilter; void UpdateVideoFilter(); @@ -53,7 +53,7 @@ public: uint32_t GetFrameCount(); FrameInfo GetFrameInfo(); - void GetScreenSize(ScreenSize &size, bool ignoreScale); + ScreenSize GetScreenSize(bool ignoreScale); void UpdateFrameSync(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber); void UpdateFrame(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber); diff --git a/InteropDLL/ConfigApiWrapper.cpp b/InteropDLL/ConfigApiWrapper.cpp new file mode 100644 index 0000000..d945e96 --- /dev/null +++ b/InteropDLL/ConfigApiWrapper.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" +#include "../Core/Console.h" +#include "../Core/EmuSettings.h" +#include "../Core/SettingTypes.h" + +extern shared_ptr _console; + +extern "C" { + DllExport void __stdcall SetVideoConfig(VideoConfig config) + { + _console->GetSettings()->SetVideoConfig(config); + } +} \ No newline at end of file diff --git a/InteropDLL/EmuApiWrapper.cpp b/InteropDLL/EmuApiWrapper.cpp index c867a47..5e09922 100644 --- a/InteropDLL/EmuApiWrapper.cpp +++ b/InteropDLL/EmuApiWrapper.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "../Core/Console.h" +#include "../Core/VideoDecoder.h" #include "../Core/MessageManager.h" #include "../Core/INotificationListener.h" #include "../Core/KeyManager.h" @@ -28,182 +29,185 @@ string _logString; shared_ptr _console; InteropNotificationListeners _listeners; -namespace InteropEmu { - extern "C" { - DllExport bool __stdcall TestDll() - { - return true; - } +extern "C" { + DllExport bool __stdcall TestDll() + { + return true; + } - DllExport uint32_t __stdcall GetMesenVersion() { return 0x00000100; } + DllExport uint32_t __stdcall GetMesenVersion() { return 0x00000100; } - DllExport void __stdcall InitDll() - { - _console.reset(new Console()); - _console->Initialize(); - } + DllExport void __stdcall InitDll() + { + _console.reset(new Console()); + _console->Initialize(); + } - DllExport void __stdcall InitializeEmu(const char* homeFolder, void *windowHandle, void *viewerHandle, bool noAudio, bool noVideo, bool noInput) - { - FolderUtilities::SetHomeFolder(homeFolder); - //_shortcutKeyHandler.reset(new ShortcutKeyHandler(_console)); + DllExport void __stdcall InitializeEmu(const char* homeFolder, void *windowHandle, void *viewerHandle, bool noAudio, bool noVideo, bool noInput) + { + FolderUtilities::SetHomeFolder(homeFolder); + //_shortcutKeyHandler.reset(new ShortcutKeyHandler(_console)); - if(windowHandle != nullptr && viewerHandle != nullptr) { - _windowHandle = windowHandle; - _viewerHandle = viewerHandle; + if(windowHandle != nullptr && viewerHandle != nullptr) { + _windowHandle = windowHandle; + _viewerHandle = viewerHandle; - if(!noVideo) { - #ifdef _WIN32 - _renderer.reset(new Renderer(_console, (HWND)_viewerHandle, true)); - #else - _renderer.reset(new SdlRenderer(_console, _viewerHandle, true)); - #endif - } - - if(!noAudio) { - #ifdef _WIN32 - _soundManager.reset(new SoundManager(_console, (HWND)_windowHandle)); - #else - _soundManager.reset(new SdlSoundManager(_console)); - #endif - } - - if(!noInput) { - #ifdef _WIN32 - _keyManager.reset(new WindowsKeyManager(_console, (HWND)_windowHandle)); - #else - _keyManager.reset(new LinuxKeyManager(_console)); - #endif - - KeyManager::RegisterKeyManager(_keyManager.get()); - } - } - } - - DllExport void __stdcall SetFullscreenMode(bool fullscreen, void *windowHandle, uint32_t monitorWidth, uint32_t monitorHeight) - { - if(_renderer) { - _renderer->SetFullscreenMode(fullscreen, windowHandle, monitorWidth, monitorHeight); - } - } - - DllExport void __stdcall LoadRom(char* filename, char* patchFile) { _console->LoadRom((VirtualFile)filename, (VirtualFile)patchFile); } - //DllExport void __stdcall AddKnownGameFolder(char* folder) { FolderUtilities::AddKnownGameFolder(folder); } - //DllExport void __stdcall SetFolderOverrides(char* saveFolder, char* saveStateFolder, char* screenshotFolder) { FolderUtilities::SetFolderOverrides(saveFolder, saveStateFolder, screenshotFolder); } - - DllExport const char* __stdcall GetArchiveRomList(char* filename) { - std::ostringstream out; - shared_ptr reader = ArchiveReader::GetReader(filename); - if(reader) { - for(string romName : reader->GetFileList({ ".sfc" })) { - out << romName << "[!|!]"; - } - } - _returnString = out.str(); - return _returnString.c_str(); - } - - DllExport void __stdcall SetMousePosition(double x, double y) { KeyManager::SetMousePosition(x, y); } - DllExport void __stdcall SetMouseMovement(int16_t x, int16_t y) { KeyManager::SetMouseMovement(x, y); } - - DllExport void __stdcall UpdateInputDevices() { if(_keyManager) { _keyManager->UpdateDevices(); } } - DllExport void __stdcall GetPressedKeys(uint32_t *keyBuffer) { - vector pressedKeys = KeyManager::GetPressedKeys(); - for(size_t i = 0; i < pressedKeys.size() && i < 3; i++) { - keyBuffer[i] = pressedKeys[i]; - } - } - DllExport void __stdcall DisableAllKeys(bool disabled) { - if(_keyManager) { - _keyManager->SetDisabled(disabled); - } - } - DllExport void __stdcall SetKeyState(int32_t scanCode, bool state) { - if(_keyManager) { - _keyManager->SetKeyState(scanCode, state); - //_shortcutKeyHandler->ProcessKeys(); + if(!noVideo) { + #ifdef _WIN32 + _renderer.reset(new Renderer(_console, (HWND)_viewerHandle, true)); + #else + _renderer.reset(new SdlRenderer(_console, _viewerHandle, true)); + #endif } - } - DllExport void __stdcall ResetKeyState() { if(_keyManager) { _keyManager->ResetKeyState(); } } - DllExport const char* __stdcall GetKeyName(uint32_t keyCode) - { - _returnString = KeyManager::GetKeyName(keyCode); - return _returnString.c_str(); - } - DllExport uint32_t __stdcall GetKeyCode(char* keyName) { - if(keyName) { - return KeyManager::GetKeyCode(keyName); - } else { - return 0; + + if(!noAudio) { + #ifdef _WIN32 + _soundManager.reset(new SoundManager(_console, (HWND)_windowHandle)); + #else + _soundManager.reset(new SdlSoundManager(_console)); + #endif } - } - DllExport void __stdcall Run() - { - if(_console) { - _console->Run(); - } - } - - DllExport void __stdcall Stop() - { - if(_console) { - _console->Stop(); - } - } - - DllExport void __stdcall Release() - { - _console->Stop(); - - _renderer.reset(); - _soundManager.reset(); - _keyManager.reset(); - - _console->Release(); - _console.reset(); - - //_shortcutKeyHandler.reset(); - } - - DllExport INotificationListener* __stdcall RegisterNotificationCallback(NotificationListenerCallback callback) - { - return _listeners.RegisterNotificationCallback(callback, _console); - } - - DllExport void __stdcall UnregisterNotificationCallback(INotificationListener *listener) - { - _listeners.UnregisterNotificationCallback(listener); - } - - DllExport void __stdcall DisplayMessage(char* title, char* message, char* param1) { MessageManager::DisplayMessage(title, message, param1 ? param1 : ""); } - DllExport const char* __stdcall GetLog() - { - _logString = MessageManager::GetLog(); - return _logString.c_str(); - } - - DllExport void __stdcall WriteLogEntry(char* message) { MessageManager::Log(message); } - - DllExport void __stdcall PgoRunTest(vector testRoms, bool enableDebugger) - { - FolderUtilities::SetHomeFolder("../PGOMesenHome"); - - for(size_t i = 0; i < testRoms.size(); i++) { - std::cout << "Running: " << testRoms[i] << std::endl; - - _console.reset(new Console()); - _console->Initialize(); - _console->LoadRom((VirtualFile)testRoms[i], VirtualFile()); - - thread testThread([=] { - _console->Run(); - }); - std::this_thread::sleep_for(std::chrono::duration(5000)); - _console->Stop(); - testThread.join(); - _console->Release(); + if(!noInput) { + #ifdef _WIN32 + _keyManager.reset(new WindowsKeyManager(_console, (HWND)_windowHandle)); + #else + _keyManager.reset(new LinuxKeyManager(_console)); + #endif + + KeyManager::RegisterKeyManager(_keyManager.get()); } } } + + DllExport void __stdcall SetFullscreenMode(bool fullscreen, void *windowHandle, uint32_t monitorWidth, uint32_t monitorHeight) + { + if(_renderer) { + _renderer->SetFullscreenMode(fullscreen, windowHandle, monitorWidth, monitorHeight); + } + } + + DllExport void __stdcall LoadRom(char* filename, char* patchFile) { _console->LoadRom((VirtualFile)filename, (VirtualFile)patchFile); } + //DllExport void __stdcall AddKnownGameFolder(char* folder) { FolderUtilities::AddKnownGameFolder(folder); } + //DllExport void __stdcall SetFolderOverrides(char* saveFolder, char* saveStateFolder, char* screenshotFolder) { FolderUtilities::SetFolderOverrides(saveFolder, saveStateFolder, screenshotFolder); } + + DllExport const char* __stdcall GetArchiveRomList(char* filename) { + std::ostringstream out; + shared_ptr reader = ArchiveReader::GetReader(filename); + if(reader) { + for(string romName : reader->GetFileList({ ".sfc" })) { + out << romName << "[!|!]"; + } + } + _returnString = out.str(); + return _returnString.c_str(); + } + + DllExport void __stdcall SetMousePosition(double x, double y) { KeyManager::SetMousePosition(x, y); } + DllExport void __stdcall SetMouseMovement(int16_t x, int16_t y) { KeyManager::SetMouseMovement(x, y); } + + DllExport void __stdcall UpdateInputDevices() { if(_keyManager) { _keyManager->UpdateDevices(); } } + DllExport void __stdcall GetPressedKeys(uint32_t *keyBuffer) { + vector pressedKeys = KeyManager::GetPressedKeys(); + for(size_t i = 0; i < pressedKeys.size() && i < 3; i++) { + keyBuffer[i] = pressedKeys[i]; + } + } + DllExport void __stdcall DisableAllKeys(bool disabled) { + if(_keyManager) { + _keyManager->SetDisabled(disabled); + } + } + DllExport void __stdcall SetKeyState(int32_t scanCode, bool state) { + if(_keyManager) { + _keyManager->SetKeyState(scanCode, state); + //_shortcutKeyHandler->ProcessKeys(); + } + } + DllExport void __stdcall ResetKeyState() { if(_keyManager) { _keyManager->ResetKeyState(); } } + DllExport const char* __stdcall GetKeyName(uint32_t keyCode) + { + _returnString = KeyManager::GetKeyName(keyCode); + return _returnString.c_str(); + } + DllExport uint32_t __stdcall GetKeyCode(char* keyName) { + if(keyName) { + return KeyManager::GetKeyCode(keyName); + } else { + return 0; + } + } + + DllExport void __stdcall Run() + { + if(_console) { + _console->Run(); + } + } + + DllExport void __stdcall Stop() + { + if(_console) { + _console->Stop(); + } + } + + DllExport void __stdcall Release() + { + _console->Stop(); + + _renderer.reset(); + _soundManager.reset(); + _keyManager.reset(); + + _console->Release(); + _console.reset(); + + //_shortcutKeyHandler.reset(); + } + + DllExport INotificationListener* __stdcall RegisterNotificationCallback(NotificationListenerCallback callback) + { + return _listeners.RegisterNotificationCallback(callback, _console); + } + + DllExport void __stdcall UnregisterNotificationCallback(INotificationListener *listener) + { + _listeners.UnregisterNotificationCallback(listener); + } + + DllExport void __stdcall DisplayMessage(char* title, char* message, char* param1) { MessageManager::DisplayMessage(title, message, param1 ? param1 : ""); } + DllExport const char* __stdcall GetLog() + { + _logString = MessageManager::GetLog(); + return _logString.c_str(); + } + + DllExport ScreenSize __stdcall GetScreenSize(bool ignoreScale) + { + return _console->GetVideoDecoder()->GetScreenSize(ignoreScale); + } + + DllExport void __stdcall WriteLogEntry(char* message) { MessageManager::Log(message); } + + DllExport void __stdcall PgoRunTest(vector testRoms, bool enableDebugger) + { + FolderUtilities::SetHomeFolder("../PGOMesenHome"); + + for(size_t i = 0; i < testRoms.size(); i++) { + std::cout << "Running: " << testRoms[i] << std::endl; + + _console.reset(new Console()); + _console->Initialize(); + _console->LoadRom((VirtualFile)testRoms[i], VirtualFile()); + + thread testThread([=] { + _console->Run(); + }); + std::this_thread::sleep_for(std::chrono::duration(5000)); + _console->Stop(); + testThread.join(); + _console->Release(); + } + } } \ No newline at end of file diff --git a/InteropDLL/InteropDLL.vcxproj b/InteropDLL/InteropDLL.vcxproj index 35700a9..cbcad71 100644 --- a/InteropDLL/InteropDLL.vcxproj +++ b/InteropDLL/InteropDLL.vcxproj @@ -453,6 +453,7 @@ + diff --git a/InteropDLL/InteropDLL.vcxproj.filters b/InteropDLL/InteropDLL.vcxproj.filters index 46010e8..d7f537e 100644 --- a/InteropDLL/InteropDLL.vcxproj.filters +++ b/InteropDLL/InteropDLL.vcxproj.filters @@ -31,5 +31,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/UI/Config/BaseConfig.cs b/UI/Config/BaseConfig.cs new file mode 100644 index 0000000..4f05139 --- /dev/null +++ b/UI/Config/BaseConfig.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Config +{ + [StructLayout(LayoutKind.Sequential)] + public class BaseConfig + { + public T Clone() + { + return (T)this.MemberwiseClone(); + } + } +} diff --git a/UI/Config/Configuration.cs b/UI/Config/Configuration.cs index ec4936f..330fa6e 100644 --- a/UI/Config/Configuration.cs +++ b/UI/Config/Configuration.cs @@ -17,6 +17,7 @@ namespace Mesen.GUI.Config public string Version = "0.1.0"; public List RecentFiles; + public VideoConfig Video; public DebugInfo Debug; public Point? WindowLocation; public Size? WindowSize; @@ -25,6 +26,7 @@ namespace Mesen.GUI.Config { RecentFiles = new List(); Debug = new DebugInfo(); + Video = new VideoConfig(); } ~Configuration() @@ -50,6 +52,7 @@ namespace Mesen.GUI.Config public void ApplyConfig() { + Video.ApplyConfig(); } public void InitializeDefaults() diff --git a/UI/Config/VideoConfig.cs b/UI/Config/VideoConfig.cs new file mode 100644 index 0000000..ce28cb5 --- /dev/null +++ b/UI/Config/VideoConfig.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Config +{ + [StructLayout(LayoutKind.Sequential)] + public class VideoConfig : BaseConfig + { + [MinMax(0.1, 10.0)] public double VideoScale = 2; + [MinMax(0.1, 5.0)] public double CustomAspectRatio = 1.0; + public VideoFilterType VideoFilter = VideoFilterType.None; + public VideoAspectRatio AspectRatio = VideoAspectRatio.NoStretching; + + [MarshalAs(UnmanagedType.I1)] public bool UseBilinearInterpolation = false; + [MarshalAs(UnmanagedType.I1)] public bool VerticalSync = false; + [MarshalAs(UnmanagedType.I1)] public bool IntegerFpsMode = false; + + [MinMax(-1, 1.0)] public double Brightness = 0; + [MinMax(-1, 1.0)] public double Contrast = 0; + [MinMax(-1, 1.0)] public double Hue = 0; + [MinMax(-1, 1.0)] public double Saturation = 0; + [MinMax(0, 1.0)] public double ScanlineIntensity = 0; + + [MinMax(-1, 1.0)] public double NtscArtifacts = 0; + [MinMax(-1, 1.0)] public double NtscBleed = 0; + [MinMax(-1, 1.0)] public double NtscFringing = 0; + [MinMax(-1, 1.0)] public double NtscGamma = 0; + [MinMax(-1, 1.0)] public double NtscResolution = 0; + [MinMax(-1, 1.0)] public double NtscSharpness = 0; + [MarshalAs(UnmanagedType.I1)] public bool NtscMergeFields = false; + + [MarshalAs(UnmanagedType.I1)] public bool FullscreenForceIntegerScale = false; + [MarshalAs(UnmanagedType.I1)] public bool UseExclusiveFullscreen = false; + public Int32 ExclusiveFullscreenRefreshRate = 60; + + public void ApplyConfig() + { + ConfigApi.SetVideoConfig(this); + } + } + + public enum VideoFilterType + { + None = 0, + NTSC, + xBRZ2x, + xBRZ3x, + xBRZ4x, + xBRZ5x, + xBRZ6x, + HQ2x, + HQ3x, + HQ4x, + Scale2x, + Scale3x, + Scale4x, + _2xSai, + Super2xSai, + SuperEagle, + Prescale2x, + Prescale3x, + Prescale4x, + Prescale6x, + Prescale8x, + Prescale10x + } + + public enum VideoAspectRatio + { + NoStretching = 0, + Auto = 1, + NTSC = 2, + PAL = 3, + Standard = 4, + Widescreen = 5, + Custom = 6 + } +} diff --git a/UI/Forms/BaseConfigForm.cs b/UI/Forms/BaseConfigForm.cs index d22330d..4b76659 100644 --- a/UI/Forms/BaseConfigForm.cs +++ b/UI/Forms/BaseConfigForm.cs @@ -74,11 +74,7 @@ namespace Mesen.GUI.Forms if(this.DialogResult == System.Windows.Forms.DialogResult.OK) { UpdateObject(); if(ApplyChangesOnOK) { - ConfigManager.ApplyChanges(); - } - } else { - if(ApplyChangesOnOK) { - ConfigManager.RejectChanges(); + OnApply(); } } base.OnFormClosed(e); @@ -89,6 +85,9 @@ namespace Mesen.GUI.Forms get { return true; } } + protected virtual void OnApply() + { + } protected virtual void UpdateConfig() { diff --git a/UI/Forms/Config/frmVideoConfig.Designer.cs b/UI/Forms/Config/frmVideoConfig.Designer.cs new file mode 100644 index 0000000..a07cfca --- /dev/null +++ b/UI/Forms/Config/frmVideoConfig.Designer.cs @@ -0,0 +1,900 @@ +namespace Mesen.GUI.Forms.Config +{ + partial class frmVideoConfig + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.tabMain = new System.Windows.Forms.TabControl(); + this.tpgGeneral = new System.Windows.Forms.TabPage(); + this.tlpMain = new System.Windows.Forms.TableLayoutPanel(); + this.chkUseExclusiveFullscreen = new System.Windows.Forms.CheckBox(); + this.lblVideoScale = new System.Windows.Forms.Label(); + this.chkVerticalSync = new System.Windows.Forms.CheckBox(); + this.lblDisplayRatio = new System.Windows.Forms.Label(); + this.nudScale = new Mesen.GUI.Controls.MesenNumericUpDown(); + this.flowLayoutPanel6 = new System.Windows.Forms.FlowLayoutPanel(); + this.cboAspectRatio = new System.Windows.Forms.ComboBox(); + this.lblCustomRatio = new System.Windows.Forms.Label(); + this.nudCustomRatio = new Mesen.GUI.Controls.MesenNumericUpDown(); + this.chkFullscreenForceIntegerScale = new System.Windows.Forms.CheckBox(); + this.chkShowFps = new System.Windows.Forms.CheckBox(); + this.chkIntegerFpsMode = new System.Windows.Forms.CheckBox(); + this.flpRefreshRate = new System.Windows.Forms.FlowLayoutPanel(); + this.lblRequestedRefreshRate = new System.Windows.Forms.Label(); + this.cboRefreshRate = new System.Windows.Forms.ComboBox(); + this.tpgPicture = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel7 = new System.Windows.Forms.TableLayoutPanel(); + this.btnSelectPreset = new System.Windows.Forms.Button(); + this.btnResetPictureSettings = new System.Windows.Forms.Button(); + this.grpNtscFilter = new System.Windows.Forms.GroupBox(); + this.tlpNtscFilter = new System.Windows.Forms.TableLayoutPanel(); + this.chkMergeFields = new System.Windows.Forms.CheckBox(); + this.trkArtifacts = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkBleed = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkFringing = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkGamma = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkResolution = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkSharpness = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.grpCommon = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); + this.chkBilinearInterpolation = new System.Windows.Forms.CheckBox(); + this.trkBrightness = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkContrast = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkHue = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.trkSaturation = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.grpScanlines = new System.Windows.Forms.GroupBox(); + this.trkScanlines = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.tableLayoutPanel8 = new System.Windows.Forms.TableLayoutPanel(); + this.cboFilter = new System.Windows.Forms.ComboBox(); + this.lblVideoFilter = new System.Windows.Forms.Label(); + this.ctxPicturePresets = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mnuPresetComposite = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPresetSVideo = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPresetRgb = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPresetMonochrome = new System.Windows.Forms.ToolStripMenuItem(); + this.tabMain.SuspendLayout(); + this.tpgGeneral.SuspendLayout(); + this.tlpMain.SuspendLayout(); + this.flowLayoutPanel6.SuspendLayout(); + this.flpRefreshRate.SuspendLayout(); + this.tpgPicture.SuspendLayout(); + this.tableLayoutPanel5.SuspendLayout(); + this.tableLayoutPanel7.SuspendLayout(); + this.grpNtscFilter.SuspendLayout(); + this.tlpNtscFilter.SuspendLayout(); + this.grpCommon.SuspendLayout(); + this.tableLayoutPanel4.SuspendLayout(); + this.grpScanlines.SuspendLayout(); + this.tableLayoutPanel8.SuspendLayout(); + this.ctxPicturePresets.SuspendLayout(); + this.SuspendLayout(); + // + // baseConfigPanel + // + this.baseConfigPanel.Location = new System.Drawing.Point(0, 408); + this.baseConfigPanel.Size = new System.Drawing.Size(574, 29); + // + // tabMain + // + this.tabMain.Controls.Add(this.tpgGeneral); + this.tabMain.Controls.Add(this.tpgPicture); + this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabMain.Location = new System.Drawing.Point(0, 0); + this.tabMain.Name = "tabMain"; + this.tabMain.SelectedIndex = 0; + this.tabMain.Size = new System.Drawing.Size(574, 408); + this.tabMain.TabIndex = 2; + // + // tpgGeneral + // + this.tpgGeneral.Controls.Add(this.tlpMain); + this.tpgGeneral.Location = new System.Drawing.Point(4, 22); + this.tpgGeneral.Name = "tpgGeneral"; + this.tpgGeneral.Padding = new System.Windows.Forms.Padding(3); + this.tpgGeneral.Size = new System.Drawing.Size(566, 382); + this.tpgGeneral.TabIndex = 5; + this.tpgGeneral.Text = "General"; + this.tpgGeneral.UseVisualStyleBackColor = true; + // + // tlpMain + // + this.tlpMain.ColumnCount = 2; + this.tlpMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tlpMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpMain.Controls.Add(this.chkUseExclusiveFullscreen, 0, 4); + this.tlpMain.Controls.Add(this.lblVideoScale, 0, 0); + this.tlpMain.Controls.Add(this.chkVerticalSync, 0, 3); + this.tlpMain.Controls.Add(this.lblDisplayRatio, 0, 1); + this.tlpMain.Controls.Add(this.nudScale, 1, 0); + this.tlpMain.Controls.Add(this.flowLayoutPanel6, 1, 1); + this.tlpMain.Controls.Add(this.chkFullscreenForceIntegerScale, 0, 6); + this.tlpMain.Controls.Add(this.chkShowFps, 0, 7); + this.tlpMain.Controls.Add(this.chkIntegerFpsMode, 0, 2); + this.tlpMain.Controls.Add(this.flpRefreshRate, 0, 5); + this.tlpMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.tlpMain.Location = new System.Drawing.Point(3, 3); + this.tlpMain.Margin = new System.Windows.Forms.Padding(0); + this.tlpMain.Name = "tlpMain"; + this.tlpMain.RowCount = 9; + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tlpMain.Size = new System.Drawing.Size(560, 376); + this.tlpMain.TabIndex = 1; + // + // chkUseExclusiveFullscreen + // + this.chkUseExclusiveFullscreen.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkUseExclusiveFullscreen.AutoSize = true; + this.tlpMain.SetColumnSpan(this.chkUseExclusiveFullscreen, 2); + this.chkUseExclusiveFullscreen.Location = new System.Drawing.Point(3, 96); + this.chkUseExclusiveFullscreen.Name = "chkUseExclusiveFullscreen"; + this.chkUseExclusiveFullscreen.Size = new System.Drawing.Size(169, 17); + this.chkUseExclusiveFullscreen.TabIndex = 24; + this.chkUseExclusiveFullscreen.Text = "Use exclusive fullscreen mode"; + this.chkUseExclusiveFullscreen.UseVisualStyleBackColor = true; + this.chkUseExclusiveFullscreen.Visible = false; + // + // lblVideoScale + // + this.lblVideoScale.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblVideoScale.AutoSize = true; + this.lblVideoScale.Location = new System.Drawing.Point(3, 4); + this.lblVideoScale.Name = "lblVideoScale"; + this.lblVideoScale.Size = new System.Drawing.Size(37, 13); + this.lblVideoScale.TabIndex = 11; + this.lblVideoScale.Text = "Scale:"; + // + // chkVerticalSync + // + this.chkVerticalSync.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkVerticalSync.AutoSize = true; + this.tlpMain.SetColumnSpan(this.chkVerticalSync, 2); + this.chkVerticalSync.Location = new System.Drawing.Point(3, 73); + this.chkVerticalSync.Name = "chkVerticalSync"; + this.chkVerticalSync.Size = new System.Drawing.Size(121, 17); + this.chkVerticalSync.TabIndex = 15; + this.chkVerticalSync.Text = "Enable vertical sync"; + this.chkVerticalSync.UseVisualStyleBackColor = true; + // + // lblDisplayRatio + // + this.lblDisplayRatio.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblDisplayRatio.AutoSize = true; + this.lblDisplayRatio.Location = new System.Drawing.Point(3, 27); + this.lblDisplayRatio.Name = "lblDisplayRatio"; + this.lblDisplayRatio.Size = new System.Drawing.Size(71, 13); + this.lblDisplayRatio.TabIndex = 17; + this.lblDisplayRatio.Text = "Aspect Ratio:"; + // + // nudScale + // + this.nudScale.DecimalPlaces = 2; + this.nudScale.Increment = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudScale.Location = new System.Drawing.Point(77, 0); + this.nudScale.Margin = new System.Windows.Forms.Padding(0); + this.nudScale.Maximum = new decimal(new int[] { + 10, + 0, + 0, + 0}); + this.nudScale.MaximumSize = new System.Drawing.Size(10000, 20); + this.nudScale.Minimum = new decimal(new int[] { + 5, + 0, + 0, + 65536}); + this.nudScale.MinimumSize = new System.Drawing.Size(0, 21); + this.nudScale.Name = "nudScale"; + this.nudScale.Size = new System.Drawing.Size(48, 21); + this.nudScale.TabIndex = 21; + this.nudScale.Value = new decimal(new int[] { + 1, + 0, + 0, + 0}); + // + // flowLayoutPanel6 + // + this.flowLayoutPanel6.Controls.Add(this.cboAspectRatio); + this.flowLayoutPanel6.Controls.Add(this.lblCustomRatio); + this.flowLayoutPanel6.Controls.Add(this.nudCustomRatio); + this.flowLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel6.Location = new System.Drawing.Point(77, 21); + this.flowLayoutPanel6.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel6.Name = "flowLayoutPanel6"; + this.flowLayoutPanel6.Size = new System.Drawing.Size(483, 26); + this.flowLayoutPanel6.TabIndex = 22; + // + // cboAspectRatio + // + this.cboAspectRatio.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboAspectRatio.FormattingEnabled = true; + this.cboAspectRatio.Items.AddRange(new object[] { + "Auto", + "NTSC (8:7)", + "PAL (18:13)", + "Standard (4:3)", + "Widescreen (16:9)"}); + this.cboAspectRatio.Location = new System.Drawing.Point(3, 3); + this.cboAspectRatio.Name = "cboAspectRatio"; + this.cboAspectRatio.Size = new System.Drawing.Size(197, 21); + this.cboAspectRatio.TabIndex = 16; + // + // lblCustomRatio + // + this.lblCustomRatio.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblCustomRatio.AutoSize = true; + this.lblCustomRatio.Location = new System.Drawing.Point(206, 7); + this.lblCustomRatio.Name = "lblCustomRatio"; + this.lblCustomRatio.Size = new System.Drawing.Size(76, 13); + this.lblCustomRatio.TabIndex = 17; + this.lblCustomRatio.Text = "Custom Ratio: "; + this.lblCustomRatio.Visible = false; + // + // nudCustomRatio + // + this.nudCustomRatio.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.nudCustomRatio.DecimalPlaces = 3; + this.nudCustomRatio.Increment = new decimal(new int[] { + 1, + 0, + 0, + 65536}); + this.nudCustomRatio.Location = new System.Drawing.Point(285, 3); + this.nudCustomRatio.Margin = new System.Windows.Forms.Padding(0); + this.nudCustomRatio.Maximum = new decimal(new int[] { + 5, + 0, + 0, + 0}); + this.nudCustomRatio.MaximumSize = new System.Drawing.Size(10000, 20); + this.nudCustomRatio.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 65536}); + this.nudCustomRatio.MinimumSize = new System.Drawing.Size(0, 21); + this.nudCustomRatio.Name = "nudCustomRatio"; + this.nudCustomRatio.Size = new System.Drawing.Size(48, 21); + this.nudCustomRatio.TabIndex = 22; + this.nudCustomRatio.Value = new decimal(new int[] { + 1, + 0, + 0, + 65536}); + this.nudCustomRatio.Visible = false; + // + // chkFullscreenForceIntegerScale + // + this.chkFullscreenForceIntegerScale.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkFullscreenForceIntegerScale.AutoSize = true; + this.tlpMain.SetColumnSpan(this.chkFullscreenForceIntegerScale, 2); + this.chkFullscreenForceIntegerScale.Location = new System.Drawing.Point(3, 146); + this.chkFullscreenForceIntegerScale.Name = "chkFullscreenForceIntegerScale"; + this.chkFullscreenForceIntegerScale.Size = new System.Drawing.Size(289, 17); + this.chkFullscreenForceIntegerScale.TabIndex = 23; + this.chkFullscreenForceIntegerScale.Text = "Use integer scale values when entering fullscreen mode"; + this.chkFullscreenForceIntegerScale.UseVisualStyleBackColor = true; + this.chkFullscreenForceIntegerScale.Visible = false; + // + // chkShowFps + // + this.chkShowFps.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkShowFps.AutoSize = true; + this.tlpMain.SetColumnSpan(this.chkShowFps, 2); + this.chkShowFps.Location = new System.Drawing.Point(3, 169); + this.chkShowFps.Name = "chkShowFps"; + this.chkShowFps.Size = new System.Drawing.Size(76, 17); + this.chkShowFps.TabIndex = 9; + this.chkShowFps.Text = "Show FPS"; + this.chkShowFps.UseVisualStyleBackColor = true; + this.chkShowFps.Visible = false; + // + // chkIntegerFpsMode + // + this.chkIntegerFpsMode.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkIntegerFpsMode.AutoSize = true; + this.tlpMain.SetColumnSpan(this.chkIntegerFpsMode, 2); + this.chkIntegerFpsMode.Location = new System.Drawing.Point(3, 50); + this.chkIntegerFpsMode.Name = "chkIntegerFpsMode"; + this.chkIntegerFpsMode.Size = new System.Drawing.Size(308, 17); + this.chkIntegerFpsMode.TabIndex = 24; + this.chkIntegerFpsMode.Text = "Enable integer FPS mode (e.g: run at 60 fps instead of 60.1)"; + this.chkIntegerFpsMode.UseVisualStyleBackColor = true; + this.chkIntegerFpsMode.Visible = false; + // + // flpRefreshRate + // + this.tlpMain.SetColumnSpan(this.flpRefreshRate, 2); + this.flpRefreshRate.Controls.Add(this.lblRequestedRefreshRate); + this.flpRefreshRate.Controls.Add(this.cboRefreshRate); + this.flpRefreshRate.Dock = System.Windows.Forms.DockStyle.Fill; + this.flpRefreshRate.Location = new System.Drawing.Point(30, 116); + this.flpRefreshRate.Margin = new System.Windows.Forms.Padding(30, 0, 0, 0); + this.flpRefreshRate.Name = "flpRefreshRate"; + this.flpRefreshRate.Size = new System.Drawing.Size(530, 27); + this.flpRefreshRate.TabIndex = 26; + this.flpRefreshRate.Visible = false; + // + // lblRequestedRefreshRate + // + this.lblRequestedRefreshRate.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.lblRequestedRefreshRate.AutoSize = true; + this.lblRequestedRefreshRate.Location = new System.Drawing.Point(3, 7); + this.lblRequestedRefreshRate.Name = "lblRequestedRefreshRate"; + this.lblRequestedRefreshRate.Size = new System.Drawing.Size(128, 13); + this.lblRequestedRefreshRate.TabIndex = 17; + this.lblRequestedRefreshRate.Text = "Requested Refresh Rate:"; + // + // cboRefreshRate + // + this.cboRefreshRate.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboRefreshRate.FormattingEnabled = true; + this.cboRefreshRate.Items.AddRange(new object[] { + "Auto", + "NTSC (8:7)", + "PAL (18:13)", + "Standard (4:3)", + "Widescreen (16:9)"}); + this.cboRefreshRate.Location = new System.Drawing.Point(137, 3); + this.cboRefreshRate.Name = "cboRefreshRate"; + this.cboRefreshRate.Size = new System.Drawing.Size(68, 21); + this.cboRefreshRate.TabIndex = 25; + // + // tpgPicture + // + this.tpgPicture.Controls.Add(this.tableLayoutPanel5); + this.tpgPicture.Location = new System.Drawing.Point(4, 22); + this.tpgPicture.Name = "tpgPicture"; + this.tpgPicture.Padding = new System.Windows.Forms.Padding(3); + this.tpgPicture.Size = new System.Drawing.Size(566, 382); + this.tpgPicture.TabIndex = 4; + this.tpgPicture.Text = "Picture"; + this.tpgPicture.UseVisualStyleBackColor = true; + // + // tableLayoutPanel5 + // + this.tableLayoutPanel5.ColumnCount = 2; + this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel5.Controls.Add(this.tableLayoutPanel7, 0, 3); + this.tableLayoutPanel5.Controls.Add(this.grpNtscFilter, 1, 1); + this.tableLayoutPanel5.Controls.Add(this.grpCommon, 0, 1); + this.tableLayoutPanel5.Controls.Add(this.grpScanlines, 0, 2); + this.tableLayoutPanel5.Controls.Add(this.tableLayoutPanel8, 0, 0); + this.tableLayoutPanel5.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel5.Location = new System.Drawing.Point(3, 3); + this.tableLayoutPanel5.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel5.Name = "tableLayoutPanel5"; + this.tableLayoutPanel5.RowCount = 4; + this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel5.Size = new System.Drawing.Size(560, 376); + this.tableLayoutPanel5.TabIndex = 5; + // + // tableLayoutPanel7 + // + this.tableLayoutPanel7.ColumnCount = 2; + this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 36.92308F)); + this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 63.07692F)); + this.tableLayoutPanel7.Controls.Add(this.btnSelectPreset, 1, 0); + this.tableLayoutPanel7.Controls.Add(this.btnResetPictureSettings, 0, 0); + this.tableLayoutPanel7.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel7.Location = new System.Drawing.Point(0, 341); + this.tableLayoutPanel7.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel7.Name = "tableLayoutPanel7"; + this.tableLayoutPanel7.RowCount = 1; + this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel7.Size = new System.Drawing.Size(280, 35); + this.tableLayoutPanel7.TabIndex = 3; + // + // btnSelectPreset + // + this.btnSelectPreset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnSelectPreset.AutoSize = true; + this.btnSelectPreset.Image = global::Mesen.GUI.Properties.Resources.DownArrow; + this.btnSelectPreset.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; + this.btnSelectPreset.Location = new System.Drawing.Point(178, 9); + this.btnSelectPreset.Name = "btnSelectPreset"; + this.btnSelectPreset.Padding = new System.Windows.Forms.Padding(0, 0, 3, 0); + this.btnSelectPreset.Size = new System.Drawing.Size(99, 23); + this.btnSelectPreset.TabIndex = 3; + this.btnSelectPreset.Text = "Select Preset..."; + this.btnSelectPreset.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; + this.btnSelectPreset.UseVisualStyleBackColor = true; + this.btnSelectPreset.Click += new System.EventHandler(this.btnSelectPreset_Click); + // + // btnResetPictureSettings + // + this.btnResetPictureSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnResetPictureSettings.AutoSize = true; + this.btnResetPictureSettings.Location = new System.Drawing.Point(3, 9); + this.btnResetPictureSettings.Name = "btnResetPictureSettings"; + this.btnResetPictureSettings.Size = new System.Drawing.Size(45, 23); + this.btnResetPictureSettings.TabIndex = 3; + this.btnResetPictureSettings.Text = "Reset"; + this.btnResetPictureSettings.UseVisualStyleBackColor = true; + this.btnResetPictureSettings.Click += new System.EventHandler(this.btnResetPictureSettings_Click); + // + // grpNtscFilter + // + this.grpNtscFilter.Controls.Add(this.tlpNtscFilter); + this.grpNtscFilter.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpNtscFilter.Location = new System.Drawing.Point(282, 27); + this.grpNtscFilter.Margin = new System.Windows.Forms.Padding(2, 0, 0, 0); + this.grpNtscFilter.Name = "grpNtscFilter"; + this.tableLayoutPanel5.SetRowSpan(this.grpNtscFilter, 3); + this.grpNtscFilter.Size = new System.Drawing.Size(278, 349); + this.grpNtscFilter.TabIndex = 4; + this.grpNtscFilter.TabStop = false; + this.grpNtscFilter.Text = "NTSC Filter"; + // + // tlpNtscFilter + // + this.tlpNtscFilter.ColumnCount = 1; + this.tlpNtscFilter.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpNtscFilter.Controls.Add(this.chkMergeFields, 0, 6); + this.tlpNtscFilter.Controls.Add(this.trkArtifacts, 0, 0); + this.tlpNtscFilter.Controls.Add(this.trkBleed, 0, 1); + this.tlpNtscFilter.Controls.Add(this.trkFringing, 0, 2); + this.tlpNtscFilter.Controls.Add(this.trkGamma, 0, 3); + this.tlpNtscFilter.Controls.Add(this.trkResolution, 0, 4); + this.tlpNtscFilter.Controls.Add(this.trkSharpness, 0, 5); + this.tlpNtscFilter.Dock = System.Windows.Forms.DockStyle.Fill; + this.tlpNtscFilter.Location = new System.Drawing.Point(3, 16); + this.tlpNtscFilter.Margin = new System.Windows.Forms.Padding(0); + this.tlpNtscFilter.Name = "tlpNtscFilter"; + this.tlpNtscFilter.RowCount = 7; + this.tlpNtscFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpNtscFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpNtscFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpNtscFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpNtscFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpNtscFilter.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpNtscFilter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpNtscFilter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tlpNtscFilter.Size = new System.Drawing.Size(272, 330); + this.tlpNtscFilter.TabIndex = 5; + // + // chkMergeFields + // + this.chkMergeFields.AutoSize = true; + this.chkMergeFields.Location = new System.Drawing.Point(3, 303); + this.chkMergeFields.Name = "chkMergeFields"; + this.chkMergeFields.Size = new System.Drawing.Size(86, 17); + this.chkMergeFields.TabIndex = 30; + this.chkMergeFields.Text = "Merge Fields"; + this.chkMergeFields.UseVisualStyleBackColor = true; + // + // trkArtifacts + // + this.trkArtifacts.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkArtifacts.Location = new System.Drawing.Point(0, 0); + this.trkArtifacts.Margin = new System.Windows.Forms.Padding(0); + this.trkArtifacts.Maximum = 100; + this.trkArtifacts.MaximumSize = new System.Drawing.Size(0, 60); + this.trkArtifacts.Minimum = -100; + this.trkArtifacts.MinimumSize = new System.Drawing.Size(206, 50); + this.trkArtifacts.Name = "trkArtifacts"; + this.trkArtifacts.Size = new System.Drawing.Size(272, 50); + this.trkArtifacts.TabIndex = 24; + this.trkArtifacts.Text = "Artifacts"; + this.trkArtifacts.Value = 0; + // + // trkBleed + // + this.trkBleed.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkBleed.Location = new System.Drawing.Point(0, 50); + this.trkBleed.Margin = new System.Windows.Forms.Padding(0); + this.trkBleed.Maximum = 100; + this.trkBleed.MaximumSize = new System.Drawing.Size(400, 55); + this.trkBleed.Minimum = -100; + this.trkBleed.MinimumSize = new System.Drawing.Size(206, 50); + this.trkBleed.Name = "trkBleed"; + this.trkBleed.Size = new System.Drawing.Size(272, 50); + this.trkBleed.TabIndex = 25; + this.trkBleed.Text = "Bleed"; + this.trkBleed.Value = 0; + // + // trkFringing + // + this.trkFringing.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkFringing.Location = new System.Drawing.Point(0, 100); + this.trkFringing.Margin = new System.Windows.Forms.Padding(0); + this.trkFringing.Maximum = 100; + this.trkFringing.MaximumSize = new System.Drawing.Size(0, 41); + this.trkFringing.Minimum = -100; + this.trkFringing.MinimumSize = new System.Drawing.Size(206, 50); + this.trkFringing.Name = "trkFringing"; + this.trkFringing.Size = new System.Drawing.Size(272, 50); + this.trkFringing.TabIndex = 26; + this.trkFringing.Text = "Fringing"; + this.trkFringing.Value = 0; + // + // trkGamma + // + this.trkGamma.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkGamma.Location = new System.Drawing.Point(0, 150); + this.trkGamma.Margin = new System.Windows.Forms.Padding(0); + this.trkGamma.Maximum = 100; + this.trkGamma.MaximumSize = new System.Drawing.Size(0, 41); + this.trkGamma.Minimum = -100; + this.trkGamma.MinimumSize = new System.Drawing.Size(206, 50); + this.trkGamma.Name = "trkGamma"; + this.trkGamma.Size = new System.Drawing.Size(272, 50); + this.trkGamma.TabIndex = 27; + this.trkGamma.Text = "Gamma"; + this.trkGamma.Value = 0; + // + // trkResolution + // + this.trkResolution.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkResolution.Location = new System.Drawing.Point(0, 200); + this.trkResolution.Margin = new System.Windows.Forms.Padding(0); + this.trkResolution.Maximum = 100; + this.trkResolution.MaximumSize = new System.Drawing.Size(0, 41); + this.trkResolution.Minimum = -100; + this.trkResolution.MinimumSize = new System.Drawing.Size(206, 50); + this.trkResolution.Name = "trkResolution"; + this.trkResolution.Size = new System.Drawing.Size(272, 50); + this.trkResolution.TabIndex = 28; + this.trkResolution.Text = "Resolution"; + this.trkResolution.Value = 0; + // + // trkSharpness + // + this.trkSharpness.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkSharpness.Location = new System.Drawing.Point(0, 250); + this.trkSharpness.Margin = new System.Windows.Forms.Padding(0); + this.trkSharpness.Maximum = 100; + this.trkSharpness.MaximumSize = new System.Drawing.Size(0, 41); + this.trkSharpness.Minimum = -100; + this.trkSharpness.MinimumSize = new System.Drawing.Size(206, 50); + this.trkSharpness.Name = "trkSharpness"; + this.trkSharpness.Size = new System.Drawing.Size(272, 50); + this.trkSharpness.TabIndex = 29; + this.trkSharpness.Text = "Sharpness"; + this.trkSharpness.Value = 0; + // + // grpCommon + // + this.grpCommon.Controls.Add(this.tableLayoutPanel4); + this.grpCommon.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpCommon.Location = new System.Drawing.Point(0, 27); + this.grpCommon.Margin = new System.Windows.Forms.Padding(0, 0, 2, 0); + this.grpCommon.Name = "grpCommon"; + this.grpCommon.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.grpCommon.Size = new System.Drawing.Size(278, 242); + this.grpCommon.TabIndex = 3; + this.grpCommon.TabStop = false; + this.grpCommon.Text = "Common Settings"; + // + // tableLayoutPanel4 + // + this.tableLayoutPanel4.ColumnCount = 1; + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel4.Controls.Add(this.chkBilinearInterpolation, 0, 4); + this.tableLayoutPanel4.Controls.Add(this.trkBrightness, 0, 0); + this.tableLayoutPanel4.Controls.Add(this.trkContrast, 0, 1); + this.tableLayoutPanel4.Controls.Add(this.trkHue, 0, 2); + this.tableLayoutPanel4.Controls.Add(this.trkSaturation, 0, 3); + this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel4.Location = new System.Drawing.Point(3, 15); + this.tableLayoutPanel4.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel4.Name = "tableLayoutPanel4"; + this.tableLayoutPanel4.RowCount = 5; + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel4.Size = new System.Drawing.Size(272, 225); + this.tableLayoutPanel4.TabIndex = 4; + // + // chkBilinearInterpolation + // + this.chkBilinearInterpolation.AutoSize = true; + this.tableLayoutPanel4.SetColumnSpan(this.chkBilinearInterpolation, 2); + this.chkBilinearInterpolation.Location = new System.Drawing.Point(3, 203); + this.chkBilinearInterpolation.Name = "chkBilinearInterpolation"; + this.chkBilinearInterpolation.Size = new System.Drawing.Size(206, 17); + this.chkBilinearInterpolation.TabIndex = 28; + this.chkBilinearInterpolation.Text = "Use bilinear interpolation when scaling"; + this.chkBilinearInterpolation.UseVisualStyleBackColor = true; + // + // trkBrightness + // + this.trkBrightness.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkBrightness.Location = new System.Drawing.Point(0, 0); + this.trkBrightness.Margin = new System.Windows.Forms.Padding(0); + this.trkBrightness.Maximum = 100; + this.trkBrightness.MaximumSize = new System.Drawing.Size(0, 60); + this.trkBrightness.Minimum = -100; + this.trkBrightness.MinimumSize = new System.Drawing.Size(206, 50); + this.trkBrightness.Name = "trkBrightness"; + this.trkBrightness.Size = new System.Drawing.Size(272, 50); + this.trkBrightness.TabIndex = 24; + this.trkBrightness.Text = "Brightness"; + this.trkBrightness.Value = 0; + // + // trkContrast + // + this.trkContrast.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkContrast.Location = new System.Drawing.Point(0, 50); + this.trkContrast.Margin = new System.Windows.Forms.Padding(0); + this.trkContrast.Maximum = 100; + this.trkContrast.MaximumSize = new System.Drawing.Size(400, 55); + this.trkContrast.Minimum = -100; + this.trkContrast.MinimumSize = new System.Drawing.Size(206, 50); + this.trkContrast.Name = "trkContrast"; + this.trkContrast.Size = new System.Drawing.Size(272, 50); + this.trkContrast.TabIndex = 25; + this.trkContrast.Text = "Contrast"; + this.trkContrast.Value = 0; + // + // trkHue + // + this.trkHue.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkHue.Location = new System.Drawing.Point(0, 100); + this.trkHue.Margin = new System.Windows.Forms.Padding(0); + this.trkHue.Maximum = 100; + this.trkHue.MaximumSize = new System.Drawing.Size(0, 41); + this.trkHue.Minimum = -100; + this.trkHue.MinimumSize = new System.Drawing.Size(206, 50); + this.trkHue.Name = "trkHue"; + this.trkHue.Size = new System.Drawing.Size(272, 50); + this.trkHue.TabIndex = 26; + this.trkHue.Text = "Hue"; + this.trkHue.Value = 0; + // + // trkSaturation + // + this.trkSaturation.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkSaturation.Location = new System.Drawing.Point(0, 150); + this.trkSaturation.Margin = new System.Windows.Forms.Padding(0); + this.trkSaturation.Maximum = 100; + this.trkSaturation.MaximumSize = new System.Drawing.Size(0, 41); + this.trkSaturation.Minimum = -100; + this.trkSaturation.MinimumSize = new System.Drawing.Size(206, 50); + this.trkSaturation.Name = "trkSaturation"; + this.trkSaturation.Size = new System.Drawing.Size(272, 50); + this.trkSaturation.TabIndex = 27; + this.trkSaturation.Text = "Saturation"; + this.trkSaturation.Value = 0; + // + // grpScanlines + // + this.grpScanlines.Controls.Add(this.trkScanlines); + this.grpScanlines.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpScanlines.Location = new System.Drawing.Point(0, 269); + this.grpScanlines.Margin = new System.Windows.Forms.Padding(0, 0, 2, 0); + this.grpScanlines.Name = "grpScanlines"; + this.grpScanlines.Size = new System.Drawing.Size(278, 72); + this.grpScanlines.TabIndex = 5; + this.grpScanlines.TabStop = false; + this.grpScanlines.Text = "Scanlines"; + // + // trkScanlines + // + this.trkScanlines.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkScanlines.Location = new System.Drawing.Point(3, 16); + this.trkScanlines.Margin = new System.Windows.Forms.Padding(0); + this.trkScanlines.Maximum = 100; + this.trkScanlines.MaximumSize = new System.Drawing.Size(0, 41); + this.trkScanlines.Minimum = 0; + this.trkScanlines.MinimumSize = new System.Drawing.Size(206, 50); + this.trkScanlines.Name = "trkScanlines"; + this.trkScanlines.Size = new System.Drawing.Size(272, 50); + this.trkScanlines.TabIndex = 28; + this.trkScanlines.Text = "Scanlines"; + this.trkScanlines.Value = 0; + // + // tableLayoutPanel8 + // + this.tableLayoutPanel8.ColumnCount = 2; + this.tableLayoutPanel5.SetColumnSpan(this.tableLayoutPanel8, 2); + this.tableLayoutPanel8.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel8.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel8.Controls.Add(this.cboFilter, 1, 0); + this.tableLayoutPanel8.Controls.Add(this.lblVideoFilter, 0, 0); + this.tableLayoutPanel8.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel8.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel8.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel8.Name = "tableLayoutPanel8"; + this.tableLayoutPanel8.RowCount = 1; + this.tableLayoutPanel8.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel8.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F)); + this.tableLayoutPanel8.Size = new System.Drawing.Size(560, 27); + this.tableLayoutPanel8.TabIndex = 6; + // + // cboFilter + // + this.cboFilter.Dock = System.Windows.Forms.DockStyle.Fill; + this.cboFilter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboFilter.FormattingEnabled = true; + this.cboFilter.Items.AddRange(new object[] { + "None", + "NTSC"}); + this.cboFilter.Location = new System.Drawing.Point(41, 3); + this.cboFilter.Name = "cboFilter"; + this.cboFilter.Size = new System.Drawing.Size(516, 21); + this.cboFilter.TabIndex = 15; + // + // lblVideoFilter + // + this.lblVideoFilter.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblVideoFilter.AutoSize = true; + this.lblVideoFilter.Location = new System.Drawing.Point(3, 7); + this.lblVideoFilter.Name = "lblVideoFilter"; + this.lblVideoFilter.Size = new System.Drawing.Size(32, 13); + this.lblVideoFilter.TabIndex = 13; + this.lblVideoFilter.Text = "Filter:"; + // + // ctxPicturePresets + // + this.ctxPicturePresets.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuPresetComposite, + this.mnuPresetSVideo, + this.mnuPresetRgb, + this.mnuPresetMonochrome}); + this.ctxPicturePresets.Name = "contextPicturePresets"; + this.ctxPicturePresets.Size = new System.Drawing.Size(148, 92); + // + // mnuPresetComposite + // + this.mnuPresetComposite.Name = "mnuPresetComposite"; + this.mnuPresetComposite.Size = new System.Drawing.Size(147, 22); + this.mnuPresetComposite.Text = "Composite"; + this.mnuPresetComposite.Click += new System.EventHandler(this.mnuPresetComposite_Click); + // + // mnuPresetSVideo + // + this.mnuPresetSVideo.Name = "mnuPresetSVideo"; + this.mnuPresetSVideo.Size = new System.Drawing.Size(147, 22); + this.mnuPresetSVideo.Text = "S-Video"; + this.mnuPresetSVideo.Click += new System.EventHandler(this.mnuPresetSVideo_Click); + // + // mnuPresetRgb + // + this.mnuPresetRgb.Name = "mnuPresetRgb"; + this.mnuPresetRgb.Size = new System.Drawing.Size(147, 22); + this.mnuPresetRgb.Text = "RGB"; + this.mnuPresetRgb.Click += new System.EventHandler(this.mnuPresetRgb_Click); + // + // mnuPresetMonochrome + // + this.mnuPresetMonochrome.Name = "mnuPresetMonochrome"; + this.mnuPresetMonochrome.Size = new System.Drawing.Size(147, 22); + this.mnuPresetMonochrome.Text = "Monochrome"; + this.mnuPresetMonochrome.Click += new System.EventHandler(this.mnuPresetMonochrome_Click); + // + // frmVideoConfig + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(574, 437); + this.Controls.Add(this.tabMain); + this.Name = "frmVideoConfig"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "frmVideoConfig"; + this.Controls.SetChildIndex(this.baseConfigPanel, 0); + this.Controls.SetChildIndex(this.tabMain, 0); + this.tabMain.ResumeLayout(false); + this.tpgGeneral.ResumeLayout(false); + this.tlpMain.ResumeLayout(false); + this.tlpMain.PerformLayout(); + this.flowLayoutPanel6.ResumeLayout(false); + this.flowLayoutPanel6.PerformLayout(); + this.flpRefreshRate.ResumeLayout(false); + this.flpRefreshRate.PerformLayout(); + this.tpgPicture.ResumeLayout(false); + this.tableLayoutPanel5.ResumeLayout(false); + this.tableLayoutPanel7.ResumeLayout(false); + this.tableLayoutPanel7.PerformLayout(); + this.grpNtscFilter.ResumeLayout(false); + this.tlpNtscFilter.ResumeLayout(false); + this.tlpNtscFilter.PerformLayout(); + this.grpCommon.ResumeLayout(false); + this.tableLayoutPanel4.ResumeLayout(false); + this.tableLayoutPanel4.PerformLayout(); + this.grpScanlines.ResumeLayout(false); + this.tableLayoutPanel8.ResumeLayout(false); + this.tableLayoutPanel8.PerformLayout(); + this.ctxPicturePresets.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabMain; + private System.Windows.Forms.TabPage tpgGeneral; + private System.Windows.Forms.TableLayoutPanel tlpMain; + private System.Windows.Forms.CheckBox chkUseExclusiveFullscreen; + private System.Windows.Forms.Label lblVideoScale; + private System.Windows.Forms.CheckBox chkVerticalSync; + private System.Windows.Forms.Label lblDisplayRatio; + private Controls.MesenNumericUpDown nudScale; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel6; + private System.Windows.Forms.ComboBox cboAspectRatio; + private System.Windows.Forms.Label lblCustomRatio; + private Controls.MesenNumericUpDown nudCustomRatio; + private System.Windows.Forms.CheckBox chkFullscreenForceIntegerScale; + private System.Windows.Forms.CheckBox chkShowFps; + private System.Windows.Forms.CheckBox chkIntegerFpsMode; + private System.Windows.Forms.FlowLayoutPanel flpRefreshRate; + private System.Windows.Forms.Label lblRequestedRefreshRate; + private System.Windows.Forms.ComboBox cboRefreshRate; + private System.Windows.Forms.TabPage tpgPicture; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel7; + private System.Windows.Forms.Button btnSelectPreset; + private System.Windows.Forms.Button btnResetPictureSettings; + private System.Windows.Forms.GroupBox grpNtscFilter; + private System.Windows.Forms.TableLayoutPanel tlpNtscFilter; + private System.Windows.Forms.CheckBox chkMergeFields; + private Controls.ctrlHorizontalTrackbar trkArtifacts; + private Controls.ctrlHorizontalTrackbar trkBleed; + private Controls.ctrlHorizontalTrackbar trkFringing; + private Controls.ctrlHorizontalTrackbar trkGamma; + private Controls.ctrlHorizontalTrackbar trkResolution; + private Controls.ctrlHorizontalTrackbar trkSharpness; + private System.Windows.Forms.GroupBox grpCommon; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4; + private System.Windows.Forms.CheckBox chkBilinearInterpolation; + private Controls.ctrlHorizontalTrackbar trkBrightness; + private Controls.ctrlHorizontalTrackbar trkContrast; + private Controls.ctrlHorizontalTrackbar trkHue; + private Controls.ctrlHorizontalTrackbar trkSaturation; + private System.Windows.Forms.GroupBox grpScanlines; + private Controls.ctrlHorizontalTrackbar trkScanlines; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel8; + private System.Windows.Forms.ComboBox cboFilter; + private System.Windows.Forms.Label lblVideoFilter; + private System.Windows.Forms.ContextMenuStrip ctxPicturePresets; + private System.Windows.Forms.ToolStripMenuItem mnuPresetComposite; + private System.Windows.Forms.ToolStripMenuItem mnuPresetSVideo; + private System.Windows.Forms.ToolStripMenuItem mnuPresetRgb; + private System.Windows.Forms.ToolStripMenuItem mnuPresetMonochrome; + } +} \ No newline at end of file diff --git a/UI/Forms/Config/frmVideoConfig.cs b/UI/Forms/Config/frmVideoConfig.cs new file mode 100644 index 0000000..00c843c --- /dev/null +++ b/UI/Forms/Config/frmVideoConfig.cs @@ -0,0 +1,128 @@ +using Mesen.GUI.Config; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms.Config +{ + public partial class frmVideoConfig : BaseConfigForm + { + public frmVideoConfig() + { + InitializeComponent(); + if(DesignMode) { + return; + } + + Entity = ConfigManager.Config.Video.Clone(); + + //AddBinding(nameof(VideoConfig.ShowFPS), chkShowFps); + AddBinding(nameof(VideoConfig.UseBilinearInterpolation), chkBilinearInterpolation); + AddBinding(nameof(VideoConfig.VerticalSync), chkVerticalSync); + AddBinding(nameof(VideoConfig.IntegerFpsMode), chkIntegerFpsMode); + AddBinding(nameof(VideoConfig.FullscreenForceIntegerScale), chkFullscreenForceIntegerScale); + AddBinding(nameof(VideoConfig.UseExclusiveFullscreen), chkUseExclusiveFullscreen); + AddBinding(nameof(VideoConfig.ExclusiveFullscreenRefreshRate), cboRefreshRate); + + AddBinding(nameof(VideoConfig.VideoScale), nudScale); + AddBinding(nameof(VideoConfig.AspectRatio), cboAspectRatio); + AddBinding(nameof(VideoConfig.CustomAspectRatio), nudCustomRatio); + AddBinding(nameof(VideoConfig.VideoFilter), cboFilter); + + AddBinding(nameof(VideoConfig.Brightness), trkBrightness); + AddBinding(nameof(VideoConfig.Contrast), trkContrast); + AddBinding(nameof(VideoConfig.Hue), trkHue); + AddBinding(nameof(VideoConfig.Saturation), trkSaturation); + AddBinding(nameof(VideoConfig.ScanlineIntensity), trkScanlines); + + AddBinding(nameof(VideoConfig.NtscArtifacts), trkArtifacts); + AddBinding(nameof(VideoConfig.NtscBleed), trkBleed); + AddBinding(nameof(VideoConfig.NtscFringing), trkFringing); + AddBinding(nameof(VideoConfig.NtscGamma), trkGamma); + AddBinding(nameof(VideoConfig.NtscResolution), trkResolution); + AddBinding(nameof(VideoConfig.NtscSharpness), trkSharpness); + AddBinding(nameof(VideoConfig.NtscMergeFields), chkMergeFields); + } + + protected override bool ValidateInput() + { + UpdateObject(); + UpdateCustomRatioVisibility(); + + VideoFilterType filter = ((VideoConfig)Entity).VideoFilter; + grpNtscFilter.Visible = (filter == VideoFilterType.NTSC); + + ((VideoConfig)this.Entity).ApplyConfig(); + + return true; + } + + protected override void OnApply() + { + ConfigManager.Config.Video = (VideoConfig)this.Entity; + ConfigManager.ApplyChanges(); + } + + private void UpdateCustomRatioVisibility() + { + VideoAspectRatio ratio = cboAspectRatio.GetEnumValue(); + lblCustomRatio.Visible = ratio == VideoAspectRatio.Custom; + nudCustomRatio.Visible = ratio == VideoAspectRatio.Custom; + } + + private void btnResetPictureSettings_Click(object sender, EventArgs e) + { + SetNtscPreset(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false); + cboFilter.SetEnumValue(VideoFilterType.None); + } + + private void btnSelectPreset_Click(object sender, EventArgs e) + { + ctxPicturePresets.Show(btnSelectPreset.PointToScreen(new Point(0, btnSelectPreset.Height - 1))); + } + + private void SetNtscPreset(int hue, int saturation, int contrast, int brightness, int sharpness, int gamma, int resolution, int artifacts, int fringing, int bleed, int scanlines, bool mergeFields) + { + cboFilter.SetEnumValue(VideoFilterType.NTSC); + trkHue.Value = hue; + trkSaturation.Value = saturation; + trkContrast.Value = contrast; + trkBrightness.Value = brightness; + trkSharpness.Value = sharpness; + trkGamma.Value = gamma; + trkResolution.Value = resolution; + trkArtifacts.Value = artifacts; + trkFringing.Value = fringing; + trkBleed.Value = bleed; + chkMergeFields.Checked = mergeFields; + + trkScanlines.Value = scanlines; + } + + private void mnuPresetComposite_Click(object sender, EventArgs e) + { + SetNtscPreset(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, false); + } + + private void mnuPresetSVideo_Click(object sender, EventArgs e) + { + SetNtscPreset(0, 0, 0, 0, 20, 0, 20, -100, -100, 0, 15, false); + } + + private void mnuPresetRgb_Click(object sender, EventArgs e) + { + SetNtscPreset(0, 0, 0, 0, 20, 0, 70, -100, -100, -100, 15, false); + } + + private void mnuPresetMonochrome_Click(object sender, EventArgs e) + { + SetNtscPreset(0, -100, 0, 0, 20, 0, 70, -20, -20, -10, 15, false); + } + } +} diff --git a/UI/Forms/Config/frmVideoConfig.resx b/UI/Forms/Config/frmVideoConfig.resx new file mode 100644 index 0000000..0af4519 --- /dev/null +++ b/UI/Forms/Config/frmVideoConfig.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 107, 17 + + \ No newline at end of file diff --git a/UI/Forms/EntityBinder.cs b/UI/Forms/EntityBinder.cs index 6b38997..f41d87b 100644 --- a/UI/Forms/EntityBinder.cs +++ b/UI/Forms/EntityBinder.cs @@ -102,7 +102,11 @@ namespace Mesen.GUI.Forms ((ctrlTrackbar)kvp.Value).Value = (int)(uint)value; } } else if(kvp.Value is ctrlHorizontalTrackbar) { - ((ctrlHorizontalTrackbar)kvp.Value).Value = (int)value; + if(field.FieldType == typeof(double)) { + ((ctrlHorizontalTrackbar)kvp.Value).Value = (int)((double)value * 100); + } else { + ((ctrlHorizontalTrackbar)kvp.Value).Value = (int)value; + } } else if(kvp.Value is TrackBar) { if(field.FieldType == typeof(Int32)) { ((TrackBar)kvp.Value).Value = (int)value; @@ -236,7 +240,11 @@ namespace Mesen.GUI.Forms field.SetValue(Entity, (UInt32)((ctrlTrackbar)kvp.Value).Value); } } else if(kvp.Value is ctrlHorizontalTrackbar) { - field.SetValue(Entity, (Int32)((ctrlHorizontalTrackbar)kvp.Value).Value); + if(field.FieldType == typeof(double)) { + field.SetValue(Entity, ((ctrlHorizontalTrackbar)kvp.Value).Value / 100.0); + } else { + field.SetValue(Entity, (Int32)((ctrlHorizontalTrackbar)kvp.Value).Value); + } } else if(kvp.Value is TrackBar) { if(field.FieldType == typeof(Int32)) { field.SetValue(Entity, ((TrackBar)kvp.Value).Value); diff --git a/UI/Forms/frmMain.Designer.cs b/UI/Forms/frmMain.Designer.cs index 586ecec..d1209f4 100644 --- a/UI/Forms/frmMain.Designer.cs +++ b/UI/Forms/frmMain.Designer.cs @@ -31,6 +31,8 @@ this.mnuMain = new Mesen.GUI.Controls.ctrlMesenMenuStrip(); this.mnuFile = new System.Windows.Forms.ToolStripMenuItem(); this.mnuOpen = new System.Windows.Forms.ToolStripMenuItem(); + this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuVideoConfig = new System.Windows.Forms.ToolStripMenuItem(); this.debugToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.mnuRun = new System.Windows.Forms.ToolStripMenuItem(); this.mnuStep = new System.Windows.Forms.ToolStripMenuItem(); @@ -41,12 +43,14 @@ this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); this.mnuTilemapViewer = new System.Windows.Forms.ToolStripMenuItem(); this.mnuEventViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.pnlRenderer = new System.Windows.Forms.Panel(); this.mnuMain.SuspendLayout(); + this.pnlRenderer.SuspendLayout(); this.SuspendLayout(); // // ctrlRenderer // - this.ctrlRenderer.Location = new System.Drawing.Point(0, 27); + this.ctrlRenderer.Location = new System.Drawing.Point(0, 0); this.ctrlRenderer.Name = "ctrlRenderer"; this.ctrlRenderer.Size = new System.Drawing.Size(512, 448); this.ctrlRenderer.TabIndex = 0; @@ -55,6 +59,7 @@ // this.mnuMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuFile, + this.optionsToolStripMenuItem, this.debugToolStripMenuItem}); this.mnuMain.Location = new System.Drawing.Point(0, 0); this.mnuMain.Name = "mnuMain"; @@ -79,6 +84,22 @@ this.mnuOpen.Text = "Open"; this.mnuOpen.Click += new System.EventHandler(this.mnuOpen_Click); // + // optionsToolStripMenuItem + // + this.optionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuVideoConfig}); + this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; + this.optionsToolStripMenuItem.Size = new System.Drawing.Size(61, 20); + this.optionsToolStripMenuItem.Text = "Options"; + // + // mnuVideoConfig + // + this.mnuVideoConfig.Image = global::Mesen.GUI.Properties.Resources.VideoOptions; + this.mnuVideoConfig.Name = "mnuVideoConfig"; + this.mnuVideoConfig.Size = new System.Drawing.Size(104, 22); + this.mnuVideoConfig.Text = "Video"; + this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click); + // // debugToolStripMenuItem // this.debugToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -164,19 +185,30 @@ this.mnuEventViewer.Text = "Event Viewer"; this.mnuEventViewer.Click += new System.EventHandler(this.mnuEventViewer_Click); // + // pnlRenderer + // + this.pnlRenderer.BackColor = System.Drawing.Color.Black; + this.pnlRenderer.Controls.Add(this.ctrlRenderer); + this.pnlRenderer.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlRenderer.Location = new System.Drawing.Point(0, 24); + this.pnlRenderer.Name = "pnlRenderer"; + this.pnlRenderer.Size = new System.Drawing.Size(512, 448); + this.pnlRenderer.TabIndex = 2; + // // frmMain // this.AllowDrop = true; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(512, 475); - this.Controls.Add(this.ctrlRenderer); + this.ClientSize = new System.Drawing.Size(512, 472); + this.Controls.Add(this.pnlRenderer); this.Controls.Add(this.mnuMain); this.MainMenuStrip = this.mnuMain; this.Name = "frmMain"; this.Text = "frmMain"; this.mnuMain.ResumeLayout(false); this.mnuMain.PerformLayout(); + this.pnlRenderer.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -198,5 +230,8 @@ private System.Windows.Forms.ToolStripMenuItem mnuMemoryTools; private System.Windows.Forms.ToolStripMenuItem mnuTilemapViewer; private System.Windows.Forms.ToolStripMenuItem mnuEventViewer; + private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuVideoConfig; + private System.Windows.Forms.Panel pnlRenderer; } } \ No newline at end of file diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs index 026ea56..a04feb2 100644 --- a/UI/Forms/frmMain.cs +++ b/UI/Forms/frmMain.cs @@ -1,5 +1,6 @@ using Mesen.GUI.Config; using Mesen.GUI.Debugger; +using Mesen.GUI.Forms.Config; using System; using System.Collections.Generic; using System.ComponentModel; @@ -30,6 +31,8 @@ namespace Mesen.GUI.Forms EmuApi.InitDll(); EmuApi.InitializeEmu(ConfigManager.HomeFolder, Handle, ctrlRenderer.Handle, false, false, false); + ConfigManager.Config.ApplyConfig(); + _notifListener = new NotificationListener(); _notifListener.OnNotification += OnNotificationReceived; @@ -45,6 +48,8 @@ namespace Mesen.GUI.Forms _notifListener = null; } + ConfigManager.ApplyChanges(); + DebugApi.ResumeExecution(); EmuApi.Stop(); EmuApi.Release(); @@ -53,10 +58,40 @@ namespace Mesen.GUI.Forms private void OnNotificationReceived(NotificationEventArgs e) { switch(e.NotificationType) { - case ConsoleNotificationType.CodeBreak: + case ConsoleNotificationType.ResolutionChanged: + ScreenSize size = EmuApi.GetScreenSize(false); + this.BeginInvoke((Action)(() => { + UpdateViewerSize(size); + })); break; } } + + private void UpdateViewerSize(ScreenSize screenSize) + { + //this.Resize -= frmMain_Resize; + + //if(forceUpdate || (!_customSize && this.WindowState != FormWindowState.Maximized)) { + Size newSize = new Size(screenSize.Width, screenSize.Height); + + //UpdateScaleMenu(size.Scale); + this.ClientSize = new Size(newSize.Width, newSize.Height + pnlRenderer.Top); + //} + + ctrlRenderer.Size = new Size(screenSize.Width, screenSize.Height); + ctrlRenderer.Top = (pnlRenderer.Height - ctrlRenderer.Height) / 2; + ctrlRenderer.Left = (pnlRenderer.Width - ctrlRenderer.Width) / 2; + + //this.Resize += frmMain_Resize; + } + + private void mnuVideoConfig_Click(object sender, EventArgs e) + { + using(frmVideoConfig frm = new frmVideoConfig()) { + frm.ShowDialog(sender, this); + } + ConfigManager.Config.Video.ApplyConfig(); + } private void mnuDebugger_Click(object sender, EventArgs e) { diff --git a/UI/Interop/ConfigApi.cs b/UI/Interop/ConfigApi.cs new file mode 100644 index 0000000..7467e11 --- /dev/null +++ b/UI/Interop/ConfigApi.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Mesen.GUI.Config; +using Mesen.GUI.Forms; + +namespace Mesen.GUI +{ + public class ConfigApi + { + private const string DllPath = "MesenSCore.dll"; + + [DllImport(DllPath)] public static extern void SetVideoConfig(VideoConfig config); + } +} diff --git a/UI/Interop/EmuApi.cs b/UI/Interop/EmuApi.cs index 9675c3f..cc1d815 100644 --- a/UI/Interop/EmuApi.cs +++ b/UI/Interop/EmuApi.cs @@ -40,7 +40,16 @@ namespace Mesen.GUI [DllImport(DllPath)] public static extern void SetDisplayLanguage(Language lang); + [DllImport(DllPath)] public static extern ScreenSize GetScreenSize([MarshalAs(UnmanagedType.I1)]bool ignoreScale); + [DllImport(DllPath, EntryPoint = "GetLog")] private static extern IntPtr GetLogWrapper(); public static string GetLog() { return Utf8Marshaler.PtrToStringUtf8(EmuApi.GetLogWrapper()).Replace("\n", Environment.NewLine); } } + + public struct ScreenSize + { + public Int32 Width; + public Int32 Height; + public double Scale; + } } diff --git a/UI/UI.csproj b/UI/UI.csproj index e9675af..eceb2c2 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -209,9 +209,11 @@ + + @@ -462,6 +464,12 @@ Form + + Form + + + frmVideoConfig.cs + Form @@ -480,6 +488,7 @@ + @@ -585,6 +594,9 @@ BaseInputForm.cs + + frmVideoConfig.cs + frmLogWindow.cs diff --git a/Utilities/Utilities.vcxproj b/Utilities/Utilities.vcxproj index 8a133d7..f0076a5 100644 --- a/Utilities/Utilities.vcxproj +++ b/Utilities/Utilities.vcxproj @@ -417,9 +417,6 @@ - - - @@ -429,6 +426,9 @@ + + + @@ -543,7 +543,6 @@ - @@ -585,6 +584,7 @@ + diff --git a/Utilities/Utilities.vcxproj.filters b/Utilities/Utilities.vcxproj.filters index 3b173eb..413dfb4 100644 --- a/Utilities/Utilities.vcxproj.filters +++ b/Utilities/Utilities.vcxproj.filters @@ -80,15 +80,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - xBRZ @@ -170,6 +161,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -217,9 +217,6 @@ Source Files - - Source Files - xBRZ @@ -292,5 +289,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/Utilities/nes_ntsc.cpp b/Utilities/nes_ntsc.cpp deleted file mode 100644 index 9c0a9be..0000000 --- a/Utilities/nes_ntsc.cpp +++ /dev/null @@ -1,291 +0,0 @@ -#include "stdafx.h" - -/* nes_ntsc 0.2.2. http://www.slack.net/~ant/ */ - -#include "nes_ntsc.h" - -/* Copyright (C) 2006-2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -nes_ntsc_setup_t const nes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0, 0, 0 }; -nes_ntsc_setup_t const nes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; -nes_ntsc_setup_t const nes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0, 0, 0 }; -nes_ntsc_setup_t const nes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0, 0, 0 }; - -#define alignment_count 3 -#define burst_count 3 -#define rescale_in 8 -#define rescale_out 7 - -#define artifacts_mid 1.0f -#define fringing_mid 1.0f -#define std_decoder_hue -15 - -#define STD_HUE_CONDITION( setup ) !(setup->base_palette || setup->palette) - -#include "nes_ntsc_impl.h" - -/* 3 input pixels -> 8 composite samples */ -pixel_info_t const nes_ntsc_pixels [alignment_count] = { - { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, - { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, - { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, -}; - -static void merge_kernel_fields( nes_ntsc_rgb_t* io ) -{ - int n; - for ( n = burst_size; n; --n ) - { - nes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias; - nes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias; - nes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias; - /* merge colors without losing precision */ - io [burst_size * 0] = - ((p0 + p1 - ((p0 ^ p1) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias; - io [burst_size * 1] = - ((p1 + p2 - ((p1 ^ p2) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias; - io [burst_size * 2] = - ((p2 + p0 - ((p2 ^ p0) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias; - ++io; - } -} - -static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out ) -{ - int n; - for ( n = burst_count; n; --n ) - { - unsigned i; - for ( i = 0; i < rgb_kernel_size / 2; i++ ) - { - nes_ntsc_rgb_t error = color - - out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - - out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; - DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 ); - } - out += alignment_count * rgb_kernel_size; - } -} - -void nes_ntsc_init( nes_ntsc_t* ntsc, nes_ntsc_setup_t const* setup ) -{ - int merge_fields; - int entry; - init_t impl; - float gamma_factor; - - if ( !setup ) - setup = &nes_ntsc_composite; - init( &impl, setup ); - - /* setup fast gamma */ - { - float gamma = (float) setup->gamma * -0.5f; - if ( STD_HUE_CONDITION( setup ) ) - gamma += 0.1333f; - - gamma_factor = (float) pow( (float) fabs( gamma ), 0.73f ); - if ( gamma < 0 ) - gamma_factor = -gamma_factor; - } - - merge_fields = setup->merge_fields; - if ( setup->artifacts <= -1 && setup->fringing <= -1 ) - merge_fields = 1; - - for ( entry = 0; entry < nes_ntsc_palette_size; entry++ ) - { - /* Base 64-color generation */ - static float const lo_levels [4] = { -0.12f, 0.00f, 0.31f, 0.72f }; - static float const hi_levels [4] = { 0.40f, 0.68f, 1.00f, 1.00f }; - int level = entry >> 4 & 0x03; - float lo = lo_levels [level]; - float hi = hi_levels [level]; - - int color = entry & 0x0F; - if ( color == 0 ) - lo = hi; - if ( color == 0x0D ) - hi = lo; - if ( color > 0x0D ) - hi = lo = 0.0f; - - { - /* phases [i] = cos( i * PI / 6 ) */ - static float const phases [0x10 + 3] = { - -1.0f, -0.866025f, -0.5f, 0.0f, 0.5f, 0.866025f, - 1.0f, 0.866025f, 0.5f, 0.0f, -0.5f, -0.866025f, - -1.0f, -0.866025f, -0.5f, 0.0f, 0.5f, 0.866025f, - 1.0f - }; - #define TO_ANGLE_SIN( color ) phases [color] - #define TO_ANGLE_COS( color ) phases [(color) + 3] - - /* Convert raw waveform to YIQ */ - float sat = (hi - lo) * 0.5f; - float i = TO_ANGLE_SIN( color ) * sat; - float q = TO_ANGLE_COS( color ) * sat; - float y = (hi + lo) * 0.5f; - - /* Optionally use base palette instead */ - if ( setup->base_palette ) - { - unsigned char const* in = &setup->base_palette [(entry & 0x3F) * 3]; - static float const to_float = 1.0f / 0xFF; - float r = to_float * in [0]; - float g = to_float * in [1]; - float b = to_float * in [2]; - q = RGB_TO_YIQ( r, g, b, y, i ); - } - - /* Apply color emphasis */ - #ifdef NES_NTSC_EMPHASIS - { - int tint = entry >> 6 & 7; - if ( tint && color <= 0x0D ) - { - static float const atten_mul = 0.79399f; - static float const atten_sub = 0.0782838f; - - if ( tint == 7 ) - { - y = y * (atten_mul * 1.13f) - (atten_sub * 1.13f); - } - else - { - static unsigned char const tints [8] = { 0, 6, 10, 8, 2, 4, 0, 0 }; - int const tint_color = tints [tint]; - float sat = hi * (0.5f - atten_mul * 0.5f) + atten_sub * 0.5f; - y -= sat * 0.5f; - if ( tint >= 3 && tint != 4 ) - { - /* combined tint bits */ - sat *= 0.6f; - y -= sat; - } - i += TO_ANGLE_SIN( tint_color ) * sat; - q += TO_ANGLE_COS( tint_color ) * sat; - } - } - } - #endif - - /* Optionally use palette instead */ - if ( setup->palette ) - { - unsigned char const* in = &setup->palette [entry * 3]; - static float const to_float = 1.0f / 0xFF; - float r = to_float * in [0]; - float g = to_float * in [1]; - float b = to_float * in [2]; - q = RGB_TO_YIQ( r, g, b, y, i ); - } - - /* Apply brightness, contrast, and gamma */ - y *= (float) setup->contrast * 0.5f + 1; - /* adjustment reduces error when using input palette */ - y += (float) setup->brightness * 0.5f - 0.5f / 256; - - { - float r, g, b = YIQ_TO_RGB( y, i, q, default_decoder, float, r, g ); - - /* fast approximation of n = pow( n, gamma ) */ - r = (r * gamma_factor - gamma_factor) * r + r; - g = (g * gamma_factor - gamma_factor) * g + g; - b = (b * gamma_factor - gamma_factor) * b + b; - - q = RGB_TO_YIQ( r, g, b, y, i ); - } - - i *= rgb_unit; - q *= rgb_unit; - y *= rgb_unit; - y += rgb_offset; - - /* Generate kernel */ - { - int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); - /* blue tends to overflow, so clamp it */ - nes_ntsc_rgb_t rgb = PACK_RGB( r, g, (b < 0x3E0 ? b: 0x3E0) ); - - if ( setup->palette_out ) - RGB_PALETTE_OUT( rgb, &setup->palette_out [entry * 3] ); - - if ( ntsc ) - { - nes_ntsc_rgb_t* kernel = ntsc->table [entry]; - gen_kernel( &impl, y, i, q, kernel ); - if ( merge_fields ) - merge_kernel_fields( kernel ); - correct_errors( rgb, kernel ); - } - } - } - } -} - -#ifndef NES_NTSC_NO_BLITTERS - -void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* input, long in_row_width, - int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) -{ - int chunk_count = (in_width - 1) / nes_ntsc_in_chunk; - for ( ; in_height; --in_height ) - { - NES_NTSC_IN_T const* line_in = input; - NES_NTSC_BEGIN_ROW( ntsc, burst_phase, - nes_ntsc_black, nes_ntsc_black, NES_NTSC_ADJ_IN( *line_in ) ); - nes_ntsc_out_t* restrict line_out = (nes_ntsc_out_t*) rgb_out; - int n; - ++line_in; - - for ( n = chunk_count; n; --n ) - { - /* order of input and output pixels must not be altered */ - NES_NTSC_COLOR_IN( 0, NES_NTSC_ADJ_IN( line_in [0] ) ); - NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH ); - - NES_NTSC_COLOR_IN( 1, NES_NTSC_ADJ_IN( line_in [1] ) ); - NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH ); - - NES_NTSC_COLOR_IN( 2, NES_NTSC_ADJ_IN( line_in [2] ) ); - NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH ); - - line_in += 3; - line_out += 7; - } - - /* finish final pixels */ - NES_NTSC_COLOR_IN( 0, nes_ntsc_black ); - NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH ); - - NES_NTSC_COLOR_IN( 1, nes_ntsc_black ); - NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH ); - - NES_NTSC_COLOR_IN( 2, nes_ntsc_black ); - NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH ); - - burst_phase = (burst_phase + 1) % nes_ntsc_burst_count; - input += in_row_width; - rgb_out = (char*) rgb_out + out_pitch; - } -} - -#endif diff --git a/Utilities/nes_ntsc.h b/Utilities/nes_ntsc.h deleted file mode 100644 index dd55f0f..0000000 --- a/Utilities/nes_ntsc.h +++ /dev/null @@ -1,199 +0,0 @@ -#pragma once -/* NES NTSC video filter */ - -/* nes_ntsc 0.2.2 */ -#ifndef NES_NTSC_H -#define NES_NTSC_H - -#if defined(_MSC_VER) - #define EXPORT __declspec(dllexport) -#else - #define EXPORT -#endif - -#include "nes_ntsc_config.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown -in parenthesis and should remain fairly stable in future versions. */ -typedef struct nes_ntsc_setup_t -{ - /* Basic parameters */ - double hue; /* -1 = -180 degrees +1 = +180 degrees */ - double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ - double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ - double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ - double sharpness; /* edge contrast enhancement/blurring */ - - /* Advanced parameters */ - double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ - double resolution; /* image resolution */ - double artifacts; /* artifacts caused by color changes */ - double fringing; /* color artifacts caused by brightness changes */ - double bleed; /* color bleed (color resolution reduction) */ - int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ - float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ - - unsigned char* palette_out; /* optional RGB palette out, 3 bytes per color */ - - /* You can replace the standard NES color generation with an RGB palette. The - first replaces all color generation, while the second replaces only the core - 64-color generation and does standard color emphasis calculations on it. */ - unsigned char const* palette;/* optional 512-entry RGB palette in, 3 bytes per color */ - unsigned char const* base_palette;/* optional 64-entry RGB palette in, 3 bytes per color */ -} nes_ntsc_setup_t; - -/* Video format presets */ -extern nes_ntsc_setup_t const nes_ntsc_composite; /* color bleeding + artifacts */ -extern nes_ntsc_setup_t const nes_ntsc_svideo; /* color bleeding only */ -extern nes_ntsc_setup_t const nes_ntsc_rgb; /* crisp image */ -extern nes_ntsc_setup_t const nes_ntsc_monochrome;/* desaturated + artifacts */ - -#ifdef NES_NTSC_EMPHASIS - enum { nes_ntsc_palette_size = 64 * 8 }; -#else - enum { nes_ntsc_palette_size = 64 }; -#endif - -/* Initializes and adjusts parameters. Can be called multiple times on the same -nes_ntsc_t object. Can pass NULL for either parameter. */ -typedef struct nes_ntsc_t nes_ntsc_t; -EXPORT void nes_ntsc_init( nes_ntsc_t* ntsc, nes_ntsc_setup_t const* setup ); - -/* Filters one or more rows of pixels. Input pixels are 6/9-bit palette indicies. -In_row_width is the number of pixels to get to the next input row. Out_pitch -is the number of *bytes* to get to the next output row. Output pixel format -is set by NES_NTSC_OUT_DEPTH (defaults to 16-bit RGB). */ -EXPORT void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* nes_in, - long in_row_width, int burst_phase, int in_width, int in_height, - void* rgb_out, long out_pitch ); - -/* Number of output pixels written by blitter for given input width. Width might -be rounded down slightly; use NES_NTSC_IN_WIDTH() on result to find rounded -value. Guaranteed not to round 256 down at all. */ -#define NES_NTSC_OUT_WIDTH( in_width ) \ - ((((in_width) - 1) / nes_ntsc_in_chunk + 1) * nes_ntsc_out_chunk) - -/* Number of input pixels that will fit within given output width. Might be -rounded down slightly; use NES_NTSC_OUT_WIDTH() on result to find rounded -value. */ -#define NES_NTSC_IN_WIDTH( out_width ) \ - (((out_width) / nes_ntsc_out_chunk - 1) * nes_ntsc_in_chunk + 1) - - -/* Interface for user-defined custom blitters */ - -enum { nes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ -enum { nes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ -enum { nes_ntsc_black = 15 }; /* palette index for black */ -enum { nes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ - -/* Begins outputting row and starts three pixels. First pixel will be cut off a bit. -Use nes_ntsc_black for unused pixels. Declares variables, so must be before first -statement in a block (unless you're using C++). */ -#define NES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ - char const* const ktable = \ - (char const*) (ntsc)->table [0] + burst * (nes_ntsc_burst_size * sizeof (nes_ntsc_rgb_t));\ - NES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, NES_NTSC_ENTRY_, ktable ) - -/* Begins input pixel */ -#define NES_NTSC_COLOR_IN( in_index, color_in ) \ - NES_NTSC_COLOR_IN_( in_index, color_in, NES_NTSC_ENTRY_, ktable ) - -/* Generates output pixel. Bits can be 24, 16, 15, 32 (treated as 24), or 0: -24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) -16: RRRRRGGG GGGBBBBB (5-6-5 RGB) -15: RRRRRGG GGGBBBBB (5-5-5 RGB) - 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ -#define NES_NTSC_RGB_OUT( index, rgb_out, bits ) \ - NES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 0 ) - - -/* private */ -enum { nes_ntsc_entry_size = 128 }; -typedef unsigned long nes_ntsc_rgb_t; -struct nes_ntsc_t { - nes_ntsc_rgb_t table [nes_ntsc_palette_size] [nes_ntsc_entry_size]; -}; -enum { nes_ntsc_burst_size = nes_ntsc_entry_size / nes_ntsc_burst_count }; - -#define NES_NTSC_ENTRY_( ktable, n ) \ - (nes_ntsc_rgb_t const*) (ktable + (n) * (nes_ntsc_entry_size * sizeof (nes_ntsc_rgb_t))) - -/* deprecated */ -#define NES_NTSC_RGB24_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 24 ) -#define NES_NTSC_RGB16_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 16 ) -#define NES_NTSC_RGB15_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 15 ) -#define NES_NTSC_RAW_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 0 ) - -enum { nes_ntsc_min_in_width = 256 }; -enum { nes_ntsc_min_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_min_in_width ) }; - -enum { nes_ntsc_640_in_width = 271 }; -enum { nes_ntsc_640_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_640_in_width ) }; -enum { nes_ntsc_640_overscan_left = 8 }; -enum { nes_ntsc_640_overscan_right = nes_ntsc_640_in_width - 256 - nes_ntsc_640_overscan_left }; - -enum { nes_ntsc_full_in_width = 283 }; -enum { nes_ntsc_full_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_full_in_width ) }; -enum { nes_ntsc_full_overscan_left = 16 }; -enum { nes_ntsc_full_overscan_right = nes_ntsc_full_in_width - 256 - nes_ntsc_full_overscan_left }; - -/* common 3->7 ntsc macros */ -#define NES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ - unsigned const nes_ntsc_pixel0_ = (pixel0);\ - nes_ntsc_rgb_t const* kernel0 = ENTRY( table, nes_ntsc_pixel0_ );\ - unsigned const nes_ntsc_pixel1_ = (pixel1);\ - nes_ntsc_rgb_t const* kernel1 = ENTRY( table, nes_ntsc_pixel1_ );\ - unsigned const nes_ntsc_pixel2_ = (pixel2);\ - nes_ntsc_rgb_t const* kernel2 = ENTRY( table, nes_ntsc_pixel2_ );\ - nes_ntsc_rgb_t const* kernelx0;\ - nes_ntsc_rgb_t const* kernelx1 = kernel0;\ - nes_ntsc_rgb_t const* kernelx2 = kernel0 - -#define NES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ - nes_ntsc_rgb_t raw_ =\ - kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ - kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ - NES_NTSC_CLAMP_( raw_, shift );\ - NES_NTSC_RGB_OUT_( rgb_out, bits, shift );\ -} - -/* common ntsc macros */ -#define nes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) -#define nes_ntsc_clamp_mask (nes_ntsc_rgb_builder * 3 / 2) -#define nes_ntsc_clamp_add (nes_ntsc_rgb_builder * 0x101) -#define NES_NTSC_CLAMP_( io, shift ) {\ - nes_ntsc_rgb_t sub = (io) >> (9-(shift)) & nes_ntsc_clamp_mask;\ - nes_ntsc_rgb_t clamp = nes_ntsc_clamp_add - sub;\ - io |= clamp;\ - clamp -= sub;\ - io &= clamp;\ -} - -#define NES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ - unsigned color_;\ - kernelx##index = kernel##index;\ - kernel##index = (color_ = (color), ENTRY( table, color_ ));\ -} - -/* x is always zero except in snes_ntsc library */ -#define NES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ - if ( bits == 16 )\ - rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ - if ( bits == 32 || bits == 24 )\ - rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ - if ( bits == 15 )\ - rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ - if ( bits == 0 )\ - rgb_out = raw_ << x;\ -} - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/Utilities/nes_ntsc_config.h b/Utilities/nes_ntsc_config.h deleted file mode 100644 index 2527822..0000000 --- a/Utilities/nes_ntsc_config.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Configure library by modifying this file */ - -#ifndef NES_NTSC_CONFIG_H -#define NES_NTSC_CONFIG_H - -/* Uncomment to enable emphasis support and use a 512 color palette instead -of the base 64 color palette. */ -#define NES_NTSC_EMPHASIS 1 - -/* The following affect the built-in blitter only; a custom blitter can -handle things however it wants. */ - -/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ -#define NES_NTSC_OUT_DEPTH 32 - -/* Type of input pixel values. You'll probably use unsigned short -if you enable emphasis above. */ -#define NES_NTSC_IN_T unsigned short - -/* Each raw pixel input value is passed through this. You might want to mask -the pixel index if you use the high bits as flags, etc. */ -#define NES_NTSC_ADJ_IN( in ) in - -/* For each pixel, this is the basic operation: -output_color = color_palette [NES_NTSC_ADJ_IN( NES_NTSC_IN_T )] */ - -#endif diff --git a/Utilities/snes_ntsc.cpp b/Utilities/snes_ntsc.cpp new file mode 100644 index 0000000..85ff666 --- /dev/null +++ b/Utilities/snes_ntsc.cpp @@ -0,0 +1,252 @@ +#include "stdafx.h" +/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */ + +#include "snes_ntsc.h" + +/* Copyright (C) 2006-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 }; + +#define alignment_count 3 +#define burst_count 3 +#define rescale_in 8 +#define rescale_out 7 + +#define artifacts_mid 1.0f +#define fringing_mid 1.0f +#define std_decoder_hue 0 + +#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */ +#define gamma_size 32 + +#include "snes_ntsc_impl.h" + +/* 3 input pixels -> 8 composite samples */ +pixel_info_t const snes_ntsc_pixels [alignment_count] = { + { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, + { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, + { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, +}; + +static void merge_kernel_fields( snes_ntsc_rgb_t* io ) +{ + int n; + for ( n = burst_size; n; --n ) + { + snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias; + snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias; + snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias; + /* merge colors without losing precision */ + io [burst_size * 0] = + ((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 1] = + ((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 2] = + ((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + ++io; + } +} + +static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out ) +{ + int n; + for ( n = burst_count; n; --n ) + { + unsigned i; + for ( i = 0; i < rgb_kernel_size / 2; i++ ) + { + snes_ntsc_rgb_t error = color - + out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - + out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; + DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 ); + } + out += alignment_count * rgb_kernel_size; + } +} + +void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ) +{ + int merge_fields; + int entry; + init_t impl; + if ( !setup ) + setup = &snes_ntsc_composite; + init( &impl, setup ); + + merge_fields = setup->merge_fields; + if ( setup->artifacts <= -1 && setup->fringing <= -1 ) + merge_fields = 1; + + for ( entry = 0; entry < snes_ntsc_palette_size; entry++ ) + { + /* Reduce number of significant bits of source color. Clearing the + low bits of R and B were least notictable. Modifying green was too + noticeable. */ + int ir = entry >> 8 & 0x1E; + int ig = entry >> 4 & 0x1F; + int ib = entry << 1 & 0x1E; + + #if SNES_NTSC_BSNES_COLORTBL + if ( setup->bsnes_colortbl ) + { + int bgr15 = (ib << 10) | (ig << 5) | ir; + unsigned long rgb16 = setup->bsnes_colortbl [bgr15]; + ir = rgb16 >> 11 & 0x1E; + ig = rgb16 >> 6 & 0x1F; + ib = rgb16 & 0x1E; + } + #endif + + { + float rr = impl.to_float [ir]; + float gg = impl.to_float [ig]; + float bb = impl.to_float [ib]; + + float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i ); + + int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); + snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b ); + + snes_ntsc_rgb_t* out = ntsc->table [entry]; + gen_kernel( &impl, y, i, q, out ); + if ( merge_fields ) + merge_kernel_fields( out ); + correct_errors( rgb, out ); + } + } +} + +#ifndef SNES_NTSC_NO_BLITTERS + +void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, + int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) +{ + int chunk_count = (in_width - 1) / snes_ntsc_in_chunk; + for ( ; in_height; --in_height ) + { + SNES_NTSC_IN_T const* line_in = input; + SNES_NTSC_BEGIN_ROW( ntsc, burst_phase, + snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) ); + snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; + int n; + ++line_in; + + for ( n = chunk_count; n; --n ) + { + /* order of input and output pixels must not be altered */ + SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); + SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); + SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); + SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + line_in += 3; + line_out += 7; + } + + /* finish final pixels */ + SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; + input += in_row_width; + rgb_out = (char*) rgb_out + out_pitch; + } +} + +void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, + int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) +{ + int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2); + for ( ; in_height; --in_height ) + { + SNES_NTSC_IN_T const* line_in = input; + SNES_NTSC_HIRES_ROW( ntsc, burst_phase, + snes_ntsc_black, snes_ntsc_black, snes_ntsc_black, + SNES_NTSC_ADJ_IN( line_in [0] ), + SNES_NTSC_ADJ_IN( line_in [1] ) ); + snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; + int n; + line_in += 2; + + for ( n = chunk_count; n; --n ) + { + /* twice as many input pixels per chunk */ + SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); + SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); + SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); + SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) ); + SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) ); + SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) ); + SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + line_in += 6; + line_out += 7; + } + + SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 3, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 4, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 5, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; + input += in_row_width; + rgb_out = (char*) rgb_out + out_pitch; + } +} + +#endif diff --git a/Utilities/snes_ntsc.h b/Utilities/snes_ntsc.h new file mode 100644 index 0000000..9f2f28f --- /dev/null +++ b/Utilities/snes_ntsc.h @@ -0,0 +1,206 @@ +/* SNES NTSC video filter */ + +/* snes_ntsc 0.2.2 */ +#ifndef SNES_NTSC_H +#define SNES_NTSC_H + +#include "snes_ntsc_config.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown +in parenthesis and should remain fairly stable in future versions. */ +typedef struct snes_ntsc_setup_t +{ + /* Basic parameters */ + double hue; /* -1 = -180 degrees +1 = +180 degrees */ + double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ + double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ + double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ + double sharpness; /* edge contrast enhancement/blurring */ + + /* Advanced parameters */ + double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ + double resolution; /* image resolution */ + double artifacts; /* artifacts caused by color changes */ + double fringing; /* color artifacts caused by brightness changes */ + double bleed; /* color bleed (color resolution reduction) */ + int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ + float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ + + unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */ +} snes_ntsc_setup_t; + +/* Video format presets */ +extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */ +extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */ +extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */ +extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */ + +/* Initializes and adjusts parameters. Can be called multiple times on the same +snes_ntsc_t object. Can pass NULL for either parameter. */ +typedef struct snes_ntsc_t snes_ntsc_t; +void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ); + +/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT +and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB. +In_row_width is the number of pixels to get to the next input row. Out_pitch +is the number of *bytes* to get to the next output row. */ +void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, + long in_row_width, int burst_phase, int in_width, int in_height, + void* rgb_out, long out_pitch ); + +void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, + long in_row_width, int burst_phase, int in_width, int in_height, + void* rgb_out, long out_pitch ); + +/* Number of output pixels written by low-res blitter for given input width. Width +might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded +value. Guaranteed not to round 256 down at all. */ +#define SNES_NTSC_OUT_WIDTH( in_width ) \ + ((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk) + +/* Number of low-res input pixels that will fit within given output width. Might be +rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded +value. */ +#define SNES_NTSC_IN_WIDTH( out_width ) \ + (((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1) + + +/* Interface for user-defined custom blitters */ + +enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ +enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ +enum { snes_ntsc_black = 0 }; /* palette index for black */ +enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ + +/* Begins outputting row and starts three pixels. First pixel will be cut off a bit. +Use snes_ntsc_black for unused pixels. Declares variables, so must be before first +statement in a block (unless you're using C++). */ +#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ + char const* ktable = \ + (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ + SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable ) + +/* Begins input pixel */ +#define SNES_NTSC_COLOR_IN( index, color ) \ + SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable ) + +/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0: +24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) +16: RRRRRGGG GGGBBBBB (5-6-5 RGB) +15: RRRRRGG GGGBBBBB (5-5-5 RGB) +14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format) + 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ +#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \ + SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 ) + +/* Hires equivalents */ +#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \ + char const* ktable = \ + (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ + unsigned const snes_ntsc_pixel1_ = (pixel1);\ + snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\ + unsigned const snes_ntsc_pixel2_ = (pixel2);\ + snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\ + unsigned const snes_ntsc_pixel3_ = (pixel3);\ + snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\ + unsigned const snes_ntsc_pixel4_ = (pixel4);\ + snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\ + unsigned const snes_ntsc_pixel5_ = (pixel5);\ + snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\ + snes_ntsc_rgb_t const* kernel0 = kernel1;\ + snes_ntsc_rgb_t const* kernelx0;\ + snes_ntsc_rgb_t const* kernelx1 = kernel1;\ + snes_ntsc_rgb_t const* kernelx2 = kernel1;\ + snes_ntsc_rgb_t const* kernelx3 = kernel1;\ + snes_ntsc_rgb_t const* kernelx4 = kernel1;\ + snes_ntsc_rgb_t const* kernelx5 = kernel1 + +#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\ + snes_ntsc_rgb_t raw_ =\ + kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\ + kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\ + kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\ + kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\ + SNES_NTSC_CLAMP_( raw_, 0 );\ + SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\ +} + + +/* private */ +enum { snes_ntsc_entry_size = 128 }; +enum { snes_ntsc_palette_size = 0x2000 }; +typedef unsigned long snes_ntsc_rgb_t; +struct snes_ntsc_t { + snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size]; +}; +enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count }; + +#define SNES_NTSC_RGB16( ktable, n ) \ + (snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \ + (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) + +#define SNES_NTSC_BGR15( ktable, n ) \ + (snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \ + (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) + +/* common 3->7 ntsc macros */ +#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ + unsigned const snes_ntsc_pixel0_ = (pixel0);\ + snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\ + unsigned const snes_ntsc_pixel1_ = (pixel1);\ + snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\ + unsigned const snes_ntsc_pixel2_ = (pixel2);\ + snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\ + snes_ntsc_rgb_t const* kernelx0;\ + snes_ntsc_rgb_t const* kernelx1 = kernel0;\ + snes_ntsc_rgb_t const* kernelx2 = kernel0 + +#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ + snes_ntsc_rgb_t raw_ =\ + kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ + kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ + SNES_NTSC_CLAMP_( raw_, shift );\ + SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\ +} + +/* common ntsc macros */ +#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) +#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2) +#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101) +#define SNES_NTSC_CLAMP_( io, shift ) {\ + snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\ + snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\ + io |= clamp;\ + clamp -= sub;\ + io &= clamp;\ +} + +#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ + unsigned color_;\ + kernelx##index = kernel##index;\ + kernel##index = (color_ = (color), ENTRY( table, color_ ));\ +} + +/* x is always zero except in snes_ntsc library */ +#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + if ( bits == 16 )\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 24 || bits == 32 )\ + rgb_out = 0xFF000000|(raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ + if ( bits == 15 )\ + rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 14 )\ + rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\ + if ( bits == 0 )\ + rgb_out = raw_ << x;\ +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/Utilities/snes_ntsc_config.h b/Utilities/snes_ntsc_config.h new file mode 100644 index 0000000..7ab94c2 --- /dev/null +++ b/Utilities/snes_ntsc_config.h @@ -0,0 +1,26 @@ +/* Configure library by modifying this file */ + +#ifndef SNES_NTSC_CONFIG_H +#define SNES_NTSC_CONFIG_H + +/* Format of source pixels */ +/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */ +#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15 + +/* The following affect the built-in blitter only; a custom blitter can +handle things however it wants. */ + +/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ +#define SNES_NTSC_OUT_DEPTH 32 + +/* Type of input pixel values */ +#define SNES_NTSC_IN_T unsigned short + +/* Each raw pixel input value is passed through this. You might want to mask +the pixel index if you use the high bits as flags, etc. */ +#define SNES_NTSC_ADJ_IN( in ) in + +/* For each pixel, this is the basic operation: +output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */ + +#endif diff --git a/Utilities/nes_ntsc_impl.h b/Utilities/snes_ntsc_impl.h similarity index 92% rename from Utilities/nes_ntsc_impl.h rename to Utilities/snes_ntsc_impl.h index de3672b..1d7adc7 100644 --- a/Utilities/nes_ntsc_impl.h +++ b/Utilities/snes_ntsc_impl.h @@ -1,4 +1,4 @@ -/* nes_ntsc 0.2.2. http://www.slack.net/~ant/ */ +/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */ /* Common implementation of NTSC filters */ @@ -44,7 +44,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define rgb_unit (1 << rgb_bits) #define rgb_offset (rgb_unit * 2 + 0.5f) -enum { burst_size = nes_ntsc_entry_size / burst_count }; +enum { burst_size = snes_ntsc_entry_size / burst_count }; enum { kernel_half = 16 }; enum { kernel_size = kernel_half * 2 + 1 }; @@ -66,7 +66,7 @@ typedef struct init_t i = t;\ } -static void init_filters( init_t* impl, nes_ntsc_setup_t const* setup ) +static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup ) { #if rescale_out > 1 float kernels [kernel_size * 2]; @@ -195,7 +195,7 @@ static void init_filters( init_t* impl, nes_ntsc_setup_t const* setup ) static float const default_decoder [6] = { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; -static void init( init_t* impl, nes_ntsc_setup_t const* setup ) +static void init( init_t* impl, snes_ntsc_setup_t const* setup ) { impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; @@ -285,7 +285,7 @@ static void init( init_t* impl, nes_ntsc_setup_t const* setup ) #define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) enum { rgb_kernel_size = burst_size / alignment_count }; -enum { rgb_bias = rgb_unit * 2 * nes_ntsc_rgb_builder }; +enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder }; typedef struct pixel_info_t { @@ -309,10 +309,10 @@ typedef struct pixel_info_t (1.0f - (((ntsc) + 100) & 2)) #endif -extern pixel_info_t const nes_ntsc_pixels [alignment_count]; +extern pixel_info_t const snes_ntsc_pixels [alignment_count]; /* Generate pixel at all burst phases and column alignments */ -static void gen_kernel( init_t* impl, float y, float i, float q, nes_ntsc_rgb_t* out ) +static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out ) { /* generate for each scanline burst phase */ float const* to_rgb = impl->to_rgb; @@ -324,7 +324,7 @@ static void gen_kernel( init_t* impl, float y, float i, float q, nes_ntsc_rgb_t* Convolve these with kernels which: filter respective components, apply sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack into integer. Based on algorithm by NewRisingSun. */ - pixel_info_t const* pixel = nes_ntsc_pixels; + pixel_info_t const* pixel = snes_ntsc_pixels; int alignment_remain = alignment_count; do { @@ -377,7 +377,7 @@ static void gen_kernel( init_t* impl, float y, float i, float q, nes_ntsc_rgb_t* while ( --burst_remain ); } -static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out ); +static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out ); #if DISABLE_CORRECTION #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } @@ -385,8 +385,8 @@ static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out ); #else #define CORRECT_ERROR( a ) { out [a] += error; } #define DISTRIBUTE_ERROR( a, b, c ) {\ - nes_ntsc_rgb_t fourth = (error + 2 * nes_ntsc_rgb_builder) >> 2;\ - fourth &= (rgb_bias >> 1) - nes_ntsc_rgb_builder;\ + snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\ + fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\ fourth -= rgb_bias >> 2;\ out [a] += fourth;\ out [b] += fourth;\ @@ -398,8 +398,8 @@ static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out ); #define RGB_PALETTE_OUT( rgb, out_ )\ {\ unsigned char* out = (out_);\ - nes_ntsc_rgb_t clamped = (rgb);\ - NES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ + snes_ntsc_rgb_t clamped = (rgb);\ + SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ out [0] = (unsigned char) (clamped >> 21);\ out [1] = (unsigned char) (clamped >> 11);\ out [2] = (unsigned char) (clamped >> 1);\ @@ -420,18 +420,18 @@ static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out ); #include -#if NES_NTSC_OUT_DEPTH <= 16 +#if SNES_NTSC_OUT_DEPTH <= 16 #if USHRT_MAX == 0xFFFF - typedef unsigned short nes_ntsc_out_t; + typedef unsigned short snes_ntsc_out_t; #else #error "Need 16-bit int type" #endif #else #if UINT_MAX == 0xFFFFFFFF - typedef unsigned int nes_ntsc_out_t; + typedef unsigned int snes_ntsc_out_t; #elif ULONG_MAX == 0xFFFFFFFF - typedef unsigned long nes_ntsc_out_t; + typedef unsigned long snes_ntsc_out_t; #else #error "Need 32-bit int type" #endif diff --git a/Windows/Renderer.cpp b/Windows/Renderer.cpp index 75400dc..e3ac04b 100644 --- a/Windows/Renderer.cpp +++ b/Windows/Renderer.cpp @@ -8,6 +8,7 @@ #include "../Core/Debugger.h" #include "../Core/MessageManager.h" #include "../Core/SettingTypes.h" +#include "../Core/EmuSettings.h" #include "../Utilities/UTF8Util.h" using namespace DirectX; @@ -40,20 +41,18 @@ void Renderer::SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t m void Renderer::SetScreenSize(uint32_t width, uint32_t height) { - ScreenSize screenSize; - _console->GetVideoDecoder()->GetScreenSize(screenSize, false); - - //TODO _resizeFilter != _console->GetSettings()->GetVideoResizeFilter() - if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _newFullscreen != _fullscreen) { + ScreenSize screenSize = _console->GetVideoDecoder()->GetScreenSize(false); + VideoConfig cfg = _console->GetSettings()->GetVideoConfig(); + if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _newFullscreen != _fullscreen || _useBilinearInterpolation != cfg.UseBilinearInterpolation) { auto frameLock = _frameLock.AcquireSafe(); auto textureLock = _textureLock.AcquireSafe(); - _console->GetVideoDecoder()->GetScreenSize(screenSize, false); - if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _newFullscreen != _fullscreen) { + screenSize = _console->GetVideoDecoder()->GetScreenSize(false); + if(_screenHeight != screenSize.Height || _screenWidth != screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _newFullscreen != _fullscreen || _useBilinearInterpolation != cfg.UseBilinearInterpolation) { _nesFrameHeight = height; _nesFrameWidth = width; _newFrameBufferSize = width*height; - bool needReset = _fullscreen != _newFullscreen;//TODO || _resizeFilter != _console->GetSettings()->GetVideoResizeFilter(); + bool needReset = _fullscreen != _newFullscreen || _useBilinearInterpolation != cfg.UseBilinearInterpolation; bool fullscreenResizeMode = _fullscreen && _newFullscreen; if(_pSwapChain && _fullscreen && !_newFullscreen) { @@ -278,7 +277,7 @@ HRESULT Renderer::InitDevice() sd.BufferDesc.Width = _realScreenWidth; sd.BufferDesc.Height = _realScreenHeight; sd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - sd.BufferDesc.RefreshRate.Numerator = 60; //TODO _console->GetSettings()->GetExclusiveRefreshRate(); + sd.BufferDesc.RefreshRate.Numerator = _console->GetSettings()->GetVideoConfig().ExclusiveFullscreenRefreshRate; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.Flags = _fullscreen ? DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH : 0; @@ -401,12 +400,12 @@ HRESULT Renderer::InitDevice() HRESULT Renderer::CreateSamplerState() { - _resizeFilter = VideoResizeFilter::NearestNeighbor; //TODO _console->GetSettings()->GetVideoResizeFilter(); + _useBilinearInterpolation = _console->GetSettings()->GetVideoConfig().UseBilinearInterpolation; //Sample state D3D11_SAMPLER_DESC samplerDesc; ZeroMemory(&samplerDesc, sizeof(samplerDesc)); - samplerDesc.Filter = _resizeFilter == VideoResizeFilter::Bilinear ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT; + samplerDesc.Filter = _useBilinearInterpolation ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT; samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; @@ -637,7 +636,7 @@ void Renderer::Render() // Present the information rendered to the back buffer to the front buffer (the screen) - bool waitVSync = false; //TODO _console->GetSettings()->CheckFlag(EmulationFlags::VerticalSync) + bool waitVSync = _console->GetSettings()->GetVideoConfig().VerticalSync; HRESULT hr = _pSwapChain->Present(waitVSync ? 1 : 0, 0); if(FAILED(hr)) { MessageManager::Log("SwapChain::Present() failed - Error:" + std::to_string(hr)); diff --git a/Windows/Renderer.h b/Windows/Renderer.h index 8672689..b8e7d6e 100644 --- a/Windows/Renderer.h +++ b/Windows/Renderer.h @@ -42,7 +42,7 @@ private: SimpleLock _frameLock; SimpleLock _textureLock; - VideoResizeFilter _resizeFilter = VideoResizeFilter::NearestNeighbor; + bool _useBilinearInterpolation = false; unique_ptr _font; unique_ptr _largeFont;