Ignore:
Timestamp:
Nov 19, 2017, 3:06:03 PM (7 years ago)
Author:
[email protected]
Message:

Clean up gradient code in preparation for conic gradients
https://bugs.webkit.org/show_bug.cgi?id=179595

Patch by Sam Weinig <[email protected]> on 2017-11-19
Reviewed by Darin Adler.

  • Switches Gradient to use a Varient with type specific data structs and makes the points / radii immutable.
  • Replaces CSSGradientValue::addStops with template function CSSGradientValue::computeStops which dispatches to a helper adoptor struct which is passed in for gradient type specific values, rather than switching on type in the function itself. In addition, the function no longer mutates a Gradient, but rather mutates type specific the data struct (e.g. Linear or Radial for now).
  • css/CSSGradientValue.cpp:

(WebCore::LinearGradientAdaptor::LinearGradientAdaptor):
(WebCore::RadialGradientAdaptor::RadialGradientAdaptor):

Add helper structs for the now templatized CSSGradientValue::computeStops that
implement type specific functionality.

(WebCore::CSSGradientValue::computeStops):

Renamed from addStops. Now takes a GradientAdaptor wrapper which abstracts out
the gradient type specific functionlity.

(WebCore::CSSLinearGradientValue::createGradient):
(WebCore::CSSRadialGradientValue::createGradient):
(WebCore::CSSConicGradientValue::createGradient):

Update to call computeStops and use new Gradient create function.

  • css/CSSGradientValue.h:

Update for new signature for computeStops.

  • html/canvas/CanvasGradient.cpp:

(WebCore::CanvasGradient::CanvasGradient):

Update to use new Gradient create function.

  • inspector/InspectorCanvas.cpp:

(WebCore::InspectorCanvas::buildArrayForCanvasGradient):

Update to get gradient point / radii information from the data accessor.

  • platform/graphics/Gradient.cpp:

(WebCore::Gradient::Gradient):

Update constructors to the new data structs.

(WebCore::Gradient::type const):

Add helper to get the type of the varient, need for inspector code.

(WebCore::Gradient::adjustParametersForTiledDrawing):
(WebCore::Gradient::isZeroSize const):
(WebCore::Gradient::hash const):

Update to switch on the underlying variant to get data values.

(WebCore::Gradient::addColorStop):

Simplify by having one addColorStop call the other.

(WebCore::Gradient::setSortedColorStops):

Added. Replaces setStopsSorted. Used now that CSSGradientValue computes and sorts
its color stops before creating the gradient.

  • platform/graphics/Gradient.h:

(WebCore::Gradient::create):

Replace create functions with ones that take data structs.

(WebCore::Gradient::data const):

Add accessor for the underlying variant.

(WebCore::Gradient::isRadial const): Deleted.

Replaced by type accessor.

(WebCore::Gradient::p0 const): Deleted.
(WebCore::Gradient::p1 const): Deleted.
(WebCore::Gradient::setP0): Deleted.
(WebCore::Gradient::setP1): Deleted.
(WebCore::Gradient::startRadius const): Deleted.
(WebCore::Gradient::endRadius const): Deleted.
(WebCore::Gradient::setStartRadius): Deleted.
(WebCore::Gradient::setEndRadius): Deleted.

Point and radii are now immutable.

(WebCore::Gradient::setStopsSorted): Deleted.

Replaced by setSortedColorStops function.

  • platform/graphics/GraphicsContext.cpp:

(WebCore::GraphicsContext::fillRect):

Pass the GraphicsContext by reference.

  • platform/graphics/cairo/GradientCairo.cpp:

(WebCore::Gradient::createPlatformGradient):

Update to switch on the underlying variant to get data values.

(WebCore::Gradient::fill):

Pass the GraphicsContext by reference.

  • platform/graphics/cg/GradientCG.cpp:

(WebCore::Gradient::platformDestroy):

Use nullptr.

(WebCore::Gradient::fill):

Pass the GraphicsContext by reference.

(WebCore::Gradient::paint):

Update to switch on the underlying variant to get data values.

  • platform/graphics/cg/GraphicsContextCG.cpp:

(WebCore::GraphicsContext::fillPath):
(WebCore::GraphicsContext::strokePath):
(WebCore::GraphicsContext::fillRect):
(WebCore::GraphicsContext::strokeRect):

Pass the GraphicsContext by reference.

  • platform/graphics/win/GradientDirect2D.cpp:

(WebCore::Gradient::generateGradient):

Update to switch on the underlying variant to get data values.

