color/internal/helper/oklab_test.go

137 lines
4.5 KiB
Go

package helper
import (
"fmt"
"image/color"
"math"
"testing"
)
func TestLMSToXYZ(t *testing.T) {
for c := range Enum(true, false, true) {
want := collect3(LRGBtoXYZ(RGBtoLRGB(uint32(c.R), uint32(c.G), uint32(c.B))))
if got := collect3(LMStoXYZ(XYZtoLMS(want[0], want[1], want[2]))); !EqFloat64SliceFuzzy(want[:], got[:]) {
t.Errorf("LMStoXYZ(XYZtoLMS(%v)) = %v, want unchanged", want, got)
return
}
}
}
func TestLMSToLRGB(t *testing.T) {
for c := range Enum(true, false, true) {
want := collect3(RGBtoLRGB(uint32(c.R), uint32(c.G), uint32(c.B)))
l, m, s := LRGBtoLMS(want[0], want[1], want[2])
// test via the optimized LMStoLRGB function.
if got := collect3(LMStoLRGB(l, m, s)); !EqFloat64SliceFuzzy(want[:], got[:]) {
t.Errorf("LMStoLRGB(LRGBtoLMS(%v)) = %v, want unchanged", want, got)
return
}
// make sure this is equivalent to going through the XYZ colourspace.
if got := collect3(XYZtoLRGB(LMStoXYZ(l, m, s))); !EqFloat64SliceFuzzy(want[:], got[:]) {
t.Errorf("XYZtoLRGB(LMStoXYZ(LRGBtoLMS(%v))) = %v, want unchanged", want, got)
return
}
}
}
func TestOKLabToLMS(t *testing.T) {
for c := range Enum(true, false, true) {
want := collect3(LRGBtoLMS(RGBtoLRGB(uint32(c.R), uint32(c.G), uint32(c.B))))
if got := collect3(OkLabToLMS(LMStoOkLab(want[0], want[1], want[2]))); !EqFloat64SliceFuzzy(want[:], got[:]) {
t.Errorf("OkLabToLMS(LMStoOKLab(%v)) = %v, want unchanged", want, got)
return
}
}
}
func TestOkLabExamplePairs(t *testing.T) {
// The page https://bottosson.github.io/posts/oklab/ lists example XYZ and OkLab pairs,
// with the results rounded to three decimal places.
examples := []struct{ xyz, lab [3]float64 }{
{[3]float64{0.950, 1.000, 1.089}, [3]float64{1.000, 0.000, 0.000}},
{[3]float64{1.000, 0.000, 0.000}, [3]float64{0.450, 1.236, -0.019}},
{[3]float64{0.000, 1.000, 0.000}, [3]float64{0.922, -0.671, 0.263}},
{[3]float64{0.000, 0.000, 1.000}, [3]float64{0.153, -1.415, -0.449}},
}
round := func(x float64) float64 {
return math.Round(x*1000) / 1000
}
round3 := func(a, b, c float64) [3]float64 {
return [3]float64{round(a), round(b), round(c)}
}
for i, e := range examples {
if gotLab := round3(LMStoOkLab(XYZtoLMS(e.xyz[0], e.xyz[1], e.xyz[2]))); gotLab != e.lab {
t.Errorf("pair %d: computed lab=%v, want=%v", i+1, gotLab, e.lab)
}
// note that the example table isn't suitable fo testing OkLab to XYZ conversion due to
// the errors introduced by rounding.
//
// we are depending the round trip conversions being correct, which is verified by TestLMSToXYZ and TestOKLabToLMS.
}
}
type testNXYZAColor [4]float64
func (c testNXYZAColor) RGBA() (_, _, _, _ uint32) {
panic("should not be called")
}
func (c testNXYZAColor) NXYZA() (_, _, _, _ float64) {
return c[0], c[1], c[2], c[3]
}
type testNOkLabAColor [4]float64
func (c testNOkLabAColor) RGBA() (_, _, _, _ uint32) {
panic("should not be called")
}
func (c testNOkLabAColor) NOkLabA() (_, _, _, _ float64) {
return c[0], c[1], c[2], c[3]
}
func TestColorToNOkLabA(t *testing.T) {
tests := []struct {
input color.Color
want [4]float64
}{
{
// test special NRGBA handling.
color.NRGBA{0x01, 0x23, 0x45, 0x67},
[4]float64{0.25462381167525894, -0.02293028913883799, -0.07098467472369072, float64(0x6767) / 0xffff},
}, {
// test special NRGBA64 handling.
color.NRGBA64{0x0123, 0x4567, 0x89ab, 0},
[4]float64{0.39601873251000413, -0.03369278598612779, -0.12401844116020128, 0},
}, {
// test a colour that can return its linear NRGBA values directly.
testNLRGBA{color.NRGBA64{0x0123, 0x4567, 0x89ab, 0xcdef}},
[4]float64{0.39601873251000413, -0.03369278598612779, -0.12401844116020128, float64(0xcdef) / 0xffff},
}, {
// test conversion of the values from a a colour that can return NXYZA values directly.
testNXYZAColor{0.95, 1., 1.089, .5},
// these were from the canonical test pairs, these values are 1, 0, 0 when rounded to the nearest thousandth.
[4]float64{0.9999686754143632, -0.0002580058168537569, -0.00011499756458199784, .5},
}, {
// test that we get the values from a colour that can return NOkLabA directly.
testNOkLabAColor{math.Inf(1), math.NaN(), math.Inf(-1), -1},
[4]float64{math.Inf(1), math.NaN(), math.Inf(-1), -1},
},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("%#+v", tt.input), func(t *testing.T) {
if got := collect4(ColorToNOkLabA(tt.input)); !EqFloat64SliceFuzzy(got[:], tt.want[:]) {
t.Errorf("ColorToNOkLabA(%#+v) = %v, want %v", tt.input, got, tt.want)
}
})
}
}