1/*
2 * Copyright (C) 2008 Apple Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CSSGradientValue.h"
28
29#include "CSSStyleSelector.h"
30#include "GeneratedImage.h"
31#include "Gradient.h"
32#include "Image.h"
33#include "IntSize.h"
34#include "IntSizeHash.h"
35#include "PlatformString.h"
36#include "RenderObject.h"
37
38using namespace std;
39
40namespace WebCore {
41
42String CSSGradientValue::cssText() const
43{
44    String result = "-webkit-gradient(";
45    if (m_type == CSSLinearGradient)
46        result += "linear, ";
47    else
48        result += "radial, ";
49    result += m_firstX->cssText() + " ";
50    result += m_firstY->cssText() + ", ";
51    if (m_type == CSSRadialGradient)
52        result += m_firstRadius->cssText() + ", ";
53    result += m_secondX->cssText() + " ";
54    result += m_secondY->cssText();
55    if (m_type == CSSRadialGradient) {
56        result += ", ";
57        result += m_secondRadius->cssText();
58    }
59    for (unsigned i = 0; i < m_stops.size(); i++) {
60        result += ", ";
61        if (m_stops[i].m_stop == 0)
62            result += "from(" + m_stops[i].m_color->cssText() + ")";
63        else if (m_stops[i].m_stop == 1)
64            result += "to(" + m_stops[i].m_color->cssText() + ")";
65        else
66            result += "color-stop(" + String::number(m_stops[i].m_stop) + ", " + m_stops[i].m_color->cssText() + ")";
67    }
68    result += ")";
69    return result;
70}
71
72PassRefPtr<Gradient> CSSGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
73{
74    ASSERT(!size.isEmpty());
75
76    float zoomFactor = renderer->style()->effectiveZoom();
77
78    FloatPoint firstPoint = resolvePoint(m_firstX.get(), m_firstY.get(), size, zoomFactor);
79    FloatPoint secondPoint = resolvePoint(m_secondX.get(), m_secondY.get(), size, zoomFactor);
80
81    RefPtr<Gradient> gradient;
82    if (m_type == CSSLinearGradient)
83        gradient = Gradient::create(firstPoint, secondPoint);
84    else {
85        float firstRadius = resolveRadius(m_firstRadius.get(), zoomFactor);
86        float secondRadius = resolveRadius(m_secondRadius.get(), zoomFactor);
87        gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius);
88    }
89
90    // Now add the stops.
91    sortStopsIfNeeded();
92
93    // We have to resolve colors.
94    for (unsigned i = 0; i < m_stops.size(); i++) {
95        Color color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(m_stops[i].m_color.get());
96        gradient->addColorStop(m_stops[i].m_stop, color);
97    }
98
99    // The back end already sorted the stops.
100    gradient->setStopsSorted(true);
101
102    return gradient.release();
103}
104
105Image* CSSGradientValue::image(RenderObject* renderer, const IntSize& size)
106{
107    ASSERT(m_clients.contains(renderer));
108
109    // Need to look up our size.  Create a string of width*height to use as a hash key.
110    Image* result = getImage(renderer, size);
111    if (result)
112        return result;
113
114    if (size.isEmpty())
115        return 0;
116
117    // We need to create an image.
118    RefPtr<Image> newImage = GeneratedImage::create(createGradient(renderer, size), size);
119    result = newImage.get();
120    putImage(size, newImage.release());
121
122    return result;
123}
124
125static inline bool compareStops(const CSSGradientColorStop& a, const CSSGradientColorStop& b)
126{
127    return a.m_stop < b.m_stop;
128}
129
130void CSSGradientValue::sortStopsIfNeeded()
131{
132    if (!m_stopsSorted) {
133        if (m_stops.size())
134            std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
135        m_stopsSorted = true;
136    }
137}
138
139FloatPoint CSSGradientValue::resolvePoint(CSSPrimitiveValue* first, CSSPrimitiveValue* second, const IntSize& size, float zoomFactor)
140{
141    FloatPoint result;
142    if (first->primitiveType() == CSSPrimitiveValue::CSS_NUMBER)
143        result.setX(first->getFloatValue() * zoomFactor);
144    else if (first->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
145        result.setX(first->getFloatValue() / 100.f * size.width());
146    if (second->primitiveType() == CSSPrimitiveValue::CSS_NUMBER)
147        result.setY(second->getFloatValue() * zoomFactor);
148    else if (second->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
149        result.setY(second->getFloatValue() / 100.f * size.height());
150
151    return result;
152}
153
154float CSSGradientValue::resolveRadius(CSSPrimitiveValue* radius, float zoomFactor)
155{
156    float result = 0.f;
157    if (radius->primitiveType() == CSSPrimitiveValue::CSS_NUMBER)
158        result = radius->getFloatValue() * zoomFactor;
159    return result;
160}
161
162} // namespace WebCore
163