| // 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. |
| |
| // Package screen provides interfaces for portable two-dimensional graphics and |
| // input events. |
| // |
| // Screens are not created directly. Instead, driver packages provide access to |
| // the screen through a Main function that is designed to be called by the |
| // program's main function. The golang.org/x/exp/shiny/driver package provides |
| // the default driver for the system, such as the X11 driver for desktop Linux, |
| // but other drivers, such as the OpenGL driver, can be explicitly invoked by |
| // calling that driver's Main function. To use the default driver: |
| // |
| // package main |
| // |
| // import ( |
| // "golang.org/x/exp/shiny/driver" |
| // "golang.org/x/exp/shiny/screen" |
| // "golang.org/x/mobile/event/lifecycle" |
| // ) |
| // |
| // func main() { |
| // driver.Main(func(s screen.Screen) { |
| // w, err := s.NewWindow(nil) |
| // if err != nil { |
| // handleError(err) |
| // return |
| // } |
| // defer w.Release() |
| // |
| // for { |
| // switch e := w.NextEvent().(type) { |
| // case lifecycle.Event: |
| // if e.To == lifecycle.StageDead { |
| // return |
| // } |
| // etc |
| // case etc: |
| // etc |
| // } |
| // } |
| // }) |
| // } |
| // |
| // Complete examples can be found in the shiny/example directory. |
| // |
| // Each driver package provides Screen, Buffer, Texture and Window |
| // implementations that work together. Such types are interface types because |
| // this package is driver-independent, but those interfaces aren't expected to |
| // be implemented outside of drivers. For example, a driver's Window |
| // implementation will generally work only with that driver's Buffer |
| // implementation, and will not work with an arbitrary type that happens to |
| // implement the Buffer methods. |
| package screen // import "golang.org/x/exp/shiny/screen" |
| |
| import ( |
| "image" |
| "image/color" |
| "image/draw" |
| "unicode/utf8" |
| |
| "golang.org/x/image/math/f64" |
| ) |
| |
| // TODO: specify image format (Alpha or Gray, not just RGBA) for NewBuffer |
| // and/or NewTexture? |
| |
| // Screen creates Buffers, Textures and Windows. |
| type Screen interface { |
| // NewBuffer returns a new Buffer for this screen. |
| NewBuffer(size image.Point) (Buffer, error) |
| |
| // NewTexture returns a new Texture for this screen. |
| NewTexture(size image.Point) (Texture, error) |
| |
| // NewWindow returns a new Window for this screen. |
| // |
| // A nil opts is valid and means to use the default option values. |
| NewWindow(opts *NewWindowOptions) (Window, error) |
| } |
| |
| // TODO: rename Buffer to Image, to be less confusing with a Window's back and |
| // front buffers. |
| |
| // Buffer is an in-memory pixel buffer. Its pixels can be modified by any Go |
| // code that takes an *image.RGBA, such as the standard library's image/draw |
| // package. A Buffer is essentially an *image.RGBA, but not all *image.RGBA |
| // values (including those returned by image.NewRGBA) are valid Buffers, as a |
| // driver may assume that the memory backing a Buffer's pixels are specially |
| // allocated. |
| // |
| // To see a Buffer's contents on a screen, upload it to a Texture (and then |
| // draw the Texture on a Window) or upload it directly to a Window. |
| // |
| // When specifying a sub-Buffer via Upload, a Buffer's top-left pixel is always |
| // (0, 0) in its own coordinate space. |
| type Buffer interface { |
| // Release releases the Buffer's resources, after all pending uploads and |
| // draws resolve. |
| // |
| // The behavior of the Buffer after Release, whether calling its methods or |
| // passing it as an argument, is undefined. |
| Release() |
| |
| // Size returns the size of the Buffer's image. |
| Size() image.Point |
| |
| // Bounds returns the bounds of the Buffer's image. It is equal to |
| // image.Rectangle{Max: b.Size()}. |
| Bounds() image.Rectangle |
| |
| // RGBA returns the pixel buffer as an *image.RGBA. |
| // |
| // Its contents should not be accessed while the Buffer is uploading. |
| // |
| // The contents of the returned *image.RGBA's Pix field (of type []byte) |
| // can be modified at other times, but that Pix slice itself (i.e. its |
| // underlying pointer, length and capacity) should not be modified at any |
| // time. |
| // |
| // The following is valid: |
| // m := buffer.RGBA() |
| // if len(m.Pix) >= 4 { |
| // m.Pix[0] = 0xff |
| // m.Pix[1] = 0x00 |
| // m.Pix[2] = 0x00 |
| // m.Pix[3] = 0xff |
| // } |
| // or, equivalently: |
| // m := buffer.RGBA() |
| // m.SetRGBA(m.Rect.Min.X, m.Rect.Min.Y, color.RGBA{0xff, 0x00, 0x00, 0xff}) |
| // and using the standard library's image/draw package is also valid: |
| // dst := buffer.RGBA() |
| // draw.Draw(dst, dst.Bounds(), etc) |
| // but the following is invalid: |
| // m := buffer.RGBA() |
| // m.Pix = anotherByteSlice |
| // and so is this: |
| // *buffer.RGBA() = anotherImageRGBA |
| RGBA() *image.RGBA |
| } |
| |
| // Texture is a pixel buffer, but not one that is directly accessible as a |
| // []byte. Conceptually, it could live on a GPU, in another process or even be |
| // across a network, instead of on a CPU in this process. |
| // |
| // Buffers can be uploaded to Textures, and Textures can be drawn on Windows. |
| // |
| // When specifying a sub-Texture via Draw, a Texture's top-left pixel is always |
| // (0, 0) in its own coordinate space. |
| type Texture interface { |
| // Release releases the Texture's resources, after all pending uploads and |
| // draws resolve. |
| // |
| // The behavior of the Texture after Release, whether calling its methods |
| // or passing it as an argument, is undefined. |
| Release() |
| |
| // Size returns the size of the Texture's image. |
| Size() image.Point |
| |
| // Bounds returns the bounds of the Texture's image. It is equal to |
| // image.Rectangle{Max: t.Size()}. |
| Bounds() image.Rectangle |
| |
| Uploader |
| |
| // TODO: also implement Drawer? If so, merge the Uploader and Drawer |
| // interfaces?? |
| } |
| |
| // EventDeque is an infinitely buffered double-ended queue of events. |
| type EventDeque interface { |
| // Send adds an event to the end of the deque. They are returned by |
| // NextEvent in FIFO order. |
| Send(event interface{}) |
| |
| // SendFirst adds an event to the start of the deque. They are returned by |
| // NextEvent in LIFO order, and have priority over events sent via Send. |
| SendFirst(event interface{}) |
| |
| // NextEvent returns the next event in the deque. It blocks until such an |
| // event has been sent. |
| // |
| // Typical event types include: |
| // - lifecycle.Event |
| // - size.Event |
| // - paint.Event |
| // - key.Event |
| // - mouse.Event |
| // - touch.Event |
| // from the golang.org/x/mobile/event/... packages. Other packages may send |
| // events, of those types above or of other types, via Send or SendFirst. |
| NextEvent() interface{} |
| |
| // TODO: LatestLifecycleEvent? Is that still worth it if the |
| // lifecycle.Event struct type loses its DrawContext field? |
| |
| // TODO: LatestSizeEvent? |
| } |
| |
| // Window is a top-level, double-buffered GUI window. |
| type Window interface { |
| // Release closes the window. |
| // |
| // The behavior of the Window after Release, whether calling its methods or |
| // passing it as an argument, is undefined. |
| Release() |
| |
| EventDeque |
| |
| Uploader |
| |
| Drawer |
| |
| // Publish flushes any pending Upload and Draw calls to the window, and |
| // swaps the back buffer to the front. |
| Publish() PublishResult |
| } |
| |
| // PublishResult is the result of an Window.Publish call. |
| type PublishResult struct { |
| // BackBufferPreserved is whether the contents of the back buffer was |
| // preserved. If false, the contents are undefined. |
| BackBufferPreserved bool |
| } |
| |
| // NewWindowOptions are optional arguments to NewWindow. |
| type NewWindowOptions struct { |
| // Width and Height specify the dimensions of the new window. If Width |
| // or Height are zero, a driver-dependent default will be used for each |
| // zero value dimension. |
| Width, Height int |
| |
| // Title specifies the window title. |
| Title string |
| |
| // TODO: fullscreen, icon, cursorHidden? |
| } |
| |
| // GetTitle returns a sanitized form of o.Title. In particular, its length will |
| // not exceed 4096, and it may be further truncated so that it is valid UTF-8 |
| // and will not contain the NUL byte. |
| // |
| // o may be nil, in which case "" is returned. |
| func (o *NewWindowOptions) GetTitle() string { |
| if o == nil { |
| return "" |
| } |
| return sanitizeUTF8(o.Title, 4096) |
| } |
| |
| func sanitizeUTF8(s string, n int) string { |
| if n < len(s) { |
| s = s[:n] |
| } |
| i := 0 |
| for i < len(s) { |
| r, n := utf8.DecodeRuneInString(s[i:]) |
| if r == 0 || (r == utf8.RuneError && n == 1) { |
| break |
| } |
| i += n |
| } |
| return s[:i] |
| } |
| |
| // Uploader is something you can upload a Buffer to. |
| type Uploader interface { |
| // Upload uploads the sub-Buffer defined by src and sr to the destination |
| // (the method receiver), such that sr.Min in src-space aligns with dp in |
| // dst-space. The destination's contents are overwritten; the draw operator |
| // is implicitly draw.Src. |
| // |
| // It is valid to upload a Buffer while another upload of the same Buffer |
| // is in progress, but a Buffer's image.RGBA pixel contents should not be |
| // accessed while it is uploading. A Buffer is re-usable, in that its pixel |
| // contents can be further modified, once all outstanding calls to Upload |
| // have returned. |
| // |
| // TODO: make it optional that a Buffer's contents is preserved after |
| // Upload? Undoing a swizzle is a non-trivial amount of work, and can be |
| // redundant if the next paint cycle starts by clearing the buffer. |
| // |
| // When uploading to a Window, there will not be any visible effect until |
| // Publish is called. |
| Upload(dp image.Point, src Buffer, sr image.Rectangle) |
| |
| // Fill fills that part of the destination (the method receiver) defined by |
| // dr with the given color. |
| // |
| // When filling a Window, there will not be any visible effect until |
| // Publish is called. |
| Fill(dr image.Rectangle, src color.Color, op draw.Op) |
| } |
| |
| // TODO: have a Downloader interface? Not every graphical app needs to be |
| // interactive or involve a window. You could use the GPU for hardware- |
| // accelerated image manipulation: upload a buffer, do some texture ops, then |
| // download the result. |
| |
| // Drawer is something you can draw Textures on. |
| // |
| // Draw is the most general purpose of this interface's methods. It supports |
| // arbitrary affine transformations, such as translations, scales and |
| // rotations. |
| // |
| // Copy and Scale are more specific versions of Draw. The affected dst pixels |
| // are an axis-aligned rectangle, quantized to the pixel grid. Copy copies |
| // pixels in a 1:1 manner, Scale is more general. They have simpler parameters |
| // than Draw, using ints instead of float64s. |
| // |
| // When drawing on a Window, there will not be any visible effect until Publish |
| // is called. |
| type Drawer interface { |
| // Draw draws the sub-Texture defined by src and sr to the destination (the |
| // method receiver). src2dst defines how to transform src coordinates to |
| // dst coordinates. For example, if src2dst is the matrix |
| // |
| // m00 m01 m02 |
| // m10 m11 m12 |
| // |
| // then the src-space point (sx, sy) maps to the dst-space point |
| // (m00*sx + m01*sy + m02, m10*sx + m11*sy + m12). |
| Draw(src2dst f64.Aff3, src Texture, sr image.Rectangle, op draw.Op, opts *DrawOptions) |
| |
| // DrawUniform is like Draw except that the src is a uniform color instead |
| // of a Texture. |
| DrawUniform(src2dst f64.Aff3, src color.Color, sr image.Rectangle, op draw.Op, opts *DrawOptions) |
| |
| // Copy copies the sub-Texture defined by src and sr to the destination |
| // (the method receiver), such that sr.Min in src-space aligns with dp in |
| // dst-space. |
| Copy(dp image.Point, src Texture, sr image.Rectangle, op draw.Op, opts *DrawOptions) |
| |
| // Scale scales the sub-Texture defined by src and sr to the destination |
| // (the method receiver), such that sr in src-space is mapped to dr in |
| // dst-space. |
| Scale(dr image.Rectangle, src Texture, sr image.Rectangle, op draw.Op, opts *DrawOptions) |
| } |
| |
| // These draw.Op constants are provided so that users of this package don't |
| // have to explicitly import "image/draw". |
| const ( |
| Over = draw.Over |
| Src = draw.Src |
| ) |
| |
| // DrawOptions are optional arguments to Draw. |
| type DrawOptions struct { |
| // TODO: transparency in [0x0000, 0xffff]? |
| // TODO: scaler (nearest neighbor vs linear)? |
| } |