package d2raster

import (
	
	
	
	
	

	
	
)

type preparedPaintKind uint8

const (
	preparedSolidPaint preparedPaintKind = iota
	preparedLinearGradient
	preparedLinearXGradient
	preparedLinearYGradient
	preparedLinearXOnlyGradient
	preparedLinearYOnlyGradient
	preparedRadialGradient
	preparedPatternPaint
)

// preparedPaint is immutable render-time paint state. Solid colors retain the
// scanline rasterizer's optimized uniform-color path. Gradients carry the
// inverse of their complete coordinate mapping. Patterns carry one prepared,
// bounded tile that is rasterized once per frame before repeated sampling.
type preparedPaint struct {
	kind     preparedPaintKind
	solid    color.NRGBA
	gradient preparedGradient
	pattern  *preparedPattern
}

type preparedGradient struct {
	deviceToGradient d2scene.Matrix
	stops            []d2scene.GradientStop
	spread           d2scene.SpreadMethod
	// COLRv1 gradients use the OpenType-mandated premultiplied linear-light
	// interpolation. SVG gradients retain their existing sRGB behavior.
	colrv1Interpolation bool

	linearStart       d2scene.Point
	linearDelta       d2scene.Point
	linearDenominator float64

	radialFocal       d2scene.Point
	radialDelta       d2scene.Point
	radialFocalRadius float64
	radialDeltaRadius float64
	radialA           float64
	radialTangent     bool
	radialConcentric  bool
	radialAverage     color.NRGBA
}

type patternPaintPreparer func(d2scene.PatternPaint, d2scene.Box, d2scene.Matrix) (*preparedPaint, error)

func prepareAnimatedPaintWithPattern( d2scene.Paint,  *color.NRGBA,  d2scene.Box,  d2scene.Matrix,  patternPaintPreparer) (*preparedPaint, error) {
	switch paint := .(type) {
	case nil:
		if  != nil {
			return nil, fmt.Errorf("color animation targets missing paint")
		}
		return nil, nil
	case d2scene.SolidPaint:
		 := .Color
		if  != nil {
			 = *
		}
		return &preparedPaint{kind: preparedSolidPaint, solid: }, nil
	case *d2scene.SolidPaint:
		if  == nil {
			if  != nil {
				return nil, fmt.Errorf("color animation targets missing paint")
			}
			return nil, nil
		}
		return (*, , , , )
	case d2scene.LinearGradient:
		if  != nil {
			return nil, fmt.Errorf("color animation targets non-solid paint")
		}
		return prepareLinearGradient(, , )
	case *d2scene.LinearGradient:
		if  == nil {
			return nil, fmt.Errorf("nil linear gradient")
		}
		return (*, , , , )
	case d2scene.RadialGradient:
		if  != nil {
			return nil, fmt.Errorf("color animation targets non-solid paint")
		}
		return prepareRadialGradient(, , )
	case *d2scene.RadialGradient:
		if  == nil {
			return nil, fmt.Errorf("nil radial gradient")
		}
		return (*, , , , )
	case d2scene.PatternPaint:
		if  != nil {
			return nil, fmt.Errorf("color animation targets pattern paint")
		}
		if  == nil {
			return nil, fmt.Errorf("unsupported pattern paint without render preflight")
		}
		return (, , )
	case *d2scene.PatternPaint:
		if  == nil {
			return nil, fmt.Errorf("nil pattern paint")
		}
		return (*, , , , )
	default:
		return nil, fmt.Errorf("unsupported paint %T", )
	}
}

func prepareLinearGradient( d2scene.LinearGradient,  d2scene.Box,  d2scene.Matrix) (*preparedPaint, error) {
	if !finitePoint(.Start) || !finitePoint(.End) {
		return nil, fmt.Errorf("linear gradient has non-finite geometry")
	}
	,  := prepareGradientTransform(.Units, .Transform, , )
	if  != nil {
		return nil, fmt.Errorf("linear gradient: %w", )
	}
	,  := normalizeGradientStops(.Stops)
	if  != nil {
		return nil, fmt.Errorf("linear gradient: %w", )
	}
	if .Spread > d2scene.SpreadRepeat {
		return nil, fmt.Errorf("linear gradient has invalid spread method %d", .Spread)
	}
	 := d2scene.Point{X: .End.X - .Start.X, Y: .End.Y - .Start.Y}
	 := .X*.X + .Y*.Y
	if !finite() {
		return nil, fmt.Errorf("linear gradient vector is outside the finite numeric domain")
	}
	// SVG 2 paints a zero-length linear gradient with its last stop color.
	if  == 0 || len() == 1 {
		return &preparedPaint{kind: preparedSolidPaint, solid: [len()-1].Color}, nil
	}
	 := preparedLinearGradient
	if .Y == 0 && affineCoordinateFiniteForRaster(.B, .D, .F, .Start.Y) {
		if .C == 0 {
			 = preparedLinearXOnlyGradient
		} else {
			 = preparedLinearXGradient
		}
	} else if .X == 0 && affineCoordinateFiniteForRaster(.A, .C, .E, .Start.X) {
		if .B == 0 {
			 = preparedLinearYOnlyGradient
		} else {
			 = preparedLinearYGradient
		}
	}
	return &preparedPaint{
		kind: ,
		gradient: preparedGradient{
			deviceToGradient:  ,
			stops:             ,
			spread:            .Spread,
			linearStart:       .Start,
			linearDelta:       ,
			linearDenominator: ,
		},
	}, nil
}

