Skip to content
This repository was archived by the owner on Sep 11, 2023. It is now read-only.

Refactor SMPTE-C implementation #93

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 22 additions & 34 deletions Core/BisqwitNtscFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
#include "EmulationSettings.h"
#include "Console.h"

BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr<Console> console, int resDivider, bool SMPTE_C) : BaseVideoFilter(console)
BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr<Console> console, int resDivider) : BaseVideoFilter(console)
{
_resDivider = resDivider;
_stopThread = false;
_workDone = false;
_SMPTE_C = SMPTE_C;

const int8_t signalLumaLow[4] = { -29, -15, 22, 71 };
const int8_t signalLumaHigh[4] = { 32, 66, 105, 105 };
Expand Down Expand Up @@ -50,7 +49,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr<Console> console, int resDivider
outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top);
}

DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (IsOddFrame() ? 8 : 0) + 327360, SMPTE_C);
DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (IsOddFrame() ? 8 : 0) + 327360);

_workDone = true;
}
Expand All @@ -70,7 +69,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)

_workDone = false;
_waitWork.Signal();
DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (IsOddFrame() ? 8 : 0) + GetOverscan().Top*341*8, _SMPTE_C);
DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (IsOddFrame() ? 8 : 0) + GetOverscan().Top*341*8);
while(!_workDone) {}
}

Expand All @@ -94,6 +93,11 @@ void BisqwitNtscFilter::OnBeforeApplyFilter()
const double pi = std::atan(1.0) * 4;
int contrast = (int)((pictureSettings.Contrast + 1.0) * (pictureSettings.Contrast + 1.0) * 167941);
int saturation = (int)((pictureSettings.Saturation + 1.0) * (pictureSettings.Saturation + 1.0) * 144044);
bool colorimetryCorrection = _console->GetSettings()->GetNtscFilterSettings().ColorimetryCorrection;

// [saturation at 0] * 100 / [I or Q width at 0]
double SatFactor = 144044 * 100 / 12;

for(int i = 0; i < 27; i++) {
_sinetable[i] = (int8_t)(8 * std::sin(i * 2 * pi / 12 + pictureSettings.Hue * pi));
}
Expand All @@ -104,30 +108,14 @@ void BisqwitNtscFilter::OnBeforeApplyFilter()

_y = contrast / _yWidth;

// magic numbers is corresponding values from the YIQ to RGB formula
// but divided by 13,995 * [arbitrary value]
/*
_ir = (int)(( 0.95599 / (13995 * 34.2457747)) * contrast * saturation / _iWidth);
_ig = (int)((-0.27201 / (13995 * 212.3864250)) * contrast * saturation / _iWidth);
_ib = (int)((-1.10674 / (13995 * 78.0674723)) * contrast * saturation / _iWidth);
_qr = (int)(( 0.62082 / (13995 * 44.7370743)) * contrast * saturation / _qWidth);
_qg = (int)((-0.64720 / (13995 * 73.0015960)) * contrast * saturation / _qWidth);
_qb = (int)(( 1.70423 / (13995 * 73.0404051)) * contrast * saturation / _qWidth);
*/
_ir = (int)(contrast * 1.994681e-6 * saturation / _iWidth);
_ig = (int)(contrast * 9.151351e-8 * saturation / _iWidth);
_ib = (int)(contrast * -1.012984e-6 * saturation / _iWidth);
_qr = (int)(contrast * 9.915742e-7 * saturation / _qWidth);
_qg = (int)(contrast * -6.334805e-7 * saturation / _qWidth);
_qb = (int)(contrast * 1.667217e-6 * saturation / _qWidth);

