diff --git a/app/internal/gpu/gpu.go b/app/internal/gpu/gpu.go index 05ab150d..3c8e86cd 100644 --- a/app/internal/gpu/gpu.go +++ b/app/internal/gpu/gpu.go @@ -80,10 +80,10 @@ type drawState struct { rect bool z int - // Current ImageOp image and size, if any. - img *image.RGBA - imgSize image.Point - // Current ColorOp, if any. + matType materialType + // Current paint.ImageOp + image imageOpData + // Current paint.ColorOp, if any. color color.RGBA } @@ -125,6 +125,12 @@ type clipOp struct { bounds f32.Rectangle } +// imageOpData is the shadow of paint.ImageOp. +type imageOpData struct { + src *image.RGBA + handle interface{} +} + func (op *clipOp) decode(data []byte) { if opconst.OpType(data[0]) != opconst.TypeClip { panic("invalid op") @@ -145,29 +151,29 @@ func (op *clipOp) decode(data []byte) { } } -func decodeImageOp(data []byte, refs []interface{}) (*image.RGBA, image.Point) { - bo := binary.LittleEndian +func decodeImageOp(data []byte, refs []interface{}) imageOpData { if opconst.OpType(data[0]) != opconst.TypeImage { panic("invalid op") } - sz := image.Point{ - X: int(int32(bo.Uint32(data[1:]))), - Y: int(int32(bo.Uint32(data[5:]))), + handle := refs[1] + if handle == nil { + 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 { panic("invalid op") } - return paint.ColorOp{ - Color: color.RGBA{ - R: data[1], - G: data[2], - B: data[3], - A: data[4], - }, + return color.RGBA{ + R: data[1], + G: data[2], + B: data[3], + A: data[4], } } @@ -746,11 +752,11 @@ loop: aux = nil auxKey = ops.Key{} case opconst.TypeColor: - op := decodeColorOp(encOp.Data) - state.img = nil - state.color = op.Color + state.matType = materialColor + state.color = decodeColorOp(encOp.Data) 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: op := decodePaintOp(encOp.Data) 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 { var m material - if d.img == nil { + switch d.matType { + case materialColor: m.material = materialColor m.color = gamma(d.color.RGBA()) m.opaque = m.color[3] == 1.0 - } else { + case materialTexture: m.material = materialTexture dr := boundRectF(rect.Add(off)) + sz := d.image.src.Bounds().Size() 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 { // 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 } } - tex, exists := cache.get(d.img) + tex, exists := cache.get(d.image.handle) if !exists { t := &texture{ - src: d.img, + src: d.image.src, } - cache.put(d.img, t) + cache.put(d.image.handle, t) tex = t } m.texture = tex.(*texture) - m.uvScale, m.uvOffset = texSpaceTransform(sr, d.img.Bounds().Size()) + m.uvScale, m.uvOffset = texSpaceTransform(sr, sz) } return m } diff --git a/internal/opconst/ops.go b/internal/opconst/ops.go index 59177a28..6bca3d05 100644 --- a/internal/opconst/ops.go +++ b/internal/opconst/ops.go @@ -34,7 +34,7 @@ const ( TypeTransformLen = 1 + 4*2 TypeLayerLen = 1 TypeRedrawLen = 1 + 8 - TypeImageLen = 1 + 4*4 + TypeImageLen = 1 TypePaintLen = 1 + 4*4 TypeColorLen = 1 + 4 TypeAreaLen = 1 + 1 + 4*4 @@ -74,8 +74,10 @@ func (t OpType) Size() int { func (t OpType) NumRefs() int { switch t { - case TypeMacro, TypeImage, TypeKeyInput, TypePointerInput, TypeProfile: + case TypeMacro, TypeKeyInput, TypePointerInput, TypeProfile: return 1 + case TypeImage: + return 2 default: return 0 } diff --git a/op/paint/paint.go b/op/paint/paint.go index 4b71d8c5..f4fa8483 100644 --- a/op/paint/paint.go +++ b/op/paint/paint.go @@ -20,6 +20,10 @@ type ImageOp struct { 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. @@ -49,8 +53,9 @@ func NewImageOp(src image.Image) ImageOp { }) draw.Draw(dst, src.Bounds(), src, image.Point{}, draw.Src) return ImageOp{ - src: dst, - size: sz, + src: dst, + size: sz, + handle: new(int), } } } @@ -66,11 +71,8 @@ func (i ImageOp) Add(o *op.Ops) { }.Add(o) return } - data := o.Write(opconst.TypeImageLen, i.src) + data := o.Write(opconst.TypeImageLen, i.src, i.handle) 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) {