// Axis-gradient sampling can omit the coordinate perpendicular to its axis,
// but the general expression remains unpainted if that coordinate overflows:
// infinity multiplied by the zero gradient delta produces NaN. Only select an
// axis fast path when every coordinate representable by an image.Rectangle is
// conservatively guaranteed to keep that otherwise-unused subtraction finite.
func affineCoordinateFiniteForRaster(, , ,  float64) bool {
	 := float64(^uint(0) >> 1)
	// Keep the absolute bound far enough below MaxFloat64 that rounding in
	// the multiply-add sequence cannot turn an accepted bound into infinity.
	 := math.MaxFloat64 / 4
	for ,  := range [...]struct {
		 float64
		  float64
	}{
		{: , : },
		{: , : },
		{: , : 1},
		{: , : 1},
	} {
		 := math.Abs(.)
		if  > /. {
			return false
		}
		 -=  * .
	}
	return true
}

func prepareRadialGradient( d2scene.RadialGradient,  d2scene.Box,  d2scene.Matrix) (*preparedPaint, error) {
	if !finitePoint(.Center) || !finitePoint(.Focal) || !finite(.Radius) || !finite(.FocalRadius) {
		return nil, fmt.Errorf("radial gradient has non-finite geometry")
	}
	if .Radius < 0 || .FocalRadius < 0 {
		return nil, fmt.Errorf("radial gradient has negative radius")
	}
	,  := prepareGradientTransform(.Units, .Transform, , )
	if  != nil {
		return nil, fmt.Errorf("radial gradient: %w", )
	}
	,  := normalizeGradientStops(.Stops)
	if  != nil {
		return nil, fmt.Errorf("radial gradient: %w", )
	}
	if .Spread > d2scene.SpreadRepeat {
		return nil, fmt.Errorf("radial gradient has invalid spread method %d", .Spread)
	}
	if len() == 1 {
		return &preparedPaint{kind: preparedSolidPaint, solid: [0].Color}, nil
	}
	 := d2scene.Point{X: .Center.X - .Focal.X, Y: .Center.Y - .Focal.Y}
	 := .Radius - .FocalRadius
	 := .X*.X + .Y*.Y - *
	if !finite() || !finite() {
		return nil, fmt.Errorf("radial gradient cone is outside the finite numeric domain")
	}
	// SVG 2 defines fully overlapping start and end circles as unpainted.
	if .X == 0 && .Y == 0 &&  == 0 {
		return &preparedPaint{kind: preparedSolidPaint, solid: color.NRGBA{}}, nil
	}
	 := .X*.X + .Y*.Y + * + 1
	return &preparedPaint{
		kind: preparedRadialGradient,
		gradient: preparedGradient{
			deviceToGradient:  ,
			stops:             ,
			spread:            .Spread,
			radialFocal:       .Focal,
			radialDelta:       ,
			radialFocalRadius: .FocalRadius,
			radialDeltaRadius: ,
			radialA:           ,
			radialTangent:     math.Abs() <= 1e-14*,
			radialConcentric:  .X == 0 && .Y == 0 && .FocalRadius == 0,
			radialAverage:     averageGradientColor(),
		},
	}, nil
}

func prepareGradientTransform( d2scene.PaintUnits,  d2scene.Matrix,  d2scene.Box,  d2scene.Matrix) (d2scene.Matrix, error) {
	if  > d2scene.UserSpaceOnUse {
		return d2scene.Matrix{}, fmt.Errorf("invalid paint units %d", )
	}
	if !.IsFinite() {
		return d2scene.Matrix{}, fmt.Errorf("non-finite gradient transform")
	}
	if !.IsFinite() {
		return d2scene.Matrix{}, fmt.Errorf("non-finite object transform")
	}
	 := d2scene.Identity()
	if  == d2scene.ObjectBoundingBox {
		if  := validateBox();  != nil {
			return d2scene.Matrix{}, fmt.Errorf("invalid object bounding box: %w", )
		}
		if .Width == 0 || .Height == 0 {
			// SVG ignores objectBoundingBox paint on zero-area geometry. The
			// scene contract rejects it instead so malformed scenes cannot
			// silently lose paint.
			return d2scene.Matrix{}, fmt.Errorf("object bounding box has zero width or height")
		}
		 = d2scene.Translate(.X, .Y).Mul(d2scene.Scale(.Width, .Height))
	}
	// gradientTransform is post-multiplied to the units transform, matching
	// SVG: object transform * bbox transform * gradient transform.
	 := .Mul().Mul()
	if !.IsFinite() {
		return d2scene.Matrix{}, fmt.Errorf("composed gradient transform is non-finite")
	}
	, ,  := finiteAffineInverse()
	if  != nil || ! {
		return d2scene.Matrix{}, fmt.Errorf("singular gradient transform")
	}
	return , nil
}

// normalizeGradientStops implements SVG's used-value rules in source order:
// offsets are clamped to [0,1], then any decreasing offset is raised to the
// previous used offset. Equal offsets are retained because the later stop owns
// the exact transition point.
func normalizeGradientStops( []d2scene.GradientStop) ([]d2scene.GradientStop, error) {
	if len() == 0 {
		return nil, fmt.Errorf("gradient has no stops")
	}
	 := true
	 := 0.0
	for ,  := range  {
		if !finite(.Offset) {
			return nil, fmt.Errorf("gradient stop %d has non-finite offset", )
		}
		 := math.Max(0, math.Min(1, .Offset))
		if  != 0 &&  <  {
			 = 
		}
		 =  &&  == .Offset
		 = 
	}
	if  {
		// Scene assets are immutable for the lifetime of a render, so a slice
		// that already contains the SVG used values needs no defensive copy.
		return , nil
	}

	 := make([]d2scene.GradientStop, len())
	 = 0
	for ,  := range  {
		.Offset = math.Max(0, math.Min(1, .Offset))
		if  != 0 && .Offset <  {
			.Offset = 
		}
		[] = 
		 = .Offset
	}
	return , nil
}

