Title: [198062] trunk
Revision
198062
Author
[email protected]
Date
2016-03-12 09:24:21 -0800 (Sat, 12 Mar 2016)

Log Message

[Forms: focus] focus rings around text fields do not follow contour (border-radius)
https://bugs.webkit.org/show_bug.cgi?id=154099
rdar://problem/9988429

Reviewed by Tim Horton.

Source/WebCore:

This patch enables outline-style: auto to follow the curve of border-radius.
When both border-radius and outline-style: auto are set, the native focusring painting will take the border-radius values
into account. This is only for outline-style: auto, other non-auto outline styles paint as if there
was no border-radius set.
It supports both single and multiline content with joint rectangles.
However in case of disjoint rectangles, we fallback to the non-radius drawing.

Tests: fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr.html
       fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl.html
       fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr.html
       fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl.html

* platform/graphics/GraphicsContext.h:
* platform/graphics/Path.cpp:
(WebCore::Path::addBeziersForRoundedRect):
* platform/graphics/Path.h:
(WebCore::Path::circleControlPoint):
* platform/graphics/PathUtilities.cpp:
(WebCore::polygonsForRect):
(WebCore::PathUtilities::pathsWithShrinkWrappedRects):
(WebCore::startAndEndPointsForCorner):
(WebCore::cornerType):
(WebCore::controlPointsForBezierCurve):
(WebCore::adjustedtRadiiForHuggingCurve):
(WebCore::PathUtilities::pathWithShrinkWrappedRectsForOutline):
* platform/graphics/PathUtilities.h:
* platform/graphics/mac/GraphicsContextMac.mm:
(WebCore::GraphicsContext::drawFocusRing):
* rendering/RenderElement.cpp:
(WebCore::RenderElement::paintFocusRing):

LayoutTests:

Unfortunately there's no proper way to test native focusring drawing.
These tests attempt to verify that we don't end up painting sharp corners.

* fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr-expected.html: Added.
* fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr.html: Added.
* fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl-expected.html: Added.
* fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl.html: Added.
* fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr-expected.html: Added.
* fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr.html: Added.
* fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl-expected.html: Added.
* fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (198061 => 198062)


--- trunk/LayoutTests/ChangeLog	2016-03-12 10:51:02 UTC (rev 198061)
+++ trunk/LayoutTests/ChangeLog	2016-03-12 17:24:21 UTC (rev 198062)
@@ -1,3 +1,23 @@
+2016-03-12  Zalan Bujtas  <[email protected]>
+
+        [Forms: focus] focus rings around text fields do not follow contour (border-radius)
+        https://bugs.webkit.org/show_bug.cgi?id=154099
+        rdar://problem/9988429
+
+        Reviewed by Tim Horton.
+
+        Unfortunately there's no proper way to test native focusring drawing.
+        These tests attempt to verify that we don't end up painting sharp corners.
+
+        * fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr-expected.html: Added.
+        * fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr.html: Added.
+        * fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl-expected.html: Added.
+        * fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl.html: Added.
+        * fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr-expected.html: Added.
+        * fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr.html: Added.
+        * fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl-expected.html: Added.
+        * fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl.html: Added.
+
 2016-03-11  Ryosuke Niwa  <[email protected]>
 
         Add Event.deepPath() and Event.scoped

Added: trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr-expected.html (0 => 198062)


--- trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr-expected.html	2016-03-12 17:24:21 UTC (rev 198062)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests hugging outlines for multiline horizontal ltr content.</title>
+</head>
+<body></body>
+</html>

Added: trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr.html (0 => 198062)


--- trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr.html	2016-03-12 17:24:21 UTC (rev 198062)
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests hugging outlines for multiline horizontal ltr content.</title>
+<style>
+body {
+    font-size: 20px;
+    font-family: ahem;
+    color: rgba(0, 0, 0, 0);
+}
+
+.container {
+    margin-bottom: 20px;
+}
+
+span {
+    border-radius: 15px 10px 8px 5px;
+    outline-offset: 0px;
+    outline: auto;
+}
+
+.cover {
+    position: absolute;
+    border-radius: 15px 4px 8px 5px;
+    border: 7px solid white;
+}
+</style>
+</head>
+<body>
+<div class=cover style="top: 3px; left: 3px; width: 56px; height: 56px; border-radius: 20px 3px 0px 3px;"></div>
+<div class=cover style="top: 23px; left: 66px; width: 53px; height: 36px; border-radius: 0px 3px 0px 0px;"></div>
+<div class=cover style="top: 43px; left: 126px; width: 133px; height: 16px; border-radius: 0px 18px 18px 0px;"></div>
+
+<div class=cover style="top: 83px; left: 3px; width: 256px; height: 16px; border-radius: 20px 3px 3px 0px;"></div>
+<div class=cover style="top: 106px; left: 3px; width: 196px; height: 13px; border-radius: 0px 0px 3px 0px;"></div>
+<div class=cover style="top: 126px; left: 3px; width: 116px; height: 13px; border-radius: 0px 0px 14px 3px;"></div>
+
+<div class=cover style="top: 163px; left: 3px; width: 116px; height: 13px; border-radius: 20px 3px 0px 0px;"></div>
+<div class=cover style="top: 183px; left: 3px; width: 256px; height: 16px; border-radius: 0px 3px 3px 0px;"></div>
+<div class=cover style="top: 206px; left: 3px; width: 116px; height: 13px; border-radius: 0px 0px 14px 3px;"></div>
+
+<div class=cover style="top: 243px; left: 3px; width: 256px; height: 15px; border-radius: 20px 3px 3px 0px;"></div>
+<div class=cover style="top: 265px; left: 3px; width: 116px; height: 13px; border-radius: 0px 0px 0px 0px;"></div>
+<div class=cover style="top: 284px; left: 3px; width: 256px; height: 14px; border-radius: 0px 18px 18px 3px;"></div>
+
+<div class=container><span>foo<br>foobar<br>foobar foobar</span></div>
+<div class=container><span>foobar foobar<br>foobar foo<br>foobar</span></div>
+<div class=container><span>foobar<br>foobar foobar<br>foobar</span></div>
+<div class=container><span>foobar foobar<br>foobar<br>foobar foobar</span></div>
+</body>
+</html>

Added: trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl-expected.html (0 => 198062)


--- trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl-expected.html	2016-03-12 17:24:21 UTC (rev 198062)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests hugging outlines for multiline horizontal rtl content.</title>
+</head>
+<body></body>
+</html>

Added: trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl.html (0 => 198062)


--- trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl.html	2016-03-12 17:24:21 UTC (rev 198062)
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests hugging outlines for multiline horizontal rtl content.</title>
+<style>
+body {
+    direction: rtl;
+    font-size: 20px;
+    font-family: ahem;
+    color: rgba(0, 0, 0, 0);
+}
+ 
+.container {
+    margin-bottom: 20px;
+}
+
+span {
+    border-radius: 15px 10px 8px 5px;
+    outline-offset: 0px;
+    outline: auto;
+}
+
+.cover {
+    position: absolute;
+    border-radius: 15px 4px 8px 5px;
+    border: 7px solid white;
+}
+</style>
+</head>
+<body>
+<div class=cover style="top: 3px; right: 3px; width: 56px; height: 56px; border-radius: 3px 16px 3px 0px;"></div>
+<div class=cover style="top: 23px; right: 66px; width: 53px; height: 36px; border-radius: 3px 0px 0px 0px;"></div>
+<div class=cover style="top: 43px; right: 126px; width: 133px; height: 16px; border-radius: 21px 0px 0px 11px;"></div>
+
+<div class=cover style="top: 83px; right: 3px; width: 256px; height: 16px; border-radius: 3px 16px 0px 3px;"></div>
+<div class=cover style="top: 106px; right: 3px; width: 196px; height: 13px; border-radius: 0px 0px 0px 3px;"></div>
+<div class=cover style="top: 126px; right: 3px; width: 116px; height: 13px; border-radius: 0px 0px 3px 12px;"></div>
+
+<div class=cover style="top: 163px; right: 3px; width: 116px; height: 13px; border-radius: 3px 16px 0px 0px;"></div>
+<div class=cover style="top: 183px; right: 3px; width: 256px; height: 16px; border-radius: 3px 0px 0px 3px;"></div>
+<div class=cover style="top: 206px; right: 3px; width: 116px; height: 13px; border-radius: 0px 0px 3px 12px;"></div>
+
+<div class=cover style="top: 243px; right: 3px; width: 256px; height: 15px; border-radius: 3px 16px 0px 3px;"></div>
+<div class=cover style="top: 265px; right: 3px; width: 116px; height: 13px; border-radius: 0px 0px 0px 0px;"></div>
+<div class=cover style="top: 284px; right: 3px; width: 256px; height: 14px; border-radius: 21px 0px 3px 11px;"></div>
+
+<div class=container><span>foo<br>foobar<br>foobar foobar</span></div>
+<div class=container><span>foobar foobar<br>foobar foo<br>foobar</span></div>
+<div class=container><span>foobar<br>foobar foobar<br>foobar</span></div>
+<div class=container><span>foobar foobar<br>foobar<br>foobar foobar</span></div>
+</body>
+</html>

Added: trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr-expected.html (0 => 198062)


--- trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr-expected.html	2016-03-12 17:24:21 UTC (rev 198062)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests hugging outlines for multiline vertical ltr content.</title>
+</head>
+<body></body>
+</html>

Added: trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr.html (0 => 198062)


--- trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr.html	2016-03-12 17:24:21 UTC (rev 198062)
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests hugging outlines for multiline vertical ltr content.</title>
+<style>
+body {
+    font-size: 20px;
+    font-family: ahem;
+    color: rgba(0, 0, 0, 0);
+}
+ 
+.container {
+    -webkit-writing-mode: vertical-lr;
+    margin-left: 20px;
+    display: inline-block;
+}
+
+span {
+    border-radius: 15px 10px 8px 5px;
+    outline-offset: 0px;
+    outline: auto;
+}
+
+.cover {
+    position: absolute;
+    border-radius: 15px 4px 8px 5px;
+    border: 7px solid white;
+}
+</style>
+</head>
+<body>
+<div class=cover style="top: 4px; left: 24px; width: 12px; height: 54px; border-radius: 15px 0px 0px 3px;"></div>
+<div class=cover style="top: 4px; left: 43px; width: 13px; height: 114px; border-radius: 0px 0px 0px 3px;"></div>
+<div class=cover style="top: 4px; left: 63px; width: 16px; height: 255px; border-radius: 0px 3px 12px 12px;"></div>
+
+<div class=cover style="top: 4px; left: 124px; width: 15px; height: 255px; border-radius: 15px 0px 3px 3px;"></div>
+<div class=cover style="top: 4px; left: 147px; width: 12px; height: 195px; border-radius: 0px 0px 3px 0px;"></div>
+<div class=cover style="top: 4px; left: 166px; width: 13px; height: 114px; border-radius: 0px 3px 11px 0px;"></div>
+
+<div class=cover style="top: 4px; left: 223px; width: 13px; height: 114px; border-radius: 15px 0px 0px 3px;"></div>
+<div class=cover style="top: 4px; left: 243px; width: 16px; height: 255px; border-radius: 0px 0px 3px 3px;"></div>
+<div class=cover style="top: 4px; left: 266px; width: 13px; height: 114px; border-radius: 0px 3px 11px 0px;"></div>
+
+<div class=cover style="top: 4px; left: 323px; width: 16px; height: 255px; border-radius: 15px 0px 3px 3px;"></div>
+<div class=cover style="top: 4px; left: 346px; width: 10px; height: 114px; border-radius: 0px 0px 0px 0px;"></div>
+<div class=cover style="top: 4px; left: 363px; width: 16px; height: 255px; border-radius: 0px 3px 12px 12px;"></div>
+
+<div class=container><span>foo<br>foobar<br>foobar foobar</span></div>
+<div class=container><span>foobar foobar<br>foobar foo<br>foobar</span></div>
+<div class=container><span>foobar<br>foobar foobar<br>foobar</span></div>
+<div class=container><span>foobar foobar<br>foobar<br>foobar foobar</span></div>
+</body>
+</html>

Added: trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl-expected.html (0 => 198062)


--- trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl-expected.html	2016-03-12 17:24:21 UTC (rev 198062)
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests hugging outlines for multiline vertical rtl content.</title>
+</head>
+<body></body>
+</html>

Added: trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl.html (0 => 198062)


--- trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl.html	2016-03-12 17:24:21 UTC (rev 198062)
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests hugging outlines for multiline vertical rtl content.</title>
+<style>
+body {
+    direction: rtl;
+    font-size: 20px;
+    font-family: ahem;
+    color: rgba(0, 0, 0, 0);
+}
+ 
+.container {
+    -webkit-writing-mode: vertical-lr;
+    margin-left: 20px;
+    display: inline-block;
+}
+
+span {
+    border-radius: 15px 10px 8px 5px;
+    outline-offset: 0px;
+    outline: auto;
+}
+
+.cover {
+    position: absolute;
+    border-radius: 15px 4px 8px 5px;
+    border: 7px solid white;
+}
+</style>
+</head>
+<body>
+<div class=cover style="top: 204px; right: 46px; width: 12px; height: 55px; border-radius: 3px 0px 0px 8px"></div>
+<div class=cover style="top: 143px; right: 26px; width: 13px; height: 116px; border-radius: 3px 0px 0px 0px;"></div>
+<div class=cover style="top: 4px; right: 3px; width: 16px; height: 255px; border-radius: 16px 11px 3px 0px;"></div>
+
+<div class=cover style="top: 3px; right: 143px; width: 16px; height: 255px; border-radius: 3px 3px 0px 8px;"></div>
+<div class=cover style="top: 63px; right: 123px; width: 13px; height: 195px; border-radius: 0px 3px 0px 0px;"></div>
+<div class=cover style="top: 143px; right: 103px; width: 13px; height: 115px; border-radius: 0px 12px 3px 0px;"></div>
+
+<div class=cover style="top: 143px; right: 203px; width: 13px; height: 115px; border-radius: 0px 12px 3px 0px;"></div>
+<div class=cover style="top: 3px; right: 223px; width: 16px; height: 255px; border-radius: 3px 3px 0px 0px;"></div>
+<div class=cover style="top: 143px; right: 246px; width: 13px; height: 115px; border-radius: 3px 0px 0px 3px;"></div>
+
+<div class=cover style="top: 3px; right: 303px; width: 16px; height: 255px; border-radius: 16px 12px 3px 0px;"></div>
+<div class=cover style="top: 144px; right: 326px; width: 10px; height: 114px; border-radius: 0px 0px 0px 0px;"></div>
+<div class=cover style="top: 3px; right: 343px; width: 16px; height: 255px; border-radius: 3px 3px 0px 3px;"></div>
+
+<div class=container><span>foo<br>foobar<br>foobar foobar</span></div>
+<div class=container><span>foobar foobar<br>foobar foo<br>foobar</span></div>
+<div class=container><span>foobar<br>foobar foobar<br>foobar</span></div>
+<div class=container><span>foobar foobar<br>foobar<br>foobar foobar</span></div>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (198061 => 198062)


--- trunk/Source/WebCore/ChangeLog	2016-03-12 10:51:02 UTC (rev 198061)
+++ trunk/Source/WebCore/ChangeLog	2016-03-12 17:24:21 UTC (rev 198062)
@@ -1,3 +1,42 @@
+2016-03-12  Zalan Bujtas  <[email protected]>
+
+        [Forms: focus] focus rings around text fields do not follow contour (border-radius)
+        https://bugs.webkit.org/show_bug.cgi?id=154099
+        rdar://problem/9988429
+
+        Reviewed by Tim Horton.
+
+        This patch enables outline-style: auto to follow the curve of border-radius.
+        When both border-radius and outline-style: auto are set, the native focusring painting will take the border-radius values
+        into account. This is only for outline-style: auto, other non-auto outline styles paint as if there
+        was no border-radius set.
+        It supports both single and multiline content with joint rectangles.
+        However in case of disjoint rectangles, we fallback to the non-radius drawing.
+
+        Tests: fast/inline/hidpi-outline-auto-with-border-radius-horizontal-ltr.html
+               fast/inline/hidpi-outline-auto-with-border-radius-horizontal-rtl.html
+               fast/inline/hidpi-outline-auto-with-border-radius-vertical-ltr.html
+               fast/inline/hidpi-outline-auto-with-border-radius-vertical-rtl.html
+
+        * platform/graphics/GraphicsContext.h:
+        * platform/graphics/Path.cpp:
+        (WebCore::Path::addBeziersForRoundedRect):
+        * platform/graphics/Path.h:
+        (WebCore::Path::circleControlPoint):
+        * platform/graphics/PathUtilities.cpp:
+        (WebCore::polygonsForRect):
+        (WebCore::PathUtilities::pathsWithShrinkWrappedRects):
+        (WebCore::startAndEndPointsForCorner):
+        (WebCore::cornerType):
+        (WebCore::controlPointsForBezierCurve):
+        (WebCore::adjustedtRadiiForHuggingCurve):
+        (WebCore::PathUtilities::pathWithShrinkWrappedRectsForOutline):
+        * platform/graphics/PathUtilities.h:
+        * platform/graphics/mac/GraphicsContextMac.mm:
+        (WebCore::GraphicsContext::drawFocusRing):
+        * rendering/RenderElement.cpp:
+        (WebCore::RenderElement::paintFocusRing):
+
 2016-03-11  Ryosuke Niwa  <[email protected]>
 
         Add Event.deepPath() and Event.scoped

Modified: trunk/Source/WebCore/platform/graphics/GraphicsContext.h (198061 => 198062)


--- trunk/Source/WebCore/platform/graphics/GraphicsContext.h	2016-03-12 10:51:02 UTC (rev 198061)
+++ trunk/Source/WebCore/platform/graphics/GraphicsContext.h	2016-03-12 17:24:21 UTC (rev 198062)
@@ -433,7 +433,8 @@
     void drawFocusRing(const Vector<FloatRect>&, float width, float offset, const Color&);
     void drawFocusRing(const Path&, float width, float offset, const Color&);
 #if PLATFORM(MAC)
-    void drawFocusRing(const Vector<FloatRect>&, float offset, double timeOffset, bool& needsRedraw);
+    void drawFocusRing(const Path&, double timeOffset, bool& needsRedraw);
+    void drawFocusRing(const Vector<FloatRect>&, double timeOffset, bool& needsRedraw);
 #endif
 
     void setLineCap(LineCap);

Modified: trunk/Source/WebCore/platform/graphics/Path.cpp (198061 => 198062)


--- trunk/Source/WebCore/platform/graphics/Path.cpp	2016-03-12 10:51:02 UTC (rev 198061)
+++ trunk/Source/WebCore/platform/graphics/Path.cpp	2016-03-12 17:24:21 UTC (rev 198062)
@@ -130,33 +130,29 @@
     addRoundedRect(FloatRoundedRect(r));
 }
 
-// Approximation of control point positions on a bezier to simulate a quarter of a circle.
-// This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3
-static const float gCircleControlPoint = 0.447715f;
-
 void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
 {
     moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y()));
 
     addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y()));
     if (topRightRadius.width() > 0 || topRightRadius.height() > 0)
