Explicitly declare the NRGBA color interface.

This commit is contained in:
Amy G. Dalin 2024-05-07 19:03:57 -04:00
parent 556d94e4ed
commit 0414ffd8b9
2 changed files with 72 additions and 37 deletions

View File

@ -149,6 +149,14 @@ func Distance(a, b Color) float64 {
return math.Sqrt(max(sqr(dL), sqr(dL+dA)) + max(sqr(da), sqr(da+dA)) + max(sqr(db), sqr(db+dA)))
}
// NRGBAColor represent a color that can give us non pre-multiplied RGBA components.
//
// This isn't a standard interface, but we implement it and check for it regardless.
type NRGBAColor interface {
color.Color
NRGBA() (r, g, b, a uint32)
}
func okLabModel(c color.Color) color.Color {
switch c := c.(type) {
case Color:
@ -161,8 +169,7 @@ func okLabModel(c color.Color) color.Color {
case color.NRGBA64:
return FromNRGBA(uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A))
// This isn't a standard interface, but I'm going to check for it regardless.
case interface{ NRGBA() (r, g, b, a uint32) }:
case NRGBAColor:
return FromNRGBA(c.NRGBA())
default:
@ -170,5 +177,10 @@ func okLabModel(c color.Color) color.Color {
}
}
// Implements a color model for converting arbitrary colors to OKLab.
var Model = color.ModelFunc(okLabModel)
var (
// A color model for converting arbitrary colors to OKLab.
Model = color.ModelFunc(okLabModel)
// Type assertions.
_ NRGBAColor = Color{}
)

View File

@ -59,7 +59,7 @@ func Test_delinearize(t *testing.T) {
// The NRGBA and NRGBA64 models don't have sensible ways to recover transparent colors from types it
// doesn't know about, so I'm going to help them out.
func fixedNRGBAModel(c color.Color) color.Color {
if c, ok := c.(interface{ NRGBA() (r, g, b, a uint32) }); ok {
if c, ok := c.(NRGBAColor); ok {
r, g, b, a := c.NRGBA()
return color.NRGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: uint8(a >> 8)}
}
@ -68,7 +68,7 @@ func fixedNRGBAModel(c color.Color) color.Color {
}
func fixedNRGBA64Model(c color.Color) color.Color {
if c, ok := c.(interface{ NRGBA() (r, g, b, a uint32) }); ok {
if c, ok := c.(NRGBAColor); ok {
r, g, b, a := c.NRGBA()
return color.NRGBA64{R: uint16(r), G: uint16(g), B: uint16(b), A: uint16(a)}
}
@ -149,31 +149,38 @@ func Test_Model(t *testing.T) {
}
}
func TestDistance(t *testing.T) {
colours := []Color{
FromNRGBA(0xffff, 0xffff, 0xffff, 0xffff),
FromNRGBA(0xffff, 0xffff, 0xffff, 0x7fff),
FromNRGBA(0xffff, 0xffff, 0xffff, 0x0000),
FromNRGBA(0x0000, 0x0000, 0x0000, 0xffff),
FromNRGBA(0x0000, 0x0000, 0x0000, 0x7fff),
FromNRGBA(0x0000, 0x0000, 0x0000, 0x0000),
}
func testDistance(t *testing.T, c0, c1 Color) {
for i, c0 := range colours {
for j, c1 := range colours {
d := Distance(c0, c1)
if i == j || (c0.A == 0 && c1.A == 0) {
if math.IsNaN(d) || math.IsInf(d, 0) {
t.Errorf("Distance(%v, %v) = %f, want finite", c0, c1, d)
return
}
if d < 0 {
t.Errorf("Distance(%v, %v) = %f, want %f >= 0", c0, c1, d, d)
return
}
if d2 := Distance(c1, c0); d2 != d {
t.Errorf("Distance(%v, %v) != Distance(%v, %v), want %f == %f", c1, c0, c0, c1, d, d2)
return
}
if c0 == c1 || (c0.A == 0 && c1.A == 0) {
// if they're the same color, or both are completely transparent,
// they should be perceived as identical.
if d != 0 {
t.Errorf("Distance(%v, %v) = %v, want 0", c0, c1, d)
t.Errorf("Distance(%v, %v) = %f, want %f == 0", c0, c1, d, d)
return
}
} else {
// otherwise, there should be some kind of difference between them.
if d <= 0 {
t.Errorf("Distance(%v, %v) = %v, want > 0", c0, c1, d)
t.Errorf("Distance(%v, %v) = %f, want %f > 0", c0, c1, d, d)
return
}
}
@ -189,7 +196,23 @@ func TestDistance(t *testing.T) {
d2 := Distance(c0, mid) + Distance(mid, c1)
if d2 < d {
t.Errorf("Distance(%v, %v)+Distance(%v, %v) < Distance(%v, %v), want %f >= %f", c0, mid, mid, c1, c0, c1, d2, d)
return
}
}
func TestDistance(t *testing.T) {
colours := []Color{
FromNRGBA(0xffff, 0xffff, 0xffff, 0xffff),
FromNRGBA(0xffff, 0xffff, 0xffff, 0x7fff),
FromNRGBA(0xffff, 0xffff, 0xffff, 0x0000),
FromNRGBA(0x0000, 0x0000, 0x0000, 0xffff),
FromNRGBA(0x0000, 0x0000, 0x0000, 0x7fff),
FromNRGBA(0x0000, 0x0000, 0x0000, 0x0000),
}
for _, c0 := range colours {
for _, c1 := range colours {
testDistance(t, c0, c1)
}
}
}