Make color-filter transform gradient colors
https://bugs.webkit.org/show_bug.cgi?id=185080

Reviewed by Zalan Bujtas.
Source/WebCore:

In CSSGradientValue::computeStops(), transform the color of each gradient color
stop through the color filter. Having a color filter makes the gradient uncacheable.

Color filters can add alpha, so we also have to fix up CSSGradientValue::knownToBeOpaque()
to take a RenderStyle and convert the colors before testing opaqueness. Clean up some related
functions to take const RenderStyle&.

Test: css3/color-filters/color-filter-gradients.html

* css/CSSCrossfadeValue.cpp:
(WebCore::subimageKnownToBeOpaque):
* css/CSSFilterImageValue.cpp:
(WebCore::CSSFilterImageValue::knownToBeOpaque const):
* css/CSSFilterImageValue.h:
* css/CSSGradientValue.cpp:
(WebCore::CSSGradientValue::image):
(WebCore::CSSGradientValue::computeStops):
(WebCore::CSSGradientValue::knownToBeOpaque const):
(WebCore::CSSLinearGradientValue::createGradient):
(WebCore::CSSRadialGradientValue::createGradient):
* css/CSSGradientValue.h:
* css/CSSImageGeneratorValue.cpp:
(WebCore::CSSImageGeneratorValue::knownToBeOpaque const):
* css/CSSImageValue.cpp:
(WebCore::CSSImageValue::knownToBeOpaque const):
* css/CSSImageValue.h:

LayoutTests:

* css3/color-filters/color-filter-gradients-expected.html: Added.
* css3/color-filters/color-filter-gradients.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@231105 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index e65c3c2..5e5d3b0 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,5 +1,15 @@
 2018-04-27  Simon Fraser  <simon.fraser@apple.com>
 
+        Make color-filter transform gradient colors
+        https://bugs.webkit.org/show_bug.cgi?id=185080
+
+        Reviewed by Zalan Bujtas.
+
+        * css3/color-filters/color-filter-gradients-expected.html: Added.
+        * css3/color-filters/color-filter-gradients.html: Added.
+
+2018-04-27  Simon Fraser  <simon.fraser@apple.com>
+
         Disable color-filter tests on Windows for now
         https://bugs.webkit.org/show_bug.cgi?id=185076
 
