Title: [287310] trunk
Revision
287310
Author
wei...@apple.com
Date
2021-12-21 04:42:19 -0800 (Tue, 21 Dec 2021)

Log Message

Add support for premultiplied alpha interpolated gradients and defaulted off option to use them for CSS Gradients
https://bugs.webkit.org/show_bug.cgi?id=234492

Reviewed by Simon Fraser.

Source/WebCore:

Tests: fast/gradients/alpha-premultiplied.html
       fast/gradients/conic-gradient-alpha-unpremultiplied.html

Extracts use of CGGradientRef out of Gradient and into the new GradientRendererCG which also implements
a CGShadingRef based gradient drawing path. The GradientRendererCG picks which strategy, CGGradientRef
or CGShadingRef, based on the intepolation mode and capabilities of the underlying CoreGraphics. For
new enough CoreGraphics, both premultiplied and non-premultiplied alpha interpolation for sRGB output
are supported in the more optimizable CGGradientRef path, but on older systems we will fallback to a
a newly implemented CGShadingRef based implementation.

In addition to the platform level work in Gradient, this adds a new setting, CSSGradientPremultipliedAlphaInterpolationEnabled,
which defaults to off for now and switches what type of interpolation we use for CSS gradients. It
does not effect other gradient uses such as by canvas or SVG. Those will be enabled separately.

* Headers.cmake:
* PlatformAppleWin.cmake:
* PlatformMac.cmake:
* SourcesCocoa.txt:
* WebCore.xcodeproj/project.pbxproj:
Add new files.

* css/CSSGradientValue.cpp:
(WebCore::LinearGradientAdapter::normalizeStopsAndEndpointsOutsideRange):
(WebCore::RadialGradientAdapter::normalizeStopsAndEndpointsOutsideRange):
(WebCore::ConicGradientAdapter::normalizeStopsAndEndpointsOutsideRange):
(WebCore::CSSGradientValue::computeStops):
Switch to using the new interpolateColors() function from ColorInterpolation.h
which accurately takes into account the current interpolation mode. These
blends now match what the created gradient does.

* css/parser/CSSParserContext.cpp:
* css/parser/CSSParserContext.h:
Add support for querying CSSGradientPremultipliedAlphaInterpolationEnabled from the CSS parser.

* css/parser/CSSPropertyParserHelpers.cpp:
(WebCore::CSSPropertyParserHelpers::gradientAlphaPremultiplication):
(WebCore::CSSPropertyParserHelpers::consumeDeprecatedGradient):
(WebCore::CSSPropertyParserHelpers::consumeDeprecatedRadialGradient):
(WebCore::CSSPropertyParserHelpers::consumeRadialGradient):
(WebCore::CSSPropertyParserHelpers::consumeLinearGradient):
(WebCore::CSSPropertyParserHelpers::consumeConicGradient):
Depending on how CSSGradientPremultipliedAlphaInterpolationEnabled is set, use either premultiplied
or non-premultiplied interpolation for CSS gradients.

* platform/graphics/ColorComponents.h:
(WebCore::ColorComponents::size const):
Add size() function for use by the shading strategy.

* platform/graphics/ColorInterpolation.h:
(WebCore::preInterpolationNormalizationForComponent):
(WebCore::preInterpolationNormalization):
(WebCore::postInterpolationNormalizationForComponent):
(WebCore::postInterpolationNormalization):
(WebCore::interpolateColorComponents):
Update to use the name InterpolationMethodColorSpace consistenly for the color space part of the
interpolation method.

(WebCore::interpolateColors):
Add helper which takes and returns Color objects rather than the strongly typed color types,
automatically converting to the appropriate interpolation color space based on the provided
ColorInterpolationMethod object.

* platform/graphics/Gradient.h:
* platform/graphics/GradientColorStop.h: Added.
Move ColorStop and ColorStopVector defintions to new GradientColorStop file, but keep the
old nested names via using directives. This is needed to make using the ColorStopVector
from GradientRendererCG (which we also want to use in Gradient.h) possible without redeclaration.
Replace CGGradientRef member with GradientRendererCG which allows choosing between either
a CGGradientRef based implementation or CGShadingRef based one.

* platform/graphics/cg/GradientCG.cpp:
Update to use the GradientRendererCG rathern than a CGGradientRef directly, calling into it
to do the actual draw calls.

* platform/graphics/cg/GradientRendererCG.h: Added.
* platform/graphics/cg/GradientRendererCG.cpp: Added.
(WebCore::GradientRendererCG::pickStrategy const):
Central function to choose which strategy to use based on system capabilities and interpolation needs.

(WebCore::GradientRendererCG::makeGradient const):
Moved from GradientCG.cpp. Removed the CGGradientCreateWithColors() path, as we can always use the
more efficient CGGradientCreateWithColorComponents() by doing the conversion to extended sRGB ourselves.
Also adds use of the kCGGradientInterpolatesPremultiplied option on supported systems to tell CoreGraphics
to use premulitplied interpolation.

(WebCore::shadingFunction):
Core function used by CGShadingRef to interpolate between color stops. Templatized to allow for optimized
versions for every ColorInterpolationColorSpace / AlphaPremultiplication pair.

(WebCore::GradientRendererCG::makeShading const):
Builds shading strategy by converting all color stops to the interpolation color space, adding stops at
0 and 1 if necessary, and creating the CGFunctionRef that the shading will own. To avoid a circular
reference, the GradientRendererCG itself is not what the CGFunctionRef retains, but rather a subobject
Data, which is a peer to the CGFunctionRef.

(WebCore::GradientRendererCG::drawLinearGradient):
(WebCore::GradientRendererCG::drawRadialGradient):
(WebCore::GradientRendererCG::drawConicGradient):
Draw the gradient or shading based on the strategy selected at construction.

Source/WebCore/PAL:

* pal/spi/cg/CoreGraphicsSPI.h:
Add forwards for creating conic shadings and enabling premultiplied alpha interpolation for gradients.

Source/WebKitLegacy/win:

Add support for tests enabling the CSSGradientPremultipliedAlphaInterpolationEnabled preference.

* WebPreferences.cpp:
(WebPreferences::cssGradientPremultipliedAlphaInterpolationEnabled):
* WebPreferences.h:
* WebView.cpp:
(WebView::notifyPreferencesChanged):

Source/WTF:

* Scripts/Preferences/WebPreferencesExperimental.yaml:
Add a new experimental setting to enable premultiplied alpha CSS gradients.

Tools:

* DumpRenderTree/TestOptions.cpp:
(WTR::TestOptions::defaults):
Add default for Windows WebKitLegacy testing which still requires it.

LayoutTests:

Add and update tests for gradients with alpha now that we have support for premultiplied interpolation.
By default, the tests now enable premultiplied interpolation (since it is an experimental feature) so
to continue testing the old path, the setting must be explicitly disabled.

* fast/gradients/alpha-premultiplied-expected.html: Added.
* fast/gradients/alpha-premultiplied.html: Added.
* fast/gradients/conic-gradient-alpha-expected.html:
* fast/gradients/conic-gradient-alpha-unpremultiplied-expected.html: Added.
* fast/gradients/conic-gradient-alpha-unpremultiplied.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (287309 => 287310)


--- trunk/LayoutTests/ChangeLog	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/LayoutTests/ChangeLog	2021-12-21 12:42:19 UTC (rev 287310)
@@ -1,3 +1,20 @@
+2021-12-21  Sam Weinig  <wei...@apple.com>
+
+        Add support for premultiplied alpha interpolated gradients and defaulted off option to use them for CSS Gradients
+        https://bugs.webkit.org/show_bug.cgi?id=234492
+
+        Reviewed by Simon Fraser.
+
+        Add and update tests for gradients with alpha now that we have support for premultiplied interpolation.
+        By default, the tests now enable premultiplied interpolation (since it is an experimental feature) so
+        to continue testing the old path, the setting must be explicitly disabled.
+
+        * fast/gradients/alpha-premultiplied-expected.html: Added.
+        * fast/gradients/alpha-premultiplied.html: Added.
+        * fast/gradients/conic-gradient-alpha-expected.html:
+        * fast/gradients/conic-gradient-alpha-unpremultiplied-expected.html: Added.
+        * fast/gradients/conic-gradient-alpha-unpremultiplied.html: Added.
+
 2021-12-20  Jon Lee  <jon...@apple.com>
 
         Unreviewed, garden GPU Process test expectations

Added: trunk/LayoutTests/fast/gradients/alpha-premultiplied-expected.html (0 => 287310)


--- trunk/LayoutTests/fast/gradients/alpha-premultiplied-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/gradients/alpha-premultiplied-expected.html	2021-12-21 12:42:19 UTC (rev 287310)
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+    div { 
+        width: 100px;
+        height: 100px;
+    }
+
+    #linear {
+        background-image: linear-gradient(transparent, rgb(0, 0, 255));
+    }
+
+    #linear-repeating {
+        background-image: repeating-linear-gradient(transparent, rgb(0, 0, 255) 25px);
+    }
+
+    #radial {
+        background-image: radial-gradient(transparent, rgb(0, 0, 255));
+    }
+
+    #radial-repeating {
+        background-image: repeating-radial-gradient(transparent, rgb(0, 0, 255) 25px);
+    }
+
+    #conic {
+        background-image: conic-gradient(transparent, rgb(0, 0, 255));
+    }
+
+    #conic-repeating {
+        background-image: repeating-conic-gradient(transparent, rgb(0, 0, 255) 30deg);
+    }
+    
+</style>
+</head>
+<body>
+<div id="linear"></div>
+<div id="linear-repeating"></div>
+<div id="radial"></div>
+<div id="radial-repeating"></div>
+<div id="conic"></div>
+<div id="conic-repeating"></div>
+</body>
+</html>

Added: trunk/LayoutTests/fast/gradients/alpha-premultiplied.html (0 => 287310)


--- trunk/LayoutTests/fast/gradients/alpha-premultiplied.html	                        (rev 0)
+++ trunk/LayoutTests/fast/gradients/alpha-premultiplied.html	2021-12-21 12:42:19 UTC (rev 287310)
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+    div { 
+        width: 100px;
+        height: 100px;
+    }
+
+    #linear {
+        background-image: linear-gradient(rgba(255, 0, 255, 0), rgb(0, 0, 255));
+    }
+
+    #linear-repeating {
+        background-image: repeating-linear-gradient(rgba(255, 0, 255, 0), rgb(0, 0, 255) 25px);
+    }
+
+    #radial {
+        background-image: radial-gradient(rgba(255, 0, 255, 0), rgb(0, 0, 255));
+    }
+
+    #radial-repeating {
+        background-image: repeating-radial-gradient(rgba(255, 0, 255, 0), rgb(0, 0, 255) 25px);
+    }
+
+    #conic {
+        background-image: conic-gradient(rgba(255, 0, 255, 0), rgb(0, 0, 255));
+    }
+
+    #conic-repeating {
+        background-image: repeating-conic-gradient(rgba(255, 0, 255, 0), rgb(0, 0, 255) 30deg);
+    }
+    
+</style>
+</head>
+<body>
+<div id="linear"></div>
+<div id="linear-repeating"></div>
+<div id="radial"></div>
+<div id="radial-repeating"></div>
+<div id="conic"></div>
+<div id="conic-repeating"></div>
+</body>
+</html>

Modified: trunk/LayoutTests/fast/gradients/conic-gradient-alpha-expected.html (287309 => 287310)


--- trunk/LayoutTests/fast/gradients/conic-gradient-alpha-expected.html	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/LayoutTests/fast/gradients/conic-gradient-alpha-expected.html	2021-12-21 12:42:19 UTC (rev 287310)
@@ -10,27 +10,27 @@
         #topleft {
             width: 0; 
             height: 0; 
-            border-left: 100px solid rgb(64,87,64);
+            border-left: 100px solid rgb(64,64,64);
             border-top: 100px solid rgb(32,32,32);
         }
         #topright {
             width: 0; 
             height: 0; 
-            border-right: 100px solid rgb(225,246,225);
-            border-top: 100px solid rgb(254,254,254);
+            border-right: 100px solid rgb(224,224,224);
+            border-top: 100px solid rgb(255,255,255);
         }
         #bottomleft {
             clear: left;
             width: 0; 
             height: 0; 
