github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/src/image/color/ycbcr_test.go (about) 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 }