Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(123)

Unified Diff: third_party/WebKit/Source/core/css/CSSGradientValue.cpp

Issue 2792163002: Add support for repeating-conic-gradient() (Closed)
Patch Set: baselines Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/css/CSSGradientValue.cpp
diff --git a/third_party/WebKit/Source/core/css/CSSGradientValue.cpp b/third_party/WebKit/Source/core/css/CSSGradientValue.cpp
index 7bfdc5272e978212178863cc3fa13d12a5dd1ed1..0bed19512a45267777c92044beaa6f14bdc2a1f7 100644
--- a/third_party/WebKit/Source/core/css/CSSGradientValue.cpp
+++ b/third_party/WebKit/Source/core/css/CSSGradientValue.cpp
@@ -309,8 +309,10 @@ void CSSGradientValue::addDeprecatedStops(GradientDesc& desc,
}
}
-static bool requiresStopsNormalization(const Vector<GradientStop>& stops,
- CSSGradientValue::GradientDesc& desc) {
+namespace {
+
+bool requiresStopsNormalization(const Vector<GradientStop>& stops,
+ CSSGradientValue::GradientDesc& desc) {
// We need at least two stops to normalize
if (stops.size() < 2)
return false;
@@ -329,8 +331,8 @@ static bool requiresStopsNormalization(const Vector<GradientStop>& stops,
// Redistribute the stops such that they fully cover [0 , 1] and add them to the
// gradient.
-static bool normalizeAndAddStops(const Vector<GradientStop>& stops,
- CSSGradientValue::GradientDesc& desc) {
+bool normalizeAndAddStops(const Vector<GradientStop>& stops,
+ CSSGradientValue::GradientDesc& desc) {
DCHECK_GT(stops.size(), 1u);
const float firstOffset = stops.front().offset;
@@ -371,7 +373,7 @@ static bool normalizeAndAddStops(const Vector<GradientStop>& stops,
// Collapse all negative-offset stops to 0 and compute an interpolated color
// value for that point.
-static void clampNegativeOffsets(Vector<GradientStop>& stops) {
+void clampNegativeOffsets(Vector<GradientStop>& stops) {
float lastNegativeOffset = 0;
for (size_t i = 0; i < stops.size(); ++i) {
@@ -397,10 +399,9 @@ static void clampNegativeOffsets(Vector<GradientStop>& stops) {
}
// Update the linear gradient points to align with the given offset range.
-static void adjustGradientPointsForOffsetRange(
- CSSGradientValue::GradientDesc& desc,
- float firstOffset,
- float lastOffset) {
+void adjustGradientPointsForOffsetRange(CSSGradientValue::GradientDesc& desc,
+ float firstOffset,
+ float lastOffset) {
DCHECK_LE(firstOffset, lastOffset);
const FloatPoint p0 = desc.p0;
@@ -413,10 +414,9 @@ static void adjustGradientPointsForOffsetRange(
}
// Update the radial gradient radii to align with the given offset range.
-static void adjustGradientRadiiForOffsetRange(
- CSSGradientValue::GradientDesc& desc,
- float firstOffset,
- float lastOffset) {
+void adjustGradientRadiiForOffsetRange(CSSGradientValue::GradientDesc& desc,
+ float firstOffset,
+ float lastOffset) {
DCHECK_LE(firstOffset, lastOffset);
// Radial offsets are relative to the [0 , endRadius] segment.
@@ -447,6 +447,101 @@ static void adjustGradientRadiiForOffsetRange(
desc.r1 = adjustedR1;
}
+// Helper for performing on-the-fly out of range color stop interpolation.
+class ConicClamper {
+ STACK_ALLOCATED();
+
+ public:
+ ConicClamper(CSSGradientValue::GradientDesc& desc) : m_desc(desc) {}
+
+ void add(float offset, const Color& color) {
+ addInternal(offset, color);
+ m_prevOffset = offset;
+ m_prevColor = color;
+ }
+
+ private:
+ void addUnique(float offset, const Color& color) {
+ // Skip duplicates.
+ if (m_desc.stops.isEmpty() || offset != m_desc.stops.back().stop ||
+ color != m_desc.stops.back().color) {
+ m_desc.stops.emplace_back(offset, color);
+ }
+ }
+
+ void addInternal(float offset, const Color& color) {
+ if (offset < 0)
+ return;
+
+ if (m_prevOffset < 0 && offset > 0) {
+ addUnique(0, blend(m_prevColor, color,
+ -m_prevOffset / (offset - m_prevOffset)));
+ }
+
+ if (offset <= 1) {
+ addUnique(offset, color);
+ return;
+ }
+
+ if (m_prevOffset < 1) {
+ addUnique(1, blend(m_prevColor, color,
+ (1 - m_prevOffset) / (offset - m_prevOffset)));
+ }
+ }
+
+ CSSGradientValue::GradientDesc& m_desc;
+
+ float m_prevOffset = 0;
+ Color m_prevColor;
+};
+
+void normalizeAndAddConicStops(const Vector<GradientStop>& stops,
+ CSSGradientValue::GradientDesc& desc) {
+ DCHECK(!stops.isEmpty());
+ ConicClamper clamper(desc);
+
+ if (desc.spreadMethod == SpreadMethodPad) {
+ for (const auto& stop : stops)
+ clamper.add(stop.offset, stop.color);
+ return;
+ }
+
+ DCHECK_EQ(desc.spreadMethod, SpreadMethodRepeat);
+
+ // The normalization trick we use for linear and radial doesn't work here,
+ // because the underlying Skia implementation doesn't support conic gradient
+ // tiling. So we emit synthetic stops to cover the whole unit interval.
+ float repeatSpan = stops.back().offset - stops.front().offset;
+ DCHECK_GE(repeatSpan, 0.0f);
+ if (repeatSpan < std::numeric_limits<float>::epsilon()) {
+ // All stops are coincident -> use a single solid color.
+ desc.stops.emplace_back(0, stops.back().color);
+ return;
+ }
+
+ // Compute an offset base as a repetition of stops[0].offset such that
+ // [ offsetBase, offsetBase + repeatSpan ] contains 0
+ // (aka the largest repeat value for stops[0] less than 0).
+ float offset = fmodf(stops.front().offset, repeatSpan) -
+ (stops.front().offset < 0 ? 0 : repeatSpan);
+ DCHECK_LE(offset, 0);
+ DCHECK_GE(offset + repeatSpan, 0);
+
+ // Start throwing repeating values at the clamper.
+ do {
+ const float offsetBase = offset;
+ for (const auto& stop : stops) {
+ offset = offsetBase + stop.offset - stops.front().offset;
+ clamper.add(offset, stop.color);
+
+ if (offset >= 1)
+ break;
+ }
+ } while (offset < 1);
+}
+
+} // anonymous ns
+
void CSSGradientValue::addStops(CSSGradientValue::GradientDesc& desc,
const CSSToLengthConversionData& conversionData,
const LayoutObject& object) {
@@ -575,9 +670,7 @@ void CSSGradientValue::addStops(CSSGradientValue::GradientDesc& desc,
// At this point we have a fully resolved set of stops. Time to perform
// adjustments for repeat gradients and degenerate values if needed.
- // Note: the normalization trick doesn't work for conic gradients, because
- // the underlying Skia implementation doesn't support tiling.
- if (isConicGradientValue() || !requiresStopsNormalization(stops, desc)) {
+ if (!requiresStopsNormalization(stops, desc)) {
// No normalization required, just add the current stops.
for (const auto& stop : stops)
desc.stops.emplace_back(stop.offset, stop.color);
@@ -604,6 +697,9 @@ void CSSGradientValue::addStops(CSSGradientValue::GradientDesc& desc,
stops.back().offset);
}
break;
+ case ConicGradientClass:
+ normalizeAndAddConicStops(stops, desc);
+ break;
default:
NOTREACHED();
}
@@ -1398,9 +1494,8 @@ PassRefPtr<Gradient> CSSConicGradientValue::createGradient(
m_repeating ? SpreadMethodRepeat : SpreadMethodPad);
addStops(desc, conversionData, object);
- RefPtr<Gradient> gradient =
- Gradient::createConic(position, angle, desc.spreadMethod,
- Gradient::ColorInterpolation::Premultiplied);
+ RefPtr<Gradient> gradient = Gradient::createConic(
+ position, angle, Gradient::ColorInterpolation::Premultiplied);
gradient->addColorStops(desc.stops);
return gradient.release();

Powered by Google App Engine
This is Rietveld 408576698