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