Move Color blending related functions to their own files
https://bugs.webkit.org/show_bug.cgi?id=213742

Reviewed by Dean Jackson.

- Moves Color::blend(const Color&), Color::blendWithWhite(), blend(const Color&, const Color&, double)
  and blendWithoutPremultiply(const Color&, const Color&, double) to their own files: ColorBlending.h/cpp
- Renames Color::blend(const Color&) to blendSourceOver(const Color&, const Color&) 
- Renames Color::blendWithWhite() to blendWithWhite(const Color&).

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
Add new files.

* platform/graphics/Color.cpp:
(WebCore::Color::blend const): Deleted.
(WebCore::Color::blendWithWhite const): Deleted.
(WebCore::blend): Deleted.
(WebCore::blendWithoutPremultiply): Deleted.
* platform/graphics/Color.h:
* platform/graphics/ColorBlending.cpp: Copied from Source/WebCore/platform/graphics/Color.cpp.
* platform/graphics/ColorBlending.h: Added.
Move declarations / implementations from Color.h/cpp to ColorBlending.h/cpp.

* css/CSSGradientValue.cpp:
* editing/FrameSelection.cpp:
(WebCore::CaretBase::computeCaretColor):
* page/FrameView.cpp:
(WebCore::FrameView::documentBackgroundColor const):
* page/TextIndicator.cpp:
(WebCore::estimatedBackgroundColorForRange):
* page/animation/CSSPropertyAnimation.cpp:
* platform/graphics/filters/FilterOperation.cpp:
* rendering/RenderBoxModelObject.cpp:
(WebCore::RenderBoxModelObject::paintFillLayerExtended):
* rendering/RenderMenuList.cpp:
(RenderMenuList::getItemBackgroundColor const):
* rendering/RenderTheme.cpp:
(WebCore::RenderTheme::transformSelectionBackgroundColor const):
Update for new signatures and #include ColorBlending.h as neeeded.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@263753 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 38e0979..79b2326 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,46 @@
+2020-06-30  Sam Weinig  <weinig@apple.com>
+
+        Move Color blending related functions to their own files
+        https://bugs.webkit.org/show_bug.cgi?id=213742
+
+        Reviewed by Dean Jackson.
+
+        - Moves Color::blend(const Color&), Color::blendWithWhite(), blend(const Color&, const Color&, double)
+          and blendWithoutPremultiply(const Color&, const Color&, double) to their own files: ColorBlending.h/cpp
+        - Renames Color::blend(const Color&) to blendSourceOver(const Color&, const Color&) 
+        - Renames Color::blendWithWhite() to blendWithWhite(const Color&).
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        Add new files.
+
+        * platform/graphics/Color.cpp:
+        (WebCore::Color::blend const): Deleted.
+        (WebCore::Color::blendWithWhite const): Deleted.
+        (WebCore::blend): Deleted.
+        (WebCore::blendWithoutPremultiply): Deleted.
+        * platform/graphics/Color.h:
+        * platform/graphics/ColorBlending.cpp: Copied from Source/WebCore/platform/graphics/Color.cpp.
+        * platform/graphics/ColorBlending.h: Added.
+        Move declarations / implementations from Color.h/cpp to ColorBlending.h/cpp.
+
+        * css/CSSGradientValue.cpp:
+        * editing/FrameSelection.cpp:
+        (WebCore::CaretBase::computeCaretColor):
+        * page/FrameView.cpp:
+        (WebCore::FrameView::documentBackgroundColor const):
+        * page/TextIndicator.cpp:
+        (WebCore::estimatedBackgroundColorForRange):
+        * page/animation/CSSPropertyAnimation.cpp:
+        * platform/graphics/filters/FilterOperation.cpp:
+        * rendering/RenderBoxModelObject.cpp:
+        (WebCore::RenderBoxModelObject::paintFillLayerExtended):
+        * rendering/RenderMenuList.cpp:
+        (RenderMenuList::getItemBackgroundColor const):
+        * rendering/RenderTheme.cpp:
+        (WebCore::RenderTheme::transformSelectionBackgroundColor const):
+        Update for new signatures and #include ColorBlending.h as neeeded.
+
 2020-06-30  Andy Estes  <aestes@apple.com>
 
         [Xcode] Enable the "My Mac (Mac Catalyst)" destination in WebKit Xcode projects
diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt
index 95c7698..4a6d1e9 100644
--- a/Source/WebCore/Sources.txt
+++ b/Source/WebCore/Sources.txt
@@ -1907,6 +1907,7 @@
 platform/graphics/ANGLEWebKitBridge.cpp
 platform/graphics/BitmapImage.cpp
 platform/graphics/Color.cpp
+platform/graphics/ColorBlending.cpp
 platform/graphics/ColorUtilities.cpp
 platform/graphics/ComplexTextController.cpp
 platform/graphics/CrossfadeGeneratedImage.cpp
diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
index fa31908..dc0e586 100644
--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -9963,6 +9963,8 @@
 		7C193BFC1F5E10C50088F3E6 /* JSImageSmoothingQuality.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSImageSmoothingQuality.h; sourceTree = "<group>"; };
 		7C193BFD1F5E10D60088F3E6 /* JSPath2D.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSPath2D.cpp; sourceTree = "<group>"; };
 		7C193BFE1F5E10D70088F3E6 /* JSPath2D.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSPath2D.h; sourceTree = "<group>"; };
+		7C1B4A6524A997590033727F /* ColorBlending.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ColorBlending.cpp; sourceTree = "<group>"; };
+		7C1B4A6724A997660033727F /* ColorBlending.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ColorBlending.h; sourceTree = "<group>"; };
 		7C1E8CFF1ED0C2BE00B1D983 /* BeforeUnloadEvent.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = BeforeUnloadEvent.idl; sourceTree = "<group>"; };
 		7C1E8D001ED0C2BE00B1D983 /* CallbackResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallbackResult.h; sourceTree = "<group>"; };
 		7C1E97251A9F9834007BF0FB /* AutoFillButtonElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AutoFillButtonElement.cpp; sourceTree = "<group>"; };
@@ -25493,6 +25495,8 @@
 				A89943260B42338700D7C802 /* BitmapImage.h */,
 				B27535380B053814002CE64F /* Color.cpp */,
 				B27535390B053814002CE64F /* Color.h */,
+				7C1B4A6524A997590033727F /* ColorBlending.cpp */,
+				7C1B4A6724A997660033727F /* ColorBlending.h */,
 				7CAC6AEC247F1C5100E61D59 /* ColorComponents.h */,
 				3103B7DE1DB01556008BB890 /* ColorHash.h */,
 				7CAC6AE8247F082000E61D59 /* ColorMatrix.h */,
diff --git a/Source/WebCore/css/CSSGradientValue.cpp b/Source/WebCore/css/CSSGradientValue.cpp
index 4b99d9b..1042fa3 100644
--- a/Source/WebCore/css/CSSGradientValue.cpp
+++ b/Source/WebCore/css/CSSGradientValue.cpp
@@ -29,6 +29,7 @@
 #include "CSSCalculationValue.h"
 #include "CSSToLengthConversionData.h"
 #include "CSSValueKeywords.h"
+#include "ColorBlending.h"
 #include "FloatSize.h"
 #include "Gradient.h"
 #include "GradientImage.h"
diff --git a/Source/WebCore/editing/FrameSelection.cpp b/Source/WebCore/editing/FrameSelection.cpp
index 873d25d..33a927d 100644
--- a/Source/WebCore/editing/FrameSelection.cpp
+++ b/Source/WebCore/editing/FrameSelection.cpp
@@ -28,6 +28,7 @@
 
 #include "AXObjectCache.h"
 #include "CharacterData.h"
+#include "ColorBlending.h"
 #include "DeleteSelectionCommand.h"
 #include "Document.h"
 #include "Editing.h"
@@ -1800,7 +1801,7 @@
     if (!elementStyle.caretColor().isValid() && rootEditableStyle) {
         auto rootEditableBackgroundColor = rootEditableStyle->visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor);
         auto elementBackgroundColor = elementStyle.visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor);
-        auto disappearsIntoBackground = rootEditableBackgroundColor.blend(elementBackgroundColor) == rootEditableBackgroundColor;
+        auto disappearsIntoBackground = blendSourceOver(rootEditableBackgroundColor, elementBackgroundColor) == rootEditableBackgroundColor;
         if (disappearsIntoBackground)
             return rootEditableStyle->visitedDependentColorWithColorFilter(CSSPropertyCaretColor);
     }
diff --git a/Source/WebCore/page/FrameView.cpp b/Source/WebCore/page/FrameView.cpp
index 7ae7299..2e0a891 100644
--- a/Source/WebCore/page/FrameView.cpp
+++ b/Source/WebCore/page/FrameView.cpp
@@ -35,6 +35,7 @@
 #include "CachedResourceLoader.h"
 #include "Chrome.h"
 #include "ChromeClient.h"
+#include "ColorBlending.h"
 #include "DOMWindow.h"
 #include "DebugPageOverlays.h"
 #include "DeprecatedGlobalSettings.h"
@@ -4037,11 +4038,11 @@
     if (!bodyBackgroundColor.isValid()) {
         if (!htmlBackgroundColor.isValid())
             return Color();
-        return baseBackgroundColor().blend(htmlBackgroundColor);
+        return blendSourceOver(baseBackgroundColor(), htmlBackgroundColor);
     }
 
     if (!htmlBackgroundColor.isValid())
-        return baseBackgroundColor().blend(bodyBackgroundColor);
+        return blendSourceOver(baseBackgroundColor(), bodyBackgroundColor);
 
     // We take the aggregate of the base background color
     // the <html> background color, and the <body>
@@ -4050,7 +4051,7 @@
     // technically part of the document background, but it
     // otherwise poses problems when the aggregate is not
     // fully opaque.
-    return baseBackgroundColor().blend(htmlBackgroundColor).blend(bodyBackgroundColor);
+    return blendSourceOver(blendSourceOver(baseBackgroundColor(), htmlBackgroundColor), bodyBackgroundColor);
 }
 
 bool FrameView::hasCustomScrollbars() const
diff --git a/Source/WebCore/page/TextIndicator.cpp b/Source/WebCore/page/TextIndicator.cpp
index 5b6ed5e..863e9ce 100644
--- a/Source/WebCore/page/TextIndicator.cpp
+++ b/Source/WebCore/page/TextIndicator.cpp
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "TextIndicator.h"
 
+#include "ColorBlending.h"
 #include "ColorHash.h"
 #include "Document.h"
 #include "Editor.h"
@@ -249,7 +250,7 @@
     }
     parentRendererBackgroundColors.reverse();
     for (const auto& backgroundColor : parentRendererBackgroundColors)
-        estimatedBackgroundColor = estimatedBackgroundColor.blend(backgroundColor);
+        estimatedBackgroundColor = blendSourceOver(estimatedBackgroundColor, backgroundColor);
 
     return estimatedBackgroundColor;
 }
diff --git a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp
index 98c3bb9..bc5ed71 100644
--- a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp
+++ b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp
@@ -41,6 +41,7 @@
 #include "CachedImage.h"
 #include "CalculationValue.h"
 #include "ClipPathOperation.h"
+#include "ColorBlending.h"
 #include "FloatConversion.h"
 #include "FontCascade.h"
 #include "FontSelectionAlgorithm.h"
diff --git a/Source/WebCore/platform/graphics/Color.cpp b/Source/WebCore/platform/graphics/Color.cpp
index 63dd040..7b0cf63 100644
--- a/Source/WebCore/platform/graphics/Color.cpp
+++ b/Source/WebCore/platform/graphics/Color.cpp
@@ -144,65 +144,6 @@
     return WebCore::luminance(toSRGBALossy());
 }
 
-Color Color::blend(const Color& source) const
-{
-    if (!isVisible() || source.isOpaque())
-        return source;
-
-    if (!source.alpha())
-        return *this;
-
-    auto [selfR, selfG, selfB, selfA] = toSRGBASimpleColorLossy();
-    auto [sourceR, sourceG, sourceB, sourceA] = source.toSRGBASimpleColorLossy();
-
-    int d = 0xFF * (selfA + sourceA) - selfA * sourceA;
-    int a = d / 0xFF;
-    int r = (selfR * selfA * (0xFF - sourceA) + 0xFF * sourceA * sourceR) / d;
-    int g = (selfG * selfA * (0xFF - sourceA) + 0xFF * sourceA * sourceG) / d;
-    int b = (selfB * selfA * (0xFF - sourceA) + 0xFF * sourceA * sourceB) / d;
-
-    return makeSimpleColor(r, g, b, a);
-}
-
-Color Color::blendWithWhite() const
-{
-    constexpr int startAlpha = 153; // 60%
-    constexpr int endAlpha = 204; // 80%;
-    constexpr int alphaIncrement = 17;
-
-    auto blendComponent = [](int c, int a) -> int {
-        float alpha = a / 255.0f;
-        int whiteBlend = 255 - a;
-        c -= whiteBlend;
-        return static_cast<int>(c / alpha);
-    };
-
-    // If the color contains alpha already, we leave it alone.
-    if (!isOpaque())
-        return *this;
-
-    auto [existingR, existingG, existingB, existingAlpha] = toSRGBASimpleColorLossy();
-
-    Color result;
-    for (int alpha = startAlpha; alpha <= endAlpha; alpha += alphaIncrement) {
-        // We have a solid color.  Convert to an equivalent color that looks the same when blended with white
-        // at the current alpha.  Try using less transparency if the numbers end up being negative.
-        int r = blendComponent(existingR, alpha);
-        int g = blendComponent(existingG, alpha);
-        int b = blendComponent(existingB, alpha);
-        
-        result = makeSimpleColor(r, g, b, alpha);
-
-        if (r >= 0 && g >= 0 && b >= 0)
-            break;
-    }
-
-    // FIXME: Why is preserving the semantic bit desired and/or correct here?
-    if (isSemantic())
-        result.tagAsSemantic();
-    return result;
-}
-
 Color Color::colorWithAlpha(float alpha) const
 {
     if (isExtended())
diff --git a/Source/WebCore/platform/graphics/Color.h b/Source/WebCore/platform/graphics/Color.h
index f3dbf97..61c998f 100644
--- a/Source/WebCore/platform/graphics/Color.h
+++ b/Source/WebCore/platform/graphics/Color.h
@@ -143,10 +143,6 @@
     // FIXME: Replace remaining uses with luminance.
     WEBCORE_EXPORT float lightness() const;
 
-    // This is an implementation of Porter-Duff's "source-over" equation
-    Color blend(const Color&) const;
-    Color blendWithWhite() const;
-
     Color invertedColorWithAlpha(Optional<float> alpha) const;
     Color invertedColorWithAlpha(float alpha) const;
 
@@ -238,9 +234,6 @@
 // One or both must be extended colors.
 bool extendedColorsEqual(const Color&, const Color&);
 
-Color blend(const Color& from, const Color& to, double progress);
-Color blendWithoutPremultiply(const Color& from, const Color& to, double progress);
-
 #if USE(CG)
 WEBCORE_EXPORT CGColorRef cachedCGColor(const Color&);
 #endif
diff --git a/Source/WebCore/platform/graphics/ColorBlending.cpp b/Source/WebCore/platform/graphics/ColorBlending.cpp
new file mode 100644
index 0000000..1b28373
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ColorBlending.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 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 INC. ``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 INC. OR
+ * 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 "ColorBlending.h"
+
+#include "AnimationUtilities.h"
+#include "Color.h"
+
+namespace WebCore {
+
+Color blendSourceOver(const Color& backdrop, const Color& source)
+{
+    if (!backdrop.isVisible() || source.isOpaque())
+        return source;
+
+    if (!source.alpha())
+        return *this;
+
+    auto [backdropR, backdropG, backdropB, backdropA] = backdrop.toSRGBASimpleColorLossy();
+    auto [sourceR, sourceG, sourceB, sourceA] = source.toSRGBASimpleColorLossy();
+
+    int d = 0xFF * (backdropA + sourceA) - backdropA * sourceA;
+    int a = d / 0xFF;
+    int r = (backdropR * backdropA * (0xFF - sourceA) + 0xFF * sourceA * sourceR) / d;
+    int g = (backdropG * backdropA * (0xFF - sourceA) + 0xFF * sourceA * sourceG) / d;
+    int b = (backdropB * backdropA * (0xFF - sourceA) + 0xFF * sourceA * sourceB) / d;
+
+    return makeSimpleColor(r, g, b, a);
+}
+
+Color blendWithWhite(const Color& color)
+{
+    constexpr int startAlpha = 153; // 60%
+    constexpr int endAlpha = 204; // 80%;
+    constexpr int alphaIncrement = 17;
+
+    auto blendComponent = [](int c, int a) -> int {
+        float alpha = a / 255.0f;
+        int whiteBlend = 255 - a;
+        c -= whiteBlend;
+        return static_cast<int>(c / alpha);
+    };
+
+    // If the color contains alpha already, we leave it alone.
+    if (!color.isOpaque())
+        return color;
+
+    auto [existingR, existingG, existingB, existingAlpha] = color.toSRGBASimpleColorLossy();
+
+    SimpleColor result;
+    for (int alpha = startAlpha; alpha <= endAlpha; alpha += alphaIncrement) {
+        // We have a solid color.  Convert to an equivalent color that looks the same when blended with white
+        // at the current alpha.  Try using less transparency if the numbers end up being negative.
+        int r = blendComponent(existingR, alpha);
+        int g = blendComponent(existingG, alpha);
+        int b = blendComponent(existingB, alpha);
+        
+        result = makeSimpleColor(r, g, b, alpha);
+
+        if (r >= 0 && g >= 0 && b >= 0)
+            break;
+    }
+
+    // FIXME: Why is preserving the semantic bit desired and/or correct here?
+    if (color.isSemantic())
+        return Color(result, Color::Semantic);
+    return result;
+}
+
+Color blend(const Color& from, const Color& to, double progress)
+{
+    // FIXME: ExtendedColor - needs to handle color spaces.
+    // We need to preserve the state of the valid flag at the end of the animation
+    if (progress == 1 && !to.isValid())
+        return { };
+
+    // Since premultiplyCeiling() bails on zero alpha, special-case that.
+    auto premultipliedFrom = from.alpha() ? premultiplyCeiling(from.toSRGBASimpleColorLossy()) : Color::transparent;
+    auto premultipliedTo = to.alpha() ? premultiplyCeiling(to.toSRGBASimpleColorLossy()) : Color::transparent;
+
+    SimpleColor premultBlended = makeSimpleColor(
+        WebCore::blend(premultipliedFrom.redComponent(), premultipliedTo.redComponent(), progress),
+        WebCore::blend(premultipliedFrom.greenComponent(), premultipliedTo.greenComponent(), progress),
+        WebCore::blend(premultipliedFrom.blueComponent(), premultipliedTo.blueComponent(), progress),
+        WebCore::blend(premultipliedFrom.alphaComponent(), premultipliedTo.alphaComponent(), progress)
+    );
+
+    return unpremultiply(premultBlended);
+}
+
+Color blendWithoutPremultiply(const Color& from, const Color& to, double progress)
+{
+    // FIXME: ExtendedColor - needs to handle color spaces.
+    // We need to preserve the state of the valid flag at the end of the animation
+    if (progress == 1 && !to.isValid())
+        return { };
+
+    auto fromSRGB = from.toSRGBASimpleColorLossy();
+    auto toSRGB = from.toSRGBASimpleColorLossy();
+
+    return makeSimpleColor(
+        WebCore::blend(fromSRGB.redComponent(), toSRGB.redComponent(), progress),
+        WebCore::blend(fromSRGB.greenComponent(), toSRGB.greenComponent(), progress),
+        WebCore::blend(fromSRGB.blueComponent(), toSRGB.blueComponent(), progress),
+        WebCore::blend(fromSRGB.alphaComponent(), toSRGB.alphaComponent(), progress)
+    );
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/ColorBlending.h b/Source/WebCore/platform/graphics/ColorBlending.h
new file mode 100644
index 0000000..ed2b807
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ColorBlending.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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 INC. 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 INC. 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
+
+namespace WebCore {
+
+class Color;
+
+// This is an implementation of Porter-Duff's "source-over" equation
+Color blendSourceOver(const Color&, const Color&);
+
+// Bespoke "whitening" algorithm used by RenderTheme::transformSelectionBackgroundColor.
+Color blendWithWhite(const Color&);
+
+Color blend(const Color& from, const Color& to, double progress);
+Color blendWithoutPremultiply(const Color& from, const Color& to, double progress);
+
+}
diff --git a/Source/WebCore/platform/graphics/filters/FilterOperation.cpp b/Source/WebCore/platform/graphics/filters/FilterOperation.cpp
index f057c24..69f764c 100644
--- a/Source/WebCore/platform/graphics/filters/FilterOperation.cpp
+++ b/Source/WebCore/platform/graphics/filters/FilterOperation.cpp
@@ -29,6 +29,7 @@
 #include "AnimationUtilities.h"
 #include "CachedResourceLoader.h"
 #include "CachedSVGDocumentReference.h"
+#include "ColorBlending.h"
 #include "ColorMatrix.h"
 #include "ColorUtilities.h"
 #include "FilterEffect.h"
diff --git a/Source/WebCore/rendering/RenderBoxModelObject.cpp b/Source/WebCore/rendering/RenderBoxModelObject.cpp
index 6c31e7d..5ddfc31 100644
--- a/Source/WebCore/rendering/RenderBoxModelObject.cpp
+++ b/Source/WebCore/rendering/RenderBoxModelObject.cpp
@@ -29,6 +29,7 @@
 #include "BitmapImage.h"
 #include "BorderEdge.h"
 #include "CachedImage.h"
+#include "ColorBlending.h"
 #include "Document.h"
 #include "DocumentTimeline.h"
 #include "FloatRoundedRect.h"
@@ -934,7 +935,7 @@
             FloatRect backgroundRectForPainting = snapRectToDevicePixels(backgroundRect, deviceScaleFactor);
             if (baseColor.isVisible()) {
                 if (!baseBgColorOnly && bgColor.isVisible())
-                    baseColor = baseColor.blend(bgColor);
+                    baseColor = blendSourceOver(baseColor, bgColor);
                 context.fillRect(backgroundRectForPainting, baseColor, CompositeOperator::Copy);
             } else if (!baseBgColorOnly && bgColor.isVisible()) {
                 auto operation = context.compositeOperation();
diff --git a/Source/WebCore/rendering/RenderMenuList.cpp b/Source/WebCore/rendering/RenderMenuList.cpp
index 8e09ea5..c02581c 100644
--- a/Source/WebCore/rendering/RenderMenuList.cpp
+++ b/Source/WebCore/rendering/RenderMenuList.cpp
@@ -29,6 +29,7 @@
 #include "AccessibilityMenuList.h"
 #include "CSSFontSelector.h"
 #include "Chrome.h"
+#include "ColorBlending.h"
 #include "Frame.h"
 #include "FrameView.h"
 #include "HTMLNames.h"
@@ -532,14 +533,14 @@
     }
 
     // Otherwise, the item's background is overlayed on top of the menu background.
-    backgroundColor = style().visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor).blend(backgroundColor);
+    backgroundColor = blendSourceOver(style().visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor), backgroundColor);
     if (backgroundColor.isOpaque()) {
         itemBackgroundColor = backgroundColor;
         return;
     }
 
     // If the menu background is not opaque, then add an opaque white background behind.
-    itemBackgroundColor = Color(Color::white).blend(backgroundColor);
+    itemBackgroundColor = blendSourceOver(Color::white, backgroundColor);
 }
 
 PopupMenuStyle RenderMenuList::menuStyle() const
diff --git a/Source/WebCore/rendering/RenderTheme.cpp b/Source/WebCore/rendering/RenderTheme.cpp
index a21d52c..fc80cb7 100644
--- a/Source/WebCore/rendering/RenderTheme.cpp
+++ b/Source/WebCore/rendering/RenderTheme.cpp
@@ -22,6 +22,7 @@
 #include "RenderTheme.h"
 
 #include "CSSValueKeywords.h"
+#include "ColorBlending.h"
 #include "ControlStates.h"
 #include "Document.h"
 #include "FileList.h"
@@ -616,7 +617,7 @@
 
 Color RenderTheme::transformSelectionBackgroundColor(const Color& color, OptionSet<StyleColor::Options>) const
 {
-    return color.blendWithWhite();
+    return blendWithWhite(color);
 }
 
 Color RenderTheme::activeSelectionForegroundColor(OptionSet<StyleColor::Options> options) const