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  }