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 cb8597bbe..d965b45b3 100644 Binary files a/docs/graphics/options_video_palettes.png and b/docs/graphics/options_video_palettes.png differ diff --git a/docs/index.html b/docs/index.html index 169bc0bc6..53f5b9e0f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2130,7 +2130,7 @@
-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