Index: Source/WebCore/platform/graphics/cg/PathCG.cpp |
=================================================================== |
--- Source/WebCore/platform/graphics/cg/PathCG.cpp (revision 104173) |
+++ Source/WebCore/platform/graphics/cg/PathCG.cpp (working copy) |
@@ -41,6 +41,59 @@ |
namespace WebCore { |
+// A class to provide an isEmpty test that considers a one-element path with only a MoveTo element |
+// to be empty. This behavior is consistent with other platforms in WebKit, and is needed to prevent |
+// incorrect (according to the spec) linecap stroking for zero length paths in SVG. |
+class PathIsEmptyOrSingleMoveTester { |
+public: |
+ PathIsEmptyOrSingleMoveTester() : m_moveCount(0) { } |
+ |
+ bool isEmpty() const |
+ { |
+ return m_moveCount <= 1; |
+ } |
+ |
+ static void testPathElement(void* info, const CGPathElement* element) |
+ { |
+ PathIsEmptyOrSingleMoveTester* tester = static_cast<PathIsEmptyOrSingleMoveTester*>(info); |
+ if (element->type == kCGPathElementMoveToPoint) |
+ ++tester->m_moveCount; |
+ else { |
+ // Any non move element implies a non-empty path; set the count to 2 to force |
+ // isEmpty to return false. |
+ tester->m_moveCount = 2; |
+ } |
+ } |
+ |
+private: |
+ // Any non-move-to element, or more than one move-to element, will make the count >= 2. |
+ unsigned m_moveCount; |
+}; |
+ |
+// Paths with only move-to elements do not draw under any circumstances, so their bound should |
+// be empty. Currently, CoreGraphics returns non-empty bounds for such paths. Radar 10450621 |
+// tracks this. This class reports paths that have only move-to elements, allowing the |
+// bounding box code to work around the CoreGraphics problem. |
+class PathHasOnlyMoveToTester { |
+public: |
+ PathHasOnlyMoveToTester() : m_hasSeenOnlyMoveTo(true) { } |
+ |
+ bool hasOnlyMoveTo() const |
+ { |
+ return m_hasSeenOnlyMoveTo; |
+ } |
+ |
+ static void testPathElement(void* info, const CGPathElement* element) |
+ { |
+ PathHasOnlyMoveToTester* tester = static_cast<PathHasOnlyMoveToTester*>(info); |
+ if (tester->m_hasSeenOnlyMoveTo && element->type != kCGPathElementMoveToPoint) |
+ tester->m_hasSeenOnlyMoveTo = false; |
+ } |
+ |
+private: |
+ bool m_hasSeenOnlyMoveTo; |
+}; |
+ |
static size_t putBytesNowhere(void*, const void*, size_t count) |
{ |
return count; |
@@ -165,6 +218,14 @@ |
{ |
// CGPathGetBoundingBox includes the path's control points, CGPathGetPathBoundingBox |
// does not, but only exists on 10.6 and above. |
+ // A bug in CoreGraphics leads to an incorrect bound on paths containing only move-to elements |
+ // with a linecap style that is non-butt. All paths with only move-to elements (regardless of |
+ // linecap) are effectively empty for bounding purposes and here we make it so. |
+ PathHasOnlyMoveToTester tester; |
+ CGPathApply(m_path, &tester, PathHasOnlyMoveToTester::testPathElement); |
+ if (tester.hasOnlyMoveTo()) |
+ return FloatRect(0, 0, 0, 0); |
+ |
#if !defined(BUILDING_ON_LEOPARD) |
return CGPathGetPathBoundingBox(m_path); |
#else |
@@ -174,6 +235,14 @@ |
FloatRect Path::fastBoundingRect() const |
{ |
+ // A bug in CoreGraphics leads to an incorrect bound on paths containing only move-to elements |
+ // with a linecap style that is non-butt. All paths with only move-to elements (regardless of |
+ // linecap) are effectively empty for bounding purposes and here we make it so. |
+ PathHasOnlyMoveToTester tester; |
+ CGPathApply(m_path, &tester, PathHasOnlyMoveToTester::testPathElement); |
+ if (tester.hasOnlyMoveTo()) |
+ return FloatRect(0, 0, 0, 0); |
+ |
return CGPathGetBoundingBox(m_path); |
} |
@@ -252,12 +321,18 @@ |
bool Path::isEmpty() const |
{ |
- return CGPathIsEmpty(m_path); |
+ // The SVG rendering code that uses this method relies on paths with a single move-to |
+ // element, and nothing else, as being empty. Until that code is refactored to avoid |
+ // the dependence on isEmpty, we match the behavior of other platforms. |
+ // When the SVG code is refactored, we could use CGPathIsEmpty(m_path); |
+ PathIsEmptyOrSingleMoveTester tester; |
+ CGPathApply(m_path, &tester, PathIsEmptyOrSingleMoveTester::testPathElement); |
+ return tester.isEmpty(); |
} |
bool Path::hasCurrentPoint() const |
{ |
- return !isEmpty(); |
+ return !CGPathIsEmpty(m_path); |
} |
FloatPoint Path::currentPoint() const |
@@ -311,7 +386,7 @@ |
void Path::transform(const AffineTransform& transform) |
{ |
- if (transform.isIdentity() || isEmpty()) |
+ if (transform.isIdentity() || CGPathIsEmpty(m_path)) |
return; |
CGMutablePathRef path = CGPathCreateMutable(); |