...

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  	video001, err := png.Decode(f)
	 438  	if err != nil {
	 439  		t.Fatalf("decode: %v", err)
	 440  	}
	 441  	b := video001.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  	sources := map[string]image.Image{
	 454  		"uniform":	&image.Uniform{color.RGBA{0xff, 0x7f, 0xff, 0xff}},
	 455  		"video001": video001,
	 456  	}
	 457  
	 458  	for dName, d := range drawers {
	 459  	loop:
	 460  		for sName, src := range sources {
	 461  			dst0 := image.NewPaletted(b, cgaPalette)
	 462  			dst1 := image.NewPaletted(b, cgaPalette)
	 463  			d.Draw(dst0, b, src, image.Point{})
	 464  			d.Draw(embeddedPaletted{dst1}, b, src, image.Point{})
	 465  			for y := b.Min.Y; y < b.Max.Y; y++ {
	 466  				for x := b.Min.X; x < b.Max.X; x++ {
	 467  					if !eq(dst0.At(x, y), dst1.At(x, y)) {
	 468  						t.Errorf("%s / %s: at (%d, %d), %v versus %v",
	 469  							dName, sName, x, y, dst0.At(x, y), dst1.At(x, y))
	 470  						continue loop
	 471  					}
	 472  				}
	 473  			}
	 474  		}
	 475  	}
	 476  }
	 477  
	 478  func TestSqDiff(t *testing.T) {
	 479  	// This test is similar to the one from the image/color package, but
	 480  	// sqDiff in this package accepts int32 instead of uint32, so test it
	 481  	// for appropriate input.
	 482  
	 483  	// canonical sqDiff implementation
	 484  	orig := func(x, y int32) uint32 {
	 485  		var d uint32
	 486  		if x > y {
	 487  			d = uint32(x - y)
	 488  		} else {
	 489  			d = uint32(y - x)
	 490  		}
	 491  		return (d * d) >> 2
	 492  	}
	 493  	testCases := []int32{
	 494  		0,
	 495  		1,
	 496  		2,
	 497  		0x0fffd,
	 498  		0x0fffe,
	 499  		0x0ffff,
	 500  		0x10000,
	 501  		0x10001,
	 502  		0x10002,
	 503  		0x7ffffffd,
	 504  		0x7ffffffe,
	 505  		0x7fffffff,
	 506  		-0x7ffffffd,
	 507  		-0x7ffffffe,
	 508  		-0x80000000,
	 509  	}
	 510  	for _, x := range testCases {
	 511  		for _, y := range testCases {
	 512  			if got, want := sqDiff(x, y), orig(x, y); got != want {
	 513  				t.Fatalf("sqDiff(%#x, %#x): got %d, want %d", x, y, got, want)
	 514  			}
	 515  		}
	 516  	}
	 517  	if err := quick.CheckEqual(orig, sqDiff, &quick.Config{MaxCountScale: 10}); err != nil {
	 518  		t.Fatal(err)
	 519  	}
	 520  }
	 521  

View as plain text