mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +00:00
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>
This commit is contained in:
+40
-29
@@ -80,10 +80,10 @@ type drawState struct {
|
|||||||
rect bool
|
rect bool
|
||||||
z int
|
z int
|
||||||
|
|
||||||
// Current ImageOp image and size, if any.
|
matType materialType
|
||||||
img *image.RGBA
|
// Current paint.ImageOp
|
||||||
imgSize image.Point
|
image imageOpData
|
||||||
// Current ColorOp, if any.
|
// Current paint.ColorOp, if any.
|
||||||
color color.RGBA
|
color color.RGBA
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +125,12 @@ type clipOp struct {
|
|||||||
bounds f32.Rectangle
|
bounds f32.Rectangle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// imageOpData is the shadow of paint.ImageOp.
|
||||||
|
type imageOpData struct {
|
||||||
|
src *image.RGBA
|
||||||
|
handle interface{}
|
||||||
|
}
|
||||||
|
|
||||||
func (op *clipOp) decode(data []byte) {
|
func (op *clipOp) decode(data []byte) {
|
||||||
if opconst.OpType(data[0]) != opconst.TypeClip {
|
if opconst.OpType(data[0]) != opconst.TypeClip {
|
||||||
panic("invalid op")
|
panic("invalid op")
|
||||||
@@ -145,29 +151,29 @@ func (op *clipOp) decode(data []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeImageOp(data []byte, refs []interface{}) (*image.RGBA, image.Point) {
|
func decodeImageOp(data []byte, refs []interface{}) imageOpData {
|
||||||
bo := binary.LittleEndian
|
|
||||||
if opconst.OpType(data[0]) != opconst.TypeImage {
|
if opconst.OpType(data[0]) != opconst.TypeImage {
|
||||||
panic("invalid op")
|
panic("invalid op")
|
||||||
}
|
}
|
||||||
sz := image.Point{
|
handle := refs[1]
|
||||||
X: int(int32(bo.Uint32(data[1:]))),
|
if handle == nil {
|
||||||
Y: int(int32(bo.Uint32(data[5:]))),
|
panic("nil handle")
|
||||||
|
}
|
||||||
|
return imageOpData{
|
||||||
|
src: refs[0].(*image.RGBA),
|
||||||
|
handle: handle,
|
||||||
}
|
}
|
||||||
return refs[0].(*image.RGBA), sz
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeColorOp(data []byte) paint.ColorOp {
|
func decodeColorOp(data []byte) color.RGBA {
|
||||||
if opconst.OpType(data[0]) != opconst.TypeColor {
|
if opconst.OpType(data[0]) != opconst.TypeColor {
|
||||||
panic("invalid op")
|
panic("invalid op")
|
||||||
}
|
}
|
||||||
return paint.ColorOp{
|
return color.RGBA{
|
||||||
Color: color.RGBA{
|
R: data[1],
|
||||||
R: data[1],
|
G: data[2],
|
||||||
G: data[2],
|
B: data[3],
|
||||||
B: data[3],
|
A: data[4],
|
||||||
A: data[4],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -746,11 +752,11 @@ loop:
|
|||||||
aux = nil
|
aux = nil
|
||||||
auxKey = ops.Key{}
|
auxKey = ops.Key{}
|
||||||
case opconst.TypeColor:
|
case opconst.TypeColor:
|
||||||
op := decodeColorOp(encOp.Data)
|
state.matType = materialColor
|
||||||
state.img = nil
|
state.color = decodeColorOp(encOp.Data)
|
||||||
state.color = op.Color
|
|
||||||
case opconst.TypeImage:
|
case opconst.TypeImage:
|
||||||
state.img, state.imgSize = decodeImageOp(encOp.Data, encOp.Refs)
|
state.matType = materialTexture
|
||||||
|
state.image = decodeImageOp(encOp.Data, encOp.Refs)
|
||||||
case opconst.TypePaint:
|
case opconst.TypePaint:
|
||||||
op := decodePaintOp(encOp.Data)
|
op := decodePaintOp(encOp.Data)
|
||||||
off := state.t.Transform(f32.Point{})
|
off := state.t.Transform(f32.Point{})
|
||||||
@@ -808,15 +814,20 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
|
|||||||
|
|
||||||
func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f32.Point, clip image.Rectangle) material {
|
func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f32.Point, clip image.Rectangle) material {
|
||||||
var m material
|
var m material
|
||||||
if d.img == nil {
|
switch d.matType {
|
||||||
|
case materialColor:
|
||||||
m.material = materialColor
|
m.material = materialColor
|
||||||
m.color = gamma(d.color.RGBA())
|
m.color = gamma(d.color.RGBA())
|
||||||
m.opaque = m.color[3] == 1.0
|
m.opaque = m.color[3] == 1.0
|
||||||
} else {
|
case materialTexture:
|
||||||
m.material = materialTexture
|
m.material = materialTexture
|
||||||
dr := boundRectF(rect.Add(off))
|
dr := boundRectF(rect.Add(off))
|
||||||
|
sz := d.image.src.Bounds().Size()
|
||||||
sr := f32.Rectangle{
|
sr := f32.Rectangle{
|
||||||
Max: f32.Point{X: float32(d.imgSize.X), Y: float32(d.imgSize.Y)},
|
Max: f32.Point{
|
||||||
|
X: float32(sz.X),
|
||||||
|
Y: float32(sz.Y),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if dx := float32(dr.Dx()); dx != 0 {
|
if dx := float32(dr.Dx()); dx != 0 {
|
||||||
// Don't clip 1 px width sources.
|
// Don't clip 1 px width sources.
|
||||||
@@ -832,16 +843,16 @@ func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f3
|
|||||||
sr.Max.Y -= (float32(dr.Max.Y-clip.Max.Y)*sdy + dy/2) / dy
|
sr.Max.Y -= (float32(dr.Max.Y-clip.Max.Y)*sdy + dy/2) / dy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tex, exists := cache.get(d.img)
|
tex, exists := cache.get(d.image.handle)
|
||||||
if !exists {
|
if !exists {
|
||||||
t := &texture{
|
t := &texture{
|
||||||
src: d.img,
|
src: d.image.src,
|
||||||
}
|
}
|
||||||
cache.put(d.img, t)
|
cache.put(d.image.handle, t)
|
||||||
tex = t
|
tex = t
|
||||||
}
|
}
|
||||||
m.texture = tex.(*texture)
|
m.texture = tex.(*texture)
|
||||||
m.uvScale, m.uvOffset = texSpaceTransform(sr, d.img.Bounds().Size())
|
m.uvScale, m.uvOffset = texSpaceTransform(sr, sz)
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const (
|
|||||||
TypeTransformLen = 1 + 4*2
|
TypeTransformLen = 1 + 4*2
|
||||||
TypeLayerLen = 1
|
TypeLayerLen = 1
|
||||||
TypeRedrawLen = 1 + 8
|
TypeRedrawLen = 1 + 8
|
||||||
TypeImageLen = 1 + 4*4
|
TypeImageLen = 1
|
||||||
TypePaintLen = 1 + 4*4
|
TypePaintLen = 1 + 4*4
|
||||||
TypeColorLen = 1 + 4
|
TypeColorLen = 1 + 4
|
||||||
TypeAreaLen = 1 + 1 + 4*4
|
TypeAreaLen = 1 + 1 + 4*4
|
||||||
@@ -74,8 +74,10 @@ func (t OpType) Size() int {
|
|||||||
|
|
||||||
func (t OpType) NumRefs() int {
|
func (t OpType) NumRefs() int {
|
||||||
switch t {
|
switch t {
|
||||||
case TypeMacro, TypeImage, TypeKeyInput, TypePointerInput, TypeProfile:
|
case TypeMacro, TypeKeyInput, TypePointerInput, TypeProfile:
|
||||||
return 1
|
return 1
|
||||||
|
case TypeImage:
|
||||||
|
return 2
|
||||||
default:
|
default:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-6
@@ -20,6 +20,10 @@ type ImageOp struct {
|
|||||||
color color.RGBA
|
color color.RGBA
|
||||||
src *image.RGBA
|
src *image.RGBA
|
||||||
size image.Point
|
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.
|
// ColorOp sets the material to a constant color.
|
||||||
@@ -49,8 +53,9 @@ func NewImageOp(src image.Image) ImageOp {
|
|||||||
})
|
})
|
||||||
draw.Draw(dst, src.Bounds(), src, image.Point{}, draw.Src)
|
draw.Draw(dst, src.Bounds(), src, image.Point{}, draw.Src)
|
||||||
return ImageOp{
|
return ImageOp{
|
||||||
src: dst,
|
src: dst,
|
||||||
size: sz,
|
size: sz,
|
||||||
|
handle: new(int),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,11 +71,8 @@ func (i ImageOp) Add(o *op.Ops) {
|
|||||||
}.Add(o)
|
}.Add(o)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := o.Write(opconst.TypeImageLen, i.src)
|
data := o.Write(opconst.TypeImageLen, i.src, i.handle)
|
||||||
data[0] = byte(opconst.TypeImage)
|
data[0] = byte(opconst.TypeImage)
|
||||||
bo := binary.LittleEndian
|
|
||||||
bo.PutUint32(data[1:], uint32(i.size.X))
|
|
||||||
bo.PutUint32(data[5:], uint32(i.size.Y))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ColorOp) Add(o *op.Ops) {
|
func (c ColorOp) Add(o *op.Ops) {
|
||||||
|
|||||||
Reference in New Issue
Block a user