Black Lives Matter. Support the Equal Justice Initiative.

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