From ec52ea80492c20dcec1e330a9c745d9815bb7d8a Mon Sep 17 00:00:00 2001 From: thrust26 Date: Sat, 31 Oct 2020 19:06:58 +0100 Subject: [PATCH] added color parameters to 'Custom' palette, fixes #657 --- Changes.txt | 6 +- docs/graphics/options_video_palettes.png | Bin 7790 -> 9809 bytes docs/index.html | 39 ++++- src/common/PaletteHandler.cxx | 201 ++++++++++++++++------- src/common/PaletteHandler.hxx | 83 +++++++++- src/emucore/EventHandler.cxx | 16 +- src/emucore/EventHandler.hxx | 6 + src/emucore/PointingDevice.cxx | 1 + src/emucore/Settings.cxx | 35 ++-- src/gui/VideoAudioDialog.cxx | 136 ++++++++++++--- src/gui/VideoAudioDialog.hxx | 10 ++ src/gui/WhatsNewDialog.cxx | 3 +- 12 files changed, 438 insertions(+), 98 deletions(-) diff --git a/Changes.txt b/Changes.txt index 88ddd7afe..0a08e446b 100644 --- a/Changes.txt +++ b/Changes.txt @@ -16,14 +16,16 @@ * Added basic (entire and single line only) text cut/copy and paste. - * Fixed bug with aspect correction and fullscreen mode; snapshots from - such a mode are now pixel-exact. + * Added color parameters to 'Custom' palette * Some improvements to AVox-USB adaptor functionality: - Made serial port used for an AtariVox-USB adaptor editable. - Autodetection of serial ports no longer messes up devices plugged into other serial ports. + * Fixed bug with aspect correction and fullscreen mode; snapshots from + such a mode are now pixel-exact. + * Fixed crash with missing or incorrectly sized SaveKey data file, and with certain functions not working (erase pages, erase entire EEPROM). diff --git a/docs/graphics/options_video_palettes.png b/docs/graphics/options_video_palettes.png index cb8597bbe58e4cbcb40b2fc8c292896c55114284..d965b45b3bc7c49b6fa16bad9e008da54ab4cf11 100644 GIT binary patch literal 9809 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 M%H(AJiHo=YAJhh41poj5 literal 7790 zcmds6i9eL<+n;`&@;eG04nmex_N6g0S%y@SvK2LCi(!y0rok9XCs|9=Il>GXl^BE} zNm*OQ2!qO&J%*7POH5;5-iJEpeT~jP@cMk_7 zK_C#H$?22lL7;6k;D`S2c3|ZEE2Uq7f7?*!jgNy0oA-?Y7r(e0ni+yX#kgI|&O3m6 zcuP}jBM%Rce9b*yzqZtqLwj4YGH*KrGYt&%U0o43Z$>_P^sv1A<;Ra7n9MO44CLs@ zi^U2P2r|!}DK#}|G8pH`dX@pj37K!qZZ( zoYg-B>3>czn|froQtA;80uhA-ixuSG!1O+zvmA0oOrfu>L`PvjHVJ~h59Dta>*8OT z=e>8X?DMBFZ}b|0f*k~L0rHPybzi2LHxxSe*7%RVzp=y*ic%CJoKPrmfYn?(_x_ga zz(e$8Y4j2Z6fY>0eo&zZ3VUsG?!zV5&sWfs7+_pDOc0!S(3AvquQ0hzyOh#*rEmiC zW(g#3BWQH(pi2TYoMe(xeW|GJO5GPs-xBDEoFM%8!9WLSQlLrkolCT=DDq)qGpfkGVuJ1sgc3Z>81KSFK zNc?*Bv@Hq*;(xpT+g9h7;|c=pb22$;XdUc0(YqtFM=$l*;KJo(!asjOvwma_^R)i+ z`}T*-)V?qY9m#G3HqD;h;XXkFkc8m^πvF(mB|8h$$g%%g$eca>mIlr={CFsA<~ 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 N6C?AJg~u=7_#Y
-palette <standard|z26|user|custom>
Set the palette to either normal Stella, the one used in the z26 emulator, a user-defined palette, or a custom palette generated - from user-defined phase shifts. + from user-defined parameters. @@ -2143,6 +2143,36 @@ Adjust phase shift of 'custom' PAL palette. + +
-pal.red_scale <number>
+ Adjust red scale of 'custom' palette (range -1.0 to 1.0). + + + +
-pal.red_shift <number>
+ Adjust red shift of 'custom' palette (range -22.5 to 22.5). + + + +
-pal.green_scale <number>
+ Adjust green scale of 'custom' palette (range -1.0 to 1.0). + + + +
-pal.green_shift <number>
+ Adjust green shift of 'custom' palette (range -22.5 to 22.5). + + + +
-pal.blue_scale <number>
+ Adjust blue scale of 'custom' palette (range -1.0 to 1.0). + + + +
-pal.blue_shift <number>
+ Adjust blue shift of 'custom' palette (range -22.5 to 22.5). + +
-pal.hue <number>
Adjust hue of current palette (range -1.0 to 1.0). @@ -3078,8 +3108,11 @@ - - + + + + + diff --git a/src/common/PaletteHandler.cxx b/src/common/PaletteHandler.cxx index 030ff4d9e..db6961af8 100644 --- a/src/common/PaletteHandler.cxx +++ b/src/common/PaletteHandler.cxx @@ -74,14 +74,38 @@ void PaletteHandler::cyclePalette(int direction) setPalette(palette); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool PaletteHandler::isCustomAdjustable() const +{ + return myCurrentAdjustable >= CUSTOM_START + && myCurrentAdjustable <= CUSTOM_END; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool PaletteHandler::isPhaseShift() const +{ + return myCurrentAdjustable == PHASE_SHIFT; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool PaletteHandler::isRGBScale() const +{ + return myCurrentAdjustable >= RED_SCALE && myCurrentAdjustable <= BLUE_SCALE; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool PaletteHandler::isRGBShift() const +{ + return myCurrentAdjustable >= RED_SHIFT && myCurrentAdjustable <= BLUE_SHIFT; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PaletteHandler::showAdjustableMessage() { - const bool isPhaseShift = myAdjustables[myCurrentAdjustable].value == nullptr; ostringstream msg, buf; msg << "Palette " << myAdjustables[myCurrentAdjustable].name; - if(isPhaseShift) + if(isPhaseShift()) { const ConsoleTiming timing = myOSystem.console().timing(); const bool isNTSC = timing == ConsoleTiming::ntsc; @@ -90,12 +114,22 @@ void PaletteHandler::showAdjustableMessage() buf << std::fixed << std::setprecision(1) << value << DEGREE; myOSystem.frameBuffer().showMessage( "Palette phase shift", buf.str(), value, - (isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) - MAX_SHIFT, - (isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) + MAX_SHIFT); + (isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) - MAX_PHASE_SHIFT, + (isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT) + MAX_PHASE_SHIFT); + } + else if(isRGBShift()) + { + const float value = *myAdjustables[myCurrentAdjustable].value; + + buf << std::fixed << std::setprecision(1) << value << DEGREE; + myOSystem.frameBuffer().showMessage( + msg.str(), buf.str(), value, -MAX_RGB_SHIFT, +MAX_RGB_SHIFT); } else { - const int value = scaleTo100(*myAdjustables[myCurrentAdjustable].value); + const int value = isRGBScale() + ? scaleRGBTo100(*myAdjustables[myCurrentAdjustable].value) + : scaleTo100(*myAdjustables[myCurrentAdjustable].value); buf << value << "%"; myOSystem.frameBuffer().showMessage( msg.str(), buf.str(), value); @@ -106,15 +140,15 @@ void PaletteHandler::showAdjustableMessage() void PaletteHandler::cycleAdjustable(int direction) { const bool isCustomPalette = SETTING_CUSTOM == myOSystem.settings().getString("palette"); - bool isPhaseShift; + bool isCustomAdj; do { myCurrentAdjustable = BSPF::clampw(int(myCurrentAdjustable + direction), 0, NUM_ADJUSTABLES - 1); - isPhaseShift = myAdjustables[myCurrentAdjustable].value == nullptr; + isCustomAdj = isCustomAdjustable(); // skip phase shift when 'Custom' palette is not selected - if(!direction && isPhaseShift && !isCustomPalette) + if(!direction && isCustomAdj && !isCustomPalette) myCurrentAdjustable++; - } while(isPhaseShift && !isCustomPalette); + } while(isCustomAdj && !isCustomPalette); showAdjustableMessage(); } @@ -122,29 +156,38 @@ void PaletteHandler::cycleAdjustable(int direction) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PaletteHandler::changeAdjustable(int adjustable, int direction) { - const bool isCustomPalette = SETTING_CUSTOM == myOSystem.settings().getString("palette"); - const bool isPhaseShift = myAdjustables[adjustable].value == nullptr; - myCurrentAdjustable = adjustable; - if(isPhaseShift && !isCustomPalette) - myCurrentAdjustable++; - changeCurrentAdjustable(direction); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PaletteHandler::changeCurrentAdjustable(int direction) { - if(myAdjustables[myCurrentAdjustable].value == nullptr) + if(isPhaseShift()) changeColorPhaseShift(direction); else { - int newVal = scaleTo100(*myAdjustables[myCurrentAdjustable].value); + if(isRGBScale()) + { + int newVal = scaleRGBTo100(*myAdjustables[myCurrentAdjustable].value); - newVal = BSPF::clamp(newVal + direction * 1, 0, 100); + newVal = BSPF::clamp(newVal + direction * 1, 0, 100); + *myAdjustables[myCurrentAdjustable].value = scaleRGBFrom100(newVal); + } + else if(isRGBShift()) + { + float newShift = *myAdjustables[myCurrentAdjustable].value; - *myAdjustables[myCurrentAdjustable].value = scaleFrom100(newVal); + newShift = BSPF::clamp(newShift + direction * 0.5F, -MAX_RGB_SHIFT, MAX_RGB_SHIFT); + *myAdjustables[myCurrentAdjustable].value = newShift; + } + else + { + int newVal = scaleTo100(*myAdjustables[myCurrentAdjustable].value); + newVal = BSPF::clamp(newVal + direction * 1, 0, 100); + *myAdjustables[myCurrentAdjustable].value = scaleFrom100(newVal); + } showAdjustableMessage(); setPalette(); } @@ -162,7 +205,7 @@ void PaletteHandler::changeColorPhaseShift(int direction) const float shift = isNTSC ? DEF_NTSC_SHIFT : DEF_PAL_SHIFT; float newPhase = isNTSC ? myPhaseNTSC : myPhasePAL; - newPhase = BSPF::clamp(newPhase + direction * 0.3F, shift - MAX_SHIFT, shift + MAX_SHIFT); + newPhase = BSPF::clamp(newPhase + direction * 0.3F, shift - MAX_PHASE_SHIFT, shift + MAX_PHASE_SHIFT); if(isNTSC) myPhaseNTSC = newPhase; @@ -181,15 +224,21 @@ void PaletteHandler::loadConfig(const Settings& settings) { // Load adjustables myPhaseNTSC = BSPF::clamp(settings.getFloat("pal.phase_ntsc"), - DEF_NTSC_SHIFT - MAX_SHIFT, DEF_NTSC_SHIFT + MAX_SHIFT); + DEF_NTSC_SHIFT - MAX_PHASE_SHIFT, DEF_NTSC_SHIFT + MAX_PHASE_SHIFT); myPhasePAL = BSPF::clamp(settings.getFloat("pal.phase_pal"), - DEF_PAL_SHIFT - MAX_SHIFT, DEF_PAL_SHIFT + MAX_SHIFT); + DEF_PAL_SHIFT - MAX_PHASE_SHIFT, DEF_PAL_SHIFT + MAX_PHASE_SHIFT); + myRedScale = BSPF::clamp(settings.getFloat("pal.red_scale"), -1.0F, 1.0F) + 1.F; + myGreenScale = BSPF::clamp(settings.getFloat("pal.green_scale"), -1.0F, 1.0F) + 1.F; + myBlueScale = BSPF::clamp(settings.getFloat("pal.blue_scale"), -1.0F, 1.0F) + 1.F; + myRedShift = BSPF::clamp(settings.getFloat("pal.red_shift"), -MAX_RGB_SHIFT, MAX_RGB_SHIFT); + myGreenShift = BSPF::clamp(settings.getFloat("pal.green_shift"), -MAX_RGB_SHIFT, MAX_RGB_SHIFT); + myBlueShift = BSPF::clamp(settings.getFloat("pal.blue_shift"), -MAX_RGB_SHIFT, MAX_RGB_SHIFT); - myHue = BSPF::clamp(settings.getFloat("pal.hue"), -1.0F, 1.0F); - mySaturation = BSPF::clamp(settings.getFloat("pal.saturation"), -1.0F, 1.0F); - myContrast = BSPF::clamp(settings.getFloat("pal.contrast"), -1.0F, 1.0F); - myBrightness = BSPF::clamp(settings.getFloat("pal.brightness"), -1.0F, 1.0F); - myGamma = BSPF::clamp(settings.getFloat("pal.gamma"), -1.0F, 1.0F); + myHue = BSPF::clamp(settings.getFloat("pal.hue"), -1.0F, 1.0F); + mySaturation = BSPF::clamp(settings.getFloat("pal.saturation"), -1.0F, 1.0F); + myContrast = BSPF::clamp(settings.getFloat("pal.contrast"), -1.0F, 1.0F); + myBrightness = BSPF::clamp(settings.getFloat("pal.brightness"), -1.0F, 1.0F); + myGamma = BSPF::clamp(settings.getFloat("pal.gamma"), -1.0F, 1.0F); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -198,6 +247,12 @@ void PaletteHandler::saveConfig(Settings& settings) const // Save adjustables settings.setValue("pal.phase_ntsc", myPhaseNTSC); settings.setValue("pal.phase_pal", myPhasePAL); + settings.setValue("pal.red_scale", myRedScale - 1.F); + settings.setValue("pal.green_scale", myGreenScale - 1.F); + settings.setValue("pal.blue_scale", myBlueScale - 1.F); + settings.setValue("pal.red_shift", myRedShift); + settings.setValue("pal.green_shift", myGreenShift); + settings.setValue("pal.blue_shift", myBlueShift); settings.setValue("pal.hue", myHue); settings.setValue("pal.saturation", mySaturation); @@ -209,8 +264,14 @@ void PaletteHandler::saveConfig(Settings& settings) const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PaletteHandler::setAdjustables(const Adjustable& adjustable) { - myPhaseNTSC = adjustable.phaseNtsc / 10.F; - myPhasePAL = adjustable.phasePal / 10.F; + myPhaseNTSC = scaleFromAngles(adjustable.phaseNtsc); + myPhasePAL = scaleFromAngles(adjustable.phasePal); + myRedScale = scaleRGBFrom100(adjustable.redScale); + myGreenScale = scaleRGBFrom100(adjustable.greenScale); + myBlueScale = scaleRGBFrom100(adjustable.blueScale); + myRedShift = scaleFromAngles(adjustable.redShift); + myGreenShift = scaleFromAngles(adjustable.greenShift); + myBlueShift = scaleFromAngles(adjustable.blueShift); myHue = scaleFrom100(adjustable.hue); mySaturation = scaleFrom100(adjustable.saturation); @@ -222,8 +283,14 @@ void PaletteHandler::setAdjustables(const Adjustable& adjustable) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PaletteHandler::getAdjustables(Adjustable& adjustable) const { - adjustable.phaseNtsc = myPhaseNTSC * 10.F; - adjustable.phasePal = myPhasePAL * 10.F; + adjustable.phaseNtsc = scaleToAngles(myPhaseNTSC); + adjustable.phasePal = scaleToAngles(myPhasePAL); + adjustable.redScale = scaleRGBTo100(myRedScale); + adjustable.greenScale = scaleRGBTo100(myGreenScale); + adjustable.blueScale = scaleRGBTo100(myBlueScale); + adjustable.redShift = scaleToAngles(myRedShift); + adjustable.greenShift = scaleToAngles(myGreenShift); + adjustable.blueShift = scaleToAngles(myBlueShift); adjustable.hue = scaleTo100(myHue); adjustable.saturation = scaleTo100(mySaturation); @@ -371,10 +438,9 @@ void PaletteHandler::generateCustomPalette(ConsoleTiming timing) constexpr int NUM_LUMA = 8; constexpr float SATURATION = 0.25F; // default saturation - float color[NUM_CHROMA][2] = {{0.0F}}; - if(timing == ConsoleTiming::ntsc) { + vector2d IQ[NUM_CHROMA]; // YIQ is YUV shifted by 33° constexpr float offset = 33 * BSPF::PI_f / 180; const float shift = myPhaseNTSC * BSPF::PI_f / 180; @@ -382,22 +448,23 @@ void PaletteHandler::generateCustomPalette(ConsoleTiming timing) // color 0 is grayscale for(int chroma = 1; chroma < NUM_CHROMA; chroma++) { - color[chroma][0] = SATURATION * sinf(offset + shift * (chroma - 1)); - color[chroma][1] = SATURATION * cosf(offset + shift * (chroma - 1) - BSPF::PI_f); + IQ[chroma] = vector2d(SATURATION * sinf(offset + shift * (chroma - 1)), + SATURATION * cosf(offset + shift * (chroma - 1) - BSPF::PI_f)); } + const vector2d IQR = scale(rotate(vector2d(+0.956F, +0.621F), myRedShift), myRedScale); + const vector2d IQG = scale(rotate(vector2d(-0.272F, -0.647F), myGreenShift), myGreenScale); + const vector2d IQB = scale(rotate(vector2d(-1.106F, +1.703F), myBlueShift), myBlueScale); for(int chroma = 0; chroma < NUM_CHROMA; chroma++) { - const float I = color[chroma][0]; - const float Q = color[chroma][1]; - for(int luma = 0; luma < NUM_LUMA; luma++) { const float Y = 0.05F + luma / 8.24F; // 0.05..~0.90 - float R = Y + 0.956F * I + 0.621F * Q; - float G = Y - 0.272F * I - 0.647F * Q; - float B = Y - 1.106F * I + 1.703F * Q; + float R = Y + dotProduct(IQ[chroma], IQR); + float G = Y + dotProduct(IQ[chroma], IQG); + float B = Y + dotProduct(IQ[chroma], IQB); + if(R < 0) R = 0; if(G < 0) G = 0; @@ -420,35 +487,37 @@ void PaletteHandler::generateCustomPalette(ConsoleTiming timing) constexpr float offset = BSPF::PI_f; const float shift = myPhasePAL * BSPF::PI_f / 180; constexpr float fixedShift = 22.5F * BSPF::PI_f / 180; + vector2d UV[NUM_CHROMA]; // colors 0, 1, 14 and 15 are grayscale for(int chroma = 2; chroma < NUM_CHROMA - 2; chroma++) { int idx = NUM_CHROMA - 1 - chroma; - color[idx][0] = SATURATION * sinf(offset - fixedShift * chroma); + + UV[idx].x = SATURATION * sinf(offset - fixedShift * chroma); if ((idx & 1) == 0) - color[idx][1] = SATURATION * sinf(offset - shift * (chroma - 3.5F) / 2.F); + UV[idx].y = SATURATION * sinf(offset - shift * (chroma - 3.5F) / 2.F); else - color[idx][1] = SATURATION * -sinf(offset - shift * chroma / 2.F); + UV[idx].y = SATURATION * -sinf(offset - shift * chroma / 2.F); } + // Most sources + const vector2d UVR = scale(rotate(vector2d( 0.000F, +1.403F), myRedShift), myRedScale); + const vector2d UVG = scale(rotate(vector2d(-0.344F, -0.714F), myGreenShift), myGreenScale); + const vector2d UVB = scale(rotate(vector2d(+0.714F, 0.000F), myBlueShift), myBlueScale); + // German Wikipedia, huh??? + //float R = Y + 1 / 0.877 * V; + //float B = Y + 1 / 0.493 * U; + //float G = 1.704 * Y - 0.590 * R - 0.194 * B; for(int chroma = 0; chroma < NUM_CHROMA; chroma++) { - const float U = color[chroma][0]; - const float V = color[chroma][1]; - for(int luma = 0; luma < NUM_LUMA; luma++) { const float Y = 0.05F + luma / 8.24F; // 0.05..~0.90 - // Most sources - float R = Y + 1.403F * V; - float G = Y - 0.344F * U - 0.714F * V; - float B = Y + 1.770F * U; - // German Wikipedia, huh??? - //float B = Y + 1 / 0.493 * U; - //float R = Y + 1 / 0.877 * V; - //float G = 1.704 * Y - 0.590 * R - 0.194 * B; + float R = Y + dotProduct(UV[chroma], UVR); + float G = Y + dotProduct(UV[chroma], UVG); + float B = Y + dotProduct(UV[chroma], UVB); if(R < 0) R = 0.0; if(G < 0) G = 0.0; @@ -491,6 +560,28 @@ void PaletteHandler::adjustHueSaturation(int& R, int& G, int& B, float H, float B = BSPF::clamp(b, 0.F, 255.F); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PaletteHandler::vector2d PaletteHandler::rotate(const PaletteHandler::vector2d& vec, float angle) const +{ + const float r = angle * BSPF::PI_f / 180; + + return PaletteHandler::vector2d(vec.x * cosf(r) - vec.y * sinf(r), + vec.x * sinf(r) + vec.y * cosf(r)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PaletteHandler::vector2d PaletteHandler::scale(const PaletteHandler::vector2d& vec, float factor) const +{ + return PaletteHandler::vector2d(vec.x * factor, vec.y * factor); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +float PaletteHandler::dotProduct(const PaletteHandler::vector2d& vec1, + const PaletteHandler::vector2d& vec2) const +{ + return vec1.x * vec2.x + vec1.y * vec2.y; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const PaletteArray PaletteHandler::ourNTSCPalette = { 0x000000, 0, 0x4a4a4a, 0, 0x6f6f6f, 0, 0x8e8e8e, 0, diff --git a/src/common/PaletteHandler.hxx b/src/common/PaletteHandler.hxx index 956fcf9b3..66da36715 100644 --- a/src/common/PaletteHandler.hxx +++ b/src/common/PaletteHandler.hxx @@ -35,20 +35,32 @@ class PaletteHandler // Phase shift default and limits static constexpr float DEF_NTSC_SHIFT = 26.2F; static constexpr float DEF_PAL_SHIFT = 31.3F; // ~= 360 / 11.5 - static constexpr float MAX_SHIFT = 4.5F; + static constexpr float MAX_PHASE_SHIFT = 4.5F; + static constexpr float DEF_RGB_SHIFT = 0.0F; + static constexpr float MAX_RGB_SHIFT = 22.5F; enum Adjustables { PHASE_SHIFT, + RED_SCALE, + GREEN_SCALE, + BLUE_SCALE, + RED_SHIFT, + GREEN_SHIFT, + BLUE_SHIFT, HUE, SATURATION, CONTRAST, BRIGHTNESS, - GAMMA + GAMMA, + CUSTOM_START = PHASE_SHIFT, + CUSTOM_END = BLUE_SHIFT, }; // Externally used adjustment parameters struct Adjustable { - float phaseNtsc{0.F}, phasePal{0.F}; + float phaseNtsc{0.F}, phasePal{0.F}, + redScale{0.F}, greenScale{0.F}, blueScale{0.F}, + redShift{0.F}, greenShift{0.F}, blueShift{0.F}; uInt32 hue{0}, saturation{0}, contrast{0}, brightness{0}, gamma{0}; }; @@ -108,6 +120,7 @@ class PaletteHandler */ void setPalette(); + private: static constexpr char DEGREE = 0x1c; @@ -121,12 +134,45 @@ class PaletteHandler MaxType = Custom }; + struct vector2d { + float x; + float y; + + explicit vector2d() + : x(0.F), y(0.F) { } + explicit vector2d(float _x, float _y) + : x(_x), y(_y) { } + }; + + /** + Convert RGB adjustables from/to 100% scale + */ + static constexpr float scaleRGBFrom100(float x) { return x / 50.F; } + static constexpr uInt32 scaleRGBTo100(float x) { return uInt32(50.0001F * (x - 0.F)); } + + /** + Convert angles + */ + static constexpr float scaleFromAngles(float x) { return x / 10.F; } + static constexpr Int32 scaleToAngles(float x) { return uInt32(10.F * x); } + /** Convert adjustables from/to 100% scale */ static constexpr float scaleFrom100(float x) { return (x / 50.F) - 1.F; } static constexpr uInt32 scaleTo100(float x) { return uInt32(50.0001F * (x + 1.F)); } + /** + Check for 'Custom' palette only adjustables + */ + bool isCustomAdjustable() const; + + bool isPhaseShift() const; + + bool isRGBScale() const; + + bool isRGBShift() const; + /** Convert palette settings name to enumeration. @@ -186,6 +232,21 @@ class PaletteHandler */ void adjustHueSaturation(int& R, int& G, int& B, float H, float S); + /** + Rotate a 2D vector. + */ + vector2d rotate(const vector2d& vec, float angle) const; + + /** + Scale a 2D vector. + */ + vector2d scale(const vector2d& vec, float factor) const; + + /** + Get the dot product of two 2D vectors. + */ + float dotProduct(const vector2d& vec1, const vector2d& vec2) const; + /** Loads a user-defined palette file (from OSystem::paletteFile), filling the appropriate user-defined palette arrays. @@ -193,7 +254,7 @@ class PaletteHandler void loadUserPalette(); private: - static constexpr int NUM_ADJUSTABLES = 6; + static constexpr int NUM_ADJUSTABLES = 12; OSystem& myOSystem; @@ -207,6 +268,12 @@ class PaletteHandler const std::array myAdjustables = { { { "phase shift", nullptr }, + { "red scale", &myRedScale }, + { "green scale", &myGreenScale }, + { "blue scale", &myBlueScale }, + { "red shift", &myRedShift }, + { "green shift", &myGreenShift }, + { "blue shift", &myBlueShift }, { "hue", &myHue }, { "saturation", &mySaturation }, { "contrast", &myContrast }, @@ -217,6 +284,14 @@ class PaletteHandler // NTSC and PAL color phase shifts float myPhaseNTSC{DEF_NTSC_SHIFT}; float myPhasePAL{DEF_PAL_SHIFT}; + // Color intensities + float myRedScale{1.0F}; + float myGreenScale{1.0F}; + float myBlueScale{1.0F}; + // Color shifts + float myRedShift{0.0F}; + float myGreenShift{0.0F}; + float myBlueShift{0.0F}; // range -1.0 to +1.0 (as in AtariNTSC) // Basic parameters float myHue{0.0F}; // -1 = -180 degrees +1 = +180 degrees diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index a5b0d5b1f..f0da1226c 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -377,7 +377,9 @@ AdjustFunction EventHandler::cycleAdjustSetting(int direction) #ifdef ADAPTABLE_REFRESH_SUPPORT || (myAdjustSetting == AdjustSetting::ADAPT_REFRESH && !isFullScreen) #endif - || (myAdjustSetting == AdjustSetting::PALETTE_PHASE && !isCustomPalette) + || (myAdjustSetting >= AdjustSetting::PALETTE_PHASE + && myAdjustSetting <= AdjustSetting::PALETTE_BLUE_SHIFT + && !isCustomPalette) || (myAdjustSetting >= AdjustSetting::NTSC_SHARPNESS && myAdjustSetting <= AdjustSetting::NTSC_BLEEDING && !isCustomFilter); @@ -425,6 +427,18 @@ AdjustFunction EventHandler::getAdjustSetting(AdjustSetting setting) std::bind(&PaletteHandler::cyclePalette, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), _1), std::bind(&PaletteHandler::changeAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), PaletteHandler::PHASE_SHIFT, _1), + std::bind(&PaletteHandler::changeAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), + PaletteHandler::RED_SCALE, _1), + std::bind(&PaletteHandler::changeAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), + PaletteHandler::RED_SHIFT, _1), + std::bind(&PaletteHandler::changeAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), + PaletteHandler::GREEN_SCALE, _1), + std::bind(&PaletteHandler::changeAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), + PaletteHandler::GREEN_SHIFT, _1), + std::bind(&PaletteHandler::changeAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), + PaletteHandler::BLUE_SCALE, _1), + std::bind(&PaletteHandler::changeAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), + PaletteHandler::BLUE_SHIFT, _1), std::bind(&PaletteHandler::changeAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), PaletteHandler::HUE, _1), std::bind(&PaletteHandler::changeAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), diff --git a/src/emucore/EventHandler.hxx b/src/emucore/EventHandler.hxx index e437de7b4..4e591b869 100644 --- a/src/emucore/EventHandler.hxx +++ b/src/emucore/EventHandler.hxx @@ -431,6 +431,12 @@ class EventHandler // Palette adjustables PALETTE, PALETTE_PHASE, + PALETTE_RED_SCALE, + PALETTE_RED_SHIFT, + PALETTE_GREEN_SCALE, + PALETTE_GREEN_SHIFT, + PALETTE_BLUE_SCALE, + PALETTE_BLUE_SHIFT, PALETTE_HUE, PALETTE_SATURATION, PALETTE_CONTRAST, diff --git a/src/emucore/PointingDevice.cxx b/src/emucore/PointingDevice.cxx index accf00a04..cbd7f89c9 100644 --- a/src/emucore/PointingDevice.cxx +++ b/src/emucore/PointingDevice.cxx @@ -81,6 +81,7 @@ void PointingDevice::update() return; // Update horizontal direction + cerr << myEvent.get(Event::MouseAxisXMove) << ", " << myHCounterRemainder << endl; updateDirection( myEvent.get(Event::MouseAxisXMove), myHCounterRemainder, myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH); diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index 08898c428..f7fa27ae8 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -62,6 +62,13 @@ Settings::Settings() setPermanent("palette", PaletteHandler::SETTING_STANDARD); setPermanent("pal.phase_ntsc", "26.2"); setPermanent("pal.phase_pal", "31.3"); + setPermanent("pal.red_scale", "0.0"); + setPermanent("pal.green_scale", "0.0"); + setPermanent("pal.blue_scale", "0.0"); + setPermanent("pal.red_shift", "0.0"); + setPermanent("pal.green_shift", "0.0"); + setPermanent("pal.blue_shift", "0.0"); + setPermanent("pal.contrast", "0.0"); setPermanent("pal.brightness", "0.0"); setPermanent("pal.hue", "0.0"); @@ -415,16 +422,24 @@ void Settings::usage() const << " -center <1|0> Centers game window in windowed modes\n" << " -windowedpos Sets the window position in windowed emulator mode\n" << " -display Sets the display for Stella's emulator\n" - << " -palette \n" - << " -pal.phase_ntsc Phase shift for NTSC 'custom' palette\n" - << " -pal.phase_pal Phase shift for PAL 'custom' palette\n" - << " -pal.hue <-1.0 - 1.0> Adjust hue for current palette\n" - << " -pal.saturation <-1.0 - 1.0> Adjust saturation of current palette\n" - << " -pal.contrast <-1.0 - 1.0> Adjust contrast of current palette\n" - << " -pal.brightness <-1.0 - 1.0> Adjust brightness of current palette\n" - << " -pal.gamma <-1.0 - 1.0> Adjust gamma of current palette\n" + << endl + << " -palette \n" + << " -pal.phase_ntsc Phase shift for NTSC 'custom' palette\n" + << " -pal.phase_pal Phase shift for PAL 'custom' palette\n" + << " -pal.red_scale <-1.0 - 1.0> Adjust red scale for 'custom' palette\n" + << " -pal.red_shift <-1.0 - 1.0> Adjust red shift for 'custom' palette\n" + << " -pal.green_scale <-1.0 - 1.0> Adjust green scale for 'custom' palette\n" + << " -pal.green_shift <-1.0 - 1.0> Adjust green shift for 'custom' palette\n" + << " -pal.blue_scale <-1.0 - 1.0> Adjust blue scale for 'custom' palette\n" + << " -pal.blue_shift <-1.0 - 1.0> Adjust blue shift for 'custom' palette\n" + << " -pal.hue <-1.0 - 1.0> Adjust hue for current palette\n" + << " -pal.saturation <-1.0 - 1.0> Adjust saturation of current palette\n" + << " -pal.contrast <-1.0 - 1.0> Adjust contrast of current palette\n" + << " -pal.brightness <-1.0 - 1.0> Adjust brightness of current palette\n" + << " -pal.gamma <-1.0 - 1.0> Adjust gamma of current palette\n" + << endl << " -speed Run emulation at the given speed\n" << " -turbo <1|0> Enable 'Turbo' mode for maximum emulation speed\n" << " -uimessages <1|0> Show onscreen UI messages for different events\n" diff --git a/src/gui/VideoAudioDialog.cxx b/src/gui/VideoAudioDialog.cxx index be08f83f8..01297b62b 100644 --- a/src/gui/VideoAudioDialog.cxx +++ b/src/gui/VideoAudioDialog.cxx @@ -223,25 +223,82 @@ void VideoAudioDialog::addPaletteTab() const int swidth = myTIAPalette->getWidth() - lwidth; const int plWidth = _font.getStringWidth("NTSC phase "); const int pswidth = swidth - INDENT + lwidth - plWidth; + xpos += INDENT; myPhaseShiftNtsc = - new SliderWidget(myTab, _font, xpos + INDENT, ypos-1, pswidth, lineHeight, + new SliderWidget(myTab, _font, xpos, ypos - 1, pswidth, lineHeight, "NTSC phase", plWidth, kNtscShiftChanged, fontWidth * 5); - myPhaseShiftNtsc->setMinValue((PaletteHandler::DEF_NTSC_SHIFT - PaletteHandler::MAX_SHIFT) * 10); - myPhaseShiftNtsc->setMaxValue((PaletteHandler::DEF_NTSC_SHIFT + PaletteHandler::MAX_SHIFT) * 10); + myPhaseShiftNtsc->setMinValue((PaletteHandler::DEF_NTSC_SHIFT - PaletteHandler::MAX_PHASE_SHIFT) * 10); + myPhaseShiftNtsc->setMaxValue((PaletteHandler::DEF_NTSC_SHIFT + PaletteHandler::MAX_PHASE_SHIFT) * 10); myPhaseShiftNtsc->setTickmarkIntervals(4); wid.push_back(myPhaseShiftNtsc); ypos += lineHeight + VGAP; myPhaseShiftPal = - new SliderWidget(myTab, _font, xpos + INDENT, ypos-1, pswidth, lineHeight, + new SliderWidget(myTab, _font, xpos, ypos - 1, pswidth, lineHeight, "PAL phase", plWidth, kPalShiftChanged, fontWidth * 5); - myPhaseShiftPal->setMinValue((PaletteHandler::DEF_PAL_SHIFT - PaletteHandler::MAX_SHIFT) * 10); - myPhaseShiftPal->setMaxValue((PaletteHandler::DEF_PAL_SHIFT + PaletteHandler::MAX_SHIFT) * 10); + myPhaseShiftPal->setMinValue((PaletteHandler::DEF_PAL_SHIFT - PaletteHandler::MAX_PHASE_SHIFT) * 10); + myPhaseShiftPal->setMaxValue((PaletteHandler::DEF_PAL_SHIFT + PaletteHandler::MAX_PHASE_SHIFT) * 10); myPhaseShiftPal->setTickmarkIntervals(4); wid.push_back(myPhaseShiftPal); ypos += lineHeight + VGAP; + const int rgblWidth = _font.getStringWidth("R "); + const int rgbsWidth = (myTIAPalette->getWidth() - INDENT - rgblWidth - fontWidth * 5) / 2; + + myTVRedScale = + new SliderWidget(myTab, _font, xpos, ypos - 1, rgbsWidth, lineHeight, + "R", rgblWidth, kPaletteUpdated, fontWidth * 4, "%"); + myTVRedScale->setMinValue(0); + myTVRedScale->setMaxValue(100); + myTVRedScale->setTickmarkIntervals(2); + wid.push_back(myTVRedScale); + + const int xposr = myTIAPalette->getRight() - rgbsWidth; + myTVRedShift = + new SliderWidget(myTab, _font, xposr, ypos - 1, rgbsWidth, lineHeight, + "", 0, kRedShiftChanged, fontWidth * 6); + myTVRedShift->setMinValue((PaletteHandler::DEF_RGB_SHIFT - PaletteHandler::MAX_RGB_SHIFT) * 10); + myTVRedShift->setMaxValue((PaletteHandler::DEF_RGB_SHIFT + PaletteHandler::MAX_RGB_SHIFT) * 10); + myTVRedShift->setTickmarkIntervals(2); + wid.push_back(myTVRedShift); + ypos += lineHeight + VGAP; + + myTVGreenScale = + new SliderWidget(myTab, _font, xpos, ypos - 1, rgbsWidth, lineHeight, + "G", rgblWidth, kPaletteUpdated, fontWidth * 4, "%"); + myTVGreenScale->setMinValue(0); + myTVGreenScale->setMaxValue(100); + myTVGreenScale->setTickmarkIntervals(2); + wid.push_back(myTVGreenScale); + + myTVGreenShift = + new SliderWidget(myTab, _font, xposr, ypos - 1, rgbsWidth, lineHeight, + "", 0, kGreenShiftChanged, fontWidth * 6); + myTVGreenShift->setMinValue((PaletteHandler::DEF_RGB_SHIFT - PaletteHandler::MAX_RGB_SHIFT) * 10); + myTVGreenShift->setMaxValue((PaletteHandler::DEF_RGB_SHIFT + PaletteHandler::MAX_RGB_SHIFT) * 10); + myTVGreenShift->setTickmarkIntervals(2); + wid.push_back(myTVGreenShift); + ypos += lineHeight + VGAP; + + myTVBlueScale = + new SliderWidget(myTab, _font, xpos, ypos - 1, rgbsWidth, lineHeight, + "B", rgblWidth, kPaletteUpdated, fontWidth * 4, "%"); + myTVBlueScale->setMinValue(0); + myTVBlueScale->setMaxValue(100); + myTVBlueScale->setTickmarkIntervals(2); + wid.push_back(myTVBlueScale); + + myTVBlueShift = + new SliderWidget(myTab, _font, xposr, ypos - 1, rgbsWidth, lineHeight, + "", 0, kBlueShiftChanged, fontWidth * 6); + myTVBlueShift->setMinValue((PaletteHandler::DEF_RGB_SHIFT - PaletteHandler::MAX_RGB_SHIFT) * 10); + myTVBlueShift->setMaxValue((PaletteHandler::DEF_RGB_SHIFT + PaletteHandler::MAX_RGB_SHIFT) * 10); + myTVBlueShift->setTickmarkIntervals(2); + wid.push_back(myTVBlueShift); + ypos += lineHeight + VGAP; + xpos -= INDENT; + CREATE_CUSTOM_SLIDERS(Hue, "Hue ", kPaletteUpdated) CREATE_CUSTOM_SLIDERS(Satur, "Saturation ", kPaletteUpdated) CREATE_CUSTOM_SLIDERS(Contrast, "Contrast ", kPaletteUpdated) @@ -524,6 +581,12 @@ void VideoAudioDialog::loadConfig() instance().frameBuffer().tiaSurface().paletteHandler().getAdjustables(myPaletteAdj); myPhaseShiftNtsc->setValue(myPaletteAdj.phaseNtsc); myPhaseShiftPal->setValue(myPaletteAdj.phasePal); + myTVRedScale->setValue(myPaletteAdj.redScale); + myTVRedShift->setValue(myPaletteAdj.redShift); + myTVGreenScale->setValue(myPaletteAdj.greenScale); + myTVGreenShift->setValue(myPaletteAdj.greenShift); + myTVBlueScale->setValue(myPaletteAdj.blueScale); + myTVBlueShift->setValue(myPaletteAdj.blueShift); myTVHue->setValue(myPaletteAdj.hue); myTVBright->setValue(myPaletteAdj.brightness); myTVContrast->setValue(myPaletteAdj.contrast); @@ -760,6 +823,12 @@ void VideoAudioDialog::setDefaults() myTIAPalette->setSelected(PaletteHandler::SETTING_STANDARD); myPhaseShiftNtsc->setValue(PaletteHandler::DEF_NTSC_SHIFT * 10); myPhaseShiftPal->setValue(PaletteHandler::DEF_PAL_SHIFT * 10); + myTVRedScale->setValue(50); + myTVRedShift->setValue(PaletteHandler::DEF_RGB_SHIFT); + myTVGreenScale->setValue(50); + myTVGreenShift->setValue(PaletteHandler::DEF_RGB_SHIFT); + myTVBlueScale->setValue(50); + myTVBlueShift->setValue(PaletteHandler::DEF_RGB_SHIFT); myTVHue->setValue(50); myTVSatur->setValue(50); myTVContrast->setValue(50); @@ -847,6 +916,23 @@ void VideoAudioDialog::handlePaletteChange() myPhaseShiftNtsc->setEnabled(enable); myPhaseShiftPal->setEnabled(enable); + myTVRedScale->setEnabled(enable); + myTVRedShift->setEnabled(enable); + myTVGreenScale->setEnabled(enable); + myTVGreenShift->setEnabled(enable); + myTVBlueScale->setEnabled(enable); + myTVBlueShift->setEnabled(enable); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void VideoAudioDialog::handleShiftChanged(SliderWidget* widget) +{ + std::ostringstream ss; + + ss << std::setw(4) << std::fixed << std::setprecision(1) + << (0.1 * (widget->getValue())) << DEGREE; + widget->setValueLabel(ss.str()); + handlePaletteUpdate(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -859,6 +945,12 @@ void VideoAudioDialog::handlePaletteUpdate() PaletteHandler::Adjustable paletteAdj; paletteAdj.phaseNtsc = myPhaseShiftNtsc->getValue(); paletteAdj.phasePal = myPhaseShiftPal->getValue(); + paletteAdj.redScale = myTVRedScale->getValue(); + paletteAdj.redShift = myTVRedShift->getValue(); + paletteAdj.greenScale = myTVGreenScale->getValue(); + paletteAdj.greenShift = myTVGreenShift->getValue(); + paletteAdj.blueScale = myTVBlueScale->getValue(); + paletteAdj.blueShift = myTVBlueShift->getValue(); paletteAdj.hue = myTVHue->getValue(); paletteAdj.saturation = myTVSatur->getValue(); paletteAdj.contrast = myTVContrast->getValue(); @@ -931,25 +1023,25 @@ void VideoAudioDialog::handleCommand(CommandSender* sender, int cmd, break; case kNtscShiftChanged: - { - std::ostringstream ss; - - ss << std::setw(4) << std::fixed << std::setprecision(1) - << (0.1 * abs(myPhaseShiftNtsc->getValue())) << DEGREE; - myPhaseShiftNtsc->setValueLabel(ss.str()); - handlePaletteUpdate(); + handleShiftChanged(myPhaseShiftNtsc); break; - } + case kPalShiftChanged: - { - std::ostringstream ss; - - ss << std::setw(4) << std::fixed << std::setprecision(1) - << (0.1 * abs(myPhaseShiftPal->getValue())) << DEGREE; - myPhaseShiftPal->setValueLabel(ss.str()); - handlePaletteUpdate(); + handleShiftChanged(myPhaseShiftPal); break; - } + + case kRedShiftChanged: + handleShiftChanged(myTVRedShift); + break; + + case kGreenShiftChanged: + handleShiftChanged(myTVGreenShift); + break; + + case kBlueShiftChanged: + handleShiftChanged(myTVBlueShift); + break; + case kVSizeChanged: { int adjust = myVSizeAdjust->getValue(); diff --git a/src/gui/VideoAudioDialog.hxx b/src/gui/VideoAudioDialog.hxx index 3581635b4..1d0e8a637 100644 --- a/src/gui/VideoAudioDialog.hxx +++ b/src/gui/VideoAudioDialog.hxx @@ -53,6 +53,7 @@ class VideoAudioDialog : public Dialog void handleTVModeChange(NTSCFilter::Preset); void loadTVAdjustables(NTSCFilter::Preset preset); void handlePaletteChange(); + void handleShiftChanged(SliderWidget* widget); void handlePaletteUpdate(); void handleFullScreenChange(); void handleOverscanChange(); @@ -105,6 +106,12 @@ class VideoAudioDialog : public Dialog PopUpWidget* myTIAPalette{nullptr}; SliderWidget* myPhaseShiftNtsc{nullptr}; SliderWidget* myPhaseShiftPal{nullptr}; + SliderWidget* myTVRedScale{nullptr}; + SliderWidget* myTVRedShift{nullptr}; + SliderWidget* myTVGreenScale{nullptr}; + SliderWidget* myTVGreenShift{nullptr}; + SliderWidget* myTVBlueScale{nullptr}; + SliderWidget* myTVBlueShift{nullptr}; SliderWidget* myTVHue{nullptr}; SliderWidget* myTVSatur{nullptr}; SliderWidget* myTVBright{nullptr}; @@ -138,6 +145,9 @@ class VideoAudioDialog : public Dialog kPaletteChanged = 'VDpl', kNtscShiftChanged = 'VDns', kPalShiftChanged = 'VDps', + kRedShiftChanged = 'VDrs', + kGreenShiftChanged = 'VDgs', + kBlueShiftChanged = 'VDbs', kPaletteUpdated = 'VDpu', kTVModeChanged = 'VDtv', diff --git a/src/gui/WhatsNewDialog.cxx b/src/gui/WhatsNewDialog.cxx index 9c37701d2..e40d8e622 100644 --- a/src/gui/WhatsNewDialog.cxx +++ b/src/gui/WhatsNewDialog.cxx @@ -47,8 +47,9 @@ WhatsNewDialog::WhatsNewDialog(OSystem& osystem, DialogContainer& parent, const add(ypos, "fixed bug with launcher not remembering last selected ROM"); #else add(ypos, "added basic text cut/copy/paste to UI"); - add(ypos, "fixed fullscreen mode, aspect correction and pixel-exact snapshots"); + add(ypos, "added color parameters to 'Custom' palette"); add(ypos, "improved AVox-USB adaptor autodetection"); + add(ypos, "fixed fullscreen mode, aspect correction and pixel-exact snapshots"); add(ypos, "fixed crash with SaveKey ROMs (EEPROM file issues)"); add(ypos, "fixed bug with launcher not remembering last selected ROM"); add(ypos, ELLIPSIS + " (for a complete list see 'docs/Changes.txt')");
ItemBrief descriptionFor more information,
see Command Line
PalettePalette used for emulation mode-palette
NTSC phaseAdjust phase shift for 'Custom' NTSC palette-pal.phase_ntsc
PAL phaseAdjust phase shift for 'Custom' PAL palette-pal.phase_pal
NTSC phaseAdjust phase shift of 'Custom' NTSC palette-pal.phase_ntsc
PAL phaseAdjust phase shift of 'Custom' PAL palette-pal.phase_pal
RAdjust red scale and shift of 'Custom' palette-pal.red_scale, -pal.red_shift
GAdjust green scale and shift of 'Custom' palette-pal.green_scale, -pal.green_shift
BAdjust blue scale and shift of 'Custom' palette-pal.blue_scale, -pal.blue_shift
HueAdjust hue of currently selected palette-pal.hue
SaturationAdjust saturation of currently selected palette-pal.saturation
ContrastAdjust contrast of currently selected palette-pal.contrast