Mesen/Core/DefaultVideoFilter.cpp
2020-01-28 20:07:42 -05:00

130 lines
4.6 KiB
C++

#include "stdafx.h"
#include "DefaultVideoFilter.h"
#include "EmulationSettings.h"
#include <math.h>
#include <algorithm>
#include "PPU.h"
#include "DebugHud.h"
#include "Console.h"
DefaultVideoFilter::DefaultVideoFilter(shared_ptr<Console> console) : BaseVideoFilter(console)
{
InitConversionMatrix(_pictureSettings.Hue, _pictureSettings.Saturation);
}
void DefaultVideoFilter::InitConversionMatrix(double hueShift, double saturationShift)
{
_pictureSettings.Hue = hueShift;
_pictureSettings.Saturation = saturationShift;
double hue = hueShift * 3.14159265358979323846264f;
double sat = saturationShift + 1;
double baseValues[6] = { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
double s = sin(hue) * sat;
double c = cos(hue) * sat;
double *output = _yiqToRgbMatrix;
double *input = baseValues;
for(int n = 0; n < 3; n++) {
double i = *input++;
double q = *input++;
*output++ = i * c - q * s;
*output++ = i * s + q * c;
}
}
FrameInfo DefaultVideoFilter::GetFrameInfo()
{
OverscanDimensions overscan = GetOverscan();
return { overscan.GetScreenWidth(), overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 };
}
void DefaultVideoFilter::OnBeforeApplyFilter()
{
PictureSettings currentSettings = _console->GetSettings()->GetPictureSettings();
if(_pictureSettings.Hue != currentSettings.Hue || _pictureSettings.Saturation != currentSettings.Saturation) {
InitConversionMatrix(currentSettings.Hue, currentSettings.Saturation);
}
_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));
}
}
void DefaultVideoFilter::DecodePpuBuffer(uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, bool displayScanlines)
{
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++;
}
}
}
}
void DefaultVideoFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
{
DecodePpuBuffer(ppuOutputBuffer, GetOutputBuffer(), _console->GetSettings()->GetVideoFilterType() <= VideoFilterType::BisqwitNtsc);
}
void DefaultVideoFilter::RgbToYiq(double r, double g, double b, double &y, double &i, double &q)
{
y = r * 0.299f + g * 0.587f + b * 0.114f;
i = r * 0.596f - g * 0.275f - b * 0.321f;
q = r * 0.212f - g * 0.523f + b * 0.311f;
}
void DefaultVideoFilter::YiqToRgb(double y, double i, double q, double &r, double &g, double &b)
{
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;
}