1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package color
6
7import (
8	"fmt"
9	"testing"
10)
11
12func delta(x, y uint8) uint8 {
13	if x >= y {
14		return x - y
15	}
16	return y - x
17}
18
19func eq(c0, c1 Color) error {
20	r0, g0, b0, a0 := c0.RGBA()
21	r1, g1, b1, a1 := c1.RGBA()
22	if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 {
23		return fmt.Errorf("got  0x%04x 0x%04x 0x%04x 0x%04x\nwant 0x%04x 0x%04x 0x%04x 0x%04x",
24			r0, g0, b0, a0, r1, g1, b1, a1)
25	}
26	return nil
27}
28
29// TestYCbCrRoundtrip tests that a subset of RGB space can be converted to YCbCr
30// and back to within 2/256 tolerance.
31func TestYCbCrRoundtrip(t *testing.T) {
32	for r := 0; r < 256; r += 7 {
33		for g := 0; g < 256; g += 5 {
34			for b := 0; b < 256; b += 3 {
35				r0, g0, b0 := uint8(r), uint8(g), uint8(b)
36				y, cb, cr := RGBToYCbCr(r0, g0, b0)
37				r1, g1, b1 := YCbCrToRGB(y, cb, cr)
38				if delta(r0, r1) > 2 || delta(g0, g1) > 2 || delta(b0, b1) > 2 {
39					t.Fatalf("\nr0, g0, b0 = %d, %d, %d\ny,  cb, cr = %d, %d, %d\nr1, g1, b1 = %d, %d, %d",
40						r0, g0, b0, y, cb, cr, r1, g1, b1)
41				}
42			}
43		}
44	}
45}
46
47// TestYCbCrToRGBConsistency tests that calling the RGBA method (16 bit color)
48// then truncating to 8 bits is equivalent to calling the YCbCrToRGB function (8
49// bit color).
50func TestYCbCrToRGBConsistency(t *testing.T) {
51	for y := 0; y < 256; y += 7 {
52		for cb := 0; cb < 256; cb += 5 {
53			for cr := 0; cr < 256; cr += 3 {
54				x := YCbCr{uint8(y), uint8(cb), uint8(cr)}
55				r0, g0, b0, _ := x.RGBA()
56				r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8)
57				r2, g2, b2 := YCbCrToRGB(x.Y, x.Cb, x.Cr)
58				if r1 != r2 || g1 != g2 || b1 != b2 {
59					t.Fatalf("y, cb, cr = %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d",
60						y, cb, cr, r1, g1, b1, r2, g2, b2)
61				}
62			}
63		}
64	}
65}
66
67// TestYCbCrGray tests that YCbCr colors are a superset of Gray colors.
68func TestYCbCrGray(t *testing.T) {
69	for i := 0; i < 256; i++ {
70		if err := eq(YCbCr{uint8(i), 0x80, 0x80}, Gray{uint8(i)}); err != nil {
71			t.Errorf("i=0x%02x:\n%v", i, err)
72		}
73	}
74}
75
76// TestCMYKRoundtrip tests that a subset of RGB space can be converted to CMYK
77// and back to within 1/256 tolerance.
78func TestCMYKRoundtrip(t *testing.T) {
79	for r := 0; r < 256; r += 7 {
80		for g := 0; g < 256; g += 5 {
81			for b := 0; b < 256; b += 3 {
82				r0, g0, b0 := uint8(r), uint8(g), uint8(b)
83				c, m, y, k := RGBToCMYK(r0, g0, b0)
84				r1, g1, b1 := CMYKToRGB(c, m, y, k)
85				if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 {
86					t.Fatalf("\nr0, g0, b0 = %d, %d, %d\nc, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d",
87						r0, g0, b0, c, m, y, k, r1, g1, b1)
88				}
89			}
90		}
91	}
92}
93
94// TestCMYKToRGBConsistency tests that calling the RGBA method (16 bit color)
95// then truncating to 8 bits is equivalent to calling the CMYKToRGB function (8
96// bit color).
97func TestCMYKToRGBConsistency(t *testing.T) {
98	for c := 0; c < 256; c += 7 {
99		for m := 0; m < 256; m += 5 {
100			for y := 0; y < 256; y += 3 {
101				for k := 0; k < 256; k += 11 {
102					x := CMYK{uint8(c), uint8(m), uint8(y), uint8(k)}
103					r0, g0, b0, _ := x.RGBA()
104					r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8)
105					r2, g2, b2 := CMYKToRGB(x.C, x.M, x.Y, x.K)
106					if r1 != r2 || g1 != g2 || b1 != b2 {
107						t.Fatalf("c, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d",
108							c, m, y, k, r1, g1, b1, r2, g2, b2)
109					}
110				}
111			}
112		}
113	}
114}
115
116// TestCMYKGray tests that CMYK colors are a superset of Gray colors.
117func TestCMYKGray(t *testing.T) {
118	for i := 0; i < 256; i++ {
119		if err := eq(CMYK{0x00, 0x00, 0x00, uint8(255 - i)}, Gray{uint8(i)}); err != nil {
120			t.Errorf("i=0x%02x:\n%v", i, err)
121		}
122	}
123}
124
125func TestPalette(t *testing.T) {
126	p := Palette{
127		RGBA{0xff, 0xff, 0xff, 0xff},
128		RGBA{0x80, 0x00, 0x00, 0xff},
129		RGBA{0x7f, 0x00, 0x00, 0x7f},
130		RGBA{0x00, 0x00, 0x00, 0x7f},
131		RGBA{0x00, 0x00, 0x00, 0x00},
132		RGBA{0x40, 0x40, 0x40, 0x40},
133	}
134	// Check that, for a Palette with no repeated colors, the closest color to
135	// each element is itself.
136	for i, c := range p {
137		j := p.Index(c)
138		if i != j {
139			t.Errorf("Index(%v): got %d (color = %v), want %d", c, j, p[j], i)
140		}
141	}
142	// Check that finding the closest color considers alpha, not just red,
143	// green and blue.
144	got := p.Convert(RGBA{0x80, 0x00, 0x00, 0x80})
145	want := RGBA{0x7f, 0x00, 0x00, 0x7f}
146	if got != want {
147		t.Errorf("got %v, want %v", got, want)
148	}
149}
150