diff --git a/LayoutTests/css3/color-filters/color-filter-gradients-expected.html b/LayoutTests/css3/color-filters/color-filter-gradients-expected.html
new file mode 100644
index 0000000..39a7963
--- /dev/null
+++ b/LayoutTests/css3/color-filters/color-filter-gradients-expected.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>CSS Test: color-filter reference</title>
+        <link rel="author" title="Apple" href="http://www.apple.com/">
+
+        <style type="text/css">
+            .test
+            {
+				width: 200px;
+				height: 200px;
+				margin: 10px;
+				float: left;
+            }
+        </style>
+    </head>
+    <body>
+        <div class="test" style="background-image: linear-gradient(blue, red);"></div>
+        <div class="test" style="background-image: radial-gradient(blue, red);"></div>
+        <div class="test" style="background-image: repeating-linear-gradient(blue, red 50px);"></div>
+        <div class="test" style="background-image: repeating-radial-gradient(blue, red 50px);"></div>
+    </body>
+</html>
diff --git a/LayoutTests/css3/color-filters/color-filter-gradients.html b/LayoutTests/css3/color-filters/color-filter-gradients.html
new file mode 100644
index 0000000..4e1cae9
--- /dev/null
+++ b/LayoutTests/css3/color-filters/color-filter-gradients.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>CSS Test: color-filter affects gradients</title>
+        <link rel="author" title="Apple" href="http://www.apple.com/">
+        <link rel="match" href="color-filter-grayscale-expected.html">
+
+        <meta name="assert" content="color-filter affects gradients">
+        <style type="text/css">
+            .test
+            {
+				width: 200px;
+				height: 200px;
+				margin: 10px;
+				float: left;
+				color-filter: invert();
+            }
+        </style>
+		<script>
+			if (window.internals)
+			    internals.settings.setColorFilterEnabled(true);
+		</script>
+    </head>
+    <body>
+        <div class="test" style="background-image: linear-gradient(yellow, cyan);"></div>
+        <div class="test" style="background-image: radial-gradient(yellow, cyan);"></div>
+        <div class="test" style="background-image: repeating-linear-gradient(yellow, cyan 50px);"></div>
+        <div class="test" style="background-image: repeating-radial-gradient(yellow, cyan 50px);"></div>
+    </body>
+</html>
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index c98d01b..05ebb31 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,37 @@
+2018-04-27  Simon Fraser  <simon.fraser@apple.com>
+
+        Make color-filter transform gradient colors
+        https://bugs.webkit.org/show_bug.cgi?id=185080
+
+        Reviewed by Zalan Bujtas.
+        
+        In CSSGradientValue::computeStops(), transform the color of each gradient color
+        stop through the color filter. Having a color filter makes the gradient uncacheable.
+        
+        Color filters can add alpha, so we also have to fix up CSSGradientValue::knownToBeOpaque()
+        to take a RenderStyle and convert the colors before testing opaqueness. Clean up some related
+        functions to take const RenderStyle&.
+
+        Test: css3/color-filters/color-filter-gradients.html
+
+        * css/CSSCrossfadeValue.cpp:
+        (WebCore::subimageKnownToBeOpaque):
+        * css/CSSFilterImageValue.cpp:
+        (WebCore::CSSFilterImageValue::knownToBeOpaque const):
+        * css/CSSFilterImageValue.h:
+        * css/CSSGradientValue.cpp:
+        (WebCore::CSSGradientValue::image):
+        (WebCore::CSSGradientValue::computeStops):
+        (WebCore::CSSGradientValue::knownToBeOpaque const):
+        (WebCore::CSSLinearGradientValue::createGradient):
+        (WebCore::CSSRadialGradientValue::createGradient):
+        * css/CSSGradientValue.h:
+        * css/CSSImageGeneratorValue.cpp:
+        (WebCore::CSSImageGeneratorValue::knownToBeOpaque const):
+        * css/CSSImageValue.cpp:
+        (WebCore::CSSImageValue::knownToBeOpaque const):
+        * css/CSSImageValue.h:
+
 2018-04-26  Simon Fraser  <simon.fraser@apple.com>
 
         Fix color-filter to apply to text decorations
diff --git a/Source/WebCore/css/CSSCrossfadeValue.cpp b/Source/WebCore/css/CSSCrossfadeValue.cpp
index 9eb6f0f..172c945 100644
--- a/Source/WebCore/css/CSSCrossfadeValue.cpp
+++ b/Source/WebCore/css/CSSCrossfadeValue.cpp
@@ -46,7 +46,7 @@
 static bool subimageKnownToBeOpaque(const CSSValue& value, const RenderElement& renderer)
 {
     if (is<CSSImageValue>(value))
-        return downcast<CSSImageValue>(value).knownToBeOpaque(&renderer);
+        return downcast<CSSImageValue>(value).knownToBeOpaque(renderer);
 
     if (is<CSSImageGeneratorValue>(value))
         return downcast<CSSImageGeneratorValue>(value).knownToBeOpaque(renderer);
diff --git a/Source/WebCore/css/CSSFilterImageValue.cpp b/Source/WebCore/css/CSSFilterImageValue.cpp
index eaab703..6acf744 100644
--- a/Source/WebCore/css/CSSFilterImageValue.cpp
+++ b/Source/WebCore/css/CSSFilterImageValue.cpp
@@ -76,7 +76,7 @@
     return CSSImageGeneratorValue::subimageIsPending(m_imageValue);
 }
 
-bool CSSFilterImageValue::knownToBeOpaque(const RenderElement*) const
+bool CSSFilterImageValue::knownToBeOpaque(const RenderElement&) const
 {
     return false;
 }
diff --git a/Source/WebCore/css/CSSFilterImageValue.h b/Source/WebCore/css/CSSFilterImageValue.h
index 3ffc4f2..8f1c999 100644
--- a/Source/WebCore/css/CSSFilterImageValue.h
+++ b/Source/WebCore/css/CSSFilterImageValue.h
@@ -58,7 +58,7 @@
     FloatSize fixedSize(const RenderElement*);
 
     bool isPending() const;
