1cad810f21b803229eb11403f9209855525a25d57Steve Block/*
2cad810f21b803229eb11403f9209855525a25d57Steve Block * Copyright (C) 2008 Apple Inc.  All rights reserved.
3cad810f21b803229eb11403f9209855525a25d57Steve Block *
4cad810f21b803229eb11403f9209855525a25d57Steve Block * Redistribution and use in source and binary forms, with or without
5cad810f21b803229eb11403f9209855525a25d57Steve Block * modification, are permitted provided that the following conditions
6cad810f21b803229eb11403f9209855525a25d57Steve Block * are met:
7cad810f21b803229eb11403f9209855525a25d57Steve Block * 1. Redistributions of source code must retain the above copyright
8cad810f21b803229eb11403f9209855525a25d57Steve Block *    notice, this list of conditions and the following disclaimer.
9cad810f21b803229eb11403f9209855525a25d57Steve Block * 2. Redistributions in binary form must reproduce the above copyright
10cad810f21b803229eb11403f9209855525a25d57Steve Block *    notice, this list of conditions and the following disclaimer in the
11cad810f21b803229eb11403f9209855525a25d57Steve Block *    documentation and/or other materials provided with the distribution.
12cad810f21b803229eb11403f9209855525a25d57Steve Block *
13cad810f21b803229eb11403f9209855525a25d57Steve Block * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14cad810f21b803229eb11403f9209855525a25d57Steve Block * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15cad810f21b803229eb11403f9209855525a25d57Steve Block * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16cad810f21b803229eb11403f9209855525a25d57Steve Block * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17cad810f21b803229eb11403f9209855525a25d57Steve Block * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18cad810f21b803229eb11403f9209855525a25d57Steve Block * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19cad810f21b803229eb11403f9209855525a25d57Steve Block * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20cad810f21b803229eb11403f9209855525a25d57Steve Block * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21cad810f21b803229eb11403f9209855525a25d57Steve Block * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22cad810f21b803229eb11403f9209855525a25d57Steve Block * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23cad810f21b803229eb11403f9209855525a25d57Steve Block * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24cad810f21b803229eb11403f9209855525a25d57Steve Block */
25cad810f21b803229eb11403f9209855525a25d57Steve Block
26cad810f21b803229eb11403f9209855525a25d57Steve Block#include "config.h"
27cad810f21b803229eb11403f9209855525a25d57Steve Block#include "CSSGradientValue.h"
28cad810f21b803229eb11403f9209855525a25d57Steve Block
29cad810f21b803229eb11403f9209855525a25d57Steve Block#include "CSSValueKeywords.h"
30cad810f21b803229eb11403f9209855525a25d57Steve Block#include "CSSStyleSelector.h"
31cad810f21b803229eb11403f9209855525a25d57Steve Block#include "GeneratedImage.h"
32cad810f21b803229eb11403f9209855525a25d57Steve Block#include "Gradient.h"
33cad810f21b803229eb11403f9209855525a25d57Steve Block#include "Image.h"
34cad810f21b803229eb11403f9209855525a25d57Steve Block#include "IntSize.h"
35cad810f21b803229eb11403f9209855525a25d57Steve Block#include "IntSizeHash.h"
36cad810f21b803229eb11403f9209855525a25d57Steve Block#include "NodeRenderStyle.h"
37cad810f21b803229eb11403f9209855525a25d57Steve Block#include "PlatformString.h"
38cad810f21b803229eb11403f9209855525a25d57Steve Block#include "RenderObject.h"
39cad810f21b803229eb11403f9209855525a25d57Steve Block
40cad810f21b803229eb11403f9209855525a25d57Steve Blockusing namespace std;
41cad810f21b803229eb11403f9209855525a25d57Steve Block
42cad810f21b803229eb11403f9209855525a25d57Steve Blocknamespace WebCore {
43cad810f21b803229eb11403f9209855525a25d57Steve Block
442fc2651226baac27029e38c9d6ef883fa32084dbSteve BlockPassRefPtr<Image> CSSGradientValue::image(RenderObject* renderer, const IntSize& size)
45cad810f21b803229eb11403f9209855525a25d57Steve Block{
46cad810f21b803229eb11403f9209855525a25d57Steve Block    if (size.isEmpty())
47cad810f21b803229eb11403f9209855525a25d57Steve Block        return 0;
48cad810f21b803229eb11403f9209855525a25d57Steve Block
492fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    bool cacheable = isCacheable();
502fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (cacheable) {
512fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (!m_clients.contains(renderer))
522fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            return 0;
532fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
542fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // Need to look up our size.  Create a string of width*height to use as a hash key.
552fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        Image* result = getImage(renderer, size);
562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (result)
572fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            return result;
582fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
592fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
60cad810f21b803229eb11403f9209855525a25d57Steve Block    // We need to create an image.
61cad810f21b803229eb11403f9209855525a25d57Steve Block    RefPtr<Image> newImage = GeneratedImage::create(createGradient(renderer, size), size);
622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (cacheable)
632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        putImage(size, newImage);
64cad810f21b803229eb11403f9209855525a25d57Steve Block
652fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    return newImage.release();
66cad810f21b803229eb11403f9209855525a25d57Steve Block}
67cad810f21b803229eb11403f9209855525a25d57Steve Block
68cad810f21b803229eb11403f9209855525a25d57Steve Block// Should only ever be called for deprecated gradients.
69cad810f21b803229eb11403f9209855525a25d57Steve Blockstatic inline bool compareStops(const CSSGradientColorStop& a, const CSSGradientColorStop& b)
70cad810f21b803229eb11403f9209855525a25d57Steve Block{
71cad810f21b803229eb11403f9209855525a25d57Steve Block    double aVal = a.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER);
72cad810f21b803229eb11403f9209855525a25d57Steve Block    double bVal = b.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER);
73cad810f21b803229eb11403f9209855525a25d57Steve Block
74cad810f21b803229eb11403f9209855525a25d57Steve Block    return aVal < bVal;
75cad810f21b803229eb11403f9209855525a25d57Steve Block}
76cad810f21b803229eb11403f9209855525a25d57Steve Block
77cad810f21b803229eb11403f9209855525a25d57Steve Blockvoid CSSGradientValue::sortStopsIfNeeded()
78cad810f21b803229eb11403f9209855525a25d57Steve Block{
79cad810f21b803229eb11403f9209855525a25d57Steve Block    ASSERT(m_deprecatedType);
80cad810f21b803229eb11403f9209855525a25d57Steve Block    if (!m_stopsSorted) {
81cad810f21b803229eb11403f9209855525a25d57Steve Block        if (m_stops.size())
82cad810f21b803229eb11403f9209855525a25d57Steve Block            std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
83cad810f21b803229eb11403f9209855525a25d57Steve Block        m_stopsSorted = true;
84cad810f21b803229eb11403f9209855525a25d57Steve Block    }
85cad810f21b803229eb11403f9209855525a25d57Steve Block}
86cad810f21b803229eb11403f9209855525a25d57Steve Block
87cad810f21b803229eb11403f9209855525a25d57Steve Blockstatic inline int blend(int from, int to, float progress)
88cad810f21b803229eb11403f9209855525a25d57Steve Block{
89cad810f21b803229eb11403f9209855525a25d57Steve Block    return int(from + (to - from) * progress);
90cad810f21b803229eb11403f9209855525a25d57Steve Block}
91cad810f21b803229eb11403f9209855525a25d57Steve Block
92cad810f21b803229eb11403f9209855525a25d57Steve Blockstatic inline Color blend(const Color& from, const Color& to, float progress)
93cad810f21b803229eb11403f9209855525a25d57Steve Block{
94cad810f21b803229eb11403f9209855525a25d57Steve Block    // FIXME: when we interpolate gradients using premultiplied colors, this should also do premultiplication.
95cad810f21b803229eb11403f9209855525a25d57Steve Block    return Color(blend(from.red(), to.red(), progress),
96cad810f21b803229eb11403f9209855525a25d57Steve Block        blend(from.green(), to.green(), progress),
97cad810f21b803229eb11403f9209855525a25d57Steve Block        blend(from.blue(), to.blue(), progress),
98cad810f21b803229eb11403f9209855525a25d57Steve Block        blend(from.alpha(), to.alpha(), progress));
99cad810f21b803229eb11403f9209855525a25d57Steve Block}
100cad810f21b803229eb11403f9209855525a25d57Steve Block
101cad810f21b803229eb11403f9209855525a25d57Steve Blockstruct GradientStop {
102cad810f21b803229eb11403f9209855525a25d57Steve Block    Color color;
103cad810f21b803229eb11403f9209855525a25d57Steve Block    float offset;
104cad810f21b803229eb11403f9209855525a25d57Steve Block    bool specified;
105cad810f21b803229eb11403f9209855525a25d57Steve Block
106cad810f21b803229eb11403f9209855525a25d57Steve Block    GradientStop()
107cad810f21b803229eb11403f9209855525a25d57Steve Block        : offset(0)
108cad810f21b803229eb11403f9209855525a25d57Steve Block        , specified(false)
109cad810f21b803229eb11403f9209855525a25d57Steve Block    { }
110cad810f21b803229eb11403f9209855525a25d57Steve Block};
111cad810f21b803229eb11403f9209855525a25d57Steve Block
112cad810f21b803229eb11403f9209855525a25d57Steve Blockvoid CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, RenderStyle* rootStyle, float maxLengthForRepeat)
113cad810f21b803229eb11403f9209855525a25d57Steve Block{
114cad810f21b803229eb11403f9209855525a25d57Steve Block    RenderStyle* style = renderer->style();
115cad810f21b803229eb11403f9209855525a25d57Steve Block
116cad810f21b803229eb11403f9209855525a25d57Steve Block    if (m_deprecatedType) {
117cad810f21b803229eb11403f9209855525a25d57Steve Block        sortStopsIfNeeded();
118cad810f21b803229eb11403f9209855525a25d57Steve Block
119cad810f21b803229eb11403f9209855525a25d57Steve Block        // We have to resolve colors.
120cad810f21b803229eb11403f9209855525a25d57Steve Block        for (unsigned i = 0; i < m_stops.size(); i++) {
121cad810f21b803229eb11403f9209855525a25d57Steve Block            const CSSGradientColorStop& stop = m_stops[i];
122cad810f21b803229eb11403f9209855525a25d57Steve Block            Color color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
123cad810f21b803229eb11403f9209855525a25d57Steve Block
124cad810f21b803229eb11403f9209855525a25d57Steve Block            float offset;
125cad810f21b803229eb11403f9209855525a25d57Steve Block            if (stop.m_position->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
126cad810f21b803229eb11403f9209855525a25d57Steve Block                offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
127cad810f21b803229eb11403f9209855525a25d57Steve Block            else
128cad810f21b803229eb11403f9209855525a25d57Steve Block                offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_NUMBER);
129cad810f21b803229eb11403f9209855525a25d57Steve Block
130cad810f21b803229eb11403f9209855525a25d57Steve Block            gradient->addColorStop(offset, color);
131cad810f21b803229eb11403f9209855525a25d57Steve Block        }
132cad810f21b803229eb11403f9209855525a25d57Steve Block
133cad810f21b803229eb11403f9209855525a25d57Steve Block        // The back end already sorted the stops.
134cad810f21b803229eb11403f9209855525a25d57Steve Block        gradient->setStopsSorted(true);
135cad810f21b803229eb11403f9209855525a25d57Steve Block        return;
136cad810f21b803229eb11403f9209855525a25d57Steve Block    }
137cad810f21b803229eb11403f9209855525a25d57Steve Block
138cad810f21b803229eb11403f9209855525a25d57Steve Block    size_t numStops = m_stops.size();
139cad810f21b803229eb11403f9209855525a25d57Steve Block
140cad810f21b803229eb11403f9209855525a25d57Steve Block    Vector<GradientStop> stops(numStops);
141cad810f21b803229eb11403f9209855525a25d57Steve Block
142cad810f21b803229eb11403f9209855525a25d57Steve Block    float gradientLength = 0;
143cad810f21b803229eb11403f9209855525a25d57Steve Block    bool computedGradientLength = false;
144cad810f21b803229eb11403f9209855525a25d57Steve Block
145cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint gradientStart = gradient->p0();
146cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint gradientEnd;
147cad810f21b803229eb11403f9209855525a25d57Steve Block    if (isLinearGradient())
148cad810f21b803229eb11403f9209855525a25d57Steve Block        gradientEnd = gradient->p1();
149cad810f21b803229eb11403f9209855525a25d57Steve Block    else if (isRadialGradient())
150cad810f21b803229eb11403f9209855525a25d57Steve Block        gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0);
151cad810f21b803229eb11403f9209855525a25d57Steve Block
152cad810f21b803229eb11403f9209855525a25d57Steve Block    for (size_t i = 0; i < numStops; ++i) {
153cad810f21b803229eb11403f9209855525a25d57Steve Block        const CSSGradientColorStop& stop = m_stops[i];
154cad810f21b803229eb11403f9209855525a25d57Steve Block
155cad810f21b803229eb11403f9209855525a25d57Steve Block        stops[i].color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
156cad810f21b803229eb11403f9209855525a25d57Steve Block
157cad810f21b803229eb11403f9209855525a25d57Steve Block        if (stop.m_position) {
158cad810f21b803229eb11403f9209855525a25d57Steve Block            int type = stop.m_position->primitiveType();
159cad810f21b803229eb11403f9209855525a25d57Steve Block            if (type == CSSPrimitiveValue::CSS_PERCENTAGE)
160cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
161cad810f21b803229eb11403f9209855525a25d57Steve Block            else if (CSSPrimitiveValue::isUnitTypeLength(type)) {
162cad810f21b803229eb11403f9209855525a25d57Steve Block                float length = stop.m_position->computeLengthFloat(style, rootStyle, style->effectiveZoom());
163cad810f21b803229eb11403f9209855525a25d57Steve Block                if (!computedGradientLength) {
164cad810f21b803229eb11403f9209855525a25d57Steve Block                    FloatSize gradientSize(gradientStart - gradientEnd);
165cad810f21b803229eb11403f9209855525a25d57Steve Block                    gradientLength = gradientSize.diagonalLength();
166cad810f21b803229eb11403f9209855525a25d57Steve Block                }
167cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0;
168cad810f21b803229eb11403f9209855525a25d57Steve Block            } else {
169cad810f21b803229eb11403f9209855525a25d57Steve Block                ASSERT_NOT_REACHED();
170cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].offset = 0;
171cad810f21b803229eb11403f9209855525a25d57Steve Block            }
172cad810f21b803229eb11403f9209855525a25d57Steve Block            stops[i].specified = true;
173cad810f21b803229eb11403f9209855525a25d57Steve Block        } else {
174cad810f21b803229eb11403f9209855525a25d57Steve Block            // If the first color-stop does not have a position, its position defaults to 0%.
175cad810f21b803229eb11403f9209855525a25d57Steve Block            // If the last color-stop does not have a position, its position defaults to 100%.
176cad810f21b803229eb11403f9209855525a25d57Steve Block            if (!i) {
177cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].offset = 0;
178cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].specified = true;
179cad810f21b803229eb11403f9209855525a25d57Steve Block            } else if (numStops > 1 && i == numStops - 1) {
180cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].offset = 1;
181cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].specified = true;
182cad810f21b803229eb11403f9209855525a25d57Steve Block            }
183cad810f21b803229eb11403f9209855525a25d57Steve Block        }
184cad810f21b803229eb11403f9209855525a25d57Steve Block
185cad810f21b803229eb11403f9209855525a25d57Steve Block        // If a color-stop has a position that is less than the specified position of any
186cad810f21b803229eb11403f9209855525a25d57Steve Block        // color-stop before it in the list, its position is changed to be equal to the
187cad810f21b803229eb11403f9209855525a25d57Steve Block        // largest specified position of any color-stop before it.
188cad810f21b803229eb11403f9209855525a25d57Steve Block        if (stops[i].specified && i > 0) {
189cad810f21b803229eb11403f9209855525a25d57Steve Block            size_t prevSpecifiedIndex;
190cad810f21b803229eb11403f9209855525a25d57Steve Block            for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
191cad810f21b803229eb11403f9209855525a25d57Steve Block                if (stops[prevSpecifiedIndex].specified)
192cad810f21b803229eb11403f9209855525a25d57Steve Block                    break;
193cad810f21b803229eb11403f9209855525a25d57Steve Block            }
194cad810f21b803229eb11403f9209855525a25d57Steve Block
195cad810f21b803229eb11403f9209855525a25d57Steve Block            if (stops[i].offset < stops[prevSpecifiedIndex].offset)
196cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].offset = stops[prevSpecifiedIndex].offset;
197cad810f21b803229eb11403f9209855525a25d57Steve Block        }
198cad810f21b803229eb11403f9209855525a25d57Steve Block    }
199cad810f21b803229eb11403f9209855525a25d57Steve Block
200cad810f21b803229eb11403f9209855525a25d57Steve Block    ASSERT(stops[0].specified && stops[numStops - 1].specified);
201cad810f21b803229eb11403f9209855525a25d57Steve Block
202cad810f21b803229eb11403f9209855525a25d57Steve Block    // If any color-stop still does not have a position, then, for each run of adjacent
203cad810f21b803229eb11403f9209855525a25d57Steve Block    // color-stops without positions, set their positions so that they are evenly spaced
204cad810f21b803229eb11403f9209855525a25d57Steve Block    // between the preceding and following color-stops with positions.
205cad810f21b803229eb11403f9209855525a25d57Steve Block    if (numStops > 2) {
206cad810f21b803229eb11403f9209855525a25d57Steve Block        size_t unspecifiedRunStart = 0;
207cad810f21b803229eb11403f9209855525a25d57Steve Block        bool inUnspecifiedRun = false;
208cad810f21b803229eb11403f9209855525a25d57Steve Block
209cad810f21b803229eb11403f9209855525a25d57Steve Block        for (size_t i = 0; i < numStops; ++i) {
210cad810f21b803229eb11403f9209855525a25d57Steve Block            if (!stops[i].specified && !inUnspecifiedRun) {
211cad810f21b803229eb11403f9209855525a25d57Steve Block                unspecifiedRunStart = i;
212cad810f21b803229eb11403f9209855525a25d57Steve Block                inUnspecifiedRun = true;
213cad810f21b803229eb11403f9209855525a25d57Steve Block            } else if (stops[i].specified && inUnspecifiedRun) {
214cad810f21b803229eb11403f9209855525a25d57Steve Block                size_t unspecifiedRunEnd = i;
215cad810f21b803229eb11403f9209855525a25d57Steve Block
216cad810f21b803229eb11403f9209855525a25d57Steve Block                if (unspecifiedRunStart < unspecifiedRunEnd) {
217cad810f21b803229eb11403f9209855525a25d57Steve Block                    float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
218cad810f21b803229eb11403f9209855525a25d57Steve Block                    float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
219cad810f21b803229eb11403f9209855525a25d57Steve Block                    float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
220cad810f21b803229eb11403f9209855525a25d57Steve Block
221cad810f21b803229eb11403f9209855525a25d57Steve Block                    for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
222cad810f21b803229eb11403f9209855525a25d57Steve Block                        stops[j].offset = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
223cad810f21b803229eb11403f9209855525a25d57Steve Block                }
224cad810f21b803229eb11403f9209855525a25d57Steve Block
225cad810f21b803229eb11403f9209855525a25d57Steve Block                inUnspecifiedRun = false;
226cad810f21b803229eb11403f9209855525a25d57Steve Block            }
227cad810f21b803229eb11403f9209855525a25d57Steve Block        }
228cad810f21b803229eb11403f9209855525a25d57Steve Block    }
229cad810f21b803229eb11403f9209855525a25d57Steve Block
230cad810f21b803229eb11403f9209855525a25d57Steve Block    // If the gradient is repeating, repeat the color stops.
231cad810f21b803229eb11403f9209855525a25d57Steve Block    // We can't just push this logic down into the platform-specific Gradient code,
232cad810f21b803229eb11403f9209855525a25d57Steve Block    // because we have to know the extent of the gradient, and possible move the end points.
233cad810f21b803229eb11403f9209855525a25d57Steve Block    if (m_repeating && numStops > 1) {
234ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        // If the difference in the positions of the first and last color-stops is 0,
235ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        // the gradient defines a solid-color image with the color of the last color-stop in the rule.
236ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        float gradientRange = stops[numStops - 1].offset - stops[0].offset;
237ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        if (!gradientRange) {
238ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            stops.first().offset = 0;
239ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            stops.first().color = stops.last().color;
240ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            stops.shrink(1);
241ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            numStops = 1;
242ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        } else {
243ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            float maxExtent = 1;
244cad810f21b803229eb11403f9209855525a25d57Steve Block
245ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            // Radial gradients may need to extend further than the endpoints, because they have
246ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            // to repeat out to the corners of the box.
247ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            if (isRadialGradient()) {
248ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                if (!computedGradientLength) {
249ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    FloatSize gradientSize(gradientStart - gradientEnd);
250ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    gradientLength = gradientSize.diagonalLength();
251ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                }
252ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch
253ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                if (maxLengthForRepeat > gradientLength)
254ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    maxExtent = maxLengthForRepeat / gradientLength;
255ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            }
256cad810f21b803229eb11403f9209855525a25d57Steve Block
257ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            size_t originalNumStops = numStops;
258ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            size_t originalFirstStopIndex = 0;
259ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch
260ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            // Work backwards from the first, adding stops until we get one before 0.
261ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            float firstOffset = stops[0].offset;
262ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            if (firstOffset > 0) {
263ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                float currOffset = firstOffset;
264ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                size_t srcStopOrdinal = originalNumStops - 1;
265ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch
266ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                while (true) {
267ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    GradientStop newStop = stops[originalFirstStopIndex + srcStopOrdinal];
268ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    newStop.offset = currOffset;
269ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    stops.prepend(newStop);
270ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    ++originalFirstStopIndex;
271ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    if (currOffset < 0)
272ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                        break;
273ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch
274ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    if (srcStopOrdinal)
275ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                        currOffset -= stops[originalFirstStopIndex + srcStopOrdinal].offset - stops[originalFirstStopIndex + srcStopOrdinal - 1].offset;
276ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    srcStopOrdinal = (srcStopOrdinal + originalNumStops - 1) % originalNumStops;
277ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                }
278cad810f21b803229eb11403f9209855525a25d57Steve Block            }
279ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch
280ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            // Work forwards from the end, adding stops until we get one after 1.
281ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            float lastOffset = stops[stops.size() - 1].offset;
282ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            if (lastOffset < maxExtent) {
283ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                float currOffset = lastOffset;
2842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                size_t srcStopOrdinal = originalFirstStopIndex;
285ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch
286ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                while (true) {
287ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    GradientStop newStop = stops[srcStopOrdinal];
288ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    newStop.offset = currOffset;
289ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    stops.append(newStop);
290ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    if (currOffset > maxExtent)
291ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                        break;
292ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    if (srcStopOrdinal < originalNumStops - 1)
2932fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                        currOffset += stops[srcStopOrdinal + 1].offset - stops[srcStopOrdinal].offset;
294ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                    srcStopOrdinal = (srcStopOrdinal + 1) % originalNumStops;
295ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                }
296cad810f21b803229eb11403f9209855525a25d57Steve Block            }
297cad810f21b803229eb11403f9209855525a25d57Steve Block        }
298cad810f21b803229eb11403f9209855525a25d57Steve Block    }
299cad810f21b803229eb11403f9209855525a25d57Steve Block
300cad810f21b803229eb11403f9209855525a25d57Steve Block    numStops = stops.size();
301cad810f21b803229eb11403f9209855525a25d57Steve Block
302cad810f21b803229eb11403f9209855525a25d57Steve Block    // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and adjusting the stops.
303cad810f21b803229eb11403f9209855525a25d57Steve Block    if (numStops > 1 && (stops[0].offset < 0 || stops[numStops - 1].offset > 1)) {
304cad810f21b803229eb11403f9209855525a25d57Steve Block        if (isLinearGradient()) {
305cad810f21b803229eb11403f9209855525a25d57Steve Block            float firstOffset = stops[0].offset;
306cad810f21b803229eb11403f9209855525a25d57Steve Block            float lastOffset = stops[numStops - 1].offset;
307cad810f21b803229eb11403f9209855525a25d57Steve Block            float scale = lastOffset - firstOffset;
308cad810f21b803229eb11403f9209855525a25d57Steve Block
309cad810f21b803229eb11403f9209855525a25d57Steve Block            for (size_t i = 0; i < numStops; ++i)
310cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].offset = (stops[i].offset - firstOffset) / scale;
311cad810f21b803229eb11403f9209855525a25d57Steve Block
312cad810f21b803229eb11403f9209855525a25d57Steve Block            FloatPoint p0 = gradient->p0();
313cad810f21b803229eb11403f9209855525a25d57Steve Block            FloatPoint p1 = gradient->p1();
314cad810f21b803229eb11403f9209855525a25d57Steve Block            gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
315cad810f21b803229eb11403f9209855525a25d57Steve Block            gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
316cad810f21b803229eb11403f9209855525a25d57Steve Block        } else if (isRadialGradient()) {
317cad810f21b803229eb11403f9209855525a25d57Steve Block            // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
318cad810f21b803229eb11403f9209855525a25d57Steve Block            float firstOffset = 0;
319cad810f21b803229eb11403f9209855525a25d57Steve Block            float lastOffset = stops[numStops - 1].offset;
320cad810f21b803229eb11403f9209855525a25d57Steve Block            float scale = lastOffset - firstOffset;
321cad810f21b803229eb11403f9209855525a25d57Steve Block
322cad810f21b803229eb11403f9209855525a25d57Steve Block            // Reset points below 0 to the first visible color.
323cad810f21b803229eb11403f9209855525a25d57Steve Block            size_t firstZeroOrGreaterIndex = numStops;
324cad810f21b803229eb11403f9209855525a25d57Steve Block            for (size_t i = 0; i < numStops; ++i) {
325cad810f21b803229eb11403f9209855525a25d57Steve Block                if (stops[i].offset >= 0) {
326cad810f21b803229eb11403f9209855525a25d57Steve Block                    firstZeroOrGreaterIndex = i;
327cad810f21b803229eb11403f9209855525a25d57Steve Block                    break;
328cad810f21b803229eb11403f9209855525a25d57Steve Block                }
329cad810f21b803229eb11403f9209855525a25d57Steve Block            }
330cad810f21b803229eb11403f9209855525a25d57Steve Block
331cad810f21b803229eb11403f9209855525a25d57Steve Block            if (firstZeroOrGreaterIndex > 0) {
332cad810f21b803229eb11403f9209855525a25d57Steve Block                if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
333cad810f21b803229eb11403f9209855525a25d57Steve Block                    float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
334cad810f21b803229eb11403f9209855525a25d57Steve Block                    float nextOffset = stops[firstZeroOrGreaterIndex].offset;
335cad810f21b803229eb11403f9209855525a25d57Steve Block
336cad810f21b803229eb11403f9209855525a25d57Steve Block                    float interStopProportion = -prevOffset / (nextOffset - prevOffset);
337cad810f21b803229eb11403f9209855525a25d57Steve Block                    Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
338cad810f21b803229eb11403f9209855525a25d57Steve Block
339cad810f21b803229eb11403f9209855525a25d57Steve Block                    // Clamp the positions to 0 and set the color.
340cad810f21b803229eb11403f9209855525a25d57Steve Block                    for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
341cad810f21b803229eb11403f9209855525a25d57Steve Block                        stops[i].offset = 0;
342cad810f21b803229eb11403f9209855525a25d57Steve Block                        stops[i].color = blendedColor;
343cad810f21b803229eb11403f9209855525a25d57Steve Block                    }
344cad810f21b803229eb11403f9209855525a25d57Steve Block                } else {
345cad810f21b803229eb11403f9209855525a25d57Steve Block                    // All stops are below 0; just clamp them.
346cad810f21b803229eb11403f9209855525a25d57Steve Block                    for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
347cad810f21b803229eb11403f9209855525a25d57Steve Block                        stops[i].offset = 0;
348cad810f21b803229eb11403f9209855525a25d57Steve Block                }
349cad810f21b803229eb11403f9209855525a25d57Steve Block            }
350cad810f21b803229eb11403f9209855525a25d57Steve Block
351cad810f21b803229eb11403f9209855525a25d57Steve Block            for (size_t i = 0; i < numStops; ++i)
352cad810f21b803229eb11403f9209855525a25d57Steve Block                stops[i].offset /= scale;
353cad810f21b803229eb11403f9209855525a25d57Steve Block
354cad810f21b803229eb11403f9209855525a25d57Steve Block            gradient->setStartRadius(gradient->startRadius() * scale);
355cad810f21b803229eb11403f9209855525a25d57Steve Block            gradient->setEndRadius(gradient->endRadius() * scale);
356cad810f21b803229eb11403f9209855525a25d57Steve Block        }
357cad810f21b803229eb11403f9209855525a25d57Steve Block    }
358cad810f21b803229eb11403f9209855525a25d57Steve Block
359cad810f21b803229eb11403f9209855525a25d57Steve Block    for (unsigned i = 0; i < numStops; i++)
360cad810f21b803229eb11403f9209855525a25d57Steve Block        gradient->addColorStop(stops[i].offset, stops[i].color);
361cad810f21b803229eb11403f9209855525a25d57Steve Block
362cad810f21b803229eb11403f9209855525a25d57Steve Block    gradient->setStopsSorted(true);
363cad810f21b803229eb11403f9209855525a25d57Steve Block}
364cad810f21b803229eb11403f9209855525a25d57Steve Block
365cad810f21b803229eb11403f9209855525a25d57Steve Blockstatic float positionFromValue(CSSPrimitiveValue* value, RenderStyle* style, RenderStyle* rootStyle, const IntSize& size, bool isHorizontal)
366cad810f21b803229eb11403f9209855525a25d57Steve Block{
367cad810f21b803229eb11403f9209855525a25d57Steve Block    float zoomFactor = style->effectiveZoom();
368cad810f21b803229eb11403f9209855525a25d57Steve Block
369cad810f21b803229eb11403f9209855525a25d57Steve Block    switch (value->primitiveType()) {
370cad810f21b803229eb11403f9209855525a25d57Steve Block    case CSSPrimitiveValue::CSS_NUMBER:
371cad810f21b803229eb11403f9209855525a25d57Steve Block        return value->getFloatValue() * zoomFactor;
372cad810f21b803229eb11403f9209855525a25d57Steve Block
373cad810f21b803229eb11403f9209855525a25d57Steve Block    case CSSPrimitiveValue::CSS_PERCENTAGE:
374cad810f21b803229eb11403f9209855525a25d57Steve Block        return value->getFloatValue() / 100.f * (isHorizontal ? size.width() : size.height());
375cad810f21b803229eb11403f9209855525a25d57Steve Block
376cad810f21b803229eb11403f9209855525a25d57Steve Block    case CSSPrimitiveValue::CSS_IDENT:
377cad810f21b803229eb11403f9209855525a25d57Steve Block        switch (value->getIdent()) {
378cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueTop:
379cad810f21b803229eb11403f9209855525a25d57Steve Block                ASSERT(!isHorizontal);
380cad810f21b803229eb11403f9209855525a25d57Steve Block                return 0;
381cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueLeft:
382cad810f21b803229eb11403f9209855525a25d57Steve Block                ASSERT(isHorizontal);
383cad810f21b803229eb11403f9209855525a25d57Steve Block                return 0;
384cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueBottom:
385cad810f21b803229eb11403f9209855525a25d57Steve Block                ASSERT(!isHorizontal);
386cad810f21b803229eb11403f9209855525a25d57Steve Block                return size.height();
387cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueRight:
388cad810f21b803229eb11403f9209855525a25d57Steve Block                ASSERT(isHorizontal);
389cad810f21b803229eb11403f9209855525a25d57Steve Block                return size.width();
390cad810f21b803229eb11403f9209855525a25d57Steve Block        }
391cad810f21b803229eb11403f9209855525a25d57Steve Block
392cad810f21b803229eb11403f9209855525a25d57Steve Block    default:
393cad810f21b803229eb11403f9209855525a25d57Steve Block        return value->computeLengthFloat(style, rootStyle, zoomFactor);
394cad810f21b803229eb11403f9209855525a25d57Steve Block    }
395cad810f21b803229eb11403f9209855525a25d57Steve Block}
396cad810f21b803229eb11403f9209855525a25d57Steve Block
397cad810f21b803229eb11403f9209855525a25d57Steve BlockFloatPoint CSSGradientValue::computeEndPoint(CSSPrimitiveValue* first, CSSPrimitiveValue* second, RenderStyle* style, RenderStyle* rootStyle, const IntSize& size)
398cad810f21b803229eb11403f9209855525a25d57Steve Block{
399cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint result;
400cad810f21b803229eb11403f9209855525a25d57Steve Block
401cad810f21b803229eb11403f9209855525a25d57Steve Block    if (first)
402cad810f21b803229eb11403f9209855525a25d57Steve Block        result.setX(positionFromValue(first, style, rootStyle, size, true));
403cad810f21b803229eb11403f9209855525a25d57Steve Block
404cad810f21b803229eb11403f9209855525a25d57Steve Block    if (second)
405cad810f21b803229eb11403f9209855525a25d57Steve Block        result.setY(positionFromValue(second, style, rootStyle, size, false));
406cad810f21b803229eb11403f9209855525a25d57Steve Block
407cad810f21b803229eb11403f9209855525a25d57Steve Block    return result;
408cad810f21b803229eb11403f9209855525a25d57Steve Block}
409cad810f21b803229eb11403f9209855525a25d57Steve Block
4102fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockbool CSSGradientValue::isCacheable() const
4112fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
4122fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    for (size_t i = 0; i < m_stops.size(); ++i) {
4132fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        const CSSGradientColorStop& stop = m_stops[i];
4142fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (!stop.m_position)
4152fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            continue;
4162fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4172fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        unsigned short unitType = stop.m_position->primitiveType();
4182fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (unitType == CSSPrimitiveValue::CSS_EMS || unitType == CSSPrimitiveValue::CSS_EXS || unitType == CSSPrimitiveValue::CSS_REMS)
4192fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            return false;
4202fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
4212fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4222fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    return true;
4232fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
4242fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
425cad810f21b803229eb11403f9209855525a25d57Steve BlockString CSSLinearGradientValue::cssText() const
426cad810f21b803229eb11403f9209855525a25d57Steve Block{
427cad810f21b803229eb11403f9209855525a25d57Steve Block    String result;
428cad810f21b803229eb11403f9209855525a25d57Steve Block    if (m_deprecatedType) {
429cad810f21b803229eb11403f9209855525a25d57Steve Block        result = "-webkit-gradient(linear, ";
430cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_firstX->cssText() + " ";
431cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_firstY->cssText() + ", ";
432cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_secondX->cssText() + " ";
433cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_secondY->cssText();
434cad810f21b803229eb11403f9209855525a25d57Steve Block
435cad810f21b803229eb11403f9209855525a25d57Steve Block        for (unsigned i = 0; i < m_stops.size(); i++) {
436cad810f21b803229eb11403f9209855525a25d57Steve Block            const CSSGradientColorStop& stop = m_stops[i];
437cad810f21b803229eb11403f9209855525a25d57Steve Block            result += ", ";
438cad810f21b803229eb11403f9209855525a25d57Steve Block            if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
439cad810f21b803229eb11403f9209855525a25d57Steve Block                result += "from(" + stop.m_color->cssText() + ")";
440cad810f21b803229eb11403f9209855525a25d57Steve Block            else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
441cad810f21b803229eb11403f9209855525a25d57Steve Block                result += "to(" + stop.m_color->cssText() + ")";
442cad810f21b803229eb11403f9209855525a25d57Steve Block            else
443cad810f21b803229eb11403f9209855525a25d57Steve Block                result += "color-stop(" + String::number(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + stop.m_color->cssText() + ")";
444cad810f21b803229eb11403f9209855525a25d57Steve Block        }
445cad810f21b803229eb11403f9209855525a25d57Steve Block    } else {
446cad810f21b803229eb11403f9209855525a25d57Steve Block        result = m_repeating ? "-webkit-repeating-linear-gradient(" : "-webkit-linear-gradient(";
447cad810f21b803229eb11403f9209855525a25d57Steve Block        if (m_angle)
448cad810f21b803229eb11403f9209855525a25d57Steve Block            result += m_angle->cssText();
449cad810f21b803229eb11403f9209855525a25d57Steve Block        else {
450cad810f21b803229eb11403f9209855525a25d57Steve Block            if (m_firstX && m_firstY)
451cad810f21b803229eb11403f9209855525a25d57Steve Block                result += m_firstX->cssText() + " " + m_firstY->cssText();
452cad810f21b803229eb11403f9209855525a25d57Steve Block            else if (m_firstX || m_firstY) {
453cad810f21b803229eb11403f9209855525a25d57Steve Block                if (m_firstX)
454cad810f21b803229eb11403f9209855525a25d57Steve Block                    result += m_firstX->cssText();
455cad810f21b803229eb11403f9209855525a25d57Steve Block
456cad810f21b803229eb11403f9209855525a25d57Steve Block                if (m_firstY)
457cad810f21b803229eb11403f9209855525a25d57Steve Block                    result += m_firstY->cssText();
458cad810f21b803229eb11403f9209855525a25d57Steve Block            }
459cad810f21b803229eb11403f9209855525a25d57Steve Block        }
460cad810f21b803229eb11403f9209855525a25d57Steve Block
461cad810f21b803229eb11403f9209855525a25d57Steve Block        for (unsigned i = 0; i < m_stops.size(); i++) {
462cad810f21b803229eb11403f9209855525a25d57Steve Block            const CSSGradientColorStop& stop = m_stops[i];
463cad810f21b803229eb11403f9209855525a25d57Steve Block            result += ", ";
464cad810f21b803229eb11403f9209855525a25d57Steve Block            result += stop.m_color->cssText();
465cad810f21b803229eb11403f9209855525a25d57Steve Block            if (stop.m_position)
466cad810f21b803229eb11403f9209855525a25d57Steve Block                result += " " + stop.m_position->cssText();
467cad810f21b803229eb11403f9209855525a25d57Steve Block        }
468cad810f21b803229eb11403f9209855525a25d57Steve Block    }
469cad810f21b803229eb11403f9209855525a25d57Steve Block
470cad810f21b803229eb11403f9209855525a25d57Steve Block    result += ")";
471cad810f21b803229eb11403f9209855525a25d57Steve Block    return result;
472cad810f21b803229eb11403f9209855525a25d57Steve Block}
473cad810f21b803229eb11403f9209855525a25d57Steve Block
474cad810f21b803229eb11403f9209855525a25d57Steve Block// Compute the endpoints so that a gradient of the given angle covers a box of the given size.
475cad810f21b803229eb11403f9209855525a25d57Steve Blockstatic void endPointsFromAngle(float angleDeg, const IntSize& size, FloatPoint& firstPoint, FloatPoint& secondPoint)
476cad810f21b803229eb11403f9209855525a25d57Steve Block{
477cad810f21b803229eb11403f9209855525a25d57Steve Block    angleDeg = fmodf(angleDeg, 360);
478cad810f21b803229eb11403f9209855525a25d57Steve Block    if (angleDeg < 0)
479cad810f21b803229eb11403f9209855525a25d57Steve Block        angleDeg += 360;
480cad810f21b803229eb11403f9209855525a25d57Steve Block
481cad810f21b803229eb11403f9209855525a25d57Steve Block    if (!angleDeg) {
482cad810f21b803229eb11403f9209855525a25d57Steve Block        firstPoint.set(0, 0);
483cad810f21b803229eb11403f9209855525a25d57Steve Block        secondPoint.set(size.width(), 0);
484cad810f21b803229eb11403f9209855525a25d57Steve Block        return;
485cad810f21b803229eb11403f9209855525a25d57Steve Block    }
486cad810f21b803229eb11403f9209855525a25d57Steve Block
487cad810f21b803229eb11403f9209855525a25d57Steve Block    if (angleDeg == 90) {
488cad810f21b803229eb11403f9209855525a25d57Steve Block        firstPoint.set(0, size.height());
489cad810f21b803229eb11403f9209855525a25d57Steve Block        secondPoint.set(0, 0);
490cad810f21b803229eb11403f9209855525a25d57Steve Block        return;
491cad810f21b803229eb11403f9209855525a25d57Steve Block    }
492cad810f21b803229eb11403f9209855525a25d57Steve Block
493cad810f21b803229eb11403f9209855525a25d57Steve Block    if (angleDeg == 180) {
494cad810f21b803229eb11403f9209855525a25d57Steve Block        firstPoint.set(size.width(), 0);
495cad810f21b803229eb11403f9209855525a25d57Steve Block        secondPoint.set(0, 0);
496cad810f21b803229eb11403f9209855525a25d57Steve Block        return;
497cad810f21b803229eb11403f9209855525a25d57Steve Block    }
498cad810f21b803229eb11403f9209855525a25d57Steve Block
499cad810f21b803229eb11403f9209855525a25d57Steve Block    float slope = tan(deg2rad(angleDeg));
500cad810f21b803229eb11403f9209855525a25d57Steve Block
501cad810f21b803229eb11403f9209855525a25d57Steve Block    // We find the endpoint by computing the intersection of the line formed by the slope,
502cad810f21b803229eb11403f9209855525a25d57Steve Block    // and a line perpendicular to it that intersects the corner.
503cad810f21b803229eb11403f9209855525a25d57Steve Block    float perpendicularSlope = -1 / slope;
504cad810f21b803229eb11403f9209855525a25d57Steve Block
505cad810f21b803229eb11403f9209855525a25d57Steve Block    // Compute start corner relative to center.
506cad810f21b803229eb11403f9209855525a25d57Steve Block    float halfHeight = size.height() / 2;
507cad810f21b803229eb11403f9209855525a25d57Steve Block    float halfWidth = size.width() / 2;
508cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint endCorner;
509cad810f21b803229eb11403f9209855525a25d57Steve Block    if (angleDeg < 90)
510cad810f21b803229eb11403f9209855525a25d57Steve Block        endCorner.set(halfWidth, halfHeight);
511cad810f21b803229eb11403f9209855525a25d57Steve Block    else if (angleDeg < 180)
512cad810f21b803229eb11403f9209855525a25d57Steve Block        endCorner.set(-halfWidth, halfHeight);
513cad810f21b803229eb11403f9209855525a25d57Steve Block    else if (angleDeg < 270)
514cad810f21b803229eb11403f9209855525a25d57Steve Block        endCorner.set(-halfWidth, -halfHeight);
515cad810f21b803229eb11403f9209855525a25d57Steve Block    else
516cad810f21b803229eb11403f9209855525a25d57Steve Block        endCorner.set(halfWidth, -halfHeight);
517cad810f21b803229eb11403f9209855525a25d57Steve Block
518cad810f21b803229eb11403f9209855525a25d57Steve Block    // Compute c (of y = mx + c) using the corner point.
519cad810f21b803229eb11403f9209855525a25d57Steve Block    float c = endCorner.y() - perpendicularSlope * endCorner.x();
520cad810f21b803229eb11403f9209855525a25d57Steve Block    float endX = c / (slope - perpendicularSlope);
521cad810f21b803229eb11403f9209855525a25d57Steve Block    float endY = perpendicularSlope * endX + c;
522cad810f21b803229eb11403f9209855525a25d57Steve Block
523cad810f21b803229eb11403f9209855525a25d57Steve Block    // We computed the end point, so set the second point, flipping the Y to account for angles going anticlockwise.
524cad810f21b803229eb11403f9209855525a25d57Steve Block    secondPoint.set(halfWidth + endX, size.height() - (halfHeight + endY));
525cad810f21b803229eb11403f9209855525a25d57Steve Block    // Reflect around the center for the start point.
526cad810f21b803229eb11403f9209855525a25d57Steve Block    firstPoint.set(size.width() - secondPoint.x(), size.height() - secondPoint.y());
527cad810f21b803229eb11403f9209855525a25d57Steve Block}
528cad810f21b803229eb11403f9209855525a25d57Steve Block
529cad810f21b803229eb11403f9209855525a25d57Steve BlockPassRefPtr<Gradient> CSSLinearGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
530cad810f21b803229eb11403f9209855525a25d57Steve Block{
531cad810f21b803229eb11403f9209855525a25d57Steve Block    ASSERT(!size.isEmpty());
532cad810f21b803229eb11403f9209855525a25d57Steve Block
533cad810f21b803229eb11403f9209855525a25d57Steve Block    RenderStyle* rootStyle = renderer->document()->documentElement()->renderStyle();
534cad810f21b803229eb11403f9209855525a25d57Steve Block
535cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint firstPoint;
536cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint secondPoint;
537cad810f21b803229eb11403f9209855525a25d57Steve Block    if (m_angle) {
538cad810f21b803229eb11403f9209855525a25d57Steve Block        float angle = m_angle->getFloatValue(CSSPrimitiveValue::CSS_DEG);
539cad810f21b803229eb11403f9209855525a25d57Steve Block        endPointsFromAngle(angle, size, firstPoint, secondPoint);
540cad810f21b803229eb11403f9209855525a25d57Steve Block    } else {
541cad810f21b803229eb11403f9209855525a25d57Steve Block        firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), renderer->style(), rootStyle, size);
542cad810f21b803229eb11403f9209855525a25d57Steve Block
543cad810f21b803229eb11403f9209855525a25d57Steve Block        if (m_secondX || m_secondY)
544cad810f21b803229eb11403f9209855525a25d57Steve Block            secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), renderer->style(), rootStyle, size);
545cad810f21b803229eb11403f9209855525a25d57Steve Block        else {
546cad810f21b803229eb11403f9209855525a25d57Steve Block            if (m_firstX)
547cad810f21b803229eb11403f9209855525a25d57Steve Block                secondPoint.setX(size.width() - firstPoint.x());
548cad810f21b803229eb11403f9209855525a25d57Steve Block            if (m_firstY)
549cad810f21b803229eb11403f9209855525a25d57Steve Block                secondPoint.setY(size.height() - firstPoint.y());
550cad810f21b803229eb11403f9209855525a25d57Steve Block        }
551cad810f21b803229eb11403f9209855525a25d57Steve Block    }
552cad810f21b803229eb11403f9209855525a25d57Steve Block
553cad810f21b803229eb11403f9209855525a25d57Steve Block    RefPtr<Gradient> gradient = Gradient::create(firstPoint, secondPoint);
554cad810f21b803229eb11403f9209855525a25d57Steve Block
555cad810f21b803229eb11403f9209855525a25d57Steve Block    // Now add the stops.
556cad810f21b803229eb11403f9209855525a25d57Steve Block    addStops(gradient.get(), renderer, rootStyle, 1);
557cad810f21b803229eb11403f9209855525a25d57Steve Block
558cad810f21b803229eb11403f9209855525a25d57Steve Block    return gradient.release();
559cad810f21b803229eb11403f9209855525a25d57Steve Block}
560cad810f21b803229eb11403f9209855525a25d57Steve Block
561cad810f21b803229eb11403f9209855525a25d57Steve BlockString CSSRadialGradientValue::cssText() const
562cad810f21b803229eb11403f9209855525a25d57Steve Block{
563cad810f21b803229eb11403f9209855525a25d57Steve Block    String result;
564cad810f21b803229eb11403f9209855525a25d57Steve Block
565cad810f21b803229eb11403f9209855525a25d57Steve Block    if (m_deprecatedType) {
566cad810f21b803229eb11403f9209855525a25d57Steve Block        result = "-webkit-gradient(radial, ";
567cad810f21b803229eb11403f9209855525a25d57Steve Block
568cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_firstX->cssText() + " ";
569cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_firstY->cssText() + ", ";
570cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_firstRadius->cssText() + ", ";
571cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_secondX->cssText() + " ";
572cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_secondY->cssText();
573cad810f21b803229eb11403f9209855525a25d57Steve Block        result += ", ";
574cad810f21b803229eb11403f9209855525a25d57Steve Block        result += m_secondRadius->cssText();
575cad810f21b803229eb11403f9209855525a25d57Steve Block
576cad810f21b803229eb11403f9209855525a25d57Steve Block        // FIXME: share?
577cad810f21b803229eb11403f9209855525a25d57Steve Block        for (unsigned i = 0; i < m_stops.size(); i++) {
578cad810f21b803229eb11403f9209855525a25d57Steve Block            const CSSGradientColorStop& stop = m_stops[i];
579cad810f21b803229eb11403f9209855525a25d57Steve Block            result += ", ";
580cad810f21b803229eb11403f9209855525a25d57Steve Block            if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
581cad810f21b803229eb11403f9209855525a25d57Steve Block                result += "from(" + stop.m_color->cssText() + ")";
582cad810f21b803229eb11403f9209855525a25d57Steve Block            else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
583cad810f21b803229eb11403f9209855525a25d57Steve Block                result += "to(" + stop.m_color->cssText() + ")";
584cad810f21b803229eb11403f9209855525a25d57Steve Block            else
585cad810f21b803229eb11403f9209855525a25d57Steve Block                result += "color-stop(" + String::number(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + stop.m_color->cssText() + ")";
586cad810f21b803229eb11403f9209855525a25d57Steve Block        }
587cad810f21b803229eb11403f9209855525a25d57Steve Block    } else {
588cad810f21b803229eb11403f9209855525a25d57Steve Block
589cad810f21b803229eb11403f9209855525a25d57Steve Block        result = m_repeating ? "-webkit-repeating-radial-gradient(" : "-webkit-radial-gradient(";
590cad810f21b803229eb11403f9209855525a25d57Steve Block        if (m_firstX && m_firstY) {
591cad810f21b803229eb11403f9209855525a25d57Steve Block            result += m_firstX->cssText() + " " + m_firstY->cssText();
592cad810f21b803229eb11403f9209855525a25d57Steve Block        } else if (m_firstX)
593cad810f21b803229eb11403f9209855525a25d57Steve Block            result += m_firstX->cssText();
594cad810f21b803229eb11403f9209855525a25d57Steve Block         else if (m_firstY)
595cad810f21b803229eb11403f9209855525a25d57Steve Block            result += m_firstY->cssText();
596cad810f21b803229eb11403f9209855525a25d57Steve Block        else
597cad810f21b803229eb11403f9209855525a25d57Steve Block            result += "center";
598cad810f21b803229eb11403f9209855525a25d57Steve Block
599cad810f21b803229eb11403f9209855525a25d57Steve Block
600cad810f21b803229eb11403f9209855525a25d57Steve Block        if (m_shape || m_sizingBehavior) {
601cad810f21b803229eb11403f9209855525a25d57Steve Block            result += ", ";
602cad810f21b803229eb11403f9209855525a25d57Steve Block            if (m_shape)
603cad810f21b803229eb11403f9209855525a25d57Steve Block                result += m_shape->cssText() + " ";
604cad810f21b803229eb11403f9209855525a25d57Steve Block            else
605cad810f21b803229eb11403f9209855525a25d57Steve Block                result += "ellipse ";
606cad810f21b803229eb11403f9209855525a25d57Steve Block
607cad810f21b803229eb11403f9209855525a25d57Steve Block            if (m_sizingBehavior)
608cad810f21b803229eb11403f9209855525a25d57Steve Block                result += m_sizingBehavior->cssText();
609cad810f21b803229eb11403f9209855525a25d57Steve Block            else
610cad810f21b803229eb11403f9209855525a25d57Steve Block                result += "cover";
611cad810f21b803229eb11403f9209855525a25d57Steve Block
612cad810f21b803229eb11403f9209855525a25d57Steve Block        } else if (m_endHorizontalSize && m_endVerticalSize) {
613cad810f21b803229eb11403f9209855525a25d57Steve Block            result += ", ";
614cad810f21b803229eb11403f9209855525a25d57Steve Block            result += m_endHorizontalSize->cssText() + " " + m_endVerticalSize->cssText();
615cad810f21b803229eb11403f9209855525a25d57Steve Block        }
616cad810f21b803229eb11403f9209855525a25d57Steve Block
617cad810f21b803229eb11403f9209855525a25d57Steve Block        for (unsigned i = 0; i < m_stops.size(); i++) {
618cad810f21b803229eb11403f9209855525a25d57Steve Block            const CSSGradientColorStop& stop = m_stops[i];
619cad810f21b803229eb11403f9209855525a25d57Steve Block            result += ", ";
620cad810f21b803229eb11403f9209855525a25d57Steve Block            result += stop.m_color->cssText();
621cad810f21b803229eb11403f9209855525a25d57Steve Block            if (stop.m_position)
622cad810f21b803229eb11403f9209855525a25d57Steve Block                result += " " + stop.m_position->cssText();
623cad810f21b803229eb11403f9209855525a25d57Steve Block        }
624cad810f21b803229eb11403f9209855525a25d57Steve Block    }
625cad810f21b803229eb11403f9209855525a25d57Steve Block
626cad810f21b803229eb11403f9209855525a25d57Steve Block    result += ")";
627cad810f21b803229eb11403f9209855525a25d57Steve Block    return result;
628cad810f21b803229eb11403f9209855525a25d57Steve Block}
629cad810f21b803229eb11403f9209855525a25d57Steve Block
630cad810f21b803229eb11403f9209855525a25d57Steve Blockfloat CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, RenderStyle* style, RenderStyle* rootStyle, float* widthOrHeight)
631cad810f21b803229eb11403f9209855525a25d57Steve Block{
632cad810f21b803229eb11403f9209855525a25d57Steve Block    float zoomFactor = style->effectiveZoom();
633cad810f21b803229eb11403f9209855525a25d57Steve Block
634cad810f21b803229eb11403f9209855525a25d57Steve Block    float result = 0;
635cad810f21b803229eb11403f9209855525a25d57Steve Block    if (radius->primitiveType() == CSSPrimitiveValue::CSS_NUMBER)  // Can the radius be a percentage?
636cad810f21b803229eb11403f9209855525a25d57Steve Block        result = radius->getFloatValue() * zoomFactor;
637cad810f21b803229eb11403f9209855525a25d57Steve Block    else if (widthOrHeight && radius->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
638cad810f21b803229eb11403f9209855525a25d57Steve Block        result = *widthOrHeight * radius->getFloatValue() / 100;
639cad810f21b803229eb11403f9209855525a25d57Steve Block    else
640cad810f21b803229eb11403f9209855525a25d57Steve Block        result = radius->computeLengthFloat(style, rootStyle, zoomFactor);
641cad810f21b803229eb11403f9209855525a25d57Steve Block
642cad810f21b803229eb11403f9209855525a25d57Steve Block    return result;
643cad810f21b803229eb11403f9209855525a25d57Steve Block}
644cad810f21b803229eb11403f9209855525a25d57Steve Block
645cad810f21b803229eb11403f9209855525a25d57Steve Blockstatic float distanceToClosestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
646cad810f21b803229eb11403f9209855525a25d57Steve Block{
647cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint topLeft;
648cad810f21b803229eb11403f9209855525a25d57Steve Block    float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
649cad810f21b803229eb11403f9209855525a25d57Steve Block
650cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint topRight(size.width(), 0);
651cad810f21b803229eb11403f9209855525a25d57Steve Block    float topRightDistance = FloatSize(p - topRight).diagonalLength();
652cad810f21b803229eb11403f9209855525a25d57Steve Block
653cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint bottomLeft(0, size.height());
654cad810f21b803229eb11403f9209855525a25d57Steve Block    float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
655cad810f21b803229eb11403f9209855525a25d57Steve Block
656cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint bottomRight(size.width(), size.height());
657cad810f21b803229eb11403f9209855525a25d57Steve Block    float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
658cad810f21b803229eb11403f9209855525a25d57Steve Block
659cad810f21b803229eb11403f9209855525a25d57Steve Block    corner = topLeft;
660cad810f21b803229eb11403f9209855525a25d57Steve Block    float minDistance = topLeftDistance;
661cad810f21b803229eb11403f9209855525a25d57Steve Block    if (topRightDistance < minDistance) {
662cad810f21b803229eb11403f9209855525a25d57Steve Block        minDistance = topRightDistance;
663cad810f21b803229eb11403f9209855525a25d57Steve Block        corner = topRight;
664cad810f21b803229eb11403f9209855525a25d57Steve Block    }
665cad810f21b803229eb11403f9209855525a25d57Steve Block
666cad810f21b803229eb11403f9209855525a25d57Steve Block    if (bottomLeftDistance < minDistance) {
667cad810f21b803229eb11403f9209855525a25d57Steve Block        minDistance = bottomLeftDistance;
668cad810f21b803229eb11403f9209855525a25d57Steve Block        corner = bottomLeft;
669cad810f21b803229eb11403f9209855525a25d57Steve Block    }
670cad810f21b803229eb11403f9209855525a25d57Steve Block
671cad810f21b803229eb11403f9209855525a25d57Steve Block    if (bottomRightDistance < minDistance) {
672cad810f21b803229eb11403f9209855525a25d57Steve Block        minDistance = bottomRightDistance;
673cad810f21b803229eb11403f9209855525a25d57Steve Block        corner = bottomRight;
674cad810f21b803229eb11403f9209855525a25d57Steve Block    }
675cad810f21b803229eb11403f9209855525a25d57Steve Block    return minDistance;
676cad810f21b803229eb11403f9209855525a25d57Steve Block}
677cad810f21b803229eb11403f9209855525a25d57Steve Block
678cad810f21b803229eb11403f9209855525a25d57Steve Blockstatic float distanceToFarthestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
679cad810f21b803229eb11403f9209855525a25d57Steve Block{
680cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint topLeft;
681cad810f21b803229eb11403f9209855525a25d57Steve Block    float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
682cad810f21b803229eb11403f9209855525a25d57Steve Block
683cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint topRight(size.width(), 0);
684cad810f21b803229eb11403f9209855525a25d57Steve Block    float topRightDistance = FloatSize(p - topRight).diagonalLength();
685cad810f21b803229eb11403f9209855525a25d57Steve Block
686cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint bottomLeft(0, size.height());
687cad810f21b803229eb11403f9209855525a25d57Steve Block    float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
688cad810f21b803229eb11403f9209855525a25d57Steve Block
689cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint bottomRight(size.width(), size.height());
690cad810f21b803229eb11403f9209855525a25d57Steve Block    float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
691cad810f21b803229eb11403f9209855525a25d57Steve Block
692cad810f21b803229eb11403f9209855525a25d57Steve Block    corner = topLeft;
693cad810f21b803229eb11403f9209855525a25d57Steve Block    float maxDistance = topLeftDistance;
694cad810f21b803229eb11403f9209855525a25d57Steve Block    if (topRightDistance > maxDistance) {
695cad810f21b803229eb11403f9209855525a25d57Steve Block        maxDistance = topRightDistance;
696cad810f21b803229eb11403f9209855525a25d57Steve Block        corner = topRight;
697cad810f21b803229eb11403f9209855525a25d57Steve Block    }
698cad810f21b803229eb11403f9209855525a25d57Steve Block
699cad810f21b803229eb11403f9209855525a25d57Steve Block    if (bottomLeftDistance > maxDistance) {
700cad810f21b803229eb11403f9209855525a25d57Steve Block        maxDistance = bottomLeftDistance;
701cad810f21b803229eb11403f9209855525a25d57Steve Block        corner = bottomLeft;
702cad810f21b803229eb11403f9209855525a25d57Steve Block    }
703cad810f21b803229eb11403f9209855525a25d57Steve Block
704cad810f21b803229eb11403f9209855525a25d57Steve Block    if (bottomRightDistance > maxDistance) {
705cad810f21b803229eb11403f9209855525a25d57Steve Block        maxDistance = bottomRightDistance;
706cad810f21b803229eb11403f9209855525a25d57Steve Block        corner = bottomRight;
707cad810f21b803229eb11403f9209855525a25d57Steve Block    }
708cad810f21b803229eb11403f9209855525a25d57Steve Block    return maxDistance;
709cad810f21b803229eb11403f9209855525a25d57Steve Block}
710cad810f21b803229eb11403f9209855525a25d57Steve Block
711cad810f21b803229eb11403f9209855525a25d57Steve Block// Compute horizontal radius of ellipse with center at 0,0 which passes through p, and has
712cad810f21b803229eb11403f9209855525a25d57Steve Block// width/height given by aspectRatio.
713cad810f21b803229eb11403f9209855525a25d57Steve Blockstatic inline float horizontalEllipseRadius(const FloatSize& p, float aspectRatio)
714cad810f21b803229eb11403f9209855525a25d57Steve Block{
715cad810f21b803229eb11403f9209855525a25d57Steve Block    // x^2/a^2 + y^2/b^2 = 1
716cad810f21b803229eb11403f9209855525a25d57Steve Block    // a/b = aspectRatio, b = a/aspectRatio
717cad810f21b803229eb11403f9209855525a25d57Steve Block    // a = sqrt(x^2 + y^2/(1/r^2))
718cad810f21b803229eb11403f9209855525a25d57Steve Block    return sqrtf(p.width() * p.width() + (p.height() * p.height()) / (1 / (aspectRatio * aspectRatio)));
719cad810f21b803229eb11403f9209855525a25d57Steve Block}
720cad810f21b803229eb11403f9209855525a25d57Steve Block
721cad810f21b803229eb11403f9209855525a25d57Steve Block// FIXME: share code with the linear version
722cad810f21b803229eb11403f9209855525a25d57Steve BlockPassRefPtr<Gradient> CSSRadialGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
723cad810f21b803229eb11403f9209855525a25d57Steve Block{
724cad810f21b803229eb11403f9209855525a25d57Steve Block    ASSERT(!size.isEmpty());
725cad810f21b803229eb11403f9209855525a25d57Steve Block
726cad810f21b803229eb11403f9209855525a25d57Steve Block    RenderStyle* rootStyle = renderer->document()->documentElement()->renderStyle();
727cad810f21b803229eb11403f9209855525a25d57Steve Block
728cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), renderer->style(), rootStyle, size);
729cad810f21b803229eb11403f9209855525a25d57Steve Block    if (!m_firstX)
730cad810f21b803229eb11403f9209855525a25d57Steve Block        firstPoint.setX(size.width() / 2);
731cad810f21b803229eb11403f9209855525a25d57Steve Block    if (!m_firstY)
732cad810f21b803229eb11403f9209855525a25d57Steve Block        firstPoint.setY(size.height() / 2);
733cad810f21b803229eb11403f9209855525a25d57Steve Block
734cad810f21b803229eb11403f9209855525a25d57Steve Block    FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), renderer->style(), rootStyle, size);
735cad810f21b803229eb11403f9209855525a25d57Steve Block    if (!m_secondX)
736cad810f21b803229eb11403f9209855525a25d57Steve Block        secondPoint.setX(size.width() / 2);
737cad810f21b803229eb11403f9209855525a25d57Steve Block    if (!m_secondY)
738cad810f21b803229eb11403f9209855525a25d57Steve Block        secondPoint.setY(size.height() / 2);
739cad810f21b803229eb11403f9209855525a25d57Steve Block
740cad810f21b803229eb11403f9209855525a25d57Steve Block    float firstRadius = 0;
741cad810f21b803229eb11403f9209855525a25d57Steve Block    if (m_firstRadius)
742cad810f21b803229eb11403f9209855525a25d57Steve Block        firstRadius = resolveRadius(m_firstRadius.get(), renderer->style(), rootStyle);
743cad810f21b803229eb11403f9209855525a25d57Steve Block
744cad810f21b803229eb11403f9209855525a25d57Steve Block    float secondRadius = 0;
745cad810f21b803229eb11403f9209855525a25d57Steve Block    float aspectRatio = 1; // width / height.
746cad810f21b803229eb11403f9209855525a25d57Steve Block    if (m_secondRadius)
747cad810f21b803229eb11403f9209855525a25d57Steve Block        secondRadius = resolveRadius(m_secondRadius.get(), renderer->style(), rootStyle);
748cad810f21b803229eb11403f9209855525a25d57Steve Block    else if (m_endHorizontalSize || m_endVerticalSize) {
749cad810f21b803229eb11403f9209855525a25d57Steve Block        float width = size.width();
750cad810f21b803229eb11403f9209855525a25d57Steve Block        float height = size.height();
751cad810f21b803229eb11403f9209855525a25d57Steve Block        secondRadius = resolveRadius(m_endHorizontalSize.get(), renderer->style(), rootStyle, &width);
752cad810f21b803229eb11403f9209855525a25d57Steve Block        aspectRatio = secondRadius / resolveRadius(m_endVerticalSize.get(), renderer->style(), rootStyle, &height);
753cad810f21b803229eb11403f9209855525a25d57Steve Block    } else {
754cad810f21b803229eb11403f9209855525a25d57Steve Block        enum GradientShape { Circle, Ellipse };
755cad810f21b803229eb11403f9209855525a25d57Steve Block        GradientShape shape = Ellipse;
756cad810f21b803229eb11403f9209855525a25d57Steve Block        if (m_shape && m_shape->primitiveType() == CSSPrimitiveValue::CSS_IDENT && m_shape->getIdent() == CSSValueCircle)
757cad810f21b803229eb11403f9209855525a25d57Steve Block            shape = Circle;
758cad810f21b803229eb11403f9209855525a25d57Steve Block
759cad810f21b803229eb11403f9209855525a25d57Steve Block        enum GradientFill { ClosestSide, ClosestCorner, FarthestSide, FarthestCorner };
760cad810f21b803229eb11403f9209855525a25d57Steve Block        GradientFill fill = FarthestCorner;
761cad810f21b803229eb11403f9209855525a25d57Steve Block
762cad810f21b803229eb11403f9209855525a25d57Steve Block        if (m_sizingBehavior && m_sizingBehavior->primitiveType() == CSSPrimitiveValue::CSS_IDENT) {
763cad810f21b803229eb11403f9209855525a25d57Steve Block            switch (m_sizingBehavior->getIdent()) {
764cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueContain:
765cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueClosestSide:
766cad810f21b803229eb11403f9209855525a25d57Steve Block                fill = ClosestSide;
767cad810f21b803229eb11403f9209855525a25d57Steve Block                break;
768cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueClosestCorner:
769cad810f21b803229eb11403f9209855525a25d57Steve Block                fill = ClosestCorner;
770cad810f21b803229eb11403f9209855525a25d57Steve Block                break;
771cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueFarthestSide:
772cad810f21b803229eb11403f9209855525a25d57Steve Block                fill = FarthestSide;
773cad810f21b803229eb11403f9209855525a25d57Steve Block                break;
774cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueCover:
775cad810f21b803229eb11403f9209855525a25d57Steve Block            case CSSValueFarthestCorner:
776cad810f21b803229eb11403f9209855525a25d57Steve Block                fill = FarthestCorner;
777cad810f21b803229eb11403f9209855525a25d57Steve Block                break;
778cad810f21b803229eb11403f9209855525a25d57Steve Block            }
779cad810f21b803229eb11403f9209855525a25d57Steve Block        }
780cad810f21b803229eb11403f9209855525a25d57Steve Block
781cad810f21b803229eb11403f9209855525a25d57Steve Block        // Now compute the end radii based on the second point, shape and fill.
782cad810f21b803229eb11403f9209855525a25d57Steve Block
783cad810f21b803229eb11403f9209855525a25d57Steve Block        // Horizontal
784cad810f21b803229eb11403f9209855525a25d57Steve Block        switch (fill) {
785cad810f21b803229eb11403f9209855525a25d57Steve Block        case ClosestSide: {
786cad810f21b803229eb11403f9209855525a25d57Steve Block            float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
787cad810f21b803229eb11403f9209855525a25d57Steve Block            float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
788cad810f21b803229eb11403f9209855525a25d57Steve Block            if (shape == Circle) {
789cad810f21b803229eb11403f9209855525a25d57Steve Block                float smaller = min(xDist, yDist);
790cad810f21b803229eb11403f9209855525a25d57Steve Block                xDist = smaller;
791cad810f21b803229eb11403f9209855525a25d57Steve Block                yDist = smaller;
792cad810f21b803229eb11403f9209855525a25d57Steve Block            }
793cad810f21b803229eb11403f9209855525a25d57Steve Block            secondRadius = xDist;
794cad810f21b803229eb11403f9209855525a25d57Steve Block            aspectRatio = xDist / yDist;
795cad810f21b803229eb11403f9209855525a25d57Steve Block            break;
796cad810f21b803229eb11403f9209855525a25d57Steve Block        }
797cad810f21b803229eb11403f9209855525a25d57Steve Block        case FarthestSide: {
798cad810f21b803229eb11403f9209855525a25d57Steve Block            float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
799cad810f21b803229eb11403f9209855525a25d57Steve Block            float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
800cad810f21b803229eb11403f9209855525a25d57Steve Block            if (shape == Circle) {
801cad810f21b803229eb11403f9209855525a25d57Steve Block                float larger = max(xDist, yDist);
802cad810f21b803229eb11403f9209855525a25d57Steve Block                xDist = larger;
803cad810f21b803229eb11403f9209855525a25d57Steve Block                yDist = larger;
804cad810f21b803229eb11403f9209855525a25d57Steve Block            }
805cad810f21b803229eb11403f9209855525a25d57Steve Block            secondRadius = xDist;
806cad810f21b803229eb11403f9209855525a25d57Steve Block            aspectRatio = xDist / yDist;
807cad810f21b803229eb11403f9209855525a25d57Steve Block            break;
808cad810f21b803229eb11403f9209855525a25d57Steve Block        }
809cad810f21b803229eb11403f9209855525a25d57Steve Block        case ClosestCorner: {
810cad810f21b803229eb11403f9209855525a25d57Steve Block            FloatPoint corner;
811cad810f21b803229eb11403f9209855525a25d57Steve Block            float distance = distanceToClosestCorner(secondPoint, size, corner);
812cad810f21b803229eb11403f9209855525a25d57Steve Block            if (shape == Circle)
813cad810f21b803229eb11403f9209855525a25d57Steve Block                secondRadius = distance;
814cad810f21b803229eb11403f9209855525a25d57Steve Block            else {
815cad810f21b803229eb11403f9209855525a25d57Steve Block                // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
816cad810f21b803229eb11403f9209855525a25d57Steve Block                // that it would if closest-side or farthest-side were specified, as appropriate.
817cad810f21b803229eb11403f9209855525a25d57Steve Block                float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
818cad810f21b803229eb11403f9209855525a25d57Steve Block                float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
819cad810f21b803229eb11403f9209855525a25d57Steve Block
820cad810f21b803229eb11403f9209855525a25d57Steve Block                secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
821cad810f21b803229eb11403f9209855525a25d57Steve Block                aspectRatio = xDist / yDist;
822cad810f21b803229eb11403f9209855525a25d57Steve Block            }
823cad810f21b803229eb11403f9209855525a25d57Steve Block            break;
824cad810f21b803229eb11403f9209855525a25d57Steve Block        }
825cad810f21b803229eb11403f9209855525a25d57Steve Block
826cad810f21b803229eb11403f9209855525a25d57Steve Block        case FarthestCorner: {
827cad810f21b803229eb11403f9209855525a25d57Steve Block            FloatPoint corner;
828cad810f21b803229eb11403f9209855525a25d57Steve Block            float distance = distanceToFarthestCorner(secondPoint, size, corner);
829cad810f21b803229eb11403f9209855525a25d57Steve Block            if (shape == Circle)
830cad810f21b803229eb11403f9209855525a25d57Steve Block                secondRadius = distance;
831cad810f21b803229eb11403f9209855525a25d57Steve Block            else {
832cad810f21b803229eb11403f9209855525a25d57Steve Block                // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
833cad810f21b803229eb11403f9209855525a25d57Steve Block                // that it would if closest-side or farthest-side were specified, as appropriate.
834cad810f21b803229eb11403f9209855525a25d57Steve Block                float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
835cad810f21b803229eb11403f9209855525a25d57Steve Block                float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
836cad810f21b803229eb11403f9209855525a25d57Steve Block
837cad810f21b803229eb11403f9209855525a25d57Steve Block                secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
838cad810f21b803229eb11403f9209855525a25d57Steve Block                aspectRatio = xDist / yDist;
839cad810f21b803229eb11403f9209855525a25d57Steve Block            }
840cad810f21b803229eb11403f9209855525a25d57Steve Block            break;
841cad810f21b803229eb11403f9209855525a25d57Steve Block        }
842cad810f21b803229eb11403f9209855525a25d57Steve Block        }
843cad810f21b803229eb11403f9209855525a25d57Steve Block    }
844cad810f21b803229eb11403f9209855525a25d57Steve Block
845cad810f21b803229eb11403f9209855525a25d57Steve Block    RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius, aspectRatio);
846cad810f21b803229eb11403f9209855525a25d57Steve Block
847cad810f21b803229eb11403f9209855525a25d57Steve Block    // addStops() only uses maxExtent for repeating gradients.
848cad810f21b803229eb11403f9209855525a25d57Steve Block    float maxExtent = 0;
849cad810f21b803229eb11403f9209855525a25d57Steve Block    if (m_repeating) {
850cad810f21b803229eb11403f9209855525a25d57Steve Block        FloatPoint corner;
851cad810f21b803229eb11403f9209855525a25d57Steve Block        maxExtent = distanceToFarthestCorner(secondPoint, size, corner);
852cad810f21b803229eb11403f9209855525a25d57Steve Block    }
853cad810f21b803229eb11403f9209855525a25d57Steve Block
854cad810f21b803229eb11403f9209855525a25d57Steve Block    // Now add the stops.
855cad810f21b803229eb11403f9209855525a25d57Steve Block    addStops(gradient.get(), renderer, rootStyle, maxExtent);
856cad810f21b803229eb11403f9209855525a25d57Steve Block
857cad810f21b803229eb11403f9209855525a25d57Steve Block    return gradient.release();
858cad810f21b803229eb11403f9209855525a25d57Steve Block}
859cad810f21b803229eb11403f9209855525a25d57Steve Block
860cad810f21b803229eb11403f9209855525a25d57Steve Block} // namespace WebCore
861