// alternate values based on the SMPTE C color primaries
_irC = (int)((0.95599 / (13995 * 80)) * contrast * saturation / _iWidth);
_igC = (int)((-0.27201 / (13995 * 80)) * contrast * saturation / _iWidth);
_ibC = (int)((-1.10674 / (13995 * 80)) * contrast * saturation / _iWidth);
_qrC = (int)((0.62082 / (13995 * 80)) * contrast * saturation / _qWidth);
_qgC = (int)((-0.64720 / (13995 * 80)) * contrast * saturation / _qWidth);
_qbC = (int)((1.70423 / (13995 * 80)) * contrast * saturation / _qWidth);
_ir = colorimetryCorrection ? (int)(contrast * 1.994681e-6 * saturation / _iWidth) : (int)(contrast * (0.95599 / SatFactor) * saturation / _iWidth);
_qr = colorimetryCorrection ? (int)(contrast * 9.915742e-7 * saturation / _qWidth) : (int)(contrast * (0.62082 / SatFactor) * saturation / _qWidth);

_ig = colorimetryCorrection ? (int)(contrast * 9.151351e-8 * saturation / _iWidth) : (int)(contrast * (-0.27201 / SatFactor) * saturation / _iWidth);
_qg = colorimetryCorrection ? (int)(contrast * -6.334805e-7 * saturation / _qWidth) : (int)(contrast * (-0.64720 / SatFactor) * saturation / _qWidth);

_ib = colorimetryCorrection ? (int)(contrast * -1.012984e-6 * saturation / _iWidth) : (int)(contrast * (-1.10674 / SatFactor) * saturation / _iWidth);
_qb = colorimetryCorrection ? (int)(contrast * 1.667217e-6 * saturation / _qWidth) : (int)(contrast * (1.70423 / SatFactor) * saturation / _qWidth);
}

void BisqwitNtscFilter::RecursiveBlend(int iterationCount, uint64_t *output, uint64_t *currentLine, uint64_t *nextLine, int pixelsPerCycle, bool verticalBlend)
Expand Down Expand Up @@ -209,7 +197,7 @@ void BisqwitNtscFilter::GenerateNtscSignal(int8_t *ntscSignal, int &phase, int r
phase += (341 - 256 - _paddingSize * 2) * _signalsPerPixel;
}

