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

Tab Atkins Jr. jackalmage at gmail.com
Tue Nov 23 14:53:13 PST 2010

On Tue, Nov 23, 2010 at 2:09 PM, Philip Taylor <excors+whatwg at gmail.com> wrote:
> 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 fact that "transparent" means "transparent black" is a technical
detail that is usually irrelevant.  A fully transparent color doesn't
really have a "color" at all.  I'm highly technical, involved in the
relevant specs, and understand the underlying math, and I *still* get
tripped up over the fact that "transparent" is black.

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

Luckily I didn't say that SVG used premultiplied colors.  ^_^  I said
they end up interpolating in a similar way, if you do the obvious
thing and just adjust the opacity.

When using CSS colors, the obvious thing is to use the "transparent"
keyword, which gives unsatisfactory behavior most of the time if you
transition in plain rgba space.

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

Possibly, but that can't be fixed now, and so as a result both
Transitions/Animations and Gradients use premultiplied colors, and
won't likely change (I know that Apple and Moz support using
premultiplied colors in transitions and gradients).  Making <canvas>
gradients match CSS seems like a better idea than the reverse at this
point, and seems more likely to have implementor support.

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

This is incompatible with both gradients and transitions (actually,
animations), because "transparent" can be simultaneously the endpoints
of two different color interpolations.

An animation or a gradient can go from a solid color to transparent to
a solid color again.  While it's theoretically possible in a
red->transparent->blue animation/gradient to have "transparent" mean
"rgba(255,0,0,0)" when going from red->transparent, and then mean
"rgba(0,0,255,0)" when going from transparent->blue, that's hacky and
weird.  Doing it in premultiplied space makes it all happen cleanly
and automatically.


More information about the whatwg mailing list