Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-color-6] add color functions for some (or all) compositing/blending operations #8431

Open
dbaron opened this issue Feb 8, 2023 · 41 comments

Comments

@dbaron
Copy link
Member

dbaron commented Feb 8, 2023

In #7358 (comment) @LeaVerou wrote:

Whatever default we pick, there will be cases where it's wrong, so it would be nice to offer a way out. How can the author select what the background is composited on? What if we add a color-over() function to do alpha compositing? Pretty sure there are other use cases for it too, and it should be relatively easy to implement.

In today's telecon, for the same reason (and I think not having read Lea's comment) I suggested something similar:

<dbaron> Seems like what Adam's suggesting could also be done with a composite(A over B) color function or similar rather than additional syntax for the contrast function.

In other words, what I was thinking was roughly a composite( <color> <composite-mode> <color> ) function, which has a slightly larger feature space. (Except I didn't want <composite-mode> exactly, because I'd rather not have the source- and dest- prefixes in this case.)

But it's possible we might also want to allow choosing the <blend-mode>.

Opening this issue because it seems like this side discussion probably deserves its own issue, and we can decide whether or not we actually want such a feature.

@LeaVerou
Copy link
Member

LeaVerou commented Feb 8, 2023

Thanks for opening the issue?

Are there use cases for every possible <composite-mode>? We don't usually add things for the sake of completeness.
<blend-mode> seems somewhat more useful, but still not nearly as much as simple alpha compositing. I think something simple like color-over() has more chances to be implemented than a bigger "all the things" function.

@dbaron
Copy link
Member Author

dbaron commented Feb 9, 2023

I think there are use cases in so far as there are use cases for setting composite modes or blend modes -- combined with the use case for wanting to use such a color function as part of a color contrast function. That said, CSS currently exposes only the blend modes and not the composite mode (which seems a little odd to me).

I admit that partly I suggested the composite mode because they produced a clear syntax with the mode as the operator.

@LeaVerou
Copy link
Member

LeaVerou commented Feb 9, 2023

Composite modes and blend modes operate on entire elements. Are there any use cases for isolated colors, that are NOT covered by existing syntax?

@nt1m
Copy link
Member

nt1m commented Jan 19, 2024

I was actually going to file this issue.

Would the proposed function here allow achieving the same results as this?

background-color: <base-opaque-color>;
background-image: linear-gradient(<semi-transparent-color>, <semi-transparent-color>);

I suggested color-mix() to someone facing this, and I realized that it wouldn't work because you would end up with a semi-transparent color. Maybe some fancy mix involving relative color syntax and color-mix() might work, but at this point, it's more convoluted than it should be.

I'm happy to file a new issue if this use-case isn't covered.

cc @svgeesus @weinig @mdubet

@nt1m
Copy link
Member

nt1m commented Jan 19, 2024

I think even a simple version that doesn't take a composite-mode would be useful here:
color-composite(<semi-transparent-color>, <base-opaque-color>)

@weinig
Copy link

weinig commented Jan 19, 2024

We should consider if extending color-mix with an additional optional blend-mode or composite-mode is viable.

@LeaVerou
Copy link
Member

I’m still missing a discussion on use cases. How can we design syntax, and discuss if we need a new function or an extension of color-mix() without a sense of the usage scenarios this would be useful in?!

@nt1m
Copy link
Member

nt1m commented Jan 20, 2024

#8431 (comment) describes exactly what I would use such a function for. Someone was asking me how they could turn a semi-transparent color into an opaque color by blending one of top of the other.

I'm not sure color-mix() is the right place to put this given color-mix() also supports percentages, but the use-case is definitely there.

@svgeesus
Copy link
Contributor

Some thoughts:

  • blending depends on the notion of foreground and background/backdrop, which color-mix() doesn't have
  • blending applies to arbitrary image inputs, not just solid colors
  • Porter-Duff compositing relies on foreground/background distinction, which color-mix() does not have.

These could be added, but currently color-mix(in space, color1, color2) is exactly the same as color-mix(in space, color2, color1) and adding a foreground/background distinction we would lose that property.

@LeaVerou
Copy link
Member

#8431 (comment) describes exactly what I would use such a function for. Someone was asking me how they could turn a semi-transparent color into an opaque color by blending one of top of the other.

I saw that comment, but that is not really a use case, it's an attempted solution to a use case. What were they trying to do that required this operation? What was the actual problem they were trying to solve?

