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 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 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/HashFunctions.h>
33#include <wtf/Hasher.h>
34
35using WTF::pairIntHash;
36
37namespace WebCore {
38
39Ref<Gradient> Gradient::create(LinearData&& data)
40{
41 return adoptRef(*new Gradient(WTFMove(data)));
42}
43
44Ref<Gradient> Gradient::create(RadialData&& data)
45{
46 return adoptRef(*new Gradient(WTFMove(data)));
47}
48
49Ref<Gradient> Gradient::create(ConicData&& data)
50{
51 return adoptRef(*new Gradient(WTFMove(data)));
52}
53
54Gradient::Gradient(LinearData&& data)
55 : m_data(WTFMove(data))
56{
57 platformInit();
58}
59
60Gradient::Gradient(RadialData&& data)
61 : m_data(WTFMove(data))
62{
63 platformInit();
64}
65
66Gradient::Gradient(ConicData&& data)
67 : m_data(WTFMove(data))
68{
69 platformInit();
70}
71
72Gradient::~Gradient()
73{
74 platformDestroy();
75}
76
77auto Gradient::type() const -> Type
78{
79 return WTF::switchOn(m_data,
80 [] (const LinearData&) {
81 return Type::Linear;
82 },
83 [] (const RadialData&) {
84 return Type::Radial;
85 },
86 [] (const ConicData&) {
87 return Type::Conic;
88 }
89 );
90}
91
92void Gradient::adjustParametersForTiledDrawing(FloatSize& size, FloatRect& srcRect, const FloatSize& spacing)
93{
94 if (srcRect.isEmpty())
95 return;
96
97 if (!spacing.isZero())
98 return;
99
100 WTF::switchOn(m_data,
101 [&] (const LinearData& data) {
102 if (data.point0.x() == data.point1.x()) {
103 size.setWidth(1);
104 srcRect.setWidth(1);
105 srcRect.setX(0);
106 return;
107 }
108 if (data.point0.y() != data.point1.y())
109 return;
110
111 size.setHeight(1);
112 srcRect.setHeight(1);
113 srcRect.setY(0);
114 },
115 [] (const RadialData&) {
116 },
117 [] (const ConicData&) {
118 }
119 );
120}
121
122bool Gradient::isZeroSize() const
123{
124 return WTF::switchOn(m_data,
125 [] (const LinearData& data) {
126 return data.point0.x() == data.point1.x() && data.point0.y() == data.point1.y();
127 },
128 [] (const RadialData& data) {
129 return data.point0.x() == data.point1.x() && data.point0.y() == data.point1.y() && data.startRadius == data.endRadius;
130 },
131 [] (const ConicData&) {
132 return false;
133 }
134 );
135}
136
137void Gradient::addColorStop(float offset, const Color& color)
138{
139 addColorStop({ offset, color });
140}
141
142void Gradient::addColorStop(const Gradient::ColorStop& stop)
143{
144 m_stops.append(stop);
145
146 m_stopsSorted = false;
147
148 platformDestroy();
149 invalidateHash();
150}
151
152void Gradient::setSortedColorStops(ColorStopVector&& stops)
153{
154 m_stops = WTFMove(stops);
155
156 m_stopsSorted = true;
157
158 platformDestroy();
159 invalidateHash();
160}
161
162static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::ColorStop& b)
163{
164 return a.offset < b.offset;
165}
166
167void Gradient::sortStopsIfNecessary()
168{
169 if (m_stopsSorted)
170 return;
171
172 m_stopsSorted = true;
173
174 if (!m_stops.size())
175 return;
176
177 std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
178 invalidateHash();
179}
180
181bool Gradient::hasAlpha() const
182{
183 for (const auto& stop : m_stops) {
184 if (!stop.color.isOpaque())
185 return true;
186 }
187
188 return false;
189}
190
191void Gradient::setSpreadMethod(GradientSpreadMethod spreadMethod)
192{
193 // FIXME: Should it become necessary, allow calls to this method after m_gradient has been set.
194 ASSERT(m_gradient == 0);
195
196 if (m_spreadMethod == spreadMethod)
197 return;
198
199 m_spreadMethod = spreadMethod;
200
201 invalidateHash();
202}
203
204void Gradient::setGradientSpaceTransform(const AffineTransform& gradientSpaceTransformation)
205{
206 if (m_gradientSpaceTransformation == gradientSpaceTransformation)
207 return;
208
209 m_gradientSpaceTransformation = gradientSpaceTransformation;
210
211 invalidateHash();
212}
213
214unsigned Gradient::hash() const
215{
216 if (m_cachedHash)
217 return m_cachedHash;
218
219 struct {
220 Type type;
221 FloatPoint point0;
222 FloatPoint point1;
223 float startRadius;
224 float endRadius;
225 float aspectRatio;
226 float angleRadians;
227 GradientSpreadMethod spreadMethod;
228 AffineTransform gradientSpaceTransformation;
229 } parameters;
230
231 // StringHasher requires that the memory it hashes be a multiple of two in size.
232 COMPILE_ASSERT(!(sizeof(parameters) % 2), Gradient_parameters_size_should_be_multiple_of_two);
233 COMPILE_ASSERT(!(sizeof(ColorStop) % 2), Color_stop_size_should_be_multiple_of_two);
234
235 // Ensure that any padding in the struct is zero-filled, so it will not affect the hash value.
236 // FIXME: This is asking for trouble, because it is a nontrivial type.
237 memset(static_cast<void*>(&parameters), 0, sizeof(parameters));
238
239 WTF::switchOn(m_data,
240 [&parameters] (const LinearData& data) {
241 parameters.point0 = data.point0;
242 parameters.point1 = data.point1;
243 parameters.startRadius = 0;
244 parameters.endRadius = 0;
245 parameters.aspectRatio = 0;
246 parameters.angleRadians = 0;
247 parameters.type = Type::Linear;
248 },
249 [&parameters] (const RadialData& data) {
250 parameters.point0 = data.point0;
251 parameters.point1 = data.point1;
252 parameters.startRadius = data.startRadius;
253 parameters.endRadius = data.endRadius;
254 parameters.aspectRatio = data.aspectRatio;
255 parameters.angleRadians = 0;
256 parameters.type = Type::Radial;
257 },
258 [&parameters] (const ConicData& data) {
259 parameters.point0 = data.point0;
260 parameters.point1 = {0, 0};
261 parameters.startRadius = 0;
262 parameters.endRadius = 0;
263 parameters.aspectRatio = 0;
264 parameters.angleRadians = data.angleRadians;
265 parameters.type = Type::Conic;
266 }
267 );
268
269 parameters.spreadMethod = m_spreadMethod;
270 parameters.gradientSpaceTransformation = m_gradientSpaceTransformation;
271
272 unsigned parametersHash = StringHasher::hashMemory(&parameters, sizeof(parameters));
273 unsigned stopHash = StringHasher::hashMemory(m_stops.data(), m_stops.size() * sizeof(ColorStop));
274
275 m_cachedHash = pairIntHash(parametersHash, stopHash);
276
277 return m_cachedHash;
278}
279
280}
281