scene.go raw
1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 // Package scene encodes and decodes graphics commands in the format used by the
4 // compute renderer.
5 package scene
6
7 import (
8 "fmt"
9 "image/color"
10 "math"
11 "unsafe"
12
13 "github.com/p9c/p9/pkg/gel/gio/f32"
14 )
15
16 type Op uint32
17
18 type Command [sceneElemSize / 4]uint32
19
20 // GPU commands from scene.h
21 const (
22 OpNop Op = iota
23 OpLine
24 OpQuad
25 OpCubic
26 OpFillColor
27 OpLineWidth
28 OpTransform
29 OpBeginClip
30 OpEndClip
31 OpFillImage
32 OpSetFillMode
33 )
34
35 // FillModes, from setup.h.
36 type FillMode uint32
37
38 const (
39 FillModeNonzero = 0
40 FillModeStroke = 1
41 )
42
43 const CommandSize = int(unsafe.Sizeof(Command{}))
44
45 const sceneElemSize = 36
46
47 func (c Command) Op() Op {
48 return Op(c[0])
49 }
50
51 func (c Command) String() string {
52 switch Op(c[0]) {
53 case OpNop:
54 return "nop"
55 case OpLine:
56 from, to := DecodeLine(c)
57 return fmt.Sprintf("line(%v, %v)", from, to)
58 case OpQuad:
59 from, ctrl, to := DecodeQuad(c)
60 return fmt.Sprintf("quad(%v, %v, %v)", from, ctrl, to)
61 case OpCubic:
62 from, ctrl0, ctrl1, to := DecodeCubic(c)
63 return fmt.Sprintf("cubic(%v, %v, %v, %v)", from, ctrl0, ctrl1, to)
64 case OpFillColor:
65 return "fillcolor"
66 case OpLineWidth:
67 return "linewidth"
68 case OpTransform:
69 t := f32.NewAffine2D(
70 math.Float32frombits(c[1]),
71 math.Float32frombits(c[3]),
72 math.Float32frombits(c[5]),
73 math.Float32frombits(c[2]),
74 math.Float32frombits(c[4]),
75 math.Float32frombits(c[6]),
76 )
77 return fmt.Sprintf("transform (%v)", t)
78 case OpBeginClip:
79 bounds := f32.Rectangle{
80 Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])),
81 Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])),
82 }
83 return fmt.Sprintf("beginclip (%v)", bounds)
84 case OpEndClip:
85 bounds := f32.Rectangle{
86 Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])),
87 Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])),
88 }
89 return fmt.Sprintf("endclip (%v)", bounds)
90 case OpFillImage:
91 return "fillimage"
92 case OpSetFillMode:
93 return "setfillmode"
94 default:
95 panic("unreachable")
96 }
97 }
98
99 func Line(start, end f32.Point) Command {
100 return Command{
101 0: uint32(OpLine),
102 1: math.Float32bits(start.X),
103 2: math.Float32bits(start.Y),
104 3: math.Float32bits(end.X),
105 4: math.Float32bits(end.Y),
106 }
107 }
108
109 func Cubic(start, ctrl0, ctrl1, end f32.Point) Command {
110 return Command{
111 0: uint32(OpCubic),
112 1: math.Float32bits(start.X),
113 2: math.Float32bits(start.Y),
114 3: math.Float32bits(ctrl0.X),
115 4: math.Float32bits(ctrl0.Y),
116 5: math.Float32bits(ctrl1.X),
117 6: math.Float32bits(ctrl1.Y),
118 7: math.Float32bits(end.X),
119 8: math.Float32bits(end.Y),
120 }
121 }
122
123 func Quad(start, ctrl, end f32.Point) Command {
124 return Command{
125 0: uint32(OpQuad),
126 1: math.Float32bits(start.X),
127 2: math.Float32bits(start.Y),
128 3: math.Float32bits(ctrl.X),
129 4: math.Float32bits(ctrl.Y),
130 5: math.Float32bits(end.X),
131 6: math.Float32bits(end.Y),
132 }
133 }
134
135 func Transform(m f32.Affine2D) Command {
136 sx, hx, ox, hy, sy, oy := m.Elems()
137 return Command{
138 0: uint32(OpTransform),
139 1: math.Float32bits(sx),
140 2: math.Float32bits(hy),
141 3: math.Float32bits(hx),
142 4: math.Float32bits(sy),
143 5: math.Float32bits(ox),
144 6: math.Float32bits(oy),
145 }
146 }
147
148 func SetLineWidth(width float32) Command {
149 return Command{
150 0: uint32(OpLineWidth),
151 1: math.Float32bits(width),
152 }
153 }
154
155 func BeginClip(bbox f32.Rectangle) Command {
156 return Command{
157 0: uint32(OpBeginClip),
158 1: math.Float32bits(bbox.Min.X),
159 2: math.Float32bits(bbox.Min.Y),
160 3: math.Float32bits(bbox.Max.X),
161 4: math.Float32bits(bbox.Max.Y),
162 }
163 }
164
165 func EndClip(bbox f32.Rectangle) Command {
166 return Command{
167 0: uint32(OpEndClip),
168 1: math.Float32bits(bbox.Min.X),
169 2: math.Float32bits(bbox.Min.Y),
170 3: math.Float32bits(bbox.Max.X),
171 4: math.Float32bits(bbox.Max.Y),
172 }
173 }
174
175 func FillColor(col color.RGBA) Command {
176 return Command{
177 0: uint32(OpFillColor),
178 1: uint32(col.R)<<24 | uint32(col.G)<<16 | uint32(col.B)<<8 | uint32(col.A),
179 }
180 }
181
182 func FillImage(index int) Command {
183 return Command{
184 0: uint32(OpFillImage),
185 1: uint32(index),
186 }
187 }
188
189 func SetFillMode(mode FillMode) Command {
190 return Command{
191 0: uint32(OpSetFillMode),
192 1: uint32(mode),
193 }
194 }
195
196 func DecodeLine(cmd Command) (from, to f32.Point) {
197 if cmd[0] != uint32(OpLine) {
198 panic("invalid command")
199 }
200 from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
201 to = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
202 return
203 }
204
205 func DecodeQuad(cmd Command) (from, ctrl, to f32.Point) {
206 if cmd[0] != uint32(OpQuad) {
207 panic("invalid command")
208 }
209 from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
210 ctrl = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
211 to = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6]))
212 return
213 }
214
215 func DecodeCubic(cmd Command) (from, ctrl0, ctrl1, to f32.Point) {
216 if cmd[0] != uint32(OpCubic) {
217 panic("invalid command")
218 }
219 from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
220 ctrl0 = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
221 ctrl1 = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6]))
222 to = f32.Pt(math.Float32frombits(cmd[7]), math.Float32frombits(cmd[8]))
223 return
224 }
225