|  | // Copyright 2015 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | //go:build example | 
|  | // +build example | 
|  |  | 
|  | // | 
|  | // This build tag means that "go install golang.org/x/exp/shiny/..." doesn't | 
|  | // install this example program. Use "go run main.go" to run it or "go install | 
|  | // -tags=example" to install it. | 
|  |  | 
|  | // Basic is a basic example of a graphical application. | 
|  | package main | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "image" | 
|  | "image/color" | 
|  | "log" | 
|  | "math" | 
|  |  | 
|  | "golang.org/x/exp/shiny/driver" | 
|  | "golang.org/x/exp/shiny/imageutil" | 
|  | "golang.org/x/exp/shiny/screen" | 
|  | "golang.org/x/image/math/f64" | 
|  | "golang.org/x/mobile/event/key" | 
|  | "golang.org/x/mobile/event/lifecycle" | 
|  | "golang.org/x/mobile/event/paint" | 
|  | "golang.org/x/mobile/event/size" | 
|  | ) | 
|  |  | 
|  | var ( | 
|  | blue0    = color.RGBA{0x00, 0x00, 0x1f, 0xff} | 
|  | blue1    = color.RGBA{0x00, 0x00, 0x3f, 0xff} | 
|  | darkGray = color.RGBA{0x3f, 0x3f, 0x3f, 0xff} | 
|  | green    = color.RGBA{0x00, 0x7f, 0x00, 0x7f} | 
|  | red      = color.RGBA{0x7f, 0x00, 0x00, 0x7f} | 
|  | yellow   = color.RGBA{0x3f, 0x3f, 0x00, 0x3f} | 
|  |  | 
|  | cos30 = math.Cos(math.Pi / 6) | 
|  | sin30 = math.Sin(math.Pi / 6) | 
|  | ) | 
|  |  | 
|  | func main() { | 
|  | driver.Main(func(s screen.Screen) { | 
|  | w, err := s.NewWindow(&screen.NewWindowOptions{ | 
|  | Title: "Basic Shiny Example", | 
|  | }) | 
|  | if err != nil { | 
|  | log.Fatal(err) | 
|  | } | 
|  | defer w.Release() | 
|  |  | 
|  | size0 := image.Point{256, 256} | 
|  | b, err := s.NewBuffer(size0) | 
|  | if err != nil { | 
|  | log.Fatal(err) | 
|  | } | 
|  | defer b.Release() | 
|  | drawGradient(b.RGBA()) | 
|  |  | 
|  | t0, err := s.NewTexture(size0) | 
|  | if err != nil { | 
|  | log.Fatal(err) | 
|  | } | 
|  | defer t0.Release() | 
|  | t0.Upload(image.Point{}, b, b.Bounds()) | 
|  |  | 
|  | size1 := image.Point{32, 20} | 
|  | t1, err := s.NewTexture(size1) | 
|  | if err != nil { | 
|  | log.Fatal(err) | 
|  | } | 
|  | defer t1.Release() | 
|  | t1.Fill(t1.Bounds(), green, screen.Src) | 
|  | t1.Fill(t1.Bounds().Inset(2), red, screen.Over) | 
|  | t1.Fill(t1.Bounds().Inset(4), red, screen.Src) | 
|  |  | 
|  | var sz size.Event | 
|  | for { | 
|  | e := w.NextEvent() | 
|  |  | 
|  | // This print message is to help programmers learn what events this | 
|  | // example program generates. A real program shouldn't print such | 
|  | // messages; they're not important to end users. | 
|  | format := "got %#v\n" | 
|  | if _, ok := e.(fmt.Stringer); ok { | 
|  | format = "got %v\n" | 
|  | } | 
|  | fmt.Printf(format, e) | 
|  |  | 
|  | switch e := e.(type) { | 
|  | case lifecycle.Event: | 
|  | if e.To == lifecycle.StageDead { | 
|  | return | 
|  | } | 
|  |  | 
|  | case key.Event: | 
|  | if e.Code == key.CodeEscape { | 
|  | return | 
|  | } | 
|  |  | 
|  | case paint.Event: | 
|  | const inset = 10 | 
|  | for _, r := range imageutil.Border(sz.Bounds(), inset) { | 
|  | w.Fill(r, blue0, screen.Src) | 
|  | } | 
|  | w.Fill(sz.Bounds().Inset(inset), blue1, screen.Src) | 
|  | w.Upload(image.Point{20, 0}, b, b.Bounds()) | 
|  | w.Fill(image.Rect(50, 50, 350, 120), red, screen.Over) | 
|  |  | 
|  | // By default, draw the entirety of the texture using the Over | 
|  | // operator. Uncomment one or both of the lines below to see | 
|  | // their different effects. | 
|  | op := screen.Over | 
|  | // op = screen.Src | 
|  | t0Rect := t0.Bounds() | 
|  | // t0Rect = image.Rect(16, 0, 240, 100) | 
|  |  | 
|  | // Draw the texture t0 twice, as a 1:1 copy and under the | 
|  | // transform src2dst. | 
|  | w.Copy(image.Point{150, 100}, t0, t0Rect, op, nil) | 
|  | src2dst := f64.Aff3{ | 
|  | +0.5 * cos30, -1.0 * sin30, 100, | 
|  | +0.5 * sin30, +1.0 * cos30, 200, | 
|  | } | 
|  | w.Draw(src2dst, t0, t0Rect, op, nil) | 
|  | w.DrawUniform(src2dst, yellow, t0Rect.Inset(30), screen.Over, nil) | 
|  |  | 
|  | // Draw crosses at the transformed corners of t0Rect. | 
|  | for _, sx := range []int{t0Rect.Min.X, t0Rect.Max.X} { | 
|  | for _, sy := range []int{t0Rect.Min.Y, t0Rect.Max.Y} { | 
|  | dx := int(src2dst[0]*float64(sx) + src2dst[1]*float64(sy) + src2dst[2]) | 
|  | dy := int(src2dst[3]*float64(sx) + src2dst[4]*float64(sy) + src2dst[5]) | 
|  | w.Fill(image.Rect(dx-0, dy-1, dx+1, dy+2), darkGray, screen.Src) | 
|  | w.Fill(image.Rect(dx-1, dy-0, dx+2, dy+1), darkGray, screen.Src) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Draw t1. | 
|  | w.Copy(image.Point{400, 50}, t1, t1.Bounds(), screen.Src, nil) | 
|  |  | 
|  | w.Publish() | 
|  |  | 
|  | case size.Event: | 
|  | sz = e | 
|  |  | 
|  | case error: | 
|  | log.Print(e) | 
|  | } | 
|  | } | 
|  | }) | 
|  | } | 
|  |  | 
|  | func drawGradient(m *image.RGBA) { | 
|  | b := m.Bounds() | 
|  | for y := b.Min.Y; y < b.Max.Y; y++ { | 
|  | for x := b.Min.X; x < b.Max.X; x++ { | 
|  | if x%64 == 0 || y%64 == 0 { | 
|  | m.SetRGBA(x, y, color.RGBA{0xff, 0xff, 0xff, 0xff}) | 
|  | } else if x%64 == 63 || y%64 == 63 { | 
|  | m.SetRGBA(x, y, color.RGBA{0x00, 0x00, 0xff, 0xff}) | 
|  | } else { | 
|  | m.SetRGBA(x, y, color.RGBA{uint8(x), uint8(y), 0x00, 0xff}) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Round off the corners. | 
|  | const radius = 64 | 
|  | lox := b.Min.X + radius - 1 | 
|  | loy := b.Min.Y + radius - 1 | 
|  | hix := b.Max.X - radius | 
|  | hiy := b.Max.Y - radius | 
|  | for y := 0; y < radius; y++ { | 
|  | for x := 0; x < radius; x++ { | 
|  | if x*x+y*y <= radius*radius { | 
|  | continue | 
|  | } | 
|  | m.SetRGBA(lox-x, loy-y, color.RGBA{}) | 
|  | m.SetRGBA(hix+x, loy-y, color.RGBA{}) | 
|  | m.SetRGBA(lox-x, hiy+y, color.RGBA{}) | 
|  | m.SetRGBA(hix+x, hiy+y, color.RGBA{}) | 
|  | } | 
|  | } | 
|  | } |