[wpilib] Tweak Color HSV formula and use in AddressableLED (#4724)

The Color algorithm was tweaked to:
a) not produce incorrect values if the user happens to input a hue outside the [0, 180) range, and
b) more accurately convert the hue remainder from range 0-30 to 0-255. The current conversion vastly overshoots the multiplier (converts 0-30 to 0-270) and relies on clamping the value when constructing the Color object to produce a slightly incorrect result.
This commit is contained in:
Colin Wong
2022-11-28 16:42:22 -06:00
committed by GitHub
parent ec124bb662
commit e82cd5147b
4 changed files with 42 additions and 25 deletions

View File

@@ -39,7 +39,7 @@ public class AddressableLEDBuffer {
* Sets a specific led in the buffer.
*
* @param index the index to write
* @param h the h value [0-180]
* @param h the h value [0-180)
* @param s the s value [0-255]
* @param v the v value [0-255]
*/
@@ -49,31 +49,48 @@ public class AddressableLEDBuffer {
return;
}
final int region = h / 30;
final int remainder = (h - (region * 30)) * 6;
// The below algorithm is copied from Color.fromHSV and moved here for
// performance reasons.
final int p = (v * (255 - s)) >> 8;
final int q = (v * (255 - ((s * remainder) >> 8))) >> 8;
final int t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
// Loosely based on
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
// The hue range is split into 60 degree regions where in each region there
// is one rgb component at a low value (m), one at a high value (v) and one
// that changes (X) from low to high (X+m) or high to low (v-X)
// Difference between highest and lowest value of any rgb component
final int chroma = (s * v) / 255;
// Beacuse hue is 0-180 rather than 0-360 use 30 not 60
final int region = (h / 30) % 6;
// Remainder converted from 0-30 to 0-255
final int remainder = (int) Math.round((h % 30) * (255 / 30.0));
// Value of the lowest rgb component
final int m = v - chroma;
// Goes from 0 to chroma as hue increases
final int X = (chroma * remainder) >> 8;
switch (region) {
case 0:
setRGB(index, v, t, p);
setRGB(index, v, X + m, m);
break;
case 1:
setRGB(index, q, v, p);
setRGB(index, v - X, v, m);
break;
case 2:
setRGB(index, p, v, t);
setRGB(index, m, v, X + m);
break;
case 3:
setRGB(index, p, q, v);
setRGB(index, m, v - X, v);
break;
case 4:
setRGB(index, t, p, v);
setRGB(index, X + m, m, v);
break;
default:
setRGB(index, v, p, q);
setRGB(index, v, m, v - X);
break;
}
}

View File

@@ -85,13 +85,13 @@ public class Color {
// that changes (X) from low to high (X+m) or high to low (v-X)
// Difference between highest and lowest value of any rgb component
final int chroma = (s * v) >> 8;
final int chroma = (s * v) / 255;
// Beacuse hue is 0-180 rather than 0-360 use 30 not 60
final int region = h / 30;
final int region = (h / 30) % 6;
// Remainder converted from 0-30 to roughly 0-255
final int remainder = (h - (region * 30)) * 9;
// Remainder converted from 0-30 to 0-255
final int remainder = (int) Math.round((h % 30) * (255 / 30.0));
// Value of the lowest rgb component
final int m = v - chroma;