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