clip.go raw

   1  package gpu
   2  
   3  import (
   4  	"github.com/p9c/p9/pkg/gel/gio/f32"
   5  	"github.com/p9c/p9/pkg/gel/gio/internal/stroke"
   6  )
   7  
   8  type quadSplitter struct {
   9  	bounds  f32.Rectangle
  10  	contour uint32
  11  	d       *drawOps
  12  }
  13  
  14  func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) {
  15  	// NW.
  16  	encodeVertex(data, meta, -1, 1, from, ctrl, to)
  17  	// NE.
  18  	encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to)
  19  	// SW.
  20  	encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to)
  21  	// SE.
  22  	encodeVertex(data[vertStride*3:], meta, 1, -1, from, ctrl, to)
  23  }
  24  
  25  func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) {
  26  	var corner float32
  27  	if cornerx == 1 {
  28  		corner += .5
  29  	}
  30  	if cornery == 1 {
  31  		corner += .25
  32  	}
  33  	v := vertex{
  34  		Corner: corner,
  35  		FromX:  from.X,
  36  		FromY:  from.Y,
  37  		CtrlX:  ctrl.X,
  38  		CtrlY:  ctrl.Y,
  39  		ToX:    to.X,
  40  		ToY:    to.Y,
  41  	}
  42  	v.encode(data, meta)
  43  }
  44  
  45  func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) {
  46  	data := qs.d.writeVertCache(vertStride * 4)
  47  	encodeQuadTo(data, qs.contour, from, ctrl, to)
  48  }
  49  
  50  func (qs *quadSplitter) splitAndEncode(quad stroke.QuadSegment) {
  51  	cbnd := f32.Rectangle{
  52  		Min: quad.From,
  53  		Max: quad.To,
  54  	}.Canon()
  55  	from, ctrl, to := quad.From, quad.Ctrl, quad.To
  56  
  57  	// If the curve contain areas where a vertical line
  58  	// intersects it twice, split the curve in two x monotone
  59  	// lower and upper curves. The stencil fragment program
  60  	// expects only one intersection per curve.
  61  
  62  	// Find the t where the derivative in x is 0.
  63  	v0 := ctrl.Sub(from)
  64  	v1 := to.Sub(ctrl)
  65  	d := v0.X - v1.X
  66  	// t = v0 / d. Split if t is in ]0;1[.
  67  	if v0.X > 0 && d > v0.X || v0.X < 0 && d < v0.X {
  68  		t := v0.X / d
  69  		ctrl0 := from.Mul(1 - t).Add(ctrl.Mul(t))
  70  		ctrl1 := ctrl.Mul(1 - t).Add(to.Mul(t))
  71  		mid := ctrl0.Mul(1 - t).Add(ctrl1.Mul(t))
  72  		qs.encodeQuadTo(from, ctrl0, mid)
  73  		qs.encodeQuadTo(mid, ctrl1, to)
  74  		if mid.X > cbnd.Max.X {
  75  			cbnd.Max.X = mid.X
  76  		}
  77  		if mid.X < cbnd.Min.X {
  78  			cbnd.Min.X = mid.X
  79  		}
  80  	} else {
  81  		qs.encodeQuadTo(from, ctrl, to)
  82  	}
  83  	// Find the y extremum, if any.
  84  	d = v0.Y - v1.Y
  85  	if v0.Y > 0 && d > v0.Y || v0.Y < 0 && d < v0.Y {
  86  		t := v0.Y / d
  87  		y := (1-t)*(1-t)*from.Y + 2*(1-t)*t*ctrl.Y + t*t*to.Y
  88  		if y > cbnd.Max.Y {
  89  			cbnd.Max.Y = y
  90  		}
  91  		if y < cbnd.Min.Y {
  92  			cbnd.Min.Y = y
  93  		}
  94  	}
  95  
  96  	qs.bounds = qs.bounds.Union(cbnd)
  97  }
  98