...

Source file src/image/color/ycbcr_test.go

Documentation: image/color

		 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  
		 5  package color
		 6  
		 7  import (
		 8  	"fmt"
		 9  	"testing"
		10  )
		11  
		12  func delta(x, y uint8) uint8 {
		13  	if x >= y {
		14  		return x - y
		15  	}
		16  	return y - x
		17  }
		18  
		19  func 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.
		31  func 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).
		50  func 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.
		68  func TestYCbCrGray(t *testing.T) {
		69  	for i := 0; i < 256; i++ {
		70  		c0 := YCbCr{uint8(i), 0x80, 0x80}
		71  		c1 := Gray{uint8(i)}
		72  		if err := eq(c0, c1); err != nil {
		73  			t.Errorf("i=0x%02x:\n%v", i, err)
		74  		}
		75  	}
		76  }
		77  
		78  // TestNYCbCrAAlpha tests that NYCbCrA colors are a superset of Alpha colors.
		79  func TestNYCbCrAAlpha(t *testing.T) {
		80  	for i := 0; i < 256; i++ {
		81  		c0 := NYCbCrA{YCbCr{0xff, 0x80, 0x80}, uint8(i)}
		82  		c1 := Alpha{uint8(i)}
		83  		if err := eq(c0, c1); err != nil {
		84  			t.Errorf("i=0x%02x:\n%v", i, err)
		85  		}
		86  	}
		87  }
		88  
		89  // TestNYCbCrAYCbCr tests that NYCbCrA colors are a superset of YCbCr colors.
		90  func TestNYCbCrAYCbCr(t *testing.T) {
		91  	for i := 0; i < 256; i++ {
		92  		c0 := NYCbCrA{YCbCr{uint8(i), 0x40, 0xc0}, 0xff}
		93  		c1 := YCbCr{uint8(i), 0x40, 0xc0}
		94  		if err := eq(c0, c1); err != nil {
		95  			t.Errorf("i=0x%02x:\n%v", i, err)
		96  		}
		97  	}
		98  }
		99  
	 100  // TestCMYKRoundtrip tests that a subset of RGB space can be converted to CMYK
	 101  // and back to within 1/256 tolerance.
	 102  func TestCMYKRoundtrip(t *testing.T) {
	 103  	for r := 0; r < 256; r += 7 {
	 104  		for g := 0; g < 256; g += 5 {
	 105  			for b := 0; b < 256; b += 3 {
	 106  				r0, g0, b0 := uint8(r), uint8(g), uint8(b)
	 107  				c, m, y, k := RGBToCMYK(r0, g0, b0)
	 108  				r1, g1, b1 := CMYKToRGB(c, m, y, k)
	 109  				if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 {
	 110  					t.Fatalf("\nr0, g0, b0 = %d, %d, %d\nc, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d",
	 111  						r0, g0, b0, c, m, y, k, r1, g1, b1)
	 112  				}
	 113  			}
	 114  		}
	 115  	}
	 116  }
	 117  
	 118  // TestCMYKToRGBConsistency tests that calling the RGBA method (16 bit color)
	 119  // then truncating to 8 bits is equivalent to calling the CMYKToRGB function (8
	 120  // bit color).
	 121  func TestCMYKToRGBConsistency(t *testing.T) {
	 122  	for c := 0; c < 256; c += 7 {
	 123  		for m := 0; m < 256; m += 5 {
	 124  			for y := 0; y < 256; y += 3 {
	 125  				for k := 0; k < 256; k += 11 {
	 126  					x := CMYK{uint8(c), uint8(m), uint8(y), uint8(k)}
	 127  					r0, g0, b0, _ := x.RGBA()
	 128  					r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8)
	 129  					r2, g2, b2 := CMYKToRGB(x.C, x.M, x.Y, x.K)
	 130  					if r1 != r2 || g1 != g2 || b1 != b2 {
	 131  						t.Fatalf("c, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d",
	 132  							c, m, y, k, r1, g1, b1, r2, g2, b2)
	 133  					}
	 134  				}
	 135  			}
	 136  		}
	 137  	}
	 138  }
	 139  
	 140  // TestCMYKGray tests that CMYK colors are a superset of Gray colors.
	 141  func TestCMYKGray(t *testing.T) {
	 142  	for i := 0; i < 256; i++ {
	 143  		if err := eq(CMYK{0x00, 0x00, 0x00, uint8(255 - i)}, Gray{uint8(i)}); err != nil {
	 144  			t.Errorf("i=0x%02x:\n%v", i, err)
	 145  		}
	 146  	}
	 147  }
	 148  
	 149  func TestPalette(t *testing.T) {
	 150  	p := Palette{
	 151  		RGBA{0xff, 0xff, 0xff, 0xff},
	 152  		RGBA{0x80, 0x00, 0x00, 0xff},
	 153  		RGBA{0x7f, 0x00, 0x00, 0x7f},
	 154  		RGBA{0x00, 0x00, 0x00, 0x7f},
	 155  		RGBA{0x00, 0x00, 0x00, 0x00},
	 156  		RGBA{0x40, 0x40, 0x40, 0x40},
	 157  	}
	 158  	// Check that, for a Palette with no repeated colors, the closest color to
	 159  	// each element is itself.
	 160  	for i, c := range p {
	 161  		j := p.Index(c)
	 162  		if i != j {
	 163  			t.Errorf("Index(%v): got %d (color = %v), want %d", c, j, p[j], i)
	 164  		}
	 165  	}
	 166  	// Check that finding the closest color considers alpha, not just red,
	 167  	// green and blue.
	 168  	got := p.Convert(RGBA{0x80, 0x00, 0x00, 0x80})
	 169  	want := RGBA{0x7f, 0x00, 0x00, 0x7f}
	 170  	if got != want {
	 171  		t.Errorf("got %v, want %v", got, want)
	 172  	}
	 173  }
	 174  
	 175  var sink8 uint8
	 176  var sink32 uint32
	 177  
	 178  func BenchmarkYCbCrToRGB(b *testing.B) {
	 179  	// YCbCrToRGB does saturating arithmetic.
	 180  	// Low, middle, and high values can take
	 181  	// different paths through the generated code.
	 182  	b.Run("0", func(b *testing.B) {
	 183  		for i := 0; i < b.N; i++ {
	 184  			sink8, sink8, sink8 = YCbCrToRGB(0, 0, 0)
	 185  		}
	 186  	})
	 187  	b.Run("128", func(b *testing.B) {
	 188  		for i := 0; i < b.N; i++ {
	 189  			sink8, sink8, sink8 = YCbCrToRGB(128, 128, 128)
	 190  		}
	 191  	})
	 192  	b.Run("255", func(b *testing.B) {
	 193  		for i := 0; i < b.N; i++ {
	 194  			sink8, sink8, sink8 = YCbCrToRGB(255, 255, 255)
	 195  		}
	 196  	})
	 197  }
	 198  
	 199  func BenchmarkRGBToYCbCr(b *testing.B) {
	 200  	// RGBToYCbCr does saturating arithmetic.
	 201  	// Different values can take different paths
	 202  	// through the generated code.
	 203  	b.Run("0", func(b *testing.B) {
	 204  		for i := 0; i < b.N; i++ {
	 205  			sink8, sink8, sink8 = RGBToYCbCr(0, 0, 0)
	 206  		}
	 207  	})
	 208  	b.Run("Cb", func(b *testing.B) {
	 209  		for i := 0; i < b.N; i++ {
	 210  			sink8, sink8, sink8 = RGBToYCbCr(0, 0, 255)
	 211  		}
	 212  	})
	 213  	b.Run("Cr", func(b *testing.B) {
	 214  		for i := 0; i < b.N; i++ {
	 215  			sink8, sink8, sink8 = RGBToYCbCr(255, 0, 0)
	 216  		}
	 217  	})
	 218  }
	 219  
	 220  func BenchmarkYCbCrToRGBA(b *testing.B) {
	 221  	// RGB does saturating arithmetic.
	 222  	// Low, middle, and high values can take
	 223  	// different paths through the generated code.
	 224  	b.Run("0", func(b *testing.B) {
	 225  		c := YCbCr{0, 0, 0}
	 226  		for i := 0; i < b.N; i++ {
	 227  			sink32, sink32, sink32, sink32 = c.RGBA()
	 228  		}
	 229  	})
	 230  	b.Run("128", func(b *testing.B) {
	 231  		c := YCbCr{128, 128, 128}
	 232  		for i := 0; i < b.N; i++ {
	 233  			sink32, sink32, sink32, sink32 = c.RGBA()
	 234  		}
	 235  	})
	 236  	b.Run("255", func(b *testing.B) {
	 237  		c := YCbCr{255, 255, 255}
	 238  		for i := 0; i < b.N; i++ {
	 239  			sink32, sink32, sink32, sink32 = c.RGBA()
	 240  		}
	 241  	})
	 242  }
	 243  
	 244  func BenchmarkNYCbCrAToRGBA(b *testing.B) {
	 245  	// RGBA does saturating arithmetic.
	 246  	// Low, middle, and high values can take
	 247  	// different paths through the generated code.
	 248  	b.Run("0", func(b *testing.B) {
	 249  		c := NYCbCrA{YCbCr{0, 0, 0}, 0xff}
	 250  		for i := 0; i < b.N; i++ {
	 251  			sink32, sink32, sink32, sink32 = c.RGBA()
	 252  		}
	 253  	})
	 254  	b.Run("128", func(b *testing.B) {
	 255  		c := NYCbCrA{YCbCr{128, 128, 128}, 0xff}
	 256  		for i := 0; i < b.N; i++ {
	 257  			sink32, sink32, sink32, sink32 = c.RGBA()
	 258  		}
	 259  	})
	 260  	b.Run("255", func(b *testing.B) {
	 261  		c := NYCbCrA{YCbCr{255, 255, 255}, 0xff}
	 262  		for i := 0; i < b.N; i++ {
	 263  			sink32, sink32, sink32, sink32 = c.RGBA()
	 264  		}
	 265  	})
	 266  }
	 267  

View as plain text