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 "CSSCalculationValue.h"
30 #include "CSSValueKeywords.h"
31 #include "GeneratorGeneratedImage.h"
35 #include "IntSizeHash.h"
36 #include "NodeRenderStyle.h"
37 #include "PlatformString.h"
38 #include "RenderObject.h"
39 #include "StyleResolver.h"
45 PassRefPtr<Image> CSSGradientValue::image(RenderObject* renderer, const IntSize& size)
50 bool cacheable = isCacheable();
52 if (!clients().contains(renderer))
55 // Need to look up our size. Create a string of width*height to use as a hash key.
56 Image* result = getImage(renderer, size);
61 // We need to create an image.
62 RefPtr<Gradient> gradient;
64 if (isLinearGradient())
65 gradient = static_cast<CSSLinearGradientValue*>(this)->createGradient(renderer, size);
67 ASSERT(isRadialGradient());
68 gradient = static_cast<CSSRadialGradientValue*>(this)->createGradient(renderer, size);
71 RefPtr<Image> newImage = GeneratorGeneratedImage::create(gradient, size);
73 putImage(size, newImage);
75 return newImage.release();
78 // Should only ever be called for deprecated gradients.
79 static inline bool compareStops(const CSSGradientColorStop& a, const CSSGradientColorStop& b)
81 double aVal = a.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER);
82 double bVal = b.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER);
87 void CSSGradientValue::sortStopsIfNeeded()
89 ASSERT(m_deprecatedType);
92 std::stable_sort(m_stops.begin(), m_stops.end(), compareStops);
108 PassRefPtr<CSSGradientValue> CSSGradientValue::gradientWithStylesResolved(StyleResolver* styleResolver)
110 bool derived = false;
111 for (unsigned i = 0; i < m_stops.size(); i++)
112 if (styleResolver->colorFromPrimitiveValueIsDerivedFromElement(m_stops[i].m_color.get())) {
113 m_stops[i].m_colorIsDerivedFromElement = true;
118 RefPtr<CSSGradientValue> result;
121 else if (isLinearGradient())
122 result = static_cast<CSSLinearGradientValue*>(this)->clone();
123 else if (isRadialGradient())
124 result = static_cast<CSSRadialGradientValue*>(this)->clone();
126 ASSERT_NOT_REACHED();
130 for (unsigned i = 0; i < result->m_stops.size(); i++)
131 result->m_stops[i].m_resolvedColor = styleResolver->colorFromPrimitiveValue(result->m_stops[i].m_color.get());
133 return result.release();
136 void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, RenderStyle* rootStyle, float maxLengthForRepeat)
138 RenderStyle* style = renderer->style();
140 if (m_deprecatedType) {
143 for (unsigned i = 0; i < m_stops.size(); i++) {
144 const CSSGradientColorStop& stop = m_stops[i];
147 if (stop.m_position->isPercentage())
148 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
150 offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_NUMBER);
152 gradient->addColorStop(offset, stop.m_resolvedColor);
155 // The back end already sorted the stops.
156 gradient->setStopsSorted(true);
160 size_t numStops = m_stops.size();
162 Vector<GradientStop> stops(numStops);
164 float gradientLength = 0;
165 bool computedGradientLength = false;
167 FloatPoint gradientStart = gradient->p0();
168 FloatPoint gradientEnd;
169 if (isLinearGradient())
170 gradientEnd = gradient->p1();
171 else if (isRadialGradient())
172 gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0);
174 for (size_t i = 0; i < numStops; ++i) {
175 const CSSGradientColorStop& stop = m_stops[i];
177 stops[i].color = stop.m_resolvedColor;
179 if (stop.m_position) {
180 if (stop.m_position->isPercentage())
181 stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
182 else if (stop.m_position->isLength() || stop.m_position->isCalculatedPercentageWithLength()) {
183 if (!computedGradientLength) {
184 FloatSize gradientSize(gradientStart - gradientEnd);
185 gradientLength = gradientSize.diagonalLength();
188 if (stop.m_position->isLength())
189 length = stop.m_position->computeLength<float>(style, rootStyle, style->effectiveZoom());
191 length = stop.m_position->cssCalcValue()->toCalcValue(style, rootStyle, style->effectiveZoom())->evaluate(gradientLength);
192 stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0;
194 ASSERT_NOT_REACHED();
197 stops[i].specified = true;
199 // If the first color-stop does not have a position, its position defaults to 0%.
200 // If the last color-stop does not have a position, its position defaults to 100%.
203 stops[i].specified = true;
204 } else if (numStops > 1 && i == numStops - 1) {
206 stops[i].specified = true;
210 // If a color-stop has a position that is less than the specified position of any
211 // color-stop before it in the list, its position is changed to be equal to the
212 // largest specified position of any color-stop before it.
213 if (stops[i].specified && i > 0) {
214 size_t prevSpecifiedIndex;
215 for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
216 if (stops[prevSpecifiedIndex].specified)
220 if (stops[i].offset < stops[prevSpecifiedIndex].offset)
221 stops[i].offset = stops[prevSpecifiedIndex].offset;
225 ASSERT(stops[0].specified && stops[numStops - 1].specified);
227 // If any color-stop still does not have a position, then, for each run of adjacent
228 // color-stops without positions, set their positions so that they are evenly spaced
229 // between the preceding and following color-stops with positions.
231 size_t unspecifiedRunStart = 0;
232 bool inUnspecifiedRun = false;
234 for (size_t i = 0; i < numStops; ++i) {
235 if (!stops[i].specified && !inUnspecifiedRun) {
236 unspecifiedRunStart = i;
237 inUnspecifiedRun = true;
238 } else if (stops[i].specified && inUnspecifiedRun) {
239 size_t unspecifiedRunEnd = i;
241 if (unspecifiedRunStart < unspecifiedRunEnd) {
242 float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
243 float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
244 float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
246 for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
247 stops[j].offset = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
250 inUnspecifiedRun = false;
255 // If the gradient is repeating, repeat the color stops.
256 // We can't just push this logic down into the platform-specific Gradient code,
257 // because we have to know the extent of the gradient, and possible move the end points.
258 if (m_repeating && numStops > 1) {
259 // If the difference in the positions of the first and last color-stops is 0,
260 // the gradient defines a solid-color image with the color of the last color-stop in the rule.
261 float gradientRange = stops[numStops - 1].offset - stops[0].offset;
262 if (!gradientRange) {
263 stops.first().offset = 0;
264 stops.first().color = stops.last().color;
270 // Radial gradients may need to extend further than the endpoints, because they have
271 // to repeat out to the corners of the box.
272 if (isRadialGradient()) {
273 if (!computedGradientLength) {
274 FloatSize gradientSize(gradientStart - gradientEnd);
275 gradientLength = gradientSize.diagonalLength();
278 if (maxLengthForRepeat > gradientLength)
279 maxExtent = maxLengthForRepeat / gradientLength;
282 size_t originalNumStops = numStops;
283 size_t originalFirstStopIndex = 0;
285 // Work backwards from the first, adding stops until we get one before 0.
286 float firstOffset = stops[0].offset;
287 if (firstOffset > 0) {
288 float currOffset = firstOffset;
289 size_t srcStopOrdinal = originalNumStops - 1;
292 GradientStop newStop = stops[originalFirstStopIndex + srcStopOrdinal];
293 newStop.offset = currOffset;
294 stops.prepend(newStop);
295 ++originalFirstStopIndex;
300 currOffset -= stops[originalFirstStopIndex + srcStopOrdinal].offset - stops[originalFirstStopIndex + srcStopOrdinal - 1].offset;
301 srcStopOrdinal = (srcStopOrdinal + originalNumStops - 1) % originalNumStops;
305 // Work forwards from the end, adding stops until we get one after 1.
306 float lastOffset = stops[stops.size() - 1].offset;
307 if (lastOffset < maxExtent) {
308 float currOffset = lastOffset;
309 size_t srcStopOrdinal = 0;
312 size_t srcStopIndex = originalFirstStopIndex + srcStopOrdinal;
313 GradientStop newStop = stops[srcStopIndex];
314 newStop.offset = currOffset;
315 stops.append(newStop);
316 if (currOffset > maxExtent)
318 if (srcStopOrdinal < originalNumStops - 1)
319 currOffset += stops[srcStopIndex + 1].offset - stops[srcStopIndex].offset;
320 srcStopOrdinal = (srcStopOrdinal + 1) % originalNumStops;
326 numStops = stops.size();
328 // If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and adjusting the stops.
329 if (numStops > 1 && (stops[0].offset < 0 || stops[numStops - 1].offset > 1)) {
330 if (isLinearGradient()) {
331 float firstOffset = stops[0].offset;
332 float lastOffset = stops[numStops - 1].offset;
333 float scale = lastOffset - firstOffset;
335 for (size_t i = 0; i < numStops; ++i)
336 stops[i].offset = (stops[i].offset - firstOffset) / scale;
338 FloatPoint p0 = gradient->p0();
339 FloatPoint p1 = gradient->p1();
340 gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
341 gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
342 } else if (isRadialGradient()) {
343 // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
344 float firstOffset = 0;
345 float lastOffset = stops[numStops - 1].offset;
346 float scale = lastOffset - firstOffset;
348 // Reset points below 0 to the first visible color.
349 size_t firstZeroOrGreaterIndex = numStops;
350 for (size_t i = 0; i < numStops; ++i) {
351 if (stops[i].offset >= 0) {
352 firstZeroOrGreaterIndex = i;
357 if (firstZeroOrGreaterIndex > 0) {
358 if (firstZeroOrGreaterIndex < numStops && stops[firstZeroOrGreaterIndex].offset > 0) {
359 float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
360 float nextOffset = stops[firstZeroOrGreaterIndex].offset;
362 float interStopProportion = -prevOffset / (nextOffset - prevOffset);
363 // FIXME: when we interpolate gradients using premultiplied colors, this should do premultiplication.
364 Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
366 // Clamp the positions to 0 and set the color.
367 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
369 stops[i].color = blendedColor;
372 // All stops are below 0; just clamp them.
373 for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i)
378 for (size_t i = 0; i < numStops; ++i)
379 stops[i].offset /= scale;
381 gradient->setStartRadius(gradient->startRadius() * scale);
382 gradient->setEndRadius(gradient->endRadius() * scale);
386 for (unsigned i = 0; i < numStops; i++)
387 gradient->addColorStop(stops[i].offset, stops[i].color);
389 gradient->setStopsSorted(true);
392 static float positionFromValue(CSSPrimitiveValue* value, RenderStyle* style, RenderStyle* rootStyle, const IntSize& size, bool isHorizontal)
394 float zoomFactor = style->effectiveZoom();
396 if (value->isNumber())
397 return value->getFloatValue() * zoomFactor;
399 int edgeDistance = isHorizontal ? size.width() : size.height();
400 if (value->isPercentage())
401 return value->getFloatValue() / 100.f * edgeDistance;
403 if (value->isCalculatedPercentageWithLength())
404 return value->cssCalcValue()->toCalcValue(style, rootStyle, style->effectiveZoom())->evaluate(edgeDistance);
406 switch (value->getIdent()) {
408 ASSERT(!isHorizontal);
411 ASSERT(isHorizontal);
414 ASSERT(!isHorizontal);
415 return size.height();
417 ASSERT(isHorizontal);
421 return value->computeLength<float>(style, rootStyle, zoomFactor);
424 FloatPoint CSSGradientValue::computeEndPoint(CSSPrimitiveValue* first, CSSPrimitiveValue* second, RenderStyle* style, RenderStyle* rootStyle, const IntSize& size)
429 result.setX(positionFromValue(first, style, rootStyle, size, true));
432 result.setY(positionFromValue(second, style, rootStyle, size, false));
437 bool CSSGradientValue::isCacheable() const
439 for (size_t i = 0; i < m_stops.size(); ++i) {
440 const CSSGradientColorStop& stop = m_stops[i];
442 if (stop.m_colorIsDerivedFromElement)
445 if (!stop.m_position)
448 if (stop.m_position->isFontRelativeLength())
455 String CSSLinearGradientValue::customCssText() const
458 if (m_deprecatedType) {
459 result = "-webkit-gradient(linear, ";
460 result += m_firstX->cssText() + " ";
461 result += m_firstY->cssText() + ", ";
462 result += m_secondX->cssText() + " ";
463 result += m_secondY->cssText();
465 for (unsigned i = 0; i < m_stops.size(); i++) {
466 const CSSGradientColorStop& stop = m_stops[i];
468 if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
469 result += "from(" + stop.m_color->cssText() + ")";
470 else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
471 result += "to(" + stop.m_color->cssText() + ")";
473 result += "color-stop(" + String::number(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + stop.m_color->cssText() + ")";
476 result = m_repeating ? "-webkit-repeating-linear-gradient(" : "-webkit-linear-gradient(";
478 result += m_angle->cssText();
480 if (m_firstX && m_firstY)
481 result += m_firstX->cssText() + " " + m_firstY->cssText();
482 else if (m_firstX || m_firstY) {
484 result += m_firstX->cssText();
487 result += m_firstY->cssText();
491 for (unsigned i = 0; i < m_stops.size(); i++) {
492 const CSSGradientColorStop& stop = m_stops[i];
494 result += stop.m_color->cssText();
496 result += " " + stop.m_position->cssText();
504 // Compute the endpoints so that a gradient of the given angle covers a box of the given size.
505 static void endPointsFromAngle(float angleDeg, const IntSize& size, FloatPoint& firstPoint, FloatPoint& secondPoint)
507 angleDeg = fmodf(angleDeg, 360);
512 firstPoint.set(0, 0);
513 secondPoint.set(size.width(), 0);
517 if (angleDeg == 90) {
518 firstPoint.set(0, size.height());
519 secondPoint.set(0, 0);
523 if (angleDeg == 180) {
524 firstPoint.set(size.width(), 0);
525 secondPoint.set(0, 0);
529 if (angleDeg == 270) {
530 firstPoint.set(0, 0);
531 secondPoint.set(0, size.height());
535 float slope = tan(deg2rad(angleDeg));
537 // We find the endpoint by computing the intersection of the line formed by the slope,
538 // and a line perpendicular to it that intersects the corner.
539 float perpendicularSlope = -1 / slope;
541 // Compute start corner relative to center.
542 float halfHeight = size.height() / 2;
543 float halfWidth = size.width() / 2;
544 FloatPoint endCorner;
546 endCorner.set(halfWidth, halfHeight);
547 else if (angleDeg < 180)
548 endCorner.set(-halfWidth, halfHeight);
549 else if (angleDeg < 270)
550 endCorner.set(-halfWidth, -halfHeight);
552 endCorner.set(halfWidth, -halfHeight);
554 // Compute c (of y = mx + c) using the corner point.
555 float c = endCorner.y() - perpendicularSlope * endCorner.x();
556 float endX = c / (slope - perpendicularSlope);
557 float endY = perpendicularSlope * endX + c;
559 // We computed the end point, so set the second point, flipping the Y to account for angles going anticlockwise.
560 secondPoint.set(halfWidth + endX, size.height() - (halfHeight + endY));
561 // Reflect around the center for the start point.
562 firstPoint.set(size.width() - secondPoint.x(), size.height() - secondPoint.y());
565 PassRefPtr<Gradient> CSSLinearGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
567 ASSERT(!size.isEmpty());
569 RenderStyle* rootStyle = renderer->document()->documentElement()->renderStyle();
571 FloatPoint firstPoint;
572 FloatPoint secondPoint;
574 float angle = m_angle->getFloatValue(CSSPrimitiveValue::CSS_DEG);
575 endPointsFromAngle(angle, size, firstPoint, secondPoint);
577 firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), renderer->style(), rootStyle, size);
579 if (m_secondX || m_secondY)
580 secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), renderer->style(), rootStyle, size);
583 secondPoint.setX(size.width() - firstPoint.x());
585 secondPoint.setY(size.height() - firstPoint.y());
589 RefPtr<Gradient> gradient = Gradient::create(firstPoint, secondPoint);
591 // Now add the stops.
592 addStops(gradient.get(), renderer, rootStyle, 1);
594 return gradient.release();
597 String CSSRadialGradientValue::customCssText() const
601 if (m_deprecatedType) {
602 result = "-webkit-gradient(radial, ";
604 result += m_firstX->cssText() + " ";
605 result += m_firstY->cssText() + ", ";
606 result += m_firstRadius->cssText() + ", ";
607 result += m_secondX->cssText() + " ";
608 result += m_secondY->cssText();
610 result += m_secondRadius->cssText();
613 for (unsigned i = 0; i < m_stops.size(); i++) {
614 const CSSGradientColorStop& stop = m_stops[i];
616 if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
617 result += "from(" + stop.m_color->cssText() + ")";
618 else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
619 result += "to(" + stop.m_color->cssText() + ")";
621 result += "color-stop(" + String::number(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + stop.m_color->cssText() + ")";
625 result = m_repeating ? "-webkit-repeating-radial-gradient(" : "-webkit-radial-gradient(";
626 if (m_firstX && m_firstY) {
627 result += m_firstX->cssText() + " " + m_firstY->cssText();
629 result += m_firstX->cssText();
631 result += m_firstY->cssText();
636 if (m_shape || m_sizingBehavior) {
639 result += m_shape->cssText() + " ";
641 result += "ellipse ";
643 if (m_sizingBehavior)
644 result += m_sizingBehavior->cssText();
648 } else if (m_endHorizontalSize && m_endVerticalSize) {
650 result += m_endHorizontalSize->cssText() + " " + m_endVerticalSize->cssText();
653 for (unsigned i = 0; i < m_stops.size(); i++) {
654 const CSSGradientColorStop& stop = m_stops[i];
656 result += stop.m_color->cssText();
658 result += " " + stop.m_position->cssText();
666 float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, RenderStyle* style, RenderStyle* rootStyle, float* widthOrHeight)
668 float zoomFactor = style->effectiveZoom();
671 if (radius->isNumber()) // Can the radius be a percentage?
672 result = radius->getFloatValue() * zoomFactor;
673 else if (widthOrHeight && radius->isPercentage())
674 result = *widthOrHeight * radius->getFloatValue() / 100;
676 result = radius->computeLength<float>(style, rootStyle, zoomFactor);
681 static float distanceToClosestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
684 float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
686 FloatPoint topRight(size.width(), 0);
687 float topRightDistance = FloatSize(p - topRight).diagonalLength();
689 FloatPoint bottomLeft(0, size.height());
690 float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
692 FloatPoint bottomRight(size.width(), size.height());
693 float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
696 float minDistance = topLeftDistance;
697 if (topRightDistance < minDistance) {
698 minDistance = topRightDistance;
702 if (bottomLeftDistance < minDistance) {
703 minDistance = bottomLeftDistance;
707 if (bottomRightDistance < minDistance) {
708 minDistance = bottomRightDistance;
709 corner = bottomRight;
714 static float distanceToFarthestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
717 float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
719 FloatPoint topRight(size.width(), 0);
720 float topRightDistance = FloatSize(p - topRight).diagonalLength();
722 FloatPoint bottomLeft(0, size.height());
723 float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
725 FloatPoint bottomRight(size.width(), size.height());
726 float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
729 float maxDistance = topLeftDistance;
730 if (topRightDistance > maxDistance) {
731 maxDistance = topRightDistance;
735 if (bottomLeftDistance > maxDistance) {
736 maxDistance = bottomLeftDistance;
740 if (bottomRightDistance > maxDistance) {
741 maxDistance = bottomRightDistance;
742 corner = bottomRight;
747 // Compute horizontal radius of ellipse with center at 0,0 which passes through p, and has
748 // width/height given by aspectRatio.
749 static inline float horizontalEllipseRadius(const FloatSize& p, float aspectRatio)
751 // x^2/a^2 + y^2/b^2 = 1
752 // a/b = aspectRatio, b = a/aspectRatio
753 // a = sqrt(x^2 + y^2/(1/r^2))
754 return sqrtf(p.width() * p.width() + (p.height() * p.height()) / (1 / (aspectRatio * aspectRatio)));
757 // FIXME: share code with the linear version
758 PassRefPtr<Gradient> CSSRadialGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
760 ASSERT(!size.isEmpty());
762 RenderStyle* rootStyle = renderer->document()->documentElement()->renderStyle();
764 FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), renderer->style(), rootStyle, size);
766 firstPoint.setX(size.width() / 2);
768 firstPoint.setY(size.height() / 2);
770 FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), renderer->style(), rootStyle, size);
772 secondPoint.setX(size.width() / 2);
774 secondPoint.setY(size.height() / 2);
776 float firstRadius = 0;
778 firstRadius = resolveRadius(m_firstRadius.get(), renderer->style(), rootStyle);
780 float secondRadius = 0;
781 float aspectRatio = 1; // width / height.
783 secondRadius = resolveRadius(m_secondRadius.get(), renderer->style(), rootStyle);
784 else if (m_endHorizontalSize || m_endVerticalSize) {
785 float width = size.width();
786 float height = size.height();
787 secondRadius = resolveRadius(m_endHorizontalSize.get(), renderer->style(), rootStyle, &width);
788 aspectRatio = secondRadius / resolveRadius(m_endVerticalSize.get(), renderer->style(), rootStyle, &height);
790 enum GradientShape { Circle, Ellipse };
791 GradientShape shape = Ellipse;
792 if (m_shape && m_shape->getIdent() == CSSValueCircle)
795 enum GradientFill { ClosestSide, ClosestCorner, FarthestSide, FarthestCorner };
796 GradientFill fill = FarthestCorner;
798 switch (m_sizingBehavior ? m_sizingBehavior->getIdent() : 0) {
799 case CSSValueContain:
800 case CSSValueClosestSide:
803 case CSSValueClosestCorner:
804 fill = ClosestCorner;
806 case CSSValueFarthestSide:
810 case CSSValueFarthestCorner:
811 fill = FarthestCorner;
815 // Now compute the end radii based on the second point, shape and fill.
820 float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
821 float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
822 if (shape == Circle) {
823 float smaller = min(xDist, yDist);
827 secondRadius = xDist;
828 aspectRatio = xDist / yDist;
832 float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
833 float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
834 if (shape == Circle) {
835 float larger = max(xDist, yDist);
839 secondRadius = xDist;
840 aspectRatio = xDist / yDist;
843 case ClosestCorner: {
845 float distance = distanceToClosestCorner(secondPoint, size, corner);
847 secondRadius = distance;
849 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
850 // that it would if closest-side or farthest-side were specified, as appropriate.
851 float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
852 float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
854 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
855 aspectRatio = xDist / yDist;
860 case FarthestCorner: {
862 float distance = distanceToFarthestCorner(secondPoint, size, corner);
864 secondRadius = distance;
866 // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
867 // that it would if closest-side or farthest-side were specified, as appropriate.
868 float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
869 float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
871 secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
872 aspectRatio = xDist / yDist;
879 RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius, aspectRatio);
881 // addStops() only uses maxExtent for repeating gradients.
885 maxExtent = distanceToFarthestCorner(secondPoint, size, corner);
888 // Now add the stops.
889 addStops(gradient.get(), renderer, rootStyle, maxExtent);
891 return gradient.release();
894 } // namespace WebCore