func ( *preparedPaint) (,  float64) (color.NRGBA, bool) {
	if  == nil {
		return color.NRGBA{}, false
	}
	if .kind == preparedSolidPaint {
		return .solid, .solid.A != 0
	}
	if .kind == preparedPatternPaint {
		return .pattern.colorAt(, )
	}
	 := &.gradient
	var  float64
	var  bool
	switch .kind {
	case preparedLinearGradient:
		 := .deviceToGradient.Point(d2scene.Point{X: , Y: })
		 = ((.X-.linearStart.X)*.linearDelta.X + (.Y-.linearStart.Y)*.linearDelta.Y) / .linearDenominator
		 = finite()
	case preparedLinearXGradient:
		 := .deviceToGradient.A* + .deviceToGradient.C* + .deviceToGradient.E
		 = ( - .linearStart.X) * .linearDelta.X / .linearDenominator
		 = finite()
	case preparedLinearYGradient:
		 := .deviceToGradient.B* + .deviceToGradient.D* + .deviceToGradient.F
		 = ( - .linearStart.Y) * .linearDelta.Y / .linearDenominator
		 = finite()
	case preparedLinearXOnlyGradient:
		 := .deviceToGradient.A* + .deviceToGradient.E
		 = ( - .linearStart.X) * .linearDelta.X / .linearDenominator
		 = finite()
	case preparedLinearYOnlyGradient:
		 := .deviceToGradient.D* + .deviceToGradient.F
		 = ( - .linearStart.Y) * .linearDelta.Y / .linearDenominator
		 = finite()
	case preparedRadialGradient:
		 := .deviceToGradient.Point(d2scene.Point{X: , Y: })
		,  = .radialParameter()
		if ! && .spread == d2scene.SpreadRepeat && .radialIsTangent() {
			return .radialAverage, .radialAverage.A != 0
		}
	default:
		return color.NRGBA{}, false
	}
	if ! {
		return color.NRGBA{}, false
	}
	 = spreadParameter(, .spread)
	if .colrv1Interpolation {
		return interpolateCOLRv1GradientStops(.stops, ), true
	}
	return interpolateGradientStops(.stops, ), true
}

// radialParameter follows the SVG 2 / Canvas two-circle cone model. It solves
// |P - (F+t(C-F))| = fr+t(r-fr), choosing the first solution whose
// interpolated radius is non-negative, as Pixman's radial-gradient algorithm
// does. Points outside the cone return false (transparent black).
func ( *preparedGradient) ( d2scene.Point) (float64, bool) {
	 := .X - .radialFocal.X
	 := .Y - .radialFocal.Y
	 := * + * - .radialFocalRadius*.radialFocalRadius
	 := .radialA
	if .radialConcentric {
		 := math.Sqrt(0 - *)
		for ,  := range [...]float64{ / , - / } {
			if .radialSolutionValid() {
				return , true
			}
		}
		return 0, false
	}
	 := *.radialDelta.X + *.radialDelta.Y + .radialFocalRadius*.radialDeltaRadius
	if .radialTangent {
		 := math.Abs(*.radialDelta.X) + math.Abs(*.radialDelta.Y) + math.Abs(.radialFocalRadius*.radialDeltaRadius) + 1
		if math.Abs() <= 1e-14* {
			return 0, false
		}
		 := 0.5 *  / 
		if .radialSolutionValid() {
			return , true
		}
		return 0, false
	}
	 := * - *
	if  < 0 {
		 := math.Abs(*) + math.Abs(*) + 1
		if  >= -1e-14* {
			 = 0
		} else {
			return 0, false
		}
	}
	 := math.Sqrt()
	for ,  := range [...]float64{( + ) / , ( - ) / } {
		if .radialSolutionValid() {
			return , true
		}
	}
	return 0, false
}

func ( *preparedGradient) ( float64) bool {
	if !finite() {
		return false
	}
	 := .radialFocalRadius + *.radialDeltaRadius
	return finite() &&  >= -1e-12
}

func ( *preparedGradient) () bool {
	return .radialTangent
}

func spreadParameter( float64,  d2scene.SpreadMethod) float64 {
	switch  {
	case d2scene.SpreadPad:
		return math.Max(0, math.Min(1, ))
	case d2scene.SpreadReflect:
		// math.Mod is comparatively expensive. Values in the nearest period
		// are already their own remainder, so preserve the exact arithmetic
		// that follows Mod without calling it.
		if  > -2 &&  < 2 {
			if  < 0 {
				 += 2
			}
			if  > 1 {
				 = 2 - 
			}
			return 
		}
		 = math.Mod(, 2)
		if  < 0 {
			 += 2
		}
		if  > 1 {
			 = 2 - 
		}
		return 
	case d2scene.SpreadRepeat:
		if  == 0 {
			return 0
		}
		if  > 0 &&  < 1 {
			return 
		}
		return  - math.Floor()
	default:
		return 
	}
}

