Added support for 512-color palette files
This commit is contained in:
parent
435c067da1
commit
3a752d6755
11 changed files with 152 additions and 128 deletions
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
DefaultVideoFilter::DefaultVideoFilter(shared_ptr<Console> console) : BaseVideoFilter(console)
|
DefaultVideoFilter::DefaultVideoFilter(shared_ptr<Console> console) : BaseVideoFilter(console)
|
||||||
{
|
{
|
||||||
InitDecodeTables();
|
|
||||||
|
|
||||||
InitConversionMatrix(_pictureSettings.Hue, _pictureSettings.Saturation);
|
InitConversionMatrix(_pictureSettings.Hue, _pictureSettings.Saturation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,53 +91,15 @@ void DefaultVideoFilter::YiqToRgb(double y, double i, double q, double &r, doubl
|
||||||
b = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[4] * i + _yiqToRgbMatrix[5] * q)));
|
b = std::max(0.0, std::min(1.0, (y + _yiqToRgbMatrix[4] * i + _yiqToRgbMatrix[5] * q)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefaultVideoFilter::InitDecodeTables()
|
|
||||||
{
|
|
||||||
for(int i = 0; i < 256; i++) {
|
|
||||||
for(int j = 0; j < 8; j++) {
|
|
||||||
double redColor = i;
|
|
||||||
double greenColor = i;
|
|
||||||
double blueColor = i;
|
|
||||||
if(j & 0x01) {
|
|
||||||
//Intensify red
|
|
||||||
redColor *= 1.1;
|
|
||||||
greenColor *= 0.9;
|
|
||||||
blueColor *= 0.9;
|
|
||||||
}
|
|
||||||
if(j & 0x02) {
|
|
||||||
//Intensify green
|
|
||||||
greenColor *= 1.1;
|
|
||||||
redColor *= 0.9;
|
|
||||||
blueColor *= 0.9;
|
|
||||||
}
|
|
||||||
if(j & 0x04) {
|
|
||||||
//Intensify blue
|
|
||||||
blueColor *= 1.1;
|
|
||||||
redColor *= 0.9;
|
|
||||||
greenColor *= 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
redColor = (redColor > 255 ? 255 : redColor) / 255.0;
|
|
||||||
greenColor = (greenColor > 255 ? 255 : greenColor) / 255.0;
|
|
||||||
blueColor = (blueColor > 255 ? 255 : blueColor) / 255.0;
|
|
||||||
|
|
||||||
_redDecodeTable[i][j] = redColor;
|
|
||||||
_greenDecodeTable[i][j] = greenColor;
|
|
||||||
_blueDecodeTable[i][j] = blueColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t DefaultVideoFilter::ProcessIntensifyBits(uint16_t ppuPixel, double scanlineIntensity)
|
uint32_t DefaultVideoFilter::ProcessIntensifyBits(uint16_t ppuPixel, double scanlineIntensity)
|
||||||
{
|
{
|
||||||
uint32_t pixelOutput = _console->GetSettings()->GetRgbPalette()[ppuPixel & 0x3F];
|
uint32_t pixelOutput = _console->GetSettings()->GetRgbPalette()[ppuPixel & 0x1FF];
|
||||||
uint32_t intensifyBits = (ppuPixel >> 6) & 0x07;
|
|
||||||
|
|
||||||
if(intensifyBits || _needToProcess || scanlineIntensity < 1.0) {
|
if(_needToProcess || scanlineIntensity < 1.0) {
|
||||||
//Incorrect emphasis bit implementation, but will do for now.
|
//Incorrect emphasis bit implementation, but will do for now.
|
||||||
double redChannel = _redDecodeTable[((pixelOutput & 0xFF0000) >> 16)][intensifyBits];
|
double redChannel = ((pixelOutput & 0xFF0000) >> 16) / 255.0;
|
||||||
double greenChannel = _greenDecodeTable[((pixelOutput & 0xFF00) >> 8)][intensifyBits];
|
double greenChannel = ((pixelOutput & 0xFF00) >> 8) / 255.0;
|
||||||
double blueChannel = _blueDecodeTable[(pixelOutput & 0xFF)][intensifyBits];
|
double blueChannel = (pixelOutput & 0xFF) / 255.0;
|
||||||
|
|
||||||
//Apply brightness, contrast, hue & saturation
|
//Apply brightness, contrast, hue & saturation
|
||||||
if(_needToProcess) {
|
if(_needToProcess) {
|
||||||
|
|
|
@ -6,15 +6,10 @@
|
||||||
class DefaultVideoFilter : public BaseVideoFilter
|
class DefaultVideoFilter : public BaseVideoFilter
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
double _redDecodeTable[256][8];
|
|
||||||
double _greenDecodeTable[256][8];
|
|
||||||
double _blueDecodeTable[256][8];
|
|
||||||
|
|
||||||
double _yiqToRgbMatrix[6];
|
double _yiqToRgbMatrix[6];
|
||||||
PictureSettings _pictureSettings;
|
PictureSettings _pictureSettings;
|
||||||
bool _needToProcess = false;
|
bool _needToProcess = false;
|
||||||
|
|
||||||
void InitDecodeTables();
|
|
||||||
void InitConversionMatrix(double hueShift, double saturationShift);
|
void InitConversionMatrix(double hueShift, double saturationShift);
|
||||||
|
|
||||||
void RgbToYiq(double r, double g, double b, double &y, double &i, double &q);
|
void RgbToYiq(double r, double g, double b, double &y, double &i, double &q);
|
||||||
|
|
|
@ -604,8 +604,9 @@ private:
|
||||||
/* 2C05-05 */ { 0xFF6D6D6D, 0xFF002491, 0xFF0000DA, 0xFF6D48DA, 0xFF91006D, 0xFFB6006D, 0xFFB62400, 0xFF914800, 0xFF6D4800, 0xFF244800, 0xFF006D24, 0xFF009100, 0xFF004848, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB6B6B6, 0xFF006DDA, 0xFF0048FF, 0xFF9100FF, 0xFFB600FF, 0xFFFF0091, 0xFFFF0000, 0xFFDA6D00, 0xFF916D00, 0xFF249100, 0xFF009100, 0xFF00B66D, 0xFF009191, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF6DB6FF, 0xFF9191FF, 0xFFDA6DFF, 0xFFFF00FF, 0xFFFF6DFF, 0xFFFF9100, 0xFFFFB600, 0xFFDADA00, 0xFF6DDA00, 0xFF00FF00, 0xFF48FFDA, 0xFF00FFFF, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFB6DAFF, 0xFFDAB6FF, 0xFFFFB6FF, 0xFFFF91FF, 0xFFFFB6B6, 0xFFFFDA91, 0xFFFFFF48, 0xFFFFFF6D, 0xFFB6FF48, 0xFF91FF6D, 0xFF48FFDA, 0xFF91DAFF, 0xFF000000, 0xFF000000, 0xFF000000 }
|
/* 2C05-05 */ { 0xFF6D6D6D, 0xFF002491, 0xFF0000DA, 0xFF6D48DA, 0xFF91006D, 0xFFB6006D, 0xFFB62400, 0xFF914800, 0xFF6D4800, 0xFF244800, 0xFF006D24, 0xFF009100, 0xFF004848, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB6B6B6, 0xFF006DDA, 0xFF0048FF, 0xFF9100FF, 0xFFB600FF, 0xFFFF0091, 0xFFFF0000, 0xFFDA6D00, 0xFF916D00, 0xFF249100, 0xFF009100, 0xFF00B66D, 0xFF009191, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF6DB6FF, 0xFF9191FF, 0xFFDA6DFF, 0xFFFF00FF, 0xFFFF6DFF, 0xFFFF9100, 0xFFFFB600, 0xFFDADA00, 0xFF6DDA00, 0xFF00FF00, 0xFF48FFDA, 0xFF00FFFF, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFB6DAFF, 0xFFDAB6FF, 0xFFFFB6FF, 0xFFFF91FF, 0xFFFFB6B6, 0xFFFFDA91, 0xFFFFFF48, 0xFFFFFF6D, 0xFFB6FF48, 0xFF91FF6D, 0xFF48FFDA, 0xFF91DAFF, 0xFF000000, 0xFF000000, 0xFF000000 }
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t _defaultPpuPalette[64] = { /* 2C02 */ 0xFF666666, 0xFF002A88, 0xFF1412A7, 0xFF3B00A4, 0xFF5C007E, 0xFF6E0040, 0xFF6C0600, 0xFF561D00, 0xFF333500, 0xFF0B4800, 0xFF005200, 0xFF004F08, 0xFF00404D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFADADAD, 0xFF155FD9, 0xFF4240FF, 0xFF7527FE, 0xFFA01ACC, 0xFFB71E7B, 0xFFB53120, 0xFF994E00, 0xFF6B6D00, 0xFF388700, 0xFF0C9300, 0xFF008F32, 0xFF007C8D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFF64B0FF, 0xFF9290FF, 0xFFC676FF, 0xFFF36AFF, 0xFFFE6ECC, 0xFFFE8170, 0xFFEA9E22, 0xFFBCBE00, 0xFF88D800, 0xFF5CE430, 0xFF45E082, 0xFF48CDDE, 0xFF4F4F4F, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFFC0DFFF, 0xFFD3D2FF, 0xFFE8C8FF, 0xFFFBC2FF, 0xFFFEC4EA, 0xFFFECCC5, 0xFFF7D8A5, 0xFFE4E594, 0xFFCFEF96, 0xFFBDF4AB, 0xFFB3F3CC, 0xFFB5EBF2, 0xFFB8B8B8, 0xFF000000, 0xFF000000 };
|
bool _isFullColorPalette = false;
|
||||||
uint32_t _currentPalette[64] = { 0xFF666666, 0xFF002A88, 0xFF1412A7, 0xFF3B00A4, 0xFF5C007E, 0xFF6E0040, 0xFF6C0600, 0xFF561D00, 0xFF333500, 0xFF0B4800, 0xFF005200, 0xFF004F08, 0xFF00404D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFADADAD, 0xFF155FD9, 0xFF4240FF, 0xFF7527FE, 0xFFA01ACC, 0xFFB71E7B, 0xFFB53120, 0xFF994E00, 0xFF6B6D00, 0xFF388700, 0xFF0C9300, 0xFF008F32, 0xFF007C8D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFF64B0FF, 0xFF9290FF, 0xFFC676FF, 0xFFF36AFF, 0xFFFE6ECC, 0xFFFE8170, 0xFFEA9E22, 0xFFBCBE00, 0xFF88D800, 0xFF5CE430, 0xFF45E082, 0xFF48CDDE, 0xFF4F4F4F, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFFC0DFFF, 0xFFD3D2FF, 0xFFE8C8FF, 0xFFFBC2FF, 0xFFFEC4EA, 0xFFFECCC5, 0xFFF7D8A5, 0xFFE4E594, 0xFFCFEF96, 0xFFBDF4AB, 0xFFB3F3CC, 0xFFB5EBF2, 0xFFB8B8B8, 0xFF000000, 0xFF000000 };
|
uint32_t _userPalette[512] = { };
|
||||||
|
uint32_t _currentPalette[512] = { };
|
||||||
|
|
||||||
const uint8_t _paletteLut[11][64] = {
|
const uint8_t _paletteLut[11][64] = {
|
||||||
/* 2C02 */ { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63 },
|
/* 2C02 */ { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63 },
|
||||||
|
@ -785,20 +786,90 @@ public:
|
||||||
UpdateCurrentPalette();
|
UpdateCurrentPalette();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PpuModel GetPpuModel()
|
||||||
|
{
|
||||||
|
return _ppuModel;
|
||||||
|
}
|
||||||
|
|
||||||
void UpdateCurrentPalette()
|
void UpdateCurrentPalette()
|
||||||
{
|
{
|
||||||
if(CheckFlag(EmulationFlags::UseCustomVsPalette)) {
|
if(CheckFlag(EmulationFlags::UseCustomVsPalette)) {
|
||||||
for(int i = 0; i < 64; i++) {
|
for(int i = 0; i < 64; i++) {
|
||||||
_currentPalette[i] = _ppuPaletteArgb[0][_paletteLut[(int)_ppuModel][i]];
|
for(int j = 0; j < 8; j++) {
|
||||||
|
_currentPalette[(j << 6) | i] = _userPalette[(j << 6) | (_paletteLut[(int)_ppuModel][i])];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else if(_ppuModel == PpuModel::Ppu2C02) {
|
||||||
|
memcpy(_currentPalette, _userPalette, sizeof(_userPalette));
|
||||||
} else {
|
} else {
|
||||||
memcpy(_currentPalette, _ppuPaletteArgb[(int)_ppuModel], sizeof(_currentPalette));
|
memcpy(_currentPalette, _ppuPaletteArgb[(int)_ppuModel], sizeof(_ppuPaletteArgb[0]));
|
||||||
|
GenerateFullColorPalette(_currentPalette);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PpuModel GetPpuModel()
|
uint32_t* GetRgbPalette()
|
||||||
{
|
{
|
||||||
return _ppuModel;
|
return _currentPalette;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetUserRgbPalette(uint32_t* paletteBuffer)
|
||||||
|
{
|
||||||
|
memcpy(paletteBuffer, _userPalette, sizeof(_userPalette));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUserRgbPalette(uint32_t* paletteBuffer, uint32_t size = 64)
|
||||||
|
{
|
||||||
|
if(size != 64 && size != 512) {
|
||||||
|
throw new std::runtime_error("Invalid palette buffer size");
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(_userPalette, paletteBuffer, size * sizeof(uint32_t));
|
||||||
|
if(size == 64) {
|
||||||
|
GenerateFullColorPalette(_userPalette);
|
||||||
|
}
|
||||||
|
_isFullColorPalette = (size == 512);
|
||||||
|
UpdateCurrentPalette();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFullColorPalette()
|
||||||
|
{
|
||||||
|
return _isFullColorPalette;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateFullColorPalette(uint32_t* paletteBuffer)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 64; i++) {
|
||||||
|
for(int j = 1; j < 8; j++) {
|
||||||
|
double redColor = (uint8_t)(paletteBuffer[i] >> 16);
|
||||||
|
double greenColor = (uint8_t)(paletteBuffer[i] >> 8);
|
||||||
|
double blueColor = (uint8_t)paletteBuffer[i];
|
||||||
|
if(j & 0x01) {
|
||||||
|
//Intensify red
|
||||||
|
redColor *= 1.1;
|
||||||
|
greenColor *= 0.9;
|
||||||
|
blueColor *= 0.9;
|
||||||
|
}
|
||||||
|
if(j & 0x02) {
|
||||||
|
//Intensify green
|
||||||
|
greenColor *= 1.1;
|
||||||
|
redColor *= 0.9;
|
||||||
|
blueColor *= 0.9;
|
||||||
|
}
|
||||||
|
if(j & 0x04) {
|
||||||
|
//Intensify blue
|
||||||
|
blueColor *= 1.1;
|
||||||
|
redColor *= 0.9;
|
||||||
|
greenColor *= 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t r = (uint8_t)(redColor > 255 ? 255 : redColor);
|
||||||
|
uint8_t g = (uint8_t)(greenColor > 255 ? 255 : greenColor);
|
||||||
|
uint8_t b = (uint8_t)(blueColor > 255 ? 255 : blueColor);
|
||||||
|
|
||||||
|
uint32_t color = 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||||
|
paletteBuffer[(j << 6) | i] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//0: Muted, 0.5: Default, 1.0: Max volume
|
//0: Muted, 0.5: Default, 1.0: Max volume
|
||||||
|
@ -1189,27 +1260,6 @@ public:
|
||||||
return _exclusiveRefreshRate;
|
return _exclusiveRefreshRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t* GetRgbPalette()
|
|
||||||
{
|
|
||||||
return _currentPalette;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetRgbPalette(uint32_t* paletteBuffer)
|
|
||||||
{
|
|
||||||
memcpy(paletteBuffer, _ppuPaletteArgb[0], sizeof(_ppuPaletteArgb[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetRgbPalette(uint32_t* paletteBuffer)
|
|
||||||
{
|
|
||||||
memcpy(_ppuPaletteArgb[0], paletteBuffer, sizeof(_ppuPaletteArgb[0]));
|
|
||||||
UpdateCurrentPalette();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsDefaultPalette()
|
|
||||||
{
|
|
||||||
return memcmp(_defaultPpuPalette, GetRgbPalette(), sizeof(_defaultPpuPalette)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetExpansionDevice(ExpansionPortDevice expansionDevice)
|
void SetExpansionDevice(ExpansionPortDevice expansionDevice)
|
||||||
{
|
{
|
||||||
_expansionDevice = expansionDevice;
|
_expansionDevice = expansionDevice;
|
||||||
|
|
|
@ -226,7 +226,7 @@ PpuModel NESHeader::GetVsSystemPpuModel()
|
||||||
switch(Byte13 & 0x0F) {
|
switch(Byte13 & 0x0F) {
|
||||||
case 0: return PpuModel::Ppu2C03;
|
case 0: return PpuModel::Ppu2C03;
|
||||||
case 1:
|
case 1:
|
||||||
MessageManager::Log("[iNes] Unsupport VS System Palette specified (2C03G).");
|
MessageManager::Log("[iNes] Unsupported VS System Palette specified (2C03G).");
|
||||||
return PpuModel::Ppu2C03;
|
return PpuModel::Ppu2C03;
|
||||||
|
|
||||||
case 2: return PpuModel::Ppu2C04A;
|
case 2: return PpuModel::Ppu2C04A;
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
NtscFilter::NtscFilter(shared_ptr<Console> console) : BaseVideoFilter(console)
|
NtscFilter::NtscFilter(shared_ptr<Console> console) : BaseVideoFilter(console)
|
||||||
{
|
{
|
||||||
memset(_basePalette, 0, 64 * 3);
|
memset(_palette, 0, sizeof(_palette));
|
||||||
_ntscData = new nes_ntsc_t();
|
memset(&_ntscData, 0, sizeof(_ntscData));
|
||||||
_ntscSetup = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
_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];
|
_ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240];
|
||||||
}
|
}
|
||||||
|
@ -41,17 +41,17 @@ void NtscFilter::OnBeforeApplyFilter()
|
||||||
{
|
{
|
||||||
bool paletteChanged = false;
|
bool paletteChanged = false;
|
||||||
uint32_t* palette = _console->GetSettings()->GetRgbPalette();
|
uint32_t* palette = _console->GetSettings()->GetRgbPalette();
|
||||||
for(int i = 0; i < 64; i++) {
|
for(int i = 0, len = _console->GetSettings()->IsFullColorPalette() ? 512 : 64; i < len; i++) {
|
||||||
uint8_t r = (palette[i] >> 16) & 0xFF;
|
uint8_t r = (palette[i] >> 16) & 0xFF;
|
||||||
uint8_t g = (palette[i] >> 8) & 0xFF;
|
uint8_t g = (palette[i] >> 8) & 0xFF;
|
||||||
uint8_t b = palette[i] & 0xFF;
|
uint8_t b = palette[i] & 0xFF;
|
||||||
|
|
||||||
if(_basePalette[i * 3] != r || _basePalette[i * 3 + 1] != g || _basePalette[i * 3 + 2] != b) {
|
if(_palette[i * 3] != r || _palette[i * 3 + 1] != g || _palette[i * 3 + 2] != b) {
|
||||||
paletteChanged = true;
|
paletteChanged = true;
|
||||||
|
|
||||||
_basePalette[i * 3] = (palette[i] >> 16) & 0xFF;
|
_palette[i * 3] = (palette[i] >> 16) & 0xFF;
|
||||||
_basePalette[i * 3 + 1] = (palette[i] >> 8) & 0xFF;
|
_palette[i * 3 + 1] = (palette[i] >> 8) & 0xFF;
|
||||||
_basePalette[i * 3 + 2] = palette[i] & 0xFF;
|
_palette[i * 3 + 2] = palette[i] & 0xFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,6 @@ void NtscFilter::OnBeforeApplyFilter()
|
||||||
if(paletteChanged || _ntscSetup.hue != pictureSettings.Hue || _ntscSetup.saturation != pictureSettings.Saturation || _ntscSetup.brightness != pictureSettings.Brightness || _ntscSetup.contrast != pictureSettings.Contrast ||
|
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.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.merge_fields == 1) != ntscSettings.MergeFields || _ntscSetup.resolution != ntscSettings.Resolution || _ntscSetup.sharpness != ntscSettings.Sharpness) {
|
||||||
|
|
||||||
_ntscSetup.hue = pictureSettings.Hue;
|
_ntscSetup.hue = pictureSettings.Hue;
|
||||||
_ntscSetup.saturation = pictureSettings.Saturation;
|
_ntscSetup.saturation = pictureSettings.Saturation;
|
||||||
_ntscSetup.brightness = pictureSettings.Brightness;
|
_ntscSetup.brightness = pictureSettings.Brightness;
|
||||||
|
@ -77,15 +76,21 @@ void NtscFilter::OnBeforeApplyFilter()
|
||||||
_ntscSetup.resolution = ntscSettings.Resolution;
|
_ntscSetup.resolution = ntscSettings.Resolution;
|
||||||
_ntscSetup.sharpness = ntscSettings.Sharpness;
|
_ntscSetup.sharpness = ntscSettings.Sharpness;
|
||||||
|
|
||||||
_ntscSetup.base_palette = _console->GetSettings()->IsDefaultPalette() ? nullptr : _basePalette;
|
if(_console->GetSettings()->IsFullColorPalette()) {
|
||||||
|
_ntscSetup.base_palette = nullptr;
|
||||||
|
_ntscSetup.palette = _palette;
|
||||||
|
} else {
|
||||||
|
_ntscSetup.base_palette = _palette;
|
||||||
|
_ntscSetup.palette = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
nes_ntsc_init(_ntscData, &_ntscSetup);
|
nes_ntsc_init(&_ntscData, &_ntscSetup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
|
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);
|
nes_ntsc_blit(&_ntscData, ppuOutputBuffer, PPU::ScreenWidth, IsOddFrame() ? 0 : 1, PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4);
|
||||||
GenerateArgbFrame(_ntscBuffer);
|
GenerateArgbFrame(_ntscBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +157,5 @@ void NtscFilter::GenerateArgbFrame(uint32_t *ntscBuffer)
|
||||||
|
|
||||||
NtscFilter::~NtscFilter()
|
NtscFilter::~NtscFilter()
|
||||||
{
|
{
|
||||||
delete _ntscData;
|
|
||||||
delete[] _ntscBuffer;
|
delete[] _ntscBuffer;
|
||||||
}
|
}
|
|
@ -9,9 +9,9 @@ class NtscFilter : public BaseVideoFilter
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
nes_ntsc_setup_t _ntscSetup;
|
nes_ntsc_setup_t _ntscSetup;
|
||||||
nes_ntsc_t* _ntscData;
|
nes_ntsc_t _ntscData;
|
||||||
bool _keepVerticalRes = false;
|
bool _keepVerticalRes = false;
|
||||||
uint8_t _basePalette[64 * 3];
|
uint8_t _palette[512 * 3];
|
||||||
uint32_t* _ntscBuffer;
|
uint32_t* _ntscBuffer;
|
||||||
|
|
||||||
void GenerateArgbFrame(uint32_t *outputBuffer);
|
void GenerateArgbFrame(uint32_t *outputBuffer);
|
||||||
|
|
|
@ -110,17 +110,26 @@ namespace Mesen.GUI.Config
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(videoInfo.PaletteData)) {
|
if(!string.IsNullOrWhiteSpace(videoInfo.PaletteData)) {
|
||||||
try {
|
try {
|
||||||
byte[] palette = System.Convert.FromBase64String(videoInfo.PaletteData);
|
byte[] palette = Convert.FromBase64String(videoInfo.PaletteData);
|
||||||
if(palette.Length == 64*4) {
|
if(palette.Length == 64*4 || palette.Length == 512*4) {
|
||||||
InteropEmu.SetRgbPalette(palette);
|
InteropEmu.SetRgbPalette(palette, (UInt32)(palette.Length / 4));
|
||||||
}
|
}
|
||||||
} catch { }
|
} catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsFullColorPalette()
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(this.PaletteData)) {
|
||||||
|
byte[] palette = Convert.FromBase64String(this.PaletteData);
|
||||||
|
return palette.Length == 512 * 4;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void AddPalette(string paletteName, byte[] paletteData)
|
public void AddPalette(string paletteName, byte[] paletteData)
|
||||||
{
|
{
|
||||||
string base64Data = System.Convert.ToBase64String(paletteData);
|
string base64Data = Convert.ToBase64String(paletteData);
|
||||||
foreach(PaletteInfo existingPalette in this.SavedPalettes) {
|
foreach(PaletteInfo existingPalette in this.SavedPalettes) {
|
||||||
if(existingPalette.Name == paletteName) {
|
if(existingPalette.Name == paletteName) {
|
||||||
//Update existing palette
|
//Update existing palette
|
||||||
|
@ -144,7 +153,7 @@ namespace Mesen.GUI.Config
|
||||||
{
|
{
|
||||||
foreach(PaletteInfo existingPalette in this.SavedPalettes) {
|
foreach(PaletteInfo existingPalette in this.SavedPalettes) {
|
||||||
if(existingPalette.Name == paletteName) {
|
if(existingPalette.Name == paletteName) {
|
||||||
byte[] paletteData = System.Convert.FromBase64String(existingPalette.Palette);
|
byte[] paletteData = Convert.FromBase64String(existingPalette.Palette);
|
||||||
|
|
||||||
int[] result = new int[paletteData.Length / sizeof(int)];
|
int[] result = new int[paletteData.Length / sizeof(int)];
|
||||||
Buffer.BlockCopy(paletteData, 0, result, 0, paletteData.Length);
|
Buffer.BlockCopy(paletteData, 0, result, 0, paletteData.Length);
|
||||||
|
|
|
@ -79,6 +79,10 @@ namespace Mesen.GUI.Forms.Config
|
||||||
AddBinding("ShowColorIndexes", chkShowColorIndexes);
|
AddBinding("ShowColorIndexes", chkShowColorIndexes);
|
||||||
|
|
||||||
_paletteData = InteropEmu.GetRgbPalette();
|
_paletteData = InteropEmu.GetRgbPalette();
|
||||||
|
if(!ConfigManager.Config.VideoInfo.IsFullColorPalette()) {
|
||||||
|
Array.Resize(ref _paletteData, 64);
|
||||||
|
}
|
||||||
|
|
||||||
RefreshPalette();
|
RefreshPalette();
|
||||||
|
|
||||||
toolTip.SetToolTip(picHdNesTooltip, ResourceHelper.GetMessage("HDNesTooltip"));
|
toolTip.SetToolTip(picHdNesTooltip, ResourceHelper.GetMessage("HDNesTooltip"));
|
||||||
|
@ -138,7 +142,7 @@ namespace Mesen.GUI.Forms.Config
|
||||||
{
|
{
|
||||||
byte[] result = new byte[_paletteData.Length * sizeof(int)];
|
byte[] result = new byte[_paletteData.Length * sizeof(int)];
|
||||||
Buffer.BlockCopy(_paletteData, 0, result, 0, result.Length);
|
Buffer.BlockCopy(_paletteData, 0, result, 0, result.Length);
|
||||||
((VideoInfo)Entity).PaletteData = System.Convert.ToBase64String(result);
|
((VideoInfo)Entity).PaletteData = Convert.ToBase64String(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ValidateInput()
|
protected override bool ValidateInput()
|
||||||
|
@ -215,13 +219,14 @@ namespace Mesen.GUI.Forms.Config
|
||||||
{
|
{
|
||||||
OpenFileDialog ofd = new OpenFileDialog();
|
OpenFileDialog ofd = new OpenFileDialog();
|
||||||
ofd.SetFilter("Palette Files (*.pal)|*.pal|All Files (*.*)|*.*");
|
ofd.SetFilter("Palette Files (*.pal)|*.pal|All Files (*.*)|*.*");
|
||||||
if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
if(ofd.ShowDialog() == DialogResult.OK) {
|
||||||
using(FileStream paletteFile = File.OpenRead(ofd.FileName)) {
|
using(FileStream paletteFile = File.OpenRead(ofd.FileName)) {
|
||||||
byte[] paletteFileData = new byte[64*3];
|
byte[] paletteFileData = new byte[512*3];
|
||||||
if(paletteFile.Read(paletteFileData, 0, 64*3) == 64*3) {
|
int byteCount = paletteFile.Read(paletteFileData, 0, 512 * 3);
|
||||||
for(int i = 0; i < 64; i++) {
|
if(byteCount == 64*3 || byteCount == 512*3) {
|
||||||
int fileOffset = i * 3;
|
_paletteData = new Int32[byteCount / 3];
|
||||||
_paletteData[i] = (Int32)((UInt32)0xFF000000 | (UInt32)paletteFileData[fileOffset+2] | (UInt32)(paletteFileData[fileOffset+1] << 8) | (UInt32)(paletteFileData[fileOffset] << 16));
|
for(int i = 0; i < byteCount; i += 3) {
|
||||||
|
_paletteData[i / 3] = (Int32)((UInt32)0xFF000000 | (UInt32)paletteFileData[i + 2] | (UInt32)(paletteFileData[i + 1] << 8) | (UInt32)(paletteFileData[i] << 16));
|
||||||
}
|
}
|
||||||
RefreshPalette();
|
RefreshPalette();
|
||||||
|
|
||||||
|
@ -366,7 +371,8 @@ namespace Mesen.GUI.Forms.Config
|
||||||
|
|
||||||
private void UpdatePalette(UInt32[] newPalette)
|
private void UpdatePalette(UInt32[] newPalette)
|
||||||
{
|
{
|
||||||
for(int i = 0; i < 64; i++) {
|
_paletteData = new Int32[newPalette.Length];
|
||||||
|
for(int i = 0; i < newPalette.Length; i++) {
|
||||||
_paletteData[i] = (Int32)newPalette[i];
|
_paletteData[i] = (Int32)newPalette[i];
|
||||||
}
|
}
|
||||||
RefreshPalette();
|
RefreshPalette();
|
||||||
|
@ -439,7 +445,7 @@ namespace Mesen.GUI.Forms.Config
|
||||||
{
|
{
|
||||||
SaveFileDialog sfd = new SaveFileDialog();
|
SaveFileDialog sfd = new SaveFileDialog();
|
||||||
sfd.SetFilter("Palette Files (*.pal)|*.pal");
|
sfd.SetFilter("Palette Files (*.pal)|*.pal");
|
||||||
if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
|
if(sfd.ShowDialog() == DialogResult.OK) {
|
||||||
List<byte>bytePalette = new List<byte>();
|
List<byte>bytePalette = new List<byte>();
|
||||||
foreach(int value in _paletteData) {
|
foreach(int value in _paletteData) {
|
||||||
bytePalette.Add((byte)(value >> 16 & 0xFF));
|
bytePalette.Add((byte)(value >> 16 & 0xFF));
|
||||||
|
|
|
@ -208,7 +208,7 @@ namespace Mesen.GUI
|
||||||
[DllImport(DLLPath)] public static extern void SetVideoAspectRatio(VideoAspectRatio aspectRatio, double customRatio);
|
[DllImport(DLLPath)] public static extern void SetVideoAspectRatio(VideoAspectRatio aspectRatio, double customRatio);
|
||||||
[DllImport(DLLPath)] public static extern void SetVideoFilter(VideoFilterType filter);
|
[DllImport(DLLPath)] public static extern void SetVideoFilter(VideoFilterType filter);
|
||||||
[DllImport(DLLPath)] public static extern void SetVideoResizeFilter(VideoResizeFilter filter);
|
[DllImport(DLLPath)] public static extern void SetVideoResizeFilter(VideoResizeFilter filter);
|
||||||
[DllImport(DLLPath)] public static extern void SetRgbPalette(byte[] palette);
|
[DllImport(DLLPath)] public static extern void SetRgbPalette(byte[] palette, UInt32 paletteSize);
|
||||||
[DllImport(DLLPath)] public static extern void SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity);
|
[DllImport(DLLPath)] public static extern void SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity);
|
||||||
[DllImport(DLLPath)] public static extern void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, [MarshalAs(UnmanagedType.I1)]bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, [MarshalAs(UnmanagedType.I1)]bool verticalBlend);
|
[DllImport(DLLPath)] public static extern void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, [MarshalAs(UnmanagedType.I1)]bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, [MarshalAs(UnmanagedType.I1)]bool verticalBlend);
|
||||||
[DllImport(DLLPath)] public static extern void SetInputDisplaySettings(byte visiblePorts, InputDisplayPosition displayPosition, [MarshalAs(UnmanagedType.I1)]bool displayHorizontally);
|
[DllImport(DLLPath)] public static extern void SetInputDisplaySettings(byte visiblePorts, InputDisplayPosition displayPosition, [MarshalAs(UnmanagedType.I1)]bool displayHorizontally);
|
||||||
|
@ -816,7 +816,7 @@ namespace Mesen.GUI
|
||||||
|
|
||||||
public static Int32[] GetRgbPalette()
|
public static Int32[] GetRgbPalette()
|
||||||
{
|
{
|
||||||
Int32[] paleteData = new Int32[64];
|
Int32[] paleteData = new Int32[512];
|
||||||
|
|
||||||
GCHandle hPaletteData = GCHandle.Alloc(paleteData, GCHandleType.Pinned);
|
GCHandle hPaletteData = GCHandle.Alloc(paleteData, GCHandleType.Pinned);
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -648,8 +648,8 @@ namespace InteropEmu {
|
||||||
DllExport void __stdcall SetVideoAspectRatio(VideoAspectRatio aspectRatio, double customRatio) { _settings->SetVideoAspectRatio(aspectRatio, customRatio); }
|
DllExport void __stdcall SetVideoAspectRatio(VideoAspectRatio aspectRatio, double customRatio) { _settings->SetVideoAspectRatio(aspectRatio, customRatio); }
|
||||||
DllExport void __stdcall SetVideoFilter(VideoFilterType filter) { _settings->SetVideoFilterType(filter); }
|
DllExport void __stdcall SetVideoFilter(VideoFilterType filter) { _settings->SetVideoFilterType(filter); }
|
||||||
DllExport void __stdcall SetVideoResizeFilter(VideoResizeFilter filter) { _settings->SetVideoResizeFilter(filter); }
|
DllExport void __stdcall SetVideoResizeFilter(VideoResizeFilter filter) { _settings->SetVideoResizeFilter(filter); }
|
||||||
DllExport void __stdcall GetRgbPalette(uint32_t *paletteBuffer) { _settings->GetRgbPalette(paletteBuffer); }
|
DllExport void __stdcall GetRgbPalette(uint32_t *paletteBuffer) { _settings->GetUserRgbPalette(paletteBuffer); }
|
||||||
DllExport void __stdcall SetRgbPalette(uint32_t *paletteBuffer) { _settings->SetRgbPalette(paletteBuffer); }
|
DllExport void __stdcall SetRgbPalette(uint32_t *paletteBuffer, uint32_t paletteSize) { _settings->SetUserRgbPalette(paletteBuffer, paletteSize); }
|
||||||
DllExport void __stdcall SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity) { _settings->SetPictureSettings(brightness, contrast, saturation, hue, scanlineIntensity); }
|
DllExport void __stdcall SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity) { _settings->SetPictureSettings(brightness, contrast, saturation, hue, scanlineIntensity); }
|
||||||
DllExport void __stdcall SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, bool verticalBlend) { _settings->SetNtscFilterSettings(artifacts, bleed, fringing, gamma, resolution, sharpness, mergeFields, yFilterLength, iFilterLength, qFilterLength, verticalBlend, false); }
|
DllExport void __stdcall SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, bool verticalBlend) { _settings->SetNtscFilterSettings(artifacts, bleed, fringing, gamma, resolution, sharpness, mergeFields, yFilterLength, iFilterLength, qFilterLength, verticalBlend, false); }
|
||||||
DllExport void __stdcall SetPauseScreenMessage(char* message) { _settings->SetPauseScreenMessage(message); }
|
DllExport void __stdcall SetPauseScreenMessage(char* message) { _settings->SetPauseScreenMessage(message); }
|
||||||
|
|
|
@ -287,23 +287,23 @@ extern "C" {
|
||||||
void load_custom_palette()
|
void load_custom_palette()
|
||||||
{
|
{
|
||||||
//Setup default palette in case we can't load the custom one
|
//Setup default palette in case we can't load the custom one
|
||||||
_console->GetSettings()->SetRgbPalette(defaultPalette);
|
_console->GetSettings()->SetUserRgbPalette(defaultPalette);
|
||||||
|
|
||||||
//Try to load the custom palette from the MesenPalette.pal file
|
//Try to load the custom palette from the MesenPalette.pal file
|
||||||
string palettePath = FolderUtilities::CombinePath(FolderUtilities::GetHomeFolder(), "MesenPalette.pal");
|
string palettePath = FolderUtilities::CombinePath(FolderUtilities::GetHomeFolder(), "MesenPalette.pal");
|
||||||
uint8_t fileData[64 * 3] = {};
|
uint8_t fileData[512 * 3] = {};
|
||||||
ifstream palette(palettePath, ios::binary);
|
ifstream palette(palettePath, ios::binary);
|
||||||
if(palette) {
|
if(palette) {
|
||||||
palette.seekg(0, ios::end);
|
palette.seekg(0, ios::end);
|
||||||
std::streampos fileSize = palette.tellg();
|
std::streamoff fileSize = palette.tellg();
|
||||||
palette.seekg(0, ios::beg);
|
palette.seekg(0, ios::beg);
|
||||||
if(fileSize >= 64 * 3) {
|
if((fileSize == 64 * 3) || (fileSize == 512 * 3)) {
|
||||||
palette.read((char*)fileData, 64 * 3);
|
palette.read((char*)fileData, fileSize);
|
||||||
uint32_t customPalette[64];
|
uint32_t customPalette[512];
|
||||||
for(int i = 0; i < 64; i++) {
|
for(int i = 0; i < fileSize / 3; i++) {
|
||||||
customPalette[i] = 0xFF000000 | fileData[i * 3 + 2] | (fileData[i * 3 + 1] << 8) | (fileData[i * 3] << 16);
|
customPalette[i] = 0xFF000000 | fileData[i * 3 + 2] | (fileData[i * 3 + 1] << 8) | (fileData[i * 3] << 16);
|
||||||
}
|
}
|
||||||
_console->GetSettings()->SetRgbPalette(customPalette);
|
_console->GetSettings()->SetUserRgbPalette(customPalette, (uint32_t)fileSize / 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,25 +369,25 @@ extern "C" {
|
||||||
if(readVariable(MesenPalette, var)) {
|
if(readVariable(MesenPalette, var)) {
|
||||||
string value = string(var.value);
|
string value = string(var.value);
|
||||||
if(value == "Default") {
|
if(value == "Default") {
|
||||||
_console->GetSettings()->SetRgbPalette(defaultPalette);
|
_console->GetSettings()->SetUserRgbPalette(defaultPalette);
|
||||||
} else if(value == "Composite Direct (by FirebrandX)") {
|
} else if(value == "Composite Direct (by FirebrandX)") {
|
||||||
_console->GetSettings()->SetRgbPalette(compositeDirectPalette);
|
_console->GetSettings()->SetUserRgbPalette(compositeDirectPalette);
|
||||||
} else if(value == "Nes Classic") {
|
} else if(value == "Nes Classic") {
|
||||||
_console->GetSettings()->SetRgbPalette(nesClassicPalette);
|
_console->GetSettings()->SetUserRgbPalette(nesClassicPalette);
|
||||||
} else if(value == "Nestopia (RGB)") {
|
} else if(value == "Nestopia (RGB)") {
|
||||||
_console->GetSettings()->SetRgbPalette(nestopiaRgbPalette);
|
_console->GetSettings()->SetUserRgbPalette(nestopiaRgbPalette);
|
||||||
} else if(value == "Original Hardware (by FirebrandX)") {
|
} else if(value == "Original Hardware (by FirebrandX)") {
|
||||||
_console->GetSettings()->SetRgbPalette(originalHardwarePalette);
|
_console->GetSettings()->SetUserRgbPalette(originalHardwarePalette);
|
||||||
} else if(value == "PVM Style (by FirebrandX)") {
|
} else if(value == "PVM Style (by FirebrandX)") {
|
||||||
_console->GetSettings()->SetRgbPalette(pvmStylePalette);
|
_console->GetSettings()->SetUserRgbPalette(pvmStylePalette);
|
||||||
} else if(value == "Sony CXA2025AS") {
|
} else if(value == "Sony CXA2025AS") {
|
||||||
_console->GetSettings()->SetRgbPalette(sonyCxa2025AsPalette);
|
_console->GetSettings()->SetUserRgbPalette(sonyCxa2025AsPalette);
|
||||||
} else if(value == "Unsaturated v6 (by FirebrandX)") {
|
} else if(value == "Unsaturated v6 (by FirebrandX)") {
|
||||||
_console->GetSettings()->SetRgbPalette(unsaturatedPalette);
|
_console->GetSettings()->SetUserRgbPalette(unsaturatedPalette);
|
||||||
} else if(value == "YUV v3 (by FirebrandX)") {
|
} else if(value == "YUV v3 (by FirebrandX)") {
|
||||||
_console->GetSettings()->SetRgbPalette(yuvPalette);
|
_console->GetSettings()->SetUserRgbPalette(yuvPalette);
|
||||||
} else if(value == "Wavebeam (by nakedarthur)") {
|
} else if(value == "Wavebeam (by nakedarthur)") {
|
||||||
_console->GetSettings()->SetRgbPalette(wavebeamPalette);
|
_console->GetSettings()->SetUserRgbPalette(wavebeamPalette);
|
||||||
} else if(value == "Custom") {
|
} else if(value == "Custom") {
|
||||||
load_custom_palette();
|
load_custom_palette();
|
||||||
} else if(value == "Raw") {
|
} else if(value == "Raw") {
|
||||||
|
|
Loading…
Add table
Reference in a new issue