[FTW] Correct radial gradient handling of various radius orderings
https://bugs.webkit.org/show_bug.cgi?id=202815

Reviewed by Per Arne Vollan.

Revise the 'generateGradient' implementation to recognize that the
radius arguments might not be in increasing order. Direct2D's
implementation does expect this, so we need to swap order and
revise the gradient range to match.

Drive-by fix: Stop building unused image decoders if building with
the Apple stack.

* PlatformFTW.cmake: Stop building unused image decoders.
* platform/graphics/win/GradientDirect2D.cpp:
(WebCore::Gradient::generateGradient):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251221 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 5be2957..9894fd2 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,22 @@
+2019-10-16  Brent Fulgham  <bfulgham@apple.com>
+
+        [FTW] Correct radial gradient handling of various radius orderings
+        https://bugs.webkit.org/show_bug.cgi?id=202815
+
+        Reviewed by Per Arne Vollan.
+
+        Revise the 'generateGradient' implementation to recognize that the
+        radius arguments might not be in increasing order. Direct2D's
+        implementation does expect this, so we need to swap order and
+        revise the gradient range to match.
+
+        Drive-by fix: Stop building unused image decoders if building with
+        the Apple stack.
+
+        * PlatformFTW.cmake: Stop building unused image decoders.
+        * platform/graphics/win/GradientDirect2D.cpp:
+        (WebCore::Gradient::generateGradient):
+
 2019-10-16  Chris Dumez  <cdumez@apple.com>
 
         Rename PageCache to BackForwardCache
diff --git a/Source/WebCore/PlatformFTW.cmake b/Source/WebCore/PlatformFTW.cmake
index e674a19..791d97b 100644
--- a/Source/WebCore/PlatformFTW.cmake
+++ b/Source/WebCore/PlatformFTW.cmake
@@ -1,5 +1,7 @@
 include(platform/Curl.cmake)
-include(platform/ImageDecoders.cmake)
+if (NOT APPLE_BUILD)
+    include(platform/ImageDecoders.cmake)
+endif ()
 include(platform/TextureMapper.cmake)
 
 list(APPEND WebCore_PRIVATE_INCLUDE_DIRECTORIES
diff --git a/Source/WebCore/platform/graphics/win/GradientDirect2D.cpp b/Source/WebCore/platform/graphics/win/GradientDirect2D.cpp
index fcb173f..89096f4 100644
--- a/Source/WebCore/platform/graphics/win/GradientDirect2D.cpp
+++ b/Source/WebCore/platform/graphics/win/GradientDirect2D.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2018 Apple Inc.  All rights reserved.
+ * Copyright (C) 2016-2019 Apple Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -56,6 +56,15 @@
     return m_gradient;
 }
 
+static bool circleIsEntirelyContained(const FloatPoint& centerA, float radiusA, const FloatPoint& centerB, float radiusB)
+{
+    double deltaX = centerB.x() - centerA.x();
+    double deltaY = centerB.y() - centerA.y();
+    double deltaRadius = radiusB - radiusA;
+
+    return deltaX * deltaX + deltaY * deltaY < deltaRadius * deltaRadius && radiusA < radiusB;
+}
+
 void Gradient::generateGradient(ID2D1RenderTarget* renderTarget)
 {
     sortStopsIfNecessary();
@@ -75,11 +84,23 @@
         [&] (const LinearData&) {
             // No action needed.
         },
-        [&] (const RadialData& data) {
-            if (data.startRadius && data.endRadius) {
-                float startPosition = std::abs(data.startRadius / data.endRadius);
-                if (startPosition > 1.0)
-                    startPosition = 1.0 / startPosition;
+        [&] (RadialData& data) {
+            RELEASE_ASSERT(data.startRadius >= 0);
+            RELEASE_ASSERT(data.endRadius >= 0);
+
+            if (data.startRadius > data.endRadius) {
+                gradientStops.reverse();
+                for (auto& stop : gradientStops)
+                    stop.position = 1.0 - stop.position;
+
+                std::swap(data.startRadius, data.endRadius);
+                std::swap(data.point0, data.point1);
+            }
+
+            if (data.startRadius) {
+                RELEASE_ASSERT(data.endRadius > 0);
+                float startPosition = data.startRadius / data.endRadius;
+                RELEASE_ASSERT(startPosition <= 1.0);
                 float availableRange = 1.0 - startPosition;
                 RELEASE_ASSERT(availableRange <= 1.0 && availableRange >= 0);
 
@@ -90,9 +111,10 @@
 
                 // Restore the 'start' position
                 auto firstStop = gradientStops.first();
-                firstStop.position = 0;
-
-                gradientStops.insert(0, firstStop);
+                if (!WTF::areEssentiallyEqual(firstStop.position, 0.0f)) {
+                    firstStop.position = 0;
+                    gradientStops.insert(0, firstStop);
+                }
             }
         },
         [&] (const ConicData&) {
@@ -120,7 +142,12 @@
             m_gradient = linearGradient;
         },
         [&] (const RadialData& data) {
-            FloatSize offset = data.point0 - data.point1;
+            FloatSize offset;
+            if (!circleIsEntirelyContained(data.point0, data.startRadius, data.point1, data.endRadius))
+                offset = (data.point1.scaled(data.startRadius) - data.point0.scaled(data.endRadius)) / (data.endRadius - data.startRadius);
+            else
+                offset = data.point0 - data.point1;
+
             FloatPoint center = data.point1;
             float radiusX = data.endRadius;
             float radiusY = radiusX / data.aspectRatio;