github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/render/main.go (about) 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 // This build tag means that "go install golang.org/x/exp/shiny/..." doesn't 6 // install this example program. Use "go run main.go" to run it or "go install 7 // -tags=example" to install it. 8 9 // Basic is a basic example of a graphical application. 10 package main 11 12 import ( 13 "image" 14 "image/color" 15 "log" 16 "sync" 17 18 "golang.org/x/exp/shiny/driver" 19 "golang.org/x/exp/shiny/screen" 20 "golang.org/x/image/math/f64" 21 "golang.org/x/mobile/event/key" 22 "golang.org/x/mobile/event/lifecycle" 23 "golang.org/x/mobile/event/paint" 24 "golang.org/x/mobile/event/size" 25 ) 26 27 var Queue RenderQueue 28 29 var Mouse struct { 30 P image.Point 31 } 32 33 var Screen struct { 34 Size image.Point 35 } 36 37 func main() { 38 driver.Main(func(s screen.Screen) { 39 w, err := s.NewWindow(nil) 40 if err != nil { 41 log.Fatal(err) 42 } 43 defer w.Release() 44 45 winSize := image.Point{256, 256} 46 b, err := s.NewBuffer(winSize) 47 if err != nil { 48 log.Fatal(err) 49 } 50 defer b.Release() 51 drawGradient(b.RGBA()) 52 53 t, err := s.NewTexture(winSize) 54 if err != nil { 55 log.Fatal(err) 56 } 57 defer t.Release() 58 t.Upload(image.Point{}, b, b.Bounds()) 59 60 var sz size.Event 61 for { 62 e := w.NextEvent() 63 switch e := e.(type) { 64 case lifecycle.Event: 65 if e.To == lifecycle.StageDead { 66 return 67 } 68 69 case key.Event: 70 if e.Code == key.CodeEscape { 71 return 72 } 73 74 case paint.Event: 75 76 w.Fill(sz.Bounds(), blue0, screen.Src) 77 w.Fill(sz.Bounds().Inset(10), blue1, screen.Src) 78 w.Upload(image.Point{20, 0}, b, b.Bounds()) 79 w.Fill(image.Rect(50, 50, 350, 120), red, screen.Over) 80 81 // By default, draw the entirety of the texture using the Over 82 // operator. Uncomment one or both of the lines below to see 83 // their different effects. 84 op := screen.Over 85 // op = screen.Src 86 tRect := t.Bounds() 87 // tRect = image.Rect(16, 0, 240, 100) 88 89 // Draw the texture t twice, as a 1:1 copy and under the 90 // transform src2dst. 91 w.Copy(image.Point{150, 100}, t, tRect, op, nil) 92 src2dst := f64.Aff3{ 93 +0.5 * cos30, -1.0 * sin30, 100, 94 +0.5 * sin30, +1.0 * cos30, 200, 95 } 96 w.Draw(src2dst, t, tRect, op, nil) 97 98 // Draw crosses at the transformed corners of tRect. 99 for _, sx := range []int{tRect.Min.X, tRect.Max.X} { 100 for _, sy := range []int{tRect.Min.Y, tRect.Max.Y} { 101 dx := int(src2dst[0]*float64(sx) + src2dst[1]*float64(sy) + src2dst[2]) 102 dy := int(src2dst[3]*float64(sx) + src2dst[4]*float64(sy) + src2dst[5]) 103 w.Fill(image.Rect(dx-0, dy-1, dx+1, dy+2), darkGray, screen.Src) 104 w.Fill(image.Rect(dx-1, dy-0, dx+2, dy+1), darkGray, screen.Src) 105 } 106 } 107 108 w.Publish() 109 110 case size.Event: 111 sz = e 112 113 case error: 114 log.Print(e) 115 } 116 } 117 }) 118 } 119 120 type Buffer struct { 121 } 122 123 func NewBuffer() *Buffer {} 124 func (b *Buffer) Release() {} 125 126 type RenderQueue struct { 127 sync.Mutex 128 129 next *Buffer 130 free *Buffer 131 size image.Point 132 } 133 134 func (q *RenderQueue) SetSize(sz image.Point) { 135 q.Lock() 136 defer q.Unlock() 137 q.size = sz 138 } 139 140 func (q *RenderQueue) Push(buffer **Buffer) { 141 q.Lock() 142 defer q.Unlock() 143 144 q.next, *buffer = *buffer, q.free 145 if *buffer == nil || (*buffer != nil && (*buffer).size != q.size) { 146 if *buffer != nil { 147 *buffer.Release() 148 } 149 *buffer = NewBuffer(q.size) 150 } 151 } 152 153 func (q *RenderQueue) Pull(buffer **Buffer) bool { 154 q.Lock() 155 defer q.Unlock() 156 157 if q.next == nil { 158 return false 159 } 160 q.free, *buffer = *buffer, q.next 161 return true 162 } 163 164 func drawGradient(m *image.RGBA) { 165 b := m.Bounds() 166 for y := b.Min.Y; y < b.Max.Y; y++ { 167 for x := b.Min.X; x < b.Max.X; x++ { 168 if x%64 == 0 || y%64 == 0 { 169 m.SetRGBA(x, y, color.RGBA{0xff, 0xff, 0xff, 0xff}) 170 } else if x%64 == 63 || y%64 == 63 { 171 m.SetRGBA(x, y, color.RGBA{0x00, 0x00, 0xff, 0xff}) 172 } else { 173 m.SetRGBA(x, y, color.RGBA{uint8(x), uint8(y), 0x00, 0xff}) 174 } 175 } 176 } 177 178 // Round off the corners. 179 const radius = 64 180 lox := b.Min.X + radius - 1 181 loy := b.Min.Y + radius - 1 182 hix := b.Max.X - radius 183 hiy := b.Max.Y - radius 184 for y := 0; y < radius; y++ { 185 for x := 0; x < radius; x++ { 186 if x*x+y*y <= radius*radius { 187 continue 188 } 189 m.SetRGBA(lox-x, loy-y, color.RGBA{}) 190 m.SetRGBA(hix+x, loy-y, color.RGBA{}) 191 m.SetRGBA(lox-x, hiy+y, color.RGBA{}) 192 m.SetRGBA(hix+x, hiy+y, color.RGBA{}) 193 } 194 } 195 }