[SCM] WebKit Debian packaging branch, debian/experimental, updated. debian/1.3.8-1-1049-g2e11a8e
simon.fraser at apple.com
simon.fraser at apple.com
Fri Jan 21 14:52:39 UTC 2011
The following commit has been merged in the debian/experimental branch:
commit a21d8b1c03e096172b16e3f723d8e24e4dddbba8
Author: simon.fraser at apple.com <simon.fraser at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date: Mon Jan 3 19:36:41 2011 +0000
2011-01-03 Simon Fraser <simon.fraser at apple.com>
Reviewed by David Hyatt.
Implement -webkit-linear-gradient and -webkit-radial-gradient
https://bugs.webkit.org/show_bug.cgi?id=28152
Add support for -webkit-radial-gradients.
Tests: fast/gradients/css3-radial-gradients.html
fast/gradients/css3-radial-gradients2.html
* css/CSSGradientValue.cpp:
(WebCore::blend): Used to blend colors, which is necessary when truncating
the start of radial gradients.
(WebCore::GradientStop): Small struct to aid gradient stop processing.
(WebCore::CSSGradientValue::addStops): Rename 'positions' to 'stops', and store
the color so that we can blend it when truncating radial gradients. Changed
to handle both linear and radial gradients.
(WebCore::CSSRadialGradientValue::cssText): Update to follow the spec.
(WebCore::CSSRadialGradientValue::resolveRadius): Stylistic change.
(WebCore::distanceToClosestCorner): New utility method.
(WebCore::distanceToFarthestCorner): Ditto.
(WebCore::CSSRadialGradientValue::createGradient): New logic to deal with shape and
fill rules.
* css/CSSGradientValue.h:
(WebCore::CSSGradientValue::isLinearGradient): Color-stop processing needs to know what
kind of gradient it's dealing with, so add new isFooGradient methods.
(WebCore::CSSGradientValue::isRadialGradient): Ditto.
(WebCore::CSSLinearGradientValue::isLinearGradient):
(WebCore::CSSRadialGradientValue::setShape): New setters for shape, size etc.
(WebCore::CSSRadialGradientValue::setSizingBehavior):
(WebCore::CSSRadialGradientValue::setEndHorizontalSize):
(WebCore::CSSRadialGradientValue::setEndVerticalSize):
(WebCore::CSSRadialGradientValue::isRadialGradient):
* css/CSSParser.h:
* css/CSSParser.h: Pass a CSSParserValueList* into parseFillPositionXY() and parseFillPosition()
so we can use parseFillPosition() when parsing a gradient function.
* css/CSSParser.cpp:
(WebCore::CSSParser::parseFillPositionXY): Pass in a CSSParserValueList*
(WebCore::CSSParser::parseFillPosition): Ditto
(WebCore::CSSParser::parseFillProperty): Pass m_valueList to parseFillPosition.
(WebCore::CSSParser::parseTransformOriginShorthand): Ditto
(WebCore::CSSParser::parseRadialGradient): Flesh out radial gradient parsing.
(WebCore::CSSParser::parseTransformOrigin): Pass m_valueList to parseFillPosition.
(WebCore::CSSParser::parsePerspectiveOrigin): Ditto
* css/CSSValueKeywords.in: New keywords for radial gradient shape and fill behaviors.
* platform/graphics/Gradient.cpp:
(WebCore::Gradient::Gradient): Pass aspect ratio for elliptical gradients.
* platform/graphics/Gradient.h: Add aspect ratio for elliptical gradients.
(WebCore::Gradient::create):
(WebCore::Gradient::startRadius): Expose radii.
(WebCore::Gradient::endRadius):
(WebCore::Gradient::setStartRadius): Setters are required for when we need to scale color stops.
(WebCore::Gradient::setEndRadius):
(WebCore::Gradient::aspectRatio):
* platform/graphics/cg/GradientCG.cpp:
(WebCore::Gradient::paint): For elliptical gradients, scale the CTM.
* platform/graphics/wince/GraphicsContextWinCE.cpp:
(WebCore::GraphicsContext::fillRect): Use start/endRadius() rather than r0() and r1().
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@74915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 5510929..291669c 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -4,6 +4,27 @@
Implement -webkit-linear-gradient and -webkit-radial-gradient
https://bugs.webkit.org/show_bug.cgi?id=28152
+
+ Enhance gradient parsing test for radial gradients, and add new tests
+ for various radial gradients.
+
+ * fast/gradients/css3-gradient-parsing-expected.txt:
+ * fast/gradients/css3-gradient-parsing.html:
+ * fast/gradients/css3-radial-gradients-expected.txt: Added.
+ * fast/gradients/css3-radial-gradients.html: Added.
+ * fast/gradients/css3-radial-gradients2-expected.txt: Added.
+ * fast/gradients/css3-radial-gradients2.html: Added.
+ * platform/mac/fast/gradients/css3-radial-gradients-expected.checksum: Added.
+ * platform/mac/fast/gradients/css3-radial-gradients-expected.png: Added.
+ * platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum: Added.
+ * platform/mac/fast/gradients/css3-radial-gradients2-expected.png: Added.
+
+2011-01-03 Simon Fraser <simon.fraser at apple.com>
+
+ Reviewed by David Hyatt.
+
+ Implement -webkit-linear-gradient and -webkit-radial-gradient
+ https://bugs.webkit.org/show_bug.cgi?id=28152
Testcases for CSS3 linear-gradients.
diff --git a/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt b/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt
index 3e60e8a..3e6f187 100644
--- a/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt
+++ b/LayoutTests/fast/gradients/css3-gradient-parsing-expected.txt
@@ -1,6 +1,4 @@
-Tests -webkit-linear-gradient and -webkit-radial-gradient
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+-webkit-linear-gradient
PASS testGradient("background-image: -webkit-linear-gradient(black 0%", "background-image") is "none"
@@ -19,6 +17,24 @@ PASS testGradient("background-image: -webkit-linear-gradient(black 10px, white 2
PASS testGradient("background-image: -webkit-linear-gradient(top left, black 0%)", "background-image") is "-webkit-linear-gradient(left top, black 0%)"
PASS testGradient("background-image: -webkit-linear-gradient(top, black 0%)", "background-image") is "-webkit-linear-gradient(top, black 0%)"
PASS testGradient("background-image: -webkit-linear-gradient(10deg, black 0%)", "background-image") is "-webkit-linear-gradient(10deg, black 0%)"
+-webkit-radial-gradient
+
+
+PASS testGradient("background-image: -webkit-radial-gradient(white, black)", "background-image") is "-webkit-radial-gradient(center, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image") is "-webkit-radial-gradient(100% 100%, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(cover, white, black)", "background-image") is "-webkit-radial-gradient(center, ellipse cover, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(circle, white, black)", "background-image") is "-webkit-radial-gradient(center, circle cover, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(circle contain, white, black)", "background-image") is "-webkit-radial-gradient(center, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(50% 0%, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(0% 0%, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(10px 20%, circle contain, white, black)", "background-image") is "-webkit-radial-gradient(10px 20%, circle contain, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(10px, 20%, circle contain, white, black)", "background-image") is "none"
+PASS testGradient("background-image: -webkit-radial-gradient(circle 10px 20%, circle contain, white, black)", "background-image") is "none"
+PASS testGradient("background-image: -webkit-radial-gradient(circle 10px, circle contain, white, black)", "background-image") is "none"
+PASS testGradient("background-image: -webkit-radial-gradient(center, 10px, white, black)", "background-image") is "none"
+PASS testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image") is "-webkit-radial-gradient(50% 50%, 10px 10px, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(ellipse farthest-corner, white, black)", "background-image") is "-webkit-radial-gradient(center, ellipse farthest-corner, white, black)"
+PASS testGradient("background-image: -webkit-radial-gradient(circle closest-side, white, black)", "background-image") is "-webkit-radial-gradient(center, circle closest-side, white, black)"
PASS successfullyParsed is true
TEST COMPLETE
diff --git a/LayoutTests/fast/gradients/css3-gradient-parsing.html b/LayoutTests/fast/gradients/css3-gradient-parsing.html
index 3d07f70..75c7f12 100644
--- a/LayoutTests/fast/gradients/css3-gradient-parsing.html
+++ b/LayoutTests/fast/gradients/css3-gradient-parsing.html
@@ -11,7 +11,6 @@
<script type="text/javascript">
-description('Tests -webkit-linear-gradient and -webkit-radial-gradient');
function testGradient(css, queryProp)
{
@@ -30,6 +29,8 @@ function testSame(dir, prop, altProp, value)
shouldBeEqualToString('test("' + dir + '", "' + altProp + ': ' + value + '", "' + prop + '")', value);
}
+debug('<p>-webkit-linear-gradient</p>');
+
shouldBe('testGradient("background-image: -webkit-linear-gradient(black 0%", "background-image")', '"none"');
shouldBe('testGradient("background-image: -webkit-linear-gradient(top)", "background-image")', '"none"');
shouldBe('testGradient("background-image: -webkit-linear-gradient(10deg)", "background-image")', '"none"');
@@ -47,6 +48,24 @@ shouldBe('testGradient("background-image: -webkit-linear-gradient(top left, blac
shouldBe('testGradient("background-image: -webkit-linear-gradient(top, black 0%)", "background-image")', '"-webkit-linear-gradient(top, black 0%)"');
shouldBe('testGradient("background-image: -webkit-linear-gradient(10deg, black 0%)", "background-image")', '"-webkit-linear-gradient(10deg, black 0%)"');
+debug('<p>-webkit-radial-gradient</p>');
+
+shouldBe('testGradient("background-image: -webkit-radial-gradient(white, black)", "background-image")', '"-webkit-radial-gradient(center, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(bottom right, white, black)", "background-image")', '"-webkit-radial-gradient(100% 100%, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(cover, white, black)", "background-image")', '"-webkit-radial-gradient(center, ellipse cover, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle cover, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(top, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(50% 0%, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(top left, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(0% 0%, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(10px 20%, circle contain, white, black)", "background-image")', '"-webkit-radial-gradient(10px 20%, circle contain, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(10px, 20%, circle contain, white, black)", "background-image")', '"none"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle 10px 20%, circle contain, white, black)", "background-image")', '"none"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle 10px, circle contain, white, black)", "background-image")', '"none"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(center, 10px, white, black)", "background-image")', '"none"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(center, 10px 10px, white, black)", "background-image")', '"-webkit-radial-gradient(50% 50%, 10px 10px, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(ellipse farthest-corner, white, black)", "background-image")', '"-webkit-radial-gradient(center, ellipse farthest-corner, white, black)"');
+shouldBe('testGradient("background-image: -webkit-radial-gradient(circle closest-side, white, black)", "background-image")', '"-webkit-radial-gradient(center, circle closest-side, white, black)"');
+
successfullyParsed = true;
</script>
diff --git a/LayoutTests/fast/gradients/css3-color-stops-expected.txt b/LayoutTests/fast/gradients/css3-radial-gradients-expected.txt
similarity index 100%
copy from LayoutTests/fast/gradients/css3-color-stops-expected.txt
copy to LayoutTests/fast/gradients/css3-radial-gradients-expected.txt
diff --git a/LayoutTests/fast/gradients/css3-radial-gradients.html b/LayoutTests/fast/gradients/css3-radial-gradients.html
new file mode 100644
index 0000000..b352419
--- /dev/null
+++ b/LayoutTests/fast/gradients/css3-radial-gradients.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+ <style type="text/css" media="screen">
+ .box {
+ display: inline-block;
+ height: 120px;
+ width: 200px;
+ margin: 10px;
+ border: 1px solid black;
+ background-repeat: no-repeat;
+ }
+
+ .gradient1 {
+ background-image: -webkit-radial-gradient(ellipse closest-corner, white, black);
+ background-image: -moz-radial-gradient(ellipse closest-corner, white, black);
+ }
+
+ .gradient2 {
+ background-image: -webkit-radial-gradient(50px 50%, circle closest-side, white, black);
+ background-image: -moz-radial-gradient(50px 50%, circle closest-side, white, black);
+ }
+
+ .gradient3 {
+ background-image: -webkit-radial-gradient(20px 50px, ellipse closest-corner, white, black);
+ background-image: -moz-radial-gradient(20px 50px, ellipse closest-corner, white, black);
+ }
+
+ .gradient4 {
+ background-image: -webkit-radial-gradient(20% 20%, circle closest-corner, white, black);
+ background-image: -moz-radial-gradient(20% 20%, circle closest-corner, white, black);
+ }
+
+ .gradient5 {
+ background-image: -webkit-radial-gradient(circle cover, red, green, blue);
+ background-image: -moz-radial-gradient(circle cover, red, green, blue);
+ }
+
+ .gradient6 {
+ background-image: -webkit-radial-gradient(left, circle cover, red, green 30%, blue);
+ background-image: -moz-radial-gradient(left, circle cover, red, green 30%, blue);
+ }
+
+ .gradient7 {
+ background-image: -webkit-radial-gradient(ellipse cover, red, green 80px, blue);
+ background-image: -moz-radial-gradient(ellipse cover, red, green 80px, blue);
+ }
+
+ .gradient8 {
+ background-image: -webkit-radial-gradient(circle cover, red 20%, green, blue 150%);
+ background-image: -moz-radial-gradient(circle cover, red 20%, green, blue 150%);
+ }
+
+ .gradient9 {
+ background-image: -webkit-radial-gradient(20% 20%, circle closest-corner, red -50%, green, blue 150%);
+ background-image: -moz-radial-gradient(20% 20%, circle closest-corner, red -50%, green, blue 150%);
+ }
+
+ </style>
+ <script type="text/javascript" charset="utf-8">
+ if (window.layoutTestController) {
+ var dumpPixels = true;
+ layoutTestController.dumpAsText(dumpPixels);
+ }
+ </script>
+</head>
+<body>
+
+ <div class="gradient1 box"></div>
+ <div class="gradient2 box"></div>
+ <div class="gradient3 box"></div>
+ <br>
+ <div class="gradient4 box"></div>
+ <div class="gradient5 box"></div>
+ <div class="gradient6 box"></div>
+ <br>
+ <div class="gradient7 box"></div>
+ <div class="gradient8 box"></div>
+ <div class="gradient9 box"></div>
+
+</body>
+</html>
diff --git a/LayoutTests/fast/gradients/css3-radial-gradients2-expected.txt b/LayoutTests/fast/gradients/css3-radial-gradients2-expected.txt
new file mode 100644
index 0000000..25447f1
--- /dev/null
+++ b/LayoutTests/fast/gradients/css3-radial-gradients2-expected.txt
@@ -0,0 +1,2 @@
+
+
diff --git a/LayoutTests/fast/gradients/css3-radial-gradients2.html b/LayoutTests/fast/gradients/css3-radial-gradients2.html
new file mode 100644
index 0000000..88cf161
--- /dev/null
+++ b/LayoutTests/fast/gradients/css3-radial-gradients2.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+ <style type="text/css" media="screen">
+ .box {
+ display: inline-block;
+ height: 250px;
+ width: 200px;
+ margin: 10px;
+ border: 1px solid black;
+ background-repeat: no-repeat;
+ }
+
+ .gradient1 {
+ /* Green should coincide with the edge of the box, with blue fill (buggy in CG). */
+ background-image: -webkit-radial-gradient(left, circle closest-corner, red, green 150px, blue);
+ background-image: -moz-radial-gradient(left, circle closest-corner, red, green 150px, blue);
+ }
+
+ .gradient2 {
+ /* Fill should be orange (buggy in CG). */
+ background-image: -webkit-radial-gradient(left, circle closest-corner, red, green 150px, blue, orange 101%);
+ background-image: -moz-radial-gradient(left, circle closest-corner, red, green 150px, blue, orange 101%);
+ }
+
+ .gradient3 {
+ background-image: -webkit-radial-gradient(-100px center, ellipse farthest-corner, black, white);
+ background-image: -moz-radial-gradient(-100px center, ellipse farthest-corner, black, white);
+ }
+
+ .gradient4 {
+ background-image: -webkit-radial-gradient(-100px center, ellipse closest-corner, black, white);
+ background-image: -moz-radial-gradient(-100px center, ellipse closest-corner, black, white);
+ }
+
+ .gradient5 {
+ background-image: -webkit-radial-gradient(bottom right, black, white);
+ background-image: -moz-radial-gradient(bottom right, black, white);
+ }
+
+ .gradient6 {
+ background-image: -webkit-radial-gradient(50% 20%, ellipse contain, black, white);
+ background-image: -moz-radial-gradient(50% 20%, ellipse contain, black, white);
+ }
+
+ </style>
+ <script type="text/javascript" charset="utf-8">
+ if (window.layoutTestController) {
+ var dumpPixels = true;
+ layoutTestController.dumpAsText(dumpPixels);
+ }
+ </script>
+</head>
+<body>
+
+ <div class="gradient1 box"></div>
+ <div class="gradient2 box"></div>
+ <div class="gradient3 box"></div>
+ <br>
+ <div class="gradient4 box"></div>
+ <div class="gradient5 box"></div>
+ <div class="gradient6 box"></div>
+
+</body>
+</html>
diff --git a/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.checksum b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.checksum
new file mode 100644
index 0000000..713f7cd
--- /dev/null
+++ b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.checksum
@@ -0,0 +1 @@
+af88d9c4da4d2856797d2ab20f7f2927
\ No newline at end of file
diff --git a/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.png b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.png
new file mode 100644
index 0000000..60f9908
Binary files /dev/null and b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients-expected.png differ
diff --git a/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum
new file mode 100644
index 0000000..a2008e4
--- /dev/null
+++ b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.checksum
@@ -0,0 +1 @@
+63d36fd71d88bb4bbe7ae42f8ce2f873
\ No newline at end of file
diff --git a/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.png b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.png
new file mode 100644
index 0000000..3b9914f
Binary files /dev/null and b/LayoutTests/platform/mac/fast/gradients/css3-radial-gradients2-expected.png differ
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index 888928a..6cbbeef 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -15,6 +15,71 @@
Implement -webkit-linear-gradient and -webkit-radial-gradient
https://bugs.webkit.org/show_bug.cgi?id=28152
+
+ Add support for -webkit-radial-gradients.
+
+ Tests: fast/gradients/css3-radial-gradients.html
+ fast/gradients/css3-radial-gradients2.html
+
+ * css/CSSGradientValue.cpp:
+ (WebCore::blend): Used to blend colors, which is necessary when truncating
+ the start of radial gradients.
+ (WebCore::GradientStop): Small struct to aid gradient stop processing.
+ (WebCore::CSSGradientValue::addStops): Rename 'positions' to 'stops', and store
+ the color so that we can blend it when truncating radial gradients. Changed
+ to handle both linear and radial gradients.
+ (WebCore::CSSRadialGradientValue::cssText): Update to follow the spec.
+ (WebCore::CSSRadialGradientValue::resolveRadius): Stylistic change.
+ (WebCore::distanceToClosestCorner): New utility method.
+ (WebCore::distanceToFarthestCorner): Ditto.
+ (WebCore::CSSRadialGradientValue::createGradient): New logic to deal with shape and
+ fill rules.
+
+ * css/CSSGradientValue.h:
+ (WebCore::CSSGradientValue::isLinearGradient): Color-stop processing needs to know what
+ kind of gradient it's dealing with, so add new isFooGradient methods.
+ (WebCore::CSSGradientValue::isRadialGradient): Ditto.
+ (WebCore::CSSLinearGradientValue::isLinearGradient):
+ (WebCore::CSSRadialGradientValue::setShape): New setters for shape, size etc.
+ (WebCore::CSSRadialGradientValue::setSizingBehavior):
+ (WebCore::CSSRadialGradientValue::setEndHorizontalSize):
+ (WebCore::CSSRadialGradientValue::setEndVerticalSize):
+ (WebCore::CSSRadialGradientValue::isRadialGradient):
+
+ * css/CSSParser.h:
+ * css/CSSParser.h: Pass a CSSParserValueList* into parseFillPositionXY() and parseFillPosition()
+ so we can use parseFillPosition() when parsing a gradient function.
+ * css/CSSParser.cpp:
+ (WebCore::CSSParser::parseFillPositionXY): Pass in a CSSParserValueList*
+ (WebCore::CSSParser::parseFillPosition): Ditto
+ (WebCore::CSSParser::parseFillProperty): Pass m_valueList to parseFillPosition.
+ (WebCore::CSSParser::parseTransformOriginShorthand): Ditto
+ (WebCore::CSSParser::parseRadialGradient): Flesh out radial gradient parsing.
+ (WebCore::CSSParser::parseTransformOrigin): Pass m_valueList to parseFillPosition.
+ (WebCore::CSSParser::parsePerspectiveOrigin): Ditto
+
+ * css/CSSValueKeywords.in: New keywords for radial gradient shape and fill behaviors.
+
+ * platform/graphics/Gradient.cpp:
+ (WebCore::Gradient::Gradient): Pass aspect ratio for elliptical gradients.
+ * platform/graphics/Gradient.h: Add aspect ratio for elliptical gradients.
+ (WebCore::Gradient::create):
+ (WebCore::Gradient::startRadius): Expose radii.
+ (WebCore::Gradient::endRadius):
+ (WebCore::Gradient::setStartRadius): Setters are required for when we need to scale color stops.
+ (WebCore::Gradient::setEndRadius):
+ (WebCore::Gradient::aspectRatio):
+ * platform/graphics/cg/GradientCG.cpp:
+ (WebCore::Gradient::paint): For elliptical gradients, scale the CTM.
+ * platform/graphics/wince/GraphicsContextWinCE.cpp:
+ (WebCore::GraphicsContext::fillRect): Use start/endRadius() rather than r0() and r1().
+
+2011-01-03 Simon Fraser <simon.fraser at apple.com>
+
+ Reviewed by David Hyatt.
+
+ Implement -webkit-linear-gradient and -webkit-radial-gradient
+ https://bugs.webkit.org/show_bug.cgi?id=28152
Add support for the parsing and rendering of non-repeating CSS3 linear gradients,
according to <http://dev.w3.org/csswg/css3-images/#linear-gradients>.
diff --git a/WebCore/css/CSSGradientValue.cpp b/WebCore/css/CSSGradientValue.cpp
index ca65709..ad0e490 100644
--- a/WebCore/css/CSSGradientValue.cpp
+++ b/WebCore/css/CSSGradientValue.cpp
@@ -82,6 +82,31 @@ void CSSGradientValue::sortStopsIfNeeded()
}
}
+static inline int blend(int from, int to, float progress)
+{
+ return int(from + (to - from) * progress);
+}
+
+static inline Color blend(const Color& from, const Color& to, float progress)
+{
+ // FIXME: when we interpolate gradients using premultiplied colors, this should also do premultiplication.
+ return Color(blend(from.red(), to.red(), progress),
+ blend(from.green(), to.green(), progress),
+ blend(from.blue(), to.blue(), progress),
+ blend(from.alpha(), to.alpha(), progress));
+}
+
+struct GradientStop {
+ Color color;
+ float offset;
+ bool specified;
+
+ GradientStop()
+ : offset(0)
+ , specified(false)
+ { }
+};
+
void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, RenderStyle* rootStyle)
{
RenderStyle* style = renderer->style();
@@ -109,63 +134,68 @@ void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, Rend
}
size_t numStops = m_stops.size();
- // Pair of <offset, specified>.
- Vector<pair<float, bool> > positions(numStops);
+
+ Vector<GradientStop> stops(numStops);
float gradientLength = 0;
bool computedGradientLength = false;
+ FloatPoint gradientStart = gradient->p0();
+ FloatPoint gradientEnd;
+ if (isLinearGradient())
+ gradientEnd = gradient->p1();
+ else if (isRadialGradient())
+ gradientEnd = gradientStart + FloatSize(gradient->endRadius(), 0);
+
for (size_t i = 0; i < numStops; ++i) {
const CSSGradientColorStop& stop = m_stops[i];
- positions[i].first = 0;
- positions[i].second = false;
+ stops[i].color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
if (stop.m_position) {
int type = stop.m_position->primitiveType();
if (type == CSSPrimitiveValue::CSS_PERCENTAGE)
- positions[i].first = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
+ stops[i].offset = stop.m_position->getFloatValue(CSSPrimitiveValue::CSS_PERCENTAGE) / 100;
else if (CSSPrimitiveValue::isUnitTypeLength(type)) {
float length = stop.m_position->computeLengthFloat(style, rootStyle, style->effectiveZoom());
if (!computedGradientLength) {
- float xDelta = gradient->p0().x() - gradient->p1().x();
- float yDelta = gradient->p0().y() - gradient->p1().y();
- gradientLength = sqrtf(xDelta * xDelta + yDelta * yDelta);
+ FloatSize gradientSize(gradientStart - gradientEnd);
+ gradientLength = gradientSize.diagonalLength();
}
- positions[i].first = (gradientLength > 0) ? length / gradientLength : 0;
+ stops[i].offset = (gradientLength > 0) ? length / gradientLength : 0;
} else {
ASSERT_NOT_REACHED();
- positions[i].first = 0;
+ stops[i].offset = 0;
}
- positions[i].second = true;
+ stops[i].specified = true;
} else {
// If the first color-stop does not have a position, its position defaults to 0%.
// If the last color-stop does not have a position, its position defaults to 100%.
if (!i) {
- positions[i].first = 0;
- positions[i].second = true;
+ stops[i].offset = 0;
+ stops[i].specified = true;
} else if (numStops > 1 && i == numStops - 1) {
- positions[i].first = 1;
- positions[i].second = true;
+ stops[i].offset = 1;
+ stops[i].specified = true;
}
}
// If a color-stop has a position that is less than the specified position of any
// color-stop before it in the list, its position is changed to be equal to the
// largest specified position of any color-stop before it.
- if (positions[i].second && i > 0) {
+ if (stops[i].specified && i > 0) {
size_t prevSpecifiedIndex;
for (prevSpecifiedIndex = i - 1; prevSpecifiedIndex; --prevSpecifiedIndex) {
- if (positions[prevSpecifiedIndex].second)
+ if (stops[prevSpecifiedIndex].specified)
break;
}
- if (positions[i].first < positions[prevSpecifiedIndex].first)
- positions[i].first = positions[prevSpecifiedIndex].first;
+ if (stops[i].offset < stops[prevSpecifiedIndex].offset)
+ stops[i].offset = stops[prevSpecifiedIndex].offset;
}
}
- ASSERT(positions[0].second && positions[numStops - 1].second);
+ ASSERT(stops[0].specified && stops[numStops - 1].specified);
// If any color-stop still does not have a position, then, for each run of adjacent
// color-stops without positions, set their positions so that they are evenly spaced
@@ -175,19 +205,19 @@ void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, Rend
bool inUnspecifiedRun = false;
for (size_t i = 0; i < numStops; ++i) {
- if (!positions[i].second && !inUnspecifiedRun) {
+ if (!stops[i].specified && !inUnspecifiedRun) {
unspecifiedRunStart = i;
inUnspecifiedRun = true;
- } else if (positions[i].second && inUnspecifiedRun) {
+ } else if (stops[i].specified && inUnspecifiedRun) {
size_t unspecifiedRunEnd = i;
if (unspecifiedRunStart < unspecifiedRunEnd) {
- float lastSpecifiedOffset = positions[unspecifiedRunStart - 1].first;
- float nextSpecifiedOffset = positions[unspecifiedRunEnd].first;
+ float lastSpecifiedOffset = stops[unspecifiedRunStart - 1].offset;
+ float nextSpecifiedOffset = stops[unspecifiedRunEnd].offset;
float delta = (nextSpecifiedOffset - lastSpecifiedOffset) / (unspecifiedRunEnd - unspecifiedRunStart + 1);
for (size_t j = unspecifiedRunStart; j < unspecifiedRunEnd; ++j)
- positions[j].first = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
+ stops[j].offset = lastSpecifiedOffset + (j - unspecifiedRunStart + 1) * delta;
}
inUnspecifiedRun = false;
@@ -196,25 +226,58 @@ void CSSGradientValue::addStops(Gradient* gradient, RenderObject* renderer, Rend
}
// If the gradient goes outside the 0-1 range, normalize it by moving the endpoints, and ajusting the stops.
- if (numStops > 1 && (positions[0].first < 0 || positions[numStops - 1].first > 1)) {
- float firstOffset = positions[0].first;
- float lastOffset = positions[numStops - 1].first;
- float scale = lastOffset - firstOffset;
-
- for (size_t i = 0; i < numStops; ++i)
- positions[i].first = (positions[i].first - firstOffset) / scale;
-
- FloatPoint p0 = gradient->p0();
- FloatPoint p1 = gradient->p1();
- gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
- gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
+ if (numStops > 1 && (stops[0].offset < 0 || stops[numStops - 1].offset > 1)) {
+ if (isLinearGradient()) {
+ float firstOffset = stops[0].offset;
+ float lastOffset = stops[numStops - 1].offset;
+ float scale = lastOffset - firstOffset;
+
+ for (size_t i = 0; i < numStops; ++i)
+ stops[i].offset = (stops[i].offset - firstOffset) / scale;
+
+ FloatPoint p0 = gradient->p0();
+ FloatPoint p1 = gradient->p1();
+ gradient->setP0(FloatPoint(p0.x() + firstOffset * (p1.x() - p0.x()), p0.y() + firstOffset * (p1.y() - p0.y())));
+ gradient->setP1(FloatPoint(p1.x() + (lastOffset - 1) * (p1.x() - p0.x()), p1.y() + (lastOffset - 1) * (p1.y() - p0.y())));
+ } else if (isRadialGradient()) {
+ // Rather than scaling the points < 0, we truncate them, so only scale according to the largest point.
+ float firstOffset = 0;
+ float lastOffset = stops[numStops - 1].offset;
+ float scale = lastOffset - firstOffset;
+
+ // Reset points below 0 to the first visible color.
+ size_t firstZeroOrGreaterIndex = 0;
+ for (size_t i = 0; i < numStops; ++i) {
+ if (stops[i].offset >= 0) {
+ firstZeroOrGreaterIndex = i;
+ break;
+ }
+ }
+
+ if (firstZeroOrGreaterIndex > 0 && stops[firstZeroOrGreaterIndex].offset > 0) {
+ float prevOffset = stops[firstZeroOrGreaterIndex - 1].offset;
+ float nextOffset = stops[firstZeroOrGreaterIndex].offset;
+
+ float interStopProportion = -prevOffset / (nextOffset - prevOffset);
+ Color blendedColor = blend(stops[firstZeroOrGreaterIndex - 1].color, stops[firstZeroOrGreaterIndex].color, interStopProportion);
+
+ // Clamp the positions to 0 and set the color.
+ for (size_t i = 0; i < firstZeroOrGreaterIndex; ++i) {
+ stops[i].offset = 0;
+ stops[i].color = blendedColor;
+ }
+ }
+
+ for (size_t i = 0; i < numStops; ++i)
+ stops[i].offset /= scale;
+
+ gradient->setStartRadius(gradient->startRadius() * scale);
+ gradient->setEndRadius(gradient->endRadius() * scale);
+ }
}
- for (unsigned i = 0; i < numStops; i++) {
- const CSSGradientColorStop& stop = m_stops[i];
- Color color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(stop.m_color.get());
- gradient->addColorStop(positions[i].first, color);
- }
+ for (unsigned i = 0; i < numStops; i++)
+ gradient->addColorStop(stops[i].offset, stops[i].color);
gradient->setStopsSorted(true);
}
@@ -404,28 +467,67 @@ String CSSRadialGradientValue::cssText() const
{
String result;
- if (m_deprecatedType)
+ if (m_deprecatedType) {
result = "-webkit-gradient(radial, ";
- else
- result = "-webkit-radial-gradient(";
- result += m_firstX->cssText() + " ";
- result += m_firstY->cssText() + ", ";
- result += m_firstRadius->cssText() + ", ";
- result += m_secondX->cssText() + " ";
- result += m_secondY->cssText();
- result += ", ";
- result += m_secondRadius->cssText();
-
- for (unsigned i = 0; i < m_stops.size(); i++) {
+ result += m_firstX->cssText() + " ";
+ result += m_firstY->cssText() + ", ";
+ result += m_firstRadius->cssText() + ", ";
+ result += m_secondX->cssText() + " ";
+ result += m_secondY->cssText();
result += ", ";
- if (m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
- result += "from(" + m_stops[i].m_color->cssText() + ")";
- else if (m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
- result += "to(" + m_stops[i].m_color->cssText() + ")";
+ result += m_secondRadius->cssText();
+
+ // FIXME: share?
+ for (unsigned i = 0; i < m_stops.size(); i++) {
+ const CSSGradientColorStop& stop = m_stops[i];
+ result += ", ";
+ if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 0)
+ result += "from(" + stop.m_color->cssText() + ")";
+ else if (stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER) == 1)
+ result += "to(" + stop.m_color->cssText() + ")";
+ else
+ result += "color-stop(" + String::number(stop.m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + stop.m_color->cssText() + ")";
+ }
+ } else {
+
+ result = "-webkit-radial-gradient(";
+ if (m_firstX && m_firstY) {
+ result += m_firstX->cssText() + " " + m_firstY->cssText();
+ } else if (m_firstX)
+ result += m_firstX->cssText();
+ else if (m_firstY)
+ result += m_firstY->cssText();
else
- result += "color-stop(" + String::number(m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER)) + ", " + m_stops[i].m_color->cssText() + ")";
+ result += "center";
+
+
+ if (m_shape || m_sizingBehavior) {
+ result += ", ";
+ if (m_shape)
+ result += m_shape->cssText() + " ";
+ else
+ result += "ellipse ";
+
+ if (m_sizingBehavior)
+ result += m_sizingBehavior->cssText();
+ else
+ result += "cover";
+
+ } else if (m_endHorizontalSize && m_endVerticalSize) {
+ result += ", ";
+ result += m_endHorizontalSize->cssText() + " " + m_endVerticalSize->cssText();
+ }
+
+ for (unsigned i = 0; i < m_stops.size(); i++) {
+ const CSSGradientColorStop& stop = m_stops[i];
+ result += ", ";
+ result += stop.m_color->cssText();
+ if (stop.m_position)
+ result += " " + stop.m_position->cssText();
+ }
}
+
result += ")";
return result;
}
@@ -434,7 +536,7 @@ float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, RenderSty
{
float zoomFactor = style->effectiveZoom();
- float result = 0.f;
+ float result = 0;
if (radius->primitiveType() == CSSPrimitiveValue::CSS_NUMBER) // Can the radius be a percentage?
result = radius->getFloatValue() * zoomFactor;
else
@@ -443,6 +545,82 @@ float CSSRadialGradientValue::resolveRadius(CSSPrimitiveValue* radius, RenderSty
return result;
}
+static float distanceToClosestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
+{
+ FloatPoint topLeft;
+ float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
+
+ FloatPoint topRight(size.width(), 0);
+ float topRightDistance = FloatSize(p - topRight).diagonalLength();
+
+ FloatPoint bottomLeft(0, size.height());
+ float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
+
+ FloatPoint bottomRight(size.width(), size.height());
+ float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
+
+ corner = topLeft;
+ float minDistance = topLeftDistance;
+ if (topRightDistance < minDistance) {
+ minDistance = topRightDistance;
+ corner = topRight;
+ }
+
+ if (bottomLeftDistance < minDistance) {
+ minDistance = bottomLeftDistance;
+ corner = bottomLeft;
+ }
+
+ if (bottomRightDistance < minDistance) {
+ minDistance = bottomRightDistance;
+ corner = bottomRight;
+ }
+ return minDistance;
+}
+
+static float distanceToFarthestCorner(const FloatPoint& p, const FloatSize& size, FloatPoint& corner)
+{
+ FloatPoint topLeft;
+ float topLeftDistance = FloatSize(p - topLeft).diagonalLength();
+
+ FloatPoint topRight(size.width(), 0);
+ float topRightDistance = FloatSize(p - topRight).diagonalLength();
+
+ FloatPoint bottomLeft(0, size.height());
+ float bottomLeftDistance = FloatSize(p - bottomLeft).diagonalLength();
+
+ FloatPoint bottomRight(size.width(), size.height());
+ float bottomRightDistance = FloatSize(p - bottomRight).diagonalLength();
+
+ corner = topLeft;
+ float maxDistance = topLeftDistance;
+ if (topRightDistance > maxDistance) {
+ maxDistance = topRightDistance;
+ corner = topRight;
+ }
+
+ if (bottomLeftDistance > maxDistance) {
+ maxDistance = bottomLeftDistance;
+ corner = bottomLeft;
+ }
+
+ if (bottomRightDistance > maxDistance) {
+ maxDistance = bottomRightDistance;
+ corner = bottomRight;
+ }
+ return maxDistance;
+}
+
+// Compute horizontal radius of ellipse with center at 0,0 which passes through p, and has
+// width/height given by aspectRatio.
+static inline float horizontalEllipseRadius(const FloatSize& p, float aspectRatio)
+{
+ // x^2/a^2 + y^2/b^2 = 1
+ // a/b = aspectRatio, b = a/aspectRatio
+ // a = sqrt(x^2 + y^2/(1/r^2))
+ return sqrtf(p.width() * p.width() + (p.height() * p.height()) / (1 / (aspectRatio * aspectRatio)));
+}
+
// FIXME: share code with the linear version
PassRefPtr<Gradient> CSSRadialGradientValue::createGradient(RenderObject* renderer, const IntSize& size)
{
@@ -451,23 +629,124 @@ PassRefPtr<Gradient> CSSRadialGradientValue::createGradient(RenderObject* render
RenderStyle* rootStyle = renderer->document()->documentElement()->renderStyle();
FloatPoint firstPoint = computeEndPoint(m_firstX.get(), m_firstY.get(), renderer->style(), rootStyle, size);
+ if (!m_firstX)
+ firstPoint.setX(size.width() / 2);
+ if (!m_firstY)
+ firstPoint.setY(size.height() / 2);
+
FloatPoint secondPoint = computeEndPoint(m_secondX.get(), m_secondY.get(), renderer->style(), rootStyle, size);
+ if (!m_secondX)
+ secondPoint.setX(size.width() / 2);
+ if (!m_secondY)
+ secondPoint.setY(size.height() / 2);
- float firstRadius = resolveRadius(m_firstRadius.get(), renderer->style(), rootStyle);
- float secondRadius = resolveRadius(m_secondRadius.get(), renderer->style(), rootStyle);
- RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius);
-
- // Now add the stops.
- sortStopsIfNeeded();
+ float firstRadius = 0;
+ if (m_firstRadius)
+ firstRadius = resolveRadius(m_firstRadius.get(), renderer->style(), rootStyle);
+
+ float secondRadius = 0;
+ float aspectRatio = 1; // width / height.
+ if (m_secondRadius)
+ secondRadius = resolveRadius(m_secondRadius.get(), renderer->style(), rootStyle);
+ else if (m_endHorizontalSize || m_endVerticalSize) {
+ secondRadius = resolveRadius(m_endHorizontalSize.get(), renderer->style(), rootStyle);
+ aspectRatio = secondRadius / resolveRadius(m_endVerticalSize.get(), renderer->style(), rootStyle);
+ } else {
+ enum GradientShape { Circle, Ellipse };
+ GradientShape shape = Ellipse;
+ if (m_shape && m_shape->primitiveType() == CSSPrimitiveValue::CSS_IDENT && m_shape->getIdent() == CSSValueCircle)
+ shape = Circle;
+
+ enum GradientFill { ClosestSide, ClosestCorner, FarthestSide, FarthestCorner };
+ GradientFill fill = FarthestCorner;
+
+ if (m_sizingBehavior && m_sizingBehavior->primitiveType() == CSSPrimitiveValue::CSS_IDENT) {
+ switch (m_sizingBehavior->getIdent()) {
+ case CSSValueContain:
+ case CSSValueClosestSide:
+ fill = ClosestSide;
+ break;
+ case CSSValueClosestCorner:
+ fill = ClosestCorner;
+ break;
+ case CSSValueFarthestSide:
+ fill = FarthestSide;
+ break;
+ case CSSValueCover:
+ case CSSValueFarthestCorner:
+ fill = FarthestCorner;
+ break;
+ }
+ }
+
+ // Now compute the end radii based on the second point, shape and fill.
+
+ // Horizontal
+ switch (fill) {
+ case ClosestSide: {
+ float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
+ float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
+ if (shape == Circle) {
+ float smaller = min(xDist, yDist);
+ xDist = smaller;
+ yDist = smaller;
+ }
+ secondRadius = xDist;
+ aspectRatio = xDist / yDist;
+ break;
+ }
+ case FarthestSide: {
+ float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
+ float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
+ if (shape == Circle) {
+ float larger = max(xDist, yDist);
+ xDist = larger;
+ yDist = larger;
+ }
+ secondRadius = xDist;
+ aspectRatio = xDist / yDist;
+ break;
+ }
+ case ClosestCorner: {
+ FloatPoint corner;
+ float distance = distanceToClosestCorner(secondPoint, size, corner);
+ if (shape == Circle)
+ secondRadius = distance;
+ else {
+ // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
+ // that it would if closest-side or farthest-side were specified, as appropriate.
+ float xDist = min(secondPoint.x(), size.width() - secondPoint.x());
+ float yDist = min(secondPoint.y(), size.height() - secondPoint.y());
+
+ secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
+ aspectRatio = xDist / yDist;
+ }
+ break;
+ }
- // We have to resolve colors.
- for (unsigned i = 0; i < m_stops.size(); i++) {
- Color color = renderer->document()->styleSelector()->getColorFromPrimitiveValue(m_stops[i].m_color.get());
- gradient->addColorStop(m_stops[i].m_position->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER), color);
+ case FarthestCorner: {
+ FloatPoint corner;
+ float distance = distanceToFarthestCorner(secondPoint, size, corner);
+ if (shape == Circle)
+ secondRadius = distance;
+ else {
+ // If <shape> is ellipse, the gradient-shape has the same ratio of width to height
+ // that it would if closest-side or farthest-side were specified, as appropriate.
+ float xDist = max(secondPoint.x(), size.width() - secondPoint.x());
+ float yDist = max(secondPoint.y(), size.height() - secondPoint.y());
+
+ secondRadius = horizontalEllipseRadius(corner - secondPoint, xDist / yDist);
+ aspectRatio = xDist / yDist;
+ }
+ break;
+ }
+ }
}
- // The back end already sorted the stops.
- gradient->setStopsSorted(true);
+ RefPtr<Gradient> gradient = Gradient::create(firstPoint, firstRadius, secondPoint, secondRadius, aspectRatio);
+
+ // Now add the stops.
+ addStops(gradient.get(), renderer, rootStyle);
return gradient.release();
}
diff --git a/WebCore/css/CSSGradientValue.h b/WebCore/css/CSSGradientValue.h
index 1db626a..0988cb6 100644
--- a/WebCore/css/CSSGradientValue.h
+++ b/WebCore/css/CSSGradientValue.h
@@ -57,6 +57,9 @@ public:
Vector<CSSGradientColorStop>& stops() { return m_stops; }
void sortStopsIfNeeded();
+
+ virtual bool isLinearGradient() const { return false; }
+ virtual bool isRadialGradient() const { return false; }
bool deprecatedType() const { return m_deprecatedType; } // came from -webkit-gradient
@@ -75,7 +78,7 @@ protected:
// Resolve points/radii to front end values.
FloatPoint computeEndPoint(CSSPrimitiveValue*, CSSPrimitiveValue*, RenderStyle*, RenderStyle* rootStyle, const IntSize&);
- // Points. Some of these may be nil for linear gradients.
+ // Points. Some of these may be null for linear gradients.
RefPtr<CSSPrimitiveValue> m_firstX;
RefPtr<CSSPrimitiveValue> m_firstY;
@@ -106,6 +109,8 @@ private:
{
}
+ virtual bool isLinearGradient() const { return true; }
+
// Create the gradient for a given size.
virtual PassRefPtr<Gradient> createGradient(RenderObject*, const IntSize&);
@@ -124,20 +129,36 @@ public:
void setFirstRadius(PassRefPtr<CSSPrimitiveValue> val) { m_firstRadius = val; }
void setSecondRadius(PassRefPtr<CSSPrimitiveValue> val) { m_secondRadius = val; }
+ void setShape(PassRefPtr<CSSPrimitiveValue> val) { m_shape = val; }
+ void setSizingBehavior(PassRefPtr<CSSPrimitiveValue> val) { m_sizingBehavior = val; }
+
+ void setEndHorizontalSize(PassRefPtr<CSSPrimitiveValue> val) { m_endHorizontalSize = val; }
+ void setEndVerticalSize(PassRefPtr<CSSPrimitiveValue> val) { m_endVerticalSize = val; }
+
private:
CSSRadialGradientValue(bool deprecatedType = false)
: CSSGradientValue(deprecatedType)
{
}
+ virtual bool isRadialGradient() const { return true; }
+
// Create the gradient for a given size.
virtual PassRefPtr<Gradient> createGradient(RenderObject*, const IntSize&);
// Resolve points/radii to front end values.
float resolveRadius(CSSPrimitiveValue*, RenderStyle*, RenderStyle* rootStyle);
+ // These may be null for non-deprecated gradients.
RefPtr<CSSPrimitiveValue> m_firstRadius;
RefPtr<CSSPrimitiveValue> m_secondRadius;
+
+ // The below are only used for non-deprecated gradients.
+ RefPtr<CSSPrimitiveValue> m_shape;
+ RefPtr<CSSPrimitiveValue> m_sizingBehavior;
+
+ RefPtr<CSSPrimitiveValue> m_endHorizontalSize;
+ RefPtr<CSSPrimitiveValue> m_endVerticalSize;
};
} // namespace WebCore
diff --git a/WebCore/css/CSSParser.cpp b/WebCore/css/CSSParser.cpp
index 441597a..bf2143c 100644
--- a/WebCore/css/CSSParser.cpp
+++ b/WebCore/css/CSSParser.cpp
@@ -2455,9 +2455,9 @@ bool CSSParser::parseFillImage(RefPtr<CSSValue>& value)
return false;
}
-PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(bool& xFound, bool& yFound)
+PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(CSSParserValueList* valueList, bool& xFound, bool& yFound)
{
- int id = m_valueList->current()->id;
+ int id = valueList->current()->id;
if (id == CSSValueLeft || id == CSSValueTop || id == CSSValueRight || id == CSSValueBottom || id == CSSValueCenter) {
int percent = 0;
if (id == CSSValueLeft || id == CSSValueRight) {
@@ -2479,27 +2479,27 @@ PassRefPtr<CSSValue> CSSParser::parseFillPositionXY(bool& xFound, bool& yFound)
percent = 50;
return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::CSS_PERCENTAGE);
}
- if (validUnit(m_valueList->current(), FPercent | FLength, m_strict))
- return CSSPrimitiveValue::create(m_valueList->current()->fValue,
- (CSSPrimitiveValue::UnitTypes)m_valueList->current()->unit);
+ if (validUnit(valueList->current(), FPercent | FLength, m_strict))
+ return CSSPrimitiveValue::create(valueList->current()->fValue,
+ (CSSPrimitiveValue::UnitTypes)valueList->current()->unit);
return 0;
}
-void CSSParser::parseFillPosition(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2)
+void CSSParser::parseFillPosition(CSSParserValueList* valueList, RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2)
{
- CSSParserValue* value = m_valueList->current();
+ CSSParserValue* value = valueList->current();
// Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length.
bool value1IsX = false, value1IsY = false;
- value1 = parseFillPositionXY(value1IsX, value1IsY);
+ value1 = parseFillPositionXY(valueList, value1IsX, value1IsY);
if (!value1)
return;
// It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we
// can assume that any other values belong to the rest of the shorthand). If we're not parsing a shorthand, though, the
// value was explicitly specified for our property.
- value = m_valueList->next();
+ value = valueList->next();
// First check for the comma. If so, we are finished parsing this value or value pair.
if (value && value->unit == CSSParserValue::Operator && value->iValue == ',')
@@ -2507,9 +2507,9 @@ void CSSParser::parseFillPosition(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& va
bool value2IsX = false, value2IsY = false;
if (value) {
- value2 = parseFillPositionXY(value2IsX, value2IsY);
+ value2 = parseFillPositionXY(valueList, value2IsX, value2IsY);
if (value2)
- m_valueList->next();
+ valueList->next();
else {
if (!inShorthand()) {
value1.clear();
@@ -2698,13 +2698,13 @@ bool CSSParser::parseFillProperty(int propId, int& propId1, int& propId2,
break;
case CSSPropertyBackgroundPosition:
case CSSPropertyWebkitMaskPosition:
- parseFillPosition(currValue, currValue2);
+ parseFillPosition(m_valueList, currValue, currValue2);
// parseFillPosition advances the m_valueList pointer
break;
case CSSPropertyBackgroundPositionX:
case CSSPropertyWebkitMaskPositionX: {
bool xFound = false, yFound = true;
- currValue = parseFillPositionXY(xFound, yFound);
+ currValue = parseFillPositionXY(m_valueList, xFound, yFound);
if (currValue)
m_valueList->next();
break;
@@ -2712,7 +2712,7 @@ bool CSSParser::parseFillProperty(int propId, int& propId1, int& propId2,
case CSSPropertyBackgroundPositionY:
case CSSPropertyWebkitMaskPositionY: {
bool xFound = true, yFound = false;
- currValue = parseFillPositionXY(xFound, yFound);
+ currValue = parseFillPositionXY(m_valueList, xFound, yFound);
if (currValue)
m_valueList->next();
break;
@@ -2863,7 +2863,7 @@ PassRefPtr<CSSValue> CSSParser::parseAnimationProperty()
bool CSSParser::parseTransformOriginShorthand(RefPtr<CSSValue>& value1, RefPtr<CSSValue>& value2, RefPtr<CSSValue>& value3)
{
- parseFillPosition(value1, value2);
+ parseFillPosition(m_valueList, value1, value2);
// now get z
if (m_valueList->current()) {
@@ -4988,10 +4988,152 @@ bool CSSParser::parseLinearGradient(RefPtr<CSSValue>& gradient)
return true;
}
-bool CSSParser::parseRadialGradient(RefPtr<CSSValue>&)
+bool CSSParser::parseRadialGradient(RefPtr<CSSValue>& gradient)
{
- // FIXME: implement.
- return false;
+ RefPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create();
+
+ // Walk the arguments.
+ CSSParserValueList* args = m_valueList->current()->function->args.get();
+ if (!args || !args->size())
+ return false;
+
+ CSSParserValue* a = args->current();
+ if (!a)
+ return false;
+
+ bool expectComma = false;
+
+ // Optional background-position
+ RefPtr<CSSValue> centerX;
+ RefPtr<CSSValue> centerY;
+ // parseFillPosition advances the args next pointer.
+ parseFillPosition(args, centerX, centerY);
+ a = args->current();
+
+ if (centerX || centerY) {
+ // Comma
+ if (a->unit != CSSParserValue::Operator || a->iValue != ',')
+ return false;
+
+ a = args->next();
+ if (!a)
+ return false;
+ }
+
+ ASSERT(!centerX || centerX->isPrimitiveValue());
+ ASSERT(!centerY || centerY->isPrimitiveValue());
+
+ result->setFirstX(static_cast<CSSPrimitiveValue*>(centerX.get()));
+ result->setSecondX(static_cast<CSSPrimitiveValue*>(centerX.get()));
+ // CSS3 radial gradients always share the same start and end point.
+ result->setFirstY(static_cast<CSSPrimitiveValue*>(centerY.get()));
+ result->setSecondY(static_cast<CSSPrimitiveValue*>(centerY.get()));
+
+ RefPtr<CSSPrimitiveValue> shapeValue;
+ RefPtr<CSSPrimitiveValue> sizeValue;
+
+ // Optional shape and/or size in any order.
+ for (int i = 0; i < 2; ++i) {
+ if (a->unit != CSSPrimitiveValue::CSS_IDENT)
+ break;
+
+ bool foundValue = false;
+ switch (a->id) {
+ case CSSValueCircle:
+ case CSSValueEllipse:
+ shapeValue = CSSPrimitiveValue::createIdentifier(a->id);
+ foundValue = true;
+ break;
+ case CSSValueClosestSide:
+ case CSSValueClosestCorner:
+ case CSSValueFarthestSide:
+ case CSSValueFarthestCorner:
+ case CSSValueContain:
+ case CSSValueCover:
+ sizeValue = CSSPrimitiveValue::createIdentifier(a->id);
+ foundValue = true;
+ break;
+ }
+
+ if (foundValue) {
+ a = args->next();
+ if (!a)
+ return false;
+
+ expectComma = true;
+ }
+ }
+
+ result->setShape(shapeValue);
+ result->setSizingBehavior(sizeValue);
+
+ // Or, two lengths or percentages
+ RefPtr<CSSPrimitiveValue> horizontalSize;
+ RefPtr<CSSPrimitiveValue> verticalSize;
+
+ if (!shapeValue && !sizeValue) {
+ if (validUnit(a, FLength | FPercent, m_strict)) {
+ horizontalSize = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit);
+ a = args->next();
+ if (!a)
+ return false;
+
+ expectComma = true;
+ }
+
+ if (validUnit(a, FLength | FPercent, m_strict)) {
+ verticalSize = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit);
+
+ a = args->next();
+ if (!a)
+ return false;
+ expectComma = true;
+ }
+ }
+
+ // Must have neither or both.
+ if (!horizontalSize != !verticalSize)
+ return false;
+
+ result->setEndHorizontalSize(horizontalSize);
+ result->setEndVerticalSize(verticalSize);
+
+ // Now look for 0 or more color stops.
+ while (a) {
+ // Look for the comma before the next stop.
+ if (expectComma) {
+ if (a->unit != CSSParserValue::Operator || a->iValue != ',')
+ return false;
+
+ a = args->next();
+ if (!a)
+ return false;
+ }
+
+ // <color-stop> = <color> [ <percentage> | <length> ]?
+ CSSGradientColorStop stop;
+ stop.m_color = parseGradientColorOrKeyword(this, a);
+ if (!stop.m_color)
+ return false;
+
+ a = args->next();
+ if (a) {
+ if (validUnit(a, FLength | FPercent, m_strict)) {
+ stop.m_position = CSSPrimitiveValue::create(a->fValue, (CSSPrimitiveValue::UnitTypes)a->unit);
+ a = args->next();
+ }
+ }
+
+ result->addStop(stop);
+ expectComma = true;
+ }
+
+ Vector<CSSGradientColorStop>& stops = result->stops();
+ if (stops.isEmpty())
+ return false;
+
+ gradient = result.release();
+ return true;
}
bool CSSParser::isGeneratedImageValue(CSSParserValue* val) const
@@ -5225,14 +5367,14 @@ bool CSSParser::parseTransformOrigin(int propId, int& propId1, int& propId2, int
break;
case CSSPropertyWebkitTransformOriginX: {
bool xFound = false, yFound = true;
- value = parseFillPositionXY(xFound, yFound);
+ value = parseFillPositionXY(m_valueList, xFound, yFound);
if (value)
m_valueList->next();
break;
}
case CSSPropertyWebkitTransformOriginY: {
bool xFound = true, yFound = false;
- value = parseFillPositionXY(xFound, yFound);
+ value = parseFillPositionXY(m_valueList, xFound, yFound);
if (value)
m_valueList->next();
break;
@@ -5260,18 +5402,18 @@ bool CSSParser::parsePerspectiveOrigin(int propId, int& propId1, int& propId2, R
switch (propId) {
case CSSPropertyWebkitPerspectiveOrigin:
- parseFillPosition(value, value2);
+ parseFillPosition(m_valueList, value, value2);
break;
case CSSPropertyWebkitPerspectiveOriginX: {
bool xFound = false, yFound = true;
- value = parseFillPositionXY(xFound, yFound);
+ value = parseFillPositionXY(m_valueList, xFound, yFound);
if (value)
m_valueList->next();
break;
}
case CSSPropertyWebkitPerspectiveOriginY: {
bool xFound = true, yFound = false;
- value = parseFillPositionXY(xFound, yFound);
+ value = parseFillPositionXY(m_valueList, xFound, yFound);
if (value)
m_valueList->next();
break;
diff --git a/WebCore/css/CSSParser.h b/WebCore/css/CSSParser.h
index 2432e79..6633072 100644
--- a/WebCore/css/CSSParser.h
+++ b/WebCore/css/CSSParser.h
@@ -84,8 +84,8 @@ namespace WebCore {
PassRefPtr<CSSValue> parseBackgroundColor();
bool parseFillImage(RefPtr<CSSValue>&);
- PassRefPtr<CSSValue> parseFillPositionXY(bool& xFound, bool& yFound);
- void parseFillPosition(RefPtr<CSSValue>&, RefPtr<CSSValue>&);
+ PassRefPtr<CSSValue> parseFillPositionXY(CSSParserValueList*, bool& xFound, bool& yFound);
+ void parseFillPosition(CSSParserValueList*, RefPtr<CSSValue>&, RefPtr<CSSValue>&);
void parseFillRepeat(RefPtr<CSSValue>&, RefPtr<CSSValue>&);
PassRefPtr<CSSValue> parseFillSize(int propId, bool &allowComma);
diff --git a/WebCore/css/CSSValueKeywords.in b/WebCore/css/CSSValueKeywords.in
index c3ca1b5..4fce4f3 100644
--- a/WebCore/css/CSSValueKeywords.in
+++ b/WebCore/css/CSSValueKeywords.in
@@ -780,3 +780,13 @@ dot
double-circle
triangle
sesame
+
+# -webkit-radial-gradient
+# circle
+ellipse
+closest-side
+closest-corner
+farthest-side
+farthest-corner
+# contain
+# cover
diff --git a/WebCore/platform/graphics/Gradient.cpp b/WebCore/platform/graphics/Gradient.cpp
index da60f3b..079dcd5 100644
--- a/WebCore/platform/graphics/Gradient.cpp
+++ b/WebCore/platform/graphics/Gradient.cpp
@@ -39,6 +39,7 @@ Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1)
, m_p1(p1)
, m_r0(0)
, m_r1(0)
+ , m_aspectRatio(1)
, m_stopsSorted(false)
, m_lastStop(0)
, m_spreadMethod(SpreadMethodPad)
@@ -46,12 +47,13 @@ Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1)
platformInit();
}
-Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1)
+Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio)
: m_radial(true)
, m_p0(p0)
, m_p1(p1)
, m_r0(r0)
, m_r1(r1)
+ , m_aspectRatio(aspectRatio)
, m_stopsSorted(false)
, m_lastStop(0)
, m_spreadMethod(SpreadMethodPad)
diff --git a/WebCore/platform/graphics/Gradient.h b/WebCore/platform/graphics/Gradient.h
index 5180a9b..19b9c6a 100644
--- a/WebCore/platform/graphics/Gradient.h
+++ b/WebCore/platform/graphics/Gradient.h
@@ -76,9 +76,9 @@ namespace WebCore {
{
return adoptRef(new Gradient(p0, p1));
}
- static PassRefPtr<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1)
+ static PassRefPtr<Gradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio = 1)
{
- return adoptRef(new Gradient(p0, r0, p1, r1));
+ return adoptRef(new Gradient(p0, r0, p1, r1, aspectRatio));
}
virtual ~Gradient();
@@ -98,9 +98,15 @@ namespace WebCore {
void setP0(const FloatPoint& p) { m_p0 = p; }
void setP1(const FloatPoint& p) { m_p1 = p; }
+ float startRadius() const { return m_r0; }
+ float endRadius() const { return m_r1; }
+
+ void setStartRadius(float r) { m_r0 = r; }
+ void setEndRadius(float r) { m_r1 = r; }
+
+ float aspectRatio() const { return m_aspectRatio; }
+
#if OS(WINCE) && !PLATFORM(QT)
- float r0() const { return m_r0; }
- float r1() const { return m_r1; }
const Vector<ColorStop, 2>& getStops() const;
#else
PlatformGradient platformGradient();
@@ -134,7 +140,7 @@ namespace WebCore {
#endif
private:
Gradient(const FloatPoint& p0, const FloatPoint& p1);
- Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1);
+ Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio);
void platformInit() { m_gradient = 0; }
void platformDestroy();
@@ -147,6 +153,7 @@ namespace WebCore {
FloatPoint m_p1;
float m_r0;
float m_r1;
+ float m_aspectRatio; // For elliptical gradient, width / height.
mutable Vector<ColorStop, 2> m_stops;
mutable bool m_stopsSorted;
mutable int m_lastStop;
diff --git a/WebCore/platform/graphics/cg/GradientCG.cpp b/WebCore/platform/graphics/cg/GradientCG.cpp
index 85037e2..0503400 100644
--- a/WebCore/platform/graphics/cg/GradientCG.cpp
+++ b/WebCore/platform/graphics/cg/GradientCG.cpp
@@ -112,14 +112,29 @@ void Gradient::fill(GraphicsContext* context, const FloatRect& rect)
void Gradient::paint(GraphicsContext* context)
{
+ CGContextRef ctx = context->platformContext();
#if USE_CG_SHADING
- CGContextDrawShading(context->platformContext(), platformGradient());
+ CGContextDrawShading(ctx, platformGradient());
#else
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
- if (m_radial)
- CGContextDrawRadialGradient(context->platformContext(), platformGradient(), m_p0, m_r0, m_p1, m_r1, extendOptions);
- else
- CGContextDrawLinearGradient(context->platformContext(), platformGradient(), m_p0, m_p1, extendOptions);
+ if (m_radial) {
+ bool needScaling = aspectRatio() != 1;
+ if (needScaling) {
+ CGContextSaveGState(ctx);
+ // Scale from the center of the gradient. We only ever scale non-deprecated gradients,
+ // for which m_p0 == m_p1.
+ ASSERT(m_p0 == m_p1);
+ CGContextTranslateCTM(ctx, m_p0.x(), m_p0.y());
+ CGContextScaleCTM(ctx, 1, 1 / aspectRatio());
+ CGContextTranslateCTM(ctx, -m_p0.x(), -m_p0.y());
+ }
+
+ CGContextDrawRadialGradient(ctx, platformGradient(), m_p0, m_r0, m_p1, m_r1, extendOptions);
+
+ if (needScaling)
+ CGContextRestoreGState(ctx);
+ } else
+ CGContextDrawLinearGradient(ctx, platformGradient(), m_p0, m_p1, extendOptions);
#endif
}
diff --git a/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp b/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp
index 1ea1a64..2def6ab 100644
--- a/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp
+++ b/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp
@@ -1425,8 +1425,8 @@ void GraphicsContext::fillRect(const FloatRect& r, const Gradient* gradient)
if (g_radialGradientFiller) {
// FIXME: don't support 2D scaling at this time
double scale = (m_data->m_transform.a() + m_data->m_transform.d()) * 0.5;
- float r0 = gradient->r0() * scale;
- float r1 = gradient->r1() * scale;
+ float r0 = gradient->startRadius() * scale;
+ float r1 = gradient->endRadius() * scale;
g_radialGradientFiller(dc, rect, p0, p1, r0, r1, gradient->getStops());
return;
}
--
WebKit Debian packaging
More information about the Pkg-webkit-commits
mailing list