...
Run Format

Source file src/image/draw/draw_test.go

Documentation: image/draw

     1  // Copyright 2010 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 draw
     6  
     7  import (
     8  	"image"
     9  	"image/color"
    10  	"image/png"
    11  	"os"
    12  	"testing"
    13  	"testing/quick"
    14  )
    15  
    16  func eq(c0, c1 color.Color) bool {
    17  	r0, g0, b0, a0 := c0.RGBA()
    18  	r1, g1, b1, a1 := c1.RGBA()
    19  	return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
    20  }
    21  
    22  func fillBlue(alpha int) image.Image {
    23  	return image.NewUniform(color.RGBA{0, 0, uint8(alpha), uint8(alpha)})
    24  }
    25  
    26  func fillAlpha(alpha int) image.Image {
    27  	return image.NewUniform(color.Alpha{uint8(alpha)})
    28  }
    29  
    30  func vgradGreen(alpha int) image.Image {
    31  	m := image.NewRGBA(image.Rect(0, 0, 16, 16))
    32  	for y := 0; y < 16; y++ {
    33  		for x := 0; x < 16; x++ {
    34  			m.Set(x, y, color.RGBA{0, uint8(y * alpha / 15), 0, uint8(alpha)})
    35  		}
    36  	}
    37  	return m
    38  }
    39  
    40  func vgradAlpha(alpha int) image.Image {
    41  	m := image.NewAlpha(image.Rect(0, 0, 16, 16))
    42  	for y := 0; y < 16; y++ {
    43  		for x := 0; x < 16; x++ {
    44  			m.Set(x, y, color.Alpha{uint8(y * alpha / 15)})
    45  		}
    46  	}
    47  	return m
    48  }
    49  
    50  func vgradGreenNRGBA(alpha int) image.Image {
    51  	m := image.NewNRGBA(image.Rect(0, 0, 16, 16))
    52  	for y := 0; y < 16; y++ {
    53  		for x := 0; x < 16; x++ {
    54  			m.Set(x, y, color.RGBA{0, uint8(y * 0x11), 0, uint8(alpha)})
    55  		}
    56  	}
    57  	return m
    58  }
    59  
    60  func vgradCr() image.Image {
    61  	m := &image.YCbCr{
    62  		Y:              make([]byte, 16*16),
    63  		Cb:             make([]byte, 16*16),
    64  		Cr:             make([]byte, 16*16),
    65  		YStride:        16,
    66  		CStride:        16,
    67  		SubsampleRatio: image.YCbCrSubsampleRatio444,
    68  		Rect:           image.Rect(0, 0, 16, 16),
    69  	}
    70  	for y := 0; y < 16; y++ {
    71  		for x := 0; x < 16; x++ {
    72  			m.Cr[y*m.CStride+x] = uint8(y * 0x11)
    73  		}
    74  	}
    75  	return m
    76  }
    77  
    78  func vgradGray() image.Image {
    79  	m := image.NewGray(image.Rect(0, 0, 16, 16))
    80  	for y := 0; y < 16; y++ {
    81  		for x := 0; x < 16; x++ {
    82  			m.Set(x, y, color.Gray{uint8(y * 0x11)})
    83  		}
    84  	}
    85  	return m
    86  }
    87  
    88  func vgradMagenta() image.Image {
    89  	m := image.NewCMYK(image.Rect(0, 0, 16, 16))
    90  	for y := 0; y < 16; y++ {
    91  		for x := 0; x < 16; x++ {
    92  			m.Set(x, y, color.CMYK{0, uint8(y * 0x11), 0, 0x3f})
    93  		}
    94  	}
    95  	return m
    96  }
    97  
    98  func hgradRed(alpha int) Image {
    99  	m := image.NewRGBA(image.Rect(0, 0, 16, 16))
   100  	for y := 0; y < 16; y++ {
   101  		for x := 0; x < 16; x++ {
   102  			m.Set(x, y, color.RGBA{uint8(x * alpha / 15), 0, 0, uint8(alpha)})
   103  		}
   104  	}
   105  	return m
   106  }
   107  
   108  func gradYellow(alpha int) Image {
   109  	m := image.NewRGBA(image.Rect(0, 0, 16, 16))
   110  	for y := 0; y < 16; y++ {
   111  		for x := 0; x < 16; x++ {
   112  			m.Set(x, y, color.RGBA{uint8(x * alpha / 15), uint8(y * alpha / 15), 0, uint8(alpha)})
   113  		}
   114  	}
   115  	return m
   116  }
   117  
   118  type drawTest struct {
   119  	desc     string
   120  	src      image.Image
   121  	mask     image.Image
   122  	op       Op
   123  	expected color.Color
   124  }
   125  
   126  var drawTests = []drawTest{
   127  	// Uniform mask (0% opaque).
   128  	{"nop", vgradGreen(255), fillAlpha(0), Over, color.RGBA{136, 0, 0, 255}},
   129  	{"clear", vgradGreen(255), fillAlpha(0), Src, color.RGBA{0, 0, 0, 0}},
   130  	// Uniform mask (100%, 75%, nil) and uniform source.
   131  	// At (x, y) == (8, 8):
   132  	// The destination pixel is {136, 0, 0, 255}.
   133  	// The source pixel is {0, 0, 90, 90}.
   134  	{"fill", fillBlue(90), fillAlpha(255), Over, color.RGBA{88, 0, 90, 255}},
   135  	{"fillSrc", fillBlue(90), fillAlpha(255), Src, color.RGBA{0, 0, 90, 90}},
   136  	{"fillAlpha", fillBlue(90), fillAlpha(192), Over, color.RGBA{100, 0, 68, 255}},
   137  	{"fillAlphaSrc", fillBlue(90), fillAlpha(192), Src, color.RGBA{0, 0, 68, 68}},
   138  	{"fillNil", fillBlue(90), nil, Over, color.RGBA{88, 0, 90, 255}},
   139  	{"fillNilSrc", fillBlue(90), nil, Src, color.RGBA{0, 0, 90, 90}},
   140  	// Uniform mask (100%, 75%, nil) and variable source.
   141  	// At (x, y) == (8, 8):
   142  	// The destination pixel is {136, 0, 0, 255}.
   143  	// The source pixel is {0, 48, 0, 90}.
   144  	{"copy", vgradGreen(90), fillAlpha(255), Over, color.RGBA{88, 48, 0, 255}},
   145  	{"copySrc", vgradGreen(90), fillAlpha(255), Src, color.RGBA{0, 48, 0, 90}},
   146  	{"copyAlpha", vgradGreen(90), fillAlpha(192), Over, color.RGBA{100, 36, 0, 255}},
   147  	{"copyAlphaSrc", vgradGreen(90), fillAlpha(192), Src, color.RGBA{0, 36, 0, 68}},
   148  	{"copyNil", vgradGreen(90), nil, Over, color.RGBA{88, 48, 0, 255}},
   149  	{"copyNilSrc", vgradGreen(90), nil, Src, color.RGBA{0, 48, 0, 90}},
   150  	// Uniform mask (100%, 75%, nil) and variable NRGBA source.
   151  	// At (x, y) == (8, 8):
   152  	// The destination pixel is {136, 0, 0, 255}.
   153  	// The source pixel is {0, 136, 0, 90} in NRGBA-space, which is {0, 48, 0, 90} in RGBA-space.
   154  	// The result pixel is different than in the "copy*" test cases because of rounding errors.
   155  	{"nrgba", vgradGreenNRGBA(90), fillAlpha(255), Over, color.RGBA{88, 46, 0, 255}},
   156  	{"nrgbaSrc", vgradGreenNRGBA(90), fillAlpha(255), Src, color.RGBA{0, 46, 0, 90}},
   157  	{"nrgbaAlpha", vgradGreenNRGBA(90), fillAlpha(192), Over, color.RGBA{100, 34, 0, 255}},
   158  	{"nrgbaAlphaSrc", vgradGreenNRGBA(90), fillAlpha(192), Src, color.RGBA{0, 34, 0, 68}},
   159  	{"nrgbaNil", vgradGreenNRGBA(90), nil, Over, color.RGBA{88, 46, 0, 255}},
   160  	{"nrgbaNilSrc", vgradGreenNRGBA(90), nil, Src, color.RGBA{0, 46, 0, 90}},
   161  	// Uniform mask (100%, 75%, nil) and variable YCbCr source.
   162  	// At (x, y) == (8, 8):
   163  	// The destination pixel is {136, 0, 0, 255}.
   164  	// The source pixel is {0, 0, 136} in YCbCr-space, which is {11, 38, 0, 255} in RGB-space.
   165  	{"ycbcr", vgradCr(), fillAlpha(255), Over, color.RGBA{11, 38, 0, 255}},
   166  	{"ycbcrSrc", vgradCr(), fillAlpha(255), Src, color.RGBA{11, 38, 0, 255}},
   167  	{"ycbcrAlpha", vgradCr(), fillAlpha(192), Over, color.RGBA{42, 28, 0, 255}},
   168  	{"ycbcrAlphaSrc", vgradCr(), fillAlpha(192), Src, color.RGBA{8, 28, 0, 192}},
   169  	{"ycbcrNil", vgradCr(), nil, Over, color.RGBA{11, 38, 0, 255}},
   170  	{"ycbcrNilSrc", vgradCr(), nil, Src, color.RGBA{11, 38, 0, 255}},
   171  	// Uniform mask (100%, 75%, nil) and variable Gray source.
   172  	// At (x, y) == (8, 8):
   173  	// The destination pixel is {136, 0, 0, 255}.
   174  	// The source pixel is {136} in Gray-space, which is {136, 136, 136, 255} in RGBA-space.
   175  	{"gray", vgradGray(), fillAlpha(255), Over, color.RGBA{136, 136, 136, 255}},
   176  	{"graySrc", vgradGray(), fillAlpha(255), Src, color.RGBA{136, 136, 136, 255}},
   177  	{"grayAlpha", vgradGray(), fillAlpha(192), Over, color.RGBA{136, 102, 102, 255}},
   178  	{"grayAlphaSrc", vgradGray(), fillAlpha(192), Src, color.RGBA{102, 102, 102, 192}},
   179  	{"grayNil", vgradGray(), nil, Over, color.RGBA{136, 136, 136, 255}},
   180  	{"grayNilSrc", vgradGray(), nil, Src, color.RGBA{136, 136, 136, 255}},
   181  	// Uniform mask (100%, 75%, nil) and variable CMYK source.
   182  	// At (x, y) == (8, 8):
   183  	// The destination pixel is {136, 0, 0, 255}.
   184  	// The source pixel is {0, 136, 0, 63} in CMYK-space, which is {192, 89, 192} in RGB-space.
   185  	{"cmyk", vgradMagenta(), fillAlpha(255), Over, color.RGBA{192, 89, 192, 255}},
   186  	{"cmykSrc", vgradMagenta(), fillAlpha(255), Src, color.RGBA{192, 89, 192, 255}},
   187  	{"cmykAlpha", vgradMagenta(), fillAlpha(192), Over, color.RGBA{178, 67, 145, 255}},
   188  	{"cmykAlphaSrc", vgradMagenta(), fillAlpha(192), Src, color.RGBA{145, 67, 145, 192}},
   189  	{"cmykNil", vgradMagenta(), nil, Over, color.RGBA{192, 89, 192, 255}},
   190  	{"cmykNilSrc", vgradMagenta(), nil, Src, color.RGBA{192, 89, 192, 255}},
   191  	// Variable mask and variable source.
   192  	// At (x, y) == (8, 8):
   193  	// The destination pixel is {136, 0, 0, 255}.
   194  	// The source pixel is {0, 0, 255, 255}.
   195  	// The mask pixel's alpha is 102, or 40%.
   196  	{"generic", fillBlue(255), vgradAlpha(192), Over, color.RGBA{81, 0, 102, 255}},
   197  	{"genericSrc", fillBlue(255), vgradAlpha(192), Src, color.RGBA{0, 0, 102, 102}},
   198  }
   199  
   200  func makeGolden(dst image.Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) image.Image {
   201  	// Since golden is a newly allocated image, we don't have to check if the
   202  	// input source and mask images and the output golden image overlap.
   203  	b := dst.Bounds()
   204  	sb := src.Bounds()
   205  	mb := image.Rect(-1e9, -1e9, 1e9, 1e9)
   206  	if mask != nil {
   207  		mb = mask.Bounds()
   208  	}
   209  	golden := image.NewRGBA(image.Rect(0, 0, b.Max.X, b.Max.Y))
   210  	for y := r.Min.Y; y < r.Max.Y; y++ {
   211  		sy := y + sp.Y - r.Min.Y
   212  		my := y + mp.Y - r.Min.Y
   213  		for x := r.Min.X; x < r.Max.X; x++ {
   214  			if !(image.Pt(x, y).In(b)) {
   215  				continue
   216  			}
   217  			sx := x + sp.X - r.Min.X
   218  			if !(image.Pt(sx, sy).In(sb)) {
   219  				continue
   220  			}
   221  			mx := x + mp.X - r.Min.X
   222  			if !(image.Pt(mx, my).In(mb)) {
   223  				continue
   224  			}
   225  
   226  			const M = 1<<16 - 1
   227  			var dr, dg, db, da uint32
   228  			if op == Over {
   229  				dr, dg, db, da = dst.At(x, y).RGBA()
   230  			}
   231  			sr, sg, sb, sa := src.At(sx, sy).RGBA()
   232  			ma := uint32(M)
   233  			if mask != nil {
   234  				_, _, _, ma = mask.At(mx, my).RGBA()
   235  			}
   236  			a := M - (sa * ma / M)
   237  			golden.Set(x, y, color.RGBA64{
   238  				uint16((dr*a + sr*ma) / M),
   239  				uint16((dg*a + sg*ma) / M),
   240  				uint16((db*a + sb*ma) / M),
   241  				uint16((da*a + sa*ma) / M),
   242  			})
   243  		}
   244  	}
   245  	return golden.SubImage(b)
   246  }
   247  
   248  func TestDraw(t *testing.T) {
   249  	rr := []image.Rectangle{
   250  		image.Rect(0, 0, 0, 0),
   251  		image.Rect(0, 0, 16, 16),
   252  		image.Rect(3, 5, 12, 10),
   253  		image.Rect(0, 0, 9, 9),
   254  		image.Rect(8, 8, 16, 16),
   255  		image.Rect(8, 0, 9, 16),
   256  		image.Rect(0, 8, 16, 9),
   257  		image.Rect(8, 8, 9, 9),
   258  		image.Rect(8, 8, 8, 8),
   259  	}
   260  	for _, r := range rr {
   261  	loop:
   262  		for _, test := range drawTests {
   263  			dst := hgradRed(255).(*image.RGBA).SubImage(r).(Image)
   264  			// Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
   265  			golden := makeGolden(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
   266  			b := dst.Bounds()
   267  			if !b.Eq(golden.Bounds()) {
   268  				t.Errorf("draw %v %s: bounds %v versus %v", r, test.desc, dst.Bounds(), golden.Bounds())
   269  				continue
   270  			}
   271  			// Draw the same combination onto the actual dst using the optimized DrawMask implementation.
   272  			DrawMask(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
   273  			if image.Pt(8, 8).In(r) {
   274  				// Check that the resultant pixel at (8, 8) matches what we expect
   275  				// (the expected value can be verified by hand).
   276  				if !eq(dst.At(8, 8), test.expected) {
   277  					t.Errorf("draw %v %s: at (8, 8) %v versus %v", r, test.desc, dst.At(8, 8), test.expected)
   278  					continue
   279  				}
   280  			}
   281  			// Check that the resultant dst image matches the golden output.
   282  			for y := b.Min.Y; y < b.Max.Y; y++ {
   283  				for x := b.Min.X; x < b.Max.X; x++ {
   284  					if !eq(dst.At(x, y), golden.At(x, y)) {
   285  						t.Errorf("draw %v %s: at (%d, %d), %v versus golden %v", r, test.desc, x, y, dst.At(x, y), golden.At(x, y))
   286  						continue loop
   287  					}
   288  				}
   289  			}
   290  		}
   291  	}
   292  }
   293  
   294  func TestDrawOverlap(t *testing.T) {
   295  	for _, op := range []Op{Over, Src} {
   296  		for yoff := -2; yoff <= 2; yoff++ {
   297  		loop:
   298  			for xoff := -2; xoff <= 2; xoff++ {
   299  				m := gradYellow(127).(*image.RGBA)
   300  				dst := m.SubImage(image.Rect(5, 5, 10, 10)).(*image.RGBA)
   301  				src := m.SubImage(image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff)).(*image.RGBA)
   302  				b := dst.Bounds()
   303  				// Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
   304  				golden := makeGolden(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
   305  				if !b.Eq(golden.Bounds()) {
   306  					t.Errorf("drawOverlap xoff=%d,yoff=%d: bounds %v versus %v", xoff, yoff, dst.Bounds(), golden.Bounds())
   307  					continue
   308  				}
   309  				// Draw the same combination onto the actual dst using the optimized DrawMask implementation.
   310  				DrawMask(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
   311  				// Check that the resultant dst image matches the golden output.
   312  				for y := b.Min.Y; y < b.Max.Y; y++ {
   313  					for x := b.Min.X; x < b.Max.X; x++ {
   314  						if !eq(dst.At(x, y), golden.At(x, y)) {
   315  							t.Errorf("drawOverlap xoff=%d,yoff=%d: at (%d, %d), %v versus golden %v", xoff, yoff, x, y, dst.At(x, y), golden.At(x, y))
   316  							continue loop
   317  						}
   318  					}
   319  				}
   320  			}
   321  		}
   322  	}
   323  }
   324  
   325  // TestNonZeroSrcPt checks drawing with a non-zero src point parameter.
   326  func TestNonZeroSrcPt(t *testing.T) {
   327  	a := image.NewRGBA(image.Rect(0, 0, 1, 1))
   328  	b := image.NewRGBA(image.Rect(0, 0, 2, 2))
   329  	b.Set(0, 0, color.RGBA{0, 0, 0, 5})
   330  	b.Set(1, 0, color.RGBA{0, 0, 5, 5})
   331  	b.Set(0, 1, color.RGBA{0, 5, 0, 5})
   332  	b.Set(1, 1, color.RGBA{5, 0, 0, 5})
   333  	Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1), Over)
   334  	if !eq(color.RGBA{5, 0, 0, 5}, a.At(0, 0)) {
   335  		t.Errorf("non-zero src pt: want %v got %v", color.RGBA{5, 0, 0, 5}, a.At(0, 0))
   336  	}
   337  }
   338  
   339  func TestFill(t *testing.T) {
   340  	rr := []image.Rectangle{
   341  		image.Rect(0, 0, 0, 0),
   342  		image.Rect(0, 0, 40, 30),
   343  		image.Rect(10, 0, 40, 30),
   344  		image.Rect(0, 20, 40, 30),
   345  		image.Rect(10, 20, 40, 30),
   346  		image.Rect(10, 20, 15, 25),
   347  		image.Rect(10, 0, 35, 30),
   348  		image.Rect(0, 15, 40, 16),
   349  		image.Rect(24, 24, 25, 25),
   350  		image.Rect(23, 23, 26, 26),
   351  		image.Rect(22, 22, 27, 27),
   352  		image.Rect(21, 21, 28, 28),
   353  		image.Rect(20, 20, 29, 29),
   354  	}
   355  	for _, r := range rr {
   356  		m := image.NewRGBA(image.Rect(0, 0, 40, 30)).SubImage(r).(*image.RGBA)
   357  		b := m.Bounds()
   358  		c := color.RGBA{11, 0, 0, 255}
   359  		src := &image.Uniform{C: c}
   360  		check := func(desc string) {
   361  			for y := b.Min.Y; y < b.Max.Y; y++ {
   362  				for x := b.Min.X; x < b.Max.X; x++ {
   363  					if !eq(c, m.At(x, y)) {
   364  						t.Errorf("%s fill: at (%d, %d), sub-image bounds=%v: want %v got %v", desc, x, y, r, c, m.At(x, y))
   365  						return
   366  					}
   367  				}
   368  			}
   369  		}
   370  		// Draw 1 pixel at a time.
   371  		for y := b.Min.Y; y < b.Max.Y; y++ {
   372  			for x := b.Min.X; x < b.Max.X; x++ {
   373  				DrawMask(m, image.Rect(x, y, x+1, y+1), src, image.ZP, nil, image.ZP, Src)
   374  			}
   375  		}
   376  		check("pixel")
   377  		// Draw 1 row at a time.
   378  		c = color.RGBA{0, 22, 0, 255}
   379  		src = &image.Uniform{C: c}
   380  		for y := b.Min.Y; y < b.Max.Y; y++ {
   381  			DrawMask(m, image.Rect(b.Min.X, y, b.Max.X, y+1), src, image.ZP, nil, image.ZP, Src)
   382  		}
   383  		check("row")
   384  		// Draw 1 column at a time.
   385  		c = color.RGBA{0, 0, 33, 255}
   386  		src = &image.Uniform{C: c}
   387  		for x := b.Min.X; x < b.Max.X; x++ {
   388  			DrawMask(m, image.Rect(x, b.Min.Y, x+1, b.Max.Y), src, image.ZP, nil, image.ZP, Src)
   389  		}
   390  		check("column")
   391  		// Draw the whole image at once.
   392  		c = color.RGBA{44, 55, 66, 77}
   393  		src = &image.Uniform{C: c}
   394  		DrawMask(m, b, src, image.ZP, nil, image.ZP, Src)
   395  		check("whole")
   396  	}
   397  }
   398  
   399  // TestFloydSteinbergCheckerboard tests that the result of Floyd-Steinberg
   400  // error diffusion of a uniform 50% gray source image with a black-and-white
   401  // palette is a checkerboard pattern.
   402  func TestFloydSteinbergCheckerboard(t *testing.T) {
   403  	b := image.Rect(0, 0, 640, 480)
   404  	// We can't represent 50% exactly, but 0x7fff / 0xffff is close enough.
   405  	src := &image.Uniform{color.Gray16{0x7fff}}
   406  	dst := image.NewPaletted(b, color.Palette{color.Black, color.White})
   407  	FloydSteinberg.Draw(dst, b, src, image.Point{})
   408  	nErr := 0
   409  	for y := b.Min.Y; y < b.Max.Y; y++ {
   410  		for x := b.Min.X; x < b.Max.X; x++ {
   411  			got := dst.Pix[dst.PixOffset(x, y)]
   412  			want := uint8(x+y) % 2
   413  			if got != want {
   414  				t.Errorf("at (%d, %d): got %d, want %d", x, y, got, want)
   415  				if nErr++; nErr == 10 {
   416  					t.Fatal("there may be more errors")
   417  				}
   418  			}
   419  		}
   420  	}
   421  }
   422  
   423  // embeddedPaletted is an Image that behaves like an *image.Paletted but whose
   424  // type is not *image.Paletted.
   425  type embeddedPaletted struct {
   426  	*image.Paletted
   427  }
   428  
   429  // TestPaletted tests that the drawPaletted function behaves the same
   430  // regardless of whether dst is an *image.Paletted.
   431  func TestPaletted(t *testing.T) {
   432  	f, err := os.Open("../testdata/video-001.png")
   433  	if err != nil {
   434  		t.Fatalf("open: %v", err)
   435  	}
   436  	defer f.Close()
   437  	src, err := png.Decode(f)
   438  	if err != nil {
   439  		t.Fatalf("decode: %v", err)
   440  	}
   441  	b := src.Bounds()
   442  
   443  	cgaPalette := color.Palette{
   444  		color.RGBA{0x00, 0x00, 0x00, 0xff},
   445  		color.RGBA{0x55, 0xff, 0xff, 0xff},
   446  		color.RGBA{0xff, 0x55, 0xff, 0xff},
   447  		color.RGBA{0xff, 0xff, 0xff, 0xff},
   448  	}
   449  	drawers := map[string]Drawer{
   450  		"src":             Src,
   451  		"floyd-steinberg": FloydSteinberg,
   452  	}
   453  
   454  loop:
   455  	for dName, d := range drawers {
   456  		dst0 := image.NewPaletted(b, cgaPalette)
   457  		dst1 := image.NewPaletted(b, cgaPalette)
   458  		d.Draw(dst0, b, src, image.Point{})
   459  		d.Draw(embeddedPaletted{dst1}, b, src, image.Point{})
   460  		for y := b.Min.Y; y < b.Max.Y; y++ {
   461  			for x := b.Min.X; x < b.Max.X; x++ {
   462  				if !eq(dst0.At(x, y), dst1.At(x, y)) {
   463  					t.Errorf("%s: at (%d, %d), %v versus %v",
   464  						dName, x, y, dst0.At(x, y), dst1.At(x, y))
   465  					continue loop
   466  				}
   467  			}
   468  		}
   469  	}
   470  }
   471  
   472  func TestSqDiff(t *testing.T) {
   473  	// This test is similar to the one from the image/color package, but
   474  	// sqDiff in this package accepts int32 instead of uint32, so test it
   475  	// for appropriate input.
   476  
   477  	// canonical sqDiff implementation
   478  	orig := func(x, y int32) uint32 {
   479  		var d uint32
   480  		if x > y {
   481  			d = uint32(x - y)
   482  		} else {
   483  			d = uint32(y - x)
   484  		}
   485  		return (d * d) >> 2
   486  	}
   487  	testCases := []int32{
   488  		0,
   489  		1,
   490  		2,
   491  		0x0fffd,
   492  		0x0fffe,
   493  		0x0ffff,
   494  		0x10000,
   495  		0x10001,
   496  		0x10002,
   497  		0x7ffffffd,
   498  		0x7ffffffe,
   499  		0x7fffffff,
   500  		-0x7ffffffd,
   501  		-0x7ffffffe,
   502  		-0x80000000,
   503  	}
   504  	for _, x := range testCases {
   505  		for _, y := range testCases {
   506  			if got, want := sqDiff(x, y), orig(x, y); got != want {
   507  				t.Fatalf("sqDiff(%#x, %#x): got %d, want %d", x, y, got, want)
   508  			}
   509  		}
   510  	}
   511  	if err := quick.CheckEqual(orig, sqDiff, &quick.Config{MaxCountScale: 10}); err != nil {
   512  		t.Fatal(err)
   513  	}
   514  }
   515  

View as plain text