func interpolateGradientStops( []d2scene.GradientStop,  float64) color.NRGBA {
	if len() == 1 ||  < [0].Offset {
		return [0].Color
	}
	if  >= [len()-1].Offset {
		return [len()-1].Color
	}
	var ,  d2scene.GradientStop
	switch len() {
	case 2:
		,  = [0], [1]
	case 3:
		if [1].Offset <=  {
			,  = [1], [2]
		} else {
			,  = [0], [1]
		}
	default:
		// Upper-bound search makes the last stop at a repeated offset own
		// the exact transition point, while the first repeated stop owns the
		// approach from the left.
		,  := 0, len()
		for  <  {
			 :=  + (-)/2
			if [].Offset <=  {
				 =  + 1
			} else {
				 = 
			}
		}
		,  = [-1], []
	}
	 := ( - .Offset) / (.Offset - .Offset)
	return lerpNRGBA(.Color, .Color, )
}

func lerpNRGBA(,  color.NRGBA,  float64) color.NRGBA {
	 := func(,  uint8) uint8 {
		return uint8(math.Round(float64() + (float64()-float64())*))
	}
	return color.NRGBA{
		R: (.R, .R),
		G: (.G, .G),
		B: (.B, .B),
		A: (.A, .A),
	}
}

// interpolateCOLRv1GradientStops implements the color interpolation required
// by OpenType CPAL/COLR: decode sRGB to linear light, premultiply each color by
// alpha, interpolate, unpremultiply, then encode back to sRGB.
func interpolateCOLRv1GradientStops( []d2scene.GradientStop,  float64) color.NRGBA {
	if len() == 1 ||  < [0].Offset {
		return [0].Color
	}
	if  >= [len()-1].Offset {
		return [len()-1].Color
	}
	var ,  d2scene.GradientStop
	switch len() {
	case 2:
		,  = [0], [1]
	case 3:
		if [1].Offset <=  {
			,  = [1], [2]
		} else {
			,  = [0], [1]
		}
	default:
		,  := 0, len()
		for  <  {
			 :=  + (-)/2
			if [].Offset <=  {
				 =  + 1
			} else {
				 = 
			}
		}
		,  = [-1], []
	}
	 := ( - .Offset) / (.Offset - .Offset)
	return lerpCOLRv1Color(.Color, .Color, )
}

func lerpCOLRv1Color(,  color.NRGBA,  float64) color.NRGBA {
	 := float64(.A) / 255
	 := float64(.A) / 255
	 :=  + (-)*
	 := color.NRGBA{A: roundedByte( * 255)}
	if  <= 0 {
		return 
	}
	 := func(,  uint8) uint8 {
		 := linearSRGBByte[] * 
		 := linearSRGBByte[] * 
		 :=  + (-)*
		return roundedByte(linearToSRGB(/) * 255)
	}
	.R = (.R, .R)
	.G = (.G, .G)
	.B = (.B, .B)
	return 
}

func averageCOLRv1GradientColor( []d2scene.GradientStop) color.NRGBA {
	if len() == 0 {
		return color.NRGBA{}
	}
	 := [4]float64{}
	 := func( color.NRGBA) [4]float64 {
		 := float64(.A) / 255
		return [4]float64{
			linearSRGBByte[.R] * ,
			linearSRGBByte[.G] * ,
			linearSRGBByte[.B] * ,
			,
		}
	}
	 := ([0].Color)
	for  := range  {
		[] += [] * [0].Offset
	}
	for  := 1;  < len(); ++ {
		 := [].Offset - [-1].Offset
		,  := ([-1].Color), ([].Color)
		for  := range  {
			[] +=  * ([] + []) / 2
		}
	}
	 := ([len()-1].Color)
	for  := range  {
		[] += [] * (1 - [len()-1].Offset)
	}
	 := color.NRGBA{A: roundedByte([3] * 255)}
	if [3] <= 0 {
		return 
	}
	.R = roundedByte(linearToSRGB([0]/[3]) * 255)
	.G = roundedByte(linearToSRGB([1]/[3]) * 255)
	.B = roundedByte(linearToSRGB([2]/[3]) * 255)
	return 
}

func averageGradientColor( []d2scene.GradientStop) color.NRGBA {
	if len() == 0 {
		return color.NRGBA{}
	}
	var  [4]float64
	 := func( color.NRGBA) [4]float64 {
		return [4]float64{float64(.R), float64(.G), float64(.B), float64(.A)}
	}
	 := ([0].Color)
	for  := range  {
		[] += [] * [0].Offset
	}
	for  := 1;  < len(); ++ {
		 := [].Offset - [-1].Offset
		,  := ([-1].Color), ([].Color)
		for  := range  {
			[] +=  * ([] + []) / 2
		}
	}
	 := ([len()-1].Color)
	for  := range  {
		[] += [] * (1 - [len()-1].Offset)
	}
	return color.NRGBA{R: uint8(math.Round([0])), G: uint8(math.Round([1])), B: uint8(math.Round([2])), A: uint8(math.Round([3]))}
}

// drawRasterizedPaint streams analytic non-zero coverage directly into paint
// compositing. Unlike even-odd fills and clip/mask operations, ordinary paint
// does not need to retain coverage after a row has been consumed.
func drawRasterizedPaint(
	 context.Context,
	 *image.RGBA,
	 image.Rectangle,
	 *preparedPaint,
	 *rasterScratch,
	 string,
	 func(*scanline.Rasterizer) error,
) error {
	if .Empty() {
		return nil
	}
	// Pattern tiles can themselves use the shared rasterizer, so render them
	// before populating the outer path.
	if  := .ensureRendered(, );  != nil {
		return 
	}
	 := .reset(image.Rect(0, 0, .Dx(), .Dy()))
	if  := ();  != nil {
		return 
	}
	if  := drawPaintCoverage(, , , , .workBudget(), );  != nil {
		return fmt.Errorf("d2raster: %s: %w", , )
	}
	return .Err()
}

