mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3bb141c499 |
@@ -0,0 +1,219 @@
|
|||||||
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package raster implements a rasterizer for Gio optimized for embedded
|
||||||
|
platforms.
|
||||||
|
|
||||||
|
Note: the implementation is incomplete.
|
||||||
|
*/
|
||||||
|
package raster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
|
||||||
|
"gioui.org/f32"
|
||||||
|
"gioui.org/internal/ops"
|
||||||
|
"gioui.org/internal/scene"
|
||||||
|
"gioui.org/layout"
|
||||||
|
"gioui.org/op"
|
||||||
|
"golang.org/x/image/vector"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Rasterizer struct {
|
||||||
|
reader ops.Reader
|
||||||
|
|
||||||
|
scratch struct {
|
||||||
|
transforms []f32.Affine2D
|
||||||
|
states []f32.Affine2D
|
||||||
|
clips []clipState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type clipState struct {
|
||||||
|
path []byte
|
||||||
|
trans f32.Affine2D
|
||||||
|
bounds image.Rectangle
|
||||||
|
paths int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rasterizer) Frame(frame *op.Ops, frameBuf *image.RGBA) {
|
||||||
|
if frame == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d := &r.reader
|
||||||
|
d.Reset(&frame.Internal)
|
||||||
|
|
||||||
|
stack := r.scratch.transforms[:0]
|
||||||
|
states := r.scratch.states[:0]
|
||||||
|
clips := r.scratch.clips
|
||||||
|
defer func() {
|
||||||
|
r.scratch.transforms = stack
|
||||||
|
r.scratch.states = states
|
||||||
|
r.scratch.clips = clips
|
||||||
|
}()
|
||||||
|
type decodeState struct {
|
||||||
|
t f32.Affine2D
|
||||||
|
material image.Image
|
||||||
|
}
|
||||||
|
var pathData struct {
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
zeroState := decodeState{
|
||||||
|
material: image.NewUniform(&color.NRGBA{}),
|
||||||
|
}
|
||||||
|
state := zeroState
|
||||||
|
for encOp, ok := d.Decode(); ok; encOp, ok = d.Decode() {
|
||||||
|
switch ops.OpType(encOp.Data[0]) {
|
||||||
|
case ops.TypeTransform:
|
||||||
|
dop, push := ops.DecodeTransform(encOp.Data)
|
||||||
|
if push {
|
||||||
|
stack = append(stack, state.t)
|
||||||
|
}
|
||||||
|
state.t = state.t.Mul(dop)
|
||||||
|
case ops.TypePopTransform:
|
||||||
|
state.t = stack[len(stack)-1]
|
||||||
|
stack = stack[:len(stack)-1]
|
||||||
|
case ops.TypeStroke:
|
||||||
|
// TODO.
|
||||||
|
case ops.TypePath:
|
||||||
|
op, ok := d.Decode()
|
||||||
|
if !ok {
|
||||||
|
panic("unexpected end of path operation")
|
||||||
|
}
|
||||||
|
pathData.data = op.Data[ops.TypeAuxLen:]
|
||||||
|
case ops.TypeClip:
|
||||||
|
var op ops.ClipOp
|
||||||
|
op.Decode(encOp.Data)
|
||||||
|
bounds := transformBounds(state.t, op.Bounds)
|
||||||
|
paths := 0
|
||||||
|
if pathData.data != nil {
|
||||||
|
paths = 1
|
||||||
|
}
|
||||||
|
if len(clips) > 0 {
|
||||||
|
parent := clips[len(clips)-1]
|
||||||
|
bounds = bounds.Intersect(parent.bounds)
|
||||||
|
paths += parent.paths
|
||||||
|
}
|
||||||
|
clips = append(clips, clipState{
|
||||||
|
path: pathData.data,
|
||||||
|
trans: state.t,
|
||||||
|
bounds: bounds,
|
||||||
|
paths: paths,
|
||||||
|
})
|
||||||
|
pathData.data = nil
|
||||||
|
case ops.TypePopClip:
|
||||||
|
clips = clips[:len(clips)-1]
|
||||||
|
case ops.TypeColor:
|
||||||
|
col := decodeColorOp(encOp.Data)
|
||||||
|
state.material = image.NewUniform(col)
|
||||||
|
case ops.TypeLinearGradient:
|
||||||
|
// TODO.
|
||||||
|
case ops.TypeImage:
|
||||||
|
state.material = encOp.Refs[0].(*image.RGBA)
|
||||||
|
case ops.TypePaint:
|
||||||
|
bounds := frameBuf.Bounds()
|
||||||
|
paths := 0
|
||||||
|
if len(clips) > 0 {
|
||||||
|
parent := clips[len(clips)-1]
|
||||||
|
bounds = bounds.Intersect(parent.bounds)
|
||||||
|
paths = parent.paths
|
||||||
|
}
|
||||||
|
if bounds.Empty() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch paths {
|
||||||
|
case 0:
|
||||||
|
draw.Draw(frameBuf, bounds, state.material, state.material.Bounds().Min, draw.Over)
|
||||||
|
case 1:
|
||||||
|
vr := vector.NewRasterizer(bounds.Dx(), bounds.Dy())
|
||||||
|
vr.DrawOp = draw.Over
|
||||||
|
for i := len(clips) - 1; i >= 0; i-- {
|
||||||
|
c := clips[i]
|
||||||
|
if c.path == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
off := layout.FPt(bounds.Min.Mul(-1))
|
||||||
|
decodePath(vr, c.path, c.trans.Offset(off))
|
||||||
|
}
|
||||||
|
vr.Draw(frameBuf, bounds, state.material, state.material.Bounds().Min)
|
||||||
|
}
|
||||||
|
case ops.TypeSave:
|
||||||
|
id := ops.DecodeSave(encOp.Data)
|
||||||
|
if extra := id - len(states) + 1; extra > 0 {
|
||||||
|
states = append(states, make([]f32.Affine2D, extra)...)
|
||||||
|
}
|
||||||
|
states[id] = state.t
|
||||||
|
case ops.TypeLoad:
|
||||||
|
id := ops.DecodeLoad(encOp.Data)
|
||||||
|
state = zeroState
|
||||||
|
state.t = states[id]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePath(r *vector.Rasterizer, pathData []byte, t f32.Affine2D) {
|
||||||
|
for len(pathData) >= scene.CommandSize+4 {
|
||||||
|
cmd := ops.DecodeCommand(pathData[4:])
|
||||||
|
var pen f32.Point
|
||||||
|
switch cmd.Op() {
|
||||||
|
case scene.OpLine:
|
||||||
|
from, to := scene.DecodeLine(cmd)
|
||||||
|
from, to = t.Transform(from), t.Transform(to)
|
||||||
|
if from != pen {
|
||||||
|
r.MoveTo(from.X, from.Y)
|
||||||
|
}
|
||||||
|
r.LineTo(to.X, to.Y)
|
||||||
|
pen = to
|
||||||
|
case scene.OpGap:
|
||||||
|
from, to := scene.DecodeGap(cmd)
|
||||||
|
from, to = t.Transform(from), t.Transform(to)
|
||||||
|
if from != pen {
|
||||||
|
r.MoveTo(from.X, from.Y)
|
||||||
|
}
|
||||||
|
r.LineTo(to.X, to.Y)
|
||||||
|
pen = to
|
||||||
|
case scene.OpQuad:
|
||||||
|
from, ctrl, to := scene.DecodeQuad(cmd)
|
||||||
|
from, ctrl, to = t.Transform(from), t.Transform(ctrl), t.Transform(to)
|
||||||
|
if from != pen {
|
||||||
|
r.MoveTo(from.X, from.Y)
|
||||||
|
}
|
||||||
|
r.QuadTo(ctrl.X, ctrl.Y, to.X, to.Y)
|
||||||
|
pen = to
|
||||||
|
case scene.OpCubic:
|
||||||
|
from, ctrl0, ctrl1, to := scene.DecodeCubic(cmd)
|
||||||
|
from, ctrl0, ctrl1, to = t.Transform(from), t.Transform(ctrl0), t.Transform(ctrl1), t.Transform(to)
|
||||||
|
if from != pen {
|
||||||
|
r.MoveTo(from.X, from.Y)
|
||||||
|
}
|
||||||
|
r.CubeTo(ctrl0.X, ctrl0.Y, ctrl1.X, ctrl1.Y, to.X, to.Y)
|
||||||
|
pen = to
|
||||||
|
default:
|
||||||
|
panic("unsupported scene command")
|
||||||
|
}
|
||||||
|
pathData = pathData[scene.CommandSize+4:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func transformBounds(t f32.Affine2D, bounds image.Rectangle) image.Rectangle {
|
||||||
|
b0 := f32.Rectangle{
|
||||||
|
Min: t.Transform(layout.FPt(bounds.Min)),
|
||||||
|
Max: t.Transform(layout.FPt(bounds.Max)),
|
||||||
|
}.Canon()
|
||||||
|
b1 := f32.Rectangle{
|
||||||
|
Min: t.Transform(layout.FPt(image.Pt(bounds.Max.X, bounds.Min.Y))),
|
||||||
|
Max: t.Transform(layout.FPt(image.Pt(bounds.Min.X, bounds.Max.Y))),
|
||||||
|
}.Canon()
|
||||||
|
return b0.Union(b1).Round()
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeColorOp(data []byte) color.NRGBA {
|
||||||
|
return color.NRGBA{
|
||||||
|
R: data[1],
|
||||||
|
G: data[2],
|
||||||
|
B: data[3],
|
||||||
|
A: data[4],
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user