openhab/openhab-core

ColorUtil bugs and improvements

Closed this issue · 1 comments

There are a number of bugs in, and potential improvements to, the ColorUtil module as follows.

1. hsbToXy() method produces invalid xyY results

The hsbToXy() method produces invalid results. Test case as follows..

// test case => produces invalid color gamut::blue
HSBType hsb = new HSBType(String.format("%d,%d,%d", 0, 50, 0));
double[] xyG = ColorUtil.hsbToXY(hsb);

Reason is that when B part of HSB is zero, the ColorUtil.hsbToXY(hsb) method follows a special code path that produces the xyY value xyY[0,0,0] which is an invalid color which causes the gamut correction to convert that to gamut:blue.

2. xyToHsb() method produces invalid results

The xyToHsb() method produces invalid HSB results when the xy co-ordinate is outside the color gamut.

// test case 2a) => produces invalid color gamut::blue
double[] xyG = new double[] {0,0,0};
HSBType hsb = ColorUtil2.xyToHsb(xyG);

Test case 2a) reason is the analogous inverse case of case 1. above.

// test case 2b) => produces invalid HSB B part
double[] xyG = new double[] {0.05,0.65,1.0};
HSBType hsb = ColorUtil2.xyToHsb(xyG);

Test case 2b) reason is that if the incoming xy coordinate is outside the color gamut, the gamut correction converts the coordinate to the closest coordinate within the gamut, however it does not adjust the Y element match.

3. Round trips hsbToXy() => xyToHsb() are not always consistent

In some cases the round trip hsbToXy() => xyToHsb() produces an HS(B) output that is widely different than the HS(B) input.

4. hsbToRgbw() method throws exceptions

For certain HSB values the hsbToRgbw() method throws a 'Non-terminating decimal expansion; no exact representable decimal result'. Test case as follows..

HSBType hsb = new HSBType(String.format("%d,%d,%d", 0, 10, 15));
int[] i = ColorUtil.hsbToRgbw(hsb);

5. hsbToRgbw() produces wrong results

For certain HSB values the hsbToRgbw() method produces wrong RGBW results. Test case as follows.

HSBType hsb = new HSBType(String.format("%d,%d,%d", 0, 0, 35));
int[] i = ColorUtil.hsbToRgbw(hsb);

This test case produces an interim RGB value of RGB[35,35,35] which should be converted to RGBW[0,0,0,89] but is actually converted to RGBW[38,38,38,51].

The reason is that the BigDecimal math in hsbToRgbwPercent() calculates using integer rounded values rather than floats.

line 191:
final BigDecimal multiplier = BIG_DECIMAL_100.divide(maxColor, 0, RoundingMode.DOWN); // NOTE: multiplier can only be INTEGER => should be float

6. hsbToRgbw() has over complex code

The hsbToRgbw() method has over complex code. For example the code below -- combined with the above correction to use floating arithmentic -- can be simplified.

final BigDecimal luminance = ((whitenessMax.add(whitenessMin).divide(BIG_DECIMAL_2).subtract(BIG_DECIMAL_50)).multiply(BIG_DECIMAL_100.divide(BIG_DECIMAL_50))).divide(multiplier);

7. Round trips hsbToRgbw() => rgbwToHsb() are not always consistent

In some cases the round trip hsbToRgbw() => rgbwToHsb() produces an HSB output that is widely different than the HSB input.


See also..

@andrewfg : I guess this issue can be closed now ?