func drawPaintCoverage( context.Context,  *image.RGBA,  image.Rectangle,  *scanline.Rasterizer,  *scanline.WorkBudget,  *preparedPaint) error {
	switch .kind {
	case preparedLinearGradient:
		return drawLinearGradientCoverage(, , , , , &.gradient)
	case preparedLinearXGradient, preparedLinearYGradient, preparedLinearXOnlyGradient, preparedLinearYOnlyGradient:
		return drawAxisLinearGradientCoverage(, , , , , &.gradient, .kind)
	case preparedRadialGradient:
		return drawRadialGradientCoverage(, , , , , &.gradient)
	case preparedPatternPaint:
		if .pattern != nil && .pattern.directPixels {
			return drawDirectPatternCoverage(, , , , , .pattern)
		}
	}
	return drawGeneralPaintCoverage(, , , , , )
}

func drawGeneralPaintCoverage( context.Context,  *image.RGBA,  image.Rectangle,  *scanline.Rasterizer,  *scanline.WorkBudget,  *preparedPaint) error {
	return .WalkCoverage(, , func(,  int, ,  []float32) error {
		if  := .Err();  != nil {
			return 
		}
		 := .Min.Y + 
		 := .PixOffset(.Min.X+, )
		var  float32
		for  := range  {
			if  != 0 && &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 += []
			 := scanline.QuantizeCoverage([] + )
			if  == 0 {
				continue
			}
			 := .Min.X +  + 
			,  := .colorAt(float64()+0.5, float64()+0.5)
			if ! || .A == 0 {
				continue
			}
			 :=  + *4
			compositeNRGBAOverRGBA(.Pix[:+4], , )
		}
		return nil
	})
}

func drawDirectPatternCoverage( context.Context,  *image.RGBA,  image.Rectangle,  *scanline.Rasterizer,  *scanline.WorkBudget,  *preparedPattern) error {
	 := .tileResource
	if  == nil || .image == nil {
		return drawGeneralPaintCoverage(, , , , , &preparedPaint{kind: preparedPatternPaint, pattern: })
	}
	 := func(, ,  int) int {
		 %= 
		if  < 0 {
			 += 
		}
		if  >= - {
			return  - ( - )
		}
		return  + 
	}
	return .WalkCoverage(, , func(,  int, ,  []float32) error {
		if  := .Err();  != nil {
			return 
		}
		 := .Min.Y + 
		 := .PixOffset(.Min.X+, )
		 := (.Min.X+, .pixelOffsetX, .width)
		 := (, .pixelOffsetY, .height)
		var  float32
		for  := range  {
			if  != 0 && &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 += []
			 := scanline.QuantizeCoverage([] + )
			if  != 0 {
				 := .image.PixOffset(, )
				 := .image.Pix[ : +4]
				 := [3]
				if  != 0 {
					 :=  + *4
					 := .Pix[ : +4]
					if  == 0xff &&  == 0xff {
						[0], [1], [2], [3] = [0], [1], [2], 0xff
					} else {
						var  color.NRGBA
						if  == 0xff {
							 = color.NRGBA{R: [0], G: [1], B: [2], A: }
						} else {
							 = color.NRGBA{
								R: uint8((uint32([0]) * 0xffff / uint32()) >> 8),
								G: uint8((uint32([1]) * 0xffff / uint32()) >> 8),
								B: uint8((uint32([2]) * 0xffff / uint32()) >> 8),
								A: ,
							}
						}
						compositeNRGBAOverRGBA(, , )
					}
				}
			}
			++
			if  == .width {
				 = 0
			}
		}
		return nil
	})
}

func drawAxisLinearGradientCoverage( context.Context,  *image.RGBA,  image.Rectangle,  *scanline.Rasterizer,  *scanline.WorkBudget,  *preparedGradient,  preparedPaintKind) error {
	if  == preparedLinearYOnlyGradient {
		return drawLinearYOnlyGradientCoverage(, , , , , )
	}
	var , , , ,  float64
	switch  {
	case preparedLinearXGradient:
		, ,  = .deviceToGradient.A, .deviceToGradient.C, .deviceToGradient.E
		,  = .linearStart.X, .linearDelta.X
	case preparedLinearYGradient:
		, ,  = .deviceToGradient.B, .deviceToGradient.D, .deviceToGradient.F
		,  = .linearStart.Y, .linearDelta.Y
	case preparedLinearXOnlyGradient:
		,  = .deviceToGradient.A, .deviceToGradient.E
		,  = .linearStart.X, .linearDelta.X
	default:
		return drawGeneralPaintCoverage(, , , , , &preparedPaint{kind: , gradient: *})
	}
	return .WalkCoverage(, , func(,  int, ,  []float32) error {
		if  := .Err();  != nil {
			return 
		}
		 := .Min.Y + 
		 := .PixOffset(.Min.X+, )
		 := float64() + 0.5
		var  float32
		for  := range  {
			if  != 0 && &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 += []
			 := scanline.QuantizeCoverage([] + )
			if  == 0 {
				continue
			}
			 := .Min.X +  + 
			 := float64() + 0.5
			 := * + * + 
			 := ( - ) *  / .linearDenominator
			if !finite() {
				continue
			}
			 = spreadParameter(, .spread)
			var  color.NRGBA
			if .colrv1Interpolation {
				 = interpolateCOLRv1GradientStops(.stops, )
			} else {
				 = interpolateGradientStops(.stops, )
			}
			if .A == 0 {
				continue
			}
			 :=  + *4
			compositeNRGBAOverRGBA(.Pix[:+4], , )
		}
		return nil
	})
}

