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.
GrGradientShader::MakeColorizer →
make_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.





Before System Chromium 146 (unpatched) vs
After Chromium dev build with Skia CL 1237936 (PS7)
applied to third_party/skia. Both captured on the same host with the
exact flags from section 9 (SwiftShader Vulkan, GPU rasterization, Ganesh).
The buffered colorizer engagement was verified via debug logging
(count=260 stops for the 130-segment stripes).
Unpatched: red→green band boundaries smear across ~13 px (256-texel bitmap, bilinear). Patched: 1-px transitions at the exact 29.24-px band positions. Blend-zone pixel share for radial/conic: 40.7% → 0.5%.






dm renders of the Skia GMs that gate this CL, produced with
the patched colorizer on Mesa llvmpipe (--config gl) next to
the raster ground truth (--config 8888). PS6 rendered the
gl images incorrectly: the buffered-colorizer effect cache
was keyed by loop count but baked the stop count into the SkSL, so
gradients reusing a cached effect clamped at the seeding gradient's stop
count. PS7 passes the stop count as a uniform.
EGL_PLATFORM=surfaceless LIBGL_ALWAYS_SOFTWARE=1 \
dm --config gl 8888 -m hardstop_gradients_many gradients_color_space_many_stops -w out








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).