mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-08 02:45:38 +00:00
op/clip: compute bounds during Path build
The current renderer transforms and processes paths before sending them to the GPU. It can compute bounds during processing. The new renderer passes paths verbatim to the GPU, but needs the bounds for constructing clip bounds. This change computes the bounds during construction, so it is available at use. As a bonus for storing the bounds with the path, path caches (such as for storing text fragments) automatically reuse the bounds calculations as well. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+1
-2
@@ -760,8 +760,7 @@ func (r *renderer) packStencils(pops *[]*pathOp) {
|
|||||||
*pops = ops
|
*pops = ops
|
||||||
}
|
}
|
||||||
|
|
||||||
// intersects intersects clip and b where b is offset by off.
|
// boundRectF returns a bounding image.Rectangle for a f32.Rectangle.
|
||||||
// ceilRect returns a bounding image.Rectangle for a f32.Rectangle.
|
|
||||||
func boundRectF(r f32.Rectangle) image.Rectangle {
|
func boundRectF(r f32.Rectangle) image.Rectangle {
|
||||||
return image.Rectangle{
|
return image.Rectangle{
|
||||||
Min: image.Point{
|
Min: image.Point{
|
||||||
|
|||||||
+65
-9
@@ -18,8 +18,7 @@ import (
|
|||||||
// Op represents a clip area. Op intersects the current clip area with
|
// Op represents a clip area. Op intersects the current clip area with
|
||||||
// itself.
|
// itself.
|
||||||
type Op struct {
|
type Op struct {
|
||||||
bounds image.Rectangle
|
path PathSpec
|
||||||
path PathSpec
|
|
||||||
|
|
||||||
outline bool
|
outline bool
|
||||||
stroke StrokeStyle
|
stroke StrokeStyle
|
||||||
@@ -47,7 +46,14 @@ func (p Op) Add(o *op.Ops) {
|
|||||||
path.spec.Add(o)
|
path.spec.Add(o)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bounds := path.bounds
|
||||||
if str.Width > 0 {
|
if str.Width > 0 {
|
||||||
|
// Expand bounds to cover stroke.
|
||||||
|
half := int(str.Width*.5 + .5)
|
||||||
|
bounds.Min.X -= half
|
||||||
|
bounds.Min.Y -= half
|
||||||
|
bounds.Max.X += half
|
||||||
|
bounds.Max.Y += half
|
||||||
data := o.Write(opconst.TypeStrokeLen)
|
data := o.Write(opconst.TypeStrokeLen)
|
||||||
data[0] = byte(opconst.TypeStroke)
|
data[0] = byte(opconst.TypeStroke)
|
||||||
bo := binary.LittleEndian
|
bo := binary.LittleEndian
|
||||||
@@ -57,10 +63,10 @@ func (p Op) Add(o *op.Ops) {
|
|||||||
data := o.Write(opconst.TypeClipLen)
|
data := o.Write(opconst.TypeClipLen)
|
||||||
data[0] = byte(opconst.TypeClip)
|
data[0] = byte(opconst.TypeClip)
|
||||||
bo := binary.LittleEndian
|
bo := binary.LittleEndian
|
||||||
bo.PutUint32(data[1:], uint32(p.bounds.Min.X))
|
bo.PutUint32(data[1:], uint32(bounds.Min.X))
|
||||||
bo.PutUint32(data[5:], uint32(p.bounds.Min.Y))
|
bo.PutUint32(data[5:], uint32(bounds.Min.Y))
|
||||||
bo.PutUint32(data[9:], uint32(p.bounds.Max.X))
|
bo.PutUint32(data[9:], uint32(bounds.Max.X))
|
||||||
bo.PutUint32(data[13:], uint32(p.bounds.Max.Y))
|
bo.PutUint32(data[13:], uint32(bounds.Max.Y))
|
||||||
if outline {
|
if outline {
|
||||||
data[17] = byte(1)
|
data[17] = byte(1)
|
||||||
}
|
}
|
||||||
@@ -133,6 +139,7 @@ type PathSpec struct {
|
|||||||
open bool
|
open bool
|
||||||
// hasSegments tracks whether there are any segments in the path.
|
// hasSegments tracks whether there are any segments in the path.
|
||||||
hasSegments bool
|
hasSegments bool
|
||||||
|
bounds image.Rectangle
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path constructs a Op clip path described by lines and
|
// Path constructs a Op clip path described by lines and
|
||||||
@@ -150,6 +157,7 @@ type Path struct {
|
|||||||
macro op.MacroOp
|
macro op.MacroOp
|
||||||
start f32.Point
|
start f32.Point
|
||||||
hasSegments bool
|
hasSegments bool
|
||||||
|
bounds f32.Rectangle
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pos returns the current pen position.
|
// Pos returns the current pen position.
|
||||||
@@ -171,6 +179,7 @@ func (p *Path) End() PathSpec {
|
|||||||
spec: c,
|
spec: c,
|
||||||
open: p.open || p.pen != p.start,
|
open: p.open || p.pen != p.start,
|
||||||
hasSegments: p.hasSegments,
|
hasSegments: p.hasSegments,
|
||||||
|
bounds: boundRectF(p.bounds),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +215,51 @@ func (p *Path) LineTo(to f32.Point) {
|
|||||||
bo.PutUint32(data[0:], uint32(p.contour))
|
bo.PutUint32(data[0:], uint32(p.contour))
|
||||||
ops.EncodeCommand(data[4:], scene.Line(p.pen, to))
|
ops.EncodeCommand(data[4:], scene.Line(p.pen, to))
|
||||||
p.pen = to
|
p.pen = to
|
||||||
p.hasSegments = true
|
p.expand(to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Path) expand(pt f32.Point) {
|
||||||
|
if !p.hasSegments {
|
||||||
|
p.hasSegments = true
|
||||||
|
p.bounds = f32.Rectangle{Min: pt, Max: pt}
|
||||||
|
} else {
|
||||||
|
b := p.bounds
|
||||||
|
if pt.X < b.Min.X {
|
||||||
|
b.Min.X = pt.X
|
||||||
|
}
|
||||||
|
if pt.Y < b.Min.Y {
|
||||||
|
b.Min.Y = pt.Y
|
||||||
|
}
|
||||||
|
if pt.X > b.Max.X {
|
||||||
|
b.Max.X = pt.X
|
||||||
|
}
|
||||||
|
if pt.Y > b.Max.Y {
|
||||||
|
b.Max.Y = pt.Y
|
||||||
|
}
|
||||||
|
p.bounds = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// boundRectF returns a bounding image.Rectangle for a f32.Rectangle.
|
||||||
|
func boundRectF(r f32.Rectangle) image.Rectangle {
|
||||||
|
return image.Rectangle{
|
||||||
|
Min: image.Point{
|
||||||
|
X: int(floor(r.Min.X)),
|
||||||
|
Y: int(floor(r.Min.Y)),
|
||||||
|
},
|
||||||
|
Max: image.Point{
|
||||||
|
X: int(ceil(r.Max.X)),
|
||||||
|
Y: int(ceil(r.Max.Y)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ceil(v float32) int {
|
||||||
|
return int(math.Ceil(float64(v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func floor(v float32) int {
|
||||||
|
return int(math.Floor(float64(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quad records a quadratic Bézier from the pen to end
|
// Quad records a quadratic Bézier from the pen to end
|
||||||
@@ -225,7 +278,8 @@ func (p *Path) QuadTo(ctrl, to f32.Point) {
|
|||||||
bo.PutUint32(data[0:], uint32(p.contour))
|
bo.PutUint32(data[0:], uint32(p.contour))
|
||||||
ops.EncodeCommand(data[4:], scene.Quad(p.pen, ctrl, to))
|
ops.EncodeCommand(data[4:], scene.Quad(p.pen, ctrl, to))
|
||||||
p.pen = to
|
p.pen = to
|
||||||
p.hasSegments = true
|
p.expand(ctrl)
|
||||||
|
p.expand(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arc adds an elliptical arc to the path. The implied ellipse is defined
|
// Arc adds an elliptical arc to the path. The implied ellipse is defined
|
||||||
@@ -265,7 +319,9 @@ func (p *Path) CubeTo(ctrl0, ctrl1, to f32.Point) {
|
|||||||
bo.PutUint32(data[0:], uint32(p.contour))
|
bo.PutUint32(data[0:], uint32(p.contour))
|
||||||
ops.EncodeCommand(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to))
|
ops.EncodeCommand(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to))
|
||||||
p.pen = to
|
p.pen = to
|
||||||
p.hasSegments = true
|
p.expand(ctrl0)
|
||||||
|
p.expand(ctrl1)
|
||||||
|
p.expand(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the path contour.
|
// Close closes the path contour.
|
||||||
|
|||||||
+3
-1
@@ -16,8 +16,10 @@ type Rect image.Rectangle
|
|||||||
// Op returns the op for the rectangle.
|
// Op returns the op for the rectangle.
|
||||||
func (r Rect) Op() Op {
|
func (r Rect) Op() Op {
|
||||||
return Op{
|
return Op{
|
||||||
bounds: image.Rectangle(r),
|
|
||||||
outline: true,
|
outline: true,
|
||||||
|
path: PathSpec{
|
||||||
|
bounds: image.Rectangle(r),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user