func drawLinearGradientCoverage( context.Context,  *image.RGBA,  image.Rectangle,  *scanline.Rasterizer,  *scanline.WorkBudget,  *preparedGradient) error {
	return .WalkCoverage(, , func(,  int, ,  []float32) error {
		if  := .Err();  != nil {
			return 
		}
		 := .Min.Y + 
		 := .PixOffset(.Min.X+, )
		 := float64() + 0.5
		var  float32
		for  := range  {
			if  != 0 && &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 += []
			 := scanline.QuantizeCoverage([] + )
			if  == 0 {
				continue
			}
			 := .Min.X +  + 
			 := float64() + 0.5
			 := .deviceToGradient.Point(d2scene.Point{X: , Y: })
			 := ((.X-.linearStart.X)*.linearDelta.X + (.Y-.linearStart.Y)*.linearDelta.Y) / .linearDenominator
			if !finite() {
				continue
			}
			 = spreadParameter(, .spread)
			var  color.NRGBA
			if .colrv1Interpolation {
				 = interpolateCOLRv1GradientStops(.stops, )
			} else {
				 = interpolateGradientStops(.stops, )
			}
			if .A == 0 {
				continue
			}
			 :=  + *4
			compositeNRGBAOverRGBA(.Pix[:+4], , )
		}
		return nil
	})
}

func drawLinearYOnlyGradientCoverage( context.Context,  *image.RGBA,  image.Rectangle,  *scanline.Rasterizer,  *scanline.WorkBudget,  *preparedGradient) error {
	return .WalkCoverage(, , func(,  int, ,  []float32) error {
		if  := .Err();  != nil {
			return 
		}
		 := .Min.Y + 
		 := .PixOffset(.Min.X+, )
		 := float64() + 0.5
		 := .deviceToGradient.D* + .deviceToGradient.F
		 := ( - .linearStart.Y) * .linearDelta.Y / .linearDenominator
		 := finite()
		var  color.NRGBA
		if  {
			 = spreadParameter(, .spread)
			if .colrv1Interpolation {
				 = interpolateCOLRv1GradientStops(.stops, )
			} else {
				 = interpolateGradientStops(.stops, )
			}
			 = .A != 0
		}
		var  float32
		for  := range  {
			if  != 0 && &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 += []
			 := scanline.QuantizeCoverage([] + )
			if  == 0 || ! {
				continue
			}
			 :=  + *4
			compositeNRGBAOverRGBA(.Pix[:+4], , )
		}
		return nil
	})
}

func drawRadialGradientCoverage( context.Context,  *image.RGBA,  image.Rectangle,  *scanline.Rasterizer,  *scanline.WorkBudget,  *preparedGradient) error {
	return .WalkCoverage(, , func(,  int, ,  []float32) error {
		if  := .Err();  != nil {
			return 
		}
		 := .Min.Y + 
		 := .PixOffset(.Min.X+, )
		 := float64() + 0.5
		var  float32
		for  := range  {
			if  != 0 && &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 += []
			 := scanline.QuantizeCoverage([] + )
			if  == 0 {
				continue
			}
			 := .Min.X +  + 
			 := float64() + 0.5
			 := d2scene.Point{
				X: .deviceToGradient.A* + .deviceToGradient.C* + .deviceToGradient.E,
				Y: .deviceToGradient.B* + .deviceToGradient.D* + .deviceToGradient.F,
			}
			,  := .radialParameter()
			var  color.NRGBA
			 := false
			if ! && .spread == d2scene.SpreadRepeat && .radialIsTangent() {
				 = .radialAverage
				,  = .A != 0, true
			}
			if ! {
				continue
			}
			if ! {
				 = spreadParameter(, .spread)
				if .colrv1Interpolation {
					 = interpolateCOLRv1GradientStops(.stops, )
				} else {
					 = interpolateGradientStops(.stops, )
				}
			}
			if .A == 0 {
				continue
			}
			 :=  + *4
			compositeNRGBAOverRGBA(.Pix[:+4], , )
		}
		return nil
	})
}

func drawPaintMask( context.Context,  *image.RGBA,  image.Rectangle,  *preparedPaint,  *rasterScratch,  string,  func(*image.Alpha) error) error {
	if .Empty() {
		return nil
	}
	if  := .ensureRendered(, );  != nil {
		return 
	}
	 := image.Rect(0, 0, .Dx(), .Dy())
	, ,  := .offscreen.newAlpha(, )
	if  != nil {
		return 
	}
	defer .offscreen.recycleAlpha(, )
	if  := ();  != nil {
		return 
	}
	// Paint kind is invariant across the mask. Specialized gradient loops keep
	// its dispatch out of the pixel loop; axis-aligned vertical gradients sample
	// only once per row.
	switch .kind {
	case preparedLinearGradient:
		return drawLinearGradientMaskPixels(, , , , &.gradient)
	case preparedLinearXGradient, preparedLinearYGradient, preparedLinearXOnlyGradient, preparedLinearYOnlyGradient:
		return drawAxisLinearGradientMaskPixels(, , , , &.gradient, .kind)
	case preparedRadialGradient:
		return drawRadialGradientMaskPixels(, , , , &.gradient)
	case preparedPatternPaint:
		if .pattern != nil && .pattern.directPixels {
			return drawDirectPatternMaskPixels(, , , , .pattern)
		}
	}
	return drawPaintMaskPixels(, , , , )
}

