diff --git a/font/opentype/opentype_test.go b/font/opentype/opentype_test.go index 59b73243..748b8e9c 100644 --- a/font/opentype/opentype_test.go +++ b/font/opentype/opentype_test.go @@ -192,8 +192,8 @@ func areShapesEqual(shape1, shape2 clip.Op) bool { shape1.Push(&ops1).Pop() shape2.Push(&ops2).Pop() var r1, r2 ops.Reader - r1.Reset(&ops1) - r2.Reset(&ops2) + r1.Reset(&ops1.Internal) + r2.Reset(&ops2.Internal) for { encOp1, ok1 := r1.Decode() encOp2, ok2 := r2.Decode() diff --git a/gpu/compute.go b/gpu/compute.go index dc440a3c..ee4eccc3 100644 --- a/gpu/compute.go +++ b/gpu/compute.go @@ -24,7 +24,6 @@ import ( "gioui.org/gpu/internal/driver" "gioui.org/internal/byteslice" "gioui.org/internal/f32color" - "gioui.org/internal/opconst" "gioui.org/internal/ops" "gioui.org/internal/scene" "gioui.org/layout" @@ -1726,7 +1725,7 @@ func (c *collector) addClip(state *encoderState, viewport, bounds f32.Rectangle, func (c *collector) collect(root *op.Ops, viewport image.Point, texOps *[]textureOp) { fview := f32.Rectangle{Max: layout.FPt(viewport)} - c.reader.Reset(root) + c.reader.Reset(&root.Internal) var state encoderState reset := func() { state = encoderState{ @@ -1747,40 +1746,40 @@ func (c *collector) collect(root *op.Ops, viewport image.Point, texOps *[]textur ) c.addClip(&state, fview, fview, nil, ops.Key{}, 0, clip.StrokeStyle{}, false) for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { - switch opconst.OpType(encOp.Data[0]) { - case opconst.TypeProfile: + switch ops.OpType(encOp.Data[0]) { + case ops.TypeProfile: c.profile = true - case opconst.TypeTransform: + case ops.TypeTransform: dop, push := ops.DecodeTransform(encOp.Data) if push { c.transStack = append(c.transStack, transEntry{t: state.t, relTrans: state.relTrans}) } state.t = state.t.Mul(dop) state.relTrans = state.relTrans.Mul(dop) - case opconst.TypePopTransform: + case ops.TypePopTransform: n := len(c.transStack) st := c.transStack[n-1] c.transStack = c.transStack[:n-1] state.t = st.t state.relTrans = st.relTrans - case opconst.TypeStroke: + case ops.TypeStroke: str = decodeStrokeOp(encOp.Data) - case opconst.TypePath: + case ops.TypePath: hash := bo.Uint64(encOp.Data[1:]) encOp, ok = r.Decode() if !ok { panic("unexpected end of path operation") } - pathData.data = encOp.Data[opconst.TypeAuxLen:] + pathData.data = encOp.Data[ops.TypeAuxLen:] pathData.key = encOp.Key pathData.hash = hash - case opconst.TypeClip: + case ops.TypeClip: var op clipOp op.decode(encOp.Data) c.addClip(&state, fview, op.bounds, pathData.data, pathData.key, pathData.hash, str, op.push) pathData.data = nil str = clip.StrokeStyle{} - case opconst.TypePopClip: + case ops.TypePopClip: for { push := state.clip.push state.relTrans = state.clip.relTrans.Mul(state.relTrans) @@ -1789,20 +1788,20 @@ func (c *collector) collect(root *op.Ops, viewport image.Point, texOps *[]textur break } } - case opconst.TypeColor: + case ops.TypeColor: state.matType = materialColor state.color = decodeColorOp(encOp.Data) - case opconst.TypeLinearGradient: + case ops.TypeLinearGradient: state.matType = materialLinearGradient op := decodeLinearGradientOp(encOp.Data) state.stop1 = op.stop1 state.stop2 = op.stop2 state.color1 = op.color1 state.color2 = op.color2 - case opconst.TypeImage: + case ops.TypeImage: state.matType = materialTexture state.image = decodeImageOp(encOp.Data, encOp.Refs) - case opconst.TypePaint: + case ops.TypePaint: paintState := state if paintState.matType == materialTexture { // Clip to the bounds of the image, to hide other images in the atlas. @@ -1846,10 +1845,10 @@ func (c *collector) collect(root *op.Ops, viewport image.Point, texOps *[]textur state: paintState.paintKey, intersect: intersect, }) - case opconst.TypeSave: + case ops.TypeSave: id := ops.DecodeSave(encOp.Data) c.save(id, state.t) - case opconst.TypeLoad: + case ops.TypeLoad: reset() id := ops.DecodeLoad(encOp.Data) state.t = c.states[id] diff --git a/gpu/gpu.go b/gpu/gpu.go index e1a01e82..7cd7df40 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -22,7 +22,6 @@ import ( "gioui.org/gpu/internal/driver" "gioui.org/internal/byteslice" "gioui.org/internal/f32color" - "gioui.org/internal/opconst" "gioui.org/internal/ops" "gioui.org/internal/scene" "gioui.org/internal/stroke" @@ -138,7 +137,7 @@ type imageOp struct { func decodeStrokeOp(data []byte) clip.StrokeStyle { _ = data[4] - if opconst.OpType(data[0]) != opconst.TypeStroke { + if ops.OpType(data[0]) != ops.TypeStroke { panic("invalid op") } bo := binary.LittleEndian @@ -192,7 +191,7 @@ type linearGradientOpData struct { } func (op *clipOp) decode(data []byte) { - if opconst.OpType(data[0]) != opconst.TypeClip { + if ops.OpType(data[0]) != ops.TypeClip { panic("invalid op") } bo := binary.LittleEndian @@ -214,7 +213,7 @@ func (op *clipOp) decode(data []byte) { } func decodeImageOp(data []byte, refs []interface{}) imageOpData { - if opconst.OpType(data[0]) != opconst.TypeImage { + if ops.OpType(data[0]) != ops.TypeImage { panic("invalid op") } handle := refs[1] @@ -228,7 +227,7 @@ func decodeImageOp(data []byte, refs []interface{}) imageOpData { } func decodeColorOp(data []byte) color.NRGBA { - if opconst.OpType(data[0]) != opconst.TypeColor { + if ops.OpType(data[0]) != ops.TypeColor { panic("invalid op") } return color.NRGBA{ @@ -240,7 +239,7 @@ func decodeColorOp(data []byte) color.NRGBA { } func decodeLinearGradientOp(data []byte) linearGradientOpData { - if opconst.OpType(data[0]) != opconst.TypeLinearGradient { + if ops.OpType(data[0]) != ops.TypeLinearGradient { panic("invalid op") } bo := binary.LittleEndian @@ -835,7 +834,7 @@ func (d *drawOps) collect(root *op.Ops, viewport image.Point) { viewf := f32.Rectangle{ Max: f32.Point{X: float32(viewport.X), Y: float32(viewport.Y)}, } - d.reader.Reset(root) + d.reader.Reset(&root.Internal) d.collectOps(&d.reader, viewf) } @@ -920,32 +919,32 @@ func (d *drawOps) collectOps(r *ops.Reader, viewport f32.Rectangle) { reset() loop: for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { - switch opconst.OpType(encOp.Data[0]) { - case opconst.TypeProfile: + switch ops.OpType(encOp.Data[0]) { + case ops.TypeProfile: d.profile = true - case opconst.TypeTransform: + case ops.TypeTransform: dop, push := ops.DecodeTransform(encOp.Data) if push { d.transStack = append(d.transStack, state.t) } state.t = state.t.Mul(dop) - case opconst.TypePopTransform: + case ops.TypePopTransform: n := len(d.transStack) state.t = d.transStack[n-1] d.transStack = d.transStack[:n-1] - case opconst.TypeStroke: + case ops.TypeStroke: str = decodeStrokeOp(encOp.Data) - case opconst.TypePath: + case ops.TypePath: encOp, ok = r.Decode() if !ok { break loop } - quads.aux = encOp.Data[opconst.TypeAuxLen:] + quads.aux = encOp.Data[ops.TypeAuxLen:] quads.key = opKey{Key: encOp.Key} - case opconst.TypeClip: + case ops.TypeClip: var op clipOp op.decode(encOp.Data) bounds := op.bounds @@ -976,7 +975,7 @@ loop: d.addClipPath(&state, quads.aux, quads.key, op.bounds, off, op.push) quads = quadsOp{} str = clip.StrokeStyle{} - case opconst.TypePopClip: + case ops.TypePopClip: for { push := state.cpath.push state.cpath = state.cpath.parent @@ -985,20 +984,20 @@ loop: } } - case opconst.TypeColor: + case ops.TypeColor: state.matType = materialColor state.color = decodeColorOp(encOp.Data) - case opconst.TypeLinearGradient: + case ops.TypeLinearGradient: state.matType = materialLinearGradient op := decodeLinearGradientOp(encOp.Data) state.stop1 = op.stop1 state.stop2 = op.stop2 state.color1 = op.color1 state.color2 = op.color2 - case opconst.TypeImage: + case ops.TypeImage: state.matType = materialTexture state.image = decodeImageOp(encOp.Data, encOp.Refs) - case opconst.TypePaint: + case ops.TypePaint: // Transform (if needed) the painting rectangle and if so generate a clip path, // for those cases also compute a partialTrans that maps texture coordinates between // the new bounding rectangle and the transformed original paint rectangle. @@ -1050,10 +1049,10 @@ loop: // we added a clip path that should not remain state.cpath = state.cpath.parent } - case opconst.TypeSave: + case ops.TypeSave: id := ops.DecodeSave(encOp.Data) d.save(id, state.t) - case opconst.TypeLoad: + case ops.TypeLoad: reset() id := ops.DecodeLoad(encOp.Data) state.t = d.states[id] diff --git a/internal/opconst/ops.go b/internal/opconst/ops.go deleted file mode 100644 index 6671a67d..00000000 --- a/internal/opconst/ops.go +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package opconst - -type OpType byte - -// Start at a high number for easier debugging. -const firstOpIndex = 200 - -const ( - TypeMacro OpType = iota + firstOpIndex - TypeCall - TypeDefer - TypeTransform - TypePopTransform - TypeInvalidate - TypeImage - TypePaint - TypeColor - TypeLinearGradient - TypeArea - TypePopArea - TypePointerInput - TypeClipboardRead - TypeClipboardWrite - TypeKeyInput - TypeKeyFocus - TypeKeySoftKeyboard - TypeSave - TypeLoad - TypeAux - TypeClip - TypePopClip - TypeProfile - TypeCursor - TypePath - TypeStroke -) - -const ( - TypeMacroLen = 1 + 4 + 4 - TypeCallLen = 1 + 4 + 4 - TypeDeferLen = 1 - TypeTransformLen = 1 + 1 + 4*6 - TypePopTransformLen = 1 - TypeRedrawLen = 1 + 8 - TypeImageLen = 1 - TypePaintLen = 1 - TypeColorLen = 1 + 4 - TypeLinearGradientLen = 1 + 8*2 + 4*2 - TypeAreaLen = 1 + 1 + 1 + 4*4 - TypePopAreaLen = 1 - TypePointerInputLen = 1 + 1 + 1 + 2*4 + 2*4 - TypeClipboardReadLen = 1 - TypeClipboardWriteLen = 1 - TypeKeyInputLen = 1 + 1 - TypeKeyFocusLen = 1 + 1 - TypeKeySoftKeyboardLen = 1 + 1 - TypeSaveLen = 1 + 4 - TypeLoadLen = 1 + 4 - TypeAuxLen = 1 - TypeClipLen = 1 + 4*4 + 1 + 1 - TypePopClipLen = 1 - TypeProfileLen = 1 - TypeCursorLen = 1 + 1 - TypePathLen = 8 + 1 - TypeStrokeLen = 1 + 4 -) - -func (t OpType) Size() int { - return [...]int{ - TypeMacroLen, - TypeCallLen, - TypeDeferLen, - TypeTransformLen, - TypePopTransformLen, - TypeRedrawLen, - TypeImageLen, - TypePaintLen, - TypeColorLen, - TypeLinearGradientLen, - TypeAreaLen, - TypePopAreaLen, - TypePointerInputLen, - TypeClipboardReadLen, - TypeClipboardWriteLen, - TypeKeyInputLen, - TypeKeyFocusLen, - TypeKeySoftKeyboardLen, - TypeSaveLen, - TypeLoadLen, - TypeAuxLen, - TypeClipLen, - TypePopClipLen, - TypeProfileLen, - TypeCursorLen, - TypePathLen, - TypeStrokeLen, - }[t-firstOpIndex] -} - -func (t OpType) NumRefs() int { - switch t { - case TypeKeyInput, TypeKeyFocus, TypePointerInput, TypeProfile, TypeCall, TypeClipboardRead, TypeClipboardWrite, TypeCursor: - return 1 - case TypeImage: - return 2 - default: - return 0 - } -} - -func (t OpType) String() string { - switch t { - case TypeMacro: - return "Macro" - case TypeCall: - return "Call" - case TypeDefer: - return "Defer" - case TypeTransform: - return "Transform" - case TypePopTransform: - return "PopTransform" - case TypeInvalidate: - return "Invalidate" - case TypeImage: - return "Image" - case TypePaint: - return "Paint" - case TypeColor: - return "Color" - case TypeLinearGradient: - return "LinearGradient" - case TypeArea: - return "Area" - case TypePopArea: - return "PopArea" - case TypePointerInput: - return "PointerInput" - case TypeClipboardRead: - return "ClipboardRead" - case TypeClipboardWrite: - return "ClipboardWrite" - case TypeKeyInput: - return "KeyInput" - case TypeKeyFocus: - return "KeyFocus" - case TypeKeySoftKeyboard: - return "KeySoftKeyboard" - case TypeSave: - return "Save" - case TypeLoad: - return "Load" - case TypeAux: - return "Aux" - case TypeClip: - return "Clip" - case TypePopClip: - return "PopClip" - case TypeProfile: - return "Profile" - case TypeCursor: - return "Cursor" - case TypePath: - return "Path" - case TypeStroke: - return "Stroke" - default: - panic("unnkown OpType") - } -} diff --git a/internal/ops/ops.go b/internal/ops/ops.go index 769c7745..0219689d 100644 --- a/internal/ops/ops.go +++ b/internal/ops/ops.go @@ -8,10 +8,250 @@ import ( "gioui.org/f32" "gioui.org/internal/byteslice" - "gioui.org/internal/opconst" "gioui.org/internal/scene" ) +type Ops struct { + // version is incremented at each Reset. + version int + // data contains the serialized operations. + data []byte + // refs hold external references for operations. + refs []interface{} + // nextStateID is the id allocated for the next + // StateOp. + nextStateID int + + macroStack stack + stacks [3]stack +} + +type OpType byte + +// Start at a high number for easier debugging. +const firstOpIndex = 200 + +const ( + TypeMacro OpType = iota + firstOpIndex + TypeCall + TypeDefer + TypePushTransform + TypeTransform + TypePopTransform + TypeInvalidate + TypeImage + TypePaint + TypeColor + TypeLinearGradient + TypeArea + TypePopArea + TypePointerInput + TypeClipboardRead + TypeClipboardWrite + TypeKeyInput + TypeKeyFocus + TypeKeySoftKeyboard + TypeSave + TypeLoad + TypeAux + TypeClip + TypePopClip + TypeProfile + TypeCursor + TypePath + TypeStroke +) + +type StackID struct { + id int + prev int +} + +// StateOp represents a saved operation snapshop to be restored +// later. +type StateOP struct { + id int + macroID int + ops *Ops +} + +// stack tracks the integer identities of stack operations to ensure correct +// pairing of their push and pop methods. +type stack struct { + currentID int + nextID int +} + +type StackKind uint8 + +const ( + ClipStack StackKind = iota + AreaStack + TransStack +) + +const ( + TypeMacroLen = 1 + 4 + 4 + TypeCallLen = 1 + 4 + 4 + TypeDeferLen = 1 + TypePushTransformLen = 1 + 4*6 + TypeTransformLen = 1 + 1 + 4*6 + TypePopTransformLen = 1 + TypeRedrawLen = 1 + 8 + TypeImageLen = 1 + TypePaintLen = 1 + TypeColorLen = 1 + 4 + TypeLinearGradientLen = 1 + 8*2 + 4*2 + TypeAreaLen = 1 + 1 + 1 + 4*4 + TypePopAreaLen = 1 + TypePointerInputLen = 1 + 1 + 1 + 2*4 + 2*4 + TypeClipboardReadLen = 1 + TypeClipboardWriteLen = 1 + TypeKeyInputLen = 1 + 1 + TypeKeyFocusLen = 1 + 1 + TypeKeySoftKeyboardLen = 1 + 1 + TypeSaveLen = 1 + 4 + TypeLoadLen = 1 + 4 + TypeAuxLen = 1 + TypeClipLen = 1 + 4*4 + 1 + 1 + TypePopClipLen = 1 + TypeProfileLen = 1 + TypeCursorLen = 1 + 1 + TypePathLen = 8 + 1 + TypeStrokeLen = 1 + 4 +) + +func (o *Ops) Reset() { + o.macroStack = stack{} + for i := range o.stacks { + o.stacks[i] = stack{} + } + // Leave references to the GC. + for i := range o.refs { + o.refs[i] = nil + } + o.data = o.data[:0] + o.refs = o.refs[:0] + o.nextStateID = 0 + o.version++ +} + +func (o *Ops) Data() []byte { + return o.data +} + +func (o *Ops) Refs() []interface{} { + return o.refs +} + +func (o *Ops) Version() int { + return o.version +} + +func (o *Ops) Write(n int) []byte { + o.data = append(o.data, make([]byte, n)...) + return o.data[len(o.data)-n:] +} + +func (o *Ops) PushMacro() StackID { + return o.macroStack.push() +} + +func (o *Ops) PopMacro(id StackID) { + o.macroStack.pop(id) +} + +func (o *Ops) FillMacro(startPC PC) { + pc := o.PC() + // Fill out the macro definition reserved in Record. + data := o.data[startPC.data:] + data = data[:TypeMacroLen] + data[0] = byte(TypeMacro) + bo := binary.LittleEndian + bo.PutUint32(data[1:], uint32(pc.data)) + bo.PutUint32(data[5:], uint32(pc.refs)) +} + +func (o *Ops) AddCall(callOps *Ops, pc PC) { + data := o.Write1(TypeCallLen, callOps) + data[0] = byte(TypeCall) + bo := binary.LittleEndian + bo.PutUint32(data[1:], uint32(pc.data)) + bo.PutUint32(data[5:], uint32(pc.refs)) +} + +func (o *Ops) PushOp(kind StackKind) (StackID, int) { + return o.stacks[kind].push(), o.macroStack.currentID +} + +func (o *Ops) PopOp(kind StackKind, sid StackID, macroID int) { + if o.macroStack.currentID != macroID { + panic("stack push and pop must not cross macro boundary") + } + o.stacks[kind].pop(sid) +} + +func (o *Ops) Write1(n int, ref1 interface{}) []byte { + o.data = append(o.data, make([]byte, n)...) + o.refs = append(o.refs, ref1) + return o.data[len(o.data)-n:] +} + +func (o *Ops) Write2(n int, ref1, ref2 interface{}) []byte { + o.data = append(o.data, make([]byte, n)...) + o.refs = append(o.refs, ref1, ref2) + return o.data[len(o.data)-n:] +} + +func (o *Ops) PC() PC { + return PC{data: len(o.data), refs: len(o.refs)} +} + +func (s *stack) push() StackID { + s.nextID++ + sid := StackID{ + id: s.nextID, + prev: s.currentID, + } + s.currentID = s.nextID + return sid +} + +func (s *stack) check(sid StackID) { + if s.currentID != sid.id { + panic("unbalanced operation") + } +} + +func (s *stack) pop(sid StackID) { + s.check(sid) + s.currentID = sid.prev +} + +// Save the effective transformation. +func (o *Ops) Save() StateOP { + o.nextStateID++ + s := StateOP{ + ops: o, + id: o.nextStateID, + macroID: o.macroStack.currentID, + } + bo := binary.LittleEndian + data := o.Write(TypeSaveLen) + data[0] = byte(TypeSave) + bo.PutUint32(data[1:], uint32(s.id)) + return s +} + +// load a previously saved operations state given +// its ID. +func (s StateOP) Load() { + bo := binary.LittleEndian + data := s.ops.Write(TypeLoadLen) + data[0] = byte(TypeLoad) + bo.PutUint32(data[1:], uint32(s.id)) +} + func DecodeCommand(d []byte) scene.Command { var cmd scene.Command copy(byteslice.Uint32(cmd[:]), d) @@ -23,7 +263,7 @@ func EncodeCommand(out []byte, cmd scene.Command) { } func DecodeTransform(data []byte) (t f32.Affine2D, push bool) { - if opconst.OpType(data[0]) != opconst.TypeTransform { + if OpType(data[0]) != TypeTransform { panic("invalid op") } push = data[1] != 0 @@ -42,7 +282,7 @@ func DecodeTransform(data []byte) (t f32.Affine2D, push bool) { // DecodeSave decodes the state id of a save op. func DecodeSave(data []byte) int { - if opconst.OpType(data[0]) != opconst.TypeSave { + if OpType(data[0]) != TypeSave { panic("invalid op") } bo := binary.LittleEndian @@ -51,9 +291,116 @@ func DecodeSave(data []byte) int { // DecodeLoad decodes the state id of a load op. func DecodeLoad(data []byte) int { - if opconst.OpType(data[0]) != opconst.TypeLoad { + if OpType(data[0]) != TypeLoad { panic("invalid op") } bo := binary.LittleEndian return int(bo.Uint32(data[1:])) } + +func (t OpType) Size() int { + return [...]int{ + TypeMacroLen, + TypeCallLen, + TypeDeferLen, + TypePushTransformLen, + TypeTransformLen, + TypePopTransformLen, + TypeRedrawLen, + TypeImageLen, + TypePaintLen, + TypeColorLen, + TypeLinearGradientLen, + TypeAreaLen, + TypePopAreaLen, + TypePointerInputLen, + TypeClipboardReadLen, + TypeClipboardWriteLen, + TypeKeyInputLen, + TypeKeyFocusLen, + TypeKeySoftKeyboardLen, + TypeSaveLen, + TypeLoadLen, + TypeAuxLen, + TypeClipLen, + TypePopClipLen, + TypeProfileLen, + TypeCursorLen, + TypePathLen, + TypeStrokeLen, + }[t-firstOpIndex] +} + +func (t OpType) NumRefs() int { + switch t { + case TypeKeyInput, TypeKeyFocus, TypePointerInput, TypeProfile, TypeCall, TypeClipboardRead, TypeClipboardWrite, TypeCursor: + return 1 + case TypeImage: + return 2 + default: + return 0 + } +} + +func (t OpType) String() string { + switch t { + case TypeMacro: + return "Macro" + case TypeCall: + return "Call" + case TypeDefer: + return "Defer" + case TypePushTransform: + return "PushTransform" + case TypeTransform: + return "Transform" + case TypePopTransform: + return "PopTransform" + case TypeInvalidate: + return "Invalidate" + case TypeImage: + return "Image" + case TypePaint: + return "Paint" + case TypeColor: + return "Color" + case TypeLinearGradient: + return "LinearGradient" + case TypeArea: + return "Area" + case TypePopArea: + return "PopArea" + case TypePointerInput: + return "PointerInput" + case TypeClipboardRead: + return "ClipboardRead" + case TypeClipboardWrite: + return "ClipboardWrite" + case TypeKeyInput: + return "KeyInput" + case TypeKeyFocus: + return "KeyFocus" + case TypeKeySoftKeyboard: + return "KeySoftKeyboard" + case TypeSave: + return "Save" + case TypeLoad: + return "Load" + case TypeAux: + return "Aux" + case TypeClip: + return "Clip" + case TypePopClip: + return "PopClip" + case TypeProfile: + return "Profile" + case TypeCursor: + return "Cursor" + case TypePath: + return "Path" + case TypeStroke: + return "Stroke" + default: + panic("unnkown OpType") + } +} diff --git a/internal/ops/reader.go b/internal/ops/reader.go index 402458da..7884122a 100644 --- a/internal/ops/reader.go +++ b/internal/ops/reader.go @@ -4,17 +4,14 @@ package ops import ( "encoding/binary" - - "gioui.org/internal/opconst" - "gioui.org/op" ) // Reader parses an ops list. type Reader struct { pc PC stack []macro - ops *op.Ops - deferOps op.Ops + ops *Ops + deferOps Ops deferDone bool } @@ -28,14 +25,14 @@ type EncodedOp struct { // Key is a unique key for a given op. type Key struct { - ops *op.Ops + ops *Ops pc int version int } // Shadow of op.MacroOp. type macroOp struct { - ops *op.Ops + ops *Ops pc PC } @@ -46,7 +43,7 @@ type PC struct { } type macro struct { - ops *op.Ops + ops *Ops retPC PC endPC PC } @@ -56,12 +53,12 @@ type opMacroDef struct { } // Reset start reading from the beginning of ops. -func (r *Reader) Reset(ops *op.Ops) { +func (r *Reader) Reset(ops *Ops) { r.ResetAt(ops, PC{}) } // ResetAt is like Reset, except it starts reading from pc. -func (r *Reader) ResetAt(ops *op.Ops, pc PC) { +func (r *Reader) ResetAt(ops *Ops, pc PC) { r.stack = r.stack[:0] r.deferOps.Reset() r.deferDone = false @@ -69,15 +66,6 @@ func (r *Reader) ResetAt(ops *op.Ops, pc PC) { r.ops = ops } -// NewPC returns a PC representing the current instruction counter of -// ops. -func NewPC(ops *op.Ops) PC { - return PC{ - data: len(ops.Data()), - refs: len(ops.Refs()), - } -} - func (r *Reader) Decode() (EncodedOp, bool) { if r.ops == nil { return EncodedOp{}, false @@ -107,25 +95,25 @@ func (r *Reader) Decode() (EncodedOp, bool) { continue } key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.Version()} - t := opconst.OpType(data[0]) + t := OpType(data[0]) n := t.Size() nrefs := t.NumRefs() data = data[:n] refs = refs[r.pc.refs:] refs = refs[:nrefs] switch t { - case opconst.TypeDefer: + case TypeDefer: deferring = true r.pc.data += n r.pc.refs += nrefs continue - case opconst.TypeAux: + case TypeAux: // An Aux operations is always wrapped in a macro, and // its length is the remaining space. block := r.stack[len(r.stack)-1] - n += block.endPC.data - r.pc.data - opconst.TypeAuxLen + n += block.endPC.data - r.pc.data - TypeAuxLen data = data[:n] - case opconst.TypeCall: + case TypeCall: if deferring { deferring = false // Copy macro for deferred execution. @@ -141,11 +129,11 @@ func (r *Reader) Decode() (EncodedOp, bool) { var op macroOp op.decode(data, refs) macroData := op.ops.Data()[op.pc.data:] - if opconst.OpType(macroData[0]) != opconst.TypeMacro { + if OpType(macroData[0]) != TypeMacro { panic("invalid macro reference") } var opDef opMacroDef - opDef.decode(macroData[:opconst.TypeMacro.Size()]) + opDef.decode(macroData[:TypeMacro.Size()]) retPC := r.pc retPC.data += n retPC.refs += nrefs @@ -156,10 +144,10 @@ func (r *Reader) Decode() (EncodedOp, bool) { }) r.ops = op.ops r.pc = op.pc - r.pc.data += opconst.TypeMacro.Size() - r.pc.refs += opconst.TypeMacro.NumRefs() + r.pc.data += TypeMacro.Size() + r.pc.refs += TypeMacro.NumRefs() continue - case opconst.TypeMacro: + case TypeMacro: var op opMacroDef op.decode(data) r.pc = op.endpc @@ -172,7 +160,7 @@ func (r *Reader) Decode() (EncodedOp, bool) { } func (op *opMacroDef) decode(data []byte) { - if opconst.OpType(data[0]) != opconst.TypeMacro { + if OpType(data[0]) != TypeMacro { panic("invalid op") } bo := binary.LittleEndian @@ -188,7 +176,7 @@ func (op *opMacroDef) decode(data []byte) { } func (m *macroOp) decode(data []byte, refs []interface{}) { - if opconst.OpType(data[0]) != opconst.TypeCall { + if OpType(data[0]) != TypeCall { panic("invalid op") } data = data[:9] @@ -196,7 +184,7 @@ func (m *macroOp) decode(data []byte, refs []interface{}) { dataIdx := int(int32(bo.Uint32(data[1:]))) refsIdx := int(int32(bo.Uint32(data[5:]))) *m = macroOp{ - ops: refs[0].(*op.Ops), + ops: refs[0].(*Ops), pc: PC{ data: dataIdx, refs: refsIdx, diff --git a/io/clipboard/clipboard.go b/io/clipboard/clipboard.go index a899f39a..de331f73 100644 --- a/io/clipboard/clipboard.go +++ b/io/clipboard/clipboard.go @@ -3,7 +3,7 @@ package clipboard import ( - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/op" ) @@ -25,13 +25,13 @@ type WriteOp struct { } func (h ReadOp) Add(o *op.Ops) { - data := o.Write1(opconst.TypeClipboardReadLen, h.Tag) - data[0] = byte(opconst.TypeClipboardRead) + data := o.Internal.Write1(ops.TypeClipboardReadLen, h.Tag) + data[0] = byte(ops.TypeClipboardRead) } func (h WriteOp) Add(o *op.Ops) { - data := o.Write1(opconst.TypeClipboardWriteLen, &h.Text) - data[0] = byte(opconst.TypeClipboardWrite) + data := o.Internal.Write1(ops.TypeClipboardWriteLen, &h.Text) + data[0] = byte(ops.TypeClipboardWrite) } func (Event) ImplementsEvent() {} diff --git a/io/key/key.go b/io/key/key.go index c2043283..6b0ddc29 100644 --- a/io/key/key.go +++ b/io/key/key.go @@ -13,7 +13,7 @@ import ( "fmt" "strings" - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/op" ) @@ -145,22 +145,22 @@ func (h InputOp) Add(o *op.Ops) { if h.Tag == nil { panic("Tag must be non-nil") } - data := o.Write1(opconst.TypeKeyInputLen, h.Tag) - data[0] = byte(opconst.TypeKeyInput) + data := o.Internal.Write1(ops.TypeKeyInputLen, h.Tag) + data[0] = byte(ops.TypeKeyInput) data[1] = byte(h.Hint) } func (h SoftKeyboardOp) Add(o *op.Ops) { - data := o.Write(opconst.TypeKeySoftKeyboardLen) - data[0] = byte(opconst.TypeKeySoftKeyboard) + data := o.Internal.Write(ops.TypeKeySoftKeyboardLen) + data[0] = byte(ops.TypeKeySoftKeyboard) if h.Show { data[1] = 1 } } func (h FocusOp) Add(o *op.Ops) { - data := o.Write1(opconst.TypeKeyFocusLen, h.Tag) - data[0] = byte(opconst.TypeKeyFocus) + data := o.Internal.Write1(ops.TypeKeyFocusLen, h.Tag) + data[0] = byte(ops.TypeKeyFocus) } func (EditEvent) ImplementsEvent() {} diff --git a/io/pointer/pointer.go b/io/pointer/pointer.go index 578a6bac..9e639c0d 100644 --- a/io/pointer/pointer.go +++ b/io/pointer/pointer.go @@ -10,7 +10,7 @@ import ( "time" "gioui.org/f32" - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/io/key" "gioui.org/op" @@ -56,8 +56,8 @@ type AreaOp struct { // AreaStack represents an AreaOp on the stack of areas. type AreaStack struct { - ops *op.Ops - id op.StackID + ops *ops.Ops + id ops.StackID macroID int } @@ -194,35 +194,35 @@ func Ellipse(size image.Rectangle) AreaOp { // Push the current area to the stack and intersects the current area with the // area represented by o. -func (o AreaOp) Push(ops *op.Ops) AreaStack { - id, macroID := ops.PushOp(op.AreaStack) - o.add(ops, true) - return AreaStack{ops: ops, id: id, macroID: macroID} +func (a AreaOp) Push(o *op.Ops) AreaStack { + id, macroID := o.Internal.PushOp(ops.AreaStack) + a.add(o, true) + return AreaStack{ops: &o.Internal, id: id, macroID: macroID} } -func (o AreaOp) add(ops *op.Ops, push bool) { - data := ops.Write(opconst.TypeAreaLen) - data[0] = byte(opconst.TypeArea) - data[1] = byte(o.kind) - if o.PassThrough { +func (a AreaOp) add(o *op.Ops, push bool) { + data := o.Internal.Write(ops.TypeAreaLen) + data[0] = byte(ops.TypeArea) + data[1] = byte(a.kind) + if a.PassThrough { data[2] = 1 } bo := binary.LittleEndian - bo.PutUint32(data[3:], uint32(o.rect.Min.X)) - bo.PutUint32(data[7:], uint32(o.rect.Min.Y)) - bo.PutUint32(data[11:], uint32(o.rect.Max.X)) - bo.PutUint32(data[15:], uint32(o.rect.Max.Y)) + bo.PutUint32(data[3:], uint32(a.rect.Min.X)) + bo.PutUint32(data[7:], uint32(a.rect.Min.Y)) + bo.PutUint32(data[11:], uint32(a.rect.Max.X)) + bo.PutUint32(data[15:], uint32(a.rect.Max.Y)) } func (o AreaStack) Pop() { - o.ops.PopOp(op.AreaStack, o.id, o.macroID) - data := o.ops.Write(opconst.TypePopAreaLen) - data[0] = byte(opconst.TypePopArea) + o.ops.PopOp(ops.AreaStack, o.id, o.macroID) + data := o.ops.Write(ops.TypePopAreaLen) + data[0] = byte(ops.TypePopArea) } func (op CursorNameOp) Add(o *op.Ops) { - data := o.Write1(opconst.TypeCursorLen, op.Name) - data[0] = byte(opconst.TypeCursor) + data := o.Internal.Write1(ops.TypeCursorLen, op.Name) + data[0] = byte(ops.TypeCursor) } // Add panics if the scroll range does not contain zero. @@ -233,8 +233,8 @@ func (op InputOp) Add(o *op.Ops) { if b := op.ScrollBounds; b.Min.X > 0 || b.Max.X < 0 || b.Min.Y > 0 || b.Max.Y < 0 { panic(fmt.Errorf("invalid scroll range value %v", b)) } - data := o.Write1(opconst.TypePointerInputLen, op.Tag) - data[0] = byte(opconst.TypePointerInput) + data := o.Internal.Write1(ops.TypePointerInputLen, op.Tag) + data[0] = byte(ops.TypePointerInput) if op.Grab { data[1] = 1 } diff --git a/io/profile/profile.go b/io/profile/profile.go index 6e6adbfe..471fe965 100644 --- a/io/profile/profile.go +++ b/io/profile/profile.go @@ -5,7 +5,7 @@ package profile import ( - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/op" ) @@ -24,8 +24,8 @@ type Event struct { } func (p Op) Add(o *op.Ops) { - data := o.Write1(opconst.TypeProfileLen, p.Tag) - data[0] = byte(opconst.TypeProfile) + data := o.Internal.Write1(ops.TypeProfileLen, p.Tag) + data[0] = byte(ops.TypeProfile) } func (p Event) ImplementsEvent() {} diff --git a/io/router/key.go b/io/router/key.go index af545162..130027c2 100644 --- a/io/router/key.go +++ b/io/router/key.go @@ -3,7 +3,6 @@ package router import ( - "gioui.org/internal/opconst" "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/io/key" @@ -61,7 +60,7 @@ func (q *keyQueue) Frame(root *op.Ops, events *handlerEvents) { for _, h := range q.handlers { h.visible, h.new = false, false } - q.reader.Reset(root) + q.reader.Reset(&root.Internal) focus, changed, state := q.resolveFocus(events) for k, h := range q.handlers { @@ -104,19 +103,19 @@ func (q *keyQueue) Push(e event.Event, events *handlerEvents) { func (q *keyQueue) resolveFocus(events *handlerEvents) (focus event.Tag, changed bool, state TextInputState) { for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() { - switch opconst.OpType(encOp.Data[0]) { - case opconst.TypeKeyFocus: + switch ops.OpType(encOp.Data[0]) { + case ops.TypeKeyFocus: op := decodeFocusOp(encOp.Data, encOp.Refs) changed = true focus = op.Tag - case opconst.TypeKeySoftKeyboard: + case ops.TypeKeySoftKeyboard: op := decodeSoftKeyboardOp(encOp.Data, encOp.Refs) if op.Show { state = TextInputOpen } else { state = TextInputClose } - case opconst.TypeKeyInput: + case ops.TypeKeyInput: op := decodeKeyInputOp(encOp.Data, encOp.Refs) h, ok := q.handlers[op.Tag] if !ok { @@ -131,7 +130,7 @@ func (q *keyQueue) resolveFocus(events *handlerEvents) (focus event.Tag, changed } func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp { - if opconst.OpType(d[0]) != opconst.TypeKeyInput { + if ops.OpType(d[0]) != ops.TypeKeyInput { panic("invalid op") } return key.InputOp{ @@ -141,7 +140,7 @@ func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp { } func decodeSoftKeyboardOp(d []byte, refs []interface{}) key.SoftKeyboardOp { - if opconst.OpType(d[0]) != opconst.TypeKeySoftKeyboard { + if ops.OpType(d[0]) != ops.TypeKeySoftKeyboard { panic("invalid op") } return key.SoftKeyboardOp{ @@ -150,7 +149,7 @@ func decodeSoftKeyboardOp(d []byte, refs []interface{}) key.SoftKeyboardOp { } func decodeFocusOp(d []byte, refs []interface{}) key.FocusOp { - if opconst.OpType(d[0]) != opconst.TypeKeyFocus { + if ops.OpType(d[0]) != ops.TypeKeyFocus { panic("invalid op") } return key.FocusOp{ diff --git a/io/router/pointer.go b/io/router/pointer.go index 598bdad6..0e3bc972 100644 --- a/io/router/pointer.go +++ b/io/router/pointer.go @@ -7,7 +7,6 @@ import ( "image" "gioui.org/f32" - "gioui.org/internal/opconst" "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/io/pointer" @@ -107,15 +106,15 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents) { } reset() for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { - switch opconst.OpType(encOp.Data[0]) { - case opconst.TypeSave: + switch ops.OpType(encOp.Data[0]) { + case ops.TypeSave: id := ops.DecodeSave(encOp.Data) q.save(id, state.t) - case opconst.TypeLoad: + case ops.TypeLoad: reset() id := ops.DecodeLoad(encOp.Data) state.t = q.states[id] - case opconst.TypeArea: + case ops.TypeArea: var op areaOp op.Decode(encOp.Data) area := -1 @@ -130,21 +129,21 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents) { area: len(q.areas) - 1, }) state.node = len(q.hitTree) - 1 - case opconst.TypePopArea: + case ops.TypePopArea: n := len(q.nodeStack) state.node = q.nodeStack[n-1] q.nodeStack = q.nodeStack[:n-1] - case opconst.TypeTransform: + case ops.TypeTransform: dop, push := ops.DecodeTransform(encOp.Data) if push { q.transStack = append(q.transStack, state.t) } state.t = state.t.Mul(dop) - case opconst.TypePopTransform: + case ops.TypePopTransform: n := len(q.transStack) state.t = q.transStack[n-1] q.transStack = q.transStack[:n-1] - case opconst.TypePointerInput: + case ops.TypePointerInput: op := pointer.InputOp{ Tag: encOp.Refs[0].(event.Tag), Grab: encOp.Data[1] != 0, @@ -184,7 +183,7 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents) { Y: int(int32(bo(encOp.Data[15:]))), }, } - case opconst.TypeCursor: + case ops.TypeCursor: q.cursors = append(q.cursors, cursorNode{ name: encOp.Refs[0].(pointer.CursorName), area: len(q.areas) - 1, @@ -258,7 +257,7 @@ func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) { q.nodeStack = q.nodeStack[:0] q.transStack = q.transStack[:0] q.cursors = q.cursors[:0] - q.reader.Reset(root) + q.reader.Reset(&root.Internal) q.collectHandlers(&q.reader, events) for k, h := range q.handlers { if !h.active { @@ -476,7 +475,7 @@ func opDecodeFloat32(d []byte) float32 { } func (op *areaOp) Decode(d []byte) { - if opconst.OpType(d[0]) != opconst.TypeArea { + if ops.OpType(d[0]) != ops.TypeArea { panic("invalid op") } rect := f32.Rectangle{ diff --git a/io/router/pointer_test.go b/io/router/pointer_test.go index 328582f5..793d7ae4 100644 --- a/io/router/pointer_test.go +++ b/io/router/pointer_test.go @@ -761,14 +761,14 @@ func BenchmarkAreaOp_Decode(b *testing.B) { ops := new(op.Ops) pointer.Rect(image.Rectangle{Max: image.Pt(100, 100)}).Push(ops).Pop() for i := 0; i < b.N; i++ { - benchAreaOp.Decode(ops.Data()) + benchAreaOp.Decode(ops.Internal.Data()) } } func BenchmarkAreaOp_Hit(b *testing.B) { ops := new(op.Ops) pointer.Rect(image.Rectangle{Max: image.Pt(100, 100)}).Push(ops).Pop() - benchAreaOp.Decode(ops.Data()) + benchAreaOp.Decode(ops.Internal.Data()) for i := 0; i < b.N; i++ { benchAreaOp.Hit(f32.Pt(50, 50)) } diff --git a/io/router/router.go b/io/router/router.go index a09ecabc..1b344dd6 100644 --- a/io/router/router.go +++ b/io/router/router.go @@ -14,7 +14,6 @@ import ( "encoding/binary" "time" - "gioui.org/internal/opconst" "gioui.org/internal/ops" "gioui.org/io/clipboard" "gioui.org/io/event" @@ -68,7 +67,7 @@ func (q *Router) Frame(ops *op.Ops) { for k := range q.profHandlers { delete(q.profHandlers, k) } - q.reader.Reset(ops) + q.reader.Reset(&ops.Internal) q.collect() q.pqueue.Frame(ops, &q.handlers) @@ -126,22 +125,22 @@ func (q *Router) Cursor() pointer.CursorName { func (q *Router) collect() { for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() { - switch opconst.OpType(encOp.Data[0]) { - case opconst.TypeInvalidate: + switch ops.OpType(encOp.Data[0]) { + case ops.TypeInvalidate: op := decodeInvalidateOp(encOp.Data) if !q.wakeup || op.At.Before(q.wakeupTime) { q.wakeup = true q.wakeupTime = op.At } - case opconst.TypeProfile: + case ops.TypeProfile: op := decodeProfileOp(encOp.Data, encOp.Refs) if q.profHandlers == nil { q.profHandlers = make(map[event.Tag]struct{}) } q.profHandlers[op.Tag] = struct{}{} - case opconst.TypeClipboardRead: + case ops.TypeClipboardRead: q.cqueue.ProcessReadClipboard(encOp.Refs) - case opconst.TypeClipboardWrite: + case ops.TypeClipboardWrite: q.cqueue.ProcessWriteClipboard(encOp.Refs) } } @@ -206,7 +205,7 @@ func (h *handlerEvents) Clear() { } func decodeProfileOp(d []byte, refs []interface{}) profile.Op { - if opconst.OpType(d[0]) != opconst.TypeProfile { + if ops.OpType(d[0]) != ops.TypeProfile { panic("invalid op") } return profile.Op{ @@ -216,7 +215,7 @@ func decodeProfileOp(d []byte, refs []interface{}) profile.Op { func decodeInvalidateOp(d []byte) op.InvalidateOp { bo := binary.LittleEndian - if opconst.OpType(d[0]) != opconst.TypeInvalidate { + if ops.OpType(d[0]) != ops.TypeInvalidate { panic("invalid op") } var o op.InvalidateOp diff --git a/op/clip/clip.go b/op/clip/clip.go index 4141bd27..7a0a2f26 100644 --- a/op/clip/clip.go +++ b/op/clip/clip.go @@ -9,7 +9,6 @@ import ( "math" "gioui.org/f32" - "gioui.org/internal/opconst" "gioui.org/internal/ops" "gioui.org/internal/scene" "gioui.org/internal/stroke" @@ -28,8 +27,8 @@ type Op struct { // Stack represents an Op pushed on the clip stack. type Stack struct { - ops *op.Ops - id op.StackID + ops *ops.Ops + id ops.StackID macroID int } @@ -42,9 +41,9 @@ func init() { // Push saves the current clip state on the stack and updates the current // state to the intersection of the current p. func (p Op) Push(o *op.Ops) Stack { - id, macroID := o.PushOp(op.ClipStack) + id, macroID := o.Internal.PushOp(ops.ClipStack) p.add(o, true) - return Stack{ops: o, id: id, macroID: macroID} + return Stack{ops: &o.Internal, id: id, macroID: macroID} } // Add is like Push except it doesn't save the current state on the stack. @@ -69,8 +68,8 @@ func (p Op) add(o *op.Ops, push bool) { bo := binary.LittleEndian if path.hasSegments { - data := o.Write(opconst.TypePathLen) - data[0] = byte(opconst.TypePath) + data := o.Internal.Write(ops.TypePathLen) + data[0] = byte(ops.TypePath) bo.PutUint64(data[1:], path.hash) path.spec.Add(o) } @@ -83,14 +82,14 @@ func (p Op) add(o *op.Ops, push bool) { bounds.Min.Y -= half bounds.Max.X += half bounds.Max.Y += half - data := o.Write(opconst.TypeStrokeLen) - data[0] = byte(opconst.TypeStroke) + data := o.Internal.Write(ops.TypeStrokeLen) + data[0] = byte(ops.TypeStroke) bo := binary.LittleEndian bo.PutUint32(data[1:], math.Float32bits(str.Width)) } - data := o.Write(opconst.TypeClipLen) - data[0] = byte(opconst.TypeClip) + data := o.Internal.Write(ops.TypeClipLen) + data[0] = byte(ops.TypeClip) bo.PutUint32(data[1:], uint32(bounds.Min.X)) bo.PutUint32(data[5:], uint32(bounds.Min.Y)) bo.PutUint32(data[9:], uint32(bounds.Max.X)) @@ -104,9 +103,9 @@ func (p Op) add(o *op.Ops, push bool) { } func (s Stack) Pop() { - s.ops.PopOp(op.ClipStack, s.id, s.macroID) - data := s.ops.Write(opconst.TypePopClipLen) - data[0] = byte(opconst.TypePopClip) + s.ops.PopOp(ops.ClipStack, s.id, s.macroID) + data := s.ops.Write(ops.TypePopClipLen) + data[0] = byte(ops.TypePopClip) } func (p Op) approximateStroke(o *op.Ops) PathSpec { @@ -117,28 +116,28 @@ func (p Op) approximateStroke(o *op.Ops) PathSpec { var r ops.Reader // Add path op for us to decode. Use a macro to omit it from later decodes. ignore := op.Record(o) - r.ResetAt(o, ops.NewPC(o)) + r.ResetAt(&o.Internal, o.Internal.PC()) p.path.spec.Add(o) ignore.Stop() encOp, ok := r.Decode() - if !ok || opconst.OpType(encOp.Data[0]) != opconst.TypeAux { + if !ok || ops.OpType(encOp.Data[0]) != ops.TypeAux { panic("corrupt path data") } - pathData := encOp.Data[opconst.TypeAuxLen:] + pathData := encOp.Data[ops.TypeAuxLen:] // Decode dashes in a similar way. var dashes stroke.DashOp if p.dashes.phase != 0 || p.dashes.size > 0 { ignore := op.Record(o) - r.ResetAt(o, ops.NewPC(o)) + r.ResetAt(&o.Internal, o.Internal.PC()) p.dashes.spec.Add(o) ignore.Stop() encOp, ok := r.Decode() - if !ok || opconst.OpType(encOp.Data[0]) != opconst.TypeAux { + if !ok || ops.OpType(encOp.Data[0]) != ops.TypeAux { panic("corrupt dash data") } dashes.Dashes = make([]float32, p.dashes.size) - dashData := encOp.Data[opconst.TypeAuxLen:] + dashData := encOp.Data[ops.TypeAuxLen:] bo := binary.LittleEndian for i := range dashes.Dashes { dashes.Dashes[i] = math.Float32frombits(bo.Uint32(dashData[i*4:])) @@ -188,7 +187,7 @@ type PathSpec struct { // Path generates no garbage and can be used for dynamic paths; path // data is stored directly in the Ops list supplied to Begin. type Path struct { - ops *op.Ops + ops *ops.Ops open bool contour int pen f32.Point @@ -203,13 +202,13 @@ type Path struct { func (p *Path) Pos() f32.Point { return p.pen } // Begin the path, storing the path data and final Op into ops. -func (p *Path) Begin(ops *op.Ops) { +func (p *Path) Begin(o *op.Ops) { p.hash.SetSeed(pathSeed) - p.ops = ops - p.macro = op.Record(ops) + p.ops = &o.Internal + p.macro = op.Record(o) // Write the TypeAux opcode - data := ops.Write(opconst.TypeAuxLen) - data[0] = byte(opconst.TypeAux) + data := p.ops.Write(ops.TypeAuxLen) + data[0] = byte(ops.TypeAux) } // End returns a PathSpec ready to use in clipping operations. diff --git a/op/clip/stroke.go b/op/clip/stroke.go index 17b661c9..623d57a9 100644 --- a/op/clip/stroke.go +++ b/op/clip/stroke.go @@ -6,7 +6,7 @@ import ( "encoding/binary" "math" - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/op" ) @@ -73,18 +73,18 @@ const ( // Dash records dashes' lengths and phase for a stroked path. type Dash struct { - ops *op.Ops + ops *ops.Ops macro op.MacroOp phase float32 size uint8 // size of the pattern } -func (d *Dash) Begin(ops *op.Ops) { - d.ops = ops - d.macro = op.Record(ops) +func (d *Dash) Begin(o *op.Ops) { + d.ops = &o.Internal + d.macro = op.Record(o) // Write the TypeAux opcode - data := ops.Write(opconst.TypeAuxLen) - data[0] = byte(opconst.TypeAux) + data := d.ops.Write(ops.TypeAuxLen) + data[0] = byte(ops.TypeAux) } func (d *Dash) Phase(v float32) { diff --git a/op/op.go b/op/op.go index 75cdaf58..0ec4033c 100644 --- a/op/op.go +++ b/op/op.go @@ -72,55 +72,29 @@ import ( "time" "gioui.org/f32" - "gioui.org/internal/opconst" + "gioui.org/internal/ops" ) // Ops holds a list of operations. Operations are stored in // serialized form to avoid garbage during construction of // the ops list. type Ops struct { - // version is incremented at each Reset. - version int - // data contains the serialized operations. - data []byte - // refs hold external references for operations. - refs []interface{} - // nextStateID is the id allocated for the next - // StateOp. - nextStateID int - - macroStack stack - stacks [3]stack -} - -type StackKind uint8 - -const ( - ClipStack StackKind = iota - AreaStack - TransStack -) - -// stateOp represents a saved operation snapshop to be restored -// later. -type stateOp struct { - id int - macroID int - ops *Ops + // Internal is for internal use, despite being exported. + Internal ops.Ops } // MacroOp records a list of operations for later use. type MacroOp struct { - ops *Ops - id StackID - pc pc + ops *ops.Ops + id ops.StackID + pc ops.PC } // CallOp invokes the operations recorded by Record. type CallOp struct { // Ops is the list of operations to invoke. - ops *Ops - pc pc + ops *ops.Ops + pc ops.PC } // InvalidateOp requests a redraw at the given time. Use @@ -137,26 +111,9 @@ type TransformOp struct { // TransformStack represents a TransformOp pushed on the transformation stack. type TransformStack struct { - id StackID + id ops.StackID macroID int - ops *Ops -} - -// stack tracks the integer identities of stack operations to ensure correct -// pairing of their push and pop methods. -type stack struct { - currentID int - nextID int -} - -type StackID struct { - id int - prev int -} - -type pc struct { - data int - refs int + ops *ops.Ops } // Defer executes c after all other operations have completed, including @@ -170,23 +127,23 @@ func Defer(o *Ops, c CallOp) { if c.ops == nil { return } - state := save(o) + state := o.Internal.Save() // Wrap c in a macro that loads the saved state before execution. m := Record(o) - state.load() + state.Load() c.Add(o) c = m.Stop() // A Defer is recorded as a TypeDefer followed by the // wrapped macro. - data := o.Write(opconst.TypeDeferLen) - data[0] = byte(opconst.TypeDefer) + data := o.Internal.Write(ops.TypeDeferLen) + data[0] = byte(ops.TypeDefer) c.Add(o) } type SaveStack struct { - ops *Ops + ops *ops.Ops clip struct { - id StackID + id ops.StackID macroID int } trans TransformStack @@ -196,17 +153,17 @@ type SaveStack struct { // etc.). func Save(o *Ops) SaveStack { st := SaveStack{ - ops: o, + ops: &o.Internal, trans: Offset(f32.Point{}).Push(o), } const inf = 1e6 bounds := image.Rectangle{Min: image.Pt(-inf, -inf), Max: image.Pt(inf, inf)} { - st.clip.id, st.clip.macroID = o.PushOp(ClipStack) + st.clip.id, st.clip.macroID = o.Internal.PushOp(ops.ClipStack) // Push clip stack with no-op (infinite) clipping rect. Copied from clip.Op.Push. bo := binary.LittleEndian - data := o.Write(opconst.TypeClipLen) - data[0] = byte(opconst.TypeClip) + data := o.Internal.Write(ops.TypeClipLen) + data[0] = byte(ops.TypeClip) bo.PutUint32(data[1:], uint32(bounds.Min.X)) bo.PutUint32(data[5:], uint32(bounds.Min.Y)) bo.PutUint32(data[9:], uint32(bounds.Max.X)) @@ -219,113 +176,28 @@ func Save(o *Ops) SaveStack { func (s SaveStack) Load() { // Pop clip. - s.ops.PopOp(ClipStack, s.clip.id, s.clip.macroID) - data := s.ops.Write(opconst.TypePopClipLen) - data[0] = byte(opconst.TypePopClip) + s.ops.PopOp(ops.ClipStack, s.clip.id, s.clip.macroID) + data := s.ops.Write(ops.TypePopClipLen) + data[0] = byte(ops.TypePopClip) s.trans.Pop() } -// save the effective transformation. -func save(o *Ops) stateOp { - o.nextStateID++ - s := stateOp{ - ops: o, - id: o.nextStateID, - macroID: o.macroStack.currentID, - } - bo := binary.LittleEndian - data := o.Write(opconst.TypeSaveLen) - data[0] = byte(opconst.TypeSave) - bo.PutUint32(data[1:], uint32(s.id)) - return s -} - -// load a previously saved operations state given -// its ID. -func (s stateOp) load() { - bo := binary.LittleEndian - data := s.ops.Write(opconst.TypeLoadLen) - data[0] = byte(opconst.TypeLoad) - bo.PutUint32(data[1:], uint32(s.id)) -} - // Reset the Ops, preparing it for re-use. Reset invalidates // any recorded macros. func (o *Ops) Reset() { - o.macroStack = stack{} - for i := range o.stacks { - o.stacks[i] = stack{} - } - // Leave references to the GC. - for i := range o.refs { - o.refs[i] = nil - } - o.data = o.data[:0] - o.refs = o.refs[:0] - o.nextStateID = 0 - o.version++ -} - -// Data is for internal use only. -func (o *Ops) Data() []byte { - return o.data -} - -// Refs is for internal use only. -func (o *Ops) Refs() []interface{} { - return o.refs -} - -// Version is for internal use only. -func (o *Ops) Version() int { - return o.version -} - -// Write is for internal use only. -func (o *Ops) Write(n int) []byte { - o.data = append(o.data, make([]byte, n)...) - return o.data[len(o.data)-n:] -} - -func (o *Ops) PushOp(kind StackKind) (StackID, int) { - return o.stacks[kind].push(), o.macroStack.currentID -} - -func (o *Ops) PopOp(kind StackKind, sid StackID, macroID int) { - if o.macroStack.currentID != macroID { - panic("stack push and pop must not cross macro boundary") - } - o.stacks[kind].pop(sid) -} - -// Write1 is for internal use only. -func (o *Ops) Write1(n int, ref1 interface{}) []byte { - o.data = append(o.data, make([]byte, n)...) - o.refs = append(o.refs, ref1) - return o.data[len(o.data)-n:] -} - -// Write2 is for internal use only. -func (o *Ops) Write2(n int, ref1, ref2 interface{}) []byte { - o.data = append(o.data, make([]byte, n)...) - o.refs = append(o.refs, ref1, ref2) - return o.data[len(o.data)-n:] -} - -func (o *Ops) pc() pc { - return pc{data: len(o.data), refs: len(o.refs)} + o.Internal.Reset() } // Record a macro of operations. func Record(o *Ops) MacroOp { m := MacroOp{ - ops: o, - id: o.macroStack.push(), - pc: o.pc(), + ops: &o.Internal, + id: o.Internal.PushMacro(), + pc: o.Internal.PC(), } // Reserve room for a macro definition. Updated in Stop. - m.ops.Write(opconst.TypeMacroLen) + m.ops.Write(ops.TypeMacroLen) m.fill() return m } @@ -333,7 +205,7 @@ func Record(o *Ops) MacroOp { // Stop ends a previously started recording and returns an // operation for replaying it. func (m MacroOp) Stop() CallOp { - m.ops.macroStack.pop(m.id) + m.ops.PopMacro(m.id) m.fill() return CallOp{ ops: m.ops, @@ -342,14 +214,7 @@ func (m MacroOp) Stop() CallOp { } func (m MacroOp) fill() { - pc := m.ops.pc() - // Fill out the macro definition reserved in Record. - data := m.ops.data[m.pc.data:] - data = data[:opconst.TypeMacroLen] - data[0] = byte(opconst.TypeMacro) - bo := binary.LittleEndian - bo.PutUint32(data[1:], uint32(pc.data)) - bo.PutUint32(data[5:], uint32(pc.refs)) + m.ops.FillMacro(m.pc) } // Add the recorded list of operations. Add @@ -359,16 +224,12 @@ func (c CallOp) Add(o *Ops) { if c.ops == nil { return } - data := o.Write1(opconst.TypeCallLen, c.ops) - data[0] = byte(opconst.TypeCall) - bo := binary.LittleEndian - bo.PutUint32(data[1:], uint32(c.pc.data)) - bo.PutUint32(data[5:], uint32(c.pc.refs)) + o.Internal.AddCall(c.ops, c.pc) } func (r InvalidateOp) Add(o *Ops) { - data := o.Write(opconst.TypeRedrawLen) - data[0] = byte(opconst.TypeInvalidate) + data := o.Internal.Write(ops.TypeRedrawLen) + data[0] = byte(ops.TypeInvalidate) bo := binary.LittleEndian // UnixNano cannot represent the zero time. if t := r.At; !t.IsZero() { @@ -392,9 +253,9 @@ func Affine(a f32.Affine2D) TransformOp { // Push the current transformation to the stack and then multiply the // current transformation with t. func (t TransformOp) Push(o *Ops) TransformStack { - id, macroID := o.PushOp(TransStack) + id, macroID := o.Internal.PushOp(ops.TransStack) t.add(o, true) - return TransformStack{ops: o, id: id, macroID: macroID} + return TransformStack{ops: &o.Internal, id: id, macroID: macroID} } // Add is like Push except it doesn't push the current transformation to the @@ -404,8 +265,8 @@ func (t TransformOp) Add(o *Ops) { } func (t TransformOp) add(o *Ops, push bool) { - data := o.Write(opconst.TypeTransformLen) - data[0] = byte(opconst.TypeTransform) + data := o.Internal.Write(ops.TypeTransformLen) + data[0] = byte(ops.TypeTransform) if push { data[1] = 1 } @@ -420,28 +281,7 @@ func (t TransformOp) add(o *Ops, push bool) { } func (t TransformStack) Pop() { - t.ops.PopOp(TransStack, t.id, t.macroID) - data := t.ops.Write(opconst.TypePopTransformLen) - data[0] = byte(opconst.TypePopTransform) -} - -func (s *stack) push() StackID { - s.nextID++ - sid := StackID{ - id: s.nextID, - prev: s.currentID, - } - s.currentID = s.nextID - return sid -} - -func (s *stack) check(sid StackID) { - if s.currentID != sid.id { - panic("unbalanced operation") - } -} - -func (s *stack) pop(sid StackID) { - s.check(sid) - s.currentID = sid.prev + t.ops.PopOp(ops.TransStack, t.id, t.macroID) + data := t.ops.Write(ops.TypePopTransformLen) + data[0] = byte(ops.TypePopTransform) } diff --git a/op/paint/paint.go b/op/paint/paint.go index 5d02037a..f39bafbe 100644 --- a/op/paint/paint.go +++ b/op/paint/paint.go @@ -10,7 +10,7 @@ import ( "math" "gioui.org/f32" - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/op" "gioui.org/op/clip" ) @@ -96,13 +96,13 @@ func (i ImageOp) Add(o *op.Ops) { } else if i.src == nil || i.src.Bounds().Empty() { return } - data := o.Write2(opconst.TypeImageLen, i.src, i.handle) - data[0] = byte(opconst.TypeImage) + data := o.Internal.Write2(ops.TypeImageLen, i.src, i.handle) + data[0] = byte(ops.TypeImage) } func (c ColorOp) Add(o *op.Ops) { - data := o.Write(opconst.TypeColorLen) - data[0] = byte(opconst.TypeColor) + data := o.Internal.Write(ops.TypeColorLen) + data[0] = byte(ops.TypeColor) data[1] = c.Color.R data[2] = c.Color.G data[3] = c.Color.B @@ -110,8 +110,8 @@ func (c ColorOp) Add(o *op.Ops) { } func (c LinearGradientOp) Add(o *op.Ops) { - data := o.Write(opconst.TypeLinearGradientLen) - data[0] = byte(opconst.TypeLinearGradient) + data := o.Internal.Write(ops.TypeLinearGradientLen) + data[0] = byte(ops.TypeLinearGradient) bo := binary.LittleEndian bo.PutUint32(data[1:], math.Float32bits(c.Stop1.X)) @@ -130,8 +130,8 @@ func (c LinearGradientOp) Add(o *op.Ops) { } func (d PaintOp) Add(o *op.Ops) { - data := o.Write(opconst.TypePaintLen) - data[0] = byte(opconst.TypePaint) + data := o.Internal.Write(ops.TypePaintLen) + data[0] = byte(ops.TypePaint) } // FillShape fills the clip shape with a color.