Diff
Modified: trunk/LayoutTests/ChangeLog (287425 => 287426)
--- trunk/LayoutTests/ChangeLog 2021-12-24 10:50:26 UTC (rev 287425)
+++ trunk/LayoutTests/ChangeLog 2021-12-24 16:46:59 UTC (rev 287426)
@@ -1,3 +1,21 @@
+2021-12-24 Sam Weinig <wei...@apple.com>
+
+ On systems without CG support for alpha premultiplied gradients, the CGGradientRef path should still be used for the subset of gradients that can transformed
+ https://bugs.webkit.org/show_bug.cgi?id=234653
+
+ Reviewed by Simon Fraser.
+
+ Add tests of gradients that can render identically with both alpha premultiplied and non-premultiplied gradients
+ either using the same color stop list or a transformed one.
+
+ The main test page contains the alpha premultiplied gradients (which is the default now for CSS gradients), while
+ the -expected.html contains the alpha non-premultiplied gradients and has alpha premultiplied interpolation explicitly
+ disabled via a CSSGradientPremultipliedAlphaInterpolationEnabled=false comment command.
+
+ * fast/gradients/alpha-premultiplied-representable-by-unpremultiplied-expected.html: Added.
+ * fast/gradients/alpha-premultiplied-representable-by-unpremultiplied.html: Added.
+ * platform/glib/TestExpectations: Disable on non-CoreGraphics ports.
+
2021-12-23 Alan Bujtas <za...@apple.com>
REGRESSION(Containment) nullptr deref in RenderBox::styleDidChange
Added: trunk/LayoutTests/fast/gradients/alpha-premultiplied-representable-by-unpremultiplied-expected.html (0 => 287426)
--- trunk/LayoutTests/fast/gradients/alpha-premultiplied-representable-by-unpremultiplied-expected.html (rev 0)
+++ trunk/LayoutTests/fast/gradients/alpha-premultiplied-representable-by-unpremultiplied-expected.html 2021-12-24 16:46:59 UTC (rev 287426)
@@ -0,0 +1,192 @@
+<!DOCTYPE html><!-- webkit-test-runner [ CSSGradientPremultipliedAlphaInterpolationEnabled=false ] -->
+<html>
+<head>
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ border: dashed black 1px;
+ display: inline-block;
+ }
+
+ :root {
+ --transparent-1: rgba(0, 0, 255, 0);
+ --transparent-2: rgba(255, 0, 0, 0);
+ --transparent-3: rgba(0, 255, 0, 0);
+ --transparent-4: rgba(255, 255, 0, 0);
+
+ --opaque-1: rgba(0, 0, 255, 1);
+ --opaque-2: rgba(255, 0, 0, 1);
+ --opaque-3: rgba(0, 255, 0, 1);
+ --opaque-4: rgba(255, 255, 0, 1);
+
+ --partial-1: rgba(0, 0, 255, 0.5);
+ --partial-2: rgba(255, 0, 0, 0.5);
+ --partial-3: rgba(0, 255, 0, 0.5);
+ --partial-4: rgba(255, 255, 0, 0.5);
+ }
+
+ /* NOTE: If we ever want to remove the option to disable alpha premultiplied gradients, and alternative way
+ to test this would be using either Canvas or SVG, which both use unpremultiplied interpolation. */
+
+ #opaque-opaque {
+ /* No transform */
+ background-image: linear-gradient(var(--opaque-1), var(--opaque-2));
+ }
+
+ #partial-partial {
+ /* No transform */
+ background-image: linear-gradient(var(--partial-1), var(--partial-2));
+ }
+
+ #transparent-transparent {
+ /* No transform */
+ background-image: linear-gradient(var(--transparent-1), var(--transparent-2));
+ }
+
+ #transparent-opaque {
+ /* Steal next */
+ background-image: linear-gradient(var(--transparent-2), var(--opaque-2));
+ }
+
+ #transparent-partial {
+ /* Steal next */
+ background-image: linear-gradient(var(--transparent-2), var(--partial-2));
+ }
+
+ #opaque-transparent {
+ /* Steal previous */
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-1));
+ }
+
+ #partial-transparent {
+ /* Steal previous */
+ background-image: linear-gradient(var(--partial-1), var(--transparent-1));
+ }
+
+ #opaque-transparent-opaque {
+ /* Split */
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-1) 50%, var(--transparent-3) 50%, var(--opaque-3));
+ }
+
+ #opaque-transparent-partial {
+ /* Split */
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-1) 50%, var(--transparent-3) 50%, var(--partial-3));
+ }
+
+ #partial-transparent-partial {
+ /* Split */
+ background-image: linear-gradient(var(--partial-1), var(--transparent-1) 50%, var(--transparent-3) 50%, var(--partial-3));
+ }
+
+ #partial-transparent-opaque {
+ /* Split */
+ background-image: linear-gradient(var(--partial-1), var(--transparent-1) 50%, var(--transparent-3) 50%, var(--opaque-3));
+ }
+
+ #opaque-transparent-transparent-opaque {
+ /* Steal previous, steal next */
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-1), var(--transparent-4), var(--opaque-4));
+ }
+
+ #opaque-transparent-transparent-partial {
+ /* Steal previous, steal next */
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-1), var(--transparent-4), var(--partial-4));
+ }
+
+ #partial-transparent-transparent-partial {
+ /* Steal previous, steal next */
+ background-image: linear-gradient(var(--partial-1), var(--transparent-1), var(--transparent-4), var(--partial-4));
+ }
+
+ #partial-transparent-transparent-opaque {
+ /* Steal previous, nothing, steal next */
+ background-image: linear-gradient(var(--partial-1), var(--transparent-1), var(--transparent-4), var(--opaque-4));
+ }
+
+ #opaque-transparent-transparent-transparent-opaque {
+ /* Steal previous, nothing, steal next */
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-1), var(--transparent-3), var(--transparent-2), var(--opaque-2));
+ }
+
+ #opaque-transparent-transparent-transparent-partial {
+ /* Steal previous, nothing, steal next */
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-1), var(--transparent-3), var(--transparent-2), var(--partial-2));
+ }
+
+ #partial-transparent-transparent-transparent-partial {
+ /* Steal previous, nothing, steal next */
+ background-image: linear-gradient(var(--partial-1), var(--transparent-1), var(--transparent-3), var(--transparent-2), var(--partial-2));
+ }
+
+ #partial-transparent-transparent-transparent-opaque {
+ /* Steal previous, nothing, steal next */
+ background-image: linear-gradient(var(--partial-1), var(--transparent-1), var(--transparent-3), var(--transparent-2), var(--opaque-2));
+ }
+
+ #opaque-transparent-transparent {
+ /* Steal previous, nothing */
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-1), var(--transparent-3));
+ }
+
+ #partial-transparent-transparent {
+ /* Steal previous, nothing */
+ background-image: linear-gradient(var(--partial-1), var(--transparent-1), var(--transparent-3));
+ }
+
+ #transparent-transparent-opaque {
+ /* Nothing, steal next */
+ background-image: linear-gradient(var(--transparent-1), var(--transparent-3), var(--opaque-3));
+ }
+
+ #transparent-transparent-partial {
+ /* Nothing, steal next */
+ background-image: linear-gradient(var(--transparent-1), var(--transparent-3), var(--partial-3));
+ }
+
+ #transparent-transparent-transparent {
+ /* No transform */
+ background-image: linear-gradient(var(--transparent-1), var(--transparent-2), var(--transparent-3));
+ }
+
+ #transparent-opaque-transparent {
+ /* Steal next, steal previous */
+ background-image: linear-gradient(var(--transparent-2), var(--opaque-2), var(--transparent-2));
+ }
+
+ #transparent-partial-transparent {
+ /* Steal next, steal previous */
+ background-image: linear-gradient(var(--transparent-2), var(--partial-2), var(--transparent-2));
+ }
+
+</style>
+</head>
+<body>
+<div id="opaque-opaque"></div>
+<div id="partial-partial"></div>
+<div id="transparent-transparent"></div>
+<div id="transparent-opaque"></div>
+<div id="transparent-partial"></div>
+<div id="opaque-transparent"></div>
+<div id="partial-transparent"></div>
+<div id="opaque-transparent-opaque"></div>
+<div id="opaque-transparent-partial"></div>
+<div id="partial-transparent-partial"></div>
+<div id="partial-transparent-opaque"></div>
+<div id="opaque-transparent-transparent-opaque"></div>
+<div id="opaque-transparent-transparent-partial"></div>
+<div id="partial-transparent-transparent-partial"></div>
+<div id="partial-transparent-transparent-opaque"></div>
+<div id="opaque-transparent-transparent-transparent-opaque"></div>
+<div id="opaque-transparent-transparent-transparent-partial"></div>
+<div id="partial-transparent-transparent-transparent-partial"></div>
+<div id="partial-transparent-transparent-transparent-opaque"></div>
+<div id="opaque-transparent-transparent"></div>
+<div id="partial-transparent-transparent"></div>
+<div id="transparent-transparent-opaque"></div>
+<div id="transparent-transparent-partial"></div>
+<div id="transparent-transparent-transparent"></div>
+<div id="transparent-opaque-transparent"></div>
+<div id="transparent-partial-transparent"></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/gradients/alpha-premultiplied-representable-by-unpremultiplied.html (0 => 287426)
--- trunk/LayoutTests/fast/gradients/alpha-premultiplied-representable-by-unpremultiplied.html (rev 0)
+++ trunk/LayoutTests/fast/gradients/alpha-premultiplied-representable-by-unpremultiplied.html 2021-12-24 16:46:59 UTC (rev 287426)
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ border: dashed black 1px;
+ display: inline-block;
+ }
+
+ :root {
+ --transparent-1: rgba(0, 0, 255, 0);
+ --transparent-2: rgba(255, 0, 0, 0);
+ --transparent-3: rgba(0, 255, 0, 0);
+ --transparent-4: rgba(255, 255, 0, 0);
+
+ --opaque-1: rgba(0, 0, 255, 1);
+ --opaque-2: rgba(255, 0, 0, 1);
+ --opaque-3: rgba(0, 255, 0, 1);
+ --opaque-4: rgba(255, 255, 0, 1);
+
+ --partial-1: rgba(0, 0, 255, 0.5);
+ --partial-2: rgba(255, 0, 0, 0.5);
+ --partial-3: rgba(0, 255, 0, 0.5);
+ --partial-4: rgba(255, 255, 0, 0.5);
+ }
+
+ #opaque-opaque {
+ background-image: linear-gradient(var(--opaque-1), var(--opaque-2));
+ }
+
+ #partial-partial {
+ background-image: linear-gradient(var(--partial-1), var(--partial-2));
+ }
+
+ #transparent-transparent {
+ background-image: linear-gradient(var(--transparent-1), var(--transparent-2));
+ }
+
+ #transparent-opaque {
+ background-image: linear-gradient(var(--transparent-1), var(--opaque-2));
+ }
+
+ #transparent-partial {
+ background-image: linear-gradient(var(--transparent-1), var(--partial-2));
+ }
+
+ #opaque-transparent {
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-2));
+ }
+
+ #partial-transparent {
+ background-image: linear-gradient(var(--partial-1), var(--transparent-2));
+ }
+
+ #opaque-transparent-opaque {
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-2), var(--opaque-3));
+ }
+
+ #opaque-transparent-partial {
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-2), var(--partial-3));
+ }
+
+ #partial-transparent-partial {
+ background-image: linear-gradient(var(--partial-1), var(--transparent-2), var(--partial-3));
+ }
+
+ #partial-transparent-opaque {
+ background-image: linear-gradient(var(--partial-1), var(--transparent-2), var(--opaque-3));
+ }
+
+ #opaque-transparent-transparent-opaque {
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-2), var(--transparent-3), var(--opaque-4));
+ }
+
+ #opaque-transparent-transparent-partial {
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-2), var(--transparent-3), var(--partial-4));
+ }
+
+ #partial-transparent-transparent-partial {
+ background-image: linear-gradient(var(--partial-1), var(--transparent-2), var(--transparent-3), var(--partial-4));
+ }
+
+ #partial-transparent-transparent-opaque {
+ background-image: linear-gradient(var(--partial-1), var(--transparent-2), var(--transparent-3), var(--opaque-4));
+ }
+
+ #opaque-transparent-transparent-transparent-opaque {
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-2), var(--transparent-3), var(--transparent-4), var(--opaque-2));
+ }
+
+ #opaque-transparent-transparent-transparent-partial {
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-2), var(--transparent-3), var(--transparent-4), var(--partial-2));
+ }
+
+ #partial-transparent-transparent-transparent-partial {
+ background-image: linear-gradient(var(--partial-1), var(--transparent-2), var(--transparent-3), var(--transparent-4), var(--partial-2));
+ }
+
+ #partial-transparent-transparent-transparent-opaque {
+ background-image: linear-gradient(var(--partial-1), var(--transparent-2), var(--transparent-3), var(--transparent-4), var(--opaque-2));
+ }
+
+ #opaque-transparent-transparent {
+ background-image: linear-gradient(var(--opaque-1), var(--transparent-2), var(--transparent-3));
+ }
+
+ #partial-transparent-transparent {
+ background-image: linear-gradient(var(--partial-1), var(--transparent-2), var(--transparent-3));
+ }
+
+ #transparent-transparent-opaque {
+ background-image: linear-gradient(var(--transparent-1), var(--transparent-2), var(--opaque-3));
+ }
+
+ #transparent-transparent-partial {
+ background-image: linear-gradient(var(--transparent-1), var(--transparent-2), var(--partial-3));
+ }
+
+ #transparent-transparent-transparent {
+ background-image: linear-gradient(var(--transparent-1), var(--transparent-2), var(--transparent-3));
+ }
+
+ #transparent-opaque-transparent {
+ background-image: linear-gradient(var(--transparent-1), var(--opaque-2), var(--transparent-3));
+ }
+ #transparent-partial-transparent {
+ background-image: linear-gradient(var(--transparent-1), var(--partial-2), var(--transparent-3));
+ }
+
+</style>
+</head>
+<body>
+<div id="opaque-opaque"></div>
+<div id="partial-partial"></div>
+<div id="transparent-transparent"></div>
+<div id="transparent-opaque"></div>
+<div id="transparent-partial"></div>
+<div id="opaque-transparent"></div>
+<div id="partial-transparent"></div>
+<div id="opaque-transparent-opaque"></div>
+<div id="opaque-transparent-partial"></div>
+<div id="partial-transparent-partial"></div>
+<div id="partial-transparent-opaque"></div>
+<div id="opaque-transparent-transparent-opaque"></div>
+<div id="opaque-transparent-transparent-partial"></div>
+<div id="partial-transparent-transparent-partial"></div>
+<div id="partial-transparent-transparent-opaque"></div>
+<div id="opaque-transparent-transparent-transparent-opaque"></div>
+<div id="opaque-transparent-transparent-transparent-partial"></div>
+<div id="partial-transparent-transparent-transparent-partial"></div>
+<div id="partial-transparent-transparent-transparent-opaque"></div>
+<div id="opaque-transparent-transparent"></div>
+<div id="partial-transparent-transparent"></div>
+<div id="transparent-transparent-opaque"></div>
+<div id="transparent-transparent-partial"></div>
+<div id="transparent-transparent-transparent"></div>
+<div id="transparent-opaque-transparent"></div>
+<div id="transparent-partial-transparent"></div>
+</body>
+</html>
Modified: trunk/LayoutTests/platform/glib/TestExpectations (287425 => 287426)
--- trunk/LayoutTests/platform/glib/TestExpectations 2021-12-24 10:50:26 UTC (rev 287425)
+++ trunk/LayoutTests/platform/glib/TestExpectations 2021-12-24 16:46:59 UTC (rev 287426)
@@ -489,9 +489,10 @@
# fast/gradients are Skip in the top level Expectation. When fixing, change this to
# Pass instead of removing this line.
-webkit.org/b/214259 fast/gradients/conic-gradient-alpha-unpremultiplied.html [ ImageOnlyFailure ]
-webkit.org/b/214259 fast/gradients/conic-gradient-alpha.html [ ImageOnlyFailure ]
-webkit.org/b/234492 fast/gradients/alpha-premultiplied.html [ ImageOnlyFailure ]
+webkit.org/b/234606 fast/gradients/conic-gradient-alpha-unpremultiplied.html [ ImageOnlyFailure ]
+webkit.org/b/234606 fast/gradients/conic-gradient-alpha.html [ ImageOnlyFailure ]
+webkit.org/b/234606 fast/gradients/alpha-premultiplied-representable-by-unpremultiplied.html [ ImageOnlyFailure ]
+webkit.org/b/234606 fast/gradients/alpha-premultiplied.html [ ImageOnlyFailure ]
webkit.org/b/169988 css3/filters/backdrop/backdrop-filter-with-border-radius-and-reflection-add.html [ ImageOnlyFailure ]
webkit.org/b/169988 css3/filters/backdrop/backdrop-filter-with-border-radius-and-reflection.html [ ImageOnlyFailure ]
Modified: trunk/Source/WebCore/ChangeLog (287425 => 287426)
--- trunk/Source/WebCore/ChangeLog 2021-12-24 10:50:26 UTC (rev 287425)
+++ trunk/Source/WebCore/ChangeLog 2021-12-24 16:46:59 UTC (rev 287426)
@@ -1,3 +1,32 @@
+2021-12-24 Sam Weinig <wei...@apple.com>
+
+ On systems without CG support for alpha premultiplied gradients, the CGGradientRef path should still be used for the subset of gradients that can transformed
+ https://bugs.webkit.org/show_bug.cgi?id=234653
+
+ Reviewed by Simon Fraser.
+
+ Test: fast/gradients/alpha-premultiplied-representable-by-unpremultiplied.html
+
+ Optimize gradient rendering on systems without a version of CoreGraphics that supports
+ alpha premultiplied gradients by using the CGGradientRef code path for the subset of
+ alpha premultiplied gradients that can be represented as alpha non-premultiplied gradients.
+
+ Two types of optimizations are possible for two classes of this subset:
+
+ 1. Any gradient that uses the same alpha value for all color stops can be used as is
+ with the alpha non-premultiplied CGGradientRef.
+ 2. Any gradient that conforms to the rule that "any two consecutive color stops must
+ either have one that is fully transparent or have the same alpha value for both"
+ can be transformed into an identical alpha non-premultiplied gradient by transforming
+ the fully transparent stops into either one or two stops. A comment in the code
+ goes into much more detail about this.
+
+ * platform/graphics/cg/GradientRendererCG.cpp:
+ (WebCore::classifyAlphaType):
+ (WebCore::analyzeColorStopsForEmulatedAlphaPremuliplicationOppertunity):
+ (WebCore::alphaTransformStopsToEmulateAlphaPremuliplication):
+ (WebCore::GradientRendererCG::pickStrategy const):
+
2021-12-24 Carlos Garcia Campos <cgar...@igalia.com>
[GTK][a11y] Expose live region attributes with ATSPI
Modified: trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.cpp (287425 => 287426)
--- trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.cpp 2021-12-24 10:50:26 UTC (rev 287425)
+++ trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.cpp 2021-12-24 16:46:59 UTC (rev 287426)
@@ -40,6 +40,352 @@
// MARK: - Strategy selection.
+#if !HAVE(CORE_GRAPHICS_PREMULTIPLIED_INTERPOLATION_GRADIENT)
+
+enum class EmulatedAlphaPremuliplicationAnalysisResult {
+ UseGradientAsIs,
+ UseGradientWithAlphaTransforms,
+ UseShading
+};
+
+enum class AlphaType {
+ Opaque,
+ PartiallyTransparent,
+ FullyTransparent
+};
+
+static AlphaType classifyAlphaType(float alpha)
+{
+ if (alpha == 1.0f)
+ return AlphaType::Opaque;
+ if (alpha == 0.0f)
+ return AlphaType::FullyTransparent;
+ return AlphaType::PartiallyTransparent;
+}
+
+static EmulatedAlphaPremuliplicationAnalysisResult analyzeColorStopsForEmulatedAlphaPremuliplicationOppertunity(const GradientColorStops& stops)
+{
+ if (stops.size() < 2)
+ return EmulatedAlphaPremuliplicationAnalysisResult::UseGradientAsIs;
+
+ bool uniformAlpha = true;
+
+ auto& initialStop = *stops.begin();
+ auto previousStopAlpha = initialStop.color.alphaAsFloat();
+ auto previousStopAlphaType = classifyAlphaType(previousStopAlpha);
+
+ auto stopIterator = stops.begin();
+ ++stopIterator;
+
+ while (stopIterator != stops.end()) {
+ auto& stop = *stopIterator;
+
+ auto stopAlpha = stop.color.alphaAsFloat();
+ auto stopAlphaType = classifyAlphaType(stopAlpha);
+
+ switch (stopAlphaType) {
+ case AlphaType::Opaque:
+ switch (previousStopAlphaType) {
+ case AlphaType::Opaque:
+ break;
+ case AlphaType::PartiallyTransparent:
+ return EmulatedAlphaPremuliplicationAnalysisResult::UseShading;
+ case AlphaType::FullyTransparent:
+ uniformAlpha = false;
+ break;
+ }
+ break;
+
+ case AlphaType::PartiallyTransparent:
+ switch (previousStopAlphaType) {
+ case AlphaType::Opaque:
+ return EmulatedAlphaPremuliplicationAnalysisResult::UseShading;
+ case AlphaType::PartiallyTransparent:
+ if (previousStopAlpha != stopAlpha)
+ return EmulatedAlphaPremuliplicationAnalysisResult::UseShading;
+ break;
+ case AlphaType::FullyTransparent:
+ uniformAlpha = false;
+ break;
+ }
+ break;
+
+ case AlphaType::FullyTransparent:
+ switch (previousStopAlphaType) {
+ case AlphaType::Opaque:
+ case AlphaType::PartiallyTransparent:
+ uniformAlpha = false;
+ break;
+
+ case AlphaType::FullyTransparent:
+ break;
+ }
+ break;
+ }
+
+ previousStopAlpha = stopAlpha;
+ previousStopAlphaType = stopAlphaType;
+
+ ++stopIterator;
+ }
+
+ return uniformAlpha ? EmulatedAlphaPremuliplicationAnalysisResult::UseGradientAsIs : EmulatedAlphaPremuliplicationAnalysisResult::UseGradientWithAlphaTransforms;
+}
+
+static GradientColorStops alphaTransformStopsToEmulateAlphaPremuliplication(const GradientColorStops& stops)
+{
+ // The following is the set of transforms that can be performed on a color stop list to transform it from one used with a premuliplied alpha gradient
+ // implmentation to one used by with an un-premultiplied gradient implementation.
+
+ // ... Opaque -> Transparent -> Opaque ... ==> ... Opaque -> TRANSFORM{Transparent(PreviousChannels)} | ADDITION{Transparent(NextChannels)} -> Opaque ...
+ // ... Partial -> Transparent -> Partial ... ==> ... Partial -> TRANSFORM{Transparent(PreviousChannels)} | ADDITION{Transparent(NextChannels)} -> Partial ...
+ // ... Opaque -> Transparent -> Partial ... ==> ... Opaque -> TRANSFORM{Transparent(PreviousChannels)} | ADDITION{Transparent(NextChannels)} -> Partial ...
+ // ... Partial -> Transparent -> Opaque ... ==> ... Partial -> TRANSFORM{Transparent(PreviousChannels)} | ADDITION{Transparent(NextChannels)} -> Opaque ...
+ //
+ // ... Opaque -> Transparent -> Transparent -> Opaque ... ==> ... Opaque -> TRANSFORM{Transparent(PreviousChannels)} -> TRANSFORM{Transparent(NextChannels)} -> Opaque ...
+ // ... Partial -> Transparent -> Transparent -> Partial ... ==> ... Partial -> TRANSFORM{Transparent(PreviousChannels)} -> TRANSFORM{Transparent(NextChannels)} -> Partial ...
+ // ... Opaque -> Transparent -> Transparent -> Partial ... ==> ... Opaque -> TRANSFORM{Transparent(PreviousChannels)} -> TRANSFORM{Transparent(NextChannels)} -> Partial ...
+ // ... Partial -> Transparent -> Transparent -> Opaque ... ==> ... Partial -> TRANSFORM{Transparent(PreviousChannels)} -> TRANSFORM{Transparent(NextChannels)} -> Opaque ...
+ //
+ // ... Opaque -> Transparent -> Transparent -> Transparent -> Opaque ... ==> ... Opaque -> TRANSFORM{Transparent(PreviousChannels)} -> Transparent -> TRANSFORM{Transparent(NextChannels)} -> Opaque ...
+ // ... Partial -> Transparent -> Transparent -> Transparent -> Partial ... ==> ... Partial -> TRANSFORM{Transparent(PreviousChannels)} -> Transparent -> TRANSFORM{Transparent(NextChannels)} -> Partial ...
+ // ... Opaque -> Transparent -> Transparent -> Transparent -> Partial ... ==> ... Opaque -> TRANSFORM{Transparent(PreviousChannels)} -> Transparent -> TRANSFORM{Transparent(NextChannels)} -> Partial ...
+ // ... Partial -> Transparent -> Transparent -> Transparent -> Opaque ... ==> ... Partial -> TRANSFORM{Transparent(PreviousChannels)} -> Transparent -> TRANSFORM{Transparent(NextChannels)} -> Opaque ...
+ //
+ // [ Transparent -> Opaque ... ==> [ TRANSFORM{Transparent(NextChannels)} -> Opaque ...
+ // [ Transparent -> Partial ... ==> [ TRANSFORM{Transparent(NextChannels)} -> Partial ...
+ // [ Transparent -> Transparent ... ==> [ Transparent -> Transparent ...
+ //
+ // ... Opaque -> Transparent ] ==> ... Opaque -> TRANSFORM{Transparent(PreviousChannels)} ]
+ // ... Partial -> Transparent ] ==> ... Partial -> TRANSFORM{Transparent(PreviousChannels)} ]
+ // ... Transparent -> Transparent ] ==> ... Transparent -> Transparent ]
+
+ // For each stop the following actions are possible:
+ // - Default Append self
+ // - Steal previous Append previous.colorWithAlpha(0.0f)
+ // - Steal next Append next.colorWithAlpha(0.0f)
+ // - Split Append previous.colorWithAlpha(0.0f) + next.colorWithAlpha(0.0f)
+
+ ASSERT(stops.size() > 1);
+
+ // Special case when we only have two stops to avoid complicating the cases with more.
+ if (stops.size() == 2) {
+ // Illegal pairs (ruled out in analysis)
+ // Opaque -> Partial
+ // Partial -> Opaque
+ //
+ // Possible pairs
+ // Opaque -> Opaque (default, default)
+ // Partial -> Partial (default, default)
+ // Transparent -> Transparent (default, default)
+ // Opaque -> Transparent (default, steal previous)
+ // Partial -> Transparent (default, steal previous)
+ // Transparent -> Opaque (steals next, default)
+ // Transparent -> Partial (steals next, default)
+
+ GradientColorStops::StopVector result;
+ result.reserveInitialCapacity(2);
+
+ auto& stop1 = stops.stops()[0];
+ auto& stop2 = stops.stops()[1];
+
+ auto stop1AlphaType = classifyAlphaType(stop1.color.alphaAsFloat());
+ auto stop2AlphaType = classifyAlphaType(stop2.color.alphaAsFloat());
+
+ switch (stop1AlphaType) {
+ case AlphaType::Opaque:
+ case AlphaType::PartiallyTransparent:
+ // ACTION (stop1): Default.
+ result.uncheckedAppend(stop1);
+
+ switch (stop2AlphaType) {
+ case AlphaType::Opaque:
+ case AlphaType::PartiallyTransparent:
+ // ACTION (stop2): Default.
+ result.uncheckedAppend(stop2);
+ break;
+ case AlphaType::FullyTransparent:
+ // ACTION (stop2): Steal previous.
+ result.uncheckedAppend({ stop2.offset, stop1.color.colorWithAlpha(0.0f) });
+ break;
+ }
+
+ break;
+
+ case AlphaType::FullyTransparent:
+ switch (stop2AlphaType) {
+ case AlphaType::Opaque:
+ case AlphaType::PartiallyTransparent:
+ // ACTION (stop1): Steal next.
+ result.uncheckedAppend({ stop1.offset, stop2.color.colorWithAlpha(0.0f) });
+ break;
+ case AlphaType::FullyTransparent:
+ // ACTION (stop1): Default.
+ result.uncheckedAppend(stop1);
+ break;
+ }
+ // ACTION (stop2): Default.
+ result.uncheckedAppend(stop2);
+ break;
+ }
+
+ return GradientColorStops::Sorted { WTFMove(result) };
+ }
+
+ GradientColorStops::StopVector result;
+
+ // 1. Handle first stop.
+ //
+ // [first stop matters]
+ //
+ // Illegal pairs (ruled out in analysis)
+ // Opaque -> Partial
+ // Partial -> Opaque
+ //
+ // Possible pairs
+ // Opaque -> Opaque (default)
+ // Partial -> Partial (default)
+ // Transparent -> Transparent (default)
+ // Opaque -> Transparent (default)
+ // Partial -> Transparent (default)
+ // Transparent -> Opaque (steals next)
+ // Transparent -> Partial (steals next)
+
+ auto& firstStop = stops.stops()[0];
+ auto& secondStop = stops.stops()[1];
+
+ auto firstStopAlphaType = classifyAlphaType(firstStop.color.alphaAsFloat());
+ auto secondStopAlphaType = classifyAlphaType(secondStop.color.alphaAsFloat());
+
+ if (firstStopAlphaType == AlphaType::FullyTransparent && secondStopAlphaType != AlphaType::FullyTransparent) {
+ // ACTION: Steal next.
+ result.append({ firstStop.offset, secondStop.color.colorWithAlpha(0.0f) });
+ } else {
+ // ACTION: Default.
+ result.append(firstStop);
+ }
+
+ // 2. Handle middle stops.
+ //
+ // [middle stop matters]
+ //
+ // Illegal triplets (ruled out in analysis)
+ // Opaque -> Opaque -> Partial
+ // Opaque -> Partial -> Partial
+ // Opaque -> Partial -> Opaque
+ // Partial -> Opaque -> Opaque
+ // Partial -> Partial -> Opaque
+ // Partial -> Opaque -> Partial
+ //
+ // Possible triplets
+ // Opaque -> Opaque -> Opaque (default)
+ // Partial -> Partial -> Partial (default)
+ // Opaque -> Opaque -> Transparent (default)
+ // Opaque -> Partial -> Transparent (default)
+ // Partial -> Partial -> Transparent (default)
+ // Partial -> Opaque -> Transparent (default)
+ // Transparent -> Opaque -> Transparent (default)
+ // Transparent -> Partial -> Transparent (default)
+ // Transparent -> Transparent -> Transparent (default)
+ // Opaque -> Transparent -> Opaque (splits, steals previous + next)
+ // Opaque -> Transparent -> Partial (splits, steals previous + next)
+ // Partial -> Transparent -> Partial (splits, steals previous + next)
+ // Partial -> Transparent -> Opaque (splits, steals previous + next)
+ // Transparent -> Transparent -> Opaque (steals next)
+ // Transparent -> Transparent -> Partial (steals next)
+ // Opaque -> Transparent -> Transparent (steals previous)
+ // Partial -> Transparent -> Transparent (steals previous)
+
+ size_t previousStopIndex = 0;
+ size_t stopIndex = 1;
+ size_t nextStopIndex = 2;
+
+ for (; nextStopIndex < stops.size(); ++previousStopIndex, ++stopIndex, ++nextStopIndex) {
+ auto& previousStop = stops.stops()[previousStopIndex];
+ auto& stop = stops.stops()[stopIndex];
+ auto& nextStop = stops.stops()[nextStopIndex];
+
+ auto previousStopAlphaType = classifyAlphaType(previousStop.color.alphaAsFloat());
+ auto stopAlphaType = classifyAlphaType(stop.color.alphaAsFloat());
+ auto nextStopAlphaType = classifyAlphaType(nextStop.color.alphaAsFloat());
+
+ if (stopAlphaType == AlphaType::FullyTransparent) {
+ switch (previousStopAlphaType) {
+ case AlphaType::Opaque:
+ case AlphaType::PartiallyTransparent:
+ switch (nextStopAlphaType) {
+ case AlphaType::Opaque:
+ case AlphaType::PartiallyTransparent:
+ // ACTION: Split.
+ result.append({ stop.offset, previousStop.color.colorWithAlpha(0.0f) });
+ result.append({ stop.offset, nextStop.color.colorWithAlpha(0.0f) });
+ break;
+
+ case AlphaType::FullyTransparent:
+ // ACTION: Steal previous.
+ result.append({ stop.offset, previousStop.color.colorWithAlpha(0.0f) });
+ break;
+ }
+ break;
+
+ case AlphaType::FullyTransparent:
+ switch (nextStopAlphaType) {
+ case AlphaType::Opaque:
+ case AlphaType::PartiallyTransparent:
+ // ACTION: Steal next.
+ result.append({ stop.offset, nextStop.color.colorWithAlpha(0.0f) });
+ break;
+
+ case AlphaType::FullyTransparent:
+ // ACTION: Default.
+ result.append(stop);
+ break;
+ }
+ break;
+ }
+ } else {
+ // ACTION: Default.
+ result.append(stop);
+ }
+ }
+
+ ASSERT(nextStopIndex == stops.size());
+
+ // 3. Handle last stop.
+ //
+ // [last stop matters]
+ //
+ // Illegal pairs (ruled out in analysis phase)
+ // Opaque -> Partial
+ // Partial -> Opaque
+ //
+ // Possible pairs
+ // Opaque -> Opaque (default)
+ // Partial -> Partial (default)
+ // Transparent -> Transparent (default)
+ // Opaque -> Transparent (steals previous)
+ // Partial -> Transparent (steals previous)
+ // Transparent -> Opaque (default)
+ // Transparent -> Partial (default)
+
+ auto& secondToLastStop = stops.stops()[previousStopIndex];
+ auto& lastStop = stops.stops()[stopIndex];
+
+ auto secondToLastStopAlphaType = classifyAlphaType(secondToLastStop.color.alphaAsFloat());
+ auto lastStopAlphaType = classifyAlphaType(lastStop.color.alphaAsFloat());
+
+ if (lastStopAlphaType == AlphaType::FullyTransparent && secondToLastStopAlphaType != AlphaType::FullyTransparent) {
+ // ACTION: Steal previous.
+ result.append({ lastStop.offset, secondToLastStop.color.colorWithAlpha(0.0f) });
+ } else {
+ // ACTION: Default.
+ result.append(lastStop);
+ }
+
+ return GradientColorStops::Sorted { WTFMove(result) };
+}
+#endif
+
GradientRendererCG::Strategy GradientRendererCG::pickStrategy(ColorInterpolationMethod colorInterpolationMethod, const GradientColorStops& stops) const
{
return WTF::switchOn(colorInterpolationMethod.colorSpace,
@@ -51,8 +397,14 @@
#if HAVE(CORE_GRAPHICS_PREMULTIPLIED_INTERPOLATION_GRADIENT)
return makeGradient(colorInterpolationMethod, stops);
#else
- // FIXME: Use gradient strategy if none of the stops have alpha.
- return makeShading(colorInterpolationMethod, stops);
+ switch (analyzeColorStopsForEmulatedAlphaPremuliplicationOppertunity(stops)) {
+ case EmulatedAlphaPremuliplicationAnalysisResult::UseGradientAsIs:
+ return makeGradient({ ColorInterpolationMethod::SRGB { }, AlphaPremultiplication::Unpremultiplied }, stops);
+ case EmulatedAlphaPremuliplicationAnalysisResult::UseGradientWithAlphaTransforms:
+ return makeGradient({ ColorInterpolationMethod::SRGB { }, AlphaPremultiplication::Unpremultiplied }, alphaTransformStopsToEmulateAlphaPremuliplication(stops));
+ case EmulatedAlphaPremuliplicationAnalysisResult::UseShading:
+ return makeShading(colorInterpolationMethod, stops);
+ }
#endif
}
},