github.com/Seikaijyu/gio@v0.0.1/internal/ops/ops.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package ops
     4  
     5  import (
     6  	"encoding/binary"
     7  	"image"
     8  	"math"
     9  
    10  	"github.com/Seikaijyu/gio/f32"
    11  	"github.com/Seikaijyu/gio/internal/byteslice"
    12  	"github.com/Seikaijyu/gio/internal/scene"
    13  )
    14  
    15  type Ops struct {
    16  	// version is incremented at each Reset.
    17  	version uint32
    18  	// data contains the serialized operations.
    19  	data []byte
    20  	// refs hold external references for operations.
    21  	refs []interface{}
    22  	// stringRefs provides space for string references, pointers to which will
    23  	// be stored in refs. Storing a string directly in refs would cause a heap
    24  	// allocation, to store the string header in an interface value. The backing
    25  	// array of stringRefs, on the other hand, gets reused between calls to
    26  	// reset, making string references free on average.
    27  	//
    28  	// Appending to stringRefs might reallocate the backing array, which will
    29  	// leave pointers to the old array in refs. This temporarily causes a slight
    30  	// increase in memory usage, but this, too, amortizes away as the capacity
    31  	// of stringRefs approaches its stable maximum.
    32  	stringRefs []string
    33  	// nextStateID is the id allocated for the next
    34  	// StateOp.
    35  	nextStateID uint32
    36  	// multipOp indicates a multi-op such as clip.Path is being added.
    37  	multipOp bool
    38  
    39  	macroStack stack
    40  	stacks     [_StackKind]stack
    41  }
    42  
    43  type OpType byte
    44  
    45  type Shape byte
    46  
    47  // Start at a high number for easier debugging.
    48  const firstOpIndex = 200
    49  
    50  const (
    51  	TypeMacro OpType = iota + firstOpIndex
    52  	TypeCall
    53  	TypeDefer
    54  	TypeTransform
    55  	TypePopTransform
    56  	TypePushOpacity
    57  	TypePopOpacity
    58  	TypeInvalidate
    59  	TypeImage
    60  	TypePaint
    61  	TypeColor
    62  	TypeLinearGradient
    63  	TypePass
    64  	TypePopPass
    65  	TypePointerInput
    66  	TypeClipboardRead
    67  	TypeClipboardWrite
    68  	TypeSource
    69  	TypeTarget
    70  	TypeOffer
    71  	TypeKeyInput
    72  	TypeKeyFocus
    73  	TypeKeySoftKeyboard
    74  	TypeSave
    75  	TypeLoad
    76  	TypeAux
    77  	TypeClip
    78  	TypePopClip
    79  	TypeProfile
    80  	TypeCursor
    81  	TypePath
    82  	TypeStroke
    83  	TypeSemanticLabel
    84  	TypeSemanticDesc
    85  	TypeSemanticClass
    86  	TypeSemanticSelected
    87  	TypeSemanticEnabled
    88  	TypeSnippet
    89  	TypeSelection
    90  	TypeActionInput
    91  )
    92  
    93  type StackID struct {
    94  	id   uint32
    95  	prev uint32
    96  }
    97  
    98  // StateOp represents a saved operation snapshot to be restored
    99  // later.
   100  type StateOp struct {
   101  	id      uint32
   102  	macroID uint32
   103  	ops     *Ops
   104  }
   105  
   106  // stack tracks the integer identities of stack operations to ensure correct
   107  // pairing of their push and pop methods.
   108  type stack struct {
   109  	currentID uint32
   110  	nextID    uint32
   111  }
   112  
   113  type StackKind uint8
   114  
   115  // ClipOp is the shadow of clip.Op.
   116  type ClipOp struct {
   117  	Bounds  image.Rectangle
   118  	Outline bool
   119  	Shape   Shape
   120  }
   121  
   122  const (
   123  	ClipStack StackKind = iota
   124  	TransStack
   125  	PassStack
   126  	OpacityStack
   127  	_StackKind
   128  )
   129  
   130  const (
   131  	Path Shape = iota
   132  	Ellipse
   133  	Rect
   134  )
   135  
   136  const (
   137  	TypeMacroLen            = 1 + 4 + 4
   138  	TypeCallLen             = 1 + 4 + 4 + 4 + 4
   139  	TypeDeferLen            = 1
   140  	TypeTransformLen        = 1 + 1 + 4*6
   141  	TypePopTransformLen     = 1
   142  	TypePushOpacityLen      = 1 + 4
   143  	TypePopOpacityLen       = 1
   144  	TypeRedrawLen           = 1 + 8
   145  	TypeImageLen            = 1 + 1
   146  	TypePaintLen            = 1
   147  	TypeColorLen            = 1 + 4
   148  	TypeLinearGradientLen   = 1 + 8*2 + 4*2
   149  	TypePassLen             = 1
   150  	TypePopPassLen          = 1
   151  	TypePointerInputLen     = 1 + 1 + 1*2 + 2*4 + 2*4
   152  	TypeClipboardReadLen    = 1
   153  	TypeClipboardWriteLen   = 1
   154  	TypeSourceLen           = 1
   155  	TypeTargetLen           = 1
   156  	TypeOfferLen            = 1
   157  	TypeKeyInputLen         = 1 + 1
   158  	TypeKeyFocusLen         = 1 + 1
   159  	TypeKeySoftKeyboardLen  = 1 + 1
   160  	TypeSaveLen             = 1 + 4
   161  	TypeLoadLen             = 1 + 4
   162  	TypeAuxLen              = 1
   163  	TypeClipLen             = 1 + 4*4 + 1 + 1
   164  	TypePopClipLen          = 1
   165  	TypeProfileLen          = 1
   166  	TypeCursorLen           = 2
   167  	TypePathLen             = 8 + 1
   168  	TypeStrokeLen           = 1 + 4
   169  	TypeSemanticLabelLen    = 1
   170  	TypeSemanticDescLen     = 1
   171  	TypeSemanticClassLen    = 2
   172  	TypeSemanticSelectedLen = 2
   173  	TypeSemanticEnabledLen  = 2
   174  	TypeSnippetLen          = 1 + 4 + 4
   175  	TypeSelectionLen        = 1 + 2*4 + 2*4 + 4 + 4
   176  	TypeActionInputLen      = 1 + 1
   177  )
   178  
   179  func (op *ClipOp) Decode(data []byte) {
   180  	if len(data) < TypeClipLen || OpType(data[0]) != TypeClip {
   181  		panic("invalid op")
   182  	}
   183  	data = data[:TypeClipLen]
   184  	bo := binary.LittleEndian
   185  	op.Bounds.Min.X = int(int32(bo.Uint32(data[1:])))
   186  	op.Bounds.Min.Y = int(int32(bo.Uint32(data[5:])))
   187  	op.Bounds.Max.X = int(int32(bo.Uint32(data[9:])))
   188  	op.Bounds.Max.Y = int(int32(bo.Uint32(data[13:])))
   189  	op.Outline = data[17] == 1
   190  	op.Shape = Shape(data[18])
   191  }
   192  
   193  func Reset(o *Ops) {
   194  	o.macroStack = stack{}
   195  	o.stacks = [_StackKind]stack{}
   196  	// Leave references to the GC.
   197  	for i := range o.refs {
   198  		o.refs[i] = nil
   199  	}
   200  	for i := range o.stringRefs {
   201  		o.stringRefs[i] = ""
   202  	}
   203  	o.data = o.data[:0]
   204  	o.refs = o.refs[:0]
   205  	o.stringRefs = o.stringRefs[:0]
   206  	o.nextStateID = 0
   207  	o.version++
   208  }
   209  
   210  func Write(o *Ops, n int) []byte {
   211  	if o.multipOp {
   212  		panic("cannot mix multi ops with single ones")
   213  	}
   214  	o.data = append(o.data, make([]byte, n)...)
   215  	return o.data[len(o.data)-n:]
   216  }
   217  
   218  func BeginMulti(o *Ops) {
   219  	if o.multipOp {
   220  		panic("cannot interleave multi ops")
   221  	}
   222  	o.multipOp = true
   223  }
   224  
   225  func EndMulti(o *Ops) {
   226  	if !o.multipOp {
   227  		panic("cannot end non multi ops")
   228  	}
   229  	o.multipOp = false
   230  }
   231  
   232  func WriteMulti(o *Ops, n int) []byte {
   233  	if !o.multipOp {
   234  		panic("cannot use multi ops in single ops")
   235  	}
   236  	o.data = append(o.data, make([]byte, n)...)
   237  	return o.data[len(o.data)-n:]
   238  }
   239  
   240  func PushMacro(o *Ops) StackID {
   241  	return o.macroStack.push()
   242  }
   243  
   244  func PopMacro(o *Ops, id StackID) {
   245  	o.macroStack.pop(id)
   246  }
   247  
   248  func FillMacro(o *Ops, startPC PC) {
   249  	pc := PCFor(o)
   250  	// Fill out the macro definition reserved in Record.
   251  	data := o.data[startPC.data:]
   252  	data = data[:TypeMacroLen]
   253  	data[0] = byte(TypeMacro)
   254  	bo := binary.LittleEndian
   255  	bo.PutUint32(data[1:], uint32(pc.data))
   256  	bo.PutUint32(data[5:], uint32(pc.refs))
   257  }
   258  
   259  func AddCall(o *Ops, callOps *Ops, pc PC, end PC) {
   260  	data := Write1(o, TypeCallLen, callOps)
   261  	data[0] = byte(TypeCall)
   262  	bo := binary.LittleEndian
   263  	bo.PutUint32(data[1:], uint32(pc.data))
   264  	bo.PutUint32(data[5:], uint32(pc.refs))
   265  	bo.PutUint32(data[9:], uint32(end.data))
   266  	bo.PutUint32(data[13:], uint32(end.refs))
   267  }
   268  
   269  func PushOp(o *Ops, kind StackKind) (StackID, uint32) {
   270  	return o.stacks[kind].push(), o.macroStack.currentID
   271  }
   272  
   273  func PopOp(o *Ops, kind StackKind, sid StackID, macroID uint32) {
   274  	if o.macroStack.currentID != macroID {
   275  		panic("stack push and pop must not cross macro boundary")
   276  	}
   277  	o.stacks[kind].pop(sid)
   278  }
   279  
   280  func Write1(o *Ops, n int, ref1 interface{}) []byte {
   281  	o.data = append(o.data, make([]byte, n)...)
   282  	o.refs = append(o.refs, ref1)
   283  	return o.data[len(o.data)-n:]
   284  }
   285  
   286  func Write1String(o *Ops, n int, ref1 string) []byte {
   287  	o.data = append(o.data, make([]byte, n)...)
   288  	o.stringRefs = append(o.stringRefs, ref1)
   289  	o.refs = append(o.refs, &o.stringRefs[len(o.stringRefs)-1])
   290  	return o.data[len(o.data)-n:]
   291  }
   292  
   293  func Write2(o *Ops, n int, ref1, ref2 interface{}) []byte {
   294  	o.data = append(o.data, make([]byte, n)...)
   295  	o.refs = append(o.refs, ref1, ref2)
   296  	return o.data[len(o.data)-n:]
   297  }
   298  
   299  func Write2String(o *Ops, n int, ref1 interface{}, ref2 string) []byte {
   300  	o.data = append(o.data, make([]byte, n)...)
   301  	o.stringRefs = append(o.stringRefs, ref2)
   302  	o.refs = append(o.refs, ref1, &o.stringRefs[len(o.stringRefs)-1])
   303  	return o.data[len(o.data)-n:]
   304  }
   305  
   306  func Write3(o *Ops, n int, ref1, ref2, ref3 interface{}) []byte {
   307  	o.data = append(o.data, make([]byte, n)...)
   308  	o.refs = append(o.refs, ref1, ref2, ref3)
   309  	return o.data[len(o.data)-n:]
   310  }
   311  
   312  func PCFor(o *Ops) PC {
   313  	return PC{data: uint32(len(o.data)), refs: uint32(len(o.refs))}
   314  }
   315  
   316  func (s *stack) push() StackID {
   317  	s.nextID++
   318  	sid := StackID{
   319  		id:   s.nextID,
   320  		prev: s.currentID,
   321  	}
   322  	s.currentID = s.nextID
   323  	return sid
   324  }
   325  
   326  func (s *stack) check(sid StackID) {
   327  	if s.currentID != sid.id {
   328  		panic("unbalanced operation")
   329  	}
   330  }
   331  
   332  func (s *stack) pop(sid StackID) {
   333  	s.check(sid)
   334  	s.currentID = sid.prev
   335  }
   336  
   337  // Save the effective transformation.
   338  func Save(o *Ops) StateOp {
   339  	o.nextStateID++
   340  	s := StateOp{
   341  		ops:     o,
   342  		id:      o.nextStateID,
   343  		macroID: o.macroStack.currentID,
   344  	}
   345  	bo := binary.LittleEndian
   346  	data := Write(o, TypeSaveLen)
   347  	data[0] = byte(TypeSave)
   348  	bo.PutUint32(data[1:], uint32(s.id))
   349  	return s
   350  }
   351  
   352  // Load a previously saved operations state given
   353  // its ID.
   354  func (s StateOp) Load() {
   355  	bo := binary.LittleEndian
   356  	data := Write(s.ops, TypeLoadLen)
   357  	data[0] = byte(TypeLoad)
   358  	bo.PutUint32(data[1:], uint32(s.id))
   359  }
   360  
   361  func DecodeCommand(d []byte) scene.Command {
   362  	var cmd scene.Command
   363  	copy(byteslice.Uint32(cmd[:]), d)
   364  	return cmd
   365  }
   366  
   367  func EncodeCommand(out []byte, cmd scene.Command) {
   368  	copy(out, byteslice.Uint32(cmd[:]))
   369  }
   370  
   371  func DecodeTransform(data []byte) (t f32.Affine2D, push bool) {
   372  	if OpType(data[0]) != TypeTransform {
   373  		panic("invalid op")
   374  	}
   375  	push = data[1] != 0
   376  	data = data[2:]
   377  	data = data[:4*6]
   378  
   379  	bo := binary.LittleEndian
   380  	a := math.Float32frombits(bo.Uint32(data))
   381  	b := math.Float32frombits(bo.Uint32(data[4*1:]))
   382  	c := math.Float32frombits(bo.Uint32(data[4*2:]))
   383  	d := math.Float32frombits(bo.Uint32(data[4*3:]))
   384  	e := math.Float32frombits(bo.Uint32(data[4*4:]))
   385  	f := math.Float32frombits(bo.Uint32(data[4*5:]))
   386  	return f32.NewAffine2D(a, b, c, d, e, f), push
   387  }
   388  
   389  func DecodeOpacity(data []byte) float32 {
   390  	if OpType(data[0]) != TypePushOpacity {
   391  		panic("invalid op")
   392  	}
   393  	bo := binary.LittleEndian
   394  	return math.Float32frombits(bo.Uint32(data[1:]))
   395  }
   396  
   397  // DecodeSave decodes the state id of a save op.
   398  func DecodeSave(data []byte) int {
   399  	if OpType(data[0]) != TypeSave {
   400  		panic("invalid op")
   401  	}
   402  	bo := binary.LittleEndian
   403  	return int(bo.Uint32(data[1:]))
   404  }
   405  
   406  // DecodeLoad decodes the state id of a load op.
   407  func DecodeLoad(data []byte) int {
   408  	if OpType(data[0]) != TypeLoad {
   409  		panic("invalid op")
   410  	}
   411  	bo := binary.LittleEndian
   412  	return int(bo.Uint32(data[1:]))
   413  }
   414  
   415  type opProp struct {
   416  	Size    byte
   417  	NumRefs byte
   418  }
   419  
   420  var opProps = [0x100]opProp{
   421  	TypeMacro:            {Size: TypeMacroLen, NumRefs: 0},
   422  	TypeCall:             {Size: TypeCallLen, NumRefs: 1},
   423  	TypeDefer:            {Size: TypeDeferLen, NumRefs: 0},
   424  	TypeTransform:        {Size: TypeTransformLen, NumRefs: 0},
   425  	TypePopTransform:     {Size: TypePopTransformLen, NumRefs: 0},
   426  	TypePushOpacity:      {Size: TypePushOpacityLen, NumRefs: 0},
   427  	TypePopOpacity:       {Size: TypePopOpacityLen, NumRefs: 0},
   428  	TypeInvalidate:       {Size: TypeRedrawLen, NumRefs: 0},
   429  	TypeImage:            {Size: TypeImageLen, NumRefs: 2},
   430  	TypePaint:            {Size: TypePaintLen, NumRefs: 0},
   431  	TypeColor:            {Size: TypeColorLen, NumRefs: 0},
   432  	TypeLinearGradient:   {Size: TypeLinearGradientLen, NumRefs: 0},
   433  	TypePass:             {Size: TypePassLen, NumRefs: 0},
   434  	TypePopPass:          {Size: TypePopPassLen, NumRefs: 0},
   435  	TypePointerInput:     {Size: TypePointerInputLen, NumRefs: 1},
   436  	TypeClipboardRead:    {Size: TypeClipboardReadLen, NumRefs: 1},
   437  	TypeClipboardWrite:   {Size: TypeClipboardWriteLen, NumRefs: 1},
   438  	TypeSource:           {Size: TypeSourceLen, NumRefs: 2},
   439  	TypeTarget:           {Size: TypeTargetLen, NumRefs: 2},
   440  	TypeOffer:            {Size: TypeOfferLen, NumRefs: 3},
   441  	TypeKeyInput:         {Size: TypeKeyInputLen, NumRefs: 2},
   442  	TypeKeyFocus:         {Size: TypeKeyFocusLen, NumRefs: 1},
   443  	TypeKeySoftKeyboard:  {Size: TypeKeySoftKeyboardLen, NumRefs: 0},
   444  	TypeSave:             {Size: TypeSaveLen, NumRefs: 0},
   445  	TypeLoad:             {Size: TypeLoadLen, NumRefs: 0},
   446  	TypeAux:              {Size: TypeAuxLen, NumRefs: 0},
   447  	TypeClip:             {Size: TypeClipLen, NumRefs: 0},
   448  	TypePopClip:          {Size: TypePopClipLen, NumRefs: 0},
   449  	TypeProfile:          {Size: TypeProfileLen, NumRefs: 1},
   450  	TypeCursor:           {Size: TypeCursorLen, NumRefs: 0},
   451  	TypePath:             {Size: TypePathLen, NumRefs: 0},
   452  	TypeStroke:           {Size: TypeStrokeLen, NumRefs: 0},
   453  	TypeSemanticLabel:    {Size: TypeSemanticLabelLen, NumRefs: 1},
   454  	TypeSemanticDesc:     {Size: TypeSemanticDescLen, NumRefs: 1},
   455  	TypeSemanticClass:    {Size: TypeSemanticClassLen, NumRefs: 0},
   456  	TypeSemanticSelected: {Size: TypeSemanticSelectedLen, NumRefs: 0},
   457  	TypeSemanticEnabled:  {Size: TypeSemanticEnabledLen, NumRefs: 0},
   458  	TypeSnippet:          {Size: TypeSnippetLen, NumRefs: 2},
   459  	TypeSelection:        {Size: TypeSelectionLen, NumRefs: 1},
   460  	TypeActionInput:      {Size: TypeActionInputLen, NumRefs: 0},
   461  }
   462  
   463  func (t OpType) props() (size, numRefs uint32) {
   464  	v := opProps[t]
   465  	return uint32(v.Size), uint32(v.NumRefs)
   466  }
   467  
   468  func (t OpType) Size() uint32 {
   469  	return uint32(opProps[t].Size)
   470  }
   471  
   472  func (t OpType) NumRefs() uint32 {
   473  	return uint32(opProps[t].NumRefs)
   474  }
   475  
   476  func (t OpType) String() string {
   477  	switch t {
   478  	case TypeMacro:
   479  		return "Macro"
   480  	case TypeCall:
   481  		return "Call"
   482  	case TypeDefer:
   483  		return "Defer"
   484  	case TypeTransform:
   485  		return "Transform"
   486  	case TypePopTransform:
   487  		return "PopTransform"
   488  	case TypePushOpacity:
   489  		return "PushOpacity"
   490  	case TypePopOpacity:
   491  		return "PopOpacity"
   492  	case TypeInvalidate:
   493  		return "Invalidate"
   494  	case TypeImage:
   495  		return "Image"
   496  	case TypePaint:
   497  		return "Paint"
   498  	case TypeColor:
   499  		return "Color"
   500  	case TypeLinearGradient:
   501  		return "LinearGradient"
   502  	case TypePass:
   503  		return "Pass"
   504  	case TypePopPass:
   505  		return "PopPass"
   506  	case TypePointerInput:
   507  		return "PointerInput"
   508  	case TypeClipboardRead:
   509  		return "ClipboardRead"
   510  	case TypeClipboardWrite:
   511  		return "ClipboardWrite"
   512  	case TypeSource:
   513  		return "Source"
   514  	case TypeTarget:
   515  		return "Target"
   516  	case TypeOffer:
   517  		return "Offer"
   518  	case TypeKeyInput:
   519  		return "KeyInput"
   520  	case TypeKeyFocus:
   521  		return "KeyFocus"
   522  	case TypeKeySoftKeyboard:
   523  		return "KeySoftKeyboard"
   524  	case TypeSave:
   525  		return "Save"
   526  	case TypeLoad:
   527  		return "Load"
   528  	case TypeAux:
   529  		return "Aux"
   530  	case TypeClip:
   531  		return "Clip"
   532  	case TypePopClip:
   533  		return "PopClip"
   534  	case TypeProfile:
   535  		return "Profile"
   536  	case TypeCursor:
   537  		return "Cursor"
   538  	case TypePath:
   539  		return "Path"
   540  	case TypeStroke:
   541  		return "Stroke"
   542  	case TypeSemanticLabel:
   543  		return "SemanticDescription"
   544  	default:
   545  		panic("unknown OpType")
   546  	}
   547  }