func drawDirectPatternMaskPixels( context.Context,  *image.RGBA,  image.Rectangle,  *image.Alpha,  *preparedPattern) error {
	 := .tileResource
	if  == nil || .image == nil {
		return drawPaintMaskPixels(, , , , &preparedPaint{kind: preparedPatternPaint, pattern: })
	}
	 := func(, ,  int) int {
		 %= 
		if  < 0 {
			 += 
		}
		// Both operands are in [0, period), so combine them without risking
		// overflow when period is close to the platform integer limit.
		if  >= - {
			return  - ( - )
		}
		return  + 
	}
	 := (.Min.Y, .pixelOffsetY, .height)
	for  := 0;  < .Bounds().Dy(); ++ {
		if &31 == 0 {
			if  := .Err();  != nil {
				return 
			}
		}
		 := .Min.Y + 
		 :=  * .Stride
		 := .PixOffset(.Min.X, )
		 := (.Min.X, .pixelOffsetX, .width)
		for  := 0;  < .Bounds().Dx(); ++ {
			if &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 := .Pix[+]
			if  != 0 {
				 := .image.PixOffset(, )
				 := .image.Pix[ : +4]
				 := .Pix[+*4 : +*4+4]
				 := [3]
				if  == 0xff &&  == 0xff {
					[0], [1], [2], [3] = [0], [1], [2], 0xff
				} else if  != 0 {
					var  color.NRGBA
					if  == 0xff {
						 = color.NRGBA{R: [0], G: [1], B: [2], A: }
					} else {
						 = color.NRGBA{
							R: uint8((uint32([0]) * 0xffff / uint32()) >> 8),
							G: uint8((uint32([1]) * 0xffff / uint32()) >> 8),
							B: uint8((uint32([2]) * 0xffff / uint32()) >> 8),
							A: ,
						}
					}
					compositeNRGBAOverRGBA(, , )
				}
			}
			++
			if  == .width {
				 = 0
			}
		}
		++
		if  == .height {
			 = 0
		}
	}
	return .Err()
}

func drawPaintMaskPixels( context.Context,  *image.RGBA,  image.Rectangle,  *image.Alpha,  *preparedPaint) error {
	for  := 0;  < .Bounds().Dy(); ++ {
		if &31 == 0 {
			if  := .Err();  != nil {
				return 
			}
		}
		 := .Min.Y + 
		 :=  * .Stride
		 := .PixOffset(.Min.X, )
		for  := 0;  < .Bounds().Dx(); ++ {
			if &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 := .Pix[+]
			if  == 0 {
				continue
			}
			 := .Min.X + 
			,  := .colorAt(float64()+0.5, float64()+0.5)
			if ! || .A == 0 {
				continue
			}
			compositeNRGBAOverRGBA(.Pix[+*4:+*4+4], , )
		}
	}
	return .Err()
}

func drawAxisLinearGradientMaskPixels( context.Context,  *image.RGBA,  image.Rectangle,  *image.Alpha,  *preparedGradient,  preparedPaintKind) error {
	if  == preparedLinearYOnlyGradient {
		return drawLinearYOnlyGradientMaskPixels(, , , , )
	}
	var , , , ,  float64
	switch  {
	case preparedLinearXGradient:
		, ,  = .deviceToGradient.A, .deviceToGradient.C, .deviceToGradient.E
		,  = .linearStart.X, .linearDelta.X
	case preparedLinearYGradient:
		, ,  = .deviceToGradient.B, .deviceToGradient.D, .deviceToGradient.F
		,  = .linearStart.Y, .linearDelta.Y
	case preparedLinearXOnlyGradient:
		,  = .deviceToGradient.A, .deviceToGradient.E
		,  = .linearStart.X, .linearDelta.X
	default:
		return drawPaintMaskPixels(, , , , &preparedPaint{kind: , gradient: *})
	}
	for  := 0;  < .Bounds().Dy(); ++ {
		if &31 == 0 {
			if  := .Err();  != nil {
				return 
			}
		}
		 := .Min.Y + 
		 :=  * .Stride
		 := .PixOffset(.Min.X, )
		 := float64() + 0.5
		for  := 0;  < .Bounds().Dx(); ++ {
			if &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 := .Pix[+]
			if  == 0 {
				continue
			}
			 := .Min.X + 
			 := float64() + 0.5
			 := * + * + 
			 := ( - ) *  / .linearDenominator
			if !finite() {
				continue
			}
			 = spreadParameter(, .spread)
			var  color.NRGBA
			if .colrv1Interpolation {
				 = interpolateCOLRv1GradientStops(.stops, )
			} else {
				 = interpolateGradientStops(.stops, )
			}
			if .A == 0 {
				continue
			}
			compositeNRGBAOverRGBA(.Pix[+*4:+*4+4], , )
		}
	}
	return .Err()
}

func drawLinearGradientMaskPixels( context.Context,  *image.RGBA,  image.Rectangle,  *image.Alpha,  *preparedGradient) error {
	for  := 0;  < .Bounds().Dy(); ++ {
		if &31 == 0 {
			if  := .Err();  != nil {
				return 
			}
		}
		 := .Min.Y + 
		 :=  * .Stride
		 := .PixOffset(.Min.X, )
		 := float64() + 0.5
		for  := 0;  < .Bounds().Dx(); ++ {
			if &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 := .Pix[+]
			if  == 0 {
				continue
			}
			 := .Min.X + 
			 := float64() + 0.5
			 := .deviceToGradient.Point(d2scene.Point{X: , Y: })
			 := ((.X-.linearStart.X)*.linearDelta.X + (.Y-.linearStart.Y)*.linearDelta.Y) / .linearDenominator
			if !finite() {
				continue
			}
			 = spreadParameter(, .spread)
			var  color.NRGBA
			if .colrv1Interpolation {
				 = interpolateCOLRv1GradientStops(.stops, )
			} else {
				 = interpolateGradientStops(.stops, )
			}
			if .A == 0 {
				continue
			}
			compositeNRGBAOverRGBA(.Pix[+*4:+*4+4], , )
		}
	}
	return .Err()
}