void BisqwitNtscFilter::DecodeFrame(int startRow, int endRow, uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, int startPhase, bool SMPTE_C)
void BisqwitNtscFilter::DecodeFrame(int startRow, int endRow, uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, int startPhase)
{
int pixelsPerCycle = 8 / _resDivider;
int phase = startPhase;
Expand All @@ -229,7 +217,7 @@ void BisqwitNtscFilter::DecodeFrame(int startRow, int endRow, uint16_t *ppuOutpu
GenerateNtscSignal(rowSignal, phase, y);

//Convert the NTSC signal to RGB
NtscDecodeLine(lineWidth * _signalsPerPixel, rowSignal, outputBuffer, (startCycle + 7) % 12, SMPTE_C);
NtscDecodeLine(lineWidth * _signalsPerPixel, rowSignal, outputBuffer, (startCycle + 7) % 12);

outputBuffer += rowPixelGap;
}
Expand Down Expand Up @@ -281,7 +269,7 @@ void BisqwitNtscFilter::DecodeFrame(int startRow, int endRow, uint16_t *ppuOutpu
* In essence it conveys in one integer the same information that real NTSC signal
* would convey in the colorburst period in the beginning of each scanline.
*/
void BisqwitNtscFilter::NtscDecodeLine(int width, const int8_t* signal, uint32_t* target, int phase0, bool SMPTE_C)
void BisqwitNtscFilter::NtscDecodeLine(int width, const int8_t* signal, uint32_t* target, int phase0)
{
auto Read = [=](int pos) -> char { return pos >= 0 ? signal[pos] : 0; };
auto Cos = [=](int pos) -> char { return _sinetable[(pos + 36) % 12 + phase0]; };
Expand All @@ -299,9 +287,9 @@ void BisqwitNtscFilter::NtscDecodeLine(int width, const int8_t* signal, uint32_t
qsum += Read(s) * Sin(s) - Read(s - _qWidth) * Sin(s - _qWidth);

if(!(s % _resDivider) && s >= leftOverscan) {
int r = std::min(255, std::max(0, (ysum*_y + isum*(SMPTE_C ? _irC : _ir) + qsum*(SMPTE_C ? _qrC : _qr)) / 65536));
int g = std::min(255, std::max(0, (ysum*_y + isum*(SMPTE_C ? _igC : _ig) + qsum*(SMPTE_C ? _qgC : _qg)) / 65536));
int b = std::min(255, std::max(0, (ysum*_y + isum*(SMPTE_C ? _ibC : _ib) + qsum*(SMPTE_C ? _qbC : _qb)) / 65536));
int r = std::min(255, std::max(0, (ysum*_y + isum*_ir + qsum*_qr) / 65536));
int g = std::min(255, std::max(0, (ysum*_y + isum*_ig + qsum*_qg) / 65536));
int b = std::min(255, std::max(0, (ysum*_y + isum*_ib + qsum*_qb) / 65536));

*target = 0xFF000000 | (r << 16) | (g << 8) | b;
target++;
Expand Down
11 changes: 5 additions & 6 deletions Core/BisqwitNtscFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class BisqwitNtscFilter : public BaseVideoFilter
atomic<bool> _workDone;

bool _keepVerticalRes = false;
bool _SMPTE_C = false;

int _resDivider = 1;
uint16_t *_ppuOutputBuffer = nullptr;
Expand All @@ -34,8 +33,8 @@ class BisqwitNtscFilter : public BaseVideoFilter
*/
int _yWidth, _iWidth, _qWidth;
int _y;
int _ir, _ig, _ib, _irC, _igC, _ibC;
int _qr, _qg, _qb, _qrC, _qgC, _qbC;
int _ir, _ig, _ib;
int _qr, _qg, _qb;

//To finetune hue, you would have to recalculate sinetable[]. (Coarse changes can be made with Phase0.)
int8_t _sinetable[27]; // 8*sin(x*2pi/12)
Expand All @@ -44,14 +43,14 @@ class BisqwitNtscFilter : public BaseVideoFilter

void RecursiveBlend(int iterationCount, uint64_t *output, uint64_t *currentLine, uint64_t *nextLine, int pixelsPerCycle, bool verticalBlend);

void NtscDecodeLine(int width, const int8_t* signal, uint32_t* target, int phase0, bool SMPTE_C);
void NtscDecodeLine(int width, const int8_t* signal, uint32_t* target, int phase0);

void GenerateNtscSignal(int8_t *ntscSignal, int &phase, int rowNumber);
void DecodeFrame(int startRow, int endRow, uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, int startPhase, bool SMPTE_C);
void DecodeFrame(int startRow, int endRow, uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, int startPhase);
void OnBeforeApplyFilter();

public:
BisqwitNtscFilter(shared_ptr<Console> console, int resDivider, bool SMPTE_C);
BisqwitNtscFilter(shared_ptr<Console> console, int resDivider);
virtual ~BisqwitNtscFilter();

virtual void ApplyFilter(uint16_t *ppuOutputBuffer);
Expand Down
7 changes: 3 additions & 4 deletions Core/EmulationSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,6 @@ enum class VideoFilterType
Prescale8x = 23,
Prescale10x = 24,
Raw = 25,
BisqwitNtscSMPTECQuarterRes = 26,
BisqwitNtscSMPTECHalfRes = 27,
BisqwitNtscSMPTEC = 28,
HdPack = 999
};

Expand Down Expand Up @@ -234,6 +231,7 @@ struct NtscFilterSettings
bool MergeFields = false;
bool VerticalBlend = false;
bool KeepVerticalResolution = false;
bool ColorimetryCorrection = true;

double YFilterLength = 0;
double IFilterLength = 0;
Expand Down Expand Up @@ -1226,7 +1224,7 @@ class EmulationSettings
return _pictureSettings;
}

void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, bool verticalBlend, bool keepVerticalResolution)
void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, bool verticalBlend, bool keepVerticalResolution, bool colorimetryCorrection)
{
_ntscFilterSettings.Artifacts = artifacts;
_ntscFilterSettings.Bleed = bleed;
Expand All @@ -1243,6 +1241,7 @@ class EmulationSettings

_ntscFilterSettings.VerticalBlend = verticalBlend;
_ntscFilterSettings.KeepVerticalResolution = keepVerticalResolution;
_ntscFilterSettings.ColorimetryCorrection = colorimetryCorrection;
}

NtscFilterSettings GetNtscFilterSettings()
Expand Down
3 changes: 0 additions & 3 deletions Core/ScaleFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,6 @@ shared_ptr<ScaleFilter> ScaleFilter::GetScaleFilter(VideoFilterType filter)
case VideoFilterType::BisqwitNtsc:
case VideoFilterType::BisqwitNtscHalfRes:
case VideoFilterType::BisqwitNtscQuarterRes:
case VideoFilterType::BisqwitNtscSMPTEC:
case VideoFilterType::BisqwitNtscSMPTECHalfRes:
case VideoFilterType::BisqwitNtscSMPTECQuarterRes:
case VideoFilterType::NTSC:
case VideoFilterType::HdPack:
case VideoFilterType::Raw:
Expand Down
9 changes: 3 additions & 6 deletions Core/VideoDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,9 @@ void VideoDecoder::UpdateVideoFilter()
switch(_videoFilterType) {
case VideoFilterType::None: break;
case VideoFilterType::NTSC: _videoFilter.reset(new NtscFilter(_console)); break;
case VideoFilterType::BisqwitNtsc: _videoFilter.reset(new BisqwitNtscFilter(_console, 1, false)); break;
case VideoFilterType::BisqwitNtscHalfRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 2, false)); break;
case VideoFilterType::BisqwitNtscQuarterRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 4, false)); break;
case VideoFilterType::BisqwitNtscSMPTEC: _videoFilter.reset(new BisqwitNtscFilter(_console, 1, true)); break;
case VideoFilterType::BisqwitNtscSMPTECHalfRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 2, true)); break;
case VideoFilterType::BisqwitNtscSMPTECQuarterRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 4, true)); 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;
}
Expand Down
3 changes: 2 additions & 1 deletion GUI.NET/Config/VideoInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class VideoInfo
[MinMax(-100, 100)] public Int32 NtscSharpness = 0;
public bool NtscMergeFields = false;
public bool NtscVerticalBlend = true;
public bool NtscColorimetryCorrection = true;

