Skip memcpy-typed-loop timing out on ARMv7 pending investigation
[WebKit-https.git] / Source / WebCore / css / CSSGradientValue.cpp
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 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 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 "CSSCalculationValue.h"
30 #include "CSSToLengthConversionData.h"
31 #include "CSSValueKeywords.h"
32 #include "FloatSize.h"
33 #include "Gradient.h"
34 #include "GradientImage.h"
35 #include "NodeRenderStyle.h"
36 #include "Pair.h"
37 #include "RenderElement.h"
38 #include "RenderView.h"
39 #include "StyleResolver.h"
40 #include <wtf/text/StringBuilder.h>
41
42 namespace WebCore {
43
44 static inline Ref<Gradient> createGradient(CSSGradientValue& value, RenderElement& renderer, FloatSize size)
45 {
46     if (is<CSSLinearGradientValue>(value))
47         return downcast<CSSLinearGradientValue>(value).createGradient(renderer, size);
48     if (is<CSSRadialGradientValue>(value))
49         return downcast<CSSRadialGradientValue>(value).createGradient(renderer, size);
50     return downcast<CSSConicGradientValue>(value).createGradient(renderer, size);
51 }
52
53 RefPtr<Image> CSSGradientValue::image(RenderElement& renderer, const FloatSize& size)
54 {
55     if (size.isEmpty())
56         return nullptr;
57     bool cacheable = isCacheable() && !renderer.style().hasAppleColorFilter();
58     if (cacheable) {
59         if (!clients().contains(&renderer))
60             return nullptr;
61         if (auto* result = cachedImageForSize(size))
62             return result;
63     }
64     auto newImage = GradientImage::create(createGradient(*this, renderer, size), size);
65     if (cacheable)
66         saveCachedImageForSize(size, newImage.get());
67     return newImage;
68 }
69
70 // Should only ever be called for deprecated gradients.
71 static inline bool compareStops(const CSSGradientColorStop& a, const CSSGradientColorStop& b)
72 {
73     double aVal = a.m_position->doubleValue(CSSPrimitiveValue::CSS_NUMBER);
74     double bVal = b.m_position->doubleValue(CSSPrimitiveValue::CSS_NUMBER);
75
76     return aVal < bVal;
77 }
78
79 void CSSGradientValue::sortStopsIfNeeded()
80 {
81     ASSERT(m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient);
82     if (!m_stopsSorted) {
83         if (m_stops.size())
84             std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
85         m_stopsSorted = true;
86     }
87 }
88
89 struct GradientStop {
90     Color color;
91     float offset { 0 };
92     bool specified { false };
93     bool isMidpoint { false };
94 };
95
96 static inline Ref<CSSGradientValue> clone(CSSGradientValue& value)
97 {
98     if (is<CSSLinearGradientValue>(value))
99         return downcast<CSSLinearGradientValue>(value).clone();
100     if (is<CSSRadialGradientValue>(value))
101         return downcast<CSSRadialGradientValue>(value).clone();
102     ASSERT(is<CSSConicGradientValue>(value));
103     return downcast<CSSConicGradientValue>(value).clone();
104 }
105
106 Ref<CSSGradientValue> CSSGradientValue::gradientWithStylesResolved(const StyleResolver& styleResolver)
107 {
108     bool colorIsDerivedFromElement = false;
109     for (auto& stop : m_stops) {
110         if (!stop.isMidpoint && styleResolver.colorFromPrimitiveValueIsDerivedFromElement(*stop.m_color)) {
111             stop.m_colorIsDerivedFromElement = true;
112             colorIsDerivedFromElement = true;
113             break;
114         }
115     }
116     auto result = colorIsDerivedFromElement ? clone(*this) : makeRef(*this);
117     for (auto& stop : result->m_stops) {
118         if (!stop.isMidpoint)
119             stop.m_resolvedColor = styleResolver.colorFromPrimitiveValue(*stop.m_color);
120     }
121     return result;
122 }
123
124 class LinearGradientAdapter {
125 public:
126     explicit LinearGradientAdapter(Gradient::LinearData& data)
127         : m_data(data)
128     {
129     }
130     
131     float gradientLength() const
132     {
133         auto gradientSize = m_data.point0 - m_data.point1;
134         return gradientSize.diagonalLength();
135     }
136     float maxExtent(float, float) const { return 1; }
137
138     void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
139     {
140         float firstOffset = stops.first().offset;
141         float lastOffset = stops.last().offset;
142         if (firstOffset != lastOffset) {
143             float scale = lastOffset - firstOffset;
144
145             for (auto& stop : stops)
146                 stop.offset = (stop.offset - firstOffset) / scale;
147
148             auto p0 = m_data.point0;
149             auto p1 = m_data.point1;
150             m_data.point0 = { p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y()) };
151             m_data.point1 = { p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y()) };
152         } else {
153             // There's a single position that is outside the scale, clamp the positions to 1.
154             for (auto& stop : stops)
155                 stop.offset = 1;
156         }
157     }
158
159 private:
160     Gradient::LinearData& m_data;
161 };
162
163 class RadialGradientAdapter {
164 public:
165     explicit RadialGradientAdapter(Gradient::RadialData& data)
166         : m_data(data)
167     {
168     }
169
170     float gradientLength() const { return m_data.endRadius; }
171
172     // Radial gradients may need to extend further than the endpoints, because they have
173     // to repeat out to the corners of the box.
174     float maxExtent(float maxLengthForRepeat, float gradientLength) const
175     {
176         if (maxLengthForRepeat > gradientLength)
177             return gradientLength > 0 ? maxLengthForRepeat / gradientLength : 0;
178         return 1;
179     }
180
181     void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
182     {
183         auto numStops = stops.size();
184
185         // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
186         float firstOffset = 0;
187         float lastOffset = stops.last().offset;
188         float scale = lastOffset - firstOffset;
189
190         // Reset points below 0 to the first visible color.
191         size_t firstZeroOrGreaterIndex = numStops;
192         for (size_t i = 0; i < numStops; ++i) {
193             if (stops[i].offset >= 0) {
194                 firstZeroOrGreaterIndex = i;
195                 break;
196             }
197         }
198
199         if (firstZeroOrGreaterIndex > 0) {
200             if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
201                 float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
202                 float nextOffset = stops[firstZeroOrGreaterIndex].offset;
203
204                 float interStopProportion = -prevOffset / (nextOffset - prevOffset);
205                 // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
206                 Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
207
208                 // Clamp the positions to 0 and set the color.
209                 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
210                     stops[i].offset = 0;
211                     stops[i].color = blendedColor;
212                 }
213             } else {
214                 // All stops are below 0; just clamp them.
215                 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
216                     stops[i].offset = 0;
217             }
218         }
219
220         for (auto& stop : stops)
221             stop.offset /= scale;
222
223         m_data.startRadius *= scale;
224         m_data.endRadius *= scale;
225     }
226
227 private:
228     Gradient::RadialData& m_data;
229 };
230
231 class ConicGradientAdapter {
232 public:
233     float gradientLength() const { return 1; }
234     float maxExtent(float, float) const { return 1; }
235
236     void normalizeStopsAndEndpointsOutsideRange(Vector<GradientStop>& stops)
237     {
238         auto numStops = stops.size();
239         
240         size_t firstZeroOrGreaterIndex = numStops;
241         for (size_t i = 0; i < numStops; ++i) {
242             if (stops[i].offset >= 0) {
243                 firstZeroOrGreaterIndex = i;
244                 break;
245             }
246         }
247
248         if (firstZeroOrGreaterIndex > 0) {
249             if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
250                 float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
251                 float nextOffset = stops[firstZeroOrGreaterIndex].offset;
252                 
253                 float interStopProportion = -prevOffset / (nextOffset - prevOffset);
254                 // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
255                 Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
256                 
257                 // Clamp the positions to 0 and set the color.
258                 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
259                     stops[i].offset = 0;
260                     stops[i].color = blendedColor;
261                 }
262             } else {
263                 // All stops are below 0; just clamp them.
264                 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
265                     stops[i].offset = 0;
266             }
267         }
268
269         size_t lastOneOrLessIndex = numStops;
270         for (int i = numStops - 1; i >= 0; --i) {
271             if (stops[i].offset <= 1) {
272                 lastOneOrLessIndex = i;
273                 break;
274             }
275         }
276         
277         if (lastOneOrLessIndex < numStops - 1) {
278             if (lastOneOrLessIndex < numStops && stops[lastOneOrLessIndex].offset < 1) {
279                 float prevOffset = stops[lastOneOrLessIndex].offset;
280                 float nextOffset = stops[lastOneOrLessIndex + 1].offset;
281                 
282                 float interStopProportion = (1 - prevOffset) / (nextOffset - prevOffset);
283                 // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
284                 Color blendedColor = blend(stops[lastOneOrLessIndex].color, stops[lastOneOrLessIndex + 1].color, interStopProportion);
285                 
286                 // Clamp the positions to 1 and set the color.
287                 for (size_t i = lastOneOrLessIndex + 1; i < numStops; ++i) {
288                     stops[i].offset = 1;
289                     stops[i].color = blendedColor;
290                 }
291             } else {
292                 // All stops are above 1; just clamp them.
293                 for (size_t i = lastOneOrLessIndex; i < numStops; ++i)
294                     stops[i].offset = 1;
295             }
296         }
297     }
298 };
299
300 template<typename GradientAdapter>
301 Gradient::ColorStopVector CSSGradientValue::computeStops(GradientAdapter& gradientAdapter, const CSSToLengthConversionData& conversionData, const RenderStyle& style, float maxLengthForRepeat)
302 {
303     if (m_gradientType == CSSDeprecatedLinearGradient || m_gradientType == CSSDeprecatedRadialGradient) {
304         sortStopsIfNeeded();
305
306         Gradient::ColorStopVector result;
307         result.reserveInitialCapacity(m_stops.size());
308
309         for (auto& stop : m_stops) {
310             float offset;
311             if (stop.m_position->isPercentage())
312                 offset = stop.m_position->floatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
313             else
314                 offset = stop.m_position->floatValue(CSSPrimitiveValue::CSS_NUMBER);
315
316             Color color = stop.m_resolvedColor;
317             if (style.hasAppleColorFilter())
318                 style.appleColorFilter().transformColor(color);
319             result.uncheckedAppend({ offset, color });
320         }
321
322         return result;
323     }
324
325     size_t numStops = m_stops.size();
326     Vector<GradientStop> stops(numStops);
327
328     float gradientLength = gradientAdapter.gradientLength();
329
330     for (size_t i = 0; i < numStops; ++i) {
331         auto& stop = m_stops[i];
332
333         stops[i].isMidpoint = stop.isMidpoint;
334
335         Color color = stop.m_resolvedColor;
336         if (style.hasAppleColorFilter())
337             style.appleColorFilter().transformColor(color);
338
339         stops[i].color = color;
340
341         if (stop.m_position) {
342             auto& positionValue = *stop.m_position;
343             if (positionValue.isPercentage())
344                 stops[i].offset = positionValue.floatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
345             else if (positionValue.isLength() || positionValue.isViewportPercentageLength() || positionValue.isCalculatedPercentageWithLength()) {
346                 float length;
347                 if (positionValue.isLength())
348                     length = positionValue.computeLength<float>(conversionData);
349                 else {
350                     Ref<CalculationValue> calculationValue { positionValue.cssCalcValue()->createCalculationValue(conversionData) };
351                     length = calculationValue->evaluate(gradientLength);
352                 }
353                 stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0;
354             } else if (positionValue.isAngle())
355                 stops[i].offset = positionValue.floatValue(CSSPrimitiveValue::CSS_DEG) / 360;
356             else {
357                 ASSERT_NOT_REACHED();
358                 stops[i].offset = 0;
359             }
360             stops[i].specified = true;
361         } else {
362             // If the first color-stop does not have a position, its position defaults to 0%.
363             // If the last color-stop does not have a position, its position defaults to 100%.
364             if (!i) {
365                 stops[i].offset = 0;
366                 stops[i].specified = true;
367             } else if (numStops > 1 && i == numStops - 1) {
368                 stops[i].offset = 1;
369                 stops[i].specified = true;
370             }
371         }
372
373         // If a color-stop has a position that is less than the specified position of any
374         // color-stop before it in the list, its position is changed to be equal to the
375         // largest specified position of any color-stop before it.
376         if (stops[i].specified && i > 0) {
377             size_t prevSpecifiedIndex;
378             for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
379                 if (stops[prevSpecifiedIndex].specified)
380                     break;
381             }
382
383             if (stops[i].offset < stops[prevSpecifiedIndex].offset)
384                 stops[i].offset = stops[prevSpecifiedIndex].offset;
385         }
386     }
387
388     ASSERT(stops[0].specified && stops[numStops - 1].specified);
389
390     // If any color-stop still does not have a position, then, for each run of adjacent
391     // color-stops without positions, set their positions so that they are evenly spaced
392     // between the preceding and following color-stops with positions.
393     if (numStops > 2) {
394         size_t unspecifiedRunStart = 0;
395         bool inUnspecifiedRun = false;
396
397         for (size_t i = 0; i < numStops; ++i) {
398             if (!stops[i].specified && !inUnspecifiedRun) {
399                 unspecifiedRunStart = i;
400                 inUnspecifiedRun = true;
401             } else if (stops[i].specified && inUnspecifiedRun) {
402                 size_t unspecifiedRunEnd = i;
403
404                 if (unspecifiedRunStart < unspecifiedRunEnd) {
405                     float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
406                     float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
407                     float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
408
409                     for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
410                         stops[j].offset = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
411                 }
412
413                 inUnspecifiedRun = false;
414             }
415         }
416     }
417
418     // Walk over the color stops, look for midpoints and add stops as needed.
419     // If mid < 50%, add 2 stops to the left and 6 to the right
420     // else add 6 stops to the left and 2 to the right.
421     // Stops on the side with the most stops start midway because the curve approximates
422     // a line in that region. We then add 5 more color stops on that side to minimize the change
423     // how the luminance changes at each of the color stops. We don't have to add as many on the other side
424     // since it becomes small which increases the differentation of luminance which hides the color stops.
425     // Even with 4 extra color stops, it *is* possible to discern the steps when the gradient is large and has
426     // large luminance differences between midpoint and color stop. If this becomes an issue, we can consider
427     // making this algorithm a bit smarter.
428
429     // Midpoints that coincide with color stops are treated specially since they don't require
430     // extra stops and generate hard lines.
431     for (size_t x = 1; x < stops.size() - 1;) {
432         if (!stops[x].isMidpoint) {
433             ++x;
434             continue;
435         }
436
437         // Find previous and next color so we know what to interpolate between.
438         // We already know they have a color since we checked for that earlier.
439         Color color1 = stops[x - 1].color;
440         Color color2 = stops[x + 1].color;
441         // Likewise find the position of previous and next color stop.
442         float offset1 = stops[x - 1].offset;
443         float offset2 = stops[x + 1].offset;
444         float offset = stops[x].offset;
445
446         // Check if everything coincides or the midpoint is exactly in the middle.
447         // If so, ignore the midpoint.
448         if (offset - offset1 == offset2 - offset) {
449             stops.remove(x);
450             continue;
451         }
452
453         // Check if we coincide with the left color stop.
454         if (offset1 == offset) {
455             // Morph the midpoint to a regular stop with the color of the next color stop.
456             stops[x].color = color2;
457             stops[x].isMidpoint = false;
458             continue;
459         }
460
461         // Check if we coincide with the right color stop.
462         if (offset2 == offset) {
463             // Morph the midpoint to a regular stop with the color of the previous color stop.
464             stops[x].color = color1;
465             stops[x].isMidpoint = false;
466             continue;
467         }
468
469         float midpoint = (offset - offset1) / (offset2 - offset1);
470         GradientStop newStops[9];
471         if (midpoint > .5f) {
472             for (size_t y = 0; y < 7; ++y)
473                 newStops[y].offset = offset1 + (offset - offset1) * (7 + y) / 13;
474
475             newStops[7].offset = offset + (offset2 - offset) / 3;
476             newStops[8].offset = offset + (offset2 - offset) * 2 / 3;
477         } else {
478             newStops[0].offset = offset1 + (offset - offset1) / 3;
479             newStops[1].offset = offset1 + (offset - offset1) * 2 / 3;
480
481             for (size_t y = 0; y < 7; ++y)
482                 newStops[y + 2].offset = offset + (offset2 - offset) * y / 13;
483         }
484         // calculate colors
485         for (size_t y = 0; y < 9; ++y) {
486             float relativeOffset = (newStops[y].offset - offset1) / (offset2 - offset1);
487             float multiplier = std::pow(relativeOffset, std::log(.5f) / std::log(midpoint));
488             // FIXME: Why not premultiply here?
489             newStops[y].color = blend(color1, color2, multiplier, false /* do not premultiply */);
490         }
491
492         stops.remove(x);
493         stops.insert(x, newStops, 9);
494         x += 9;
495     }
496
497     numStops = stops.size();
498
499     // If the gradient is repeating, repeat the color stops.
500     // We can't just push this logic down into the platform-specific Gradient code,
501     // because we have to know the extent of the gradient, and possible move the end points.
502     if (m_repeating && numStops > 1) {
503         // If the difference in the positions of the first and last color-stops is 0,
504         // the gradient defines a solid-color image with the color of the last color-stop in the rule.
505         float gradientRange = stops.last().offset - stops.first().offset;
506         if (!gradientRange) {
507             stops.first().offset = 0;
508             stops.first().color = stops.last().color;
509             stops.shrink(1);
510             numStops = 1;
511         } else {
512             float maxExtent = gradientAdapter.maxExtent(maxLengthForRepeat, gradientLength);
513
514             size_t originalNumStops = numStops;
515             size_t originalFirstStopIndex = 0;
516
517             // Work backwards from the first, adding stops until we get one before 0.
518             float firstOffset = stops[0].offset;
519             if (firstOffset > 0) {
520                 float currOffset = firstOffset;
521                 size_t srcStopOrdinal = originalNumStops - 1;
522
523                 while (true) {
524                     GradientStop newStop = stops[originalFirstStopIndex + srcStopOrdinal];
525                     newStop.offset = currOffset;
526                     stops.insert(0, newStop);
527                     ++originalFirstStopIndex;
528                     if (currOffset < 0)
529                         break;
530
531                     if (srcStopOrdinal)
532                         currOffset -= stops[originalFirstStopIndex + srcStopOrdinal].offset - stops[originalFirstStopIndex + srcStopOrdinal - 1].offset;
533                     srcStopOrdinal = (srcStopOrdinal + originalNumStops - 1) % originalNumStops;
534                 }
535             }
536
537             // Work forwards from the end, adding stops until we get one after 1.
538             float lastOffset = stops[stops.size() - 1].offset;
539             if (lastOffset < maxExtent) {
540                 float currOffset = lastOffset;
541                 size_t srcStopOrdinal = 0;
542
543                 while (true) {
544                     size_t srcStopIndex = originalFirstStopIndex + srcStopOrdinal;
545                     GradientStop newStop = stops[srcStopIndex];
546                     newStop.offset = currOffset;
547                     stops.append(newStop);
548                     if (currOffset > maxExtent)
549                         break;
550                     if (srcStopOrdinal < originalNumStops - 1)
551                         currOffset += stops[srcStopIndex + 1].offset - stops[srcStopIndex].offset;
552                     srcStopOrdinal = (srcStopOrdinal + 1) % originalNumStops;
553                 }
554             }
555         }
556     }
557
558     // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and adjusting the stops.
559     if (stops.size() > 1 && (stops.first().offset < 0 || stops.last().offset > 1))
560         gradientAdapter.normalizeStopsAndEndpointsOutsideRange(stops);
561     
562     Gradient::ColorStopVector result;
563     result.reserveInitialCapacity(stops.size());
564     for (auto& stop : stops)
565         result.uncheckedAppend({ stop.offset, stop.color });
566
567     return result;
568 }
569
570 static float positionFromValue(const CSSPrimitiveValue* value, const CSSToLengthConversionData& conversionData, const FloatSize& size, bool isHorizontal)
571 {
572     int origin = 0;
573     int sign = 1;
574     int edgeDistance = isHorizontal ? size.width() : size.height();
575     
576     // In this case the center of the gradient is given relative to an edge in the
577     // form of: [ top | bottom | right | left ] [ <percentage> | <length> ].
578     if (value->isPair()) {
579         CSSValueID originID = value->pairValue()->first()->valueID();
580         value = value->pairValue()->second();
581         
582         if (originID == CSSValueRight || originID == CSSValueBottom) {
583             // For right/bottom, the offset is relative to the far edge.
584             origin = edgeDistance;
585             sign = -1;
586         }
587     }
588     
589     if (value->isNumber())
590         return origin + sign * value->floatValue() * conversionData.zoom();
591     
592     if (value->isPercentage())
593         return origin + sign * value->floatValue() / 100.f * edgeDistance;
594
595     if (value->isCalculatedPercentageWithLength()) {
596         Ref<CalculationValue> calculationValue { value->cssCalcValue()->createCalculationValue(conversionData) };
597         return origin + sign * calculationValue->evaluate(edgeDistance);
598     }
599     
600     switch (value->valueID()) {
601     case CSSValueTop:
602         ASSERT(!isHorizontal);
603         return 0;
604     case CSSValueLeft:
605         ASSERT(isHorizontal);
606         return 0;
607     case CSSValueBottom:
608         ASSERT(!isHorizontal);
609         return size.height();
610     case CSSValueRight:
611         ASSERT(isHorizontal);
612         return size.width();
613     case CSSValueCenter:
614         return origin + sign * .5f * edgeDistance;
615     default:
616         break;
617     }
618
619     return origin + sign * value->computeLength<float>(conversionData);
620 }
621
622 FloatPoint CSSGradientValue::computeEndPoint(CSSPrimitiveValue* horizontal, CSSPrimitiveValue* vertical, const CSSToLengthConversionData& conversionData, const FloatSize& size)
623 {
624     FloatPoint result;
625
626     if (horizontal)
627         result.setX(positionFromValue(horizontal, conversionData, size, true));
628
629     if (vertical)
630         result.setY(positionFromValue(vertical, conversionData, size, false));
631
632     return result;
633 }
634
635 bool CSSGradientValue::isCacheable() const
636 {
637     for (auto& stop : m_stops) {
638         if (stop.m_colorIsDerivedFromElement)
639             return false;
640
641         if (!stop.m_position)
642             continue;
643
644         if (stop.m_position->isFontRelativeLength())
645             return false;
646     }
647
648     return true;
649 }
650
651 bool CSSGradientValue::knownToBeOpaque(const RenderElement& renderer) const
652 {
653     bool hasColorFilter = renderer.style().hasAppleColorFilter();
654
655     for (auto& stop : m_stops) {
656         if (hasColorFilter) {
657             Color stopColor = stop.m_resolvedColor;
658             renderer.style().appleColorFilter().transformColor(stopColor);
659             if (!stopColor.isOpaque())
660                 return false;
661         }
662
663         if (!stop.m_resolvedColor.isOpaque())
664             return false;
665     }
666     return true;
667 }
668
669 static void appendGradientStops(StringBuilder& builder, const Vector<CSSGradientColorStop, 2>& stops)
670 {
671     for (auto& stop : stops) {
672         double position = stop.m_position->doubleValue(CSSPrimitiveValue::CSS_NUMBER);
673         if (!position)
674             builder.append(", from(", stop.m_color->cssText(), ')');
675         else if (position == 1)
676             builder.append(", to(", stop.m_color->cssText(), ')');
677         else
678             builder.append(", color-stop(", FormattedNumber::fixedPrecision(position), ", ", stop.m_color->cssText(), ')');
679     }
680 }
681
682 String CSSLinearGradientValue::customCSSText() const
683 {
684     StringBuilder result;
685     if (m_gradientType == CSSDeprecatedLinearGradient) {
686         result.append("-webkit-gradient(linear, ", m_firstX->cssText(), ' ', m_firstY->cssText(), ", ", m_secondX->cssText(), ' ', m_secondY->cssText());
687         appendGradientStops(result, m_stops);
688     } else if (m_gradientType == CSSPrefixedLinearGradient) {
689         if (m_repeating)
690             result.appendLiteral("-webkit-repeating-linear-gradient(");
691         else
692             result.appendLiteral("-webkit-linear-gradient(");
693
694         if (m_angle)
695             result.append(m_angle->cssText());
696         else {
697             if (m_firstX && m_firstY)
698                 result.append(m_firstX->cssText(), ' ', m_firstY->cssText());
699             else if (m_firstX)
700                 result.append(m_firstX->cssText());
701             else if (m_firstY)
702                 result.append(m_firstY->cssText());
703         }
704
705         for (auto& stop : m_stops) {
706             result.append(", ", stop.m_color->cssText());
707             if (stop.m_position)
708                 result.append(' ', stop.m_position->cssText());
709         }
710     } else {
711         if (m_repeating)
712             result.appendLiteral("repeating-linear-gradient(");
713         else
714             result.appendLiteral("linear-gradient(");
715
716         bool wroteSomething = false;
717
718         if (m_angle && m_angle->computeDegrees() != 180) {
719             result.append(m_angle->cssText());
720             wroteSomething = true;
721         } else if ((m_firstX || m_firstY) && !(!m_firstX && m_firstY && m_firstY->valueID() == CSSValueBottom)) {
722             result.appendLiteral("to ");
723             if (m_firstX && m_firstY)
724                 result.append(m_firstX->cssText(), ' ', m_firstY->cssText());
725             else if (m_firstX)
726                 result.append(m_firstX->cssText());
727             else
728                 result.append(m_firstY->cssText());
729             wroteSomething = true;
730         }
731
732         if (wroteSomething)
733             result.appendLiteral(", ");
734
735         bool wroteFirstStop = false;
736         for (auto& stop : m_stops) {
737             if (wroteFirstStop)
738                 result.appendLiteral(", ");
739             wroteFirstStop = true;
740             if (!stop.isMidpoint)
741                 result.append(stop.m_color->cssText());
742             if (stop.m_position) {
743                 if (!stop.isMidpoint)
744                     result.append(' ');
745                 result.append(stop.m_position->cssText());
746             }
747         }
748     }
749
750     result.append(')');
751     return result.toString();
752 }
753
754 // Compute the endpoints so that a gradient of the given angle covers a box of the given size.
755 static void endPointsFromAngle(float angleDeg, const FloatSize& size, FloatPoint& firstPoint, FloatPoint& secondPoint, CSSGradientType type)
756 {
757     // Prefixed gradients use "polar coordinate" angles, rather than "bearing" angles.
758     if (type == CSSPrefixedLinearGradient)
759         angleDeg = 90 - angleDeg;
760
761     angleDeg = fmodf(angleDeg, 360);
762     if (angleDeg < 0)
763         angleDeg += 360;
764
765     if (!angleDeg) {
766         firstPoint.set(0, size.height());
767         secondPoint.set(0, 0);
768         return;
769     }
770
771     if (angleDeg == 90) {
772         firstPoint.set(0, 0);
773         secondPoint.set(size.width(), 0);
774         return;
775     }
776
777     if (angleDeg == 180) {
778         firstPoint.set(0, 0);
779         secondPoint.set(0, size.height());
780         return;
781     }
782
783     if (angleDeg == 270) {
784         firstPoint.set(size.width(), 0);
785         secondPoint.set(0, 0);
786         return;
787     }
788
789     // angleDeg is a "bearing angle" (0deg = N, 90deg = E),
790     // but tan expects 0deg = E, 90deg = N.
791     float slope = tan(deg2rad(90 - angleDeg));
792
793     // We find the endpoint by computing the intersection of the line formed by the slope,
794     // and a line perpendicular to it that intersects the corner.
795     float perpendicularSlope = -1 / slope;
796
797     // Compute start corner relative to center, in Cartesian space (+y = up).
798     float halfHeight = size.height() / 2;
799     float halfWidth = size.width() / 2;
800     FloatPoint endCorner;
801     if (angleDeg < 90)
802         endCorner.set(halfWidth, halfHeight);
803     else if (angleDeg < 180)
804         endCorner.set(halfWidth, -halfHeight);
805     else if (angleDeg < 270)
806         endCorner.set(-halfWidth, -halfHeight);
807     else
808         endCorner.set(-halfWidth, halfHeight);
809
810     // Compute c (of y = mx + c) using the corner point.
811     float c = endCorner.y() - perpendicularSlope * endCorner.x();
812     float endX = c / (slope - perpendicularSlope);
813     float endY = perpendicularSlope * endX + c;
814
815     // We computed the end point, so set the second point, 
816     // taking into account the moved origin and the fact that we're in drawing space (+y = down).
817     secondPoint.set(halfWidth + endX, halfHeight - endY);
818     // Reflect around the center for the start point.
819     firstPoint.set(halfWidth - endX, halfHeight + endY);
820 }
821
822 Ref<Gradient> CSSLinearGradientValue::createGradient(RenderElement& renderer, const FloatSize& size)
823 {
824     ASSERT(!size.isEmpty());
825
826     CSSToLengthConversionData conversionData(&renderer.style(), renderer.document().documentElement()->renderStyle(), &renderer.view());
827
828     FloatPoint firstPoint;
829     FloatPoint secondPoint;
830     if (m_angle) {
831         float angle = m_angle->floatValue(CSSPrimitiveValue::CSS_DEG);
832         endPointsFromAngle(angle, size, firstPoint, secondPoint, m_gradientType);
833     } else {
834         switch (m_gradientType) {
835         case CSSDeprecatedLinearGradient:
836             firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
837             if (m_secondX || m_secondY)
838                 secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), conversionData, size);
839             else {
840                 if (m_firstX)
841                     secondPoint.setX(size.width() - firstPoint.x());
842                 if (m_firstY)
843                     secondPoint.setY(size.height() - firstPoint.y());
844             }
845             break;
846         case CSSPrefixedLinearGradient:
847             firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
848             if (m_firstX)
849                 secondPoint.setX(size.width() - firstPoint.x());
850             if (m_firstY)
851                 secondPoint.setY(size.height() - firstPoint.y());
852             break;
853         case CSSLinearGradient:
854             if (m_firstX && m_firstY) {
855                 // "Magic" corners, so the 50% line touches two corners.
856                 float rise = size.width();
857                 float run = size.height();
858                 if (m_firstX && m_firstX->valueID() == CSSValueLeft)
859                     run *= -1;
860                 if (m_firstY && m_firstY->valueID() == CSSValueBottom)
861                     rise *= -1;
862                 // Compute angle, and flip it back to "bearing angle" degrees.
863                 float angle = 90 - rad2deg(atan2(rise, run));
864                 endPointsFromAngle(angle, size, firstPoint, secondPoint, m_gradientType);
865             } else if (m_firstX || m_firstY) { 
866                 secondPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
867                 if (m_firstX)
868                     firstPoint.setX(size.width() - secondPoint.x());
869                 if (m_firstY)
870                     firstPoint.setY(size.height() - secondPoint.y());
871             } else
872                 secondPoint.setY(size.height());
873             break;
874         default:
875             ASSERT_NOT_REACHED();
876         }
877     }
878
879     Gradient::LinearData data { firstPoint, secondPoint };
880     LinearGradientAdapter adapter { data };
881     auto stops = computeStops(adapter, conversionData, renderer.style(), 1);
882
883     auto gradient = Gradient::create(WTFMove(data));
884     gradient->setSortedColorStops(WTFMove(stops));
885     return gradient;
886 }
887
888 bool CSSLinearGradientValue::equals(const CSSLinearGradientValue& other) const
889 {
890     if (m_gradientType == CSSDeprecatedLinearGradient)
891         return other.m_gradientType == m_gradientType
892             && compareCSSValuePtr(m_firstX, other.m_firstX)
893             && compareCSSValuePtr(m_firstY, other.m_firstY)
894             && compareCSSValuePtr(m_secondX, other.m_secondX)
895             && compareCSSValuePtr(m_secondY, other.m_secondY)
896             && m_stops == other.m_stops;
897
898     if (m_repeating != other.m_repeating)
899         return false;
900
901     if (m_angle)
902         return compareCSSValuePtr(m_angle, other.m_angle) && m_stops == other.m_stops;
903
904     if (other.m_angle)
905         return false;
906
907     bool equalXandY = false;
908     if (m_firstX && m_firstY)
909         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
910     else if (m_firstX)
911         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
912     else if (m_firstY)
913         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
914     else
915         equalXandY = !other.m_firstX && !other.m_firstY;
916
917     return equalXandY && m_stops == other.m_stops;
918 }
919
920 String CSSRadialGradientValue::customCSSText() const
921 {
922     StringBuilder result;
923
924     if (m_gradientType == CSSDeprecatedRadialGradient) {
925         result.append("-webkit-gradient(radial, ", m_firstX->cssText(), ' ', m_firstY->cssText(), ", ", m_firstRadius->cssText(),
926             ", ", m_secondX->cssText(), ' ', m_secondY->cssText(), ", ", m_secondRadius->cssText());
927         appendGradientStops(result, m_stops);
928     } else if (m_gradientType == CSSPrefixedRadialGradient) {
929         if (m_repeating)
930             result.appendLiteral("-webkit-repeating-radial-gradient(");
931         else
932             result.appendLiteral("-webkit-radial-gradient(");
933
934         if (m_firstX && m_firstY)
935             result.append(m_firstX->cssText(), ' ', m_firstY->cssText());
936         else if (m_firstX)
937             result.append(m_firstX->cssText());
938         else if (m_firstY)
939             result.append(m_firstY->cssText());
940         else
941             result.appendLiteral("center");
942
943         if (m_shape || m_sizingBehavior) {
944             result.appendLiteral(", ");
945             if (m_shape)
946                 result.append(m_shape->cssText(), ' ');
947             else
948                 result.appendLiteral("ellipse ");
949             if (m_sizingBehavior)
950                 result.append(m_sizingBehavior->cssText());
951             else
952                 result.appendLiteral("cover");
953         } else if (m_endHorizontalSize && m_endVerticalSize)
954             result.append(", ", m_endHorizontalSize->cssText(), ' ', m_endVerticalSize->cssText());
955
956         for (auto& stop : m_stops) {
957             result.append(", ", stop.m_color->cssText());
958             if (stop.m_position)
959                 result.append(' ', stop.m_position->cssText());
960         }
961     } else {
962         if (m_repeating)
963             result.appendLiteral("repeating-radial-gradient(");
964         else
965             result.appendLiteral("radial-gradient(");
966
967         bool wroteSomething = false;
968
969         // The only ambiguous case that needs an explicit shape to be provided
970         // is when a sizing keyword is used (or all sizing is omitted).
971         if (m_shape && m_shape->valueID() != CSSValueEllipse && (m_sizingBehavior || (!m_sizingBehavior && !m_endHorizontalSize))) {
972             result.appendLiteral("circle");
973             wroteSomething = true;
974         }
975
976         if (m_sizingBehavior && m_sizingBehavior->valueID() != CSSValueFarthestCorner) {
977             if (wroteSomething)
978                 result.append(' ');
979             result.append(m_sizingBehavior->cssText());
980             wroteSomething = true;
981         } else if (m_endHorizontalSize) {
982             if (wroteSomething)
983                 result.append(' ');
984             result.append(m_endHorizontalSize->cssText());
985             if (m_endVerticalSize)
986                 result.append(' ', m_endVerticalSize->cssText());
987             wroteSomething = true;
988         }
989
990         if (m_firstX || m_firstY) {
991             if (wroteSomething)
992                 result.append(' ');
993             result.appendLiteral("at ");
994             if (m_firstX && m_firstY)
995                 result.append(m_firstX->cssText(), ' ', m_firstY->cssText());
996             else if (m_firstX)
997                 result.append(m_firstX->cssText());
998             else
999                 result.append(m_firstY->cssText());
1000             wroteSomething = true;
1001         }
1002
1003         if (wroteSomething)
1004             result.appendLiteral(", ");
1005
1006         for (unsigned i = 0; i < m_stops.size(); i++) {
1007             const CSSGradientColorStop& stop = m_stops[i];
1008             if (i)
1009                 result.appendLiteral(", ");
1010             if (!stop.isMidpoint)
1011                 result.append(stop.m_color->cssText());
1012             if (stop.m_position) {
1013                 if (!stop.isMidpoint)
1014                     result.append(' ');
1015                 result.append(stop.m_position->cssText());
1016             }
1017         }
1018
1019     }
1020
1021     result.append(')');
1022     return result.toString();
1023 }
1024
1025 float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue& radius, const CSSToLengthConversionData& conversionData, float* widthOrHeight)
1026 {
1027     float result = 0;
1028     if (radius.isNumber())
1029         result = radius.floatValue() * conversionData.zoom();
1030     else if (widthOrHeight && radius.isPercentage())
1031         result = *widthOrHeight * radius.floatValue() / 100;
1032     else
1033         result = radius.computeLength<float>(conversionData);
1034     return result;
1035 }
1036
1037 static float distanceToClosestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
1038 {
1039     FloatPoint topLeft;
1040     float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
1041
1042     FloatPoint topRight(size.width(), 0);
1043     float topRightDistance = FloatSize(p - topRight).diagonalLength();
1044
1045     FloatPoint bottomLeft(0, size.height());
1046     float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
1047
1048     FloatPoint bottomRight(size.width(), size.height());
1049     float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
1050
1051     corner = topLeft;
1052     float minDistance = topLeftDistance;
1053     if (topRightDistance < minDistance) {
1054         minDistance = topRightDistance;
1055         corner = topRight;
1056     }
1057
1058     if (bottomLeftDistance < minDistance) {
1059         minDistance = bottomLeftDistance;
1060         corner = bottomLeft;
1061     }
1062
1063     if (bottomRightDistance < minDistance) {
1064         minDistance = bottomRightDistance;
1065         corner = bottomRight;
1066     }
1067     return minDistance;
1068 }
1069
1070 static float distanceToFarthestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
1071 {
1072     FloatPoint topLeft;
1073     float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
1074
1075     FloatPoint topRight(size.width(), 0);
1076     float topRightDistance = FloatSize(p - topRight).diagonalLength();
1077
1078     FloatPoint bottomLeft(0, size.height());
1079     float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
1080
1081     FloatPoint bottomRight(size.width(), size.height());
1082     float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
1083
1084     corner = topLeft;
1085     float maxDistance = topLeftDistance;
1086     if (topRightDistance > maxDistance) {
1087         maxDistance = topRightDistance;
1088         corner = topRight;
1089     }
1090
1091     if (bottomLeftDistance > maxDistance) {
1092         maxDistance = bottomLeftDistance;
1093         corner = bottomLeft;
1094     }
1095
1096     if (bottomRightDistance > maxDistance) {
1097         maxDistance = bottomRightDistance;
1098         corner = bottomRight;
1099     }
1100     return maxDistance;
1101 }
1102
1103 // Compute horizontal radius of ellipse with center at 0,0 which passes through p, and has
1104 // width/height given by aspectRatio.
1105 static inline float horizontalEllipseRadius(const FloatSize& p, float aspectRatio)
1106 {
1107     // x^2/a^2 + y^2/b^2 = 1
1108     // a/b = aspectRatio, b = a/aspectRatio
1109     // a = sqrt(x^2 + y^2/(1/r^2))
1110     return sqrtf(p.width() * p.width() + (p.height() * p.height()) / (1 / (aspectRatio * aspectRatio)));
1111 }
1112
1113 // FIXME: share code with the linear version
1114 Ref<Gradient> CSSRadialGradientValue::createGradient(RenderElement& renderer, const FloatSize& size)
1115 {
1116     ASSERT(!size.isEmpty());
1117
1118     CSSToLengthConversionData conversionData(&renderer.style(), renderer.document().documentElement()->renderStyle(), &renderer.view());
1119
1120     FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
1121     if (!m_firstX)
1122         firstPoint.setX(size.width() / 2);
1123     if (!m_firstY)
1124         firstPoint.setY(size.height() / 2);
1125
1126     FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), conversionData, size);
1127     if (!m_secondX)
1128         secondPoint.setX(size.width() / 2);
1129     if (!m_secondY)
1130         secondPoint.setY(size.height() / 2);
1131
1132     float firstRadius = 0;
1133     if (m_firstRadius)
1134         firstRadius = resolveRadius(*m_firstRadius, conversionData);
1135
1136     float secondRadius = 0;
1137     float aspectRatio = 1; // width / height.
1138     if (m_secondRadius)
1139         secondRadius = resolveRadius(*m_secondRadius, conversionData);
1140     else if (m_endHorizontalSize) {
1141         float width = size.width();
1142         float height = size.height();
1143         secondRadius = resolveRadius(*m_endHorizontalSize, conversionData, &width);
1144         if (m_endVerticalSize)
1145             aspectRatio = secondRadius / resolveRadius(*m_endVerticalSize, conversionData, &height);
1146         else
1147             aspectRatio = 1;
1148     } else {
1149         enum GradientShape { Circle, Ellipse };
1150         GradientShape shape = Ellipse;
1151         if ((m_shape && m_shape->valueID() == CSSValueCircle)
1152             || (!m_shape && !m_sizingBehavior && m_endHorizontalSize && !m_endVerticalSize))
1153             shape = Circle;
1154
1155         enum GradientFill { ClosestSide, ClosestCorner, FarthestSide, FarthestCorner };
1156         GradientFill fill = FarthestCorner;
1157
1158         switch (m_sizingBehavior ? m_sizingBehavior->valueID() : 0) {
1159         case CSSValueContain:
1160         case CSSValueClosestSide:
1161             fill = ClosestSide;
1162             break;
1163         case CSSValueClosestCorner:
1164             fill = ClosestCorner;
1165             break;
1166         case CSSValueFarthestSide:
1167             fill = FarthestSide;
1168             break;
1169         case CSSValueCover:
1170         case CSSValueFarthestCorner:
1171             fill = FarthestCorner;
1172             break;
1173         default:
1174             break;
1175         }
1176
1177         // Now compute the end radii based on the second point, shape and fill.
1178
1179         // Horizontal
1180         switch (fill) {
1181         case ClosestSide: {
1182             float xDist = std::min(secondPoint.x(), size.width() - secondPoint.x());
1183             float yDist = std::min(secondPoint.y(), size.height() - secondPoint.y());
1184             if (shape == Circle) {
1185                 float smaller = std::min(xDist, yDist);
1186                 xDist = smaller;
1187                 yDist = smaller;
1188             }
1189             secondRadius = xDist;
1190             aspectRatio = xDist / yDist;
1191             break;
1192         }
1193         case FarthestSide: {
1194             float xDist = std::max(secondPoint.x(), size.width() - secondPoint.x());
1195             float yDist = std::max(secondPoint.y(), size.height() - secondPoint.y());
1196             if (shape == Circle) {
1197                 float larger = std::max(xDist, yDist);
1198                 xDist = larger;
1199                 yDist = larger;
1200             }
1201             secondRadius = xDist;
1202             aspectRatio = xDist / yDist;
1203             break;
1204         }
1205         case ClosestCorner: {
1206             FloatPoint corner;
1207             float distance = distanceToClosestCorner(secondPoint, size, corner);
1208             if (shape == Circle)
1209                 secondRadius = distance;
1210             else {
1211                 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
1212                 // that it would if closest-side or farthest-side were specified, as appropriate.
1213                 float xDist = std::min(secondPoint.x(), size.width() - secondPoint.x());
1214                 float yDist = std::min(secondPoint.y(), size.height() - secondPoint.y());
1215
1216                 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
1217                 aspectRatio = xDist / yDist;
1218             }
1219             break;
1220         }
1221
1222         case FarthestCorner: {
1223             FloatPoint corner;
1224             float distance = distanceToFarthestCorner(secondPoint, size, corner);
1225             if (shape == Circle)
1226                 secondRadius = distance;
1227             else {
1228                 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
1229                 // that it would if closest-side or farthest-side were specified, as appropriate.
1230                 float xDist = std::max(secondPoint.x(), size.width() - secondPoint.x());
1231                 float yDist = std::max(secondPoint.y(), size.height() - secondPoint.y());
1232
1233                 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
1234                 aspectRatio = xDist / yDist;
1235             }
1236             break;
1237         }
1238         }
1239     }
1240
1241     // computeStops() only uses maxExtent for repeating gradients.
1242     float maxExtent = 0;
1243     if (m_repeating) {
1244         FloatPoint corner;
1245         maxExtent = distanceToFarthestCorner(secondPoint, size, corner);
1246     }
1247
1248     Gradient::RadialData data { firstPoint, secondPoint, firstRadius, secondRadius, aspectRatio };
1249     RadialGradientAdapter adapter { data };
1250     auto stops = computeStops(adapter, conversionData, renderer.style(), maxExtent);
1251
1252     auto gradient = Gradient::create(WTFMove(data));
1253     gradient->setSortedColorStops(WTFMove(stops));
1254     return gradient;
1255 }
1256
1257 bool CSSRadialGradientValue::equals(const CSSRadialGradientValue& other) const
1258 {
1259     if (m_gradientType == CSSDeprecatedRadialGradient)
1260         return other.m_gradientType == m_gradientType
1261             && compareCSSValuePtr(m_firstX, other.m_firstX)
1262             && compareCSSValuePtr(m_firstY, other.m_firstY)
1263             && compareCSSValuePtr(m_secondX, other.m_secondX)
1264             && compareCSSValuePtr(m_secondY, other.m_secondY)
1265             && compareCSSValuePtr(m_firstRadius, other.m_firstRadius)
1266             && compareCSSValuePtr(m_secondRadius, other.m_secondRadius)
1267             && m_stops == other.m_stops;
1268
1269     if (m_repeating != other.m_repeating)
1270         return false;
1271
1272     bool equalXandY = false;
1273     if (m_firstX && m_firstY)
1274         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
1275     else if (m_firstX)
1276         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
1277     else if (m_firstY)
1278         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
1279     else
1280         equalXandY = !other.m_firstX && !other.m_firstY;
1281
1282     if (!equalXandY)
1283         return false;
1284
1285     bool equalShape = true;
1286     bool equalSizingBehavior = true;
1287     bool equalHorizontalAndVerticalSize = true;
1288
1289     if (m_shape)
1290         equalShape = compareCSSValuePtr(m_shape, other.m_shape);
1291     else if (m_sizingBehavior)
1292         equalSizingBehavior = compareCSSValuePtr(m_sizingBehavior, other.m_sizingBehavior);
1293     else if (m_endHorizontalSize && m_endVerticalSize)
1294         equalHorizontalAndVerticalSize = compareCSSValuePtr(m_endHorizontalSize, other.m_endHorizontalSize) && compareCSSValuePtr(m_endVerticalSize, other.m_endVerticalSize);
1295     else {
1296         equalShape = !other.m_shape;
1297         equalSizingBehavior = !other.m_sizingBehavior;
1298         equalHorizontalAndVerticalSize = !other.m_endHorizontalSize && !other.m_endVerticalSize;
1299     }
1300     return equalShape && equalSizingBehavior && equalHorizontalAndVerticalSize && m_stops == other.m_stops;
1301 }
1302
1303
1304 String CSSConicGradientValue::customCSSText() const
1305 {
1306     StringBuilder result;
1307
1308     if (m_repeating)
1309         result.appendLiteral("repeating-conic-gradient(");
1310     else
1311         result.appendLiteral("conic-gradient(");
1312
1313     bool wroteSomething = false;
1314
1315     if (m_angle) {
1316         result.append("from ", m_angle->cssText());
1317         wroteSomething = true;
1318     }
1319
1320     if (m_firstX && m_firstY) {
1321         if (wroteSomething)
1322             result.append(' ');
1323         result.append("at ", m_firstX->cssText(), ' ', m_firstY->cssText());
1324         wroteSomething = true;
1325     }
1326
1327     if (wroteSomething)
1328         result.appendLiteral(", ");
1329
1330     bool wroteFirstStop = false;
1331     for (auto& stop : m_stops) {
1332         if (wroteFirstStop)
1333             result.appendLiteral(", ");
1334         wroteFirstStop = true;
1335         if (!stop.isMidpoint)
1336             result.append(stop.m_color->cssText());
1337         if (stop.m_position) {
1338             if (!stop.isMidpoint)
1339                 result.append(' ');
1340             result.append(stop.m_position->cssText());
1341         }
1342     }
1343     
1344     result.append(')');
1345     return result.toString();
1346 }
1347
1348 Ref<Gradient> CSSConicGradientValue::createGradient(RenderElement& renderer, const FloatSize& size)
1349 {
1350     ASSERT(!size.isEmpty());
1351
1352     CSSToLengthConversionData conversionData(&renderer.style(), renderer.document().documentElement()->renderStyle(), &renderer.view());
1353
1354     FloatPoint centerPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), conversionData, size);
1355     if (!m_firstX)
1356         centerPoint.setX(size.width() / 2);
1357     if (!m_firstY)
1358         centerPoint.setY(size.height() / 2);
1359
1360     float angleRadians = 0;
1361     if (m_angle)
1362         angleRadians = m_angle->floatValue(CSSPrimitiveValue::CSS_RAD);
1363
1364     Gradient::ConicData data { centerPoint, angleRadians };
1365     ConicGradientAdapter adapter;
1366     auto stops = computeStops(adapter, conversionData, renderer.style(), 1);
1367
1368     auto gradient = Gradient::create(WTFMove(data));
1369     gradient->setSortedColorStops(WTFMove(stops));
1370     return gradient;
1371 }
1372
1373 bool CSSConicGradientValue::equals(const CSSConicGradientValue& other) const
1374 {
1375     if (m_repeating != other.m_repeating)
1376         return false;
1377
1378     if (!compareCSSValuePtr(m_angle, other.m_angle))
1379         return false;
1380
1381     bool equalXandY = false;
1382     if (m_firstX && m_firstY)
1383         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && compareCSSValuePtr(m_firstY, other.m_firstY);
1384     else if (m_firstX)
1385         equalXandY = compareCSSValuePtr(m_firstX, other.m_firstX) && !other.m_firstY;
1386     else if (m_firstY)
1387         equalXandY = compareCSSValuePtr(m_firstY, other.m_firstY) && !other.m_firstX;
1388     else
1389         equalXandY = !other.m_firstX && !other.m_firstY;
1390
1391     return equalXandY && m_stops == other.m_stops;
1392 }
1393
1394 } // namespace WebCore