func drawLinearYOnlyGradientMaskPixels( context.Context,  *image.RGBA,  image.Rectangle,  *image.Alpha,  *preparedGradient) error {
	for  := 0;  < .Bounds().Dy(); ++ {
		if &31 == 0 {
			if  := .Err();  != nil {
				return 
			}
		}
		 := .Min.Y + 
		 :=  * .Stride
		 := .PixOffset(.Min.X, )
		 := float64() + 0.5
		 := .deviceToGradient.D* + .deviceToGradient.F
		 := ( - .linearStart.Y) * .linearDelta.Y / .linearDenominator
		 := finite()
		var  color.NRGBA
		if  {
			 = spreadParameter(, .spread)
			if .colrv1Interpolation {
				 = interpolateCOLRv1GradientStops(.stops, )
			} else {
				 = interpolateGradientStops(.stops, )
			}
			 = .A != 0
		}
		for  := 0;  < .Bounds().Dx(); ++ {
			if &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 := .Pix[+]
			if  == 0 || ! {
				continue
			}
			compositeNRGBAOverRGBA(.Pix[+*4:+*4+4], , )
		}
	}
	return .Err()
}

func drawRadialGradientMaskPixels( context.Context,  *image.RGBA,  image.Rectangle,  *image.Alpha,  *preparedGradient) error {
	for  := 0;  < .Bounds().Dy(); ++ {
		if &31 == 0 {
			if  := .Err();  != nil {
				return 
			}
		}
		 := .Min.Y + 
		 :=  * .Stride
		 := .PixOffset(.Min.X, )
		 := float64() + 0.5
		for  := 0;  < .Bounds().Dx(); ++ {
			if &4095 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			 := .Pix[+]
			if  == 0 {
				continue
			}
			 := .Min.X + 
			 := float64() + 0.5
			 := d2scene.Point{
				X: .deviceToGradient.A* + .deviceToGradient.C* + .deviceToGradient.E,
				Y: .deviceToGradient.B* + .deviceToGradient.D* + .deviceToGradient.F,
			}
			,  := .radialParameter()
			var  color.NRGBA
			 := false
			if ! && .spread == d2scene.SpreadRepeat && .radialIsTangent() {
				 = .radialAverage
				,  = .A != 0, true
			}
			if ! {
				continue
			}
			if ! {
				 = spreadParameter(, .spread)
				if .colrv1Interpolation {
					 = interpolateCOLRv1GradientStops(.stops, )
				} else {
					 = interpolateGradientStops(.stops, )
				}
			}
			if .A == 0 {
				continue
			}
			compositeNRGBAOverRGBA(.Pix[+*4:+*4+4], , )
		}
	}
	return .Err()
}

func compositeNRGBAOverRGBA( []byte,  color.NRGBA,  uint8) {
	if .A == 0xff &&  == 0xff {
		[0], [1], [2], [3] = .R, .G, .B, 0xff
		return
	}
	 := func(,  uint32) uint32 { return (* + 127) / 255 }
	 := (uint32(.A), uint32())
	if [0]|[1]|[2]|[3] == 0 {
		[0] = uint8((uint32(.R), ))
		[1] = uint8((uint32(.G), ))
		[2] = uint8((uint32(.B), ))
		[3] = uint8()
		return
	}
	 := 255 - 
	for ,  := range [...]uint8{.R, .G, .B} {
		 := (uint32(), )
		 :=  + (uint32([]), )
		if  > 255 {
			 = 255
		}
		[] = uint8()
	}
	 :=  + (uint32([3]), )
	if  > 255 {
		 = 255
	}
	[3] = uint8()
}

func subpathPixelBounds( []subpath,  d2scene.Matrix,  float64,  image.Rectangle) image.Rectangle {
	,  := math.Inf(1), math.Inf(1)
	,  := math.Inf(-1), math.Inf(-1)
	for ,  := range  {
		for ,  := range .points {
			 := .Point()
			 = math.Min(, .X)
			 = math.Min(, .Y)
			 = math.Max(, .X)
			 = math.Max(, .Y)
		}
	}
	if math.IsInf(, 1) {
		return image.Rectangle{}
	}
	// One extra pixel covers vector antialiasing and flattening tolerance.
	 += 1
	,  = -, -
	,  = +, +
	if  < float64(.Min.X) ||  < float64(.Min.Y) ||  > float64(.Max.X) ||  > float64(.Max.Y) {
		return image.Rectangle{}
	}
	 = math.Max(, float64(.Min.X))
	 = math.Max(, float64(.Min.Y))
	 = math.Min(, float64(.Max.X))
	 = math.Min(, float64(.Max.Y))
	return image.Rect(int(math.Floor()), int(math.Floor()), int(math.Ceil()), int(math.Ceil())).Intersect()
}

func localObjectBounds( []subpath) d2scene.Box {
	var  d2scene.Bounds
	for ,  := range  {
		for ,  := range .points {
			 = .Include()
		}
	}
	return .Box()
}