Documentation
¶
Overview ¶
Package raster provides CPU-based rasterization primitives for gg.
This package contains the core algorithms for scanline conversion, edge processing, and anti-aliased filling. It is independent of any GPU backend and can be used for pure software rendering.
Architecture ¶
The raster package follows the Skia/tiny-skia design where CPU rendering code is separated from GPU code. This allows:
- Clear separation of concerns (CPU vs GPU)
- Reuse of CPU algorithms in different contexts
- Testing of rendering logic without GPU dependencies
Key Components ¶
- Fixed-point math (FDot6, FDot16) for precise curve rasterization
- Edge types (Line, Quadratic, Cubic) with forward differencing
- EdgeBuilder for converting paths to edges
- AET (Active Edge Table) for scanline processing
- Filler for analytic anti-aliased rendering
- AlphaRuns for RLE-compressed coverage storage
Usage ¶
The typical flow is:
- Convert path to edges using EdgeBuilder
- Process edges through AET (Active Edge Table)
- Accumulate coverage using AlphaRuns
- Call Filler to render with anti-aliasing
References ¶
Index ¶
- Constants
- func ChopCubicAtYExtrema(src [4]GeomPoint, dst *[10]GeomPoint) int
- func ChopQuadAtYExtrema(src [3]GeomPoint, dst *[5]GeomPoint) int
- func CubicIsYMonotonic(p0, p1, p2, p3 GeomPoint) bool
- func FDot6CanConvertToFDot16(v FDot6) bool
- func FDot6Ceil(v FDot6) int32
- func FDot6Floor(v FDot6) int32
- func FDot6Round(v FDot6) int32
- func FDot6SmallScale(value uint8, dot6 FDot6) uint8
- func FDot6ToFloat32(v FDot6) float32
- func FDot6ToFloat64(v FDot6) float64
- func FDot6UpShift(v FDot6, upShift int) int32
- func FDot16CeilToInt(v FDot16) int32
- func FDot16FloorToInt(v FDot16) int32
- func FDot16RoundToInt(v FDot16) int32
- func FDot16ToFloat32(v FDot16) float32
- func FDot16ToFloat64(v FDot16) float64
- func FillPath(eb *EdgeBuilder, width, height int, fillRule FillRule, ...)
- func FillToBuffer(eb *EdgeBuilder, width, height int, fillRule FillRule, buffer []uint8)
- func QuadIsYMonotonic(p0, p1, p2 GeomPoint) bool
- type ActiveEdge
- type AlphaRun
- type AlphaRuns
- func (ar *AlphaRuns) Add(x int, startAlpha uint8, middleCount int, endAlpha uint8)
- func (ar *AlphaRuns) AddWithCoverage(x int, startAlpha uint8, middleCount int, endAlpha uint8, maxValue uint8)
- func (ar *AlphaRuns) Clear()
- func (ar *AlphaRuns) CopyTo(dst []uint8)
- func (ar *AlphaRuns) GetAlpha(x int) uint8
- func (ar *AlphaRuns) IsEmpty() bool
- func (ar *AlphaRuns) Iter() iter.Seq2[int, uint8]
- func (ar *AlphaRuns) IterRuns() iter.Seq[AlphaRun]
- func (ar *AlphaRuns) Reset()
- func (ar *AlphaRuns) SetOffset(offset int)
- func (ar *AlphaRuns) Width() int
- type AnalyticFiller
- func (af *AnalyticFiller) AlphaRuns() *AlphaRuns
- func (af *AnalyticFiller) Coverage() []float32
- func (af *AnalyticFiller) Fill(eb *EdgeBuilder, fillRule FillRule, callback func(y int, runs *AlphaRuns))
- func (af *AnalyticFiller) Height() int
- func (af *AnalyticFiller) Reset()
- func (af *AnalyticFiller) Width() int
- type CubicEdge
- type CurveAwareAET
- func (aet *CurveAwareAET) AdvanceX()
- func (aet *CurveAwareAET) ComputeSpans(_ int32, fillRule FillRule, callback func(x, width int, coverage float32))
- func (aet *CurveAwareAET) EdgeAt(i int) *CurveEdgeVariant
- func (aet *CurveAwareAET) Edges() []CurveEdgeVariant
- func (aet *CurveAwareAET) ForEach(fn func(edge *CurveEdgeVariant) bool)
- func (aet *CurveAwareAET) Insert(e CurveEdgeVariant)
- func (aet *CurveAwareAET) IsEmpty() bool
- func (aet *CurveAwareAET) Len() int
- func (aet *CurveAwareAET) RemoveExpired(y int32)
- func (aet *CurveAwareAET) RemoveExpiredSubpixel(ySubpixel int32)
- func (aet *CurveAwareAET) Reset()
- func (aet *CurveAwareAET) SortByX()
- func (aet *CurveAwareAET) StepCurves()
- type CurveEdgeVariant
- type CurveEdger
- type CurvePoint
- type Edge
- type EdgeBuilder
- func (eb *EdgeBuilder) AAShift() int
- func (eb *EdgeBuilder) AllEdges() iter.Seq[CurveEdgeVariant]
- func (eb *EdgeBuilder) Bounds() Rect
- func (eb *EdgeBuilder) BuildFromPath(path PathLike, transform Transform)
- func (eb *EdgeBuilder) CubicEdgeCount() int
- func (eb *EdgeBuilder) CubicEdges() iter.Seq[*CubicEdge]
- func (eb *EdgeBuilder) EdgeCount() int
- func (eb *EdgeBuilder) FlattenCurves() bool
- func (eb *EdgeBuilder) IsEmpty() bool
- func (eb *EdgeBuilder) LineEdgeCount() int
- func (eb *EdgeBuilder) LineEdges() iter.Seq[*LineEdge]
- func (eb *EdgeBuilder) QuadraticEdgeCount() int
- func (eb *EdgeBuilder) QuadraticEdges() iter.Seq[*QuadraticEdge]
- func (eb *EdgeBuilder) Reset()
- func (eb *EdgeBuilder) SetFlattenCurves(flatten bool)
- func (eb *EdgeBuilder) VelloLines() []VelloLine
- type EdgeList
- type EdgeRange
- type EdgeType
- type FDot6
- type FDot8
- type FDot16
- func FDot6Div(a, b FDot6) FDot16
- func FDot6ToFDot16(v FDot6) FDot16
- func FDot6ToFixedDiv2(v FDot6) FDot16
- func FDot16Div(numer, denom int32) FDot16
- func FDot16FastDiv(a, b FDot6) FDot16
- func FDot16FromFloat32(f float32) FDot16
- func FDot16FromFloat64(f float64) FDot16
- func FDot16Mul(a, b FDot16) FDot16
- type FillRule
- type GeomPoint
- type IdentityTransform
- type LineEdge
- type PathLike
- type PathVerb
- type QuadraticEdge
- type Rect
- type ScenePathAdapter
- type SimpleAET
- type Transform
- type VelloLine
Constants ¶
const ( // FDot6One is 1.0 in FDot6 representation (2^6 = 64). FDot6One FDot6 = 64 // FDot6Half is 0.5 in FDot6 representation (2^5 = 32). FDot6Half FDot6 = 32 // FDot6Shift is the number of fractional bits in FDot6. FDot6Shift = 6 // FDot6Mask is the mask for the fractional part of FDot6. FDot6Mask = FDot6One - 1 )
Fixed-point constants for FDot6.
const ( // FDot16One is 1.0 in FDot16 representation (2^16 = 65536). FDot16One FDot16 = 1 << 16 // FDot16Half is 0.5 in FDot16 representation (2^15 = 32768). FDot16Half FDot16 = 1 << 15 // FDot16Shift is the number of fractional bits in FDot16. FDot16Shift = 16 // FDot16Mask is the mask for the fractional part of FDot16. FDot16Mask = FDot16One - 1 )
Fixed-point constants for FDot16.
const Epsilon = 1e-6
Epsilon is a small value for floating point comparison.
const MaxCoeffShift = 6
MaxCoeffShift limits the number of subdivisions for a curve. We store 1<<shift in a signed byte (int8), so max value is 1<<6 = 64. This limits the number of line segments per curve.
Variables ¶
This section is empty.
Functions ¶
func ChopCubicAtYExtrema ¶ added in v0.25.1
ChopCubicAtYExtrema chops a cubic Bezier at its Y extrema (if any).
A cubic Bezier can have up to 2 Y extrema. This function splits the curve at all extrema to produce 1-3 monotonic cubic segments.
Parameters:
- src: 4 control points [p0, p1, p2, p3]
- dst: output array, must have capacity for 10 points
Returns:
- number of chops (0, 1, or 2)
- 0 means dst[0..4] contains the original (already monotonic)
- 1 means dst[0..4] and dst[3..7] are two monotonic cubics
- 2 means dst[0..4], dst[3..7], and dst[6..10] are three monotonic cubics
func ChopQuadAtYExtrema ¶ added in v0.25.1
ChopQuadAtYExtrema chops a quadratic Bezier at its Y extremum (if any).
A quadratic Bezier can have at most one Y extremum. If the curve is not monotonic in Y, this function splits it at the extremum point.
Parameters:
- src: 3 control points [p0, p1, p2]
- dst: output array, must have capacity for 5 points
Returns:
- number of chops (0 or 1)
- 0 means dst[0..3] contains the original (already monotonic)
- 1 means dst[0..3] and dst[2..5] are two monotonic quads
The control points are structured as:
- dst[0] = first quad start
- dst[1] = first quad control
- dst[2] = first quad end = second quad start (shared)
- dst[3] = second quad control
- dst[4] = second quad end
func CubicIsYMonotonic ¶ added in v0.25.1
CubicIsYMonotonic returns true if the cubic is monotonic in Y. A cubic is Y-monotonic if it has no Y extrema in (0, 1).
func FDot6CanConvertToFDot16 ¶ added in v0.25.1
FDot6CanConvertToFDot16 returns true if the FDot6 value can be converted to FDot16 without overflow.
func FDot6Floor ¶ added in v0.21.2
FDot6Floor returns the integer part (floor) of an FDot6.
func FDot6Round ¶ added in v0.21.2
FDot6Round returns the nearest integer to an FDot6.
func FDot6SmallScale ¶ added in v0.21.2
FDot6SmallScale scales a byte value by an FDot6 factor (0 to 64). Used for alpha blending calculations.
func FDot6ToFloat32 ¶ added in v0.25.1
FDot6ToFloat32 converts an FDot6 to float32.
func FDot6ToFloat64 ¶ added in v0.25.1
FDot6ToFloat64 converts an FDot6 to float64.
func FDot6UpShift ¶ added in v0.25.1
FDot6UpShift performs a left shift on an FDot6 value. Used in cubic edge coefficient computation.
func FDot16CeilToInt ¶ added in v0.25.1
FDot16CeilToInt returns the ceiling of an FDot16.
func FDot16FloorToInt ¶ added in v0.25.1
FDot16FloorToInt returns the integer part (floor) of an FDot16.
func FDot16RoundToInt ¶ added in v0.25.1
FDot16RoundToInt returns the nearest integer to an FDot16.
func FDot16ToFloat32 ¶ added in v0.25.1
FDot16ToFloat32 converts an FDot16 to float32.
func FDot16ToFloat64 ¶ added in v0.25.1
FDot16ToFloat64 converts an FDot16 to float64.
func FillPath ¶ added in v0.25.1
func FillPath( eb *EdgeBuilder, width, height int, fillRule FillRule, callback func(y int, runs *AlphaRuns), )
FillPath is a convenience function that creates a filler and fills a path. For repeated fills, create a filler once and reuse it.
func FillToBuffer ¶ added in v0.25.1
func FillToBuffer( eb *EdgeBuilder, width, height int, fillRule FillRule, buffer []uint8, )
FillToBuffer fills a path and writes coverage to a buffer. The buffer must have width * height elements. Coverage values are written as 0-255 alpha values.
func QuadIsYMonotonic ¶ added in v0.25.1
QuadIsYMonotonic returns true if the quadratic is monotonic in Y. A quadratic is Y-monotonic if its control point's Y is between the endpoints' Y values. This is more permissive than isNotMonotonic to handle curves flattened at extrema.
Types ¶
type ActiveEdge ¶
ActiveEdge holds an edge with its current X position.
type AlphaRun ¶ added in v0.25.1
type AlphaRun struct {
X int // Starting X position
Alpha uint8 // Coverage value (0-255)
Count int // Run length
}
AlphaRun represents a single run of coverage. Used for iteration output.
type AlphaRuns ¶ added in v0.19.0
type AlphaRuns struct {
// contains filtered or unexported fields
}
AlphaRuns provides run-length-encoded (RLE) storage for coverage values.
This is efficient for paths with long horizontal spans of constant coverage. Instead of storing per-pixel alpha values, it stores runs of consecutive pixels with the same alpha.
The implementation follows tiny-skia's alpha_runs.rs pattern but with Go 1.25+ iterators for efficient traversal.
Usage:
ar := NewAlphaRuns(width)
ar.Reset()
ar.Add(10, 128, 20, 128) // Add coverage from x=10, 20 pixels wide
for x, alpha := range ar.Iter() {
// Process pixel at x with alpha value
}
func NewAlphaRuns ¶ added in v0.19.0
NewAlphaRuns creates a new AlphaRuns buffer for the given width.
func (*AlphaRuns) Add ¶ added in v0.19.0
Add inserts coverage into the buffer.
Parameters:
- x: starting x coordinate
- startAlpha: alpha for first pixel (fractional left edge)
- middleCount: number of full-coverage pixels
- endAlpha: alpha for last pixel (fractional right edge)
The method accumulates coverage - multiple Add calls can contribute to the same pixels. This is essential for correct winding rule handling.
func (*AlphaRuns) AddWithCoverage ¶ added in v0.25.1
func (ar *AlphaRuns) AddWithCoverage(x int, startAlpha uint8, middleCount int, endAlpha uint8, maxValue uint8)
AddWithCoverage inserts coverage with a specified maximum value. This is useful when the coverage itself represents partial opacity.
func (*AlphaRuns) Clear ¶ added in v0.25.1
func (ar *AlphaRuns) Clear()
Clear sets all alpha values to zero and resets to a single run.
func (*AlphaRuns) CopyTo ¶ added in v0.25.1
CopyTo copies the coverage values to a destination slice. The destination must have at least ar.width elements.
func (*AlphaRuns) GetAlpha ¶ added in v0.25.1
GetAlpha returns the alpha value at position x. Returns 0 if x is out of bounds.
func (*AlphaRuns) IsEmpty ¶ added in v0.19.0
IsEmpty returns true if the scanline contains only a single run of alpha 0.
func (*AlphaRuns) Iter ¶ added in v0.25.1
Iter returns an iterator over all runs with non-zero alpha. Each iteration yields the X position and alpha value. This uses Go 1.25+ iter.Seq2 for efficient iteration.
func (*AlphaRuns) IterRuns ¶ added in v0.25.1
IterRuns returns an iterator over runs (not individual pixels). More efficient when processing entire runs at once.
func (*AlphaRuns) Reset ¶ added in v0.19.0
func (ar *AlphaRuns) Reset()
Reset reinitializes the buffer for a new scanline. This is O(1) - it doesn't clear the entire buffer.
type AnalyticFiller ¶ added in v0.25.1
type AnalyticFiller struct {
// contains filtered or unexported fields
}
AnalyticFiller computes per-pixel coverage using exact geometric calculations.
Unlike supersampling approaches that sample multiple points per pixel, analytic AA computes the exact area of the shape within each pixel using trapezoidal integration. This provides higher quality anti-aliasing with no supersampling overhead.
The algorithm is based on vello's CPU fine rasterizer (fine.rs), which uses the following approach:
- For each edge crossing a pixel row, compute the Y range it covers
- Find the X intersections at the top and bottom of the pixel
- Compute the trapezoidal area within the pixel bounds
- Accumulate coverage based on winding direction
Usage:
filler := NewAnalyticFiller(width, height)
filler.Fill(edgeBuilder, FillRuleNonZero, func(y int, runs *AlphaRuns) {
// Blend alpha runs to the destination row
})
func NewAnalyticFiller ¶ added in v0.25.1
func NewAnalyticFiller(width, height int) *AnalyticFiller
NewAnalyticFiller creates a new analytic filler for the given dimensions.
func (*AnalyticFiller) AlphaRuns ¶ added in v0.25.1
func (af *AnalyticFiller) AlphaRuns() *AlphaRuns
AlphaRuns returns the alpha runs for the last processed scanline.
func (*AnalyticFiller) Coverage ¶ added in v0.25.1
func (af *AnalyticFiller) Coverage() []float32
Coverage returns the raw coverage buffer for the last processed scanline. Values are in [0, 1] range. The buffer is reused between scanlines.
func (*AnalyticFiller) Fill ¶ added in v0.25.1
func (af *AnalyticFiller) Fill( eb *EdgeBuilder, fillRule FillRule, callback func(y int, runs *AlphaRuns), )
Fill renders a path using analytic coverage calculation.
Parameters:
- eb: EdgeBuilder containing the path edges
- fillRule: NonZero or EvenOdd fill rule
- callback: called for each scanline with the alpha runs
The callback receives the Y coordinate and AlphaRuns for that scanline. The caller is responsible for blending the runs to the destination.
func (*AnalyticFiller) Height ¶ added in v0.25.1
func (af *AnalyticFiller) Height() int
Height returns the filler height.
func (*AnalyticFiller) Reset ¶ added in v0.25.1
func (af *AnalyticFiller) Reset()
Reset clears the filler state for reuse.
func (*AnalyticFiller) Width ¶ added in v0.25.1
func (af *AnalyticFiller) Width() int
Width returns the filler width.
type CubicEdge ¶ added in v0.25.1
type CubicEdge struct {
// TopY is the curve's overall top scanline (for AET insertion timing).
// This is set once at creation and never changes, unlike line.FirstY
// which changes as we step through curve segments.
TopY int32
// BottomY is the curve's overall bottom scanline.
BottomY int32
// contains filtered or unexported fields
}
CubicEdge represents a cubic Bezier curve for scanline conversion. Uses forward differencing for O(1) per-step evaluation.
A cubic Bezier is defined by:
p(t) = (1-t)^3 * p0 + 3*t*(1-t)^2 * p1 + 3*t^2*(1-t) * p2 + t^3 * p3
Rewritten in polynomial form:
p(t) = A*t^3 + B*t^2 + C*t + D where: A = -p0 + 3*p1 - 3*p2 + p3 B = 3*p0 - 6*p1 + 3*p2 C = -3*p0 + 3*p1 D = p0
func NewCubicEdge ¶ added in v0.25.1
func NewCubicEdge(p0, p1, p2, p3 CurvePoint, shift int) *CubicEdge
NewCubicEdge creates a cubic edge from control points. Returns nil if the curve has no vertical extent.
Parameters:
- p0: start point
- p1: first control point
- p2: second control point
- p3: end point
- shift: AA shift (0 for no AA, 2 for 4x AA quality)
func (*CubicEdge) CurveCount ¶ added in v0.25.1
CurveCount returns the remaining number of segments. For cubic, this is negative (counts up to 0).
type CurveAwareAET ¶ added in v0.25.1
type CurveAwareAET struct {
// contains filtered or unexported fields
}
CurveAwareAET is an Active Edge Table that handles all edge types.
Unlike a simple line-based AET, this table can hold LineEdge, QuadraticEdge, and CubicEdge types. Curve edges are stepped through their segments during scanline traversal using forward differencing.
The AET maintains edges sorted by their current X position, which is essential for correct scanline rasterization and winding number calculation.
Usage:
aet := NewCurveAwareAET()
for y := yMin; y < yMax; y++ {
aet.RemoveExpired(y)
for edge := range eb.EdgesStartingAt(y) {
aet.Insert(edge)
}
aet.StepCurves()
aet.SortByX()
// Process edges in X order
}
func NewCurveAwareAET ¶ added in v0.25.1
func NewCurveAwareAET() *CurveAwareAET
NewCurveAwareAET creates a new curve-aware Active Edge Table.
func (*CurveAwareAET) AdvanceX ¶ added in v0.25.1
func (aet *CurveAwareAET) AdvanceX()
AdvanceX advances all edges to the next scanline. This updates the X position based on the slope (DX).
func (*CurveAwareAET) ComputeSpans ¶ added in v0.25.1
func (aet *CurveAwareAET) ComputeSpans(_ int32, fillRule FillRule, callback func(x, width int, coverage float32))
ComputeSpans computes the horizontal spans for the current scanline. This implements the core of scanline rasterization:
- For each pair of edges, compute the winding contribution
- Apply fill rule to determine coverage
- Return spans with their coverage values
The callback is called for each span with x, width, and coverage.
func (*CurveAwareAET) EdgeAt ¶ added in v0.25.1
func (aet *CurveAwareAET) EdgeAt(i int) *CurveEdgeVariant
EdgeAt returns the edge at index i. Panics if i is out of bounds.
func (*CurveAwareAET) Edges ¶ added in v0.25.1
func (aet *CurveAwareAET) Edges() []CurveEdgeVariant
Edges returns the slice of edge variants for processing. The edges are in X-sorted order after SortByX() is called.
func (*CurveAwareAET) ForEach ¶ added in v0.25.1
func (aet *CurveAwareAET) ForEach(fn func(edge *CurveEdgeVariant) bool)
ForEach calls fn for each edge in the AET. Iteration stops if fn returns false.
func (*CurveAwareAET) Insert ¶ added in v0.25.1
func (aet *CurveAwareAET) Insert(e CurveEdgeVariant)
Insert adds an edge to the AET. The edge's current line segment is used for initial positioning.
func (*CurveAwareAET) IsEmpty ¶ added in v0.25.1
func (aet *CurveAwareAET) IsEmpty() bool
IsEmpty returns true if there are no active edges.
func (*CurveAwareAET) Len ¶ added in v0.25.1
func (aet *CurveAwareAET) Len() int
Len returns the number of active edges.
func (*CurveAwareAET) RemoveExpired ¶ added in v0.25.1
func (aet *CurveAwareAET) RemoveExpired(y int32)
RemoveExpired removes edges that have ended before scanline y. An edge is expired when its LastY < y.
func (*CurveAwareAET) RemoveExpiredSubpixel ¶ added in v0.25.1
func (aet *CurveAwareAET) RemoveExpiredSubpixel(ySubpixel int32)
RemoveExpiredSubpixel removes edges that have completely ended. Uses the edge's overall BottomY (not current segment) for expiration.
This is used when coordinates are in sub-pixel space with AA scaling. An edge is expired when its BottomY <= ySubpixel.
func (*CurveAwareAET) Reset ¶ added in v0.25.1
func (aet *CurveAwareAET) Reset()
Reset clears the AET for reuse.
func (*CurveAwareAET) SortByX ¶ added in v0.25.1
func (aet *CurveAwareAET) SortByX()
SortByX sorts all edges by their current X position. This is essential for correct scanline processing.
func (*CurveAwareAET) StepCurves ¶ added in v0.25.1
func (aet *CurveAwareAET) StepCurves()
StepCurves advances curve edges to their next line segment. This should be called once per scanline after RemoveExpired.
For each curve edge whose current segment has ended, Update() is called to compute the next segment. This is where forward differencing happens.
type CurveEdgeVariant ¶ added in v0.25.1
type CurveEdgeVariant struct {
Type EdgeType
Line *LineEdge
Quadratic *QuadraticEdge
Cubic *CubicEdge
}
CurveEdgeVariant wraps different edge types for uniform handling in the AET. This is Go's equivalent of Rust's enum Edge { Line, Quadratic, Cubic }.
func NewCubicEdgeVariant ¶ added in v0.25.1
func NewCubicEdgeVariant(p0, p1, p2, p3 CurvePoint, shift int) *CurveEdgeVariant
NewCubicEdgeVariant creates a CurveEdgeVariant for a cubic.
func NewLineEdgeVariant ¶ added in v0.25.1
func NewLineEdgeVariant(p0, p1 CurvePoint, shift int) *CurveEdgeVariant
NewLineEdgeVariant creates a CurveEdgeVariant for a line.
func NewQuadraticEdgeVariant ¶ added in v0.25.1
func NewQuadraticEdgeVariant(p0, p1, p2 CurvePoint, shift int) *CurveEdgeVariant
NewQuadraticEdgeVariant creates a CurveEdgeVariant for a quadratic.
func (*CurveEdgeVariant) AsLine ¶ added in v0.25.1
func (e *CurveEdgeVariant) AsLine() *LineEdge
AsLine returns the LineEdge for this edge, regardless of type. All edge types contain a LineEdge for AET compatibility.
func (*CurveEdgeVariant) BottomY ¶ added in v0.25.1
func (e *CurveEdgeVariant) BottomY() int32
BottomY returns the curve's overall bottom Y coordinate. For line edges, this is the same as LastY + 1. For curve edges, this is the curve's original bottom Y.
func (*CurveEdgeVariant) TopY ¶ added in v0.25.1
func (e *CurveEdgeVariant) TopY() int32
TopY returns the curve's overall top Y coordinate (for AET insertion timing). For line edges, this is the same as FirstY. For curve edges, this is the curve's original top Y before stepping.
func (*CurveEdgeVariant) Update ¶ added in v0.25.1
func (e *CurveEdgeVariant) Update() bool
Update advances a curve edge to the next line segment. Returns true if a valid segment was produced. For line edges, always returns false (no more segments).
type CurveEdger ¶ added in v0.25.1
type CurveEdger interface {
// Update advances the curve to the next line segment.
// Returns true if a valid segment was produced, false if done.
Update() bool
// Line returns the current line segment for AET processing.
Line() *LineEdge
// CurveCount returns the remaining number of segments.
CurveCount() int8
// Winding returns the winding direction (+1 or -1).
Winding() int8
}
CurveEdger is the interface implemented by curve edges (quadratic, cubic). It allows polymorphic handling of different curve types in the AET.
type CurvePoint ¶ added in v0.25.1
type CurvePoint struct {
X, Y float32
}
CurvePoint represents a 2D point for curve edge construction. Using separate type to avoid coupling with scene.Point.
type Edge ¶
type Edge struct {
// YMin is the minimum Y coordinate (top of edge)
YMin float32
// YMax is the maximum Y coordinate (bottom of edge)
YMax float32
// XAtYMin is the X coordinate at YMin
XAtYMin float32
// DXDY is the inverse slope: change in X per unit Y
DXDY float32
// Winding indicates the direction: +1 for downward, -1 for upward
Winding int8
}
Edge represents a line segment for scanline conversion. Edges are derived from path segments (lines, curves flattened to lines) and used by the Active Edge Table algorithm.
func NewEdge ¶
NewEdge creates a new edge from two points. Returns nil if the edge is horizontal (no Y extent).
func NewEdgeWithWinding ¶ added in v0.25.1
NewEdgeWithWinding creates a new edge with explicit winding.
func (*Edge) ContainsY ¶ added in v0.25.1
ContainsY returns true if Y is within the edge's Y range (inclusive).
func (*Edge) IsActiveAt ¶ added in v0.25.1
IsActiveAt returns true if the edge is active at the given Y coordinate. An edge is active when YMin <= y < YMax.
type EdgeBuilder ¶ added in v0.25.1
type EdgeBuilder struct {
// contains filtered or unexported fields
}
EdgeBuilder converts paths to typed edges for analytic anti-aliasing.
By default, EdgeBuilder preserves curve information (QuadraticEdge, CubicEdge) which enables higher quality anti-aliasing by evaluating curve coverage analytically.
Alternatively, with flattenCurves=true, all curves are converted to line segments at build time. This is simpler and more reliable for the AnalyticFiller.
The builder ensures all edges are Y-monotonic by chopping curves at their Y extrema before creating edge objects.
Usage:
eb := NewEdgeBuilder(2) // 4x AA quality
eb.SetFlattenCurves(true) // Flatten curves to lines
eb.BuildFromPath(path, IdentityTransform{})
for edge := range eb.AllEdges() {
// Process edges sorted by top Y
}
Reference: tiny-skia/src/edge_builder.rs
func NewEdgeBuilder ¶ added in v0.25.1
func NewEdgeBuilder(aaShift int) *EdgeBuilder
NewEdgeBuilder creates a new edge builder with specified AA quality.
Parameters:
- aaShift: anti-aliasing shift (0 = no AA, 2 = 4x AA quality)
Higher shift values provide better AA quality but require more memory and computation.
func (*EdgeBuilder) AAShift ¶ added in v0.25.1
func (eb *EdgeBuilder) AAShift() int
AAShift returns the anti-aliasing shift value.
func (*EdgeBuilder) AllEdges ¶ added in v0.25.1
func (eb *EdgeBuilder) AllEdges() iter.Seq[CurveEdgeVariant]
AllEdges returns an iterator over all edges sorted by top Y coordinate.
This uses Go 1.25+ iter.Seq for efficient iteration. Edges are yielded in scanline order (top to bottom), which is required for Active Edge Table processing.
Usage:
for edge := range eb.AllEdges() {
line := edge.AsLine()
// Process edge.Line().FirstY, etc.
}
func (*EdgeBuilder) Bounds ¶ added in v0.25.1
func (eb *EdgeBuilder) Bounds() Rect
Bounds returns the bounding rectangle of all edges.
func (*EdgeBuilder) BuildFromPath ¶ added in v0.25.1
func (eb *EdgeBuilder) BuildFromPath(path PathLike, transform Transform)
BuildFromPath processes a PathLike and creates typed edges.
This is the main entry point for path processing. It:
- Iterates through path verbs
- Applies transform to all points
- Chops curves at Y extrema for monotonicity
- Creates appropriate edge types
Parameters:
- path: the path to process (implements PathLike interface)
- transform: transformation to apply to all points
func (*EdgeBuilder) CubicEdgeCount ¶ added in v0.25.1
func (eb *EdgeBuilder) CubicEdgeCount() int
CubicEdgeCount returns the number of cubic edges.
func (*EdgeBuilder) CubicEdges ¶ added in v0.25.1
func (eb *EdgeBuilder) CubicEdges() iter.Seq[*CubicEdge]
CubicEdges returns an iterator over cubic edges only.
func (*EdgeBuilder) EdgeCount ¶ added in v0.25.1
func (eb *EdgeBuilder) EdgeCount() int
EdgeCount returns the total number of edges.
func (*EdgeBuilder) FlattenCurves ¶ added in v0.25.1
func (eb *EdgeBuilder) FlattenCurves() bool
FlattenCurves returns whether curve flattening is enabled.
func (*EdgeBuilder) IsEmpty ¶ added in v0.25.1
func (eb *EdgeBuilder) IsEmpty() bool
IsEmpty returns true if no edges have been added.
func (*EdgeBuilder) LineEdgeCount ¶ added in v0.25.1
func (eb *EdgeBuilder) LineEdgeCount() int
LineEdgeCount returns the number of line edges.
func (*EdgeBuilder) LineEdges ¶ added in v0.25.1
func (eb *EdgeBuilder) LineEdges() iter.Seq[*LineEdge]
LineEdges returns an iterator over line edges only.
func (*EdgeBuilder) QuadraticEdgeCount ¶ added in v0.25.1
func (eb *EdgeBuilder) QuadraticEdgeCount() int
QuadraticEdgeCount returns the number of quadratic edges.
func (*EdgeBuilder) QuadraticEdges ¶ added in v0.25.1
func (eb *EdgeBuilder) QuadraticEdges() iter.Seq[*QuadraticEdge]
QuadraticEdges returns an iterator over quadratic edges only.
func (*EdgeBuilder) Reset ¶ added in v0.25.1
func (eb *EdgeBuilder) Reset()
Reset clears the builder for reuse without deallocating memory.
func (*EdgeBuilder) SetFlattenCurves ¶ added in v0.25.1
func (eb *EdgeBuilder) SetFlattenCurves(flatten bool)
SetFlattenCurves enables or disables curve flattening mode. When enabled, all curves are converted to line segments at build time. This is simpler and more reliable for AnalyticFiller.
func (*EdgeBuilder) VelloLines ¶ added in v0.25.1
func (eb *EdgeBuilder) VelloLines() []VelloLine
VelloLines returns the stored float-coordinate lines. Only populated when flattenCurves is true.
type EdgeList ¶ added in v0.25.1
type EdgeList struct {
// contains filtered or unexported fields
}
EdgeList is a collection of edges with utility methods.
func NewEdgeList ¶ added in v0.25.1
func NewEdgeList() *EdgeList
NewEdgeList creates a new empty edge list.
func (*EdgeList) Reset ¶ added in v0.25.1
func (el *EdgeList) Reset()
Reset clears the edge list for reuse.
func (*EdgeList) SortByYMin ¶ added in v0.25.1
func (el *EdgeList) SortByYMin()
SortByYMin sorts edges by their minimum Y coordinate.
type EdgeRange ¶ added in v0.25.1
type EdgeRange struct {
StartX int32 // Left X of the span
EndX int32 // Right X of the span
Winding int32 // Accumulated winding number
Coverage float32 // Accumulated coverage
EdgeCount int // Number of edges in this range
}
EdgeRange represents a range of edges for a scanline span.
type EdgeType ¶ added in v0.25.1
type EdgeType int
EdgeType represents the type of an edge for polymorphic handling.
type FDot6 ¶ added in v0.21.2
type FDot6 = int32
FDot6 is a 26.6 fixed-point number (6 fractional bits). Used for intermediate calculations in edge setup where sub-pixel precision (1/64 pixel) is sufficient.
Range: approximately -33 million to +33 million with 1/64 precision.
func FDot6FromFloat32 ¶ added in v0.25.1
FDot6FromFloat32 converts a float32 to FDot6. Values are scaled by 64 and truncated toward zero.
func FDot6FromFloat64 ¶ added in v0.25.1
FDot6FromFloat64 converts a float64 to FDot6.
func FDot6FromInt ¶ added in v0.25.1
FDot6FromInt converts an integer to FDot6. The integer must fit in 26 bits (approximately -33M to +33M).
type FDot8 ¶ added in v0.25.1
type FDot8 = int32
FDot8 is a 24.8 fixed-point number (8 fractional bits). Used for anti-aliased coverage values.
func FDot8FromFDot16 ¶ added in v0.25.1
FDot8FromFDot16 converts an FDot16 to FDot8 with rounding.
type FDot16 ¶ added in v0.21.2
type FDot16 = int32
FDot16 is a 16.16 fixed-point number (16 fractional bits). Used for positions, slopes, and forward differencing coefficients where higher precision is needed.
Range: approximately -32768 to +32768 with 1/65536 precision.
func FDot6Div ¶ added in v0.25.1
FDot6Div divides two FDot6 values and returns an FDot16. This is used for computing slopes (dx / dy). If the numerator fits in 16 bits, uses fast path.
func FDot6ToFDot16 ¶ added in v0.21.2
FDot6ToFDot16 converts an FDot6 to FDot16. This is a left shift by 10 bits (16 - 6 = 10).
func FDot6ToFixedDiv2 ¶ added in v0.25.1
FDot6ToFixedDiv2 converts an FDot6 to FDot16 divided by 2. This is used in quadratic edge setup to avoid overflow. The result is (value / 2) in FDot16 representation.
func FDot16Div ¶ added in v0.25.1
FDot16Div divides two FDot6/FDot16 values and returns an FDot16. Uses 64-bit intermediate to avoid overflow.
func FDot16FastDiv ¶ added in v0.21.2
FDot16FastDiv divides two FDot6 values using fast 32-bit math. Only valid when the numerator fits in 16 bits.
func FDot16FromFloat32 ¶ added in v0.25.1
FDot16FromFloat32 converts a float32 to FDot16.
func FDot16FromFloat64 ¶ added in v0.25.1
FDot16FromFloat64 converts a float64 to FDot16.
type FillRule ¶
type FillRule int
FillRule determines how overlapping paths are filled.
const ( // FillRuleNonZero fills regions where the winding number is non-zero. // This is the default fill rule and handles self-intersecting paths well. FillRuleNonZero FillRule = iota // FillRuleEvenOdd fills regions where the winding number is odd. // This creates a checkerboard pattern for self-intersecting paths. FillRuleEvenOdd )
type GeomPoint ¶ added in v0.25.1
type GeomPoint struct {
X, Y float32
}
GeomPoint represents a 2D point for geometry calculations. Using a local type to avoid coupling with scene.Point and CurvePoint.
func ZeroGeomPoint ¶ added in v0.25.1
func ZeroGeomPoint() GeomPoint
ZeroGeomPoint returns the origin point.
type IdentityTransform ¶ added in v0.25.1
type IdentityTransform struct{}
IdentityTransform is a no-op transform that returns points unchanged.
func (IdentityTransform) TransformPoint ¶ added in v0.25.1
func (IdentityTransform) TransformPoint(x, y float32) (float32, float32)
TransformPoint returns the point unchanged.
type LineEdge ¶ added in v0.25.1
type LineEdge struct {
// Linked list pointers (indices into edge array).
// Using Option<u32> pattern from Rust as nullable int32.
Prev int32
Next int32
// X is the current X position in FDot16 (16.16 fixed-point).
X FDot16
// DX is the slope: change in X per scanline (in FDot16).
DX FDot16
// FirstY is the first scanline this edge covers.
FirstY int32
// LastY is the last scanline this edge covers (inclusive).
LastY int32
// Winding indicates direction: +1 for downward, -1 for upward.
Winding int8
}
LineEdge represents a single line segment in the Active Edge Table. This is the base type that QuadraticEdge and CubicEdge use internally. Derived from tiny-skia's LineEdge.
func NewLineEdge ¶ added in v0.25.1
func NewLineEdge(p0, p1 CurvePoint, shift int) *LineEdge
NewLineEdge creates a new line edge from two points. Returns nil if the edge is horizontal (no Y extent).
Parameters:
- p0, p1: endpoints in pixel coordinates
- shift: AA shift (0 for no AA, 2 for 4x AA quality)
func (*LineEdge) IsVertical ¶ added in v0.25.1
IsVertical returns true if the edge has zero slope.
type PathLike ¶ added in v0.25.1
type PathLike interface {
// IsEmpty returns true if the path has no commands.
IsEmpty() bool
// Verbs returns the verb stream.
Verbs() []PathVerb
// Points returns the point data stream (pairs of float32 x,y coordinates).
Points() []float32
}
PathLike is the interface for path-like objects that can be processed by EdgeBuilder. This allows core package to work with any path implementation without importing scene.
type PathVerb ¶ added in v0.25.1
type PathVerb uint8
PathVerb represents a path construction command. Mirrors scene.PathVerb for core package independence.
const ( // VerbMoveTo moves the current point without drawing. VerbMoveTo PathVerb = iota // VerbLineTo draws a line to the specified point. VerbLineTo // VerbQuadTo draws a quadratic Bezier curve. VerbQuadTo // VerbCubicTo draws a cubic Bezier curve. VerbCubicTo // VerbClose closes the current subpath. VerbClose )
Path verb constants.
type QuadraticEdge ¶ added in v0.25.1
type QuadraticEdge struct {
// TopY is the curve's overall top scanline (for AET insertion timing).
// This is set once at creation and never changes, unlike line.FirstY
// which changes as we step through curve segments.
TopY int32
// BottomY is the curve's overall bottom scanline.
BottomY int32
// contains filtered or unexported fields
}
QuadraticEdge represents a quadratic Bezier curve for scanline conversion. Uses forward differencing for O(1) per-step evaluation.
A quadratic Bezier is defined by:
p(t) = (1-t)^2 * p0 + 2*t*(1-t) * p1 + t^2 * p2
Rewritten in polynomial form:
p(t) = A*t^2 + B*t + C where A = p0 - 2*p1 + p2, B = 2*(p1 - p0), C = p0
func NewQuadraticEdge ¶ added in v0.25.1
func NewQuadraticEdge(p0, p1, p2 CurvePoint, shift int) *QuadraticEdge
NewQuadraticEdge creates a quadratic edge from control points. Returns nil if the curve has no vertical extent.
Parameters:
- p0: start point
- p1: control point
- p2: end point
- shift: AA shift (0 for no AA, 2 for 4x AA quality)
func (*QuadraticEdge) CurveCount ¶ added in v0.25.1
func (q *QuadraticEdge) CurveCount() int8
CurveCount returns the remaining number of segments.
func (*QuadraticEdge) Line ¶ added in v0.25.1
func (q *QuadraticEdge) Line() *LineEdge
Line returns the current line segment for AET processing.
func (*QuadraticEdge) Update ¶ added in v0.25.1
func (q *QuadraticEdge) Update() bool
Update advances the quadratic curve to the next line segment. Returns true if a valid segment was produced.
This is the core of the forward differencing algorithm:
newx = oldx + (dx >> shift) dx += ddx // Second derivative is constant!
func (*QuadraticEdge) Winding ¶ added in v0.25.1
func (q *QuadraticEdge) Winding() int8
Winding returns the winding direction.
type Rect ¶ added in v0.25.1
Rect represents a bounding rectangle.
type ScenePathAdapter ¶ added in v0.25.1
type ScenePathAdapter struct {
// contains filtered or unexported fields
}
ScenePathAdapter adapts a scene-like path to the core.PathLike interface. This allows EdgeBuilder to work with any path implementation without creating an import cycle.
Usage:
// In a package that can import scene:
type scenePathWrapper struct {
path *scene.Path
}
func (w *scenePathWrapper) IsEmpty() bool { return w.path.IsEmpty() }
func (w *scenePathWrapper) Verbs() []core.PathVerb {
// Convert scene.PathVerb to core.PathVerb
return convertVerbs(w.path.Verbs())
}
func (w *scenePathWrapper) Points() []float32 { return w.path.Points() }
Then create an EdgeBuilder and use:
eb.BuildFromPath(&scenePathWrapper{path}, core.IdentityTransform{})
func NewScenePathAdapter ¶ added in v0.25.1
func NewScenePathAdapter(isEmpty bool, verbs []PathVerb, points []float32) *ScenePathAdapter
NewScenePathAdapter creates a new adapter from verb and point data.
This is a low-level constructor. Higher-level packages should provide convenience functions that convert from their path types.
func (*ScenePathAdapter) IsEmpty ¶ added in v0.25.1
func (a *ScenePathAdapter) IsEmpty() bool
IsEmpty returns true if the path has no commands.
func (*ScenePathAdapter) Points ¶ added in v0.25.1
func (a *ScenePathAdapter) Points() []float32
Points returns the point data stream.
func (*ScenePathAdapter) Verbs ¶ added in v0.25.1
func (a *ScenePathAdapter) Verbs() []PathVerb
Verbs returns the verb stream.
type SimpleAET ¶ added in v0.25.1
type SimpleAET struct {
// contains filtered or unexported fields
}
SimpleAET manages active edges during scanline conversion. This is the simple linear edge table (not curve-aware).
func NewSimpleAET ¶ added in v0.25.1
func NewSimpleAET() *SimpleAET
NewSimpleAET creates a new simple active edge table.
func (*SimpleAET) Active ¶ added in v0.25.1
func (aet *SimpleAET) Active() []ActiveEdge
Active returns the list of active edges for iteration.
func (*SimpleAET) InsertEdge ¶ added in v0.25.1
InsertEdge adds an edge to the active list.
func (*SimpleAET) RemoveExpired ¶ added in v0.25.1
RemoveExpired removes edges that end at or before the given Y.
func (*SimpleAET) Reset ¶ added in v0.25.1
func (aet *SimpleAET) Reset()
Reset clears the active edge table.
type Transform ¶ added in v0.25.1
type Transform interface {
// TransformPoint transforms a point (x, y) and returns the result.
TransformPoint(x, y float32) (float32, float32)
}
Transform is the interface for affine transformations. This allows core package to work with any transform implementation.
type VelloLine ¶ added in v0.25.1
type VelloLine struct {
P0 [2]float32 // Start point (pixel coords, normalized: P0.y <= P1.y)
P1 [2]float32 // End point (pixel coords)
IsDown bool // true if original direction was downward (y0 < y1)
}
VelloLine stores a line segment with original float32 coordinates. Used by Vello tile rasterizer to avoid fixed-point quantization loss.