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  }