1// Copyright 2015 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// +build example
6//
7// This build tag means that "go install golang.org/x/exp/shiny/..." doesn't
8// install this example program. Use "go run main.go" to run it or "go install
9// -tags=example" to install it.
10
11// Basic is a basic example of a graphical application.
12package main
13
14import (
15	"fmt"
16	"image"
17	"image/color"
18	"log"
19	"math"
20
21	"golang.org/x/exp/shiny/driver"
22	"golang.org/x/exp/shiny/imageutil"
23	"golang.org/x/exp/shiny/screen"
24	"golang.org/x/image/math/f64"
25	"golang.org/x/mobile/event/key"
26	"golang.org/x/mobile/event/lifecycle"
27	"golang.org/x/mobile/event/paint"
28	"golang.org/x/mobile/event/size"
29)
30
31var (
32	blue0    = color.RGBA{0x00, 0x00, 0x1f, 0xff}
33	blue1    = color.RGBA{0x00, 0x00, 0x3f, 0xff}
34	darkGray = color.RGBA{0x3f, 0x3f, 0x3f, 0xff}
35	green    = color.RGBA{0x00, 0x7f, 0x00, 0x7f}
36	red      = color.RGBA{0x7f, 0x00, 0x00, 0x7f}
37	yellow   = color.RGBA{0x3f, 0x3f, 0x00, 0x3f}
38
39	cos30 = math.Cos(math.Pi / 6)
40	sin30 = math.Sin(math.Pi / 6)
41)
42
43func main() {
44	driver.Main(func(s screen.Screen) {
45		w, err := s.NewWindow(&screen.NewWindowOptions{
46			Title: "Basic Shiny Example",
47		})
48		if err != nil {
49			log.Fatal(err)
50		}
51		defer w.Release()
52
53		size0 := image.Point{256, 256}
54		b, err := s.NewBuffer(size0)
55		if err != nil {
56			log.Fatal(err)
57		}
58		defer b.Release()
59		drawGradient(b.RGBA())
60
61		t0, err := s.NewTexture(size0)
62		if err != nil {
63			log.Fatal(err)
64		}
65		defer t0.Release()
66		t0.Upload(image.Point{}, b, b.Bounds())
67
68		size1 := image.Point{32, 20}
69		t1, err := s.NewTexture(size1)
70		if err != nil {
71			log.Fatal(err)
72		}
73		defer t1.Release()
74		t1.Fill(t1.Bounds(), green, screen.Src)
75		t1.Fill(t1.Bounds().Inset(2), red, screen.Over)
76		t1.Fill(t1.Bounds().Inset(4), red, screen.Src)
77
78		var sz size.Event
79		for {
80			e := w.NextEvent()
81
82			// This print message is to help programmers learn what events this
83			// example program generates. A real program shouldn't print such
84			// messages; they're not important to end users.
85			format := "got %#v\n"
86			if _, ok := e.(fmt.Stringer); ok {
87				format = "got %v\n"
88			}
89			fmt.Printf(format, e)
90
91			switch e := e.(type) {
92			case lifecycle.Event:
93				if e.To == lifecycle.StageDead {
94					return
95				}
96
97			case key.Event:
98				if e.Code == key.CodeEscape {
99					return
100				}
101
102			case paint.Event:
103				const inset = 10
104				for _, r := range imageutil.Border(sz.Bounds(), inset) {
105					w.Fill(r, blue0, screen.Src)
106				}
107				w.Fill(sz.Bounds().Inset(inset), blue1, screen.Src)
108				w.Upload(image.Point{20, 0}, b, b.Bounds())
109				w.Fill(image.Rect(50, 50, 350, 120), red, screen.Over)
110
111				// By default, draw the entirety of the texture using the Over
112				// operator. Uncomment one or both of the lines below to see
113				// their different effects.
114				op := screen.Over
115				// op = screen.Src
116				t0Rect := t0.Bounds()
117				// t0Rect = image.Rect(16, 0, 240, 100)
118
119				// Draw the texture t0 twice, as a 1:1 copy and under the
120				// transform src2dst.
121				w.Copy(image.Point{150, 100}, t0, t0Rect, op, nil)
122				src2dst := f64.Aff3{
123					+0.5 * cos30, -1.0 * sin30, 100,
124					+0.5 * sin30, +1.0 * cos30, 200,
125				}
126				w.Draw(src2dst, t0, t0Rect, op, nil)
127				w.DrawUniform(src2dst, yellow, t0Rect.Inset(30), screen.Over, nil)
128
129				// Draw crosses at the transformed corners of t0Rect.
130				for _, sx := range []int{t0Rect.Min.X, t0Rect.Max.X} {
131					for _, sy := range []int{t0Rect.Min.Y, t0Rect.Max.Y} {
132						dx := int(src2dst[0]*float64(sx) + src2dst[1]*float64(sy) + src2dst[2])
133						dy := int(src2dst[3]*float64(sx) + src2dst[4]*float64(sy) + src2dst[5])
134						w.Fill(image.Rect(dx-0, dy-1, dx+1, dy+2), darkGray, screen.Src)
135						w.Fill(image.Rect(dx-1, dy-0, dx+2, dy+1), darkGray, screen.Src)
136					}
137				}
138
139				// Draw t1.
140				w.Copy(image.Point{400, 50}, t1, t1.Bounds(), screen.Src, nil)
141
142				w.Publish()
143
144			case size.Event:
145				sz = e
146
147			case error:
148				log.Print(e)
149			}
150		}
151	})
152}
153
154func drawGradient(m *image.RGBA) {
155	b := m.Bounds()
156	for y := b.Min.Y; y < b.Max.Y; y++ {
157		for x := b.Min.X; x < b.Max.X; x++ {
158			if x%64 == 0 || y%64 == 0 {
159				m.SetRGBA(x, y, color.RGBA{0xff, 0xff, 0xff, 0xff})
160			} else if x%64 == 63 || y%64 == 63 {
161				m.SetRGBA(x, y, color.RGBA{0x00, 0x00, 0xff, 0xff})
162			} else {
163				m.SetRGBA(x, y, color.RGBA{uint8(x), uint8(y), 0x00, 0xff})
164			}
165		}
166	}
167
168	// Round off the corners.
169	const radius = 64
170	lox := b.Min.X + radius - 1
171	loy := b.Min.Y + radius - 1
172	hix := b.Max.X - radius
173	hiy := b.Max.Y - radius
174	for y := 0; y < radius; y++ {
175		for x := 0; x < radius; x++ {
176			if x*x+y*y <= radius*radius {
177				continue
178			}
179			m.SetRGBA(lox-x, loy-y, color.RGBA{})
180			m.SetRGBA(hix+x, loy-y, color.RGBA{})
181			m.SetRGBA(lox-x, hiy+y, color.RGBA{})
182			m.SetRGBA(hix+x, hiy+y, color.RGBA{})
183		}
184	}
185}
186