123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- package gpu
- import (
- "encoding/binary"
- "math"
- "gioui.org/internal/f32"
- "gioui.org/internal/stroke"
- )
- type quadSplitter struct {
- bounds f32.Rectangle
- contour uint32
- d *drawOps
- // scratch space used by calls to stroke.SplitCubic
- scratch []stroke.QuadSegment
- }
- func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) {
- // inlined code:
- // encodeVertex(data, meta, -1, 1, from, ctrl, to)
- // encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to)
- // encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to)
- // encodeVertex(data[vertStride*3:], meta, 1, -1, from, ctrl, to)
- // this code needs to stay in sync with `vertex.encode`.
- bo := binary.LittleEndian
- data = data[:vertStride*4]
- // encode the main template
- bo.PutUint32(data[4:8], meta)
- bo.PutUint32(data[8:12], math.Float32bits(from.X))
- bo.PutUint32(data[12:16], math.Float32bits(from.Y))
- bo.PutUint32(data[16:20], math.Float32bits(ctrl.X))
- bo.PutUint32(data[20:24], math.Float32bits(ctrl.Y))
- bo.PutUint32(data[24:28], math.Float32bits(to.X))
- bo.PutUint32(data[28:32], math.Float32bits(to.Y))
- copy(data[vertStride*1:vertStride*2], data[vertStride*0:vertStride*1])
- copy(data[vertStride*2:vertStride*3], data[vertStride*0:vertStride*1])
- copy(data[vertStride*3:vertStride*4], data[vertStride*0:vertStride*1])
- bo.PutUint32(data[vertStride*0:vertStride*0+4], math.Float32bits(nwCorner))
- bo.PutUint32(data[vertStride*1:vertStride*1+4], math.Float32bits(neCorner))
- bo.PutUint32(data[vertStride*2:vertStride*2+4], math.Float32bits(swCorner))
- bo.PutUint32(data[vertStride*3:vertStride*3+4], math.Float32bits(seCorner))
- }
- const (
- nwCorner = 1*0.25 + 0*0.5
- neCorner = 1*0.25 + 1*0.5
- swCorner = 0*0.25 + 0*0.5
- seCorner = 0*0.25 + 1*0.5
- )
- 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 stroke.QuadSegment) {
- 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 = unionRect(qs.bounds, cbnd)
- }
- // Union is like f32.Rectangle.Union but ignores empty rectangles.
- func unionRect(r, s f32.Rectangle) f32.Rectangle {
- if r.Min.X > s.Min.X {
- r.Min.X = s.Min.X
- }
- if r.Min.Y > s.Min.Y {
- r.Min.Y = s.Min.Y
- }
- if r.Max.X < s.Max.X {
- r.Max.X = s.Max.X
- }
- if r.Max.Y < s.Max.Y {
- r.Max.Y = s.Max.Y
- }
- return r
- }
|