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  }