op: move Ops internal methods and state to internal package ops

Merge package opconsts into ops as well; it only existed to break
import cycles.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-10-04 15:44:48 +02:00
parent 936c266b03
commit 391725b9d0
18 changed files with 558 additions and 561 deletions
+25 -26
View File
@@ -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.
+7 -7
View File
@@ -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) {
+40 -200
View File
@@ -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)
}
+9 -9
View File
@@ -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.