-        addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * gCircleControlPoint, rect.y()),
-            FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * gCircleControlPoint),
+        addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * circleControlPoint(), rect.y()),
+            FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * circleControlPoint()),
             FloatPoint(rect.maxX(), rect.y() + topRightRadius.height()));
     addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height()));
     if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0)
-        addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * gCircleControlPoint),
-            FloatPoint(rect.maxX() - bottomRightRadius.width() * gCircleControlPoint, rect.maxY()),
+        addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * circleControlPoint()),
+            FloatPoint(rect.maxX() - bottomRightRadius.width() * circleControlPoint(), rect.maxY()),
             FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY()));
     addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY()));
     if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0)
-        addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, rect.maxY()),
-            FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * gCircleControlPoint),
+        addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * circleControlPoint(), rect.maxY()),
+            FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * circleControlPoint()),
             FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height()));
     addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height()));
     if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0)
-        addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * gCircleControlPoint),
-            FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, rect.y()),
+        addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * circleControlPoint()),
+            FloatPoint(rect.x() + topLeftRadius.width() * circleControlPoint(), rect.y()),
             FloatPoint(rect.x() + topLeftRadius.width(), rect.y()));
 
     closeSubpath();

Modified: trunk/Source/WebCore/platform/graphics/Path.h (198061 => 198062)


--- trunk/Source/WebCore/platform/graphics/Path.h	2016-03-12 10:51:02 UTC (rev 198061)
+++ trunk/Source/WebCore/platform/graphics/Path.h	2016-03-12 17:24:21 UTC (rev 198062)
@@ -162,6 +162,13 @@
         WEBCORE_EXPORT void apply(const PathApplierFunction&) const;
         void transform(const AffineTransform&);
 
+        static float circleControlPoint()
+        {
+            // Approximation of control point positions on a bezier to simulate a quarter of a circle.
+            // This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3
+            return 0.447715;
+        }
+
         void addBeziersForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius);
 
 #if USE(CG)

Modified: trunk/Source/WebCore/platform/graphics/PathUtilities.cpp (198061 => 198062)


--- trunk/Source/WebCore/platform/graphics/PathUtilities.cpp	2016-03-12 10:51:02 UTC (rev 198061)
+++ trunk/Source/WebCore/platform/graphics/PathUtilities.cpp	2016-03-12 17:24:21 UTC (rev 198062)
@@ -28,8 +28,10 @@
 #include "PathUtilities.h"
 
 #include "AffineTransform.h"
+#include "BorderData.h"
 #include "FloatPoint.h"
 #include "FloatRect.h"
+#include "FloatRoundedRect.h"
 #include "GeometryUtilities.h"
 #include <math.h>
 #include <wtf/MathExtras.h>
@@ -255,25 +257,11 @@
     });
 }
 
-Vector<Path> PathUtilities::pathsWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius)
+static Vector<FloatPointGraph::Polygon> polygonsForRect(const Vector<FloatRect>& rects, FloatPointGraph& graph)
 {
-    Vector<Path> paths;
-
-    if (rects.isEmpty())
-        return paths;
-
-    if (rects.size() > 20) {
-        Path path;
-        path.addRoundedRect(unionRect(rects), FloatSize(radius, radius));
-        paths.append(path);
-        return paths;
-    }
-
     Vector<FloatRect> sortedRects = rects;
-
     std::sort(sortedRects.begin(), sortedRects.end(), [](FloatRect a, FloatRect b) { return b.y() > a.y(); });
 
-    FloatPointGraph graph;
     Vector<FloatPointGraph::Polygon> rectPolygons;
     rectPolygons.reserveInitialCapacity(sortedRects.size());
 
@@ -291,12 +279,28 @@
         if (!isContained)
             rectPolygons.append(edgesForRect(rect, graph));
     }
+    return unitePolygons(rectPolygons, graph);
+}
 
-    Vector<FloatPointGraph::Polygon> polys = unitePolygons(rectPolygons, graph);
+Vector<Path> PathUtilities::pathsWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius)
+{
+    Vector<Path> paths;
 
+    if (rects.isEmpty())
+        return paths;
+
+    if (rects.size() > 20) {
+        Path path;
+        path.addRoundedRect(unionRect(rects), FloatSize(radius, radius));
+        paths.append(path);
+        return paths;
+    }
+
+    FloatPointGraph graph;
+    Vector<FloatPointGraph::Polygon> polys = polygonsForRect(rects, graph);
     if (polys.isEmpty()) {
         Path path;
-        path.addRoundedRect(unionRect(sortedRects), FloatSize(radius, radius));
+        path.addRoundedRect(unionRect(rects), FloatSize(radius, radius));
         paths.append(path);
         return paths;
     }
@@ -331,11 +335,9 @@
                 path.addLineTo(*fromEdge.second - fromOffset);
             path.addArcTo(*fromEdge.second, *toEdge.first + toOffset, clampedRadius);
         }
