| // Copyright 2011 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package color | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | 	"testing" | 
 | ) | 
 |  | 
 | func delta(x, y uint8) uint8 { | 
 | 	if x >= y { | 
 | 		return x - y | 
 | 	} | 
 | 	return y - x | 
 | } | 
 |  | 
 | func eq(c0, c1 Color) error { | 
 | 	r0, g0, b0, a0 := c0.RGBA() | 
 | 	r1, g1, b1, a1 := c1.RGBA() | 
 | 	if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 { | 
 | 		return fmt.Errorf("got  0x%04x 0x%04x 0x%04x 0x%04x\nwant 0x%04x 0x%04x 0x%04x 0x%04x", | 
 | 			r0, g0, b0, a0, r1, g1, b1, a1) | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // TestYCbCrRoundtrip tests that a subset of RGB space can be converted to YCbCr | 
 | // and back to within 2/256 tolerance. | 
 | func TestYCbCrRoundtrip(t *testing.T) { | 
 | 	for r := 0; r < 256; r += 7 { | 
 | 		for g := 0; g < 256; g += 5 { | 
 | 			for b := 0; b < 256; b += 3 { | 
 | 				r0, g0, b0 := uint8(r), uint8(g), uint8(b) | 
 | 				y, cb, cr := RGBToYCbCr(r0, g0, b0) | 
 | 				r1, g1, b1 := YCbCrToRGB(y, cb, cr) | 
 | 				if delta(r0, r1) > 2 || delta(g0, g1) > 2 || delta(b0, b1) > 2 { | 
 | 					t.Fatalf("\nr0, g0, b0 = %d, %d, %d\ny,  cb, cr = %d, %d, %d\nr1, g1, b1 = %d, %d, %d", | 
 | 						r0, g0, b0, y, cb, cr, r1, g1, b1) | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // TestYCbCrToRGBConsistency tests that calling the RGBA method (16 bit color) | 
 | // then truncating to 8 bits is equivalent to calling the YCbCrToRGB function (8 | 
 | // bit color). | 
 | func TestYCbCrToRGBConsistency(t *testing.T) { | 
 | 	for y := 0; y < 256; y += 7 { | 
 | 		for cb := 0; cb < 256; cb += 5 { | 
 | 			for cr := 0; cr < 256; cr += 3 { | 
 | 				x := YCbCr{uint8(y), uint8(cb), uint8(cr)} | 
 | 				r0, g0, b0, _ := x.RGBA() | 
 | 				r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8) | 
 | 				r2, g2, b2 := YCbCrToRGB(x.Y, x.Cb, x.Cr) | 
 | 				if r1 != r2 || g1 != g2 || b1 != b2 { | 
 | 					t.Fatalf("y, cb, cr = %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d", | 
 | 						y, cb, cr, r1, g1, b1, r2, g2, b2) | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // TestYCbCrGray tests that YCbCr colors are a superset of Gray colors. | 
 | func TestYCbCrGray(t *testing.T) { | 
 | 	for i := 0; i < 256; i++ { | 
 | 		c0 := YCbCr{uint8(i), 0x80, 0x80} | 
 | 		c1 := Gray{uint8(i)} | 
 | 		if err := eq(c0, c1); err != nil { | 
 | 			t.Errorf("i=0x%02x:\n%v", i, err) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // TestNYCbCrAAlpha tests that NYCbCrA colors are a superset of Alpha colors. | 
 | func TestNYCbCrAAlpha(t *testing.T) { | 
 | 	for i := 0; i < 256; i++ { | 
 | 		c0 := NYCbCrA{YCbCr{0xff, 0x80, 0x80}, uint8(i)} | 
 | 		c1 := Alpha{uint8(i)} | 
 | 		if err := eq(c0, c1); err != nil { | 
 | 			t.Errorf("i=0x%02x:\n%v", i, err) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // TestNYCbCrAYCbCr tests that NYCbCrA colors are a superset of YCbCr colors. | 
 | func TestNYCbCrAYCbCr(t *testing.T) { | 
 | 	for i := 0; i < 256; i++ { | 
 | 		c0 := NYCbCrA{YCbCr{uint8(i), 0x40, 0xc0}, 0xff} | 
 | 		c1 := YCbCr{uint8(i), 0x40, 0xc0} | 
 | 		if err := eq(c0, c1); err != nil { | 
 | 			t.Errorf("i=0x%02x:\n%v", i, err) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // TestCMYKRoundtrip tests that a subset of RGB space can be converted to CMYK | 
 | // and back to within 1/256 tolerance. | 
 | func TestCMYKRoundtrip(t *testing.T) { | 
 | 	for r := 0; r < 256; r += 7 { | 
 | 		for g := 0; g < 256; g += 5 { | 
 | 			for b := 0; b < 256; b += 3 { | 
 | 				r0, g0, b0 := uint8(r), uint8(g), uint8(b) | 
 | 				c, m, y, k := RGBToCMYK(r0, g0, b0) | 
 | 				r1, g1, b1 := CMYKToRGB(c, m, y, k) | 
 | 				if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 { | 
 | 					t.Fatalf("\nr0, g0, b0 = %d, %d, %d\nc, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d", | 
 | 						r0, g0, b0, c, m, y, k, r1, g1, b1) | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // TestCMYKToRGBConsistency tests that calling the RGBA method (16 bit color) | 
 | // then truncating to 8 bits is equivalent to calling the CMYKToRGB function (8 | 
 | // bit color). | 
 | func TestCMYKToRGBConsistency(t *testing.T) { | 
 | 	for c := 0; c < 256; c += 7 { | 
 | 		for m := 0; m < 256; m += 5 { | 
 | 			for y := 0; y < 256; y += 3 { | 
 | 				for k := 0; k < 256; k += 11 { | 
 | 					x := CMYK{uint8(c), uint8(m), uint8(y), uint8(k)} | 
 | 					r0, g0, b0, _ := x.RGBA() | 
 | 					r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8) | 
 | 					r2, g2, b2 := CMYKToRGB(x.C, x.M, x.Y, x.K) | 
 | 					if r1 != r2 || g1 != g2 || b1 != b2 { | 
 | 						t.Fatalf("c, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d", | 
 | 							c, m, y, k, r1, g1, b1, r2, g2, b2) | 
 | 					} | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // TestCMYKGray tests that CMYK colors are a superset of Gray colors. | 
 | func TestCMYKGray(t *testing.T) { | 
 | 	for i := 0; i < 256; i++ { | 
 | 		if err := eq(CMYK{0x00, 0x00, 0x00, uint8(255 - i)}, Gray{uint8(i)}); err != nil { | 
 | 			t.Errorf("i=0x%02x:\n%v", i, err) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestPalette(t *testing.T) { | 
 | 	p := Palette{ | 
 | 		RGBA{0xff, 0xff, 0xff, 0xff}, | 
 | 		RGBA{0x80, 0x00, 0x00, 0xff}, | 
 | 		RGBA{0x7f, 0x00, 0x00, 0x7f}, | 
 | 		RGBA{0x00, 0x00, 0x00, 0x7f}, | 
 | 		RGBA{0x00, 0x00, 0x00, 0x00}, | 
 | 		RGBA{0x40, 0x40, 0x40, 0x40}, | 
 | 	} | 
 | 	// Check that, for a Palette with no repeated colors, the closest color to | 
 | 	// each element is itself. | 
 | 	for i, c := range p { | 
 | 		j := p.Index(c) | 
 | 		if i != j { | 
 | 			t.Errorf("Index(%v): got %d (color = %v), want %d", c, j, p[j], i) | 
 | 		} | 
 | 	} | 
 | 	// Check that finding the closest color considers alpha, not just red, | 
 | 	// green and blue. | 
 | 	got := p.Convert(RGBA{0x80, 0x00, 0x00, 0x80}) | 
 | 	want := RGBA{0x7f, 0x00, 0x00, 0x7f} | 
 | 	if got != want { | 
 | 		t.Errorf("got %v, want %v", got, want) | 
 | 	} | 
 | } | 
 |  | 
 | var sink8 uint8 | 
 | var sink32 uint32 | 
 |  | 
 | func BenchmarkYCbCrToRGB(b *testing.B) { | 
 | 	// YCbCrToRGB does saturating arithmetic. | 
 | 	// Low, middle, and high values can take | 
 | 	// different paths through the generated code. | 
 | 	b.Run("0", func(b *testing.B) { | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink8, sink8, sink8 = YCbCrToRGB(0, 0, 0) | 
 | 		} | 
 | 	}) | 
 | 	b.Run("128", func(b *testing.B) { | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink8, sink8, sink8 = YCbCrToRGB(128, 128, 128) | 
 | 		} | 
 | 	}) | 
 | 	b.Run("255", func(b *testing.B) { | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink8, sink8, sink8 = YCbCrToRGB(255, 255, 255) | 
 | 		} | 
 | 	}) | 
 | } | 
 |  | 
 | func BenchmarkRGBToYCbCr(b *testing.B) { | 
 | 	// RGBToYCbCr does saturating arithmetic. | 
 | 	// Different values can take different paths | 
 | 	// through the generated code. | 
 | 	b.Run("0", func(b *testing.B) { | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink8, sink8, sink8 = RGBToYCbCr(0, 0, 0) | 
 | 		} | 
 | 	}) | 
 | 	b.Run("Cb", func(b *testing.B) { | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink8, sink8, sink8 = RGBToYCbCr(0, 0, 255) | 
 | 		} | 
 | 	}) | 
 | 	b.Run("Cr", func(b *testing.B) { | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink8, sink8, sink8 = RGBToYCbCr(255, 0, 0) | 
 | 		} | 
 | 	}) | 
 | } | 
 |  | 
 | func BenchmarkYCbCrToRGBA(b *testing.B) { | 
 | 	// RGB does saturating arithmetic. | 
 | 	// Low, middle, and high values can take | 
 | 	// different paths through the generated code. | 
 | 	b.Run("0", func(b *testing.B) { | 
 | 		c := YCbCr{0, 0, 0} | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink32, sink32, sink32, sink32 = c.RGBA() | 
 | 		} | 
 | 	}) | 
 | 	b.Run("128", func(b *testing.B) { | 
 | 		c := YCbCr{128, 128, 128} | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink32, sink32, sink32, sink32 = c.RGBA() | 
 | 		} | 
 | 	}) | 
 | 	b.Run("255", func(b *testing.B) { | 
 | 		c := YCbCr{255, 255, 255} | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink32, sink32, sink32, sink32 = c.RGBA() | 
 | 		} | 
 | 	}) | 
 | } | 
 |  | 
 | func BenchmarkNYCbCrAToRGBA(b *testing.B) { | 
 | 	// RGBA does saturating arithmetic. | 
 | 	// Low, middle, and high values can take | 
 | 	// different paths through the generated code. | 
 | 	b.Run("0", func(b *testing.B) { | 
 | 		c := NYCbCrA{YCbCr{0, 0, 0}, 0xff} | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink32, sink32, sink32, sink32 = c.RGBA() | 
 | 		} | 
 | 	}) | 
 | 	b.Run("128", func(b *testing.B) { | 
 | 		c := NYCbCrA{YCbCr{128, 128, 128}, 0xff} | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink32, sink32, sink32, sink32 = c.RGBA() | 
 | 		} | 
 | 	}) | 
 | 	b.Run("255", func(b *testing.B) { | 
 | 		c := NYCbCrA{YCbCr{255, 255, 255}, 0xff} | 
 | 		for i := 0; i < b.N; i++ { | 
 | 			sink32, sink32, sink32, sink32 = c.RGBA() | 
 | 		} | 
 | 	}) | 
 | } |