Files
gio/internal/scene/scene.go
T
Pierre Curto 11bb86166a op/clip: automatically close Path in Outlines
Unclosed path segments in Path will be automatically
closed by a line.

Fixes: https://todo.sr.ht/~eliasnaur/gio/320
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2021-12-21 10:13:20 +01:00

252 lines
5.8 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
// Package scene encodes and decodes graphics commands in the format used by the
// compute renderer.
package scene
import (
"fmt"
"image"
"image/color"
"math"
"unsafe"
"gioui.org/f32"
)
type Op uint32
type Command [sceneElemSize / 4]uint32
// GPU commands from piet/scene.h in package gioui.org/shaders.
const (
OpNop Op = iota
OpLine
OpQuad
OpCubic
OpFillColor
OpLineWidth
OpTransform
OpBeginClip
OpEndClip
OpFillImage
OpSetFillMode
OpGap
)
// FillModes, from setup.h.
type FillMode uint32
const (
FillModeNonzero = 0
FillModeStroke = 1
)
const CommandSize = int(unsafe.Sizeof(Command{}))
const sceneElemSize = 36
func (c Command) Op() Op {
return Op(c[0])
}
func (c Command) String() string {
switch Op(c[0]) {
case OpNop:
return "nop"
case OpLine:
from, to := DecodeLine(c)
return fmt.Sprintf("line(%v, %v)", from, to)
case OpGap:
from, to := DecodeLine(c)
return fmt.Sprintf("gap(%v, %v)", from, to)
case OpQuad:
from, ctrl, to := DecodeQuad(c)
return fmt.Sprintf("quad(%v, %v, %v)", from, ctrl, to)
case OpCubic:
from, ctrl0, ctrl1, to := DecodeCubic(c)
return fmt.Sprintf("cubic(%v, %v, %v, %v)", from, ctrl0, ctrl1, to)
case OpFillColor:
return fmt.Sprintf("fillcolor %#.8x", c[1])
case OpLineWidth:
return "linewidth"
case OpTransform:
t := f32.NewAffine2D(
math.Float32frombits(c[1]),
math.Float32frombits(c[3]),
math.Float32frombits(c[5]),
math.Float32frombits(c[2]),
math.Float32frombits(c[4]),
math.Float32frombits(c[6]),
)
return fmt.Sprintf("transform (%v)", t)
case OpBeginClip:
bounds := f32.Rectangle{
Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])),
Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])),
}
return fmt.Sprintf("beginclip (%v)", bounds)
case OpEndClip:
bounds := f32.Rectangle{
Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])),
Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])),
}
return fmt.Sprintf("endclip (%v)", bounds)
case OpFillImage:
return "fillimage"
case OpSetFillMode:
return "setfillmode"
default:
panic("unreachable")
}
}
func Line(start, end f32.Point) Command {
return Command{
0: uint32(OpLine),
1: math.Float32bits(start.X),
2: math.Float32bits(start.Y),
3: math.Float32bits(end.X),
4: math.Float32bits(end.Y),
}
}
func Gap(start, end f32.Point) Command {
return Command{
0: uint32(OpGap),
1: math.Float32bits(start.X),
2: math.Float32bits(start.Y),
3: math.Float32bits(end.X),
4: math.Float32bits(end.Y),
}
}
func Cubic(start, ctrl0, ctrl1, end f32.Point) Command {
return Command{
0: uint32(OpCubic),
1: math.Float32bits(start.X),
2: math.Float32bits(start.Y),
3: math.Float32bits(ctrl0.X),
4: math.Float32bits(ctrl0.Y),
5: math.Float32bits(ctrl1.X),
6: math.Float32bits(ctrl1.Y),
7: math.Float32bits(end.X),
8: math.Float32bits(end.Y),
}
}
func Quad(start, ctrl, end f32.Point) Command {
return Command{
0: uint32(OpQuad),
1: math.Float32bits(start.X),
2: math.Float32bits(start.Y),
3: math.Float32bits(ctrl.X),
4: math.Float32bits(ctrl.Y),
5: math.Float32bits(end.X),
6: math.Float32bits(end.Y),
}
}
func Transform(m f32.Affine2D) Command {
sx, hx, ox, hy, sy, oy := m.Elems()
return Command{
0: uint32(OpTransform),
1: math.Float32bits(sx),
2: math.Float32bits(hy),
3: math.Float32bits(hx),
4: math.Float32bits(sy),
5: math.Float32bits(ox),
6: math.Float32bits(oy),
}
}
func SetLineWidth(width float32) Command {
return Command{
0: uint32(OpLineWidth),
1: math.Float32bits(width),
}
}
func BeginClip(bbox f32.Rectangle) Command {
return Command{
0: uint32(OpBeginClip),
1: math.Float32bits(bbox.Min.X),
2: math.Float32bits(bbox.Min.Y),
3: math.Float32bits(bbox.Max.X),
4: math.Float32bits(bbox.Max.Y),
}
}
func EndClip(bbox f32.Rectangle) Command {
return Command{
0: uint32(OpEndClip),
1: math.Float32bits(bbox.Min.X),
2: math.Float32bits(bbox.Min.Y),
3: math.Float32bits(bbox.Max.X),
4: math.Float32bits(bbox.Max.Y),
}
}
func FillColor(col color.RGBA) Command {
return Command{
0: uint32(OpFillColor),
1: uint32(col.R)<<24 | uint32(col.G)<<16 | uint32(col.B)<<8 | uint32(col.A),
}
}
func FillImage(index int, offset image.Point) Command {
x := int16(offset.X)
y := int16(offset.Y)
return Command{
0: uint32(OpFillImage),
1: uint32(index),
2: uint32(uint16(x)) | uint32(uint16(y))<<16,
}
}
func SetFillMode(mode FillMode) Command {
return Command{
0: uint32(OpSetFillMode),
1: uint32(mode),
}
}
func DecodeLine(cmd Command) (from, to f32.Point) {
if cmd[0] != uint32(OpLine) {
panic("invalid command")
}
from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
to = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
return
}
func DecodeGap(cmd Command) (from, to f32.Point) {
if cmd[0] != uint32(OpGap) {
panic("invalid command")
}
from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
to = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
return
}
func DecodeQuad(cmd Command) (from, ctrl, to f32.Point) {
if cmd[0] != uint32(OpQuad) {
panic("invalid command")
}
from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
ctrl = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
to = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6]))
return
}
func DecodeCubic(cmd Command) (from, ctrl0, ctrl1, to f32.Point) {
if cmd[0] != uint32(OpCubic) {
panic("invalid command")
}
from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
ctrl0 = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
ctrl1 = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6]))
to = f32.Pt(math.Float32frombits(cmd[7]), math.Float32frombits(cmd[8]))
return
}