ui/app/internal/gpu: track image and color in a state stack

Just like the other drawing state, the current image or color must
be tracked in a stack.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-06-01 18:43:51 +02:00
parent d752040f17
commit 7ae8fdaae7
+37 -33
View File
@@ -67,12 +67,6 @@ type drawOps struct {
zimageOps []imageOp zimageOps []imageOp
pathOps []*pathOp pathOps []*pathOp
pathOpCache []pathOp pathOpCache []pathOp
// Current OpImage image and rect, if any.
img image.Image
imgRect image.Rectangle
// Current OpColor, if any.
color color.NRGBA
} }
type drawState struct { type drawState struct {
@@ -81,6 +75,12 @@ type drawState struct {
cpath *pathOp cpath *pathOp
rect bool rect bool
z int z int
// Current OpImage image and rect, if any.
img image.Image
imgRect image.Rectangle
// Current OpColor, if any.
color color.NRGBA
} }
type pathOp struct { type pathOp struct {
@@ -652,7 +652,11 @@ func (d *drawOps) collect(cache *resourceCache, root *ui.Ops, viewport image.Poi
Max: f32.Point{X: float32(viewport.X), Y: float32(viewport.Y)}, Max: f32.Point{X: float32(viewport.X), Y: float32(viewport.Y)},
} }
d.reader.Reset(root.Data(), root.Refs()) d.reader.Reset(root.Data(), root.Refs())
d.collectOps(&d.reader, clip, ui.Transform{}, nil, true, 0) state := drawState{
clip: clip,
rect: true,
}
d.collectOps(&d.reader, state)
} }
func (d *drawOps) newPathOp() *pathOp { func (d *drawOps) newPathOp() *pathOp {
@@ -660,7 +664,7 @@ func (d *drawOps) newPathOp() *pathOp {
return &d.pathOpCache[len(d.pathOpCache)-1] return &d.pathOpCache[len(d.pathOpCache)-1]
} }
func (d *drawOps) collectOps(r *ops.Reader, clip f32.Rectangle, t ui.Transform, cpath *pathOp, rect bool, z int) int { func (d *drawOps) collectOps(r *ops.Reader, state drawState) int {
loop: loop:
for { for {
data, ok := r.Decode() data, ok := r.Decode()
@@ -671,84 +675,84 @@ loop:
case ops.TypeTransform: case ops.TypeTransform:
var op ui.OpTransform var op ui.OpTransform
op.Decode(data) op.Decode(data)
t = t.Mul(op.Transform) state.t = state.t.Mul(op.Transform)
case ops.TypeClip: case ops.TypeClip:
var op gdraw.OpClip var op gdraw.OpClip
op.Decode(data, r.Refs) op.Decode(data, r.Refs)
if op.Path == nil { if op.Path == nil {
clip = f32.Rectangle{} state.clip = f32.Rectangle{}
continue continue
} }
data := op.Path.Data().(*path.Path) data := op.Path.Data().(*path.Path)
off := t.Transform(f32.Point{}) off := state.t.Transform(f32.Point{})
clip = clip.Intersect(data.Bounds.Add(off)) state.clip = state.clip.Intersect(data.Bounds.Add(off))
if clip.Empty() { if state.clip.Empty() {
continue continue
} }
npath := d.newPathOp() npath := d.newPathOp()
*npath = pathOp{ *npath = pathOp{
parent: cpath, parent: state.cpath,
off: off, off: off,
} }
cpath = npath state.cpath = npath
if len(data.Vertices) > 0 { if len(data.Vertices) > 0 {
rect = false state.rect = false
cpath.path = data state.cpath.path = data
d.pathOps = append(d.pathOps, cpath) d.pathOps = append(d.pathOps, state.cpath)
} }
case ops.TypeColor: case ops.TypeColor:
var op gdraw.OpColor var op gdraw.OpColor
op.Decode(data, r.Refs) op.Decode(data, r.Refs)
d.img = nil state.img = nil
d.color = op.Col state.color = op.Col
case ops.TypeImage: case ops.TypeImage:
var op gdraw.OpImage var op gdraw.OpImage
op.Decode(data, r.Refs) op.Decode(data, r.Refs)
d.img = op.Img state.img = op.Img
d.imgRect = op.Rect state.imgRect = op.Rect
case ops.TypeDraw: case ops.TypeDraw:
var op gdraw.OpDraw var op gdraw.OpDraw
op.Decode(data, r.Refs) op.Decode(data, r.Refs)
off := t.Transform(f32.Point{}) off := state.t.Transform(f32.Point{})
clip := clip.Intersect(op.Rect.Add(off)) clip := state.clip.Intersect(op.Rect.Add(off))
if clip.Empty() { if clip.Empty() {
continue continue
} }
bounds := boundRectF(clip) bounds := boundRectF(clip)
mat := d.materialFor(d.cache, op.Rect, off, bounds) mat := state.materialFor(d.cache, op.Rect, off, bounds)
if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && mat.opaque && mat.material == materialColor { if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && mat.opaque && mat.material == materialColor {
// The image is a uniform opaque color and takes up the whole screen. // The image is a uniform opaque color and takes up the whole screen.
// Scrap images up to and including this image and set clear color. // Scrap images up to and including this image and set clear color.
d.zimageOps = d.zimageOps[:0] d.zimageOps = d.zimageOps[:0]
d.imageOps = d.imageOps[:0] d.imageOps = d.imageOps[:0]
z = 0 state.z = 0
copy(d.clearColor[:], mat.color[:3]) copy(d.clearColor[:], mat.color[:3])
continue continue
} }
z++ state.z++
// Assume 16-bit depth buffer. // Assume 16-bit depth buffer.
const zdepth = 1 << 16 const zdepth = 1 << 16
// Convert z to window-space, assuming depth range [0;1]. // Convert z to window-space, assuming depth range [0;1].
zf := float32(z)*2/zdepth - 1.0 zf := float32(state.z)*2/zdepth - 1.0
img := imageOp{ img := imageOp{
z: zf, z: zf,
path: cpath, path: state.cpath,
off: off, off: off,
clip: bounds, clip: bounds,
material: mat, material: mat,
} }
if rect && img.material.opaque { if state.rect && img.material.opaque {
d.zimageOps = append(d.zimageOps, img) d.zimageOps = append(d.zimageOps, img)
} else { } else {
d.imageOps = append(d.imageOps, img) d.imageOps = append(d.imageOps, img)
} }
case ops.TypePush: case ops.TypePush:
z = d.collectOps(r, clip, t, cpath, rect, z) state.z = d.collectOps(r, state)
case ops.TypePop: case ops.TypePop:
break loop break loop
} }
} }
return z return state.z
} }
func expandPathOp(p *pathOp, clip image.Rectangle) { func expandPathOp(p *pathOp, clip image.Rectangle) {
@@ -762,7 +766,7 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
} }
} }
func (d *drawOps) 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 { if d.img == nil {
m.material = materialColor m.material = materialColor