-
         path.closeSubpath();
         paths.append(path);
     }
-
     return paths;
 }
 
@@ -350,4 +352,193 @@
     return unionPath;
 }
 
+static std::pair<FloatPoint, FloatPoint> startAndEndPointsForCorner(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge, const FloatSize& radius)
+{
+    FloatPoint startPoint;
+    FloatPoint endPoint;
+    
+    FloatSize fromEdgeVector = *fromEdge.second - *fromEdge.first;
+    FloatSize toEdgeVector = *toEdge.second - *toEdge.first;
+
+    FloatPoint fromEdgeNorm = toFloatPoint(fromEdgeVector);
+    fromEdgeNorm.normalize();
+    FloatSize fromOffset = FloatSize(radius.width() * fromEdgeNorm.x(), radius.height() * fromEdgeNorm.y());
+    startPoint = *fromEdge.second - fromOffset;
+
+    FloatPoint toEdgeNorm = toFloatPoint(toEdgeVector);
+    toEdgeNorm.normalize();
+    FloatSize toOffset = FloatSize(radius.width() * toEdgeNorm.x(), radius.height() * toEdgeNorm.y());
+    endPoint = *toEdge.first + toOffset;
+    return std::make_pair(startPoint, endPoint);
 }
+
+enum class CornerType { TopLeft, TopRight, BottomRight, BottomLeft, Other };
+static CornerType cornerType(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge, const Vector<FloatPoint>& corners)
+{
+    auto fromEdgeVector = *fromEdge.second - *fromEdge.first;
+    auto toEdgeVector = *toEdge.second - *toEdge.first;
+
+    if (fromEdgeVector.height() < 0 && toEdgeVector.width() > 0 && corners.at(0) == *fromEdge.second)
+        return CornerType::TopLeft;
+    if (fromEdgeVector.width() > 0 && toEdgeVector.height() > 0 && corners.at(1) == *fromEdge.second)
+        return CornerType::TopRight;
+    if (fromEdgeVector.height() > 0 && toEdgeVector.width() < 0 && corners.at(2) == *fromEdge.second)
+        return CornerType::BottomRight;
+    if (fromEdgeVector.width() < 0 && toEdgeVector.height() < 0 && corners.at(3) == *fromEdge.second)
+        return CornerType::BottomLeft;
+    return CornerType::Other;
+}
+
+static std::pair<FloatPoint, FloatPoint> controlPointsForBezierCurve(CornerType cornerType, const FloatPointGraph::Edge& fromEdge,
+    const FloatPointGraph::Edge& toEdge, const FloatSize& radius)
+{
+    FloatPoint cp1;
+    FloatPoint cp2;
+    switch (cornerType) {
+    case CornerType::TopLeft: {
+        cp1 = FloatPoint(fromEdge.second->x(), fromEdge.second->y() + radius.height() * Path::circleControlPoint());
+        cp2 = FloatPoint(toEdge.first->x() + radius.width() * Path::circleControlPoint(), toEdge.first->y());
+        break;
+    }
+    case CornerType::TopRight: {
+        cp1 = FloatPoint(fromEdge.second->x() - radius.width() * Path::circleControlPoint(), fromEdge.second->y());
+        cp2 = FloatPoint(toEdge.first->x(), toEdge.first->y() + radius.height() * Path::circleControlPoint());
+        break;
+    }
+    case CornerType::BottomRight: {
+        cp1 = FloatPoint(fromEdge.second->x(), fromEdge.second->y() - radius.height() * Path::circleControlPoint());
+        cp2 = FloatPoint(toEdge.first->x() - radius.width() * Path::circleControlPoint(), toEdge.first->y());
+        break;
+    }
+    case CornerType::BottomLeft: {
+        cp1 = FloatPoint(fromEdge.second->x() + radius.width() * Path::circleControlPoint(), fromEdge.second->y());
+        cp2 = FloatPoint(toEdge.first->x(), toEdge.first->y() - radius.height() * Path::circleControlPoint());
+        break;
+    }
+    case CornerType::Other: {
+        ASSERT_NOT_REACHED();
+        break;
+    }
+    }
+    return std::make_pair(cp1, cp2);
+}
+
+static FloatRoundedRect::Radii adjustedtRadiiForHuggingCurve(const FloatSize& topLeftRadius, const FloatSize& topRightRadius,
+    const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, float outlineOffset)
+{
+    FloatRoundedRect::Radii radii;
+    // This adjusts the radius so that it follows the border curve even when offset is present.
+    auto adjustedRadius = [outlineOffset](const FloatSize& radius)
+    {
+        FloatSize adjustedRadius = radius;
+        if (radius.width() > outlineOffset)
+            adjustedRadius.expand(std::min(outlineOffset, radius.width() - outlineOffset), 0);
+        if (radius.height() > outlineOffset)
+            adjustedRadius.expand(0, std::min(outlineOffset, radius.height() - outlineOffset));
+        return adjustedRadius;
+    };
+
+    radii.setTopLeft(adjustedRadius(topLeftRadius));
+    radii.setTopRight(adjustedRadius(topRightRadius));
+    radii.setBottomRight(adjustedRadius(bottomRightRadius));
+    radii.setBottomLeft(adjustedRadius(bottomLeftRadius));
+    return radii;
+}
+    
+Path PathUtilities::pathWithShrinkWrappedRectsForOutline(const Vector<FloatRect>& rects, const BorderData& borderData, float outlineOffset, TextDirection direction,
+    WritingMode writingMode)
+{
+    ASSERT(borderData.hasBorderRadius());
+    FloatSize topLeftRadius = FloatSize(borderData.topLeft().width().value(), borderData.topLeft().height().value());
+    FloatSize topRightRadius = FloatSize(borderData.topRight().width().value(), borderData.topRight().height().value());
+    FloatSize bottomRightRadius = FloatSize(borderData.bottomRight().width().value(), borderData.bottomRight().height().value());
+    FloatSize bottomLeftRadius = FloatSize(borderData.bottomLeft().width().value(), borderData.bottomLeft().height().value());
+    if (rects.size() == 1) {
+        FloatRect rect = rects.at(0);
+        auto radii = adjustedtRadiiForHuggingCurve(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, outlineOffset);
+        radii.scale(calcBorderRadiiConstraintScaleFor(rect, radii));
+
+        Path path;
+        path.addRoundedRect(FloatRoundedRect(rect, radii));
+        return path;
+    }
+
+    FloatPointGraph graph;
+    const auto polys = polygonsForRect(rects, graph);
+    // Fall back to corner painting with no radius for empty and disjoint rectangles.
+    if (!polys.size() || polys.size() > 1)
+        return Path();
+    Path path;
+    // Multiline outline needs to match multiline border painting. Only first and last lines are getting rounded borders.
+    auto isLeftToRight = isLeftToRightDirection(direction);
+    auto firstLineRect = isLeftToRight ? rects.at(0) : rects.at(rects.size() - 1);
+    auto lastLineRect = isLeftToRight ? rects.at(rects.size() - 1) : rects.at(0);
+    // Adjust radius so that it matches the box border.
+    auto firstLineRadii = FloatRoundedRect::Radii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+    auto lastLineRadii = FloatRoundedRect::Radii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+    firstLineRadii.scale(calcBorderRadiiConstraintScaleFor(firstLineRect, firstLineRadii));
+    lastLineRadii.scale(calcBorderRadiiConstraintScaleFor(lastLineRect, lastLineRadii));
+    topLeftRadius = firstLineRadii.topLeft();
+    bottomLeftRadius = firstLineRadii.bottomLeft();
+    topRightRadius = lastLineRadii.topRight();
+    bottomRightRadius = lastLineRadii.bottomRight();
+    Vector<FloatPoint> corners;
+    // physical topLeft/topRight/bottomRight/bottomLeft
+    auto isHorizontal = isHorizontalWritingMode(writingMode);
+    corners.append(firstLineRect.minXMinYCorner());
+    corners.append(isHorizontal ? lastLineRect.maxXMinYCorner() : firstLineRect.maxXMinYCorner());
+    corners.append(lastLineRect.maxXMaxYCorner());
+    corners.append(isHorizontal ? firstLineRect.minXMaxYCorner() : lastLineRect.minXMaxYCorner());
+
+    const auto& poly = polys.at(0);
+    for (unsigned i = 0; i < poly.size(); i++) {
+        auto moveOrAddLineTo = [i, &path] (const FloatPoint& startPoint)
+        {
+            if (!i)
+                path.moveTo(startPoint);
+            else
+                path.addLineTo(startPoint);
+        };
+        const auto& toEdge = poly[i];
+        const auto& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1];
+        FloatSize radius;
+        auto corner = cornerType(fromEdge, toEdge, corners);
+        switch (corner) {
+        case CornerType::TopLeft: {
+            radius = topLeftRadius;
+            break;
+        }
+        case CornerType::TopRight: {
+            radius = topRightRadius;
+            break;
+        }
+        case CornerType::BottomRight: {
+            radius = bottomRightRadius;
+            break;
+        }
+        case CornerType::BottomLeft: {
+            radius = bottomLeftRadius;
+            break;
+        }
+        case CornerType::Other: {
+            // Do not apply border radius on corners that normal border painting skips. (multiline content)
+            moveOrAddLineTo(*fromEdge.second);
+            continue;
+        }
+        }
+        FloatPoint startPoint;
+        FloatPoint endPoint;
+        std::tie(startPoint, endPoint) = startAndEndPointsForCorner(fromEdge, toEdge, radius);
+        moveOrAddLineTo(startPoint);
+
+        FloatPoint cp1;
+        FloatPoint cp2;
+        std::tie(cp1, cp2) = controlPointsForBezierCurve(corner, fromEdge, toEdge, radius);
+        path.addBezierCurveTo(cp1, cp2, endPoint);
+    }
+    path.closeSubpath();
+    return path;
+}
+
+
+}

