Mesen/Core/NtscFilter.cpp

161 lines
5.7 KiB
C++

#include "stdafx.h"
#include "NtscFilter.h"
#include "PPU.h"
#include "EmulationSettings.h"
#include "Console.h"
NtscFilter::NtscFilter(shared_ptr<Console> console) : BaseVideoFilter(console)
{
memset(_palette, 0, sizeof(_palette));
memset(&_ntscData, 0, sizeof(_ntscData));
_ntscSetup = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
_ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240];
}
FrameInfo NtscFilter::GetFrameInfo()
{
OverscanDimensions overscan = GetOverscan();
uint32_t overscanLeft = overscan.Left > 0 ? NES_NTSC_OUT_WIDTH(overscan.Left) : 0;
uint32_t overscanRight = overscan.Right > 0 ? NES_NTSC_OUT_WIDTH(overscan.Right) : 0;
if(_keepVerticalRes) {
return {
(NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) - overscanLeft - overscanRight),
(PPU::ScreenHeight - overscan.Top - overscan.Bottom),
NES_NTSC_OUT_WIDTH(PPU::ScreenWidth),
PPU::ScreenHeight,
4
};
} else {
return {
NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) - overscanLeft - overscanRight,
(PPU::ScreenHeight - overscan.Top - overscan.Bottom) * 2,
NES_NTSC_OUT_WIDTH(PPU::ScreenWidth),
PPU::ScreenHeight * 2,
4
};
}
}
void NtscFilter::OnBeforeApplyFilter()
{
bool paletteChanged = false;
uint32_t* palette = _console->GetSettings()->GetRgbPalette();
for(int i = 0, len = _console->GetSettings()->IsFullColorPalette() ? 512 : 64; i < len; i++) {
uint8_t r = (palette[i] >> 16) & 0xFF;
uint8_t g = (palette[i] >> 8) & 0xFF;
uint8_t b = palette[i] & 0xFF;
if(_palette[i * 3] != r || _palette[i * 3 + 1] != g || _palette[i * 3 + 2] != b) {
paletteChanged = true;
_palette[i * 3] = (palette[i] >> 16) & 0xFF;
_palette[i * 3 + 1] = (palette[i] >> 8) & 0xFF;
_palette[i * 3 + 2] = palette[i] & 0xFF;
}
}
PictureSettings pictureSettings = _console->GetSettings()->GetPictureSettings();
NtscFilterSettings ntscSettings = _console->GetSettings()->GetNtscFilterSettings();
_keepVerticalRes = ntscSettings.KeepVerticalResolution;
if(paletteChanged || _ntscSetup.hue != pictureSettings.Hue || _ntscSetup.saturation != pictureSettings.Saturation || _ntscSetup.brightness != pictureSettings.Brightness || _ntscSetup.contrast != pictureSettings.Contrast ||
_ntscSetup.artifacts != ntscSettings.Artifacts || _ntscSetup.bleed != ntscSettings.Bleed || _ntscSetup.fringing != ntscSettings.Fringing || _ntscSetup.gamma != ntscSettings.Gamma ||
(_ntscSetup.merge_fields == 1) != ntscSettings.MergeFields || _ntscSetup.resolution != ntscSettings.Resolution || _ntscSetup.sharpness != ntscSettings.Sharpness) {
_ntscSetup.hue = pictureSettings.Hue;
_ntscSetup.saturation = pictureSettings.Saturation;
_ntscSetup.brightness = pictureSettings.Brightness;
_ntscSetup.contrast = pictureSettings.Contrast;
_ntscSetup.artifacts = ntscSettings.Artifacts;
_ntscSetup.bleed = ntscSettings.Bleed;
_ntscSetup.fringing = ntscSettings.Fringing;
_ntscSetup.gamma = ntscSettings.Gamma;
_ntscSetup.merge_fields = (int)ntscSettings.MergeFields;
_ntscSetup.resolution = ntscSettings.Resolution;
_ntscSetup.sharpness = ntscSettings.Sharpness;
if(_console->GetSettings()->IsFullColorPalette()) {
_ntscSetup.base_palette = nullptr;
_ntscSetup.palette = _palette;
} else {
_ntscSetup.base_palette = _palette;
_ntscSetup.palette = nullptr;
}
nes_ntsc_init(&_ntscData, &_ntscSetup);
}
}
void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
{
nes_ntsc_blit(&_ntscData, ppuOutputBuffer, PPU::ScreenWidth, IsOddFrame() ? 0 : 1, PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4);
GenerateArgbFrame(_ntscBuffer);
}
void NtscFilter::GenerateArgbFrame(uint32_t *ntscBuffer)
{
uint32_t* outputBuffer = GetOutputBuffer();
OverscanDimensions overscan = GetOverscan();
int overscanLeft = overscan.Left > 0 ? NES_NTSC_OUT_WIDTH(overscan.Left) : 0;
int overscanRight = overscan.Right > 0 ? NES_NTSC_OUT_WIDTH(overscan.Right) : 0;
int rowWidth = NES_NTSC_OUT_WIDTH(PPU::ScreenWidth);
int rowWidthOverscan = rowWidth - overscanLeft - overscanRight;
if(_keepVerticalRes) {
ntscBuffer += rowWidth * overscan.Top + overscanLeft;
for(uint32_t i = 0, len = overscan.GetScreenHeight(); i < len; i++) {
memcpy(outputBuffer, ntscBuffer, rowWidthOverscan * sizeof(uint32_t));
outputBuffer += rowWidthOverscan;
ntscBuffer += rowWidth;
}
} else {
double scanlineIntensity = 1.0 - _console->GetSettings()->GetPictureSettings().ScanlineIntensity;
bool verticalBlend = _console->GetSettings()->GetNtscFilterSettings().VerticalBlend;
for(int y = PPU::ScreenHeight - 1 - overscan.Bottom; y >= (int)overscan.Top; y--) {
uint32_t const* in = ntscBuffer + y * rowWidth;
uint32_t* out = outputBuffer + (y - overscan.Top) * 2 * rowWidthOverscan;
if(verticalBlend || scanlineIntensity < 1.0) {
for(int x = 0; x < rowWidthOverscan; x++) {
uint32_t prev = in[overscanLeft];
uint32_t next = y < 239 ? in[overscanLeft + rowWidth] : 0;
*out = 0xFF000000 | prev;
/* mix 24-bit rgb without losing low bits */
uint32_t mixed;
if(verticalBlend) {
mixed = (prev + next + ((prev ^ next) & 0x030303)) >> 1;
} else {
mixed = prev;
}
if(scanlineIntensity < 1.0) {
uint8_t r = (mixed >> 16) & 0xFF, g = (mixed >> 8) & 0xFF, b = mixed & 0xFF;
r = (uint8_t)(r * scanlineIntensity);
g = (uint8_t)(g * scanlineIntensity);
b = (uint8_t)(b * scanlineIntensity);
*(out + rowWidthOverscan) = 0xFF000000 | (r << 16) | (g << 8) | b;
} else {
*(out + rowWidthOverscan) = 0xFF000000 | mixed;
}
in++;
out++;
}
} else {
for(int i = 0; i < rowWidthOverscan; i++) {
out[i] = 0xFF000000 | in[overscanLeft+i];
}
memcpy(out + rowWidthOverscan, out, rowWidthOverscan * sizeof(uint32_t));
}
}
}
}
NtscFilter::~NtscFilter()
{
delete[] _ntscBuffer;
}