Skip to content

html/layout: defer gradient rasterization for lazy stop-position resolution #318

Description

@carlos7ags

Background

Split-off from #266. PR #312/#314 closed the percent-only calc half of parseGradientStops. A separate follow-up PR closes the bg-position half via lazy resolution at draw time. This issue tracks the remaining gradient piece, which requires an architectural change rather than a localized parser fix.

Problem

parseLinearGradient and parseRadialGradient in html/converter_helpers.go (lines 550, 572) call RenderLinearGradient / RenderRadialGradient at parse time. The renderers bake the gradient into a fixed 200x200 bitmap (layout/gradient.go:52-74).

Because rasterization happens before layout, the gradient line length is unknown, so stop positions can only be expressed as fractions of an arbitrary canvas. The percent-only calc walker added in #312 covers the common case. Plain lengths (100px, 1em) and mixed-unit calc (calc(50% + 10px)) still drop to the default position.

What needs to change

To resolve stop positions against the actual rendered gradient line, rasterization must move from parse-time to render-time so it can receive the painting box dimensions.

Open design questions:

  • Cache key: include resolved dimensions, or rasterize per unique container size?
  • Sharing: gradients used at multiple sizes need multiple bitmaps; impact on PDF object dedup
  • Stop-position type: GradientStop.Position float64 becomes *cssLength (matches the BackgroundImage.Position change from the bg-position PR)
  • Two-axis resolution: linear gradient stops resolve against the gradient line length, not the box width or height directly

Reproducer

background: linear-gradient(red, blue 100px, green);

Expected: blue stop 100pt from the start of the gradient line, green at end.
Actual: blue stop position drops to 0, gradient becomes blue-to-green from start.

Acceptance

  • Plain-length stop positions (100px, 1em, 2rem) resolve against the actual gradient line length
  • Mixed-unit calc stop positions (calc(50% + 10px)) resolve correctly
  • Existing percent-only stops keep working without regression
  • No visible quality loss on common gradient sizes (verify rasterization-at-actual-size doesn't introduce moire on small boxes)

Target: 0.9.0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions