Diff
Modified: trunk/LayoutTests/ChangeLog (289121 => 289122)
--- trunk/LayoutTests/ChangeLog 2022-02-04 17:06:32 UTC (rev 289121)
+++ trunk/LayoutTests/ChangeLog 2022-02-04 17:26:30 UTC (rev 289122)
@@ -1,3 +1,15 @@
+2022-02-04 Sam Weinig <wei...@apple.com>
+
+ Gradients don't correctly interpolate missing/none color components correctly
+ https://bugs.webkit.org/show_bug.cgi?id=236025
+
+ Reviewed by Simon Fraser.
+
+ Add tests of gradients with explicit 'none' components in some of the colors.
+
+ * fast/gradients/gradient-with-missing-components-expected.html: Added.
+ * fast/gradients/gradient-with-missing-components.html: Added.
+
2022-02-04 Kate Cheney <katherine_che...@apple.com>
Fix App Privacy Report redirect attribution
Added: trunk/LayoutTests/fast/gradients/gradient-with-missing-components-expected.html (0 => 289122)
--- trunk/LayoutTests/fast/gradients/gradient-with-missing-components-expected.html (rev 0)
+++ trunk/LayoutTests/fast/gradients/gradient-with-missing-components-expected.html 2022-02-04 17:26:30 UTC (rev 289122)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ border: dashed black 1px;
+ display: inline-block;
+ }
+
+ #blue-white-none {
+ background-image: linear-gradient(in oklch, oklch(45% 0.3 265) 0%, oklch(from white l c 265) 100%);
+ }
+ #white-none-blue {
+ background-image: linear-gradient(in oklch, oklch(from white l c 265) 0%, oklch(45% 0.3 265) 100%);
+ }
+ #blue-white-none-red {
+ background-image: linear-gradient(in oklch, oklch(45% 0.3 265) 0%, oklch(from white l c 265) 50%, oklch(from white l c 30) 50%, oklch(60% 0.25 30) 100%);
+ }
+ #white-none-red-white-none {
+ background-image: linear-gradient(in oklch, oklch(from white l c none) 0%, oklch(60% 0.25 30) 50%, oklch(from white l c none) 100%);
+ }
+
+ #blue-black-none {
+ background-image: linear-gradient(in oklch, oklch(45% 0.3 265) 0%, oklch(from black l c 265) 100%);
+ }
+ #black-none-blue {
+ background-image: linear-gradient(in oklch, oklch(from black l c 265) 0%, oklch(45% 0.3 265) 100%);
+ }
+ #blue-black-none-red {
+ background-image: linear-gradient(in oklch, oklch(45% 0.3 265) 0%, oklch(from black l c 265) 50%, oklch(from black l c 30) 50%, oklch(60% 0.25 30) 100%);
+ }
+ #black-none-red-black-none {
+ background-image: linear-gradient(in oklch, oklch(from black l c none) 0%, oklch(60% 0.25 30) 50%, oklch(from black l c none) 100%);
+ }
+</style>
+</head>
+<body>
+<div id="blue-white-none"></div>
+<div id="white-none-blue"></div>
+<div id="blue-white-none-red"></div>
+<div id="white-none-red-white-none"></div>
+
+<div id="blue-black-none"></div>
+<div id="black-none-blue"></div>
+<div id="blue-black-none-red"></div>
+<div id="black-none-red-black-none"></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/gradients/gradient-with-missing-components.html (0 => 289122)
--- trunk/LayoutTests/fast/gradients/gradient-with-missing-components.html (rev 0)
+++ trunk/LayoutTests/fast/gradients/gradient-with-missing-components.html 2022-02-04 17:26:30 UTC (rev 289122)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ border: dashed black 1px;
+ display: inline-block;
+ }
+
+ #blue-white-none {
+ background-image: linear-gradient(in oklch, oklch(45% 0.3 265) 0%, oklch(from white l c none) 100%);
+ }
+ #white-none-blue {
+ background-image: linear-gradient(in oklch, oklch(from white l c none) 0%, oklch(45% 0.3 265) 100%);
+ }
+ #blue-white-none-red {
+ background-image: linear-gradient(in oklch, oklch(45% 0.3 265) 0%, oklch(from white l c none) 50%, oklch(60% 0.25 30) 100%);
+ }
+ #white-none-red-white-none {
+ background-image: linear-gradient(in oklch, oklch(from white l c none) 0%, oklch(60% 0.25 30) 50%, oklch(from white l c none) 100%);
+ }
+
+ #blue-black-none {
+ background-image: linear-gradient(in oklch, oklch(45% 0.3 265) 0%, oklch(from black l c none) 100%);
+ }
+ #black-none-blue {
+ background-image: linear-gradient(in oklch, oklch(from black l c none) 0%, oklch(45% 0.3 265) 100%);
+ }
+ #blue-black-none-red {
+ background-image: linear-gradient(in oklch, oklch(45% 0.3 265) 0%, oklch(from black l c none) 50%, oklch(60% 0.25 30) 100%);
+ }
+ #black-none-red-black-none {
+ background-image: linear-gradient(in oklch, oklch(from black l c none) 0%, oklch(60% 0.25 30) 50%, oklch(from black l c none) 100%);
+ }
+</style>
+</head>
+<body>
+<div id="blue-white-none"></div>
+<div id="white-none-blue"></div>
+<div id="blue-white-none-red"></div>
+<div id="white-none-red-white-none"></div>
+
+<div id="blue-black-none"></div>
+<div id="black-none-blue"></div>
+<div id="blue-black-none-red"></div>
+<div id="black-none-red-black-none"></div>
+</body>
+</html>
Modified: trunk/Source/WTF/ChangeLog (289121 => 289122)
--- trunk/Source/WTF/ChangeLog 2022-02-04 17:06:32 UTC (rev 289121)
+++ trunk/Source/WTF/ChangeLog 2022-02-04 17:26:30 UTC (rev 289122)
@@ -1,3 +1,15 @@
+2022-02-04 Sam Weinig <wei...@apple.com>
+
+ Gradients don't correctly interpolate missing/none color components correctly
+ https://bugs.webkit.org/show_bug.cgi?id=236025
+
+ Reviewed by Simon Fraser.
+
+ Move isNaNConstExpr to MathExtras.h from WebCore so it can used in multiple places.
+
+ * wtf/MathExtras.h:
+ (WTF::isNaNConstExpr):
+
2022-02-03 Ryosuke Niwa <rn...@webkit.org>
Delete SelectionAcrossShadowBoundariesEnabled
Modified: trunk/Source/WTF/wtf/MathExtras.h (289121 => 289122)
--- trunk/Source/WTF/wtf/MathExtras.h 2022-02-04 17:06:32 UTC (rev 289121)
+++ trunk/Source/WTF/wtf/MathExtras.h 2022-02-04 17:26:30 UTC (rev 289122)
@@ -770,6 +770,21 @@
#endif
}
+// FIXME: Replace with std::isnan() once std::isnan() is constexpr.
+template<typename T> typename std::enable_if_t<std::is_floating_point_v<T>, bool> isNaNConstExpr(T value)
+{
+#if COMPILER_HAS_CLANG_BUILTIN(__builtin_isnan)
+ return __builtin_isnan(value);
+#else
+ return value != value;
+#endif
+}
+
+template<typename T> typename std::enable_if_t<std::is_integral_v<T>, bool> isNaNConstExpr(T)
+{
+ return false;
+}
+
} // namespace WTF
using WTF::shuffleVector;
@@ -777,4 +792,5 @@
using WTF::ctz;
using WTF::getLSBSet;
using WTF::getMSBSet;
+using WTF::isNaNConstExpr;
using WTF::reverseBits32;
Modified: trunk/Source/WebCore/ChangeLog (289121 => 289122)
--- trunk/Source/WebCore/ChangeLog 2022-02-04 17:06:32 UTC (rev 289121)
+++ trunk/Source/WebCore/ChangeLog 2022-02-04 17:26:30 UTC (rev 289122)
@@ -1,3 +1,37 @@
+2022-02-04 Sam Weinig <wei...@apple.com>
+
+ Gradients don't correctly interpolate missing/none color components correctly
+ https://bugs.webkit.org/show_bug.cgi?id=236025
+
+ Reviewed by Simon Fraser.
+
+ Test: fast/gradients/gradient-with-missing-components.html
+
+ * platform/graphics/Color.cpp:
+ (WebCore::Color::anyComponentIsNone const):
+ * platform/graphics/Color.h:
+ Add helper to check if any component is 'none'.
+
+ * platform/graphics/ColorComponents.h:
+ (WebCore::operator==):
+ Update operator== for ColorComponents to be none-aware and treat
+ two components that are both none as equal.
+
+ * platform/graphics/ColorTypes.h:
+ (WebCore::assertInRange):
+ (WebCore::constexprIsNaN): Deleted.
+ Adopt shared version of the constexpr isnan.
+
+ * platform/graphics/cg/GradientRendererCG.cpp:
+ (WebCore::anyComponentIsNone):
+ (WebCore::GradientRendererCG::pickStrategy const):
+ (WebCore::GradientRendererCG::makeShading const):
+ Use the CGShaderRef strategy if any component of any stop is none, as it
+ is the only one that currently supports it correctly. Ensure none is preserved
+ by utilizing the unresolved component values. This can be optimized in the
+ future by preprocessing the color stops to pre-resolve the none components,
+ even allowing the CGGradientRef path to be used for supported cases.
+
2022-02-04 Kate Cheney <katherine_che...@apple.com>
Fix App Privacy Report redirect attribution
Modified: trunk/Source/WebCore/platform/graphics/Color.cpp (289121 => 289122)
--- trunk/Source/WebCore/platform/graphics/Color.cpp 2022-02-04 17:06:32 UTC (rev 289121)
+++ trunk/Source/WebCore/platform/graphics/Color.cpp 2022-02-04 17:26:30 UTC (rev 289122)
@@ -28,6 +28,7 @@
#include "ColorLuminance.h"
#include "ColorSerialization.h"
+#include <cmath>
#include <wtf/Assertions.h>
#include <wtf/text/TextStream.h>
@@ -124,6 +125,20 @@
});
}
+bool Color::anyComponentIsNone() const
+{
+ return callOnUnderlyingType([&] (const auto& underlyingColor) {
+ using ColorType = std::decay_t<decltype(underlyingColor)>;
+
+ if constexpr (std::is_same_v<ColorType, SRGBA<uint8_t>>) {
+ return false;
+ } else {
+ auto [c1, c2, c3, alpha] = underlyingColor.unresolved();
+ return std::isnan(c1) || std::isnan(c2) || std::isnan(c3) || std::isnan(alpha);
+ }
+ });
+}
+
Color Color::colorWithAlpha(float alpha) const
{
return callOnUnderlyingType([&] (const auto& underlyingColor) -> Color {
Modified: trunk/Source/WebCore/platform/graphics/Color.h (289121 => 289122)
--- trunk/Source/WebCore/platform/graphics/Color.h 2022-02-04 17:06:32 UTC (rev 289121)
+++ trunk/Source/WebCore/platform/graphics/Color.h 2022-02-04 17:26:30 UTC (rev 289122)
@@ -100,6 +100,8 @@
WEBCORE_EXPORT double luminance() const;
WEBCORE_EXPORT double lightness() const; // FIXME: Replace remaining uses with luminance.
+ bool anyComponentIsNone() const;
+
template<typename Functor> decltype(auto) callOnUnderlyingType(Functor&&) const;
// This will convert the underlying color into ColorType, potentially lossily if the gamut
Modified: trunk/Source/WebCore/platform/graphics/ColorComponents.h (289121 => 289122)
--- trunk/Source/WebCore/platform/graphics/ColorComponents.h 2022-02-04 17:06:32 UTC (rev 289121)
+++ trunk/Source/WebCore/platform/graphics/ColorComponents.h 2022-02-04 17:26:30 UTC (rev 289122)
@@ -29,6 +29,7 @@
#include <array>
#include <cmath>
#include <tuple>
+#include <wtf/MathExtras.h>
namespace WebCore {
@@ -165,7 +166,15 @@
template<typename T, size_t N>
constexpr bool operator==(const ColorComponents<T, N>& a, const ColorComponents<T, N>& b)
{
- return a.components == b.components;
+ for (size_t i = 0; i < N; ++i) {
+ if (a[i] == b[i])
+ continue;
+ if (isNaNConstExpr(a[i]) && isNaNConstExpr(b[i]))
+ continue;
+ return false;
+ }
+
+ return true;
}
template<typename T, size_t N>
Modified: trunk/Source/WebCore/platform/graphics/ColorTypes.h (289121 => 289122)
--- trunk/Source/WebCore/platform/graphics/ColorTypes.h 2022-02-04 17:06:32 UTC (rev 289121)
+++ trunk/Source/WebCore/platform/graphics/ColorTypes.h 2022-02-04 17:26:30 UTC (rev 289122)
@@ -132,24 +132,17 @@
#if ASSERT_ENABLED
-template<typename ComponentType>
-constexpr bool constexprIsNaN(ComponentType value)
-{
- // FIXME: Replace with std::isnan() once std::isnan() is constexpr.
- return value != value;
-}
-
template<typename ColorType, typename std::enable_if_t<std::is_same_v<typename ColorType::ComponentType, float>>* = nullptr>
constexpr void assertInRange(ColorType color)
{
auto components = asColorComponents(color.unresolved());
for (unsigned i = 0; i < 3; ++i) {
- if (constexprIsNaN(components[i]))
+ if (isNaNConstExpr(components[i]))
continue;
ASSERT_WITH_MESSAGE(components[i] >= ColorType::Model::componentInfo[i].min, "Component at index %d is %f and is less than the allowed minimum %f", i, components[i], ColorType::Model::componentInfo[i].min);
ASSERT_WITH_MESSAGE(components[i] <= ColorType::Model::componentInfo[i].max, "Component at index %d is %f and is greater than the allowed maximum %f", i, components[i], ColorType::Model::componentInfo[i].max);
}
- if (!constexprIsNaN(components[3])) {
+ if (!isNaNConstExpr(components[3])) {
ASSERT_WITH_MESSAGE(components[3] >= AlphaTraits<typename ColorType::ComponentType>::transparent, "Alpha is %f and is less than the allowed minimum (transparent) %f", components[3], AlphaTraits<typename ColorType::ComponentType>::transparent);
ASSERT_WITH_MESSAGE(components[3] <= AlphaTraits<typename ColorType::ComponentType>::opaque, "Alpha is %f and is greater than the allowed maximum (opaque) %f", components[3], AlphaTraits<typename ColorType::ComponentType>::opaque);
}
Modified: trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.cpp (289121 => 289122)
--- trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.cpp 2022-02-04 17:06:32 UTC (rev 289121)
+++ trunk/Source/WebCore/platform/graphics/cg/GradientRendererCG.cpp 2022-02-04 17:26:30 UTC (rev 289122)
@@ -386,10 +386,24 @@
}
#endif
+static bool anyComponentIsNone(const GradientColorStops& stops)
+{
+ for (auto& stop : stops) {
+ if (stop.color.anyComponentIsNone())
+ return true;
+ }
+
+ return false;
+}
+
GradientRendererCG::Strategy GradientRendererCG::pickStrategy(ColorInterpolationMethod colorInterpolationMethod, const GradientColorStops& stops) const
{
return WTF::switchOn(colorInterpolationMethod.colorSpace,
[&] (const ColorInterpolationMethod::SRGB&) -> Strategy {
+ // FIXME: As an optimization we can precompute 'none' replacements and create a transformed stop list rather than falling back on CGShadingRef.
+ if (anyComponentIsNone(stops))
+ return makeShading(colorInterpolationMethod, stops);
+
switch (colorInterpolationMethod.alphaPremultiplication) {
case AlphaPremultiplication::Unpremultiplied:
return makeGradient(colorInterpolationMethod, stops);
@@ -552,7 +566,7 @@
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>().resolved());
+ return asColorComponents(color.template toColorTypeLossy<ColorType>().unresolved());
}
);
};
@@ -577,6 +591,10 @@
if (!hasOne)
totalNumberOfStops++;
+ // FIXME: To avoid duplicate work in the shader function, we could precompute a few things:
+ // - If we have a polar coordinate color space, we can pre-fixup the hues, inserting an extra stop at the same offset if both the fixup on the left and right require different results.
+ // - If we have 'none' components, we can precompute 'none' replacements, inserting an extra stop at the same offset if the replacements on the left and right are different.
+
Vector<ColorConvertedToInterpolationColorSpaceStop> convertedStops;
convertedStops.reserveInitialCapacity(totalNumberOfStops);