github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/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/color" 10 "math" 11 "unsafe" 12 13 "github.com/cybriq/giocore/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 }