1/*
2 * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "Gradient.h"
29
30#include "Color.h"
31#include "FloatRect.h"
32#include <wtf/UnusedParam.h>
33
34namespace WebCore {
35
36Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1)
37    : m_radial(false)
38    , m_p0(p0)
39    , m_p1(p1)
40    , m_r0(0)
41    , m_r1(0)
42    , m_aspectRatio(1)
43    , m_stopsSorted(false)
44    , m_lastStop(0)
45    , m_spreadMethod(SpreadMethodPad)
46{
47    platformInit();
48}
49
50Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio)
51    : m_radial(true)
52    , m_p0(p0)
53    , m_p1(p1)
54    , m_r0(r0)
55    , m_r1(r1)
56    , m_aspectRatio(aspectRatio)
57    , m_stopsSorted(false)
58    , m_lastStop(0)
59    , m_spreadMethod(SpreadMethodPad)
60{
61    platformInit();
62}
63
64Gradient::~Gradient()
65{
66    platformDestroy();
67}
68
69void Gradient::adjustParametersForTiledDrawing(IntSize& size, FloatRect& srcRect)
70{
71    if (m_radial)
72        return;
73
74    if (srcRect.isEmpty())
75        return;
76
77    if (m_p0.x() == m_p1.x()) {
78        size.setWidth(1);
79        srcRect.setWidth(1);
80        srcRect.setX(0);
81        return;
82    }
83    if (m_p0.y() != m_p1.y())
84        return;
85
86    size.setHeight(1);
87    srcRect.setHeight(1);
88    srcRect.setY(0);
89}
90
91void Gradient::addColorStop(float value, const Color& color)
92{
93    float r;
94    float g;
95    float b;
96    float a;
97    color.getRGBA(r, g, b, a);
98    m_stops.append(ColorStop(value, r, g, b, a));
99
100    m_stopsSorted = false;
101    platformDestroy();
102}
103
104void Gradient::addColorStop(const Gradient::ColorStop& stop)
105{
106    m_stops.append(stop);
107
108    m_stopsSorted = false;
109    platformDestroy();
110}
111
112static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::ColorStop& b)
113{
114    return a.stop < b.stop;
115}
116
117void Gradient::sortStopsIfNecessary()
118{
119    if (m_stopsSorted)
120        return;
121
122    m_stopsSorted = true;
123
124    if (!m_stops.size())
125        return;
126
127    // Shortcut for the ideal case (ordered 2-stop gradient)
128    if (m_stops.size() == 2 && compareStops(*m_stops.begin(), *m_stops.end()))
129        return;
130
131    std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
132}
133
134void Gradient::getColor(float value, float* r, float* g, float* b, float* a) const
135{
136    ASSERT(value >= 0);
137    ASSERT(value <= 1);
138
139    if (m_stops.isEmpty()) {
140        *r = 0;
141        *g = 0;
142        *b = 0;
143        *a = 0;
144        return;
145    }
146    if (!m_stopsSorted) {
147        if (m_stops.size())
148            std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
149        m_stopsSorted = true;
150    }
151    if (value <= 0 || value <= m_stops.first().stop) {
152        *r = m_stops.first().red;
153        *g = m_stops.first().green;
154        *b = m_stops.first().blue;
155        *a = m_stops.first().alpha;
156        return;
157    }
158    if (value >= 1 || value >= m_stops.last().stop) {
159        *r = m_stops.last().red;
160        *g = m_stops.last().green;
161        *b = m_stops.last().blue;
162        *a = m_stops.last().alpha;
163        return;
164    }
165
166    // Find stop before and stop after and interpolate.
167    int stop = findStop(value);
168    const ColorStop& lastStop = m_stops[stop];
169    const ColorStop& nextStop = m_stops[stop + 1];
170    float stopFraction = (value - lastStop.stop) / (nextStop.stop - lastStop.stop);
171    *r = lastStop.red + (nextStop.red - lastStop.red) * stopFraction;
172    *g = lastStop.green + (nextStop.green - lastStop.green) * stopFraction;
173    *b = lastStop.blue + (nextStop.blue - lastStop.blue) * stopFraction;
174    *a = lastStop.alpha + (nextStop.alpha - lastStop.alpha) * stopFraction;
175}
176
177int Gradient::findStop(float value) const
178{
179    ASSERT(value >= 0);
180    ASSERT(value <= 1);
181    ASSERT(m_stopsSorted);
182
183    int numStops = m_stops.size();
184    ASSERT(numStops >= 2);
185    ASSERT(m_lastStop < numStops - 1);
186
187    int i = m_lastStop;
188    if (value < m_stops[i].stop)
189        i = 1;
190    else
191        i = m_lastStop + 1;
192
193    for (; i < numStops - 1; ++i)
194        if (value < m_stops[i].stop)
195            break;
196
197    m_lastStop = i - 1;
198    return m_lastStop;
199}
200
201bool Gradient::hasAlpha() const
202{
203    for (size_t i = 0; i < m_stops.size(); i++) {
204        if (m_stops[i].alpha < 1)
205            return true;
206    }
207
208    return false;
209}
210
211void Gradient::setSpreadMethod(GradientSpreadMethod spreadMethod)
212{
213    // FIXME: Should it become necessary, allow calls to this method after m_gradient has been set.
214    ASSERT(m_gradient == 0);
215    m_spreadMethod = spreadMethod;
216}
217
218void Gradient::setGradientSpaceTransform(const AffineTransform& gradientSpaceTransformation)
219{
220    m_gradientSpaceTransformation = gradientSpaceTransformation;
221    setPlatformGradientSpaceTransform(gradientSpaceTransformation);
222}
223
224#if !USE(SKIA) && !USE(CAIRO)
225void Gradient::setPlatformGradientSpaceTransform(const AffineTransform&)
226{
227}
228#endif
229
230
231} //namespace
232