gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/gpu/clip.go (about)

     1  package gpu
     2  
     3  import (
     4  	"encoding/binary"
     5  	"math"
     6  
     7  	"gioui.org/internal/f32"
     8  	"gioui.org/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  }