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