-            border-left: 100px solid rgb(96,135,96);
-            border-bottom: 100px solid rgb(128,175,128);
+            border-left: 100px solid rgb(96,96,96);
+            border-bottom: 100px solid rgb(128,128,128);
         }
         #bottomright {
             width: 0; 
             height: 0; 
-            border-right: 100px solid rgb(193,231,193);
-            border-bottom: 100px solid rgb(160,207,160);
+            border-right: 100px solid rgb(192,192,192);
+            border-bottom: 100px solid rgb(160,160,160);
         }
         .box {
             position: absolute;

Copied: trunk/LayoutTests/fast/gradients/conic-gradient-alpha-unpremultiplied-expected.html (from rev 287309, trunk/LayoutTests/fast/gradients/conic-gradient-alpha-expected.html) (0 => 287310)


--- trunk/LayoutTests/fast/gradients/conic-gradient-alpha-unpremultiplied-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/gradients/conic-gradient-alpha-unpremultiplied-expected.html	2021-12-21 12:42:19 UTC (rev 287310)
@@ -0,0 +1,67 @@
+<html><!-- webkit-test-runner [ CSSGradientPremultipliedAlphaInterpolationEnabled=false ] -->
+<head>
+    <style>
+        div {
+            width: 200px;
+            height: 200px;
+            float: left;
+        }
+        /*tweak this until it's actually right... */
+        #topleft {
+            width: 0; 
+            height: 0; 
+            border-left: 100px solid rgb(64,87,64);
+            border-top: 100px solid rgb(32,32,32);
+        }
+        #topright {
+            width: 0; 
+            height: 0; 
+            border-right: 100px solid rgb(225,246,225);
+            border-top: 100px solid rgb(254,254,254);
+        }
+        #bottomleft {
+            clear: left;
+            width: 0; 
+            height: 0; 
+            border-left: 100px solid rgb(96,135,96);
+            border-bottom: 100px solid rgb(128,175,128);
+        }
+        #bottomright {
+            width: 0; 
+            height: 0; 
+            border-right: 100px solid rgb(193,231,193);
+            border-bottom: 100px solid rgb(160,207,160);
+        }
+        .box {
+            position: absolute;
+        }
+        .x {
+            position: absolute;
+            -webkit-clip-path: polygon(0% 0%, 0% 10%, 40% 50%, 0% 90%, 0% 100%, 10% 100%, 50% 60%, 90% 100%, 100% 100%, 100% 90%, 60% 50%, 100% 10%, 100% 0%, 90% 0%, 50% 40%, 10% 0%);
+        }
+        #x {
+            background-color:white;
+        }
+        .plus {
+            position: absolute;
+            -webkit-clip-path: polygon(45% 0%, 55% 0%, 55% 45%, 100% 45%, 100% 55%, 55% 55%, 55% 100%, 45% 100%, 45% 55%, 0% 55%, 0% 45%, 45% 45%);
+        }
+        #plus {
+            background-color:white;
+        }
+        
+    </style>
+</head>
+<body>
+    <div>
+        <div class="box">
+            <div id="topleft"></div>
+            <div id="topright"></div>
+            <div id="bottomleft"></div>
+            <div id="bottomright"></div>
+        </div>
+        <div id="x" class="x"></div>
+        <div id="plus" class="plus"></div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/fast/gradients/conic-gradient-alpha-unpremultiplied.html (0 => 287310)


--- trunk/LayoutTests/fast/gradients/conic-gradient-alpha-unpremultiplied.html	                        (rev 0)
+++ trunk/LayoutTests/fast/gradients/conic-gradient-alpha-unpremultiplied.html	2021-12-21 12:42:19 UTC (rev 287310)
@@ -0,0 +1,56 @@
+<html><!-- webkit-test-runner [ CSSGradientPremultipliedAlphaInterpolationEnabled=false ] -->
+<head>
+<style>
+    svg {
+        width: 800px;
+        display: none;
+    }
+    div {
+        width: 200px;
+        height: 200px;
+    }
+    #gradient {
+        position: absolute;
+        width: 200px;
+        height: 200px;
+        background-image: conic-gradient(rgba(0,255,0,0), black);
+        filter: url(#posterize);
+    }
+
+    .x {
+        position: absolute;
+        -webkit-clip-path: polygon(0% 0%, 0% 10%, 40% 50%, 0% 90%, 0% 100%, 10% 100%, 50% 60%, 90% 100%, 100% 100%, 100% 90%, 60% 50%, 100% 10%, 100% 0%, 90% 0%, 50% 40%, 10% 0%);
+    }
+    #x {
+        background-color:white;
+    }
+    .plus {
+        position: absolute;
+        -webkit-clip-path: polygon(45% 0%, 55% 0%, 55% 45%, 100% 45%, 100% 55%, 55% 55%, 55% 100%, 45% 100%, 45% 55%, 0% 55%, 0% 45%, 45% 45%);
+    }
+    #plus {
+        background-color:white;
+    }
+</style>
+</head>
+
+<body>
+<svg viewBox="0 0 700 100">
+<defs>
+    <filter id="posterize" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox">
+        <feComponentTransfer>
+            <feFuncR type="discrete" tableValues="0 0.125 0.25 0.375 0.5 0.625 0.75 0.875"/>
+            <feFuncG type="discrete" tableValues="0 0.125 0.25 0.375 0.5 0.625 0.75 0.875"/>
+            <feFuncB type="discrete" tableValues="0 0.125 0.25 0.375 0.5 0.625 0.75 0.875"/>
+            <feFuncA type="discrete" tableValues="0 0.125 0.25 0.375 0.5 0.625 0.75 0.875"/>
+        </feComponentTransfer>
+    </filter>
+</defs>
+</svg>
+
+<div>
+    <div id="gradient"></div>
+    <div id="x" class="x"></div>
+    <div id="plus" class="plus"></div>
+</div>
+</html>

Modified: trunk/LayoutTests/platform/glib/TestExpectations (287309 => 287310)


--- trunk/LayoutTests/platform/glib/TestExpectations	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/LayoutTests/platform/glib/TestExpectations	2021-12-21 12:42:19 UTC (rev 287310)
@@ -489,7 +489,9 @@
 
 # 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/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/LayoutTests/platform/win/TestExpectations (287309 => 287310)


--- trunk/LayoutTests/platform/win/TestExpectations	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/LayoutTests/platform/win/TestExpectations	2021-12-21 12:42:19 UTC (rev 287310)
@@ -295,6 +295,7 @@
 fast/gradients/conic-extended-stops.html [ Skip ]
 fast/gradients/conic-from-angle.html [ Skip ]
 fast/gradients/conic-repeating-last-stop.html [ Skip ]
+fast/gradients/conic-gradient-alpha-unpremultiplied.html [ Skip ]
 fast/gradients/conic-gradient-alpha.html [ Skip ]
 fast/gradients/conic-gradient-extended-stops.html [ Skip ]
 fast/gradients/conic-gradient.html [ Skip ]

Modified: trunk/Source/WTF/ChangeLog (287309 => 287310)


--- trunk/Source/WTF/ChangeLog	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WTF/ChangeLog	2021-12-21 12:42:19 UTC (rev 287310)
@@ -1,3 +1,13 @@
+2021-12-21  Sam Weinig  <wei...@apple.com>
+
+        Add support for premultiplied alpha interpolated gradients and defaulted off option to use them for CSS Gradients
+        https://bugs.webkit.org/show_bug.cgi?id=234492
+
+        Reviewed by Simon Fraser.
+
+        * Scripts/Preferences/WebPreferencesExperimental.yaml:
+        Add a new experimental setting to enable premultiplied alpha CSS gradients. 
+
 2021-12-21  Yusuke Suzuki  <ysuz...@apple.com>
 
         [WTF] Remove RefCountedArray and use RefCountedFixedVector

Modified: trunk/Source/WTF/Scripts/Preferences/WebPreferencesExperimental.yaml (287309 => 287310)


--- trunk/Source/WTF/Scripts/Preferences/WebPreferencesExperimental.yaml	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WTF/Scripts/Preferences/WebPreferencesExperimental.yaml	2021-12-21 12:42:19 UTC (rev 287310)
@@ -284,6 +284,18 @@
     WebCore:
       default: false
 
+CSSGradientPremultipliedAlphaInterpolationEnabled:
+  type: bool
+  humanReadableName: "CSS Gradient Premultiplied Alpha Interpolation"
+  humanReadableDescription: "Enable premultiplied alpha interpolated CSS gradients"
+  defaultValue:
+    WebKitLegacy:
+      default: false
+    WebKit:
+      default: false
+    WebCore:
+      default: false
+
 # FIXME: This is enabled when ENABLE(EXPERIMENTAL_FEATURES) is true in WebKit2. Perhaps we should consider using that for WebKitLegacy as well.
 CSSIndividualTransformPropertiesEnabled:
   type: bool

Modified: trunk/Source/WebCore/ChangeLog (287309 => 287310)


--- trunk/Source/WebCore/ChangeLog	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/ChangeLog	2021-12-21 12:42:19 UTC (rev 287310)
@@ -1,3 +1,110 @@
+2021-12-21  Sam Weinig  <wei...@apple.com>
+
+        Add support for premultiplied alpha interpolated gradients and defaulted off option to use them for CSS Gradients
+        https://bugs.webkit.org/show_bug.cgi?id=234492
+
+        Reviewed by Simon Fraser.
+
+        Tests: fast/gradients/alpha-premultiplied.html
+               fast/gradients/conic-gradient-alpha-unpremultiplied.html
+
+        Extracts use of CGGradientRef out of Gradient and into the new GradientRendererCG which also implements
+        a CGShadingRef based gradient drawing path. The GradientRendererCG picks which strategy, CGGradientRef
+        or CGShadingRef, based on the intepolation mode and capabilities of the underlying CoreGraphics. For 
+        new enough CoreGraphics, both premultiplied and non-premultiplied alpha interpolation for sRGB output
+        are supported in the more optimizable CGGradientRef path, but on older systems we will fallback to a 
+        a newly implemented CGShadingRef based implementation.
+
+        In addition to the platform level work in Gradient, this adds a new setting, CSSGradientPremultipliedAlphaInterpolationEnabled,
+        which defaults to off for now and switches what type of interpolation we use for CSS gradients. It
+        does not effect other gradient uses such as by canvas or SVG. Those will be enabled separately.
+
+        * Headers.cmake:
+        * PlatformAppleWin.cmake:
+        * PlatformMac.cmake:
+        * SourcesCocoa.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        Add new files.
+
+        * css/CSSGradientValue.cpp:
+        (WebCore::LinearGradientAdapter::normalizeStopsAndEndpointsOutsideRange):
+        (WebCore::RadialGradientAdapter::normalizeStopsAndEndpointsOutsideRange):
+        (WebCore::ConicGradientAdapter::normalizeStopsAndEndpointsOutsideRange):
+        (WebCore::CSSGradientValue::computeStops):
+        Switch to using the new interpolateColors() function from ColorInterpolation.h
+        which accurately takes into account the current interpolation mode. These
+        blends now match what the created gradient does.
+
+        * css/parser/CSSParserContext.cpp:
+        * css/parser/CSSParserContext.h:
+        Add support for querying CSSGradientPremultipliedAlphaInterpolationEnabled from the CSS parser.
+
+        * css/parser/CSSPropertyParserHelpers.cpp:
+        (WebCore::CSSPropertyParserHelpers::gradientAlphaPremultiplication):
+        (WebCore::CSSPropertyParserHelpers::consumeDeprecatedGradient):
+        (WebCore::CSSPropertyParserHelpers::consumeDeprecatedRadialGradient):
+        (WebCore::CSSPropertyParserHelpers::consumeRadialGradient):
+        (WebCore::CSSPropertyParserHelpers::consumeLinearGradient):
+        (WebCore::CSSPropertyParserHelpers::consumeConicGradient):
+        Depending on how CSSGradientPremultipliedAlphaInterpolationEnabled is set, use either premultiplied
+        or non-premultiplied interpolation for CSS gradients.
+
+        * platform/graphics/ColorComponents.h:
+        (WebCore::ColorComponents::size const):
+        Add size() function for use by the shading strategy.
+
+        * platform/graphics/ColorInterpolation.h:
+        (WebCore::preInterpolationNormalizationForComponent):
+        (WebCore::preInterpolationNormalization):
+        (WebCore::postInterpolationNormalizationForComponent):
+        (WebCore::postInterpolationNormalization):
+        (WebCore::interpolateColorComponents):
+        Update to use the name InterpolationMethodColorSpace consistenly for the color space part of the
+        interpolation method.
+  
+        (WebCore::interpolateColors):
+        Add helper which takes and returns Color objects rather than the strongly typed color types, 
+        automatically converting to the appropriate interpolation color space based on the provided
+        ColorInterpolationMethod object.
+
+        * platform/graphics/Gradient.h:
+        * platform/graphics/GradientColorStop.h: Added.
+        Move ColorStop and ColorStopVector defintions to new GradientColorStop file, but keep the
+        old nested names via using directives. This is needed to make using the ColorStopVector
+        from GradientRendererCG (which we also want to use in Gradient.h) possible without redeclaration.
+        Replace CGGradientRef member with GradientRendererCG which allows choosing between either
+        a CGGradientRef based implementation or CGShadingRef based one.
+
+        * platform/graphics/cg/GradientCG.cpp:
+        Update to use the GradientRendererCG rathern than a CGGradientRef directly, calling into it
+        to do the actual draw calls.
+
+        * platform/graphics/cg/GradientRendererCG.h: Added.
+        * platform/graphics/cg/GradientRendererCG.cpp: Added.
+        (WebCore::GradientRendererCG::pickStrategy const):
+        Central function to choose which strategy to use based on system capabilities and interpolation needs.
+
+        (WebCore::GradientRendererCG::makeGradient const):
+        Moved from GradientCG.cpp. Removed the CGGradientCreateWithColors() path, as we can always use the
+        more efficient CGGradientCreateWithColorComponents() by doing the conversion to extended sRGB ourselves.
+        Also adds use of the kCGGradientInterpolatesPremultiplied option on supported systems to tell CoreGraphics
+        to use premulitplied interpolation.
+
+        (WebCore::shadingFunction):
+        Core function used by CGShadingRef to interpolate between color stops. Templatized to allow for optimized
+        versions for every ColorInterpolationColorSpace / AlphaPremultiplication pair. 
+
+        (WebCore::GradientRendererCG::makeShading const):
+        Builds shading strategy by converting all color stops to the interpolation color space, adding stops at
+        0 and 1 if necessary, and creating the CGFunctionRef that the shading will own. To avoid a circular 
+        reference, the GradientRendererCG itself is not what the CGFunctionRef retains, but rather a subobject
+        Data, which is a peer to the CGFunctionRef.
+
+        (WebCore::GradientRendererCG::drawLinearGradient):
+        (WebCore::GradientRendererCG::drawRadialGradient):
+        (WebCore::GradientRendererCG::drawConicGradient):
+        Draw the gradient or shading based on the strategy selected at construction.
+
 2021-12-21  Tim Nguyen  <n...@apple.com>
 
         <dialog> should generate implied end tags

