| Index: Source/core/css/CSSGradientValue.cpp
|
| diff --git a/Source/core/css/CSSGradientValue.cpp b/Source/core/css/CSSGradientValue.cpp
|
| index b4a8229c7ef31dc99b63226ecbba4e2596ab1f9f..59aa9420fd79ba6fd62225e48583cc729b9cc689 100644
|
| --- a/Source/core/css/CSSGradientValue.cpp
|
| +++ b/Source/core/css/CSSGradientValue.cpp
|
| @@ -37,6 +37,7 @@
|
| #include "platform/graphics/Gradient.h"
|
| #include "platform/graphics/GradientGeneratedImage.h"
|
| #include "platform/graphics/Image.h"
|
| +#include "platform/graphics/skia/SkiaUtils.h"
|
| #include "wtf/text/StringBuilder.h"
|
| #include "wtf/text/WTFString.h"
|
|
|
| @@ -114,12 +115,13 @@ struct GradientStop {
|
| PassRefPtrWillBeRawPtr<CSSGradientValue> CSSGradientValue::gradientWithStylesResolved(const TextLinkColors& textLinkColors, Color currentColor)
|
| {
|
| bool derived = false;
|
| - for (unsigned i = 0; i < m_stops.size(); i++)
|
| - if (m_stops[i].m_color->colorIsDerivedFromElement()) {
|
| - m_stops[i].m_colorIsDerivedFromElement = true;
|
| + for (auto& stop : m_stops) {
|
| + if (!stop.isHint() && stop.m_color->colorIsDerivedFromElement()) {
|
| + stop.m_colorIsDerivedFromElement = true;
|
| derived = true;
|
| break;
|
| }
|
| + }
|
|
|
| RefPtrWillBeRawPtr<CSSGradientValue> result = nullptr;
|
| if (!derived)
|
| @@ -133,12 +135,102 @@ PassRefPtrWillBeRawPtr<CSSGradientValue> CSSGradientValue::gradientWithStylesRes
|
| return nullptr;
|
| }
|
|
|
| - for (unsigned i = 0; i < result->m_stops.size(); i++)
|
| - result->m_stops[i].m_resolvedColor = textLinkColors.colorFromPrimitiveValue(result->m_stops[i].m_color.get(), currentColor);
|
| + for (auto& stop : result->m_stops) {
|
| + if (!stop.isHint())
|
| + stop.m_resolvedColor = textLinkColors.colorFromPrimitiveValue(stop.m_color.get(), currentColor);
|
| + }
|
|
|
| return result.release();
|
| }
|
|
|
| +static void replaceColorHintsWithColorStops(Vector<GradientStop>& stops, const Vector<CSSGradientColorStop, 2>& cssGradientStops)
|
| +{
|
| + // This algorithm will replace each color interpolation hint with 9 regular
|
| + // color stops. The color values for the new color stops will be calculated
|
| + // using the color weighting formula defined in the spec. The new color
|
| + // stops will be positioned in such a way that all the pixels between the two
|
| + // user defined color stops have color values close to the interpolation curve.
|
| + // If the hint is closer to the left color stop, add 2 stops to the left and
|
| + // 6 to the right, else add 6 stops to the left and 2 to the right.
|
| + // The color stops on the side with more space start midway because
|
| + // the curve approximates a line in that region.
|
| + // Using this aproximation, it is possible to discern the color steps when
|
| + // the gradient is large. If this becomes an issue, we can consider improving
|
| + // the algorithm, or adding support for color interpolation hints to skia shaders.
|
| +
|
| + int indexOffset = 0;
|
| +
|
| + // The first and the last color stops cannot be color hints.
|
| + for (size_t i = 1; i < cssGradientStops.size() - 1; ++i) {
|
| + if (!cssGradientStops[i].isHint())
|
| + continue;
|
| +
|
| + // The current index of the stops vector.
|
| + size_t x = i + indexOffset;
|
| + ASSERT(x >= 1);
|
| +
|
| + // offsetLeft offset offsetRight
|
| + // |-------------------|---------------------------------|
|
| + // leftDist rightDist
|
| +
|
| + float offsetLeft = stops[x - 1].offset;
|
| + float offsetRight = stops[x + 1].offset;
|
| + float offset = stops[x].offset;
|
| + float leftDist = offset - offsetLeft;
|
| + float rightDist = offsetRight - offset;
|
| + float totalDist = offsetRight - offsetLeft;
|
| +
|
| + Color leftColor = stops[x - 1].color;
|
| + Color rightColor = stops[x + 1].color;
|
| +
|
| + ASSERT(offsetLeft <= offset && offset <= offsetRight);
|
| +
|
| + if (WebCoreFloatNearlyEqual(leftDist, rightDist)) {
|
| + stops.remove(x);
|
| + --indexOffset;
|
| + continue;
|
| + }
|
| +
|
| + if (WebCoreFloatNearlyEqual(leftDist, .0f)) {
|
| + stops[x].color = rightColor;
|
| + continue;
|
| + }
|
| +
|
| + if (WebCoreFloatNearlyEqual(rightDist, .0f)) {
|
| + stops[x].color = leftColor;
|
| + continue;
|
| + }
|
| +
|
| + GradientStop newStops[9];
|
| + // Position the new color stops.
|
| + if (leftDist > rightDist) {
|
| + for (size_t y = 0; y < 7; ++y)
|
| + newStops[y].offset = offsetLeft + leftDist * (7 + y) / 13;
|
| + newStops[7].offset = offset + rightDist / 3;
|
| + newStops[8].offset = offset + rightDist * 2 / 3;
|
| + } else {
|
| + newStops[0].offset = offsetLeft + leftDist / 3;
|
| + newStops[1].offset = offsetLeft + leftDist * 2 / 3;
|
| + for (size_t y = 0; y < 7; ++y)
|
| + newStops[y + 2].offset = offset + rightDist * y / 13;
|
| + }
|
| +
|
| + // calculate colors for the new color hints.
|
| + // The color weighting for the new color stops will be pointRelativeOffset^(ln(0.5)/ln(hintRelativeOffset)).
|
| + float hintRelativeOffset = leftDist / totalDist;
|
| + for (size_t y = 0; y < 9; ++y) {
|
| + float pointRelativeOffset = (newStops[y].offset - offsetLeft) / totalDist;
|
| + float weighting = powf(pointRelativeOffset, logf(.5f) / logf(hintRelativeOffset));
|
| + newStops[y].color = blend(leftColor, rightColor, weighting);
|
| + }
|
| +
|
| + // Replace the color hint with the new color stops.
|
| + stops.remove(x);
|
| + stops.insert(x, newStops, 9);
|
| + indexOffset += 8;
|
| + }
|
| +}
|
| +
|
| void CSSGradientValue::addStops(Gradient* gradient, const CSSToLengthConversionData& conversionData, float maxLengthForRepeat)
|
| {
|
| if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient) {
|
| @@ -166,6 +258,8 @@ void CSSGradientValue::addStops(Gradient* gradient, const CSSToLengthConversionD
|
| float gradientLength = 0;
|
| bool computedGradientLength = false;
|
|
|
| + bool hasHints = false;
|
| +
|
| FloatPoint gradientStart = gradient->p0();
|
| FloatPoint gradientEnd;
|
| if (isLinearGradientValue())
|
| @@ -176,7 +270,10 @@ void CSSGradientValue::addStops(Gradient* gradient, const CSSToLengthConversionD
|
| for (size_t i = 0; i < numStops; ++i) {
|
| const CSSGradientColorStop& stop = m_stops[i];
|
|
|
| - stops[i].color = stop.m_resolvedColor;
|
| + if (stop.isHint())
|
| + hasHints = true;
|
| + else
|
| + stops[i].color = stop.m_resolvedColor;
|
|
|
| if (stop.m_position) {
|
| if (stop.m_position->isPercentage())
|
| @@ -254,6 +351,12 @@ void CSSGradientValue::addStops(Gradient* gradient, const CSSToLengthConversionD
|
| }
|
| }
|
|
|
| + ASSERT(stops.size() == m_stops.size());
|
| + if (hasHints) {
|
| + replaceColorHintsWithColorStops(stops, m_stops);
|
| + numStops = stops.size();
|
| + }
|
| +
|
| // If the gradient is repeating, repeat the color stops.
|
| // We can't just push this logic down into the platform-specific Gradient code,
|
| // because we have to know the extent of the gradient, and possible move the end points.
|
| @@ -469,8 +572,8 @@ bool CSSGradientValue::isCacheable() const
|
|
|
| bool CSSGradientValue::knownToBeOpaque(const RenderObject*) const
|
| {
|
| - for (size_t i = 0; i < m_stops.size(); ++i) {
|
| - if (m_stops[i].m_resolvedColor.hasAlpha())
|
| + for (auto& stop : m_stops) {
|
| + if (!stop.isHint() && stop.m_resolvedColor.hasAlpha())
|
| return false;
|
| }
|
| return true;
|
| @@ -580,11 +683,12 @@ String CSSLinearGradientValue::customCSSText() const
|
| const CSSGradientColorStop& stop = m_stops[i];
|
| if (i)
|
| result.appendLiteral(", ");
|
| - result.append(stop.m_color->cssText());
|
| - if (stop.m_position) {
|
| + if (stop.m_color)
|
| + result.append(stop.m_color->cssText());
|
| + if (stop.m_color && stop.m_position)
|
| result.append(' ');
|
| + if (stop.m_position)
|
| result.append(stop.m_position->cssText());
|
| - }
|
| }
|
|
|
| }
|
| @@ -902,11 +1006,12 @@ String CSSRadialGradientValue::customCSSText() const
|
| const CSSGradientColorStop& stop = m_stops[i];
|
| if (i)
|
| result.appendLiteral(", ");
|
| - result.append(stop.m_color->cssText());
|
| - if (stop.m_position) {
|
| + if (stop.m_color)
|
| + result.append(stop.m_color->cssText());
|
| + if (stop.m_color && stop.m_position)
|
| result.append(' ');
|
| + if (stop.m_position)
|
| result.append(stop.m_position->cssText());
|
| - }
|
| }
|
|
|
| }
|
|
|