Skip to content

Commit f6909a1

Browse files
committed
[css-images-4] Fix definition of cross-fade() image blending, per WG discussion. Also fix typo.
1 parent b08db46 commit f6909a1

1 file changed

Lines changed: 48 additions & 23 deletions

File tree

css-images-4/Overview.bs

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ Combining images: the ''cross-fade()'' notation {#cross-fade-function}
441441

442442
2. For each |argument| of the ''cross-fade()'' function with an <<image>> value:
443443
1. Let |item| be a [=tuple=] consisting of a width, a height, and a percentage.
444-
2. Run the [=object size negotation=] algorithm for the <<image>>,
444+
2. Run the [=object size negotiation=] algorithm for the <<image>>,
445445
as appropriate for the context in which the ''cross-fade()'' appears,
446446
and set |item|’s width and height
447447
to the width and height of the resulting [=concrete object size=].
@@ -499,7 +499,15 @@ Combining images: the ''cross-fade()'' notation {#cross-fade-function}
499499

500500
4. Let |percentage sum| be the sum of all the percentages of the [=list/items=] in |images|.
501501

502-
5. [=list/For each=] |item| in |images|,
502+
5. If |percentage sum| is less than ''100%'',
503+
append a [=tuple=] to |images|
504+
consisting of a solid-color transparent-black image
505+
with |size|’s dimensions,
506+
and a percentage equal to ''100%'' minus |percentage sum|.
507+
508+
Otherwise,
509+
if |percentage sum| is greater than ''100%'',
510+
then [=list/for each=] |item| in |images|,
503511
divide |item|’s percentage by |percentage sum|,
504512
and set |item|’s percentage to the result.
505513

@@ -508,27 +516,44 @@ Combining images: the ''cross-fade()'' notation {#cross-fade-function}
508516
and every pixel being the weighted linear average of the corresponding pixels of [=list/for each|each=] |item|’s image in |images|,
509517
weighted according to the |item|’s percentage.
510518
(Average both the color channels and the alpha channel of the pixels.)
511-
512-
Note: This is applying the Porter-Duff <code>dissolve</code> operator to each source image,
513-
then combining them all together with the Porter-Duff <code>plus</code> operator. [[PORTERDUFF]]
514-
515-
Issue: What color space does this average take place in? sRGB? Controlled by @color-space?
516-
517-
8. If |percentage sum| is less than ''100%'',
518-
multiply the alpha channel of every pixel in |final image| by |percentage sum|.
519-
520-
Note: This ensures that ''cross-fade(img 50%)'' doesn't darken the image
521-
(as would happen by blending with <a>transparent black</a>),
522-
but just makes it more transparent,
523-
as the author almost certainly intends.
524-
525-
Issue: Should this also apply to explicit ''transparent'' arguments?
526-
See <a href="https://github.com/w3c/csswg-drafts/issues/2722">issue 2722</a>
527-
for making ''transparent'' generally act like just “transparency”,
528-
rather than specifically being <a>transparent black</a>.
529-
We'd specify this by filtering them out in step 3.
530-
531-
8. Return |final image|.
519+
For the purpose of this calculation,
520+
each pixel's color must be in pre-multiplied sRGB.
521+
522+
<details class=note>
523+
<summary>Details on the above operation</summary>
524+
525+
This is applying an N-way Porter-Duff <code>dissolve</code> operation to the source images.
526+
Wikipedia defines <code>dissolve</code> as a stochastic operation,
527+
with the result pixels independently randomly chosen from the source images’ corresponding pixels
528+
according to their source images’ weights,
529+
but as pixels shrink to infinitely small,
530+
this converges to doing color-averaging in pre-multiplied color space.
531+
532+
In particular, this means that `cross-fade(white 50%, transparent 50%)`
533+
will produce a partially-transparent solid white image.
534+
(Rather than a partially-transparent gray,
535+
which is what you'd get if you averaged the opaque white and transparent black pixels
536+
in non-premultiplied space.)
537+
538+
As converting to pre-multiplied does entail some loss of precision,
539+
and graphics libraries may or may not support this operation natively,
540+
as per usual any method can be used so long as it achieves the specified effect.
541+
542+
For example, one can instead rebalance the percentages
543+
according to the alphas of each pixel,
544+
then do the color-channel averages in non-premultiplied space.
545+
E.g., to render ''cross-fade(rgb(255 0 0 / 1) 40%, rgb(0 255 0 / .5) 20%, rgb(0 0 255 / 0) 40%)'',
546+
rebalancing the percentages according to the 1 / .5 / 0 alphas
547+
would produce 40% / 10% / 0%
548+
(which renormalizes to 80% / 20% / 0%),
549+
at which point you can average the raw color channel values
550+
and end up with an ''rgb(204 51 0 / .5)'' image.
551+
(Note that the alpha channel is still averaged using the original percentages,
552+
not the rebalanced ones.)
553+
</details>
554+
555+
556+
7. Return |final image|.
532557
</div>
533558

534559
### Simplifying Complex ''cross-fade()'' ### {#cross-fade-complex}

0 commit comments

Comments
 (0)