[whatwg] Canvas gradients color interpolation - change to premultiplied?

Philip Taylor excors+whatwg at gmail.com
Tue Nov 23 14:09:40 PST 2010

On Tue, Nov 23, 2010 at 8:43 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:
> Right now, canvas gradients interpolate their colors in
> non-premultiplied space; that is, the raw values of r, g, b, and a are
> interpolated independently.  This has the unfortunate effect that
> colors darken as they transition to transparent, as "transparent" is
> defined as "rgba(0,0,0,0)", a transparent black.  Under this scheme,
> the color halfway between "yellow" and "transparent" is
> "rgba(127,127,0,.5)", a partially-transparent dark yellow, rather than
> "rgba(255,255,0,.5)".*

If you define the gradient as interpolating from solid yellow to
transparent black, I'd expect that it *should* be semi-transparent
blackish-yellow in the middle.

If you want it to be pure yellow, don't use a keyword which is
explicitly specified as transparent black - define the gradient from
rgba(255,255,0,1) to rgba(255,255,0,0) instead. Then you'll get
rgba(255,255,0,0.5) in the middle.

> The rest of the platform has switched to using premultiplied colors
> for interpolation, because they react better in cases like this**.
> CSS transitions and CSS gradients now explicitly use premultiplied
> colors, and SVG ends up interpolating similarly (they don't quite have
> the same problem - they track opacity separate from color, so
> transitioning from "color:yellow;opacity:1" to
> "color:yellow;opacity:0" gives you "color:yellow;opacity:.5" in the
> middle, which is the moral equivalent of "rgba(255,255,0,.5)").

That sounds like SVG gradients *can't* be using premultiplied colours.
A transition from "color:yellow;opacity:1" to "color:black;opacity:0"
will have rgba(127,127,0,0.5) in the middle, and it's impossible to
get that if you are using premultiplied colours. You'd have to have
A=1 at the start and A=0 at the end, so (with premultiplied colour)
the end would be interpreted as rgba(0,0,0,0), so you'd get the same
as interpolating to "color:yellow;opacity:0" (i.e. rgba(255,255,0,0.5)
in the middle), which is not what SVG does.

http://www.w3.org/TR/SVGTiny12/painting.html#Gradients says explicitly
its behaviour is the non-premultiplied behaviour we currently get with
canvas. ("gradient from fully transparent red, via partly transparent
dark yellow, to fully opaque lime" - the RGB components of fully
transparent colours are preserved.)

Maybe CSS should have originally used the keyword "transparentblack"
instead of "transparent" (though the distinction didn't matter before
gradients existed) - changing the gradient algorithm solely to work
more intuitively when people happen to use that one particular
incorrectly-named keyword seems backwards, and a mistake in CSS.

(Perhaps CSS gradients could avoid this problem by overriding the
meaning of the "transparent" keyword, so that instead of rgba(0,0,0,0)
it means A=0 with the mean RGB of the adjacent colour stops. That
would let it work as people naturally expect when they use that
keyword, and they can use the rgba() syntax if they really want
transparent black or transparent yellow or transparent red etc.)

Philip Taylor
excors at gmail.com

More information about the whatwg mailing list