Modified: trunk/Source/WebCore/Headers.cmake (287309 => 287310)


--- trunk/Source/WebCore/Headers.cmake	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/Headers.cmake	2021-12-21 12:42:19 UTC (rev 287310)
@@ -1462,6 +1462,7 @@
     platform/graphics/GlyphMetricsMap.h
     platform/graphics/GlyphPage.h
     platform/graphics/Gradient.h
+    platform/graphics/GradientColorStop.h
     platform/graphics/GraphicsContext.h
     platform/graphics/GraphicsContextFlushIdentifier.h
     platform/graphics/GraphicsContextGL.h

Modified: trunk/Source/WebCore/PAL/ChangeLog (287309 => 287310)


--- trunk/Source/WebCore/PAL/ChangeLog	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/PAL/ChangeLog	2021-12-21 12:42:19 UTC (rev 287310)
@@ -1,3 +1,13 @@
+2021-12-21  Sam Weinig  <wei...@apple.com>
+
+        Add support for premultiplied alpha interpolated gradients and defaulted off option to use them for CSS Gradients
+        https://bugs.webkit.org/show_bug.cgi?id=234492
+
+        Reviewed by Simon Fraser.
+
+        * pal/spi/cg/CoreGraphicsSPI.h:
+        Add forwards for creating conic shadings and enabling premultiplied alpha interpolation for gradients.
+
 2021-12-20  Wenson Hsieh  <wenson_hs...@apple.com>
 
         Add PAL soft linking headers for CoreML and NaturalLanguage frameworks

Modified: trunk/Source/WebCore/PAL/pal/spi/cg/CoreGraphicsSPI.h (287309 => 287310)


--- trunk/Source/WebCore/PAL/pal/spi/cg/CoreGraphicsSPI.h	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/PAL/pal/spi/cg/CoreGraphicsSPI.h	2021-12-21 12:42:19 UTC (rev 287310)
@@ -41,8 +41,9 @@
 #include <CoreGraphics/CGContextDelegatePrivate.h>
 #include <CoreGraphics/CGFontCache.h>
 #include <CoreGraphics/CGPathPrivate.h>
+#include <CoreGraphics/CGShadingPrivate.h>
+#include <CoreGraphics/CGStylePrivate.h>
 #include <CoreGraphics/CoreGraphicsPrivate.h>
-#include <CoreGraphics/CGStylePrivate.h>
 
 #if PLATFORM(MAC)
 #include <CoreGraphics/CGAccessibility.h>
@@ -323,7 +324,17 @@
 void CGContextDrawConicGradient(CGContextRef, CGGradientRef, CGPoint center, CGFloat angle);
 void CGPathAddUnevenCornersRoundedRect(CGMutablePathRef, const CGAffineTransform *, CGRect, const CGSize corners[4]);
 bool CGFontRenderingGetFontSmoothingDisabled(void);
+CGShadingRef CGShadingCreateConic(CGColorSpaceRef, CGPoint center, CGFloat angle, CGFunctionRef);
 
+#if HAVE(CORE_GRAPHICS_GRADIENT_CREATE_WITH_OPTIONS)
+CGGradientRef CGGradientCreateWithColorComponentsAndOptions(CGColorSpaceRef, const CGFloat*, const CGFloat*, size_t, CFDictionaryRef);
+CGGradientRef CGGradientCreateWithColorsAndOptions(CGColorSpaceRef, CFArrayRef, const CGFloat*, CFDictionaryRef);
+#endif
+
+#if HAVE(CORE_GRAPHICS_PREMULTIPLIED_INTERPOLATION_GRADIENT)
+extern const CFStringRef kCGGradientInterpolatesPremultiplied;
+#endif
+
 #endif // PLATFORM(COCOA)
 
 #if PLATFORM(WIN)

Modified: trunk/Source/WebCore/PlatformAppleWin.cmake (287309 => 287310)


--- trunk/Source/WebCore/PlatformAppleWin.cmake	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/PlatformAppleWin.cmake	2021-12-21 12:42:19 UTC (rev 287310)
@@ -131,6 +131,7 @@
         platform/graphics/cg/FloatRectCG.cpp
         platform/graphics/cg/FloatSizeCG.cpp
         platform/graphics/cg/GradientCG.cpp
+        platform/graphics/cg/GradientRendererCG.cpp
         platform/graphics/cg/GraphicsContextGLCG.cpp
         platform/graphics/cg/GraphicsContextCG.cpp
         platform/graphics/cg/IOSurfacePool.cpp
@@ -182,6 +183,7 @@
         platform/graphics/ca/win/PlatformCALayerWin.h
 
         platform/graphics/cg/ColorSpaceCG.h
+        platform/graphics/cg/GradientRendererCG.h
         platform/graphics/cg/GraphicsContextCG.h
         platform/graphics/cg/IOSurfacePool.h
         platform/graphics/cg/ImageBufferCGBackend.h

Modified: trunk/Source/WebCore/PlatformMac.cmake (287309 => 287310)


--- trunk/Source/WebCore/PlatformMac.cmake	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/PlatformMac.cmake	2021-12-21 12:42:19 UTC (rev 287310)
@@ -291,6 +291,7 @@
     platform/graphics/cg/FloatRectCG.cpp
     platform/graphics/cg/FloatSizeCG.cpp
     platform/graphics/cg/GradientCG.cpp
+    platform/graphics/cg/GradientRendererCG.cpp
     platform/graphics/cg/GraphicsContextGLCG.cpp
     platform/graphics/cg/GraphicsContextCG.cpp
     platform/graphics/cg/IOSurfacePool.cpp

Modified: trunk/Source/WebCore/SourcesCocoa.txt (287309 => 287310)


--- trunk/Source/WebCore/SourcesCocoa.txt	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/SourcesCocoa.txt	2021-12-21 12:42:19 UTC (rev 287310)
@@ -348,6 +348,7 @@
 platform/graphics/cg/FloatRectCG.cpp
 platform/graphics/cg/FloatSizeCG.cpp
 platform/graphics/cg/GradientCG.cpp
+platform/graphics/cg/GradientRendererCG.cpp
 platform/graphics/cg/GraphicsContextCG.cpp
 platform/graphics/cg/GraphicsContextGLCG.cpp
 platform/graphics/cg/IOSurfacePool.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (287309 => 287310)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-12-21 12:42:19 UTC (rev 287310)