Modified: trunk/Source/WebCore/platform/graphics/PathUtilities.h (198061 => 198062)


--- trunk/Source/WebCore/platform/graphics/PathUtilities.h	2016-03-12 10:51:02 UTC (rev 198061)
+++ trunk/Source/WebCore/platform/graphics/PathUtilities.h	2016-03-12 17:24:21 UTC (rev 198062)
@@ -27,14 +27,18 @@
 #define PathUtilities_h
 
 #include "Path.h"
+#include "WritingMode.h"
 #include <wtf/Vector.h>
 
 namespace WebCore {
-
+class BorderData;
+    
 class PathUtilities {
 public:
     WEBCORE_EXPORT static Path pathWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius);
     WEBCORE_EXPORT static Vector<Path> pathsWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius);
+
+    static Path pathWithShrinkWrappedRectsForOutline(const Vector<FloatRect>&, const BorderData&, float outlineOffset, TextDirection, WritingMode);
 };
 
 }

Modified: trunk/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm (198061 => 198062)


--- trunk/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm	2016-03-12 10:51:02 UTC (rev 198061)
+++ trunk/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm	2016-03-12 17:24:21 UTC (rev 198062)
@@ -85,14 +85,22 @@
 }
 
 #if PLATFORM(MAC)
-void GraphicsContext::drawFocusRing(const Vector<FloatRect>& rects, float offset, double timeOffset, bool& needsRedraw)
+void GraphicsContext::drawFocusRing(const Path& path, double timeOffset, bool& needsRedraw)
 {
+    if (paintingDisabled() || path.isNull())
+        return;
+    
+    needsRedraw = drawFocusRingToContextAtTime(platformContext(), path.platformPath(), timeOffset);
+}
+
+void GraphicsContext::drawFocusRing(const Vector<FloatRect>& rects, double timeOffset, bool& needsRedraw)
+{
     if (paintingDisabled())
         return;
 
     RetainPtr<CGMutablePathRef> focusRingPath = adoptCF(CGPathCreateMutable());
-    for (auto& rect : rects)
-        CGPathAddRect(focusRingPath.get(), 0, CGRectInset(rect, -offset, -offset));
+    for (const auto& rect : rects)
+        CGPathAddRect(focusRingPath.get(), 0, CGRect(rect));
 
     needsRedraw = drawFocusRingToContextAtTime(platformContext(), focusRingPath.get(), timeOffset);
 }

