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 | |
35 | using WTF::pairIntHash; |
36 | |
37 | namespace WebCore { |
38 | |
39 | Ref<Gradient> Gradient::create(LinearData&& data) |
40 | { |
41 | return adoptRef(*new Gradient(WTFMove(data))); |
42 | } |
43 | |
44 | Ref<Gradient> Gradient::create(RadialData&& data) |
45 | { |
46 | return adoptRef(*new Gradient(WTFMove(data))); |
47 | } |
48 | |
49 | Ref<Gradient> Gradient::create(ConicData&& data) |
50 | { |
51 | return adoptRef(*new Gradient(WTFMove(data))); |
52 | } |
53 | |
54 | Gradient::Gradient(LinearData&& data) |
55 | : m_data(WTFMove(data)) |
56 | { |
57 | platformInit(); |
58 | } |
59 | |
60 | Gradient::Gradient(RadialData&& data) |
61 | : m_data(WTFMove(data)) |
62 | { |
63 | platformInit(); |
64 | } |
65 | |
66 | Gradient::Gradient(ConicData&& data) |
67 | : m_data(WTFMove(data)) |
68 | { |
69 | platformInit(); |
70 | } |
71 | |
72 | Gradient::~Gradient() |
73 | { |
74 | platformDestroy(); |
75 | } |
76 | |
77 | auto 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 | |
92 | void 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 | |
122 | bool 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 | |
137 | void Gradient::addColorStop(float offset, const Color& color) |
138 | { |
139 | addColorStop({ offset, color }); |
140 | } |
141 | |
142 | void Gradient::addColorStop(const Gradient::ColorStop& stop) |
143 | { |
144 | m_stops.append(stop); |
145 | |
146 | m_stopsSorted = false; |
147 | |
148 | platformDestroy(); |
149 | invalidateHash(); |
150 | } |
151 | |
152 | void Gradient::setSortedColorStops(ColorStopVector&& stops) |
153 | { |
154 | m_stops = WTFMove(stops); |
155 | |
156 | m_stopsSorted = true; |
157 | |
158 | platformDestroy(); |
159 | invalidateHash(); |
160 | } |
161 | |
162 | static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::ColorStop& b) |
163 | { |
164 | return a.offset < b.offset; |
165 | } |
166 | |
167 | void 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 | |
181 | bool 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 | |
191 | void 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 | |
204 | void Gradient::setGradientSpaceTransform(const AffineTransform& gradientSpaceTransformation) |
205 | { |
206 | if (m_gradientSpaceTransformation == gradientSpaceTransformation) |
207 | return; |
208 | |
209 | m_gradientSpaceTransformation = gradientSpaceTransformation; |
210 | |
211 | invalidateHash(); |
212 | } |
213 | |
214 | unsigned 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*>(¶meters), 0, sizeof(parameters)); |
238 | |
239 | WTF::switchOn(m_data, |
240 | [¶meters] (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 | [¶meters] (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 | [¶meters] (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(¶meters, 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 | |