Wide multi-stop CSS gradient smearing

What this page demonstrates

CSS gradients with more than 128 color stops fall off Skia Ganesh's analytic uniform colorizer onto a 256-texel pre-rasterized bitmap fallback, which visibly smears the color bands at wide canvas widths.

A proposed patch adds a many-stop analytic colorizer in Ganesh that mirrors Graphite's storage-buffer gradient path using existing texture plumbing. The same code path is used by all CSS gradient types -- the sampler below exercises linear-gradient, radial-gradient and conic-gradient at 130 hard-stop segments.

1. Root cause

GrGradientShader::MakeColorizermake_looping_binary_colorizer caps at kMaxLoopingColorCount = 128. Anything above falls to make_textured_colorizer, which pre-rasterizes the gradient into a 1 x 256 RGBA bitmap and bilinearly samples it. At 3800-px gradient widths each canvas pixel maps to ~0.067 of a texel, so band boundaries get bilinearly smeared into 2-3-pixel-wide blends.

2. Linear -- 130 stops, 3801px width

linear-3801 -- background-size: 3801px, 130-stop hard-stop pattern

expected linear-3801
Rendered live in your browser. Expected from patched build.

3. Linear -- 130 stops, 3800px width

linear-3800 -- same pattern, background-size: 3800px

expected linear-3800
Rendered / Expected.

4. Linear -- codepen extra-stop variant

linear-3800-extra -- 64 segments + green 3800px-3801px hard stop

expected linear-3800-extra
Mirrors the third codepen stripe; the extra past-1.0 green stop pushes the input over the cap after CSS normalization.

5. Radial -- 130 stops

radial-130 -- circle at center

expected radial-130
Same colorizer code path -- crisp concentric bands post-fix; pre-fix shows wash from bilinear smear.

6. Conic -- 130 stops

conic-130 -- from 0deg at center

expected conic-130
Crisp pie slices post-fix; pre-fix shows smudged wedge edges.

7. Reproducing locally on Linux

Linux Chrome with hardware GPU rasterization hits the Ganesh path. On a box without a real GPU, force SwiftShader Vulkan so the same code path runs:

chromium \
  --user-data-dir=/tmp/i-443106929 \
  --ignore-gpu-blocklist \
  --enable-unsafe-swiftshader \
  --enable-features=Vulkan --use-vulkan=swiftshader \
  --enable-gpu-rasterization \
  --disable-features=SkiaGraphite \
  https://static.januschka.com/i-443106929/

macOS Chrome and software-rasterization Linux Chrome render this correctly because they do not use the Ganesh many-stop fallback (Graphite has a real SSBO-backed many-stop path; software raster pipeline has no cap).

References

Chromium issue 443106929 Skia CL 1237936 Original codepen repro