raylib-go icon indicating copy to clipboard operation
raylib-go copied to clipboard

rl.ColorNormalize does not return normalized values

Open ScienceMarc opened this issue 3 years ago • 2 comments

Hi. It seems that the method rl.ColorNormalize() does not actually return normalized values, instead returning values close to the original RGBA values.

For example:

c := rl.NewColor(0x7F, 0x7F, 0x7F, 0xFF) // Arbitrary
fmt.Println(c)                           // {127 127 127 255}
normalized := rl.ColorNormalize(c)       // Should normalize c to {0.5,0.5,0.5,1}
fmt.Println(normalized)                  // Instead we get {127.99608 127.99608 127.99608 257}

For some reason this issue is masked by the fact that rl.ColorFromNormalized() returns the original values if they haven't been modified:

fmt.Println(rl.ColorFromNormalized(normalized)) // {127 127 127 255}

However modifying the supposedly normalized values yields unpredictable results when they are converted back to colors

normalized.X /= 2
fmt.Println(normalized) // {63.99804 127.99608 127.99608 257}
fmt.Println(rl.ColorFromNormalized(normalized)) // {191 127 127 255}

This problem seems to stem from the implementation of rl.ColorNormalize()

// ColorNormalize - Returns color normalized as float [0..1]
func ColorNormalize(col color.RGBA) Vector4 {
	result := Vector4{}
	r, g, b, a := col.RGBA()
	result.X = float32(r) / 255
	result.Y = float32(g) / 255
	result.Z = float32(b) / 255
	result.W = float32(a) / 255

	return result
}

This would work fine if color.RGBA returned the r,g,b,a values of the original color struct, however, this method actually returns the 32-bit "alpha-premultiplied color" values in the range 0 <= C <= A[^1]. This means that the return values have been scaled to a range of 0->65535 instead of the expected 0->255

The simplest solution would be to amend ColorNormalize() to avoid the RGBA method and keep the values in their original uint8 form:

func ColorNormalize(col color.RGBA) Vector4 {
	result := Vector4{}
	r, g, b, a := col.R, col.G, col.B, col.A
	result.X = float32(r) / 255
	result.Y = float32(g) / 255
	result.Z = float32(b) / 255
	result.W = float32(a) / 255

	return result
}

I can make a PR to fix the issue, unless I'm missing something obvious.

[^1]: This is what the documentation claims. The actual code does not seem to do this. I'm planning on starting an issue/PR for image/color to correct this. Even if this gets fixed it wouldn't change the issue presented here.

ScienceMarc avatar Sep 21 '22 18:09 ScienceMarc

Ignore the footnote. Turns out it's up to the developer to ensure the values are premultiplied, making the use of the color.RGBA struct in raylib-go wrong as I'm pretty sure Raylib doesn't use premultiplied values for color. Instead color.NRGBA should be used. Should I open a second issue/PR to address this?

ScienceMarc avatar Sep 21 '22 22:09 ScienceMarc

Yes, PR would be nice, thanks.

gen2brain avatar Sep 21 '22 22:09 gen2brain