mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-02 16:06:19 +00:00
4bab6fcf32
Package material's ad-hoc mulAlpha didn't take the sRGB color-space into account, which meant that alpha-scaled colors were subtly wrong. Introduce f32color.MulAlpha and convert all uses to it. Thanks to René Post for finding and debugging the issue. Signed-off-by: Elias Naur <mail@eliasnaur.com>
88 lines
1.9 KiB
Go
88 lines
1.9 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package f32color
|
|
|
|
import (
|
|
"image/color"
|
|
"math"
|
|
)
|
|
|
|
// RGBA is a 32 bit floating point linear space color.
|
|
type RGBA struct {
|
|
R, G, B, A float32
|
|
}
|
|
|
|
// Array returns rgba values in a [4]float32 array.
|
|
func (rgba RGBA) Array() [4]float32 {
|
|
return [4]float32{rgba.R, rgba.G, rgba.B, rgba.A}
|
|
}
|
|
|
|
// Float32 returns r, g, b, a values.
|
|
func (col RGBA) Float32() (r, g, b, a float32) {
|
|
return col.R, col.G, col.B, col.A
|
|
}
|
|
|
|
// SRGBA converts from linear to sRGB color space.
|
|
func (col RGBA) SRGB() color.RGBA {
|
|
return color.RGBA{
|
|
R: uint8(linearTosRGB(col.R)*255 + .5),
|
|
G: uint8(linearTosRGB(col.G)*255 + .5),
|
|
B: uint8(linearTosRGB(col.B)*255 + .5),
|
|
A: uint8(col.A*255 + .5),
|
|
}
|
|
}
|
|
|
|
// Opaque returns the color without alpha component.
|
|
func (col RGBA) Opaque() RGBA {
|
|
col.A = 1.0
|
|
return col
|
|
}
|
|
|
|
// RGBAFromSRGB converts from SRGBA to RGBA.
|
|
func RGBAFromSRGB(col color.RGBA) RGBA {
|
|
r, g, b, a := col.RGBA()
|
|
return RGBA{
|
|
R: sRGBToLinear(float32(r) / 0xffff),
|
|
G: sRGBToLinear(float32(g) / 0xffff),
|
|
B: sRGBToLinear(float32(b) / 0xffff),
|
|
A: float32(a) / 0xFFFF,
|
|
}
|
|
}
|
|
|
|
// linearTosRGB transforms color value from linear to sRGB.
|
|
func linearTosRGB(c float32) float32 {
|
|
// Formula from EXT_sRGB.
|
|
switch {
|
|
case c <= 0:
|
|
return 0
|
|
case 0 < c && c < 0.0031308:
|
|
return 12.92 * c
|
|
case 0.0031308 <= c && c < 1:
|
|
return 1.055*float32(math.Pow(float64(c), 0.41666)) - 0.055
|
|
}
|
|
|
|
return 1
|
|
}
|
|
|
|
// sRGBToLinear transforms color value from sRGB to linear.
|
|
func sRGBToLinear(c float32) float32 {
|
|
// Formula from EXT_sRGB.
|
|
if c <= 0.04045 {
|
|
return c / 12.92
|
|
} else {
|
|
return float32(math.Pow(float64((c+0.055)/1.055), 2.4))
|
|
}
|
|
}
|
|
|
|
// MulAlpha scales all color components by alpha/255.
|
|
func MulAlpha(c color.RGBA, alpha uint8) color.RGBA {
|
|
// TODO: Optimize. This is pretty slow.
|
|
a := float32(alpha) / 255.
|
|
rgba := RGBAFromSRGB(c)
|
|
rgba.A *= a
|
|
rgba.R *= a
|
|
rgba.G *= a
|
|
rgba.B *= a
|
|
return rgba.SRGB()
|
|
}
|