gpu,internal/ops: decode scene commands directly, not through quads

We're about to let clip.Path use more of the compute renderer features
(lines, cubic béziers). This change prepares the gpu package for reading
one of several commands types, not just the quadratic béziers of before.

The old Quad type is still the basis for the stroking algorithms, but
this change moves it into package gpu which is the only user.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-03-11 13:47:33 +01:00
parent 07572387e3
commit eb9bf60b09
7 changed files with 116 additions and 82 deletions
+1 -2
View File
@@ -2,7 +2,6 @@ package gpu
import (
"gioui.org/f32"
"gioui.org/internal/ops"
)
type quadSplitter struct {
@@ -47,7 +46,7 @@ func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) {
encodeQuadTo(data, qs.contour, from, ctrl, to)
}
func (qs *quadSplitter) splitAndEncode(quad ops.Quad) {
func (qs *quadSplitter) splitAndEncode(quad quadSegment) {
cbnd := f32.Rectangle{
Min: quad.From,
Max: quad.To,
+2 -3
View File
@@ -11,7 +11,6 @@ import (
"sort"
"gioui.org/f32"
"gioui.org/internal/ops"
)
func isSolidLine(sty dashOp) bool {
@@ -238,7 +237,7 @@ func (qs strokeQuads) splitAt(contour *uint32, ts ...float64) []strokeQuads {
oi = append(oi, strokeQuad{
contour: *contour,
quad: ops.Quad{
quad: quadSegment{
From: from,
Ctrl: q1,
To: r0,
@@ -256,7 +255,7 @@ func (qs strokeQuads) splitAt(contour *uint32, ts ...float64) []strokeQuads {
}
oi = append(oi, strokeQuad{
contour: *contour,
quad: ops.Quad{
quad: quadSegment{
From: r0,
Ctrl: r1,
To: r2,
+64 -21
View File
@@ -25,6 +25,7 @@ import (
"gioui.org/internal/f32color"
"gioui.org/internal/opconst"
"gioui.org/internal/ops"
"gioui.org/internal/scene"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
@@ -141,6 +142,10 @@ type dashOp struct {
dashes []float32
}
type quadSegment struct {
From, Ctrl, To f32.Point
}
func decodeDashOp(data []byte) dashOp {
_ = data[5]
if opconst.OpType(data[0]) != opconst.TypeDash {
@@ -1340,28 +1345,19 @@ func (d *drawOps) writeVertCache(n int) []byte {
}
// transform, split paths as needed, calculate maxY, bounds and create GPU vertices.
func (d *drawOps) buildVerts(aux []byte, tr f32.Affine2D, outline bool, stroke clip.StrokeStyle, dashes dashOp) (verts []byte, bounds f32.Rectangle) {
func (d *drawOps) buildVerts(pathData []byte, tr f32.Affine2D, outline bool, stroke clip.StrokeStyle, dashes dashOp) (verts []byte, bounds f32.Rectangle) {
inf := float32(math.Inf(+1))
d.qs.bounds = f32.Rectangle{
Min: f32.Point{X: inf, Y: inf},
Max: f32.Point{X: -inf, Y: -inf},
}
d.qs.d = d
bo := binary.LittleEndian
startLength := len(d.vertCache)
switch {
case stroke.Width > 0:
// Stroke path.
quads := make(strokeQuads, 0, 2*len(aux)/(ops.QuadSize+4))
for len(aux) >= ops.QuadSize+4 {
quad := strokeQuad{
contour: bo.Uint32(aux),
quad: ops.DecodeQuad(aux[4:]),
}
quads = append(quads, quad)
aux = aux[ops.QuadSize+4:]
}
quads := decodeToStrokeQuads(pathData)
quads = quads.stroke(stroke, dashes)
for _, quad := range quads {
d.qs.contour = quad.contour
@@ -1371,22 +1367,56 @@ func (d *drawOps) buildVerts(aux []byte, tr f32.Affine2D, outline bool, stroke c
}
case outline:
// Outline path.
for len(aux) >= ops.QuadSize+4 {
d.qs.contour = bo.Uint32(aux)
quad := ops.DecodeQuad(aux[4:])
quad = quad.Transform(tr)
d.qs.splitAndEncode(quad)
aux = aux[ops.QuadSize+4:]
}
decodeToOutlineQuads(&d.qs, tr, pathData)
}
fillMaxY(d.vertCache[startLength:])
return d.vertCache[startLength:], d.qs.bounds
}
// decodeOutlineQuads decodes scene commands, splits them into quadratic béziers
// as needed and feeds them to the supplied splitter.
func decodeToOutlineQuads(qs *quadSplitter, tr f32.Affine2D, pathData []byte) {
for len(pathData) >= scene.CommandSize+4 {
qs.contour = bo.Uint32(pathData)
cmd := ops.DecodeCommand(pathData[4:])
switch cmd.Op() {
case scene.OpFillQuad:
var q quadSegment
q.From, q.Ctrl, q.To = scene.DecodeQuad(cmd)
q = q.Transform(tr)
qs.splitAndEncode(q)
default:
panic("unsupported scene command")
}
pathData = pathData[scene.CommandSize+4:]
}
}
// decodeToStrokeQuads is like decodeOutlineQuads, except it returns a list of stroke
// quads ready to stroke.
func decodeToStrokeQuads(pathData []byte) strokeQuads {
quads := make(strokeQuads, 0, 2*len(pathData)/(scene.CommandSize+4))
for len(pathData) >= scene.CommandSize+4 {
contour := bo.Uint32(pathData)
cmd := ops.DecodeCommand(pathData[4:])
switch cmd.Op() {
case scene.OpFillQuad:
var q quadSegment
q.From, q.Ctrl, q.To = scene.DecodeQuad(cmd)
quad := strokeQuad{
contour: contour,
quad: q,
}
quads = append(quads, quad)
default:
panic("unsupported scene command")
}
pathData = pathData[scene.CommandSize+4:]
}
return quads
}
// create GPU vertices for transformed r, find the bounds and establish texture transform.
func (d *drawOps) boundsForTransformedRect(r f32.Rectangle, tr f32.Affine2D) (aux []byte, bnd f32.Rectangle, ptr f32.Affine2D) {
if isPureOffset(tr) {
@@ -1448,3 +1478,16 @@ func isPureOffset(t f32.Affine2D) bool {
a, b, _, d, e, _ := t.Elems()
return a == 1 && b == 0 && d == 0 && e == 1
}
func (q quadSegment) Transform(t f32.Affine2D) quadSegment {
q.From = t.Transform(q.From)
q.Ctrl = t.Transform(q.Ctrl)
q.To = t.Transform(q.To)
return q
}
func decodeQuad(d []byte) (q quadSegment) {
cmd := ops.DecodeCommand(d)
q.From, q.Ctrl, q.To = scene.DecodeQuad(cmd)
return
}
+9 -9
View File
@@ -27,7 +27,7 @@ import (
"math"
"gioui.org/f32"
"gioui.org/internal/ops"
"gioui.org/internal/scene"
"gioui.org/op"
"gioui.org/op/clip"
)
@@ -43,7 +43,7 @@ const strokeTolerance = 0.01
type strokeQuad struct {
contour uint32
quad ops.Quad
quad quadSegment
}
type strokeState struct {
@@ -74,7 +74,7 @@ func (qs *strokeQuads) closed() bool {
func (qs *strokeQuads) lineTo(pt f32.Point) {
end := qs.pen()
*qs = append(*qs, strokeQuad{
quad: ops.Quad{
quad: quadSegment{
From: end,
Ctrl: end.Add(pt).Mul(0.5),
To: pt,
@@ -94,9 +94,9 @@ func (qs *strokeQuads) arc(f1, f2 f32.Point, angle float32) {
end := len(o.Data())
raw := o.Data()[beg:end]
for qi := 0; len(raw) >= (ops.QuadSize + 4); qi++ {
quad := ops.DecodeQuad(raw[4:])
raw = raw[ops.QuadSize+4:]
for qi := 0; len(raw) >= (scene.CommandSize + 4); qi++ {
quad := decodeQuad(raw[4:])
raw = raw[scene.CommandSize+4:]
*qs = append(*qs, strokeQuad{
quad: quad,
})
@@ -240,7 +240,7 @@ func (qs *strokeQuads) close() {
}
*qs = append(*qs, strokeQuad{
quad: ops.Quad{
quad: quadSegment{
From: p0,
Ctrl: p0.Add(p1).Mul(0.5),
To: p1,
@@ -293,7 +293,7 @@ func (qs strokeQuads) append(ps strokeQuads) strokeQuads {
p1 := ps[0].quad.From
if p0 != p1 && lenPt(p0.Sub(p1)) < strokeTolerance {
qs = append(qs, strokeQuad{
quad: ops.Quad{
quad: quadSegment{
From: p0,
Ctrl: p0.Add(p1).Mul(0.5),
To: p1,
@@ -477,7 +477,7 @@ func (qs *strokeQuads) addLine(p0, ctrl, p1 f32.Point, t, d float32) {
*qs = append(*qs,
strokeQuad{
quad: ops.Quad{
quad: quadSegment{
From: p0,
Ctrl: p0.Add(p1).Mul(0.5),
To: p1,