forked from joejulian/gio
5b277757cf
This is a first step towards supporting affine drawing transforms. The rendering algorithm relies on quadratic curves that do not cross x = 0 more than once, thus curves must be split after any rotation/shear transforms. Move this logic and the generation of vertices to package gpu. Also close all curves and draw zero-width edges as preparation for transform since the will no longer implicitly be vertical with no effect. This commit will severely affect performance since vertexes are now transformed also for cached items, using cpu resources. Signed-off-by: Viktor <viktor.ogeman@gmail.com>
99 lines
2.2 KiB
Go
99 lines
2.2 KiB
Go
package gpu
|
|
|
|
import (
|
|
"gioui.org/f32"
|
|
"gioui.org/internal/ops"
|
|
)
|
|
|
|
type quadSplitter struct {
|
|
verts []byte
|
|
bounds f32.Rectangle
|
|
contour uint32
|
|
d *drawOps
|
|
}
|
|
|
|
func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) {
|
|
// NW.
|
|
encodeVertex(data, meta, -1, 1, from, ctrl, to)
|
|
// NE.
|
|
encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to)
|
|
// SW.
|
|
encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to)
|
|
// SE.
|
|
encodeVertex(data[vertStride*3:], meta, 1, -1, from, ctrl, to)
|
|
}
|
|
|
|
func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) {
|
|
var corner float32
|
|
if cornerx == 1 {
|
|
corner += .5
|
|
}
|
|
if cornery == 1 {
|
|
corner += .25
|
|
}
|
|
v := vertex{
|
|
Corner: corner,
|
|
FromX: from.X,
|
|
FromY: from.Y,
|
|
CtrlX: ctrl.X,
|
|
CtrlY: ctrl.Y,
|
|
ToX: to.X,
|
|
ToY: to.Y,
|
|
}
|
|
v.encode(data, meta)
|
|
}
|
|
|
|
func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) {
|
|
data := qs.d.writeVertCache(vertStride * 4)
|
|
encodeQuadTo(data, qs.contour, from, ctrl, to)
|
|
}
|
|
|
|
func (qs *quadSplitter) splitAndEncode(quad ops.Quad) {
|
|
cbnd := f32.Rectangle{
|
|
Min: quad.From,
|
|
Max: quad.To,
|
|
}.Canon()
|
|
from, ctrl, to := quad.From, quad.Ctrl, quad.To
|
|
|
|
// If the curve contain areas where a vertical line
|
|
// intersects it twice, split the curve in two x monotone
|
|
// lower and upper curves. The stencil fragment program
|
|
// expects only one intersection per curve.
|
|
|
|
// Find the t where the derivative in x is 0.
|
|
v0 := ctrl.Sub(from)
|
|
v1 := to.Sub(ctrl)
|
|
d := v0.X - v1.X
|
|
// t = v0 / d. Split if t is in ]0;1[.
|
|
if v0.X > 0 && d > v0.X || v0.X < 0 && d < v0.X {
|
|
t := v0.X / d
|
|
ctrl0 := from.Mul(1 - t).Add(ctrl.Mul(t))
|
|
ctrl1 := ctrl.Mul(1 - t).Add(to.Mul(t))
|
|
mid := ctrl0.Mul(1 - t).Add(ctrl1.Mul(t))
|
|
qs.encodeQuadTo(from, ctrl0, mid)
|
|
qs.encodeQuadTo(mid, ctrl1, to)
|
|
if mid.X > cbnd.Max.X {
|
|
cbnd.Max.X = mid.X
|
|
}
|
|
if mid.X < cbnd.Min.X {
|
|
cbnd.Min.X = mid.X
|
|
}
|
|
} else {
|
|
qs.encodeQuadTo(from, ctrl, to)
|
|
}
|
|
// Find the y extremum, if any.
|
|
d = v0.Y - v1.Y
|
|
if v0.Y > 0 && d > v0.Y || v0.Y < 0 && d < v0.Y {
|
|
t := v0.Y / d
|
|
y := (1-t)*(1-t)*from.Y + 2*(1-t)*t*ctrl.Y + t*t*to.Y
|
|
if y > cbnd.Max.Y {
|
|
cbnd.Max.Y = y
|
|
}
|
|
if y < cbnd.Min.Y {
|
|
cbnd.Min.Y = y
|
|
}
|
|
}
|
|
|
|
qs.bounds = qs.bounds.Union(cbnd)
|
|
}
|