Modified: trunk/Source/WebCore/rendering/RenderElement.cpp (198061 => 198062)


--- trunk/Source/WebCore/rendering/RenderElement.cpp	2016-03-12 10:51:02 UTC (rev 198061)
+++ trunk/Source/WebCore/rendering/RenderElement.cpp	2016-03-12 17:24:21 UTC (rev 198062)
@@ -38,6 +38,7 @@
 #include "HTMLHtmlElement.h"
 #include "HTMLNames.h"
 #include "FlowThreadController.h"
+#include "PathUtilities.h"
 #include "RenderBlock.h"
 #include "RenderCounter.h"
 #include "RenderDeprecatedFlexibleBox.h"
@@ -2087,13 +2088,24 @@
 
     Vector<LayoutRect> focusRingRects;
     addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer);
+    float outlineOffset = style.outlineOffset();
     Vector<FloatRect> pixelSnappedFocusRingRects;
     float deviceScaleFactor = document().deviceScaleFactor();
-    for (const auto& rect : focusRingRects)
+    for (auto rect : focusRingRects) {
+        rect.inflate(outlineOffset);
         pixelSnappedFocusRingRects.append(snapRectToDevicePixels(rect, deviceScaleFactor));
+    }
 #if PLATFORM(MAC)
     bool needsRepaint;
-    paintInfo.context().drawFocusRing(pixelSnappedFocusRingRects, style.outlineOffset(), document().page()->focusController().timeSinceFocusWasSet(), needsRepaint);
+    if (style.hasBorderRadius()) {
+        Path path = PathUtilities::pathWithShrinkWrappedRectsForOutline(pixelSnappedFocusRingRects, style.border(), outlineOffset, style.direction(), style.writingMode());
+        if (path.isEmpty()) {
+            for (auto rect : pixelSnappedFocusRingRects)
+                path.addRect(rect);
+        }
+        paintInfo.context().drawFocusRing(path, document().page()->focusController().timeSinceFocusWasSet(), needsRepaint);
+    } else
+        paintInfo.context().drawFocusRing(pixelSnappedFocusRingRects, document().page()->focusController().timeSinceFocusWasSet(), needsRepaint);
     if (needsRepaint)
         document().page()->focusController().setFocusedElementNeedsRepaint();
 #else
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to