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