-    bool knownToBeOpaque(const RenderElement*) const;
+    bool knownToBeOpaque(const RenderElement&) const;
 
     void loadSubimages(CachedResourceLoader&, const ResourceLoaderOptions&);
 
diff --git a/Source/WebCore/css/CSSGradientValue.cpp b/Source/WebCore/css/CSSGradientValue.cpp
index eef295b..171d4726 100644
--- a/Source/WebCore/css/CSSGradientValue.cpp
+++ b/Source/WebCore/css/CSSGradientValue.cpp
@@ -52,7 +52,7 @@
 {
     if (size.isEmpty())
         return nullptr;
-    bool cacheable = isCacheable();
+    bool cacheable = isCacheable() && !renderer.style().hasColorFilter();
     if (cacheable) {
         if (!clients().contains(&renderer))
             return nullptr;
@@ -225,7 +225,7 @@
 };
 
 template<typename GradientAdapter>
-Gradient::ColorStopVector CSSGradientValue::computeStops(GradientAdapter& gradient, const CSSToLengthConversionData& conversionData, float maxLengthForRepeat)
+Gradient::ColorStopVector CSSGradientValue::computeStops(GradientAdapter& gradient, const CSSToLengthConversionData& conversionData, const RenderStyle& style, float maxLengthForRepeat)
 {
     if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient) {
         sortStopsIfNeeded();
@@ -240,7 +240,10 @@
             else
                 offset = stop.m_position->floatValue(CSSPrimitiveValue::CSS_NUMBER);
 
-            result.uncheckedAppend({ offset, stop.m_resolvedColor });
+            Color color = stop.m_resolvedColor;
+            if (style.hasColorFilter())
+                style.colorFilter().transformColor(color);
+            result.uncheckedAppend({ offset, color });
         }
 
         return result;
@@ -259,7 +262,12 @@
         auto& stop = m_stops[i];
 
         stops[i].isMidpoint = stop.isMidpoint;
-        stops[i].color = stop.m_resolvedColor;
+
+        Color color = stop.m_resolvedColor;
+        if (style.hasColorFilter())
+            style.colorFilter().transformColor(color);
+
+        stops[i].color = color;
 
         if (stop.m_position) {
             auto& positionValue = *stop.m_position;
@@ -569,9 +577,18 @@
     return true;
 }
 
-bool CSSGradientValue::knownToBeOpaque() const
+bool CSSGradientValue::knownToBeOpaque(const RenderElement& renderer) const
 {
+    bool hasColorFilter = renderer.style().hasColorFilter();
+
     for (auto& stop : m_stops) {
+        if (hasColorFilter) {
+            Color stopColor = stop.m_resolvedColor;
+            renderer.style().colorFilter().transformColor(stopColor);
+            if (!stopColor.isOpaque())
+                return false;
+        }
+
         if (!stop.m_resolvedColor.isOpaque())
             return false;
     }
@@ -814,7 +831,7 @@
 
     Gradient::LinearData data { firstPoint, secondPoint };
     LinearGradientAdapter adapter { data };
-    auto stops = computeStops(adapter, conversionData, 1);
+    auto stops = computeStops(adapter, conversionData, renderer.style(), 1);
 
     auto gradient = Gradient::create(WTFMove(data));
     gradient->setSortedColorStops(WTFMove(stops));
@@ -1231,7 +1248,7 @@
 
     Gradient::RadialData data { firstPoint, secondPoint, firstRadius, secondRadius, aspectRatio };
     RadialGradientAdapter adapter { data };
-    auto stops = computeStops(adapter, conversionData, maxExtent);
+    auto stops = computeStops(adapter, conversionData, renderer.style(), maxExtent);
 
     auto gradient = Gradient::create(WTFMove(data));
     gradient->setSortedColorStops(WTFMove(stops));
diff --git a/Source/WebCore/css/CSSGradientValue.h b/Source/WebCore/css/CSSGradientValue.h
index 9cb03f4..68f8240 100644
--- a/Source/WebCore/css/CSSGradientValue.h
+++ b/Source/WebCore/css/CSSGradientValue.h
@@ -82,7 +82,7 @@
     FloatSize fixedSize(const RenderElement&) const { return FloatSize(); }
 
     bool isPending() const { return false; }
-    bool knownToBeOpaque() const;
+    bool knownToBeOpaque(const RenderElement&) const;
 
     void loadSubimages(CachedResourceLoader&, const ResourceLoaderOptions&) { }
     Ref<CSSGradientValue> gradientWithStylesResolved(const StyleResolver&);
@@ -110,7 +110,7 @@
     }
 
     template<typename GradientAdapter>
-    Gradient::ColorStopVector computeStops(GradientAdapter&, const CSSToLengthConversionData&, float maxLengthForRepeat);
+    Gradient::ColorStopVector computeStops(GradientAdapter&, const CSSToLengthConversionData&, const RenderStyle&, float maxLengthForRepeat);
 
     // Resolve points/radii to front end values.
     FloatPoint computeEndPoint(CSSPrimitiveValue*, CSSPrimitiveValue*, const CSSToLengthConversionData&, const FloatSize&);
diff --git a/Source/WebCore/css/CSSImageGeneratorValue.cpp b/Source/WebCore/css/CSSImageGeneratorValue.cpp
index a7e664c..7a8e19f 100644
--- a/Source/WebCore/css/CSSImageGeneratorValue.cpp
+++ b/Source/WebCore/css/CSSImageGeneratorValue.cpp
@@ -233,13 +233,13 @@
     case NamedImageClass:
         return false;
     case FilterImageClass:
-        return downcast<CSSFilterImageValue>(*this).knownToBeOpaque(&renderer);
+        return downcast<CSSFilterImageValue>(*this).knownToBeOpaque(renderer);
     case LinearGradientClass:
-        return downcast<CSSLinearGradientValue>(*this).knownToBeOpaque();
+        return downcast<CSSLinearGradientValue>(*this).knownToBeOpaque(renderer);
     case RadialGradientClass:
-        return downcast<CSSRadialGradientValue>(*this).knownToBeOpaque();
+        return downcast<CSSRadialGradientValue>(*this).knownToBeOpaque(renderer);
     case ConicGradientClass:
-        return downcast<CSSConicGradientValue>(*this).knownToBeOpaque();
+        return downcast<CSSConicGradientValue>(*this).knownToBeOpaque(renderer);
     default:
         ASSERT_NOT_REACHED();
     }
