2 * Copyright (C) 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "CSSGradientValue.h"
29 #include "CSSValueKeywords.h"
30 #include "CSSStyleSelector.h"
31 #include "GeneratedImage.h"
35 #include "IntSizeHash.h"
36 #include "NodeRenderStyle.h"
37 #include "PlatformString.h"
38 #include "RenderObject.h"
44 PassRefPtr<Image> CSSGradientValue::image(RenderObject* renderer, const IntSize& size)
49 bool cacheable = isCacheable();
51 if (!m_clients.contains(renderer))
54 // Need to look up our size. Create a string of width*height to use as a hash key.
55 Image* result = getImage(renderer, size);
60 // We need to create an image.
61 RefPtr<Image> newImage = GeneratedImage::create(createGradient(renderer, size), size);
63 putImage(size, newImage);
65 return newImage.release();
68 // Should only ever be called for deprecated gradients.
69 static inline bool compareStops(const CSSGradientColorStop& a, const CSSGradientColorStop& b)
71 double aVal = a.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER);
72 double bVal = b.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER);
77 void CSSGradientValue::sortStopsIfNeeded()
79 ASSERT(m_deprecatedType);
82 std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
87 static inline int blend(int from, int to, float progress)
89 return int(from + (to - from) * progress);
92 static inline Color blend(const Color& from, const Color& to, float progress)
94 // FIXME: when we interpolate gradients using premultiplied colors, this should also do premultiplication.
95 return Color(blend(from.red(), to.red(), progress),
96 blend(from.green(), to.green(), progress),
97 blend(from.blue(), to.blue(), progress),
98 blend(from.alpha(), to.alpha(), progress));
101 struct GradientStop {
112 void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, RenderStyle* rootStyle, float maxLengthForRepeat)
114 RenderStyle* style = renderer->style();
116 if (m_deprecatedType) {
119 // We have to resolve colors.
120 for (unsigned i = 0; i < m_stops.size(); i++) {
121 const CSSGradientColorStop& stop = m_stops[i];
122 Color color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
125 if (stop.m_position->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
126 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
128 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_NUMBER);
130 gradient->addColorStop(offset, color);
133 // The back end already sorted the stops.
134 gradient->setStopsSorted(true);
138 size_t numStops = m_stops.size();
140 Vector<GradientStop> stops(numStops);
142 float gradientLength = 0;
143 bool computedGradientLength = false;
145 FloatPoint gradientStart = gradient->p0();
146 FloatPoint gradientEnd;
147 if (isLinearGradient())
148 gradientEnd = gradient->p1();
149 else if (isRadialGradient())
150 gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0);
152 for (size_t i = 0; i < numStops; ++i) {
153 const CSSGradientColorStop& stop = m_stops[i];
155 stops[i].color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
157 if (stop.m_position) {
158 int type = stop.m_position->primitiveType();
159 if (type == CSSPrimitiveValue::CSS_PERCENTAGE)
160 stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
161 else if (CSSPrimitiveValue::isUnitTypeLength(type)) {
162 float length = stop.m_position->computeLength<float>(style, rootStyle, style->effectiveZoom());
163 if (!computedGradientLength) {
164 FloatSize gradientSize(gradientStart - gradientEnd);
165 gradientLength = gradientSize.diagonalLength();
167 stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0;
169 ASSERT_NOT_REACHED();
172 stops[i].specified = true;
174 // If the first color-stop does not have a position, its position defaults to 0%.
175 // If the last color-stop does not have a position, its position defaults to 100%.
178 stops[i].specified = true;
179 } else if (numStops > 1 && i == numStops - 1) {
181 stops[i].specified = true;
185 // If a color-stop has a position that is less than the specified position of any
186 // color-stop before it in the list, its position is changed to be equal to the
187 // largest specified position of any color-stop before it.
188 if (stops[i].specified && i > 0) {
189 size_t prevSpecifiedIndex;
190 for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
191 if (stops[prevSpecifiedIndex].specified)
195 if (stops[i].offset < stops[prevSpecifiedIndex].offset)
196 stops[i].offset = stops[prevSpecifiedIndex].offset;
200 ASSERT(stops[0].specified && stops[numStops - 1].specified);
202 // If any color-stop still does not have a position, then, for each run of adjacent
203 // color-stops without positions, set their positions so that they are evenly spaced
204 // between the preceding and following color-stops with positions.
206 size_t unspecifiedRunStart = 0;
207 bool inUnspecifiedRun = false;
209 for (size_t i = 0; i < numStops; ++i) {
210 if (!stops[i].specified && !inUnspecifiedRun) {
211 unspecifiedRunStart = i;
212 inUnspecifiedRun = true;
213 } else if (stops[i].specified && inUnspecifiedRun) {
214 size_t unspecifiedRunEnd = i;
216 if (unspecifiedRunStart < unspecifiedRunEnd) {
217 float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
218 float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
219 float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
221 for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
222 stops[j].offset = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
225 inUnspecifiedRun = false;
230 // If the gradient is repeating, repeat the color stops.
231 // We can't just push this logic down into the platform-specific Gradient code,
232 // because we have to know the extent of the gradient, and possible move the end points.
233 if (m_repeating && numStops > 1) {
234 // If the difference in the positions of the first and last color-stops is 0,
235 // the gradient defines a solid-color image with the color of the last color-stop in the rule.
236 float gradientRange = stops[numStops - 1].offset - stops[0].offset;
237 if (!gradientRange) {
238 stops.first().offset = 0;
239 stops.first().color = stops.last().color;
245 // Radial gradients may need to extend further than the endpoints, because they have
246 // to repeat out to the corners of the box.
247 if (isRadialGradient()) {
248 if (!computedGradientLength) {
249 FloatSize gradientSize(gradientStart - gradientEnd);
250 gradientLength = gradientSize.diagonalLength();
253 if (maxLengthForRepeat > gradientLength)
254 maxExtent = maxLengthForRepeat / gradientLength;
257 size_t originalNumStops = numStops;
258 size_t originalFirstStopIndex = 0;
260 // Work backwards from the first, adding stops until we get one before 0.
261 float firstOffset = stops[0].offset;
262 if (firstOffset > 0) {
263 float currOffset = firstOffset;
264 size_t srcStopOrdinal = originalNumStops - 1;
267 GradientStop newStop = stops[originalFirstStopIndex + srcStopOrdinal];
268 newStop.offset = currOffset;
269 stops.prepend(newStop);
270 ++originalFirstStopIndex;
275 currOffset -= stops[originalFirstStopIndex + srcStopOrdinal].offset - stops[originalFirstStopIndex + srcStopOrdinal - 1].offset;
276 srcStopOrdinal = (srcStopOrdinal + originalNumStops - 1) % originalNumStops;
280 // Work forwards from the end, adding stops until we get one after 1.
281 float lastOffset = stops[stops.size() - 1].offset;
282 if (lastOffset < maxExtent) {
283 float currOffset = lastOffset;
284 size_t srcStopOrdinal = 0;
287 size_t srcStopIndex = originalFirstStopIndex + srcStopOrdinal;
288 GradientStop newStop = stops[srcStopIndex];
289 newStop.offset = currOffset;
290 stops.append(newStop);
291 if (currOffset > maxExtent)
293 if (srcStopOrdinal < originalNumStops - 1)
294 currOffset += stops[srcStopIndex + 1].offset - stops[srcStopIndex].offset;
295 srcStopOrdinal = (srcStopOrdinal + 1) % originalNumStops;
301 numStops = stops.size();
303 // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and adjusting the stops.
304 if (numStops > 1 && (stops[0].offset < 0 || stops[numStops - 1].offset > 1)) {
305 if (isLinearGradient()) {
306 float firstOffset = stops[0].offset;
307 float lastOffset = stops[numStops - 1].offset;
308 float scale = lastOffset - firstOffset;
310 for (size_t i = 0; i < numStops; ++i)
311 stops[i].offset = (stops[i].offset - firstOffset) / scale;
313 FloatPoint p0 = gradient->p0();
314 FloatPoint p1 = gradient->p1();
315 gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
316 gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
317 } else if (isRadialGradient()) {
318 // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
319 float firstOffset = 0;
320 float lastOffset = stops[numStops - 1].offset;
321 float scale = lastOffset - firstOffset;
323 // Reset points below 0 to the first visible color.
324 size_t firstZeroOrGreaterIndex = numStops;
325 for (size_t i = 0; i < numStops; ++i) {
326 if (stops[i].offset >= 0) {
327 firstZeroOrGreaterIndex = i;
332 if (firstZeroOrGreaterIndex > 0) {
333 if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
334 float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
335 float nextOffset = stops[firstZeroOrGreaterIndex].offset;
337 float interStopProportion = -prevOffset / (nextOffset - prevOffset);
338 Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
340 // Clamp the positions to 0 and set the color.
341 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
343 stops[i].color = blendedColor;
346 // All stops are below 0; just clamp them.
347 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
352 for (size_t i = 0; i < numStops; ++i)
353 stops[i].offset /= scale;
355 gradient->setStartRadius(gradient->startRadius() * scale);
356 gradient->setEndRadius(gradient->endRadius() * scale);
360 for (unsigned i = 0; i < numStops; i++)
361 gradient->addColorStop(stops[i].offset, stops[i].color);
363 gradient->setStopsSorted(true);
366 static float positionFromValue(CSSPrimitiveValue* value, RenderStyle* style, RenderStyle* rootStyle, const IntSize& size, bool isHorizontal)
368 float zoomFactor = style->effectiveZoom();
370 switch (value->primitiveType()) {
371 case CSSPrimitiveValue::CSS_NUMBER:
372 return value->getFloatValue() * zoomFactor;
374 case CSSPrimitiveValue::CSS_PERCENTAGE:
375 return value->getFloatValue() / 100.f * (isHorizontal ? size.width() : size.height());
377 case CSSPrimitiveValue::CSS_IDENT:
378 switch (value->getIdent()) {
380 ASSERT(!isHorizontal);
383 ASSERT(isHorizontal);
386 ASSERT(!isHorizontal);
387 return size.height();
389 ASSERT(isHorizontal);
394 return value->computeLength<float>(style, rootStyle, zoomFactor);
398 FloatPoint CSSGradientValue::computeEndPoint(CSSPrimitiveValue* first, CSSPrimitiveValue* second, RenderStyle* style, RenderStyle* rootStyle, const IntSize& size)
403 result.setX(positionFromValue(first, style, rootStyle, size, true));
406 result.setY(positionFromValue(second, style, rootStyle, size, false));
411 bool CSSGradientValue::isCacheable() const
413 for (size_t i = 0; i < m_stops.size(); ++i) {
414 const CSSGradientColorStop& stop = m_stops[i];
415 if (!stop.m_position)
418 unsigned short unitType = stop.m_position->primitiveType();
419 if (unitType == CSSPrimitiveValue::CSS_EMS || unitType == CSSPrimitiveValue::CSS_EXS || unitType == CSSPrimitiveValue::CSS_REMS)
426 String CSSLinearGradientValue::cssText() const
429 if (m_deprecatedType) {
430 result = "-webkit-gradient(linear, ";
431 result += m_firstX->cssText() + " ";
432 result += m_firstY->cssText() + ", ";
433 result += m_secondX->cssText() + " ";
434 result += m_secondY->cssText();
436 for (unsigned i = 0; i < m_stops.size(); i++) {
437 const CSSGradientColorStop& stop = m_stops[i];
439 if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
440 result += "from(" + stop.m_color->cssText() + ")";
441 else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
442 result += "to(" + stop.m_color->cssText() + ")";
444 result += "color-stop(" + String::number(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + stop.m_color->cssText() + ")";
447 result = m_repeating ? "-webkit-repeating-linear-gradient(" : "-webkit-linear-gradient(";
449 result += m_angle->cssText();
451 if (m_firstX && m_firstY)
452 result += m_firstX->cssText() + " " + m_firstY->cssText();
453 else if (m_firstX || m_firstY) {
455 result += m_firstX->cssText();
458 result += m_firstY->cssText();
462 for (unsigned i = 0; i < m_stops.size(); i++) {
463 const CSSGradientColorStop& stop = m_stops[i];
465 result += stop.m_color->cssText();
467 result += " " + stop.m_position->cssText();
475 // Compute the endpoints so that a gradient of the given angle covers a box of the given size.
476 static void endPointsFromAngle(float angleDeg, const IntSize& size, FloatPoint& firstPoint, FloatPoint& secondPoint)
478 angleDeg = fmodf(angleDeg, 360);
483 firstPoint.set(0, 0);
484 secondPoint.set(size.width(), 0);
488 if (angleDeg == 90) {
489 firstPoint.set(0, size.height());
490 secondPoint.set(0, 0);
494 if (angleDeg == 180) {
495 firstPoint.set(size.width(), 0);
496 secondPoint.set(0, 0);
500 if (angleDeg == 270) {
501 firstPoint.set(0, 0);
502 secondPoint.set(0, size.height());
506 float slope = tan(deg2rad(angleDeg));
508 // We find the endpoint by computing the intersection of the line formed by the slope,
509 // and a line perpendicular to it that intersects the corner.
510 float perpendicularSlope = -1 / slope;
512 // Compute start corner relative to center.
513 float halfHeight = size.height() / 2;
514 float halfWidth = size.width() / 2;
515 FloatPoint endCorner;
517 endCorner.set(halfWidth, halfHeight);
518 else if (angleDeg < 180)
519 endCorner.set(-halfWidth, halfHeight);
520 else if (angleDeg < 270)
521 endCorner.set(-halfWidth, -halfHeight);
523 endCorner.set(halfWidth, -halfHeight);
525 // Compute c (of y = mx + c) using the corner point.
526 float c = endCorner.y() - perpendicularSlope * endCorner.x();
527 float endX = c / (slope - perpendicularSlope);
528 float endY = perpendicularSlope * endX + c;
530 // We computed the end point, so set the second point, flipping the Y to account for angles going anticlockwise.
531 secondPoint.set(halfWidth + endX, size.height() - (halfHeight + endY));
532 // Reflect around the center for the start point.
533 firstPoint.set(size.width() - secondPoint.x(), size.height() - secondPoint.y());
536 PassRefPtr<Gradient> CSSLinearGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
538 ASSERT(!size.isEmpty());
540 RenderStyle* rootStyle = renderer->document()->documentElement()->renderStyle();
542 FloatPoint firstPoint;
543 FloatPoint secondPoint;
545 float angle = m_angle->getFloatValue(CSSPrimitiveValue::CSS_DEG);
546 endPointsFromAngle(angle, size, firstPoint, secondPoint);
548 firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), renderer->style(), rootStyle, size);
550 if (m_secondX || m_secondY)
551 secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), renderer->style(), rootStyle, size);
554 secondPoint.setX(size.width() - firstPoint.x());
556 secondPoint.setY(size.height() - firstPoint.y());
560 RefPtr<Gradient> gradient = Gradient::create(firstPoint, secondPoint);
562 // Now add the stops.
563 addStops(gradient.get(), renderer, rootStyle, 1);
565 return gradient.release();
568 String CSSRadialGradientValue::cssText() const
572 if (m_deprecatedType) {
573 result = "-webkit-gradient(radial, ";
575 result += m_firstX->cssText() + " ";
576 result += m_firstY->cssText() + ", ";
577 result += m_firstRadius->cssText() + ", ";
578 result += m_secondX->cssText() + " ";
579 result += m_secondY->cssText();
581 result += m_secondRadius->cssText();
584 for (unsigned i = 0; i < m_stops.size(); i++) {
585 const CSSGradientColorStop& stop = m_stops[i];
587 if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
588 result += "from(" + stop.m_color->cssText() + ")";
589 else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
590 result += "to(" + stop.m_color->cssText() + ")";
592 result += "color-stop(" + String::number(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + stop.m_color->cssText() + ")";
596 result = m_repeating ? "-webkit-repeating-radial-gradient(" : "-webkit-radial-gradient(";
597 if (m_firstX && m_firstY) {
598 result += m_firstX->cssText() + " " + m_firstY->cssText();
600 result += m_firstX->cssText();
602 result += m_firstY->cssText();
607 if (m_shape || m_sizingBehavior) {
610 result += m_shape->cssText() + " ";
612 result += "ellipse ";
614 if (m_sizingBehavior)
615 result += m_sizingBehavior->cssText();
619 } else if (m_endHorizontalSize && m_endVerticalSize) {
621 result += m_endHorizontalSize->cssText() + " " + m_endVerticalSize->cssText();
624 for (unsigned i = 0; i < m_stops.size(); i++) {
625 const CSSGradientColorStop& stop = m_stops[i];
627 result += stop.m_color->cssText();
629 result += " " + stop.m_position->cssText();
637 float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, RenderStyle* style, RenderStyle* rootStyle, float* widthOrHeight)
639 float zoomFactor = style->effectiveZoom();
642 if (radius->primitiveType() == CSSPrimitiveValue::CSS_NUMBER) // Can the radius be a percentage?
643 result = radius->getFloatValue() * zoomFactor;
644 else if (widthOrHeight && radius->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
645 result = *widthOrHeight * radius->getFloatValue() / 100;
647 result = radius->computeLength<float>(style, rootStyle, zoomFactor);
652 static float distanceToClosestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
655 float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
657 FloatPoint topRight(size.width(), 0);
658 float topRightDistance = FloatSize(p - topRight).diagonalLength();
660 FloatPoint bottomLeft(0, size.height());
661 float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
663 FloatPoint bottomRight(size.width(), size.height());
664 float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
667 float minDistance = topLeftDistance;
668 if (topRightDistance < minDistance) {
669 minDistance = topRightDistance;
673 if (bottomLeftDistance < minDistance) {
674 minDistance = bottomLeftDistance;
678 if (bottomRightDistance < minDistance) {
679 minDistance = bottomRightDistance;
680 corner = bottomRight;
685 static float distanceToFarthestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
688 float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
690 FloatPoint topRight(size.width(), 0);
691 float topRightDistance = FloatSize(p - topRight).diagonalLength();
693 FloatPoint bottomLeft(0, size.height());
694 float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
696 FloatPoint bottomRight(size.width(), size.height());
697 float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
700 float maxDistance = topLeftDistance;
701 if (topRightDistance > maxDistance) {
702 maxDistance = topRightDistance;
706 if (bottomLeftDistance > maxDistance) {
707 maxDistance = bottomLeftDistance;
711 if (bottomRightDistance > maxDistance) {
712 maxDistance = bottomRightDistance;
713 corner = bottomRight;
718 // Compute horizontal radius of ellipse with center at 0,0 which passes through p, and has
719 // width/height given by aspectRatio.
720 static inline float horizontalEllipseRadius(const FloatSize& p, float aspectRatio)
722 // x^2/a^2 + y^2/b^2 = 1
723 // a/b = aspectRatio, b = a/aspectRatio
724 // a = sqrt(x^2 + y^2/(1/r^2))
725 return sqrtf(p.width() * p.width() + (p.height() * p.height()) / (1 / (aspectRatio * aspectRatio)));
728 // FIXME: share code with the linear version
729 PassRefPtr<Gradient> CSSRadialGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
731 ASSERT(!size.isEmpty());
733 RenderStyle* rootStyle = renderer->document()->documentElement()->renderStyle();
735 FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), renderer->style(), rootStyle, size);
737 firstPoint.setX(size.width() / 2);
739 firstPoint.setY(size.height() / 2);
741 FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), renderer->style(), rootStyle, size);
743 secondPoint.setX(size.width() / 2);
745 secondPoint.setY(size.height() / 2);
747 float firstRadius = 0;
749 firstRadius = resolveRadius(m_firstRadius.get(), renderer->style(), rootStyle);
751 float secondRadius = 0;
752 float aspectRatio = 1; // width / height.
754 secondRadius = resolveRadius(m_secondRadius.get(), renderer->style(), rootStyle);
755 else if (m_endHorizontalSize || m_endVerticalSize) {
756 float width = size.width();
757 float height = size.height();
758 secondRadius = resolveRadius(m_endHorizontalSize.get(), renderer->style(), rootStyle, &width);
759 aspectRatio = secondRadius / resolveRadius(m_endVerticalSize.get(), renderer->style(), rootStyle, &height);
761 enum GradientShape { Circle, Ellipse };
762 GradientShape shape = Ellipse;
763 if (m_shape && m_shape->primitiveType() == CSSPrimitiveValue::CSS_IDENT && m_shape->getIdent() == CSSValueCircle)
766 enum GradientFill { ClosestSide, ClosestCorner, FarthestSide, FarthestCorner };
767 GradientFill fill = FarthestCorner;
769 if (m_sizingBehavior && m_sizingBehavior->primitiveType() == CSSPrimitiveValue::CSS_IDENT) {
770 switch (m_sizingBehavior->getIdent()) {
771 case CSSValueContain:
772 case CSSValueClosestSide:
775 case CSSValueClosestCorner:
776 fill = ClosestCorner;
778 case CSSValueFarthestSide:
782 case CSSValueFarthestCorner:
783 fill = FarthestCorner;
788 // Now compute the end radii based on the second point, shape and fill.
793 float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
794 float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
795 if (shape == Circle) {
796 float smaller = min(xDist, yDist);
800 secondRadius = xDist;
801 aspectRatio = xDist / yDist;
805 float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
806 float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
807 if (shape == Circle) {
808 float larger = max(xDist, yDist);
812 secondRadius = xDist;
813 aspectRatio = xDist / yDist;
816 case ClosestCorner: {
818 float distance = distanceToClosestCorner(secondPoint, size, corner);
820 secondRadius = distance;
822 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
823 // that it would if closest-side or farthest-side were specified, as appropriate.
824 float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
825 float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
827 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
828 aspectRatio = xDist / yDist;
833 case FarthestCorner: {
835 float distance = distanceToFarthestCorner(secondPoint, size, corner);
837 secondRadius = distance;
839 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
840 // that it would if closest-side or farthest-side were specified, as appropriate.
841 float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
842 float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
844 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
845 aspectRatio = xDist / yDist;
852 RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius, aspectRatio);
854 // addStops() only uses maxExtent for repeating gradients.
858 maxExtent = distanceToFarthestCorner(secondPoint, size, corner);
861 // Now add the stops.
862 addStops(gradient.get(), renderer, rootStyle, maxExtent);
864 return gradient.release();
867 } // namespace WebCore