ops.go 12 KB

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