github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/sprite/portable/affine_test.go (about) 1 // Copyright 2014 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 portable 6 7 import ( 8 "image" 9 "image/color" 10 "image/draw" 11 "image/png" 12 "io/ioutil" 13 "math" 14 "os" 15 "testing" 16 17 "golang.org/x/mobile/f32" 18 "golang.org/x/mobile/geom" 19 ) 20 21 func TestAffine(t *testing.T) { 22 f, err := os.Open("../../testdata/testpattern.png") 23 if err != nil { 24 t.Fatal(err) 25 } 26 defer f.Close() 27 srcOrig, _, err := image.Decode(f) 28 if err != nil { 29 t.Fatal(err) 30 } 31 src := image.NewRGBA(srcOrig.Bounds()) 32 draw.Draw(src, src.Rect, srcOrig, srcOrig.Bounds().Min, draw.Src) 33 34 const ( 35 pixW = 100 36 pixH = 100 37 ptW = geom.Pt(50) 38 ptH = geom.Pt(50) 39 ) 40 geom.PixelsPerPt = float32(pixW) / float32(ptW) 41 geom.Width = ptW 42 geom.Height = ptH 43 44 got := image.NewRGBA(image.Rect(0, 0, pixW, pixH)) 45 blue := image.NewUniform(color.RGBA{B: 0xff, A: 0xff}) 46 draw.Draw(got, got.Bounds(), blue, image.Point{}, draw.Src) 47 48 b := src.Bounds() 49 b.Min.X += 10 50 b.Max.Y /= 2 51 52 var a f32.Affine 53 a.Identity() 54 a.Scale(&a, geom.PixelsPerPt, geom.PixelsPerPt) 55 a.Translate(&a, 0, 24) 56 a.Rotate(&a, float32(math.Asin(12./20))) 57 // See commentary in the render method defined in portable.go. 58 a.Scale(&a, 40/float32(b.Dx()), 20/float32(b.Dy())) 59 a.Inverse(&a) 60 61 affine(got, src, b, nil, &a, draw.Over) 62 63 ptTopLeft := geom.Point{0, 24} 64 ptBottomRight := geom.Point{12 + 32, 16} 65 66 drawCross(got, 0, 0) 67 drawCross(got, int(ptTopLeft.X.Px()), int(ptTopLeft.Y.Px())) 68 drawCross(got, int(ptBottomRight.X.Px()), int(ptBottomRight.Y.Px())) 69 drawCross(got, pixW-1, pixH-1) 70 71 const wantPath = "../../testdata/testpattern-window.png" 72 f, err = os.Open(wantPath) 73 if err != nil { 74 t.Fatal(err) 75 } 76 defer f.Close() 77 wantSrc, _, err := image.Decode(f) 78 if err != nil { 79 t.Fatal(err) 80 } 81 want, ok := wantSrc.(*image.RGBA) 82 if !ok { 83 b := wantSrc.Bounds() 84 want = image.NewRGBA(b) 85 draw.Draw(want, b, wantSrc, b.Min, draw.Src) 86 } 87 88 if !imageEq(got, want) { 89 gotPath, err := writeTempPNG("testpattern-window-got", got) 90 if err != nil { 91 t.Fatal(err) 92 } 93 t.Errorf("got\n%s\nwant\n%s", gotPath, wantPath) 94 } 95 } 96 97 func TestAffineMask(t *testing.T) { 98 f, err := os.Open("../../testdata/testpattern.png") 99 if err != nil { 100 t.Fatal(err) 101 } 102 defer f.Close() 103 srcOrig, _, err := image.Decode(f) 104 if err != nil { 105 t.Fatal(err) 106 } 107 b := srcOrig.Bounds() 108 src := image.NewRGBA(b) 109 draw.Draw(src, src.Rect, srcOrig, b.Min, draw.Src) 110 mask := image.NewAlpha(b) 111 for y := b.Min.Y; y < b.Max.Y; y++ { 112 for x := b.Min.X; x < b.Max.X; x++ { 113 mask.Set(x, y, color.Alpha{A: uint8(x - b.Min.X)}) 114 } 115 } 116 want := image.NewRGBA(b) 117 draw.DrawMask(want, want.Rect, src, b.Min, mask, b.Min, draw.Src) 118 119 a := new(f32.Affine) 120 a.Identity() 121 got := image.NewRGBA(b) 122 affine(got, src, b, mask, a, draw.Src) 123 124 if !imageEq(got, want) { 125 gotPath, err := writeTempPNG("testpattern-mask-got", got) 126 if err != nil { 127 t.Fatal(err) 128 } 129 wantPath, err := writeTempPNG("testpattern-mask-want", want) 130 if err != nil { 131 t.Fatal(err) 132 } 133 t.Errorf("got\n%s\nwant\n%s", gotPath, wantPath) 134 } 135 } 136 137 func writeTempPNG(prefix string, m image.Image) (string, error) { 138 f, err := ioutil.TempFile("", prefix+"-") 139 if err != nil { 140 return "", err 141 } 142 f.Close() 143 path := f.Name() + ".png" 144 f, err = os.Create(path) 145 if err != nil { 146 return "", err 147 } 148 if err := png.Encode(f, m); err != nil { 149 f.Close() 150 return "", err 151 } 152 return path, f.Close() 153 } 154 155 func drawCross(m *image.RGBA, x, y int) { 156 c := color.RGBA{0xff, 0, 0, 0xff} // red 157 m.SetRGBA(x+0, y-2, c) 158 m.SetRGBA(x+0, y-1, c) 159 m.SetRGBA(x-2, y+0, c) 160 m.SetRGBA(x-1, y+0, c) 161 m.SetRGBA(x+0, y+0, c) 162 m.SetRGBA(x+1, y+0, c) 163 m.SetRGBA(x+2, y+0, c) 164 m.SetRGBA(x+0, y+1, c) 165 m.SetRGBA(x+0, y+2, c) 166 } 167 168 func eqEpsilon(x, y uint8) bool { 169 const epsilon = 9 170 return x-y < epsilon || y-x < epsilon 171 } 172 173 func colorEq(c0, c1 color.RGBA) bool { 174 return eqEpsilon(c0.R, c1.R) && eqEpsilon(c0.G, c1.G) && eqEpsilon(c0.B, c1.B) && eqEpsilon(c0.A, c1.A) 175 } 176 177 func imageEq(m0, m1 *image.RGBA) bool { 178 b0 := m0.Bounds() 179 b1 := m1.Bounds() 180 if b0 != b1 { 181 return false 182 } 183 badPx := 0 184 for y := b0.Min.Y; y < b0.Max.Y; y++ { 185 for x := b0.Min.X; x < b0.Max.X; x++ { 186 c0, c1 := m0.At(x, y).(color.RGBA), m1.At(x, y).(color.RGBA) 187 if !colorEq(c0, c1) { 188 badPx++ 189 } 190 } 191 } 192 badFrac := float64(badPx) / float64(b0.Dx()*b0.Dy()) 193 return badFrac < 0.01 194 }