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