diff --git a/Source/WebCore/css/CSSImageValue.cpp b/Source/WebCore/css/CSSImageValue.cpp
index c191ecc..4ea4114 100644
--- a/Source/WebCore/css/CSSImageValue.cpp
+++ b/Source/WebCore/css/CSSImageValue.cpp
@@ -100,11 +100,11 @@
     return DeprecatedCSSOMPrimitiveValue::create(CSSPrimitiveValue::create(m_url, CSSPrimitiveValue::CSS_URI), styleDeclaration);
 }
 
-bool CSSImageValue::knownToBeOpaque(const RenderElement* renderer) const
+bool CSSImageValue::knownToBeOpaque(const RenderElement& renderer) const
 {
     if (!m_cachedImage)
         return false;
-    return m_cachedImage->currentFrameKnownToBeOpaque(renderer);
+    return m_cachedImage->currentFrameKnownToBeOpaque(&renderer);
 }
 
 } // namespace WebCore
diff --git a/Source/WebCore/css/CSSImageValue.h b/Source/WebCore/css/CSSImageValue.h
index 3ab5599..76f94df 100644
--- a/Source/WebCore/css/CSSImageValue.h
+++ b/Source/WebCore/css/CSSImageValue.h
@@ -54,7 +54,7 @@
 
     bool equals(const CSSImageValue&) const;
 
-    bool knownToBeOpaque(const RenderElement*) const;
+    bool knownToBeOpaque(const RenderElement&) const;
 
     void setInitiator(const AtomicString& name) { m_initiatorName = name; }