@@ -4087,6 +4087,8 @@
 		BC124F000C26447A009E2349 /* JSBarProp.h in Headers */ = {isa = PBXBuildFile; fileRef = BC124EFE0C26447A009E2349 /* JSBarProp.h */; };
 		BC128A73137C867C00CAC845 /* RenderGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = BC128A72137C867C00CAC845 /* RenderGrid.h */; };
 		BC14028B0E83680800319717 /* ScrollbarThemeComposite.h in Headers */ = {isa = PBXBuildFile; fileRef = BC1402890E83680800319717 /* ScrollbarThemeComposite.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		BC19CDF2276FFC6D0053F734 /* GradientRendererCG.h in Headers */ = {isa = PBXBuildFile; fileRef = BC19CDF0276FFB260053F734 /* GradientRendererCG.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		BC19CDF4277106390053F734 /* GradientColorStop.h in Headers */ = {isa = PBXBuildFile; fileRef = BC19CDF3277106390053F734 /* GradientColorStop.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		BC1A7D9818FCB5B000421879 /* RenderMultiColumnSpannerPlaceholder.h in Headers */ = {isa = PBXBuildFile; fileRef = BC1A7D9618FCB5B000421879 /* RenderMultiColumnSpannerPlaceholder.h */; };
 		BC2272870E82E70700E7F975 /* StyleReflection.h in Headers */ = {isa = PBXBuildFile; fileRef = BC2272860E82E70700E7F975 /* StyleReflection.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		BC2272A20E82E87C00E7F975 /* CursorData.h in Headers */ = {isa = PBXBuildFile; fileRef = BC2272A10E82E87C00E7F975 /* CursorData.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -15134,6 +15136,9 @@
 		BC128B00137C8D4600CAC845 /* RenderGrid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderGrid.cpp; sourceTree = "<group>"; };
 		BC1402880E83680800319717 /* ScrollbarThemeComposite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollbarThemeComposite.cpp; sourceTree = "<group>"; };
 		BC1402890E83680800319717 /* ScrollbarThemeComposite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollbarThemeComposite.h; sourceTree = "<group>"; };
+		BC19CDEF276FFB260053F734 /* GradientRendererCG.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GradientRendererCG.cpp; sourceTree = "<group>"; };
+		BC19CDF0276FFB260053F734 /* GradientRendererCG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GradientRendererCG.h; sourceTree = "<group>"; };
+		BC19CDF3277106390053F734 /* GradientColorStop.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GradientColorStop.h; sourceTree = "<group>"; };
 		BC1A7D9518FCB5B000421879 /* RenderMultiColumnSpannerPlaceholder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderMultiColumnSpannerPlaceholder.cpp; sourceTree = "<group>"; };
 		BC1A7D9618FCB5B000421879 /* RenderMultiColumnSpannerPlaceholder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderMultiColumnSpannerPlaceholder.h; sourceTree = "<group>"; };
 		BC20FB7E0C0E8E6C00D1447F /* JSDeprecatedCSSOMValueCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSDeprecatedCSSOMValueCustom.cpp; sourceTree = "<group>"; };
@@ -28571,6 +28576,8 @@
 				B275352C0B053814002CE64F /* FloatRectCG.cpp */,
 				B275352D0B053814002CE64F /* FloatSizeCG.cpp */,
 				BC53C60A0DA56CF10021EB5D /* GradientCG.cpp */,
+				BC19CDEF276FFB260053F734 /* GradientRendererCG.cpp */,
+				BC19CDF0276FFB260053F734 /* GradientRendererCG.h */,
 				B2ED97700B1F55CE00257D0F /* GraphicsContextCG.cpp */,
 				934907E3125BBBC8007F23A0 /* GraphicsContextCG.h */,
 				6E21C6C11126339900A7BE02 /* GraphicsContextGLCG.cpp */,
@@ -28780,6 +28787,7 @@
 				0873B86A136064EA00A522C2 /* GlyphPage.h */,
 				BC53C6070DA56C570021EB5D /* Gradient.cpp */,
 				BC53C5F40DA56B920021EB5D /* Gradient.h */,
+				BC19CDF3277106390053F734 /* GradientColorStop.h */,
 				2D2FC0561460CD6F00263633 /* GradientImage.cpp */,
 				2D2FC0571460CD6F00263633 /* GradientImage.h */,
 				B2A015920AF6CD53006BCE0E /* GraphicsContext.cpp */,
@@ -33341,6 +33349,7 @@
 				8378878224D8A609000D4A5B /* AudioBufferSourceOptions.h in Headers */,
 				FD31607C12B026F700C1A359 /* AudioBus.h in Headers */,
 				FD31607E12B026F700C1A359 /* AudioChannel.h in Headers */,
+				BC19CDF2276FFC6D0053F734 /* GradientRendererCG.h in Headers */,
 				CD3EEF3A25799F88006563BB /* AudioConfiguration.h in Headers */,
 				FD31600512B0267600C1A359 /* AudioContext.h in Headers */,
 				E785D96224B7F7350014DB21 /* AudioContextLatencyCategory.h in Headers */,
@@ -34897,6 +34906,7 @@
 				A5B81CAD1FAA44620037D1E6 /* InspectorDOMStorageAgent.h in Headers */,
 				994C603A253A277300BDF060 /* InspectorFrontendAPIDispatcher.h in Headers */,
 				F344C7141125B82C00F26EEE /* InspectorFrontendClient.h in Headers */,
+				BC19CDF4277106390053F734 /* GradientColorStop.h in Headers */,
 				F344C75311294D9D00F26EEE /* InspectorFrontendClientLocal.h in Headers */,
 				7A0E770F10C00A8800A0276E /* InspectorFrontendHost.h in Headers */,
 				7A54858014E02D51006AE05A /* InspectorHistory.h in Headers */,

Modified: trunk/Source/WebCore/css/CSSGradientValue.cpp (287309 => 287310)


--- trunk/Source/WebCore/css/CSSGradientValue.cpp	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/css/CSSGradientValue.cpp	2021-12-21 12:42:19 UTC (rev 287310)
@@ -26,11 +26,10 @@
 #include "config.h"
 #include "CSSGradientValue.h"
 
-#include "AnimationUtilities.h"
 #include "CSSCalcValue.h"
 #include "CSSToLengthConversionData.h"
 #include "CSSValueKeywords.h"
-#include "ColorBlending.h"
+#include "ColorInterpolation.h"
 #include "GradientImage.h"
 #include "NodeRenderStyle.h"
 #include "Pair.h"
@@ -138,7 +137,7 @@
     }
     float maxExtent(float, float) const { return 1; }
 
-    void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
+    void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops, ColorInterpolationMethod)
     {
         float firstOffset = *stops.first().offset;
         float lastOffset = *stops.last().offset;
@@ -181,7 +180,7 @@
         return 1;
     }
 
-    void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
+    void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops, ColorInterpolationMethod colorInterpolationMethod)
     {
         auto numStops = stops.size();
 
@@ -205,8 +204,7 @@
                 float nextOffset = *stops[firstZeroOrGreaterIndex].offset;
 
                 float interStopProportion = -prevOffset / (nextOffset - prevOffset);
-                // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
-                Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, { interStopProportion });
+                auto blendedColor = interpolateColors(colorInterpolationMethod, stops[firstZeroOrGreaterIndex - 1].color, 1.0f - interStopProportion, stops[firstZeroOrGreaterIndex].color, interStopProportion);
 
                 // Clamp the positions to 0 and set the color.
                 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
@@ -236,7 +234,7 @@
     float gradientLength() const { return 1; }
     float maxExtent(float, float) const { return 1; }
 
-    void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
+    void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops, ColorInterpolationMethod colorInterpolationMethod)
     {
         size_t numStops = stops.size();
         size_t lastStopIndex = numStops - 1;
@@ -256,8 +254,7 @@
                 float nextOffset = *stops[index].offset;
 
                 float interStopProportion = -previousOffset / (nextOffset - previousOffset);
-                // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
-                Color blendedColor = blend(stops[index - 1].color, stops[index].color, { interStopProportion });
+                auto blendedColor = interpolateColors(colorInterpolationMethod, stops[index - 1].color, 1.0f - interStopProportion, stops[index].color, interStopProportion);
 
                 // Clamp the positions to 0 and set the color.
                 for (size_t i = 0; i < index; ++i) {
@@ -286,8 +283,7 @@
                 float nextOffset = *stops[index + 1].offset;
 
                 float interStopProportion = (1 - previousOffset) / (nextOffset - previousOffset);
-                // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
-                Color blendedColor = blend(stops[index].color, stops[index + 1].color, { interStopProportion });
+                auto blendedColor = interpolateColors(colorInterpolationMethod, stops[index].color, 1.0f - interStopProportion, stops[index + 1].color, interStopProportion);
 
                 // Clamp the positions to 1 and set the color.
                 for (size_t i = index + 1; i < numStops; ++i) {
@@ -485,8 +481,7 @@
         for (size_t y = 0; y < 9; ++y) {
             float relativeOffset = (*newStops[y].offset - offset1) / (offset2 - offset1);
             float multiplier = std::pow(relativeOffset, std::log(.5f) / std::log(midpoint));
-            // FIXME: Why not premultiply here?
-            newStops[y].color = blendWithoutPremultiply(color1, color2, { multiplier });
+            newStops[y].color = interpolateColors(m_colorInterpolationMethod, color1, 1.0f - multiplier, color2, multiplier);
         }
 
         stops.remove(x);
@@ -557,7 +552,7 @@
 
     // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and adjusting the stops.
     if (stops.size() > 1 && (*stops.first().offset < 0 || *stops.last().offset > 1))
-        gradientAdapter.normalizeStopsAndEndpointsOutsideRange(stops);
+        gradientAdapter.normalizeStopsAndEndpointsOutsideRange(stops, m_colorInterpolationMethod);
     
     Gradient::ColorStopVector result;
     result.reserveInitialCapacity(stops.size());

Modified: trunk/Source/WebCore/css/parser/CSSParserContext.cpp (287309 => 287310)


--- trunk/Source/WebCore/css/parser/CSSParserContext.cpp	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/css/parser/CSSParserContext.cpp	2021-12-21 12:42:19 UTC (rev 287310)
@@ -106,10 +106,11 @@
     , hasPseudoClassEnabled { document.settings().hasPseudoClassEnabled() }
     , cascadeLayersEnabled { document.settings().cssCascadeLayersEnabled() }
     , containerQueriesEnabled { document.settings().cssContainerQueriesEnabled() }
+    , overflowClipEnabled { document.settings().overflowClipEnabled() }
+    , gradientPremultipliedAlphaInterpolationEnabled { document.settings().cssGradientPremultipliedAlphaInterpolationEnabled() }
 #if ENABLE(ATTACHMENT_ELEMENT)
     , attachmentEnabled { RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled() }
 #endif
-    , overflowClipEnabled { document.settings().overflowClipEnabled() }
 {
 }
 
@@ -153,10 +154,11 @@
         && a.hasPseudoClassEnabled == b.hasPseudoClassEnabled
         && a.cascadeLayersEnabled == b.cascadeLayersEnabled
         && a.containerQueriesEnabled == b.containerQueriesEnabled
+        && a.overflowClipEnabled == b.overflowClipEnabled
+        && a.gradientPremultipliedAlphaInterpolationEnabled == b.gradientPremultipliedAlphaInterpolationEnabled
 #if ENABLE(ATTACHMENT_ELEMENT)
         && a.attachmentEnabled == b.attachmentEnabled
 #endif
-        && a.overflowClipEnabled == b.overflowClipEnabled
     ;
 }
 
@@ -193,12 +195,13 @@
         | context.hasPseudoClassEnabled                     << 22
         | context.cascadeLayersEnabled                      << 23
         | context.containerQueriesEnabled                   << 24
+        | context.overflowClipEnabled                       << 25
+        | context.gradientPremultipliedAlphaInterpolationEnabled << 26
 #if ENABLE(ATTACHMENT_ELEMENT)
-        | context.attachmentEnabled                         << 25
+        | context.attachmentEnabled                         << 27
 #endif
-        | context.overflowClipEnabled                       << 26
-        | context.accentColorEnabled                        << 27
-        | context.mode                                      << 28; // This is multiple bits, so keep it last.
+        | context.accentColorEnabled                        << 28
+        | context.mode                                      << 29; // This is multiple bits, so keep it last.
     add(hasher, context.baseURL, context.charset, bits);
 }
 

Modified: trunk/Source/WebCore/css/parser/CSSParserContext.h (287309 => 287310)


--- trunk/Source/WebCore/css/parser/CSSParserContext.h	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/css/parser/CSSParserContext.h	2021-12-21 12:42:19 UTC (rev 287310)
@@ -85,6 +85,8 @@
     bool hasPseudoClassEnabled { false };
     bool cascadeLayersEnabled { false };
     bool containerQueriesEnabled { false };
+    bool overflowClipEnabled { false };
+    bool gradientPremultipliedAlphaInterpolationEnabled { false };
 
     // RuntimeEnabledFeatures.
 #if ENABLE(ATTACHMENT_ELEMENT)
@@ -91,8 +93,6 @@
     bool attachmentEnabled { false };
 #endif
 
-    bool overflowClipEnabled { false };
-
     CSSParserContext(CSSParserMode, const URL& baseURL = URL());
     WEBCORE_EXPORT CSSParserContext(const Document&, const URL& baseURL = URL(), const String& charset = emptyString());
     bool isPropertyRuntimeDisabled(CSSPropertyID) const;

Modified: trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp (287309 => 287310)


--- trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp	2021-12-21 12:42:19 UTC (rev 287310)
@@ -2439,24 +2439,6 @@
     return result;
 }
 
-// After interpolation, if the color is in the HWB or HSL color space, it needs to be canonicalized back to sRGB as
-// is done at parse time.
-
-template<typename ColorType> Color makeCanonicalColor(ColorType color)
-{
-    return color;
-}
-
-template<> Color makeCanonicalColor<HWBA<float>>(HWBA<float> color)
-{
-    return convertColor<SRGBA<uint8_t>>(color);
-}
-
-template<> Color makeCanonicalColor<HSLA<float>>(HSLA<float> color)
-{
-    return convertColor<SRGBA<uint8_t>>(color);
-}
-
 template<typename InterpolationMethod> static Color mixColorComponentsUsingColorInterpolationMethod(InterpolationMethod interpolationMethod, ColorMixPercentages mixPercentages, const Color& color1, const Color& color2)
 {
     using ColorType = typename InterpolationMethod::ColorType;
@@ -2946,6 +2928,11 @@
     return stop.color && args.atEnd();
 }
 
+static AlphaPremultiplication gradientAlphaPremultiplication(const CSSParserContext& context)
+{
+    return context.gradientPremultipliedAlphaInterpolationEnabled ? AlphaPremultiplication::Premultiplied : AlphaPremultiplication::Unpremultiplied;
+}
+
 static RefPtr<CSSValue> consumeDeprecatedGradient(CSSParserTokenRange& args, const CSSParserContext& context)
 {
     RefPtr<CSSGradientValue> result;
@@ -2952,9 +2939,9 @@
     CSSValueID id = args.consumeIncludingWhitespace().id();
     bool isDeprecatedRadialGradient = (id == CSSValueRadial);
     if (isDeprecatedRadialGradient)
-        result = CSSRadialGradientValue::create(NonRepeating, CSSDeprecatedRadialGradient, { ColorInterpolationMethod::SRGB { }, AlphaPremultiplication::Unpremultiplied });
+        result = CSSRadialGradientValue::create(NonRepeating, CSSDeprecatedRadialGradient, { ColorInterpolationMethod::SRGB { }, gradientAlphaPremultiplication(context) });
     else if (id == CSSValueLinear)
-        result = CSSLinearGradientValue::create(NonRepeating, CSSDeprecatedLinearGradient, { ColorInterpolationMethod::SRGB { }, AlphaPremultiplication::Unpremultiplied });
+        result = CSSLinearGradientValue::create(NonRepeating, CSSDeprecatedLinearGradient, { ColorInterpolationMethod::SRGB { }, gradientAlphaPremultiplication(context) });
     if (!result || !consumeCommaIncludingWhitespace(args))
         return nullptr;
 
@@ -3054,7 +3041,7 @@
 
 static RefPtr<CSSValue> consumeDeprecatedRadialGradient(CSSParserTokenRange& args, const CSSParserContext& context, CSSGradientRepeat repeating)
 {
-    auto result = CSSRadialGradientValue::create(repeating, CSSPrefixedRadialGradient, { ColorInterpolationMethod::SRGB { }, AlphaPremultiplication::Unpremultiplied });
+    auto result = CSSRadialGradientValue::create(repeating, CSSPrefixedRadialGradient, { ColorInterpolationMethod::SRGB { }, gradientAlphaPremultiplication(context) });
 
     auto centerCoordinate = consumeOneOrTwoValuedPositionCoordinates(args, context.mode, UnitlessQuirk::Forbid);
     if (centerCoordinate && !consumeCommaIncludingWhitespace(args))
@@ -3098,7 +3085,7 @@
 
 static RefPtr<CSSValue> consumeRadialGradient(CSSParserTokenRange& args, const CSSParserContext& context, CSSGradientRepeat repeating)
 {
-    RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient, { ColorInterpolationMethod::SRGB { }, AlphaPremultiplication::Unpremultiplied });
+    RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient, { ColorInterpolationMethod::SRGB { }, gradientAlphaPremultiplication(context) });
 
     RefPtr<CSSPrimitiveValue> shape;
     RefPtr<CSSPrimitiveValue> sizeKeyword;
@@ -3186,7 +3173,7 @@
 
 static RefPtr<CSSValue> consumeLinearGradient(CSSParserTokenRange& args, const CSSParserContext& context, CSSGradientRepeat repeating, CSSGradientType gradientType)
 {
-    RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, gradientType, { ColorInterpolationMethod::SRGB { }, AlphaPremultiplication::Unpremultiplied });
+    RefPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, gradientType, { ColorInterpolationMethod::SRGB { }, gradientAlphaPremultiplication(context) });
 
     bool expectComma = true;
     RefPtr<CSSPrimitiveValue> angle = consumeAngle(args, context.mode, UnitlessQuirk::Forbid, UnitlessZeroQuirk::Allow);
@@ -3220,7 +3207,7 @@
 static RefPtr<CSSValue> consumeConicGradient(CSSParserTokenRange& args, const CSSParserContext& context, CSSGradientRepeat repeating)
 {
 #if ENABLE(CSS_CONIC_GRADIENTS)
-    RefPtr<CSSConicGradientValue> result = CSSConicGradientValue::create(repeating, { ColorInterpolationMethod::SRGB { }, AlphaPremultiplication::Unpremultiplied });
+    RefPtr<CSSConicGradientValue> result = CSSConicGradientValue::create(repeating, { ColorInterpolationMethod::SRGB { }, gradientAlphaPremultiplication(context) });
 
     bool expectComma = false;
     if (args.peek().type() == IdentToken) {

Modified: trunk/Source/WebCore/platform/graphics/ColorComponents.h (287309 => 287310)


--- trunk/Source/WebCore/platform/graphics/ColorComponents.h	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/platform/graphics/ColorComponents.h	2021-12-21 12:42:19 UTC (rev 287310)
@@ -68,6 +68,8 @@
     template<size_t Start, size_t End>
     constexpr ColorComponents<T, End - Start> subset() const;
 
+    constexpr size_t size() const { return Size; }
+
     std::array<T, N> components;
 };
 

Modified: trunk/Source/WebCore/platform/graphics/ColorInterpolation.h (287309 => 287310)


--- trunk/Source/WebCore/platform/graphics/ColorInterpolation.h	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/platform/graphics/ColorInterpolation.h	2021-12-21 12:42:19 UTC (rev 287310)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "AlphaPremultiplication.h"
+#include "Color.h"
 #include "ColorInterpolationMethod.h"
 #include "ColorNormalization.h"
 #include "ColorTypes.h"
@@ -36,14 +37,14 @@
 
 std::pair<float, float> fixupHueComponentsPriorToInterpolation(HueInterpolationMethod, float, float);
 
-template<size_t I, AlphaPremultiplication alphaPremultiplication, typename InterpolationMethod>
-std::pair<float, float> preInterpolationNormalizationForComponent(InterpolationMethod interpolationMethod, ColorComponents<float, 4> colorComponents1, ColorComponents<float, 4> colorComponents2)
+template<size_t I, AlphaPremultiplication alphaPremultiplication, typename InterpolationMethodColorSpace>
+std::pair<float, float> preInterpolationNormalizationForComponent(InterpolationMethodColorSpace interpolationMethodColorSpace, ColorComponents<float, 4> colorComponents1, ColorComponents<float, 4> colorComponents2)
 {
-    using ColorType = typename InterpolationMethod::ColorType;
+    using ColorType = typename InterpolationMethodColorSpace::ColorType;
     constexpr auto componentInfo = ColorType::Model::componentInfo;
 
     if constexpr (componentInfo[I].type == ColorComponentType::Angle)
-        return fixupHueComponentsPriorToInterpolation(interpolationMethod.hueInterpolationMethod, colorComponents1[I], colorComponents2[I]);
+        return fixupHueComponentsPriorToInterpolation(interpolationMethodColorSpace.hueInterpolationMethod, colorComponents1[I], colorComponents2[I]);
     else {
         if constexpr (alphaPremultiplication == AlphaPremultiplication::Premultiplied)
             return { colorComponents1[I] * colorComponents1[3], colorComponents2[I] * colorComponents2[3] };
@@ -52,12 +53,12 @@
     }
 }
 
-template<AlphaPremultiplication alphaPremultiplication, typename InterpolationMethod>
-std::pair<ColorComponents<float, 4>, ColorComponents<float, 4>> preInterpolationNormalization(InterpolationMethod interpolationMethod, ColorComponents<float, 4> colorComponents1, ColorComponents<float, 4> colorComponents2)
+template<AlphaPremultiplication alphaPremultiplication, typename InterpolationMethodColorSpace>
+std::pair<ColorComponents<float, 4>, ColorComponents<float, 4>> preInterpolationNormalization(InterpolationMethodColorSpace interpolationMethodColorSpace, ColorComponents<float, 4> colorComponents1, ColorComponents<float, 4> colorComponents2)
 {
-    auto [colorA0, colorB0] = preInterpolationNormalizationForComponent<0, alphaPremultiplication>(interpolationMethod, colorComponents1, colorComponents2);
-    auto [colorA1, colorB1] = preInterpolationNormalizationForComponent<1, alphaPremultiplication>(interpolationMethod, colorComponents1, colorComponents2);
-    auto [colorA2, colorB2] = preInterpolationNormalizationForComponent<2, alphaPremultiplication>(interpolationMethod, colorComponents1, colorComponents2);
+    auto [colorA0, colorB0] = preInterpolationNormalizationForComponent<0, alphaPremultiplication>(interpolationMethodColorSpace, colorComponents1, colorComponents2);
+    auto [colorA1, colorB1] = preInterpolationNormalizationForComponent<1, alphaPremultiplication>(interpolationMethodColorSpace, colorComponents1, colorComponents2);
+    auto [colorA2, colorB2] = preInterpolationNormalizationForComponent<2, alphaPremultiplication>(interpolationMethodColorSpace, colorComponents1, colorComponents2);
 
     return {
         { colorA0, colorA1, colorA2, colorComponents1[3] },
@@ -68,10 +69,10 @@
 
 // MARK: - Post-interpolation normalization/fixup
 
-template<size_t I, AlphaPremultiplication alphaPremultiplication, typename InterpolationMethod>
-float postInterpolationNormalizationForComponent(InterpolationMethod, ColorComponents<float, 4> colorComponents)
+template<size_t I, AlphaPremultiplication alphaPremultiplication, typename InterpolationMethodColorSpace>
+float postInterpolationNormalizationForComponent(InterpolationMethodColorSpace, ColorComponents<float, 4> colorComponents)
 {
-    using ColorType = typename InterpolationMethod::ColorType;
+    using ColorType = typename InterpolationMethodColorSpace::ColorType;
     constexpr auto componentInfo = ColorType::Model::componentInfo;
 
     if constexpr (componentInfo[I].type != ColorComponentType::Angle && alphaPremultiplication == AlphaPremultiplication::Premultiplied) {
@@ -82,13 +83,13 @@
         return colorComponents[I];
 }
 
-template<AlphaPremultiplication alphaPremultiplication, typename InterpolationMethod>
-ColorComponents<float, 4> postInterpolationNormalization(InterpolationMethod interpolationMethod, ColorComponents<float, 4> colorComponents)
+template<AlphaPremultiplication alphaPremultiplication, typename InterpolationMethodColorSpace>
+ColorComponents<float, 4> postInterpolationNormalization(InterpolationMethodColorSpace interpolationMethodColorSpace, ColorComponents<float, 4> colorComponents)
 {
     return {
-        postInterpolationNormalizationForComponent<0, alphaPremultiplication>(interpolationMethod, colorComponents),
-        postInterpolationNormalizationForComponent<1, alphaPremultiplication>(interpolationMethod, colorComponents),
-        postInterpolationNormalizationForComponent<2, alphaPremultiplication>(interpolationMethod, colorComponents),
+        postInterpolationNormalizationForComponent<0, alphaPremultiplication>(interpolationMethodColorSpace, colorComponents),
+        postInterpolationNormalizationForComponent<1, alphaPremultiplication>(interpolationMethodColorSpace, colorComponents),
+        postInterpolationNormalizationForComponent<2, alphaPremultiplication>(interpolationMethodColorSpace, colorComponents),
         colorComponents[3]
     };
  }
@@ -96,11 +97,11 @@
 
 // MARK: - Interpolation
 
-template<AlphaPremultiplication alphaPremultiplication, typename InterpolationMethod>
-typename InterpolationMethod::ColorType interpolateColorComponents(InterpolationMethod interpolationMethod, typename InterpolationMethod::ColorType color1, double color1Multiplier, typename InterpolationMethod::ColorType color2, double color2Multiplier)
+template<AlphaPremultiplication alphaPremultiplication, typename InterpolationMethodColorSpace>
+typename InterpolationMethodColorSpace::ColorType interpolateColorComponents(InterpolationMethodColorSpace interpolationMethodColorSpace, typename InterpolationMethodColorSpace::ColorType color1, double color1Multiplier, typename InterpolationMethodColorSpace::ColorType color2, double color2Multiplier)
 {
     // 1. Apply pre-interpolation transforms (hue fixup for polar color spaces, alpha premultiplication if required).
-    auto [normalizedColorComponents1, normalizedColorComponents2] = preInterpolationNormalization<alphaPremultiplication>(interpolationMethod, asColorComponents(color1), asColorComponents(color2));
+    auto [normalizedColorComponents1, normalizedColorComponents2] = preInterpolationNormalization<alphaPremultiplication>(interpolationMethodColorSpace, asColorComponents(color1), asColorComponents(color2));
 
     // 2. Interpolate using the normalized components.
     auto interpolatedColorComponents = mapColorComponents([&] (auto componentFromColor1, auto componentFromColor2) -> float {
@@ -108,10 +109,25 @@
     }, normalizedColorComponents1, normalizedColorComponents2);
 
     // 3. Apply post-interpolation trasforms (alpha un-premultiplication if required).
-    auto normalizedInterpolatedColorComponents = postInterpolationNormalization<alphaPremultiplication>(interpolationMethod, interpolatedColorComponents);
+    auto normalizedInterpolatedColorComponents = postInterpolationNormalization<alphaPremultiplication>(interpolationMethodColorSpace, interpolatedColorComponents);
 
     // 4. Create color type from components, normalizing any components that may be out of range.
-    return makeColorTypeByNormalizingComponents<typename InterpolationMethod::ColorType>(normalizedInterpolatedColorComponents);
+    return makeColorTypeByNormalizingComponents<typename InterpolationMethodColorSpace::ColorType>(normalizedInterpolatedColorComponents);
 }
 
+inline Color interpolateColors(ColorInterpolationMethod colorInterpolationMethod, Color color1, double color1Multiplier, Color color2, double color2Multiplier)
+{
+    return WTF::switchOn(colorInterpolationMethod.colorSpace,
+        [&] (auto& colorSpace) {
+            using ColorType = typename std::remove_reference_t<decltype(colorSpace)>::ColorType;
+            switch (colorInterpolationMethod.alphaPremultiplication) {
+            case AlphaPremultiplication::Premultiplied:
+                return makeCanonicalColor(interpolateColorComponents<AlphaPremultiplication::Premultiplied>(colorSpace, color1.toColorTypeLossy<ColorType>(), color1Multiplier, color2.toColorTypeLossy<ColorType>(), color2Multiplier));
+            case AlphaPremultiplication::Unpremultiplied:
+                return makeCanonicalColor(interpolateColorComponents<AlphaPremultiplication::Unpremultiplied>(colorSpace, color1.toColorTypeLossy<ColorType>(), color1Multiplier, color2.toColorTypeLossy<ColorType>(), color2Multiplier));
+            }
+        }
+    );
 }
+
+}

Modified: trunk/Source/WebCore/platform/graphics/ColorNormalization.h (287309 => 287310)


--- trunk/Source/WebCore/platform/graphics/ColorNormalization.h	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/platform/graphics/ColorNormalization.h	2021-12-21 12:42:19 UTC (rev 287310)
@@ -25,6 +25,8 @@
 
 #pragma once
 
+#include "Color.h"
+#include "ColorConversion.h"
 #include "ColorTypes.h"
 #include <wtf/MathExtras.h>
 
@@ -31,8 +33,11 @@
 namespace WebCore {
 
 template<typename ColorType> ColorType makeColorTypeByNormalizingComponents(const ColorComponents<float, 4>&);
+template<typename ColorType> Color makeCanonicalColor(ColorType);
 
 
+// MARK: - Normalization
+
 template<typename ComponentType> struct WhitenessBlackness {
     ComponentType whiteness;
     ComponentType blackness;
@@ -101,4 +106,21 @@
     return { lightness, chroma, normalizedHue, alpha };
 }
 
+// MARK: - Canonicalization
+
+template<typename ColorType> inline Color makeCanonicalColor(ColorType color)
+{
+    return color;
 }
+
+template<> inline Color makeCanonicalColor<HWBA<float>>(HWBA<float> color)
+{
+    return convertColor<SRGBA<uint8_t>>(color);
+}
+
+template<> inline Color makeCanonicalColor<HSLA<float>>(HSLA<float> color)
+{
+    return convertColor<SRGBA<uint8_t>>(color);
+}
+
+}

Modified: trunk/Source/WebCore/platform/graphics/Gradient.h (287309 => 287310)


--- trunk/Source/WebCore/platform/graphics/Gradient.h	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/platform/graphics/Gradient.h	2021-12-21 12:42:19 UTC (rev 287310)
@@ -27,16 +27,16 @@
 
 #pragma once
 
-#include "AffineTransform.h"
 #include "Color.h"
 #include "ColorInterpolationMethod.h"
 #include "FloatPoint.h"
+#include "GradientColorStop.h"
 #include "GraphicsTypes.h"
 #include <variant>
 #include <wtf/Vector.h>
 
 #if USE(CG)
-#include <wtf/RetainPtr.h>
+#include "GradientRendererCG.h"
 #endif
 
 #if USE(DIRECT2D)
@@ -45,7 +45,6 @@
 
 #if USE(CG)
 typedef struct CGContext* CGContextRef;
-typedef struct CGGradient* CGGradientRef;
 #endif
 
 #if USE(DIRECT2D)
@@ -59,21 +58,15 @@
 
 namespace WebCore {
 
+class AffineTransform;
 class FloatRect;
 class GraphicsContext;
 
 class Gradient : public RefCounted<Gradient> {
 public:
-    struct ColorStop {
-        float offset { 0 };
-        Color color;
+    using ColorStop = GradientColorStop;
+    using ColorStopVector = GradientColorStopVector;
 
-        template<typename Encoder> void encode(Encoder&) const;
-        template<typename Decoder> static std::optional<ColorStop> decode(Decoder&);
-    };
-
-    using ColorStopVector = Vector<ColorStop, 2>;
-
     struct LinearData {
         FloatPoint point0;
         FloatPoint point1;
@@ -144,11 +137,6 @@
     void sortStops() const;
     void stopsChanged();
 
-#if USE(CG)
-    void createCGGradient();
-    bool hasOnlyBoundedSRGBColorStops() const;
-#endif
-
     Data m_data;
     ColorInterpolationMethod m_colorInterpolationMethod;
     mutable ColorStopVector m_stops;
@@ -157,7 +145,7 @@
     mutable unsigned m_cachedHash { 0 };
 
 #if USE(CG)
-    RetainPtr<CGGradientRef> m_gradient;
+    std::optional<GradientRendererCG> m_platformRenderer;
 #endif
 
 #if USE(DIRECT2D)
@@ -165,27 +153,6 @@
 #endif
 };
 
-template<typename Encoder> void Gradient::ColorStop::encode(Encoder& encoder) const
-{
-    encoder << offset;
-    encoder << color;
-}
-
-template<typename Decoder> std::optional<Gradient::ColorStop> Gradient::ColorStop::decode(Decoder& decoder)
-{
-    std::optional<float> offset;
-    decoder >> offset;
-    if (!offset)
-        return std::nullopt;
-
-    std::optional<Color> color;
-    decoder >> color;
-    if (!color)
-        return std::nullopt;
-
-    return {{ *offset, *color }};
-}
-
 template<typename Encoder> void Gradient::LinearData::encode(Encoder& encoder) const
 {
     encoder << point0;

Added: trunk/Source/WebCore/platform/graphics/GradientColorStop.h (0 => 287310)


--- trunk/Source/WebCore/platform/graphics/GradientColorStop.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/GradientColorStop.h	2021-12-21 12:42:19 UTC (rev 287310)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Color.h"
+#include <optional>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+struct GradientColorStop {
+    float offset { 0 };
+    Color color;
+
+    template<typename Encoder> void encode(Encoder&) const;
+    template<typename Decoder> static std::optional<GradientColorStop> decode(Decoder&);
+};
+
+using GradientColorStopVector = Vector<GradientColorStop, 2>;
+
+template<typename Encoder> void GradientColorStop::encode(Encoder& encoder) const
+{
+    encoder << offset;
+    encoder << color;
+}
+
+template<typename Decoder> std::optional<GradientColorStop> GradientColorStop::decode(Decoder& decoder)
+{
+    std::optional<float> offset;
+    decoder >> offset;
+    if (!offset)
+        return std::nullopt;
+
+    std::optional<Color> color;
+    decoder >> color;
+    if (!color)
+        return std::nullopt;
+
+    return {{ *offset, *color }};
+}
+
+}

Modified: trunk/Source/WebCore/platform/graphics/cg/GradientCG.cpp (287309 => 287310)


--- trunk/Source/WebCore/platform/graphics/cg/GradientCG.cpp	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebCore/platform/graphics/cg/GradientCG.cpp	2021-12-21 12:42:19 UTC (rev 287310)
@@ -29,72 +29,17 @@
 
 #if USE(CG)
 
+#include "GradientRendererCG.h"
 #include "GraphicsContextCG.h"
 #include <pal/spi/cg/CoreGraphicsSPI.h>
-#include <wtf/RetainPtr.h>
 
 namespace WebCore {
 
 void Gradient::stopsChanged()
 {
-    m_gradient = nullptr;
+    m_platformRenderer = { };
 }
 
-bool Gradient::hasOnlyBoundedSRGBColorStops() const
-{
-    for (const auto& stop : m_stops) {
-        if (stop.color.colorSpace() != ColorSpace::SRGB)
-            return false;
-    }
-    return true;
-}
-
-void Gradient::createCGGradient()
-{
-    sortStops();
-
-    unsigned numStops = m_stops.size();
-
-    const int reservedStops = 3;
-    Vector<CGFloat, reservedStops> locations;
-    locations.reserveInitialCapacity(numStops);
-
-    // If all the stops are bounded sRGB (as represented by the color having the color space
-    // ColorSpace::SRGB), it is faster to create a gradient using components than CGColors.
-    if (hasOnlyBoundedSRGBColorStops()) {
-        Vector<CGFloat, 4 * reservedStops> colorComponents;
-        colorComponents.reserveInitialCapacity(numStops * 4);
-
-        for (const auto& stop : m_stops) {
-            auto [colorSpace, components] = stop.color.colorSpaceAndComponents();
-            auto [r, g, b, a] = components;
-            colorComponents.uncheckedAppend(r);
-            colorComponents.uncheckedAppend(g);
-            colorComponents.uncheckedAppend(b);
-            colorComponents.uncheckedAppend(a);
-
-            locations.uncheckedAppend(stop.offset);
-        }
-
-        m_gradient = adoptCF(CGGradientCreateWithColorComponents(sRGBColorSpaceRef(), colorComponents.data(), locations.data(), numStops));
-        return;
-    }
-
-    auto colorsArray = adoptCF(CFArrayCreateMutable(0, m_stops.size(), &kCFTypeArrayCallBacks));
-    for (const auto& stop : m_stops) {
-        CFArrayAppendValue(colorsArray.get(), cachedCGColor(stop.color).get());
-        locations.uncheckedAppend(stop.offset);
-    }
-
-#if HAVE(CORE_GRAPHICS_EXTENDED_SRGB_COLOR_SPACE)
-    auto extendedColorsGradientColorSpace = extendedSRGBColorSpaceRef();
-#else
-    auto extendedColorsGradientColorSpace = sRGBColorSpaceRef();
-#endif
-
-    m_gradient = adoptCF(CGGradientCreateWithColors(extendedColorsGradientColorSpace, colorsArray.get(), locations.data()));
-}
-
 void Gradient::fill(GraphicsContext& context, const FloatRect& rect)
 {
     context.clip(rect);
@@ -108,11 +53,11 @@
 
 void Gradient::paint(CGContextRef platformContext)
 {
-    CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
+    if (!m_platformRenderer) {
+        sortStops();
+        m_platformRenderer = GradientRendererCG { m_colorInterpolationMethod, m_stops };
+    }
 
-    if (!m_gradient)
-        createCGGradient();
-
     WTF::switchOn(m_data,
         [&] (const LinearData& data) {
             switch (m_spreadMethod) {
@@ -119,7 +64,7 @@
             case GradientSpreadMethod::Repeat:
             case GradientSpreadMethod::Reflect: {
                 CGContextStateSaver saveState(platformContext);
-                extendOptions = 0;
+                CGGradientDrawingOptions extendOptions = 0;
 
                 FloatPoint gradientVectorNorm(data.point1 - data.point0);
                 gradientVectorNorm.normalize();
@@ -143,7 +88,7 @@
                     CGPoint left = CGPointMake(flip ? end : start, 0);
                     CGPoint right = CGPointMake(flip ? start : end, 0);
 
-                    CGContextDrawLinearGradient(platformContext, m_gradient.get(), left, right, extendOptions);
+                    m_platformRenderer->drawLinearGradient(platformContext, left, right, extendOptions);
                 };
 
                 auto isLeftOf = [](CGFloat start, CGFloat end, CGRect boundingBox) -> bool {
@@ -189,12 +134,14 @@
                     drawLinearGradient(end - dx, end, flip);
                     flip = !flip && m_spreadMethod == GradientSpreadMethod::Reflect;
                 }
-
                 break;
             }
-            case GradientSpreadMethod::Pad:
-                CGContextDrawLinearGradient(platformContext, m_gradient.get(), data.point0, data.point1, extendOptions);
+            case GradientSpreadMethod::Pad: {
+                CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
+                m_platformRenderer->drawLinearGradient(platformContext, data.point0, data.point1, extendOptions);
+                break;
             }
+            }
         },
         [&] (const RadialData& data) {
             bool needScaling = data.aspectRatio != 1;
@@ -208,7 +155,8 @@
                 CGContextTranslateCTM(platformContext, -data.point0.x(), -data.point0.y());
             }
 
-            CGContextDrawRadialGradient(platformContext, m_gradient.get(), data.point0, data.startRadius, data.point1, data.endRadius, extendOptions);
+            CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
+            m_platformRenderer->drawRadialGradient(platformContext, data.point0, data.startRadius, data.point1, data.endRadius, extendOptions);
 
             if (needScaling)
                 CGContextRestoreGState(platformContext);
@@ -221,7 +169,7 @@
             CGContextTranslateCTM(platformContext, data.point0.x(), data.point0.y());
             CGContextRotateCTM(platformContext, (CGFloat)-M_PI_2);
             CGContextTranslateCTM(platformContext, -data.point0.x(), -data.point0.y());
-            CGContextDrawConicGradient(platformContext, m_gradient.get(), data.point0, data.angleRadians);
+            m_platformRenderer->drawConicGradient(platformContext, data.point0, data.angleRadians);
             CGContextRestoreGState(platformContext);
 #else
             UNUSED_PARAM(data);

Added: trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.cpp (0 => 287310)


--- trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.cpp	2021-12-21 12:42:19 UTC (rev 287310)
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "GradientRendererCG.h"
+
+#include "ColorInterpolation.h"
+#include "ColorSpaceCG.h"
+#include <pal/spi/cg/CoreGraphicsSPI.h>
+
+namespace WebCore {
+
+GradientRendererCG::GradientRendererCG(ColorInterpolationMethod colorInterpolationMethod, const GradientColorStopVector& stops)
+    : m_strategy { pickStrategy(colorInterpolationMethod, stops) }
+{
+}
+
+// MARK: - Strategy selection.
+
+GradientRendererCG::Strategy GradientRendererCG::pickStrategy(ColorInterpolationMethod colorInterpolationMethod, const GradientColorStopVector& stops) const
+{
+    return WTF::switchOn(colorInterpolationMethod.colorSpace,
+        [&] (const ColorInterpolationMethod::SRGB&) -> Strategy {
+            switch (colorInterpolationMethod.alphaPremultiplication) {
+            case AlphaPremultiplication::Unpremultiplied:
+                return makeGradient(colorInterpolationMethod, stops);
+            case AlphaPremultiplication::Premultiplied:
+#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);
+#endif
+            }
+        },
+        [&] (const auto&) -> Strategy {
+            // FIXME: Add support for the other interpolation color spaces.
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+    );
+}
+
+// MARK: - Gradient strategy.
+
+GradientRendererCG::Strategy GradientRendererCG::makeGradient(ColorInterpolationMethod colorInterpolationMethod, const GradientColorStopVector& stops) const
+{
+    ASSERT_UNUSED(colorInterpolationMethod, std::holds_alternative<ColorInterpolationMethod::SRGB>(colorInterpolationMethod.colorSpace));
+#if !HAVE(CORE_GRAPHICS_PREMULTIPLIED_INTERPOLATION_GRADIENT)
+    ASSERT_UNUSED(colorInterpolationMethod, colorInterpolationMethod.alphaPremultiplication == AlphaPremultiplication::Unpremultiplied);
+#endif
+
+#if HAVE(CORE_GRAPHICS_GRADIENT_CREATE_WITH_OPTIONS)
+#if HAVE(CORE_GRAPHICS_PREMULTIPLIED_INTERPOLATION_GRADIENT)
+    auto gradientInterpolatesPremultipliedOptionsDictionary = [] () -> CFDictionaryRef {
+        static CFTypeRef keys[] = { kCGGradientInterpolatesPremultiplied };
+        static CFTypeRef values[] = { kCFBooleanTrue };
+        static CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+        return options;
+    };
+
+   auto gradientOptionsDictionary = [&] (auto colorInterpolationMethod) -> CFDictionaryRef {
+        switch (colorInterpolationMethod.alphaPremultiplication) {
+        case AlphaPremultiplication::Unpremultiplied:
+            return nullptr;
+        case AlphaPremultiplication::Premultiplied:
+            return gradientInterpolatesPremultipliedOptionsDictionary();
+        }
+   };
+#else
+   auto gradientOptionsDictionary = [] (auto) -> CFDictionaryRef {
+        return nullptr;
+   };
+#endif
+#endif
+
+    auto hasOnlyBoundedSRGBColorStops = [] (const auto& stops) {
+        for (const auto& stop : stops) {
+            if (stop.color.colorSpace() != ColorSpace::SRGB)
+                return false;
+        }
+        return true;
+    };
+
+    auto numberOfStops = stops.size();
+
+    static constexpr auto reservedStops = 3;
+    Vector<CGFloat, reservedStops> locations;
+    locations.reserveInitialCapacity(numberOfStops);
+
+    Vector<CGFloat, 4 * reservedStops> colorComponents;
+    colorComponents.reserveInitialCapacity(numberOfStops * 4);
+
+    auto gradientColorSpace = sRGBColorSpaceRef();
+
+    // FIXME: Now that we only ever use CGGradientCreateWithColorComponents, we should investigate
+    // if there is any real benefit to using sRGB when all the stops are bounded vs just using
+    // extended sRGB for all gradients.
+    if (hasOnlyBoundedSRGBColorStops(stops)) {
+        for (const auto& stop : stops) {
+            auto [colorSpace, components] = stop.color.colorSpaceAndComponents();
+            auto [r, g, b, a] = components;
+            colorComponents.uncheckedAppend(r);
+            colorComponents.uncheckedAppend(g);
+            colorComponents.uncheckedAppend(b);
+            colorComponents.uncheckedAppend(a);
+
+            locations.uncheckedAppend(stop.offset);
+        }
+    } else {
+#if HAVE(CORE_GRAPHICS_EXTENDED_SRGB_COLOR_SPACE)
+        gradientColorSpace = extendedSRGBColorSpaceRef();
+#endif
+
+        for (const auto& stop : stops) {
+#if HAVE(CORE_GRAPHICS_EXTENDED_SRGB_COLOR_SPACE)
+            auto [r, g, b, a] = stop.color.toColorTypeLossy<ExtendedSRGBA<float>>();
+#else
+            auto [r, g, b, a] = stop.color.toColorTypeLossy<SRGBA<float>>();
+#endif
+            colorComponents.uncheckedAppend(r);
+            colorComponents.uncheckedAppend(g);
+            colorComponents.uncheckedAppend(b);
+            colorComponents.uncheckedAppend(a);
+
+            locations.uncheckedAppend(stop.offset);
+        }
+    }
+
+#if HAVE(CORE_GRAPHICS_GRADIENT_CREATE_WITH_OPTIONS)
+    return Gradient { adoptCF(CGGradientCreateWithColorComponentsAndOptions(gradientColorSpace, colorComponents.data(), locations.data(), numberOfStops, gradientOptionsDictionary(colorInterpolationMethod))) };
+#else
+    return Gradient { adoptCF(CGGradientCreateWithColorComponents(gradientColorSpace, colorComponents.data(), locations.data(), numberOfStops)) };
+#endif
+}
+
+// MARK: - Shading strategy.
+
+template<typename InterpolationSpace, AlphaPremultiplication alphaPremultiplication>
+void GradientRendererCG::Shading::shadingFunction(void* info, const CGFloat* in, CGFloat* out)
+{
+    using InterpolationSpaceColorType = typename InterpolationSpace::ColorType;
+#if HAVE(CORE_GRAPHICS_EXTENDED_SRGB_COLOR_SPACE)
+    using OutputSpaceColorType = ExtendedSRGBA<float>;
+#else
+    using OutputSpaceColorType = SRGBA<float>;
+#endif
+
+    auto* data = ""
+
+    // Compute color at offset 'in[0]' and assign the components to out[0 -> 3].
+
+    float requestedOffset = in[0];
+
+    // 1. Find stops that bound the requested offset.
+    auto [stop0, stop1] = [&] {
+        for (size_t stop = 1; stop < data->stops().size(); ++stop) {
+            if (requestedOffset <= data->stops()[stop].offset)
+                return std::tie(data->stops()[stop - 1], data->stops()[stop]);
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+    }();
+
+    // 2. Compute percentage offset between the two stops.
+    float offset = (stop1.offset == stop0.offset) ? 0.0f : (requestedOffset - stop0.offset) / (stop1.offset - stop0.offset);
+
+    // 3. Interpolate the two stops' colors by the computed offset.
+    // FIXME: We don't want to due hue fixup for each call, so we should figure out how to precompute that.
+    auto interpolatedColor = interpolateColorComponents<alphaPremultiplication>(
+        std::get<InterpolationSpace>(data->colorInterpolationMethod().colorSpace),
+        makeFromComponents<InterpolationSpaceColorType>(stop0.colorComponents), 1.0f - offset,
+        makeFromComponents<InterpolationSpaceColorType>(stop1.colorComponents), offset);
+
+    // 4. Convert to the output color space.
+    auto interpolatedColorConvertedToOutputSpace = asColorComponents(convertColor<OutputSpaceColorType>(interpolatedColor));
+
+    // 5. Write color components to 'out' pointer.
+    for (size_t componentIndex = 0; componentIndex < interpolatedColorConvertedToOutputSpace.size(); ++componentIndex)
+        out[componentIndex] = interpolatedColorConvertedToOutputSpace[componentIndex];
+}
+
+GradientRendererCG::Strategy GradientRendererCG::makeShading(ColorInterpolationMethod colorInterpolationMethod, const GradientColorStopVector& stops) const
+{
+    auto makeData = [&] (auto colorInterpolationMethod, auto& stops) {
+        auto convertColorToColorInterpolationSpace = [&] (const Color& color, auto colorInterpolationMethod) -> ColorComponents<float, 4> {
+            return WTF::switchOn(colorInterpolationMethod.colorSpace,
+                [&] (auto& colorSpace) -> ColorComponents<float, 4> {
+                    using ColorType = typename std::remove_reference_t<decltype(colorSpace)>::ColorType;
+                    return asColorComponents(color.template toColorTypeLossy<ColorType>());
+                }
+            );
+        };
+
+        auto totalNumberOfStops = stops.size();
+        bool hasZero = false;
+        bool hasOne = false;
+
+        for (const auto& stop : stops) {
+            auto offset = stop.offset;
+            ASSERT(offset >= 0);
+            ASSERT(offset <= 1);
+            
+            if (offset == 0)
+                hasZero = true;
+            else if (offset == 1)
+                hasOne = true;
+        }
+
+        if (!hasZero)
+            totalNumberOfStops++;
+        if (!hasOne)
+            totalNumberOfStops++;
+
+        Vector<ColorConvertedToInterpolationColorSpaceStop> convertedStops;
+        convertedStops.reserveInitialCapacity(totalNumberOfStops);
+
+        if (!hasZero)
+            convertedStops.uncheckedAppend({ 0.0f, { 0.0f, 0.0f, 0.0f, 0.0f } });
+
+        for (const auto& stop : stops)
+            convertedStops.uncheckedAppend({ stop.offset, convertColorToColorInterpolationSpace(stop.color, colorInterpolationMethod) });
+
+        if (!hasOne)
+            convertedStops.uncheckedAppend({ 1.0f, convertedStops.last().colorComponents });
+
+        if (!hasZero)
+            convertedStops[0].colorComponents = convertedStops[1].colorComponents;
+
+        return Shading::Data::create(colorInterpolationMethod, WTFMove(convertedStops));
+    };
+
+    auto makeFunction = [&] (auto colorInterpolationMethod, auto& data) {
+        auto makeEvaluateCallback = [&] (auto colorInterpolationMethod) -> CGFunctionEvaluateCallback {
+            return WTF::switchOn(colorInterpolationMethod.colorSpace,
+                [&] (auto& colorSpace) -> CGFunctionEvaluateCallback {
+                    using InterpolationMethodColorSpace = typename std::remove_reference_t<decltype(colorSpace)>;
+                    
+                    switch (colorInterpolationMethod.alphaPremultiplication) {
+                    case AlphaPremultiplication::Unpremultiplied:
+                        return &Shading::shadingFunction<InterpolationMethodColorSpace, AlphaPremultiplication::Unpremultiplied>;
+                    case AlphaPremultiplication::Premultiplied:
+                        return &Shading::shadingFunction<InterpolationMethodColorSpace, AlphaPremultiplication::Premultiplied>;
+                    }
+                }
+            );
+        };
+
+        const CGFunctionCallbacks callbacks = {
+            0,
+            makeEvaluateCallback(colorInterpolationMethod),
+            [] (void* info) {
+                static_cast<GradientRendererCG::Shading::Data*>(info)->deref();
+            }
+        };
+
+        static const CGFloat domain[2] = { 0, 1 };
+#if HAVE(CORE_GRAPHICS_EXTENDED_SRGB_COLOR_SPACE)
+        static const CGFloat range[8] = { -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), 0, 1 };
+#else
+        static const CGFloat range[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
+#endif
+
+        Ref dataRefCopy = data;
+        return adoptCF(CGFunctionCreate(&dataRefCopy.leakRef(), 1, domain, 4, range, &callbacks));
+    };
+
+    auto data = "" stops);
+    auto function = makeFunction(colorInterpolationMethod, data);
+
+    // FIXME: Investigate using bounded sRGB when the input stops are all bounded sRGB.
+#if HAVE(CORE_GRAPHICS_EXTENDED_SRGB_COLOR_SPACE)
+    auto colorSpace = extendedSRGBColorSpaceRef();
+#else
+    auto colorSpace = sRGBColorSpaceRef();
+#endif
+
+    return Shading { WTFMove(data), WTFMove(function), colorSpace };
+}
+
+// MARK: - Drawing functions.
+
+void GradientRendererCG::drawLinearGradient(CGContextRef platformContext, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions options)
+{
+    WTF::switchOn(m_strategy,
+        [&] (Gradient& gradient) {
+            CGContextDrawLinearGradient(platformContext, gradient.gradient.get(), startPoint, endPoint, options);
+        },
+        [&] (Shading& shading) {
+            bool startExtend = (options & kCGGradientDrawsBeforeStartLocation) != 0;
+            bool endExtend = (options & kCGGradientDrawsAfterEndLocation) != 0;
+
+            CGContextDrawShading(platformContext, adoptCF(CGShadingCreateAxial(shading.colorSpace.get(), startPoint, endPoint, shading.function.get(), startExtend, endExtend)).get());
+        }
+    );
+}
+
+void GradientRendererCG::drawRadialGradient(CGContextRef platformContext, CGPoint startCenter, CGFloat startRadius, CGPoint endCenter, CGFloat endRadius, CGGradientDrawingOptions options)
+{
+    WTF::switchOn(m_strategy,
+        [&] (Gradient& gradient) {
+            CGContextDrawRadialGradient(platformContext, gradient.gradient.get(), startCenter, startRadius, endCenter, endRadius, options);
+        },
+        [&] (Shading& shading) {
+            bool startExtend = (options & kCGGradientDrawsBeforeStartLocation) != 0;
+            bool endExtend = (options & kCGGradientDrawsAfterEndLocation) != 0;
+
+            CGContextDrawShading(platformContext, adoptCF(CGShadingCreateRadial(shading.colorSpace.get(), startCenter, startRadius, endCenter, endRadius, shading.function.get(), startExtend, endExtend)).get());
+        }
+    );
+}
+
+void GradientRendererCG::drawConicGradient(CGContextRef platformContext, CGPoint center, CGFloat angle)
+{
+// FIXME: Seems like this should be HAVE(CG_CONTEXT_DRAW_CONIC_GRADIENT).
+// FIXME: Can we change tvOS to be like the other Cocoa platforms?
+#if PLATFORM(COCOA) && !PLATFORM(APPLETV)
+    WTF::switchOn(m_strategy,
+        [&] (Gradient& gradient) {
+            CGContextDrawConicGradient(platformContext, gradient.gradient.get(), center, angle);
+        },
+        [&] (Shading& shading) {
+            CGContextDrawShading(platformContext, adoptCF(CGShadingCreateConic(shading.colorSpace.get(), center, angle, shading.function.get())).get());
+        }
+    );
+#else
+    UNUSED_PARAM(platformContext);
+    UNUSED_PARAM(center);
+    UNUSED_PARAM(angle);
+#endif
+}
+
+}

Added: trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.h (0 => 287310)


--- trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.h	2021-12-21 12:42:19 UTC (rev 287310)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ColorComponents.h"
+#include "ColorInterpolationMethod.h"
+#include "GradientColorStop.h"
+#include <CoreGraphics/CoreGraphics.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+struct ColorConvertedToInterpolationColorSpaceStop {
+    float offset;
+    ColorComponents<float, 4> colorComponents;
+};
+
+class GradientRendererCG {
+public:
+    GradientRendererCG(ColorInterpolationMethod, const GradientColorStopVector&);
+
+    void drawLinearGradient(CGContextRef, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions);
+    void drawRadialGradient(CGContextRef, CGPoint startCenter, CGFloat startRadius, CGPoint endCenter, CGFloat endRadius, CGGradientDrawingOptions);
+    void drawConicGradient(CGContextRef, CGPoint center, CGFloat angle);
+
+private:
+    struct Gradient {
+        RetainPtr<CGGradientRef> gradient;
+    };
+
+    struct Shading {
+        template<typename InterpolationSpace, AlphaPremultiplication> static void shadingFunction(void*, const CGFloat*, CGFloat*);
+
+        class Data : public ThreadSafeRefCounted<Data> {
+        public:
+            static Ref<Data> create(ColorInterpolationMethod colorInterpolationMethod, Vector<ColorConvertedToInterpolationColorSpaceStop> stops)
+            {
+                return adoptRef(*new Data(colorInterpolationMethod, WTFMove(stops)));
+            }
+
+            ColorInterpolationMethod colorInterpolationMethod() const { return m_colorInterpolationMethod; }
+            const Vector<ColorConvertedToInterpolationColorSpaceStop>& stops() const { return m_stops; }
+
+        private:
+            Data(ColorInterpolationMethod colorInterpolationMethod, Vector<ColorConvertedToInterpolationColorSpaceStop> stops)
+                : m_colorInterpolationMethod { colorInterpolationMethod }
+                , m_stops { WTFMove(stops) }
+            {
+            }
+
+            ColorInterpolationMethod m_colorInterpolationMethod;
+            Vector<ColorConvertedToInterpolationColorSpaceStop> m_stops;
+        };
+
+        Ref<Data> data;
+        RetainPtr<CGFunctionRef> function;
+        RetainPtr<CGColorSpaceRef> colorSpace;
+    };
+
+    using Strategy = std::variant<Gradient, Shading>;
+
+    Strategy pickStrategy(ColorInterpolationMethod, const GradientColorStopVector&) const;
+    Strategy makeGradient(ColorInterpolationMethod, const GradientColorStopVector&) const;
+    Strategy makeShading(ColorInterpolationMethod, const GradientColorStopVector&) const;
+
+    Strategy m_strategy;
+};
+
+}

Modified: trunk/Source/WebKitLegacy/win/ChangeLog (287309 => 287310)


--- trunk/Source/WebKitLegacy/win/ChangeLog	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebKitLegacy/win/ChangeLog	2021-12-21 12:42:19 UTC (rev 287310)
@@ -1,3 +1,18 @@
+2021-12-21  Sam Weinig  <wei...@apple.com>
+
+        Add support for premultiplied alpha interpolated gradients and defaulted off option to use them for CSS Gradients
+        https://bugs.webkit.org/show_bug.cgi?id=234492
+
+        Reviewed by Simon Fraser.
+
+        Add support for tests enabling the CSSGradientPremultipliedAlphaInterpolationEnabled preference.
+
+        * WebPreferences.cpp:
+        (WebPreferences::cssGradientPremultipliedAlphaInterpolationEnabled):
+        * WebPreferences.h:
+        * WebView.cpp:
+        (WebView::notifyPreferencesChanged):
+
 2021-12-20  Fujii Hironori  <hironori.fu...@sony.com>
 
         [Win] MSVC reports "COMPropertyBag.h(233): error C2385: ambiguous access of 'IUnknown'" with /std:c++20

Modified: trunk/Source/WebKitLegacy/win/WebPreferences.cpp (287309 => 287310)


--- trunk/Source/WebKitLegacy/win/WebPreferences.cpp	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebKitLegacy/win/WebPreferences.cpp	2021-12-21 12:42:19 UTC (rev 287310)
@@ -2637,6 +2637,11 @@
     return boolValueForKey("WebKitCanvasColorSpaceEnabled");
 }
 
+bool WebPreferences::cssGradientPremultipliedAlphaInterpolationEnabled()
+{
+    return boolValueForKey("WebKitCSSGradientPremultipliedAlphaInterpolationEnabled");
+}
+
 bool WebPreferences::mockScrollbarsControllerEnabled()
 {
     return boolValueForKey("WebKitMockScrollbarsControllerEnabled");

Modified: trunk/Source/WebKitLegacy/win/WebPreferences.h (287309 => 287310)


--- trunk/Source/WebKitLegacy/win/WebPreferences.h	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebKitLegacy/win/WebPreferences.h	2021-12-21 12:42:19 UTC (rev 287310)
@@ -346,6 +346,7 @@
     // The following preference accessors are not exposed via IWebPreferences* as they are only
     // needed for testing purposes and can be toggled via the set*PreferenceForTesting functions.
     bool canvasColorSpaceEnabled();
+    bool cssGradientPremultipliedAlphaInterpolationEnabled();
     bool mockScrollbarsControllerEnabled();
 
 private:

Modified: trunk/Source/WebKitLegacy/win/WebView.cpp (287309 => 287310)


--- trunk/Source/WebKitLegacy/win/WebView.cpp	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Source/WebKitLegacy/win/WebView.cpp	2021-12-21 12:42:19 UTC (rev 287310)
@@ -5600,6 +5600,7 @@
     settings.setOverscrollBehaviorEnabled(!!enabled);
 
     settings.setCanvasColorSpaceEnabled(m_preferences->canvasColorSpaceEnabled());
+    settings.setCSSGradientPremultipliedAlphaInterpolationEnabled(m_preferences->cssGradientPremultipliedAlphaInterpolationEnabled());
     settings.setMockScrollbarsControllerEnabled(m_preferences->mockScrollbarsControllerEnabled());
 
     return S_OK;

Modified: trunk/Tools/ChangeLog (287309 => 287310)


--- trunk/Tools/ChangeLog	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Tools/ChangeLog	2021-12-21 12:42:19 UTC (rev 287310)
@@ -1,3 +1,14 @@
+2021-12-21  Sam Weinig  <wei...@apple.com>
+
+        Add support for premultiplied alpha interpolated gradients and defaulted off option to use them for CSS Gradients
+        https://bugs.webkit.org/show_bug.cgi?id=234492
+
+        Reviewed by Simon Fraser.
+
+        * DumpRenderTree/TestOptions.cpp:
+        (WTR::TestOptions::defaults):
+        Add default for Windows WebKitLegacy testing which still requires it.
+
 2021-12-21  Carlos Garcia Campos  <cgar...@igalia.com>
 
         [GTK][a11y] Implement list markers when building with ATSPI

Modified: trunk/Tools/DumpRenderTree/TestOptions.cpp (287309 => 287310)


--- trunk/Tools/DumpRenderTree/TestOptions.cpp	2021-12-21 12:38:52 UTC (rev 287309)
+++ trunk/Tools/DumpRenderTree/TestOptions.cpp	2021-12-21 12:42:19 UTC (rev 287310)
@@ -135,6 +135,7 @@
             { "CSSContainmentEnabled", false },
             { "CSSCounterStyleAtRuleImageSymbolsEnabled", false },
             { "CSSCounterStyleAtRulesEnabled", false },
+            { "CSSGradientPremultipliedAlphaInterpolationEnabled", true },
             { "CSSLogicalEnabled", false },
             { "CSSOMViewSmoothScrollingEnabled", false },
             { "CanvasColorSpaceEnabled", true },
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to