@nt1m
Copy link
Member

nt1m commented Jan 23, 2024

The person wanted to blend one semi-transparent system color (-apple-[...]-fill only available to UA sheets) on top of an opaque one (Canvas) to get an opaque color.

@nt1m
Copy link
Member

nt1m commented Jan 23, 2024

I could see people wanting this when working with design systems too that have both opaque & semi-transparent colors

@LeaVerou
Copy link
Member

So are there any use cases that require more than simple alpha compositing?

@nt1m
Copy link
Member

nt1m commented Jan 23, 2024

So are there any use cases that require more than simple alpha compositing?

Not that I'm aware of, I would be satisfied with a color-over(<foreground> over <background>) syntax or something.

@LeaVerou
Copy link
Member

We can still do color-composite(<color> over <color>) that has potential for future expansion, should the use cases arise.

@nt1m nt1m added the Agenda+ label Jun 24, 2024
@LeaVerou
Copy link
Member

Looking at it again, that seems unnecessarily wordy. Let’s just do color-over(<color> / <color>). Or even color-over(<color>#), which covers multiple operations without the need for nesting function calls.

@benface
Copy link

benface commented Jul 23, 2024

I was looking for this! Thought for sure this was possible with color-mix(), but I guess it's not. color-over() or color-composite() would be great!

@svgeesus
Copy link
Contributor

So, in terms of specifying this: we should presumably reference General Formula for Compositing and Blending from Compositing 1. However, that spec is silent about colorspace, at least in the normative parts, and simply refers to "the premultiplied color".

The examples use Red Green and Blue, natural given the age of the spec, and at the time sRGB would be assumed. The more complex parts such as blend modes even use the NTSC luma formula (operating on gamma-encoded RGB values)

Lum(C) = 0.3 x Cred + 0.59 x Cgreen + 0.11 x Cblue

but that doesn't affect the simple color-over(<color>#) being proposed here. Assuming sRGB though, implies either

  1. Forcing all colors to be in the sRGB gamut, which is undesirable, or
  2. Converting all colors to extended-range sRGB and verifying that the formulae do something sensible with negative component values, or component values greater than 1

I recall hearing that on some systems, compositing happens in the native RGB colorspace of the display (eg P3).

@svgeesus
Copy link
Contributor

Is anyone arguing against this? If not we could do an async resolution.

PROPOSED Resolution: Add a color-over(<color>#) function to CSS Color 6 to do simple source-over compositing

@svgeesus svgeesus added the Async Resolution: Proposed Candidate for auto-resolve with stated time limit label Jul 29, 2024
@nt1m
Copy link
Member

nt1m commented Jul 29, 2024

What happens when you put more than 2 colors?

@svgeesus
Copy link
Contributor

svgeesus commented Jul 30, 2024

What happens when you put more than 2 colors?

You start from the end, and do them pairwise. In other words Edited to correct syntax

color-over(var(--col1), var(--col2), var(--col3)) is the same as
color-over(var(--col1) color-over(var(--col2), var(--col3)))

As @LeaVerou said

which covers multiple operations without the need for nesting function calls.

@astearns
Copy link
Member

The CSSWG will automatically accept this resolution one week from now if no objections are raised here. Anyone can add an emoji to this comment to express support. If you do not support this resolution, please add a new comment.

PROPOSED Resolution: Add a color-over(#) function to CSS Color 6 to do simple source-over compositing

@astearns astearns added Async Resolution: Call For Consensus Resolution will be called after time limit expires and removed Agenda+ Async Resolution: Proposed Candidate for auto-resolve with stated time limit labels Jul 30, 2024
@benface
Copy link

benface commented Jul 30, 2024

(Not an objection because I would love to have this feature in CSS, but note that there are only 7 participants in this thread, so only 7 people got a notification with a chance to object / propose alternatives.)

@astearns
Copy link
Member

@benface that’s fair, but note that I also posted this to www-style (https://lists.w3.org/Archives/Public/www-style/2024Jul/0012.html) and the resolution is not entirely binding. If this resolution passes then we will add it to the spec draft, but I expect we will continue to debate syntax and even whether we should remove it from the draft.

Nothing is ever final in CSS specs until it’s interoperably implemented and web content depends on those implementation details.

@fantasai
Copy link
Collaborator

I think I'd prefer to take up this topic on a call.

@astearns
Copy link
Member

@fantasai can you give us some indication of what you would like to change about the proposed resolution?

@fantasai
Copy link
Collaborator

Well, first off I don't think there's clarity in this thread about what the proposal actually is. :) For example, Chris's proposed resolution uses a comma-separated syntax, but his subsequent examples are space-separated.

But in general I think it's better to discuss non-trivial issues on a call (for a variety of reasons that aren't specific to this issue), and I don't think adding a new feature can be considered trivial.

@svgeesus
Copy link
Contributor

Well, first off I don't think there's clarity in this thread about what the proposal actually is. :) For example, Chris's proposed resolution uses a comma-separated syntax, but his subsequent examples are space-separated.

My mistake.

I don't think adding a new feature can be considered trivial.

Fair enough.

@astearns astearns added Agenda+ and removed Async Resolution: Call For Consensus Resolution will be called after time limit expires labels Jul 31, 2024
@fantasai
Copy link
Collaborator

fantasai commented Aug 4, 2024

I also suggest picking a name that can later accept different compositing/blending operations as the first argument (similar to how we set up parameters for a gradient at the start of a gradient stop list), since it seems reasonably likely we'll want to expand in that direction at some point.

(I don't think we need an operator per list item; if authors are getting that fancy, they can nest these functions.)

@nt1m
Copy link
Member

nt1m commented Aug 4, 2024

I agree with @LeaVerou that <composite-mode> is actually not that useful, since we're dealing with the same shape (it'd essentially be the same as reordering the color list to change the composite mode). <blend-mode> seems more useful.

I would suggest either:

color-over([<blend-mode>, ]? <color>#)

or (based on @fantasai's internal suggestion):

color-layer([<blend-mode>, ]? <color>#)

(potentially the blend mode could be preceded by the using keyword, no strong opinion)

@svgeesus
Copy link
Contributor

svgeesus commented Aug 7, 2024

color-layer might be more self explanatory than color-over (particularly by those unfamiliar with Porter-Duff terminology).

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-color-6] add color functions for some (or all) compositing/blending operations, and agreed to the following:

  • RESOLVED: color-layers([<blend-mode>, ]? <color>#)
  • RESOLVED: function name is "color-layers()"
The full IRC log of that discussion <ChrisL> q+
<Rossen4> ack ChrisL
<matthieud> ChrisL: you want to composite color1 on color2
<matthieud> ntim: currently web devs need background gradient to composite one color on top of the other.
<TabAtkins> +1 to the functionality too, seems easy and moderately useful
<matthieud> ChrisL: proposition is using color-layer
<fantasai> color-layer([<blend-mode>, ]? <color>#)
<astearns> slight preference for color-over, as I associate *-layer with stacks of things
<matthieud> ntim: either color-over() or color-layer() and inside the function you have a comma separated list of colors and optional blend-mode
<miriam> layer-colors() reads better to me
<matthieud> ChrisL: optional blend-mode preference, if not specified you get the default
<matthieud> fantasai: we agree on the arguments of the function [<blend-mode>, ]? <color>#
<Rossen4> ack miriam
<ChrisL> q+
<ntim> q+
<matthieud> miriam: color-layer read weirds because it's not a single layer
<matthieud> miriam: proposal for layer-colors() or color-over()
<fantasai> color-layers?
<Rossen4> ack ChrisL
<Rossen4> ack ntim
<matthieud> ChrisL: all color functions start with color-*
<ChrisL> ntim++
<matthieud> ntim: consistency with color-mix()
<miriam> color-stack(), color-over()…
<matthieud> ChrisL: color-stack() sounds like stacking context, weird
<fantasai> POLL: A) color-over B) color-layer C) color-stack
<fantasai> D) color-layers
<nicole> D
<fantasai> not A
<astearns> A
<matthieud> D
<ntim> A or B (no strong opinion)
<Rossen4> D B
<schenney> B
<ChrisL> D
<TabAtkins> d
<miriam> A C or D
<kbabbitt> A > D > B> C
<schenney> Change: D
<ethanjv> D > A
<masonf> someone needs to write some code to see which option won.
<nicole> haha
<matthieud> Rossen4: based on first choice color-layers() win
<ChrisL> \0/
<fantasai> RESOLVED: color-layers([<blend-mode>, ]? <color>#)
<matthieud> RESOLVED: function name is "color-layers()"

@LeaVerou
Copy link
Member

LeaVerou commented Aug 9, 2024

Big -1 to this new color-layers() name, to the point I'd consider objecting.

This is a function creating a <color>, not returning a layer of any sort (or multiple layers as the current name implies!). As an author, I find it incredibly confusing. color-over() was much more natural, and this has nothing to do with Porter-Duff. You're literally putting a color over another and just optionally specifying the blending mode. This is how you'd talk about it in natural language.

color-blend() or color-overlay() would also be fine, and I could live with color-stack(). But gosh not color-layers().

@facelessuser
Copy link

I'm not sure I would immediately think color-layers() is that bad of a name. But in the plural form, I could see it being interpreted as a plural noun when compared to something like color-mix() which is used like a verb.

Something like color-layer() may remove possible confusion and imply the function layers the colors on top of each other no differently than color-overlay() overlays colors and doesn't return an overlay (noun). Context matters. In reality though, I have no strong feelings either way.

I don't think either instantly calls to mind Porter-Duff more than the other. Mainly people who know how compositing works are the ones that would make the association, and whether you use layer, overlay, over, stack, or blend Porter Duff still applies. Maybe all the Porter Duff methods aren't desired to be exposed, but at the very least you would be using "source over" if all you expose is blend modes.

Out of all the suggestions, I probably don't like color-stack() if I'm being honest, but that is completely subjective as I have no strong argument against it, I just don't like it 🙃. I probably like color-overlay() more than color-over() just because color-overlay() seems more descriptive (to me) as to what is being done, but ultimately I'm fairly indifferent.

@benface
Copy link

benface commented Aug 9, 2024

color-overlay() is great 👍

@nt1m
Copy link
Member

nt1m commented Aug 10, 2024

@fantasai's suggestion of layers comes from background-layers fwiw (nothing to do with Porter-duff), just to create a parallel between the two, given the ordering is from top to bottom in both cases.

If I'd choose another name, I'd choose either:

  • color-over() (not a huge fan of overlay since it has other significations)
  • color-layer() (Using layer as a verb, like mix is used as a verb in color-mix())

Not a fan of color-blend() (too close to color-mix()) or color-stack().

@romainmenke
Copy link
Member

fyi: WebKit has included color-layers() in Technical Preview: https://webkit.org/blog/15798/release-notes-for-safari-technology-preview-202/

@nt1m
Copy link
Member

nt1m commented Aug 29, 2024

(It does not include support for the <blend-mode> argument, but I didn't want to spend too much time on it until we had the name finalized)

@svgeesus
Copy link
Contributor

svgeesus commented Aug 29, 2024

Besides kicking around syntax, I'm hoping that we could also lock down some less important aspects like how it actually works. Following Compositing-1 it seems that the color space should be (gamma-encoded) sRGB and assuming we want to allow non-sRGB colors then they would get converted into extended-range sRGB before the compositing and blending operations.

Is that right or do we want to address that implementations are sometimes compositing in device RGB space?

It would be nice to have operations that are testable, rather than "it depends".

@facelessuser
Copy link

If various blend modes are allowed, I assume you would need to allow other color modes as extended would not be sufficient. IIRC some of the blend algorithms assume colors between 0 - 1. Some blend modes would likely blend incorrectly in an extended sRGB if given Rec. 2020, etc.

@facelessuser
Copy link

From my reading of the compositing spec, blend is applied, values clamped to maximum and minimum, then source-over for the alpha blending. The clamp will obviously cause differences if just an extended sRGB space is used:

Below shows color(rec2020 0 1 1 / 0.5) layered on top of color(rec2020 1 0 1 ). Blending is processed in sRGB, Display-P3, and Rec. 2020 (in that order). Gamut mapping is applied afterward.

Screenshot 2024-08-29 at 1 28 59 PM

If we remove the clamp after blend to try and allow the extended sRGB to behave better, the results are different, but still vary depending on the compositing space.

Screenshot 2024-08-29 at 1 32 23 PM

If we process color(srgb 0 1 1 / 0.5) layered on top of color(srgb 1 0 1 ) in sRGB, and color(display-p3 0 1 1 / 0.5) layered on top of color(display-p3 1 0 1 ) in Display P3, and do the same for Rec. 2020, the results seem more like you'd expect.

Screenshot 2024-08-29 at 1 37 44 PM

I imagine that allowing different compositing spaces depending on your working gamut would make sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests