Yes, I agree that this happens in some cases. But I still think that this is not what is happening here. Notice how in your example the error is of 0.0000000298023224
, but in the case of the hue()
it’s 0.1666, 5.5 million times larger.
If all the color calculations where done in floats or doubles, the error would be insignificant, but I think that converting ints to floats and back with multiplications and divisions might have this effect.
When calling hue(), Processing uses java.awt.Color.RGBtoHSB
to do the transformation.
But when calling color() Processing calculates the value itself.
I decompiled java.awt.Color and this is the source of that function:
public static int HSBtoRGB(float hue, float saturation, float brightness) {
int r = 0;
int g = 0;
int b = 0;
if (saturation == 0.0F) {
r = g = b = (int)(brightness * 255.0F + 0.5F);
} else {
float h = (hue - (float)Math.floor((double)hue)) * 6.0F;
float f = h - (float)Math.floor((double)h);
float p = brightness * (1.0F - saturation);
float q = brightness * (1.0F - saturation * f);
float t = brightness * (1.0F - saturation * (1.0F - f));
switch((int)h) {
case 0:
r = (int)(brightness * 255.0F + 0.5F);
g = (int)(t * 255.0F + 0.5F);
b = (int)(p * 255.0F + 0.5F);
break;
case 1:
r = (int)(q * 255.0F + 0.5F);
g = (int)(brightness * 255.0F + 0.5F);
b = (int)(p * 255.0F + 0.5F);
break;
case 2:
r = (int)(p * 255.0F + 0.5F);
g = (int)(brightness * 255.0F + 0.5F);
b = (int)(t * 255.0F + 0.5F);
break;
case 3:
r = (int)(p * 255.0F + 0.5F);
g = (int)(q * 255.0F + 0.5F);
b = (int)(brightness * 255.0F + 0.5F);
break;
case 4:
r = (int)(t * 255.0F + 0.5F);
g = (int)(p * 255.0F + 0.5F);
b = (int)(brightness * 255.0F + 0.5F);
break;
case 5:
r = (int)(brightness * 255.0F + 0.5F);
g = (int)(p * 255.0F + 0.5F);
b = (int)(q * 255.0F + 0.5F);
}
}
return -16777216 | r << 16 | g << 8 | b << 0;
}
And this is from the Processing color()
function:
case HSB:
x /= colorModeX; // h
y /= colorModeY; // s
z /= colorModeZ; // b
calcA = colorModeScale ? (a/colorModeA) : a;
if (y == 0) { // saturation == 0
calcR = calcG = calcB = z;
} else {
float which = (x - (int)x) * 6.0f;
float f = which - (int)which;
float p = z * (1.0f - y);
float q = z * (1.0f - y * f);
float t = z * (1.0f - (y * (1.0f - f)));
switch ((int)which) {
case 0: calcR = z; calcG = t; calcB = p; break;
case 1: calcR = q; calcG = z; calcB = p; break;
case 2: calcR = p; calcG = z; calcB = t; break;
case 3: calcR = p; calcG = q; calcB = z; break;
case 4: calcR = t; calcG = p; calcB = z; break;
case 5: calcR = z; calcG = p; calcB = q; break;
}
}
break;
AWT adds a 0.5f offset in many cases but Processing doesn’t do that. Is that maybe part of the reason for this discrepancy?
Update: I can confirm that the issue is the different way color()
calculates the color when in HSB mode, compared to AWT. If you replace color()
by a call to java.awt.Color.HSBtoRGB()
the unexpected values are gone and you only get the minor float rounding errors @Lexyth was mentioning above :
import java.awt.*;
void setup() {
for (int i=0; i<256; i++) {
float h = hue(Color.HSBtoRGB(i/255.0, 1.0, 1.0));
println("hue", i, "becomes", h);
}
}
void draw() {}
...
hue 57 becomes 57.0
hue 58 becomes 57.999996
hue 59 becomes 59.0
hue 60 becomes 59.999996
hue 61 becomes 61.0
hue 62 becomes 61.999996
hue 63 becomes 63.0
hue 64 becomes 63.999996
hue 65 becomes 65.0
hue 66 becomes 66.0
hue 67 becomes 67.0
hue 68 becomes 67.99999
hue 69 becomes 69.0
hue 70 becomes 70.0
...