github.com/utopiagio/gio@v0.0.8/gpu/clip.go (about) 1 package gpu 2 3 import ( 4 "encoding/binary" 5 "math" 6 7 "github.com/utopiagio/gio/internal/f32" 8 "github.com/utopiagio/gio/internal/stroke" 9 ) 10 11 type quadSplitter struct { 12 bounds f32.Rectangle 13 contour uint32 14 d *drawOps 15 16 // scratch space used by calls to stroke.SplitCubic 17 scratch []stroke.QuadSegment 18 } 19 20 func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) { 21 // inlined code: 22 // encodeVertex(data, meta, -1, 1, from, ctrl, to) 23 // encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to) 24 // encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to) 25 // encodeVertex(data[vertStride*3:], meta, 1, -1, from, ctrl, to) 26 // this code needs to stay in sync with `vertex.encode`. 27 28 bo := binary.LittleEndian 29 data = data[:vertStride*4] 30 31 // encode the main template 32 bo.PutUint32(data[4:8], meta) 33 bo.PutUint32(data[8:12], math.Float32bits(from.X)) 34 bo.PutUint32(data[12:16], math.Float32bits(from.Y)) 35 bo.PutUint32(data[16:20], math.Float32bits(ctrl.X)) 36 bo.PutUint32(data[20:24], math.Float32bits(ctrl.Y)) 37 bo.PutUint32(data[24:28], math.Float32bits(to.X)) 38 bo.PutUint32(data[28:32], math.Float32bits(to.Y)) 39 40 copy(data[vertStride*1:vertStride*2], data[vertStride*0:vertStride*1]) 41 copy(data[vertStride*2:vertStride*3], data[vertStride*0:vertStride*1]) 42 copy(data[vertStride*3:vertStride*4], data[vertStride*0:vertStride*1]) 43 44 bo.PutUint32(data[vertStride*0:vertStride*0+4], math.Float32bits(nwCorner)) 45 bo.PutUint32(data[vertStride*1:vertStride*1+4], math.Float32bits(neCorner)) 46 bo.PutUint32(data[vertStride*2:vertStride*2+4], math.Float32bits(swCorner)) 47 bo.PutUint32(data[vertStride*3:vertStride*3+4], math.Float32bits(seCorner)) 48 } 49 50 const ( 51 nwCorner = 1*0.25 + 0*0.5 52 neCorner = 1*0.25 + 1*0.5 53 swCorner = 0*0.25 + 0*0.5 54 seCorner = 0*0.25 + 1*0.5 55 ) 56 57 func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) { 58 var corner float32 59 if cornerx == 1 { 60 corner += .5 61 } 62 if cornery == 1 { 63 corner += .25 64 } 65 v := vertex{ 66 Corner: corner, 67 FromX: from.X, 68 FromY: from.Y, 69 CtrlX: ctrl.X, 70 CtrlY: ctrl.Y, 71 ToX: to.X, 72 ToY: to.Y, 73 } 74 v.encode(data, meta) 75 } 76 77 func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) { 78 data := qs.d.writeVertCache(vertStride * 4) 79 encodeQuadTo(data, qs.contour, from, ctrl, to) 80 } 81 82 func (qs *quadSplitter) splitAndEncode(quad stroke.QuadSegment) { 83 cbnd := f32.Rectangle{ 84 Min: quad.From, 85 Max: quad.To, 86 }.Canon() 87 from, ctrl, to := quad.From, quad.Ctrl, quad.To 88 89 // If the curve contain areas where a vertical line 90 // intersects it twice, split the curve in two x monotone 91 // lower and upper curves. The stencil fragment program 92 // expects only one intersection per curve. 93 94 // Find the t where the derivative in x is 0. 95 v0 := ctrl.Sub(from) 96 v1 := to.Sub(ctrl) 97 d := v0.X - v1.X 98 // t = v0 / d. Split if t is in ]0;1[. 99 if v0.X > 0 && d > v0.X || v0.X < 0 && d < v0.X { 100 t := v0.X / d 101 ctrl0 := from.Mul(1 - t).Add(ctrl.Mul(t)) 102 ctrl1 := ctrl.Mul(1 - t).Add(to.Mul(t)) 103 mid := ctrl0.Mul(1 - t).Add(ctrl1.Mul(t)) 104 qs.encodeQuadTo(from, ctrl0, mid) 105 qs.encodeQuadTo(mid, ctrl1, to) 106 if mid.X > cbnd.Max.X { 107 cbnd.Max.X = mid.X 108 } 109 if mid.X < cbnd.Min.X { 110 cbnd.Min.X = mid.X 111 } 112 } else { 113 qs.encodeQuadTo(from, ctrl, to) 114 } 115 // Find the y extremum, if any. 116 d = v0.Y - v1.Y 117 if v0.Y > 0 && d > v0.Y || v0.Y < 0 && d < v0.Y { 118 t := v0.Y / d 119 y := (1-t)*(1-t)*from.Y + 2*(1-t)*t*ctrl.Y + t*t*to.Y 120 if y > cbnd.Max.Y { 121 cbnd.Max.Y = y 122 } 123 if y < cbnd.Min.Y { 124 cbnd.Min.Y = y 125 } 126 } 127 128 qs.bounds = unionRect(qs.bounds, cbnd) 129 } 130 131 // Union is like f32.Rectangle.Union but ignores empty rectangles. 132 func unionRect(r, s f32.Rectangle) f32.Rectangle { 133 if r.Min.X > s.Min.X { 134 r.Min.X = s.Min.X 135 } 136 if r.Min.Y > s.Min.Y { 137 r.Min.Y = s.Min.Y 138 } 139 if r.Max.X < s.Max.X { 140 r.Max.X = s.Max.X 141 } 142 if r.Max.Y < s.Max.Y { 143 r.Max.Y = s.Max.Y 144 } 145 return r 146 }