[MinMax(-50, 400)] public Int32 NtscYFilterLength = 0;
[MinMax(0, 400)] public Int32 NtscIFilterLength = 50;
Expand Down Expand Up @@ -109,7 +110,7 @@ static public void ApplyConfig()
InteropEmu.SetVideoAspectRatio(videoInfo.AspectRatio, videoInfo.CustomAspectRatio);

InteropEmu.SetPictureSettings(videoInfo.Brightness / 100.0, videoInfo.Contrast / 100.0, videoInfo.Saturation / 100.0, videoInfo.Hue / 100.0, videoInfo.ScanlineIntensity / 100.0);
InteropEmu.SetNtscFilterSettings(videoInfo.NtscArtifacts / 100.0, videoInfo.NtscBleed / 100.0, videoInfo.NtscFringing / 100.0, videoInfo.NtscGamma / 100.0, videoInfo.NtscResolution / 100.0, videoInfo.NtscSharpness / 100.0, videoInfo.NtscMergeFields, videoInfo.NtscYFilterLength / 100.0, videoInfo.NtscIFilterLength / 100.0, videoInfo.NtscQFilterLength / 100.0, videoInfo.NtscVerticalBlend);
InteropEmu.SetNtscFilterSettings(videoInfo.NtscArtifacts / 100.0, videoInfo.NtscBleed / 100.0, videoInfo.NtscFringing / 100.0, videoInfo.NtscGamma / 100.0, videoInfo.NtscResolution / 100.0, videoInfo.NtscSharpness / 100.0, videoInfo.NtscMergeFields, videoInfo.NtscYFilterLength / 100.0, videoInfo.NtscIFilterLength / 100.0, videoInfo.NtscQFilterLength / 100.0, videoInfo.NtscVerticalBlend, videoInfo.NtscColorimetryCorrection);

if(!string.IsNullOrWhiteSpace(videoInfo.PaletteData)) {
try {
Expand Down
Loading