(WebCore::Gradient::fill):

Pass the GraphicsContext by reference.

  • rendering/RenderThemeIOS.mm:

(WebCore::RenderThemeIOS::paintProgressBar):

  • rendering/svg/RenderSVGResourceLinearGradient.cpp:

(WebCore::RenderSVGResourceLinearGradient::buildGradient const):

  • rendering/svg/RenderSVGResourceRadialGradient.cpp:

(WebCore::RenderSVGResourceRadialGradient::buildGradient const):

Update for new Gradient create function signature.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/css/CSSGradientValue.cpp

    r224165 r225036  
    136136}
    137137
    138 void CSSGradientValue::addStops(Gradient& gradient, const CSSToLengthConversionData& conversionData, float maxLengthForRepeat)
     138class LinearGradientAdapter {
     139public:
     140    explicit LinearGradientAdapter(Gradient::LinearData& data)
     141        : m_data(data)
     142    {
     143    }
     144
     145    FloatPoint startPoint() const { return m_data.point0; }
     146    FloatPoint endPoint() const { return m_data.point1; }
     147    float maxExtent(float, float) const { return 1; }
     148
     149    void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
     150    {
     151        float firstOffset = stops.first().offset;
     152        float lastOffset = stops.last().offset;
     153        if (firstOffset != lastOffset) {
     154            float scale = lastOffset - firstOffset;
     155
     156            for (auto& stop : stops)
     157                stop.offset = (stop.offset - firstOffset) / scale;
     158
     159            auto p0 = m_data.point0;
     160            auto p1 = m_data.point1;
     161            m_data.point0 = { p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y()) };
     162            m_data.point1 = { p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y()) };
     163        } else {
     164            // There's a single position that is outside the scale, clamp the positions to 1.
     165            for (auto& stop : stops)
     166                stop.offset = 1;
     167        }
     168    }
     169
     170private:
     171    Gradient::LinearData& m_data;
     172};
     173
     174class RadialGradientAdapter {
     175public:
     176    explicit RadialGradientAdapter(Gradient::RadialData& data)
     177        : m_data(data)
     178    {
     179    }
     180
     181    FloatPoint startPoint() const { return m_data.point0; }
     182    FloatPoint endPoint() const { return m_data.point0 + FloatSize { m_data.endRadius, 0 }; }
     183
     184    // Radial gradients may need to extend further than the endpoints, because they have
     185    // to repeat out to the corners of the box.
     186    float maxExtent(float maxLengthForRepeat, float gradientLength) const
     187    {
     188        if (maxLengthForRepeat > gradientLength)
     189            return gradientLength > 0 ? maxLengthForRepeat / gradientLength : 0;
     190        return 1;
     191    }
     192
     193    void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
     194    {
     195        auto numStops = stops.size();
     196
     197        // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
     198        float firstOffset = 0;
     199        float lastOffset = stops.last().offset;
     200        float scale = lastOffset - firstOffset;
     201
     202        // Reset points below 0 to the first visible color.
     203        size_t firstZeroOrGreaterIndex = numStops;
     204        for (size_t i = 0; i < numStops; ++i) {
     205            if (stops[i].offset >= 0) {
     206                firstZeroOrGreaterIndex = i;
     207                break;
     208            }
     209        }
     210
     211        if (firstZeroOrGreaterIndex > 0) {
     212            if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
     213                float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
     214                float nextOffset = stops[firstZeroOrGreaterIndex].offset;
     215
     216                float interStopProportion = -prevOffset / (nextOffset - prevOffset);
     217                // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
     218                Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
     219
     220                // Clamp the positions to 0 and set the color.
     221                for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
     222                    stops[i].offset = 0;
     223                    stops[i].color = blendedColor;
     224                }
     225            } else {
     226                // All stops are below 0; just clamp them.
     227                for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
     228                    stops[i].offset = 0;
     229            }
     230        }
     231
     232        for (auto& stop : stops)
     233            stop.offset /= scale;
     234
     235        m_data.startRadius *= scale;
     236        m_data.endRadius *= scale;
     237    }
     238
     239private:
     240    Gradient::RadialData& m_data;
     241};
     242
     243template<typename GradientAdapter>
     244Gradient::ColorStopVector CSSGradientValue::computeStops(GradientAdapter& gradient, const CSSToLengthConversionData& conversionData, float maxLengthForRepeat)
    139245{
    140246    if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient) {
    141247        sortStopsIfNeeded();
    142248
    143         for (unsigned i = 0; i < m_stops.size(); i++) {
    144             const CSSGradientColorStop& stop = m_stops[i];
    145 
     249        Gradient::ColorStopVector result;
     250        result.reserveInitialCapacity(m_stops.size());
     251
     252        for (auto& stop : m_stops) {
    146253            float offset;
    147254            if (stop.m_position->isPercentage())
     
    150257                offset = stop.m_position->floatValue(CSSPrimitiveValue::CSS_NUMBER);
    151258
    152             gradient.addColorStop(offset, stop.m_resolvedColor);
    153         }
    154 
    155         // The back end already sorted the stops.
    156         gradient.setStopsSorted(true);
    157         return;
     259            result.uncheckedAppend({ offset, stop.m_resolvedColor });
     260        }
     261
     262        return result;
    158263    }
    159264
    160265    size_t numStops = m_stops.size();
    161 
    162266    Vector<GradientStop> stops(numStops);
    163267
    164     float gradientLength = 0;
    165     bool computedGradientLength = false;
    166 
    167     FloatPoint gradientStart = gradient.p0();
    168     FloatPoint gradientEnd;
    169     if (isLinearGradientValue())
    170         gradientEnd = gradient.p1();
    171     else if (isRadialGradientValue())
    172         gradientEnd = gradientStart + FloatSize(gradient.endRadius(), 0);
     268    auto gradientStart = gradient.startPoint();
     269    auto gradientEnd = gradient.endPoint();
     270
     271    auto gradientSize = gradientStart - gradientEnd;
     272    float gradientLength = gradientSize.diagonalLength();
    173273
    174274    for (size_t i = 0; i < numStops; ++i) {
    175         const CSSGradientColorStop& stop = m_stops[i];
     275        auto& stop = m_stops[i];
    176276
    177277        stops[i].isMidpoint = stop.isMidpoint;
     
    179279
    180280        if (stop.m_position) {
    181             const CSSPrimitiveValue& positionValue = *stop.m_position;
     281            auto& positionValue = *stop.m_position;
    182282            if (positionValue.isPercentage())
    183283                stops[i].offset = positionValue.floatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
    184284            else if (positionValue.isLength() || positionValue.isViewportPercentageLength() || positionValue.isCalculatedPercentageWithLength()) {
    185                 if (!computedGradientLength) {
    186                     FloatSize gradientSize(gradientStart - gradientEnd);
    187                     gradientLength = gradientSize.diagonalLength();
    188                 }
    189285                float length;
    190286                if (positionValue.isLength())
     
    343439        // If the difference in the positions of the first and last color-stops is 0,
    344440        // the gradient defines a solid-color image with the color of the last color-stop in the rule.
    345         float gradientRange = stops[numStops - 1].offset - stops[0].offset;
     441        float gradientRange = stops.last().offset - stops.first().offset;
    346442        if (!gradientRange) {
    347443            stops.first().offset = 0;
     
    350446            numStops = 1;
    351447        } else {
    352             float maxExtent = 1;
    353 
    354             // Radial gradients may need to extend further than the endpoints, because they have
    355             // to repeat out to the corners of the box.
    356             if (isRadialGradientValue()) {
    357                 if (!computedGradientLength) {
    358                     FloatSize gradientSize(gradientStart - gradientEnd);
    359                     gradientLength = gradientSize.diagonalLength();
    360                 }
    361 
    362                 if (maxLengthForRepeat > gradientLength)
    363                     maxExtent = gradientLength > 0 ? maxLengthForRepeat / gradientLength : 0;
    364             }
     448            float maxExtent = gradient.maxExtent(maxLengthForRepeat, gradientLength);
    365449
    366450            size_t originalNumStops = numStops;
     
    408492    }
    409493
    410     numStops = stops.size();
    411 
    412494    // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and adjusting the stops.
    413     if (numStops > 1 && (stops[0].offset < 0 || stops[numStops - 1].offset > 1)) {
    414         if (isLinearGradientValue()) {
    415             float firstOffset = stops[0].offset;
    416             float lastOffset = stops[numStops - 1].offset;
    417             if (firstOffset != lastOffset) {
    418                 float scale = lastOffset - firstOffset;
    419 
    420                 for (size_t i = 0; i < numStops; ++i)
    421                     stops[i].offset = (stops[i].offset - firstOffset) / scale;
    422 
    423                 FloatPoint p0 = gradient.p0();
    424                 FloatPoint p1 = gradient.p1();
    425                 gradient.setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
    426                 gradient.setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
    427             } else {
    428                 // There's a single position that is outside the scale, clamp the positions to 1.
    429                 for (size_t i = 0; i < numStops; ++i)
    430                     stops[i].offset = 1;
    431             }
    432         } else if (isRadialGradientValue()) {
    433             // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
    434             float firstOffset = 0;
    435             float lastOffset = stops[numStops - 1].offset;
    436             float scale = lastOffset - firstOffset;
    437 
    438             // Reset points below 0 to the first visible color.
    439             size_t firstZeroOrGreaterIndex = numStops;
    440             for (size_t i = 0; i < numStops; ++i) {
    441                 if (stops[i].offset >= 0) {
    442                     firstZeroOrGreaterIndex = i;
    443                     break;
    444                 }
    445             }
    446 
    447             if (firstZeroOrGreaterIndex > 0) {
    448                 if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
    449                     float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
    450                     float nextOffset = stops[firstZeroOrGreaterIndex].offset;
    451 
    452                     float interStopProportion = -prevOffset / (nextOffset - prevOffset);
    453                     // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
    454                     Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
    455 
    456                     // Clamp the positions to 0 and set the color.
    457                     for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
    458                         stops[i].offset = 0;
    459                         stops[i].color = blendedColor;
    460                     }
    461                 } else {
    462                     // All stops are below 0; just clamp them.
    463                     for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
    464                         stops[i].offset = 0;
    465                 }
    466             }
    467 
    468             for (size_t i = 0; i < numStops; ++i)
    469                 stops[i].offset /= scale;
    470 
    471             gradient.setStartRadius(gradient.startRadius() * scale);
    472             gradient.setEndRadius(gradient.endRadius() * scale);
    473         }
    474     }
    475 
    476     for (unsigned i = 0; i < numStops; i++)
    477         gradient.addColorStop(stops[i].offset, stops[i].color);
    478 
    479     gradient.setStopsSorted(true);
     495    if (stops.size() > 1 && (stops.first().offset < 0 || stops.last().offset > 1))
     496        gradient.normalizeStopsAndEndpointsOutsideRange(stops);
     497   
     498    Gradient::ColorStopVector result;
     499    result.reserveInitialCapacity(stops.size());
     500    for (auto& stop : stops)
     501        result.uncheckedAppend({ stop.offset, stop.color });
     502
     503    return result;
    480504}
    481505
     
    625649
    626650        for (unsigned i = 0; i < m_stops.size(); i++) {
    627             const CSSGradientColorStop& stop = m_stops[i];
     651            auto& stop = m_stops[i];
    628652            result.appendLiteral(", ");
    629653            result.append(stop.m_color->cssText());
     
    802826            ASSERT_NOT_REACHED();
    803827        }
    804 
    805     }
    806 
    807     Ref<Gradient> gradient = Gradient::create(firstPoint, secondPoint);
    808 
    809     // Now add the stops.
    810     addStops(gradient, conversionData, 1);
    811 
     828    }
     829
     830    Gradient::LinearData data { firstPoint, secondPoint };
     831    LinearGradientAdapter adapter { data };
     832    auto stops = computeStops(adapter, conversionData, 1);
     833
     834    auto gradient = Gradient::create(WTFMove(data));
     835    gradient->setSortedColorStops(WTFMove(stops));
    812836    return gradient;
    813837}
     
    12141238    }
    12151239
    1216     Ref<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius, aspectRatio);
    1217 
    1218     // addStops() only uses maxExtent for repeating gradients.
     1240    // computeStops() only uses maxExtent for repeating gradients.
    12191241    float maxExtent = 0;
    12201242    if (m_repeating) {
     
    12231245    }
    12241246
    1225     // Now add the stops.
    1226     addStops(gradient, conversionData, maxExtent);
    1227 
     1247    Gradient::RadialData data { firstPoint, secondPoint, firstRadius, secondRadius, aspectRatio };
     1248    RadialGradientAdapter adapter { data };
     1249    auto stops = computeStops(adapter, conversionData, maxExtent);
     1250
     1251    auto gradient = Gradient::create(WTFMove(data));
     1252    gradient->setSortedColorStops(WTFMove(stops));
    12281253    return gradient;
    12291254}
     
    13271352{
    13281353    // FIXME: Implement.
    1329     return Gradient::create(FloatPoint { }, FloatPoint { });
     1354    return Gradient::create(Gradient::LinearData { });
    13301355}
    13311356
Note: See TracChangeset for help on using the changeset viewer.