Explicitly declare the NRGBA color interface.
This commit is contained in:
parent
556d94e4ed
commit
0414ffd8b9
20
oklab.go
20
oklab.go
@ -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)))
|
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 {
|
func okLabModel(c color.Color) color.Color {
|
||||||
switch c := c.(type) {
|
switch c := c.(type) {
|
||||||
case Color:
|
case Color:
|
||||||
@ -161,8 +169,7 @@ func okLabModel(c color.Color) color.Color {
|
|||||||
case color.NRGBA64:
|
case color.NRGBA64:
|
||||||
return FromNRGBA(uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A))
|
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 NRGBAColor:
|
||||||
case interface{ NRGBA() (r, g, b, a uint32) }:
|
|
||||||
return FromNRGBA(c.NRGBA())
|
return FromNRGBA(c.NRGBA())
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -170,5 +177,10 @@ func okLabModel(c color.Color) color.Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements a color model for converting arbitrary colors to OKLab.
|
var (
|
||||||
var Model = color.ModelFunc(okLabModel)
|
// A color model for converting arbitrary colors to OKLab.
|
||||||
|
Model = color.ModelFunc(okLabModel)
|
||||||
|
|
||||||
|
// Type assertions.
|
||||||
|
_ NRGBAColor = Color{}
|
||||||
|
)
|
||||||
|
@ -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
|
// 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.
|
// doesn't know about, so I'm going to help them out.
|
||||||
func fixedNRGBAModel(c color.Color) color.Color {
|
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()
|
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)}
|
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 {
|
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()
|
r, g, b, a := c.NRGBA()
|
||||||
return color.NRGBA64{R: uint16(r), G: uint16(g), B: uint16(b), A: uint16(a)}
|
return color.NRGBA64{R: uint16(r), G: uint16(g), B: uint16(b), A: uint16(a)}
|
||||||
}
|
}
|
||||||
@ -149,6 +149,57 @@ func Test_Model(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDistance(t *testing.T, c0, c1 Color) {
|
||||||
|
|
||||||
|
d := Distance(c0, c1)
|
||||||
|
|
||||||
|
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) = %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) = %f, want %f > 0", c0, c1, d, d)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mid := Color{
|
||||||
|
Lightness: (c0.Lightness + c1.Lightness) * 0.5,
|
||||||
|
ChromaA: (c0.ChromaA + c1.ChromaA) * 0.5,
|
||||||
|
ChromaB: (c0.ChromaB + c1.ChromaB) * 0.5,
|
||||||
|
A: (c0.A + c1.A) * 0.5,
|
||||||
|
}
|
||||||
|
|
||||||
|
// traveling from c0 to c1 via mid can't possibly be
|
||||||
|
// shorter than traveling from c0 to c1 directly.
|
||||||
|
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) {
|
func TestDistance(t *testing.T) {
|
||||||
colours := []Color{
|
colours := []Color{
|
||||||
FromNRGBA(0xffff, 0xffff, 0xffff, 0xffff),
|
FromNRGBA(0xffff, 0xffff, 0xffff, 0xffff),
|
||||||
@ -159,37 +210,9 @@ func TestDistance(t *testing.T) {
|
|||||||
FromNRGBA(0x0000, 0x0000, 0x0000, 0x0000),
|
FromNRGBA(0x0000, 0x0000, 0x0000, 0x0000),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, c0 := range colours {
|
for _, c0 := range colours {
|
||||||
for j, c1 := range colours {
|
for _, c1 := range colours {
|
||||||
d := Distance(c0, c1)
|
testDistance(t, c0, c1)
|
||||||
|
|
||||||
if i == j || (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)
|
|
||||||
}
|
|
||||||
} 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mid := Color{
|
|
||||||
Lightness: (c0.Lightness + c1.Lightness) * 0.5,
|
|
||||||
ChromaA: (c0.ChromaA + c1.ChromaA) * 0.5,
|
|
||||||
ChromaB: (c0.ChromaB + c1.ChromaB) * 0.5,
|
|
||||||
A: (c0.A + c1.A) * 0.5,
|
|
||||||
}
|
|
||||||
|
|
||||||
// traveling from c0 to c1 via mid can't possibly be
|
|
||||||
// shorter than traveling from c0 to c1 directly.
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user