Csswg-drafts: [css-color] "transparent" keyword being a special color value, which resolves to rgba(0,0,0,0) by default?

Created on 31 May 2018  ·  12Comments  ·  Source: w3c/csswg-drafts

https://drafts.csswg.org/css-color-4/#transparent-color

@tabatkins -> https://twitter.com/tabatkins/status/1001884320229830656

I used to forget about gradient interop issues with the transparent keyword all the time (https://stackoverflow.com/questions/30673204/css-linear-gradient-transparency-misbehaving-only-in-safari) and I know safari still doesn't "automagically" (https://twitter.com/tabatkins/status/1001885856716615680) treat the case described.

Is it possible that implementation of transparent isn't beyond tweaking the spec to *fix this?

css-color-4 css-images-4

Most helpful comment

Summary of the twitter thread:

  • My original goal was to make linear-gradient(white, transparent) work "as expected" - white gradually fading away. As originally specced, it instead became grayish as it faded, as 'transparent' is actually #0000. You could fix this by saying white, #fff0, but then you still can't easily fade back in with a different color - white, #fff0, red will be pinkish at the 3/4 mark. You instead needed to explicitly add two transparent stops with different color, like white, #fff0 50%, #f000 50%, red, and now this is a 4-stop gradient rather than the 3-stop that it looks like (so it won't animate with other 3-stop gradients).
  • I assumed the 'transparent' equivalency was fixed, so I attacked the problem from a different angle, and specified that gradients transition in premultiplied space. white, transparent now works properly, as does white, transparent, red.
  • Downside is that now if you want to color-shift as you fade, you can't do it easily. You instead have to add multiple intermediate stops to force it to "curve" in premultiplied space. (But at least it's easy to figure out - you just manually do the linear interpolation you want to see - rgba(100%, 100%, 100%, 1), rgba(50%, 50%, 50%, .5), rgba(0,0,0,0) goes grayish, tho in practice you'll want a few more stops to make it smoother.)
  • Further downside is that this doesn't match implementation internals - all the graphics libraries transition in un-premultiplied space, like the spec originally mandated. So to get the premultiplied behavior, browsers have to internally add several intermediate steps to "curve" it thru un-premultiplied space. ^_^

After my rant, @fantasai suggested that maybe we could still fix this, and just special-case transparent to normally resolve directly to #0000, but let gradients key off of it specially and do the no-color-change behavior. That way if you explicitly write a transparent color, you will get the standard un-premultiplied color shift. This would allow browsers to switch back to the native behavior of the underlying graphics library, and just special-case "transparent" by turning it into one or two stops.

Thoughts?

All 12 comments

Summary of the twitter thread:

  • My original goal was to make linear-gradient(white, transparent) work "as expected" - white gradually fading away. As originally specced, it instead became grayish as it faded, as 'transparent' is actually #0000. You could fix this by saying white, #fff0, but then you still can't easily fade back in with a different color - white, #fff0, red will be pinkish at the 3/4 mark. You instead needed to explicitly add two transparent stops with different color, like white, #fff0 50%, #f000 50%, red, and now this is a 4-stop gradient rather than the 3-stop that it looks like (so it won't animate with other 3-stop gradients).
  • I assumed the 'transparent' equivalency was fixed, so I attacked the problem from a different angle, and specified that gradients transition in premultiplied space. white, transparent now works properly, as does white, transparent, red.
  • Downside is that now if you want to color-shift as you fade, you can't do it easily. You instead have to add multiple intermediate stops to force it to "curve" in premultiplied space. (But at least it's easy to figure out - you just manually do the linear interpolation you want to see - rgba(100%, 100%, 100%, 1), rgba(50%, 50%, 50%, .5), rgba(0,0,0,0) goes grayish, tho in practice you'll want a few more stops to make it smoother.)
  • Further downside is that this doesn't match implementation internals - all the graphics libraries transition in un-premultiplied space, like the spec originally mandated. So to get the premultiplied behavior, browsers have to internally add several intermediate steps to "curve" it thru un-premultiplied space. ^_^

After my rant, @fantasai suggested that maybe we could still fix this, and just special-case transparent to normally resolve directly to #0000, but let gradients key off of it specially and do the no-color-change behavior. That way if you explicitly write a transparent color, you will get the standard un-premultiplied color shift. This would allow browsers to switch back to the native behavior of the underlying graphics library, and just special-case "transparent" by turning it into one or two stops.

Thoughts?

If that is a faster path to the better behavior, then I'm all for it.

@fantasai suggested that maybe we could still fix this, and just special-case transparent to normally resolve directly to #0000, but let gradients key off of it specially and do the no-color-change behavior.

That sounds tricky to explain in a spec, but would map better to underlying libraries. Premultiplication suffers from erosion of resolution (effective number of bits) unless calculations are done in an elevated bit depth, which most graphics libraries don't.

Agenda+ because this also ends up mattering for cross-fade(); I don't want cross-fade(img 50%) to darken the image, and it would be weird for that syntax to work differently than cross-fade(img 50%, transparent 50%).

So we would special-case cross-fade() as well as *-gradient()? Anywhere else?

Anywhere else?

Any color transition, e.g. between solid colors over time.

The Working Group just discussed "transparent" keyword being a special color value, which resolves to rgba(0,0,0,0) by default?.

The full IRC log of that discussion
<dael> topic: "transparent" keyword being a special color value, which resolves to rgba(0,0,0,0) by default?

<dael> github: https://github.com/w3c/csswg-drafts/issues/2722

<dael> chris: One point TabAtkins was [missed]

<TabAtkins> Calling in

<dael> chris: It's if what TabAtkins did was right [missed]

<dbaron> lea: ... wifi accidentally ... we'll drop off for ...

<dael> TabAtkins: You remember back when we did gradients and there was an issue with transparent and I made it so gradients do a transtion through pre-multiplied space. THat has weird implications. All impl do non-pre-multiplied

<dael> TabAtkins: To fake pre-multiplied you have to put in a number of additional stops to do it they way transparent intends.

<dael> TabAtkins: I've come to regret this because transparent wants to be special in more places. When I wrote edits for cross-fade from last F2F I realized that crossfading with transparent would darken the image half way through. Same exact case. WHen you fade to transparent you expect ti to become more transparent not change color

<dael> TabAtkins: Don't want a special case where if you leave it blank you get a differnet behavior

<dbaron> q+

<dael> TabAtkins: Solution is slightly change original resolution. Transparent retains itself as a spcieal keyword through computed value. Some cases use it as meaning something special some can be to a more transparent version but otherwise it can act the same where it's rgba(0,0,0,0). Any opinions or in particular compat concerns?

<dael> dbaron: More concerned about if it's giving right behavior. IN particular sounds like you define cross fade so not in pre-multiplied.

<bradk> New keyword: “transparent-2”

<dael> TabAtkins: Most likely, yes. I suspect most libraries we're using aren't so we want to match

<AmeliaBR> Concern: How does this affect partially transparent colors? Can you divide an animation from red to transparent into a midpoint of #f008 and still get the same results?

<dael> dbaron: Not so sure on that. And if you're crossfading 2 images and one has area that's almost but not quite transparent you get bad results. Suppose you're looking at one region of the image and it happens to be mostly a solid. And in another image light and opaque and you could get darkening and then lightening. seems not good.

<dael> dbaron: Also I think a lot of compositing is done with pre-multiplied.

<dael> TabAtkins: Fine with defining in pre-multiplied

<dael> dbaron: Worth researching

<dael> chrishtr: Crossfade paints the bottom image with source overoperator

<dael> TabAtkins: It's not. It's not a source over it dissoves and plus operation

<dael> s/chrishtr/??

<dael> TabAtkins: You're not drawing one and then fading a separate image over it. If that's true the first image always shows if there's any transparent areas in the second and that's not right

<dbaron> ok, yeah, that makes it sound like premultiplied doesn't even matter

<dbaron> s/??/smfr/

<dael> Rossen_: We're running out of time. I don't want to go to overtime so I encourage people to go back to github

<dael> TabAtkins: I'll talk with graphics people and find out how our image fading works in the platform

I discussed the question of how cross-fade() can work with one of Chrome's implementors, and they said it didn't matter, they could do the averaging in premultiplied or non-premultiplied space, as Chrome does both at different parts of our compositing/rendering pipeline anyway.

I also researched the dissolve operation a little more; whatever reference I was originally looking at treated it as an unary operation that you applied to a single image, but Wikipedia defines it as a binary op (trivially extensible to an N-way). It's oddly defined as a stochastic operation that just randomly takes pixels from the source images according to their weights, but in the limit of infinitely small pixels, this converges to averaging in premultiplied space.

So, I've changed the spec to be clear that this averaging is done on premultiplied colors, and provided a good bit of additional detail on the operation to make sure it's clear. f6909a183

The Working Group just discussed "transparent" keyword being a special color value, which resolves to rgba(0,0,0,0) by default?, and agreed to the following:

  • RESOLVED: Crossfade blends images in premultiplied space

The full IRC log of that discussion
<dael> Topic: "transparent" keyword being a special color value, which resolves to rgba(0,0,0,0) by default?

<dael> github: https://github.com/w3c/csswg-drafts/issues/2722

<dael> TabAtkins: dbaron thought their libraries did blending already. I checked, same is true for us.

<TabAtkins> https://drafts.csswg.org/css-images-4/#cross-fade-painting

<dael> TabAtkins: Given that I edited spec ^

<dael> TabAtkins: Spec blending of crossfade is done premultiplied space so transparent parts of images work as expected

<smfr> seems fine

<dael> TabAtkins: Any further opinions let me know else this is what i'm going with.

<dael> TabAtkins: And I'm most of the way through crossfade edits from last F2F

<dael> astearns: smfr says seems fine. Other opinions?

<dael> astearns: TabAtkins want resolution? Or publish and get feedback?

<dael> TabAtkins: Get resolution to end thread. Crossfade blends images in premultiplied space

<dael> astearns: Objections?

<dael> RESOLVED: Crossfade blends images in premultiplied space

So @tabatkins does your https://github.com/w3c/csswg-drafts/commit/f6909a183350f15c245be0c70083a14e0c30c470 on css-images-4 completely solve this issue, or are changes also needed for css-color-4 ?

Solves it now. The direction seems to be "do pre-multiplied blending when appropriate, for whatever needs it".

I want to reference https://lists.w3.org/Archives/Public/www-style/2016Jan/0181.html here, where transition to and from transparent was discussed earlier.

Sebastian

Was this page helpful?
0 / 5 - 0 ratings