added color parameters to 'Custom' palette, fixes #657

This commit is contained in:
thrust26 2020-10-31 19:06:58 +01:00
parent e92b9f3b94
commit ec52ea8049
12 changed files with 438 additions and 98 deletions

View File

@ -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).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -2130,7 +2130,7 @@
<td><pre>-palette &lt;standard|z26|user|custom&gt;</pre></td>
<td>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.</td>
from user-defined parameters.</td>
</tr>
<tr>
@ -2143,6 +2143,36 @@
<td>Adjust phase shift of 'custom' PAL palette.</td>
</tr>
<tr>
<td><pre>-pal.red_scale &lt;number&gt;</pre></td>
<td>Adjust red scale of 'custom' palette (range -1.0 to 1.0).</td>
</tr>
<tr>
<td><pre>-pal.red_shift &lt;number&gt;</pre></td>
<td>Adjust red shift of 'custom' palette (range -22.5 to 22.5).</td>
</tr>
<tr>
<td><pre>-pal.green_scale &lt;number&gt;</pre></td>
<td>Adjust green scale of 'custom' palette (range -1.0 to 1.0).</td>
</tr>
<tr>
<td><pre>-pal.green_shift &lt;number&gt;</pre></td>
<td>Adjust green shift of 'custom' palette (range -22.5 to 22.5).</td>
</tr>
<tr>
<td><pre>-pal.blue_scale &lt;number&gt;</pre></td>
<td>Adjust blue scale of 'custom' palette (range -1.0 to 1.0).</td>
</tr>
<tr>
<td><pre>-pal.blue_shift &lt;number&gt;</pre></td>
<td>Adjust blue shift of 'custom' palette (range -22.5 to 22.5).</td>
</tr>
<tr>
<td><pre>-pal.hue &lt;number&gt;</pre></td>
<td>Adjust hue of current palette (range -1.0 to 1.0).</td>
@ -3078,8 +3108,11 @@
<table border="1" cellpadding="4">
<tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">Command Line</a></th></tr>
<tr><td>Palette</td><td>Palette used for emulation mode</td><td>-palette</td></tr>
<tr><td>NTSC phase</td><td>Adjust phase shift for 'Custom' NTSC palette</td><td>-pal.phase_ntsc</td></tr>
<tr><td>PAL phase</td><td>Adjust phase shift for 'Custom' PAL palette</td><td>-pal.phase_pal</td></tr>
<tr><td>NTSC phase</td><td>Adjust phase shift of 'Custom' NTSC palette</td><td>-pal.phase_ntsc</td></tr>
<tr><td>PAL phase</td><td>Adjust phase shift of 'Custom' PAL palette</td><td>-pal.phase_pal</td></tr>
<tr><td>R</td><td>Adjust red scale and shift of 'Custom' palette</td><td>-pal.red_scale, -pal.red_shift</td></tr>
<tr><td>G</td><td>Adjust green scale and shift of 'Custom' palette</td><td>-pal.green_scale, -pal.green_shift</td></tr>
<tr><td>B</td><td>Adjust blue scale and shift of 'Custom' palette</td><td>-pal.blue_scale, -pal.blue_shift</td></tr>
<tr><td>Hue</td><td>Adjust hue of currently selected palette</td><td>-pal.hue</td></tr>
<tr><td>Saturation</td><td>Adjust saturation of currently selected palette</td><td>-pal.saturation</td></tr>
<tr><td>Contrast</td><td>Adjust contrast of currently selected palette</td><td>-pal.contrast</td></tr>

View File

@ -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,

View File

@ -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<AdjustableTag, NUM_ADJUSTABLES> 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

View File

@ -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(),

View File

@ -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,

View File

@ -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);

View File

@ -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 <XxY> Sets the window position in windowed emulator mode\n"
<< " -display <number> Sets the display for Stella's emulator\n"
<< " -palette <standard| Use the specified color palette\n"
<< " z26|user|\n"
<< " custom>\n"
<< " -pal.phase_ntsc <number> Phase shift for NTSC 'custom' palette\n"
<< " -pal.phase_pal <number> 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 <standard| Use the specified color palette\n"
<< " z26|user|\n"
<< " custom>\n"
<< " -pal.phase_ntsc <number> Phase shift for NTSC 'custom' palette\n"
<< " -pal.phase_pal <number> 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 <number> 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"

View File

@ -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();

View File

@ -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',

View File

@ -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')");