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