Files
gio/op/paint/paint.go
T
Elias Naur 97299dc2f9 op/paint: make every ImageOp unique
The gioui.org/commit/74407a50d598bfd27e8f8e48b6832cc5df04de77
added a NewImageOp constructor that always copies the supplied
image. It does that for two reasons:

First, the image.Image reference is used in the image=>texture
map of cached textures. Without a copy, we wouldn't detect a
modified image even if a new ImageOp was created.

Second, we don't want the program to touch the image while the GPU
is uploading it.

The second reason was removed in a previous change that blocks
FrameEvent.Frame until we're done with the operations, including
uploading images to the GPU.

The first reason is easily fixed by using a unique per ImageOp,
as pointed out by Alessandro Arzilli.

This change switches to using the unique key. Alessandro's patch
avoids the copy when possible.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2019-11-07 18:27:06 +01:00

109 lines
2.3 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package paint
import (
"encoding/binary"
"image"
"image/color"
"image/draw"
"math"
"gioui.org/f32"
"gioui.org/internal/opconst"
"gioui.org/op"
)
// ImageOp sets the material to an image.
type ImageOp struct {
uniform bool
color color.RGBA
src *image.RGBA
size image.Point
// handle is a key to uniquely identify this ImageOp
// in a map of cached textures.
handle interface{}
}
// ColorOp sets the material to a constant color.
type ColorOp struct {
Color color.RGBA
}
// PaintOp draws the current material, respecting the
// clip path and transformation.
type PaintOp struct {
Rect f32.Rectangle
}
func NewImageOp(src image.Image) ImageOp {
switch src := src.(type) {
case *image.Uniform:
col := color.RGBAModel.Convert(src.C).(color.RGBA)
return ImageOp{
uniform: true,
color: col,
}
default:
sz := src.Bounds().Size()
// Copy the image into a GPU friendly format.
dst := image.NewRGBA(image.Rectangle{
Max: sz,
})
draw.Draw(dst, src.Bounds(), src, image.Point{}, draw.Src)
return ImageOp{
src: dst,
size: sz,
handle: new(int),
}
}
}
func (i ImageOp) Size() image.Point {
return i.size
}
func (i ImageOp) Add(o *op.Ops) {
if i.uniform {
ColorOp{
Color: i.color,
}.Add(o)
return
}
data := o.Write(opconst.TypeImageLen, i.src, i.handle)
data[0] = byte(opconst.TypeImage)
}
func (c ColorOp) Add(o *op.Ops) {
data := o.Write(opconst.TypeColorLen)
data[0] = byte(opconst.TypeColor)
data[1] = c.Color.R
data[2] = c.Color.G
data[3] = c.Color.B
data[4] = c.Color.A
}
func (d PaintOp) Add(o *op.Ops) {
data := o.Write(opconst.TypePaintLen)
data[0] = byte(opconst.TypePaint)
bo := binary.LittleEndian
bo.PutUint32(data[1:], math.Float32bits(d.Rect.Min.X))
bo.PutUint32(data[5:], math.Float32bits(d.Rect.Min.Y))
bo.PutUint32(data[9:], math.Float32bits(d.Rect.Max.X))
bo.PutUint32(data[13:], math.Float32bits(d.Rect.Max.Y))
}
// RectClip returns a ClipOp corresponding to a pixel aligned
// rectangular area.
func RectClip(r image.Rectangle) ClipOp {
return ClipOp{bounds: toRectF(r)}
}
func toRectF(r image.Rectangle) f32.Rectangle {
return f32.Rectangle{
Min: f32.Point{X: float32(r.Min.X), Y: float32(r.Min.Y)},
Max: f32.Point{X: float32(r.Max.X), Y: float32(r.Max.Y)},
}
}