SVG 1.1: ineffectual transform attribute for ClipPath
authorkrit@webkit.org <krit@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 5 Oct 2011 16:20:32 +0000 (16:20 +0000)
committerkrit@webkit.org <krit@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 5 Oct 2011 16:20:32 +0000 (16:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=55361

Source/WebCore:

Reviewed by Nikolas Zimmermann.

Respect 'transform' attribute/property for <clip-path>.
If the masking code path is used the mask context gets transformed, otherwise the path itself.

Tests: svg/clip-path/clip-path-transform-1.svg
       svg/clip-path/clip-path-transform-2.svg
       svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting.html

* rendering/svg/RenderSVGResourceClipper.cpp:
(WebCore::RenderSVGResourceClipper::pathOnlyClipping): Transform the clip path.
(WebCore::RenderSVGResourceClipper::applyClippingToContext):
(WebCore::RenderSVGResourceClipper::drawContentIntoMaskImage): Transform the context of the mask image.
(WebCore::RenderSVGResourceClipper::calculateClipContentRepaintRect): Repaint rect must get concatenated with the current animated transformation.
(WebCore::RenderSVGResourceClipper::hitTestClipContent): Point for hit testing must be transformed by the current animated transformation.
* rendering/svg/RenderSVGResourceClipper.h:

LayoutTests:

Reviewed by Nikolas Zimmermann.

Test that the clip path gets concatenated with the current animated transformation and
that hit testing is affected correctly.

* platform/mac/svg/clip-path/clip-path-transform-1-expected.png: Added.
* platform/mac/svg/clip-path/clip-path-transform-1-expected.txt: Added.
* platform/mac/svg/clip-path/clip-path-transform-2-expected.png: Added.
* platform/mac/svg/clip-path/clip-path-transform-2-expected.txt: Added.
* svg/clip-path/clip-path-transform-1.svg: Added. Test clipping to path with transform.
* svg/clip-path/clip-path-transform-2.svg: Added. Test masking with transform.
* svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.png: Added.
* svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.txt: Added.
* svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting.html: Added.
* svg/dynamic-updates/script-tests/SVGClipPathElement-transform-influences-hitTesting.js: Added.
(executeBackgroundTest):
(executeTest):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@96712 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/mac/svg/clip-path/clip-path-transform-1-expected.png [new file with mode: 0644]
LayoutTests/platform/mac/svg/clip-path/clip-path-transform-1-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/svg/clip-path/clip-path-transform-2-expected.png [new file with mode: 0644]
LayoutTests/platform/mac/svg/clip-path/clip-path-transform-2-expected.txt [new file with mode: 0644]
LayoutTests/svg/clip-path/clip-path-transform-1.svg [new file with mode: 0644]
LayoutTests/svg/clip-path/clip-path-transform-2.svg [new file with mode: 0644]
LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.png [new file with mode: 0644]
LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.txt [new file with mode: 0644]
LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting.html [new file with mode: 0644]
LayoutTests/svg/dynamic-updates/script-tests/SVGClipPathElement-transform-influences-hitTesting.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp
Source/WebCore/rendering/svg/RenderSVGResourceClipper.h

index 1411ea0..ccd8852 100644 (file)
@@ -1,3 +1,26 @@
+2011-10-05  Dirk Schulze  <krit@webkit.org>
+
+        SVG 1.1: ineffectual transform attribute for ClipPath
+        https://bugs.webkit.org/show_bug.cgi?id=55361
+
+        Reviewed by Nikolas Zimmermann.
+        
+        Test that the clip path gets concatenated with the current animated transformation and
+        that hit testing is affected correctly.
+
+        * platform/mac/svg/clip-path/clip-path-transform-1-expected.png: Added.
+        * platform/mac/svg/clip-path/clip-path-transform-1-expected.txt: Added.
+        * platform/mac/svg/clip-path/clip-path-transform-2-expected.png: Added.
+        * platform/mac/svg/clip-path/clip-path-transform-2-expected.txt: Added.
+        * svg/clip-path/clip-path-transform-1.svg: Added. Test clipping to path with transform.
+        * svg/clip-path/clip-path-transform-2.svg: Added. Test masking with transform.
+        * svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.png: Added.
+        * svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.txt: Added.
+        * svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting.html: Added.
+        * svg/dynamic-updates/script-tests/SVGClipPathElement-transform-influences-hitTesting.js: Added.
+        (executeBackgroundTest):
+        (executeTest):
+
 2011-10-05  Pavel Feldman  <pfeldman@google.com>
 
         Web Inspector: move elements panel update semantics from ElementsPanel to ElementsTreeOutline.
diff --git a/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-1-expected.png b/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-1-expected.png
new file mode 100644 (file)
index 0000000..a7dbe13
Binary files /dev/null and b/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-1-expected.png differ
diff --git a/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-1-expected.txt b/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-1-expected.txt
new file mode 100644 (file)
index 0000000..f10265e
--- /dev/null
@@ -0,0 +1,12 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderSVGRoot {svg} at (20,20) size 200x200
+    RenderSVGHiddenContainer {defs} at (0,0) size 0x0
+      RenderSVGResourceClipper {clipPath} [id="clip"] [clipPathUnits=userSpaceOnUse]
+        RenderSVGPath {circle} at (0,0) size 20x20 [fill={[type=SOLID] [color=#000000]}] [cx=10.00] [cy=10.00] [r=10.00]
+        RenderSVGPath {circle} at (0,0) size 20x20 [fill={[type=SOLID] [color=#000000]}] [cx=10.00] [cy=10.00] [r=10.00]
+    RenderSVGPath {circle} at (21,21) size 198x198 [fill={[type=SOLID] [color=#FF0000]}] [cx=120.00] [cy=120.00] [r=99.00]
+    RenderSVGContainer {a} at (20,20) size 200x200
+      RenderSVGPath {rect} at (20,20) size 200x200 [fill={[type=SOLID] [color=#008000]}] [x=0.00] [y=0.00] [width=220.00] [height=220.00]
+        [clipPath="clip"] RenderSVGResourceClipper {clipPath} at (20,20) size 200x200
diff --git a/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-2-expected.png b/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-2-expected.png
new file mode 100644 (file)
index 0000000..0bfd587
Binary files /dev/null and b/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-2-expected.png differ
diff --git a/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-2-expected.txt b/LayoutTests/platform/mac/svg/clip-path/clip-path-transform-2-expected.txt
new file mode 100644 (file)
index 0000000..6ad2175
--- /dev/null
@@ -0,0 +1,11 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderSVGRoot {svg} at (20,20) size 200x200
+    RenderSVGHiddenContainer {defs} at (0,0) size 0x0
+      RenderSVGResourceClipper {clipPath} [id="clip"] [clipPathUnits=userSpaceOnUse]
+        RenderSVGPath {circle} at (0,0) size 20x20 [fill={[type=SOLID] [color=#000000]}] [cx=10.00] [cy=10.00] [r=10.00]
+    RenderSVGPath {circle} at (21,21) size 198x198 [fill={[type=SOLID] [color=#FF0000]}] [cx=120.00] [cy=120.00] [r=99.00]
+    RenderSVGContainer {a} at (20,20) size 200x200
+      RenderSVGPath {rect} at (20,20) size 200x200 [fill={[type=SOLID] [color=#008000]}] [x=0.00] [y=0.00] [width=220.00] [height=220.00]
+        [clipPath="clip"] RenderSVGResourceClipper {clipPath} at (20,20) size 200x200
diff --git a/LayoutTests/svg/clip-path/clip-path-transform-1.svg b/LayoutTests/svg/clip-path/clip-path-transform-1.svg
new file mode 100644 (file)
index 0000000..0495fcd
--- /dev/null
@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<defs>
+<clipPath id="clip" clipPathUnits="userSpaceOnUse" transform="scale(10) translate(2, 2)">
+    <circle cx="10" cy="10" r="10"/>
+    <!-- second rect causes masking -->
+    <circle cx="10" cy="10" r="10"/>
+</clipPath>
+</defs>
+<circle cx="120" cy="120" r="99" fill="red"/>
+<a xlink:href="#"><rect width="220" height="220" fill="green" clip-path="url(#clip)"/></a>
+</svg>
\ No newline at end of file
diff --git a/LayoutTests/svg/clip-path/clip-path-transform-2.svg b/LayoutTests/svg/clip-path/clip-path-transform-2.svg
new file mode 100644 (file)
index 0000000..46126ed
--- /dev/null
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<defs>
+<clipPath id="clip" clipPathUnits="userSpaceOnUse" transform="scale(10) translate(2, 2) ">
+    <circle cx="10" cy="10" r="10"/>
+</clipPath>
+</defs>
+<circle cx="120" cy="120" r="99" fill="red"/>
+<a xlink:href="#"><rect width="220" height="220" fill="green" clip-path="url(#clip)"/></a>
+</svg>
\ No newline at end of file
diff --git a/LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.png b/LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.png
new file mode 100644 (file)
index 0000000..2021fb0
Binary files /dev/null and b/LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.png differ
diff --git a/LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.txt b/LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting-expected.txt
new file mode 100644 (file)
index 0000000..fae0587
--- /dev/null
@@ -0,0 +1,13 @@
+SVG 1.1 dynamic update tests
+
+Tests hitTesting on clipped Elements. Clip-path gets transformed.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Hit thrown on not clipped area of rect.
+PASS Hit thrown on not clipped area of rect.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting.html b/LayoutTests/svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting.html
new file mode 100644 (file)
index 0000000..b860cde
--- /dev/null
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="resources/SVGTestCase.js"></script>
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<h1>SVG 1.1 dynamic update tests</h1>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/SVGClipPathElement-transform-influences-hitTesting.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/svg/dynamic-updates/script-tests/SVGClipPathElement-transform-influences-hitTesting.js b/LayoutTests/svg/dynamic-updates/script-tests/SVGClipPathElement-transform-influences-hitTesting.js
new file mode 100644 (file)
index 0000000..6aafaff
--- /dev/null
@@ -0,0 +1,44 @@
+// [Name] SVGClipPathElement-transform-influences-hitTesting.js
+// [Expected rendering result] green circle - and a series of PASS messages
+
+description("Tests hitTesting on clipped Elements. Clip-path gets transformed.")
+createSVGTestCase();
+
+var defsElement = createSVGElement("defs");
+rootSVGElement.appendChild(defsElement);
+
+var clipPathElement = createSVGElement("clipPath");
+clipPathElement.setAttribute("id", "clipper");
+
+var clipRectElement = createSVGElement("rect");
+clipRectElement.setAttribute("width", "5");
+clipRectElement.setAttribute("height", "5");
+clipRectElement.setAttribute("transform", "scale(20)");
+clipPathElement.appendChild(clipRectElement);
+
+defsElement.appendChild(clipPathElement);
+
+var foregroundRect = createSVGElement("rect");
+foregroundRect.setAttribute("width", "100");
+foregroundRect.setAttribute("height", "100");
+foregroundRect.setAttribute("fill", "green");
+foregroundRect.setAttribute("clip-path", "url(#clipper)");
+foregroundRect.setAttribute("onclick", "executeBackgroundTest()");
+rootSVGElement.appendChild(foregroundRect);
+
+// The clipPath gets scaled by 20. This should influence the hit testing,
+// since the area of the clipped content is affected as well. 
+function executeBackgroundTest() {
+    window.setTimeout("triggerUpdate(75,50)", 0);
+    startTest(foregroundRect, 25, 50);
+}
+
+function executeTest() {
+    testPassed("Hit thrown on not clipped area of rect.");
+
+    completeTest();
+}
+
+executeBackgroundTest();
+
+var successfullyParsed = true;
index 666a3c2..ae03469 100644 (file)
@@ -1,3 +1,25 @@
+2011-10-05  Dirk Schulze  <krit@webkit.org>
+
+        SVG 1.1: ineffectual transform attribute for ClipPath
+        https://bugs.webkit.org/show_bug.cgi?id=55361
+
+        Reviewed by Nikolas Zimmermann.
+        
+        Respect 'transform' attribute/property for <clip-path>.
+        If the masking code path is used the mask context gets transformed, otherwise the path itself.
+
+        Tests: svg/clip-path/clip-path-transform-1.svg
+               svg/clip-path/clip-path-transform-2.svg
+               svg/dynamic-updates/SVGClipPathElement-transform-influences-hitTesting.html
+
+        * rendering/svg/RenderSVGResourceClipper.cpp:
+        (WebCore::RenderSVGResourceClipper::pathOnlyClipping): Transform the clip path.
+        (WebCore::RenderSVGResourceClipper::applyClippingToContext):
+        (WebCore::RenderSVGResourceClipper::drawContentIntoMaskImage): Transform the context of the mask image.
+        (WebCore::RenderSVGResourceClipper::calculateClipContentRepaintRect): Repaint rect must get concatenated with the current animated transformation.
+        (WebCore::RenderSVGResourceClipper::hitTestClipContent): Point for hit testing must be transformed by the current animated transformation.
+        * rendering/svg/RenderSVGResourceClipper.h:
+
 2011-10-05  Pavel Feldman  <pfeldman@google.com>
 
         Web Inspector: move elements panel update semantics from ElementsPanel to ElementsTreeOutline.
index 22b41fa..f0b11c2 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org>
  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ * Copyright (C) 2011 Dirk Schulze <krit@webkit.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -104,7 +105,7 @@ bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*,
     return applyClippingToContext(object, object->objectBoundingBox(), object->repaintRectInLocalCoordinates(), context);
 }
 
-bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const FloatRect& objectBoundingBox)
+bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox)
 {
     // If the current clip-path gets clipped itself, we have to fallback to masking.
     if (!style()->svgStyle()->clipperResource().isEmpty())
@@ -148,6 +149,10 @@ bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const
         transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
         clipPath.transform(transform);
     }
+
+    // Transform path by animatedLocalTransform.
+    clipPath.transform(animatedLocalTransform);
+
     // The SVG specification wants us to clip everything, if clip-path doesn't have a child.
     if (clipPath.isEmpty())
         clipPath.addRect(FloatRect());
@@ -162,9 +167,10 @@ bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, cons
         m_clipper.set(object, new ClipperData);
 
     bool shouldCreateClipData = false;
+    AffineTransform animatedLocalTransform = static_cast<SVGClipPathElement*>(node())->animatedLocalTransform();
     ClipperData* clipperData = m_clipper.get(object);
     if (!clipperData->clipMaskImage) {
-        if (pathOnlyClipping(context, objectBoundingBox))
+        if (pathOnlyClipping(context, animatedLocalTransform, objectBoundingBox))
             return true;
         shouldCreateClipData = true;
     }
@@ -185,6 +191,7 @@ bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, cons
         // The save/restore pair is needed for clipToImageBuffer - it doesn't work without it on non-Cg platforms.
         GraphicsContextStateSaver stateSaver(*maskContext);
         maskContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
+        maskContext->concatCTM(animatedLocalTransform);
         maskContext->concatCTM(absoluteTransform);
 
         // clipPath can also be clipped by another clipPath.
@@ -254,9 +261,9 @@ bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData
         svgStyle->setFillPaint(SVGRenderStyle::initialFillPaintType(), SVGRenderStyle::initialFillPaintColor(), SVGRenderStyle::initialFillPaintUri());
         svgStyle->setStrokePaint(SVGRenderStyle::initialStrokePaintType(), SVGRenderStyle::initialStrokePaintColor(), SVGRenderStyle::initialStrokePaintUri());
         svgStyle->setFillRule(newClipRule);
-        newRenderStyle.get()->setOpacity(1.0f);
-        svgStyle->setFillOpacity(1.0f);
-        svgStyle->setStrokeOpacity(1.0f);
+        newRenderStyle.get()->setOpacity(1);
+        svgStyle->setFillOpacity(1);
+        svgStyle->setStrokeOpacity(1);
         svgStyle->setFilterResource(String());
         svgStyle->setMaskerResource(String());
 
@@ -291,6 +298,7 @@ void RenderSVGResourceClipper::calculateClipContentRepaintRect()
              continue;
         m_clipBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
     }
+    m_clipBoundaries = static_cast<SVGClipPathElement*>(node())->animatedLocalTransform().mapRect(m_clipBoundaries);
 }
 
 bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundingBox, const FloatPoint& nodeAtPoint)
@@ -299,13 +307,16 @@ bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundin
     if (!SVGRenderSupport::pointInClippingArea(this, point))
         return false;
 
-    if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+    SVGClipPathElement* clipPathElement = static_cast<SVGClipPathElement*>(node());
+    if (clipPathElement->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
         AffineTransform transform;
         transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
         transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
         point = transform.inverse().mapPoint(point);
     }
 
+    point = clipPathElement->animatedLocalTransform().inverse().mapPoint(point);
+
     for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
         RenderObject* renderer = childNode->renderer();
         if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
index 228fc54..2ff8a5d 100644 (file)
@@ -65,7 +65,7 @@ private:
     // clipPath can be clipped too, but don't have a boundingBox or repaintRect. So we can't call
     // applyResource directly and use the rects from the object, since they are empty for RenderSVGResources
     bool applyClippingToContext(RenderObject*, const FloatRect&, const FloatRect&, GraphicsContext*);
-    bool pathOnlyClipping(GraphicsContext*, const FloatRect&);
+    bool pathOnlyClipping(GraphicsContext*, const AffineTransform&, const FloatRect&);
     bool drawContentIntoMaskImage(ClipperData*, const FloatRect& objectBoundingBox);
     void calculateClipContentRepaintRect();