Title: [123624] trunk
Revision
123624
Author
[email protected]
Date
2012-07-25 09:09:53 -0700 (Wed, 25 Jul 2012)

Log Message

NodesFromRect and area-based hit-testing can not handle CSS transforms.
https://bugs.webkit.org/show_bug.cgi?id=85792

Reviewed by Eric Seidel.

Source/WebCore:

To support the combination of CSS transforms and rect based hit testing,
we need to test against the transformed rect, instead of the original rect.

This patch makes HitTestPoint store the exact transformed FloatPoint and
FloatQuad, and modifies the intersection methods so that they will use a
new FloatQuad based intersection when transforms requires it.

Tests: fast/dom/nodesFromRect/nodesFromRect-rotate.html
       fast/dom/nodesFromRect/nodesFromRect-scale.html

* platform/graphics/FloatQuad.cpp:
(WebCore::determinant):
(WebCore::rightMostCornerToVector):
(WebCore::FloatQuad::intersectsRect):
(WebCore::FloatQuad::isCounterclockwise):
* platform/graphics/FloatQuad.h:
(FloatQuad):
* rendering/HitTestResult.cpp:
(WebCore::HitTestPoint::HitTestPoint):
(WebCore::HitTestPoint::operator=):
(WebCore::HitTestPoint::move):
(WebCore::HitTestPoint::intersectsRect):
(WebCore::HitTestPoint::intersects):
* rendering/HitTestResult.h:
(HitTestPoint):
(WebCore::HitTestPoint::isRectilinear):
(WebCore::HitTestPoint::transformedPoint):
(WebCore::HitTestPoint::transformedRect):
* rendering/HitTestingTransformState.cpp:
(WebCore::HitTestingTransformState::flattenWithTransform):
(WebCore::HitTestingTransformState::mappedArea):
(WebCore::HitTestingTransformState::boundsOfMappedArea):
* rendering/HitTestingTransformState.h:
(WebCore::HitTestingTransformState::create):
(WebCore::HitTestingTransformState::HitTestingTransformState):
* rendering/RenderFlowThread.cpp:
(WebCore::RenderFlowThread::hitTestRegion):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::hitTest):
(WebCore::RenderLayer::createLocalTransformState):
(WebCore::RenderLayer::hitTestLayer):
(WebCore::RenderLayer::hitTestChildLayerColumns):
* rendering/RenderLayer.h:
* rendering/svg/RenderSVGText.cpp:
(WebCore::RenderSVGText::nodeAtFloatPoint):

LayoutTests:

Adds nodesFromRect tests that tests the API under the CSS transforms
scale and rotate.

* fast/dom/nodesFromRect/nodesFromRect-rotate-expected.txt: Added.
* fast/dom/nodesFromRect/nodesFromRect-rotate.html: Added.
* fast/dom/nodesFromRect/nodesFromRect-scale-expected.txt: Added.
* fast/dom/nodesFromRect/nodesFromRect-scale.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (123623 => 123624)


--- trunk/LayoutTests/ChangeLog	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/LayoutTests/ChangeLog	2012-07-25 16:09:53 UTC (rev 123624)
@@ -1,3 +1,18 @@
+2012-07-25  Allan Sandfeld Jensen  <[email protected]>
+
+        NodesFromRect and area-based hit-testing can not handle CSS transforms.
+        https://bugs.webkit.org/show_bug.cgi?id=85792
+
+        Reviewed by Eric Seidel.
+
+        Adds nodesFromRect tests that tests the API under the CSS transforms
+        scale and rotate.
+
+        * fast/dom/nodesFromRect/nodesFromRect-rotate-expected.txt: Added.
+        * fast/dom/nodesFromRect/nodesFromRect-rotate.html: Added.
+        * fast/dom/nodesFromRect/nodesFromRect-scale-expected.txt: Added.
+        * fast/dom/nodesFromRect/nodesFromRect-scale.html: Added.
+
 2012-07-25  Caio Marcelo de Oliveira Filho  <[email protected]>
 
         [Qt] css2.1/t1* tests needs rebaseline after new testfonts

Added: trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate-expected.txt (0 => 123624)


--- trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate-expected.txt	2012-07-25 16:09:53 UTC (rev 123624)
@@ -0,0 +1,49 @@
+Document::nodesFromRect : CSS rotate transforms - bug 85792
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Check unrotated area-testing for sanity
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check rotated 180deg
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check rotated 90deg
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check rotated 45deg
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+

Added: trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate.html (0 => 123624)


--- trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-rotate.html	2012-07-25 16:09:53 UTC (rev 123624)
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Document::nodesFromRect : CSS rotate transforms - bug 85792</title>
+<style type="text/css">
+    #sandbox {
+        position: absolute;
+        left: 0px;
+        top: 0px;
+        width: 700px;
+        height: 700px;
+    }
+    #layer {
+        position: absolute;
+        left: 200px;
+        top: 200px;
+        width: 300px;
+        height: 300px;
+    }
+    .rotate45 { -webkit-transform: rotate(45deg); }
+    .rotate90 { -webkit-transform: rotate(90deg); }
+    .rotate180 { -webkit-transform: rotate(180deg); }
+    #layer > #fleft { float: left; width: 50px; height: 300px; }
+    #layer > #fright { float: right; width: 50px; height: 300px; }
+    #layer > .hbox { height: 100px; margin-right: 50px; margin-left: 50px }
+</style>
+<script src=""
+<script src=""
+</head>
+
+<body>
+    <div id=sandbox>
+        <div id=layer>
+            <div id=fleft></div>
+            <div id=fright></div>
+            <div id=box1 class=hbox></div>
+            <div id=box2 class=hbox></div>
+            <div id=box3 class=hbox></div>
+        </div>
+    </div>
+
+    <script>
+        function runTest()
+        {
+            description(document.title);
+            var e = {};
+
+            // Set up shortcut access to elements
+            ['sandbox', 'layer', 'fleft', 'fright', 'box1', 'box2', 'box3'].forEach(function(a) {
+                e[a] = document.getElementById(a);
+            });
+
+            window.scrollTo(0, 0);
+            debug('Check unrotated area-testing for sanity');
+            check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+            check(220, 220, 10, 10, 10, 10, [e.fleft]);
+            check(470, 220, 10, 10, 10, 10, [e.fright]);
+            check(270, 220, 10, 10, 10, 10, [e.box1]);
+            check(270, 320, 10, 10, 10, 10, [e.box2]);
+            check(240, 290, 30, 30, 30, 30, [e.fleft, e.box2, e.box1, e.layer]);
+            check(180, 220, 10, 30, 10, 0, [e.fleft, e.layer, e.sandbox]);
+            check(270, 280, 0, 10, 30, 10, [e.box2, e.box1, e.layer]);
+            check(180, 220, 10, 0, 10, 30, [e.sandbox]);
+
+
+            debug('Check rotated 180deg');
+            e['layer'].setAttribute('class', 'rotate180');
+            check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+            check(220, 220, 10, 10, 10, 10, [e.fright]);
+            check(470, 220, 10, 10, 10, 10, [e.fleft]);
+            check(270, 220, 10, 10, 10, 10, [e.box3]);
+            check(270, 320, 10, 10, 10, 10, [e.box2]);
+            check(240, 290, 30, 30, 30, 30, [e.fright, e.box3, e.box2, e.layer]);
+            check(180, 220, 10, 30, 10, 0, [e.fright, e.layer, e.sandbox]);
+            check(270, 280, 0, 10, 30, 10, [e.box3, e.box2, e.layer]);
+            check(180, 220, 10, 0, 10, 30, [e.sandbox]);
+
+            debug('Check rotated 90deg');
+            e['layer'].setAttribute('class', 'rotate90');
+            check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+            check(220, 280, 10, 10, 10, 10, [e.box3]);
+            check(470, 280, 10, 10, 10, 10, [e.box1]);
+            check(270, 220, 10, 10, 10, 10, [e.fleft]);
+            check(270, 470, 10, 10, 10, 10, [e.fright]);
+            check(290, 240, 30, 30, 30, 30, [e.fleft, e.box3, e.box2, e.layer]);
+            check(180, 280, 10, 30, 10, 0, [e.box3, e.layer, e.sandbox]);
+            check(330, 180, 0, 10, 30, 10, [e.fleft, e.layer, e.sandbox]);
+            check(280, 260, 40, 10, 10, 10, [e.fleft, e.box3, e.layer]);
+
+            debug('Check rotated 45deg');
+            e['layer'].setAttribute('class', 'rotate45');
+            check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+            check(260, 260, 10, 10, 10, 10, [e.fleft]);
+            check(440, 440, 10, 10, 10, 10, [e.fright]);
+            check(440, 260, 10, 10, 10, 10, [e.box1]);
+            check(260, 440, 10, 10, 10, 10, [e.box3]);
+            check(350, 140, 20, 20, 20, 20, [e.fleft, e.layer, e.sandbox]);
+            check(350, 140, 20, 20, 70, 20, [e.fleft, e.box1, e.layer, e.sandbox]);
+            check(350, 350, 40, 40, 40, 40, [e.box3, e.box2, e.box1, e.layer]);
+            check(260, 260, 1, 27, 1, 27, [e.fleft]);
+
+        }
+
+        window._onload_ = runTest;
+    </script>
+
+    <p id='description'></p>
+    <span id="console"></span>
+    <script src=""
+</body>
+</html>
+

Added: trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale-expected.txt (0 => 123624)


--- trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale-expected.txt	2012-07-25 16:09:53 UTC (rev 123624)
@@ -0,0 +1,38 @@
+Document::nodesFromRect : CSS scale transforms - bug 85792
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Check unscaled area-testing for sanity
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check scaling 2X
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+Check scaling 0.5X
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+PASS All correct nodes found for rect
+

Added: trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale.html (0 => 123624)


--- trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/nodesFromRect/nodesFromRect-scale.html	2012-07-25 16:09:53 UTC (rev 123624)
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Document::nodesFromRect : CSS scale transforms - bug 85792</title>
+<style type="text/css">
+    #sandbox {
+        position: absolute;
+        left: 0px;
+        top: 0px;
+        width: 600px;
+        height: 600px;
+    }
+    #layer {
+        position: absolute;
+        left: 200px;
+        top: 200px;
+        width: 200px;
+        height: 200px;
+    }
+    .scaleup { -webkit-transform: scale(2); }
+    .scaledown { -webkit-transform: scale(0.5); }
+    #layer > #fleft { float: left; width: 50px; height: 200px; }
+    #layer > #fright { float: right; width: 50px; height: 200px; }
+    #layer > .hbox { height: 100px; margin-right: 50px; margin-left: 50px }
+</style>
+<script src=""
+<script src=""
+</head>
+
+<body>
+    <div id=sandbox>
+        <div id=layer>
+            <div id=fleft></div>
+            <div id=fright></div>
+            <div id=box1 class=hbox></div>
+            <div id=box2 class=hbox></div>
+        </div>
+    </div>
+
+    <script>
+        function runTest()
+        {
+            description(document.title);
+            var e = {};
+
+            // Set up shortcut access to elements
+            ['sandbox', 'layer', 'fleft', 'fright', 'box1', 'box2', 'box3'].forEach(function(a) {
+                e[a] = document.getElementById(a);
+            });
+
+            window.scrollTo(0, 0);
+            debug('Check unscaled area-testing for sanity');
+            check(150, 150, 30, 30, 30, 30, [e.sandbox]);
+            check(220, 220, 10, 10, 10, 10, [e.fleft]);
+            check(370, 220, 10, 10, 10, 10, [e.fright]);
+            check(270, 220, 10, 10, 10, 10, [e.box1]);
+            check(270, 320, 10, 10, 10, 10, [e.box2]);
+            check(240, 290, 30, 30, 30, 30, [e.fleft, e.box2, e.box1, e.layer]);
+            check(180, 220, 10, 30, 10, 0, [e.fleft, e.layer, e.sandbox]);
+            check(270, 280, 0, 10, 30, 10, [e.box2, e.box1, e.layer]);
+            check(180, 220, 10, 0, 10, 30, [e.sandbox]);
+
+            debug('Check scaling 2X');
+            e['layer'].setAttribute('class', 'scaleup');
+            check(150, 150, 30, 30, 30, 30, [e.fleft]);
+            check(450, 150, 30, 30, 30, 30, [e.fright]);
+            check(350, 220, 10, 10, 10, 10, [e.box1]);
+            check(350, 420, 10, 10, 10, 10, [e.box2]);
+            check(180, 280, 60, 60, 60, 60, [e.fleft, e.box2, e.box1, e.layer]);
+            check(60, 140, 20, 60, 20, 0, [e.fleft, e.layer, e.sandbox]);
+            check(240, 260, 0, 20, 60, 20, [e.box2, e.box1, e.layer]);
+            check(60, 140, 20, 0, 20, 60, [e.sandbox]);
+
+            debug('Check scaling 0.5X');
+            e['layer'].setAttribute('class', 'scaledown');
+            check(225, 225, 15, 15, 15, 15, [e.sandbox]);
+            check(260, 260, 5, 5, 5, 5, [e.fleft]);
+            check(335, 260, 5, 5, 5, 5, [e.fright]);
+            check(285, 260, 5, 5, 5, 5, [e.box1]);
+            check(285, 310, 5, 5, 5, 5, [e.box2]);
+            check(270, 295, 15, 15, 15, 15, [e.fleft, e.box2, e.box1, e.layer]);
+            check(240, 260, 5, 15, 5, 0, [e.fleft, e.layer, e.sandbox]);
+            check(285, 290, 0, 5, 15, 5, [e.box2, e.box1, e.layer]);
+            check(240, 260, 5, 0, 5, 15, [e.sandbox]);
+
+        }
+
+        window._onload_ = runTest;
+    </script>
+
+    <p id='description'></p>
+    <span id="console"></span>
+    <script src=""
+</body>
+</html>
+

Modified: trunk/Source/WebCore/ChangeLog (123623 => 123624)


--- trunk/Source/WebCore/ChangeLog	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/ChangeLog	2012-07-25 16:09:53 UTC (rev 123624)
@@ -1,3 +1,56 @@
+2012-07-25  Allan Sandfeld Jensen  <[email protected]>
+
+        NodesFromRect and area-based hit-testing can not handle CSS transforms.
+        https://bugs.webkit.org/show_bug.cgi?id=85792
+
+        Reviewed by Eric Seidel.
+
+        To support the combination of CSS transforms and rect based hit testing,
+        we need to test against the transformed rect, instead of the original rect.
+
+        This patch makes HitTestPoint store the exact transformed FloatPoint and 
+        FloatQuad, and modifies the intersection methods so that they will use a 
+        new FloatQuad based intersection when transforms requires it.
+
+        Tests: fast/dom/nodesFromRect/nodesFromRect-rotate.html
+               fast/dom/nodesFromRect/nodesFromRect-scale.html
+
+        * platform/graphics/FloatQuad.cpp:
+        (WebCore::determinant):
+        (WebCore::rightMostCornerToVector):
+        (WebCore::FloatQuad::intersectsRect):
+        (WebCore::FloatQuad::isCounterclockwise):
+        * platform/graphics/FloatQuad.h:
+        (FloatQuad):
+        * rendering/HitTestResult.cpp:
+        (WebCore::HitTestPoint::HitTestPoint):
+        (WebCore::HitTestPoint::operator=):
+        (WebCore::HitTestPoint::move):
+        (WebCore::HitTestPoint::intersectsRect):
+        (WebCore::HitTestPoint::intersects):
+        * rendering/HitTestResult.h:
+        (HitTestPoint):
+        (WebCore::HitTestPoint::isRectilinear):
+        (WebCore::HitTestPoint::transformedPoint):
+        (WebCore::HitTestPoint::transformedRect):
+        * rendering/HitTestingTransformState.cpp:
+        (WebCore::HitTestingTransformState::flattenWithTransform):
+        (WebCore::HitTestingTransformState::mappedArea):
+        (WebCore::HitTestingTransformState::boundsOfMappedArea):
+        * rendering/HitTestingTransformState.h:
+        (WebCore::HitTestingTransformState::create):
+        (WebCore::HitTestingTransformState::HitTestingTransformState):
+        * rendering/RenderFlowThread.cpp:
+        (WebCore::RenderFlowThread::hitTestRegion):
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::hitTest):
+        (WebCore::RenderLayer::createLocalTransformState):
+        (WebCore::RenderLayer::hitTestLayer):
+        (WebCore::RenderLayer::hitTestChildLayerColumns):
+        * rendering/RenderLayer.h:
+        * rendering/svg/RenderSVGText.cpp:
+        (WebCore::RenderSVGText::nodeAtFloatPoint):
+
 2012-07-25  Kwang Yul Seo  <[email protected]>
 
         Add HTMLStackItem.h to project files

Modified: trunk/Source/WebCore/platform/graphics/FloatQuad.cpp (123623 => 123624)


--- trunk/Source/WebCore/platform/graphics/FloatQuad.cpp	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/platform/graphics/FloatQuad.cpp	2012-07-25 16:09:53 UTC (rev 123624)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -51,6 +52,11 @@
     return a.width() * b.width() + a.height() * b.height();
 }
 
+inline float determinant(const FloatSize& a, const FloatSize& b)
+{
+    return a.width() * b.height() - a.height() * b.width();
+}
+
 inline bool isPointInTriangle(const FloatPoint& p, const FloatPoint& t1, const FloatPoint& t2, const FloatPoint& t3)
 {
     // Compute vectors        
@@ -107,11 +113,74 @@
     return containsPoint(other.p1()) && containsPoint(other.p2()) && containsPoint(other.p3()) && containsPoint(other.p4());
 }
 
+static inline FloatPoint rightMostCornerToVector(const FloatRect& rect, const FloatSize& vector)
+{
+    // Return the corner of the rectangle that if it is to the left of the vector
+    // would mean all of the rectangle is to the left of the vector.
+    // The vector here represents the side between two points in a clockwise convex polygon.
+    //
+    //  Q  XXX
+    // QQQ XXX   If the lower left corner of X is left of the vector that goes from the top corner of Q to
+    //  QQQ      the right corner of Q, then all of X is left of the vector, and intersection impossible.
+    //   Q
+    //
+    FloatPoint point;
+    if (vector.width() >= 0)
+        point.setY(rect.maxY());
+    else
+        point.setY(rect.y());
+    if (vector.height() >= 0)
+        point.setX(rect.x());
+    else
+        point.setX(rect.maxX());
+    return point;
+}
+
+bool FloatQuad::intersectsRect(const FloatRect& rect) const
+{
+    // For each side of the quad clockwise we check if the rectangle is to the left of it
+    // since only content on the right can onlap with the quad.
+    // This only works if the quad is convex.
+    FloatSize v1, v2, v3, v4;
+
+    // Ensure we use clockwise vectors.
+    if (!isCounterclockwise()) {
+        v1 = m_p2 - m_p1;
+        v2 = m_p3 - m_p2;
+        v3 = m_p4 - m_p3;
+        v4 = m_p1 - m_p4;
+    } else {
+        v1 = m_p4 - m_p1;
+        v2 = m_p1 - m_p2;
+        v3 = m_p2 - m_p3;
+        v4 = m_p3 - m_p4;
+    }
+
+    FloatPoint p = rightMostCornerToVector(rect, v1);
+    if (determinant(v1, p - m_p1) < 0)
+        return false;
+
+    p = rightMostCornerToVector(rect, v2);
+    if (determinant(v2, p - m_p2) < 0)
+        return false;
+
+    p = rightMostCornerToVector(rect, v3);
+    if (determinant(v3, p - m_p3) < 0)
+        return false;
+
+    p = rightMostCornerToVector(rect, v4);
+    if (determinant(v4, p - m_p4) < 0)
+        return false;
+
+    // If not all of the rectangle is outside one of the quad's four sides, then that means at least
+    // a part of the rectangle is overlapping the quad.
+    return true;
+}
+
 bool FloatQuad::isCounterclockwise() const
 {
-    FloatPoint v1 = FloatPoint(m_p2.x() - m_p1.x(), m_p2.y() - m_p1.y());
-    FloatPoint v2 = FloatPoint(m_p3.x() - m_p2.x(), m_p3.y() - m_p2.y());
-    return (v1.x() * v2.y() - v1.y() * v2.x()) < 0;
+    // Return if the two first vectors are turning clockwise. If the quad is convex then all following vectors will turn the same way.
+    return determinant(m_p2 - m_p1, m_p3 - m_p2) < 0;
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/FloatQuad.h (123623 => 123624)


--- trunk/Source/WebCore/platform/graphics/FloatQuad.h	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/platform/graphics/FloatQuad.h	2012-07-25 16:09:53 UTC (rev 123624)
@@ -88,6 +88,10 @@
     // from transformed rects.
     bool containsQuad(const FloatQuad&) const;
 
+    // Tests whether any part of the rectangle intersects with this quad.
+    // This only works for convex quads.
+    bool intersectsRect(const FloatRect&) const;
+
     // The center of the quad. If the quad is the result of a affine-transformed rectangle this is the same as the original center transformed.
     FloatPoint center() const
     {

Modified: trunk/Source/WebCore/rendering/HitTestResult.cpp (123623 => 123624)


--- trunk/Source/WebCore/rendering/HitTestResult.cpp	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/HitTestResult.cpp	2012-07-25 16:09:53 UTC (rev 123624)
@@ -50,27 +50,57 @@
 
 HitTestPoint::HitTestPoint()
     : m_isRectBased(false)
+    , m_isRectilinear(true)
 {
 }
 
 HitTestPoint::HitTestPoint(const LayoutPoint& point)
     : m_point(point)
     , m_boundingBox(rectForPoint(point, 0, 0, 0, 0))
+    , m_transformedPoint(point)
+    , m_transformedRect(m_boundingBox)
     , m_isRectBased(false)
+    , m_isRectilinear(true)
 {
 }
 
+HitTestPoint::HitTestPoint(const FloatPoint& point)
+    : m_point(roundedLayoutPoint(point))
+    , m_boundingBox(rectForPoint(m_point, 0, 0, 0, 0))
+    , m_transformedPoint(point)
+    , m_transformedRect(m_boundingBox)
+    , m_isRectBased(false)
+    , m_isRectilinear(true)
+{
+}
+
+HitTestPoint::HitTestPoint(const FloatPoint& point, const FloatQuad& quad)
+    : m_transformedPoint(point)
+    , m_transformedRect(quad)
+    , m_isRectBased(true)
+{
+    m_point = roundedLayoutPoint(point);
+    m_boundingBox = enclosingIntRect(quad.boundingBox());
+    m_isRectilinear = quad.isRectilinear();
+}
+
 HitTestPoint::HitTestPoint(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
     : m_point(centerPoint)
     , m_boundingBox(rectForPoint(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding))
+    , m_transformedPoint(centerPoint)
     , m_isRectBased(topPadding || rightPadding || bottomPadding || leftPadding)
+    , m_isRectilinear(true)
 {
+    m_transformedRect = FloatQuad(m_boundingBox);
 }
 
 HitTestPoint::HitTestPoint(const HitTestPoint& other)
     : m_point(other.m_point)
     , m_boundingBox(other.m_boundingBox)
+    , m_transformedPoint(other.m_transformedPoint)
+    , m_transformedRect(other.m_transformedRect)
     , m_isRectBased(other.m_isRectBased)
+    , m_isRectilinear(other.m_isRectilinear)
 {
 }
 
@@ -82,40 +112,52 @@
 {
     m_point = other.m_point;
     m_boundingBox = other.m_boundingBox;
+    m_transformedPoint = other.m_transformedPoint;
+    m_transformedRect = other.m_transformedRect;
     m_isRectBased = other.m_isRectBased;
+    m_isRectilinear = other.m_isRectilinear;
 
     return *this;
 }
 
-void HitTestPoint::setPoint(const LayoutPoint& point)
+void HitTestPoint::move(const LayoutSize& offset)
 {
-    m_boundingBox.move(roundedIntPoint(point) - roundedIntPoint(m_point));
-    m_point = point;
+    m_point.move(offset);
+    m_transformedPoint.move(offset);
+    m_transformedRect.move(offset);
+    m_boundingBox = enclosingIntRect(m_transformedRect.boundingBox());
 }
 
 template<typename RectType>
-bool hitTestPointIntersects(const HitTestPoint& hitTestPoint, const RectType& rect)
+bool HitTestPoint::intersectsRect(const RectType& rect) const
 {
     // FIXME: When the hit test is not rect based we should use rect.contains(m_point).
     // That does change some corner case tests though.
 
-    // First check if rect even intersects our bounding rect.
-    if (!rect.intersects(hitTestPoint.boundingBox()))
+    // First check if rect even intersects our bounding box.
+    if (!rect.intersects(m_boundingBox))
         return false;
 
-    // FIXME: Implement quad based intersection test to handle transformed hit test rectangles.
-    return true;
+    // If the transformed rect is rectilinear the bounding box intersection was accurate.
+    if (m_isRectilinear)
+        return true;
 
+    // If rect fully contains our bounding box, we are also sure of an intersection.
+    if (rect.contains(m_boundingBox))
+        return true;
+
+    // Otherwise we need to do a slower quad based intersection test.
+    return m_transformedRect.intersectsRect(rect);
 }
 
 bool HitTestPoint::intersects(const LayoutRect& rect) const
 {
-    return hitTestPointIntersects(*this, rect);
+    return intersectsRect(rect);
 }
 
 bool HitTestPoint::intersects(const FloatRect& rect) const
 {
-    return hitTestPointIntersects(*this, rect);
+    return intersectsRect(rect);
 }
 
 IntRect HitTestPoint::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)

Modified: trunk/Source/WebCore/rendering/HitTestResult.h (123623 => 123624)


--- trunk/Source/WebCore/rendering/HitTestResult.h	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/HitTestResult.h	2012-07-25 16:09:53 UTC (rev 123624)
@@ -22,6 +22,7 @@
 #ifndef HitTestResult_h
 #define HitTestResult_h
 
+#include "FloatQuad.h"
 #include "FloatRect.h"
 #include "HitTestRequest.h"
 #include "LayoutTypes.h"
@@ -51,6 +52,8 @@
 
     HitTestPoint();
     HitTestPoint(const LayoutPoint&);
+    HitTestPoint(const FloatPoint&);
+    HitTestPoint(const FloatPoint&, const FloatQuad&);
     // Pass non-zero padding values to perform a rect-based hit test.
     HitTestPoint(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding);
     HitTestPoint(const HitTestPoint&);
@@ -60,10 +63,11 @@
     LayoutPoint point() const { return m_point; }
     IntPoint roundedPoint() const { return roundedIntPoint(m_point); }
 
-    void setPoint(const LayoutPoint&);
+    void move(const LayoutSize& offset);
 
     // Rect-based hit test related methods.
     bool isRectBasedTest() const { return m_isRectBased; }
+    bool isRectilinear() const { return m_isRectilinear; }
     IntRect boundingBox() const { return m_boundingBox; }
 
     static IntRect rectForPoint(const LayoutPoint&, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding);
@@ -75,11 +79,21 @@
     bool intersects(const LayoutRect&) const;
     bool intersects(const FloatRect&) const;
 
+    const FloatPoint& transformedPoint() const { return m_transformedPoint; }
+    const FloatQuad& transformedRect() const { return m_transformedRect; }
+
 private:
+    template<typename RectType>
+    bool intersectsRect(const RectType&) const;
+
+    // This is cached forms of the more accurate point and area below.
     LayoutPoint m_point;
+    IntRect m_boundingBox;
 
-    IntRect m_boundingBox;
+    FloatPoint m_transformedPoint;
+    FloatQuad m_transformedRect;
     bool m_isRectBased;
+    bool m_isRectilinear;
 };
 
 class HitTestResult : public HitTestPoint {

Modified: trunk/Source/WebCore/rendering/HitTestingTransformState.cpp (123623 => 123624)


--- trunk/Source/WebCore/rendering/HitTestingTransformState.cpp	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/HitTestingTransformState.cpp	2012-07-25 16:09:53 UTC (rev 123624)
@@ -58,6 +58,7 @@
     TransformationMatrix inverseTransform = t.inverse();
     m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint);
     m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad);
+    m_lastPlanarArea = inverseTransform.projectQuad(m_lastPlanarArea);
 
     m_accumulatedTransform.makeIdentity();
     m_accumulatingTransform = false;
@@ -73,9 +74,14 @@
     return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarQuad);
 }
 
-LayoutRect HitTestingTransformState::boundsOfMappedQuad() const
+FloatQuad HitTestingTransformState::mappedArea() const
 {
-    return m_accumulatedTransform.inverse().clampedBoundsOfProjectedQuad(m_lastPlanarQuad);
+    return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarArea);
 }
 
+LayoutRect HitTestingTransformState::boundsOfMappedArea() const
+{
+    return m_accumulatedTransform.inverse().clampedBoundsOfProjectedQuad(m_lastPlanarArea);
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/rendering/HitTestingTransformState.h (123623 => 123624)


--- trunk/Source/WebCore/rendering/HitTestingTransformState.h	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/HitTestingTransformState.h	2012-07-25 16:09:53 UTC (rev 123624)
@@ -42,9 +42,9 @@
 // differently than move()) so care has to be taken when this is done.
 class HitTestingTransformState : public RefCounted<HitTestingTransformState> {
 public:
-    static PassRefPtr<HitTestingTransformState> create(const FloatPoint& p, const FloatQuad& quad)
+    static PassRefPtr<HitTestingTransformState> create(const FloatPoint& p, const FloatQuad& quad, const FloatQuad& area)
     {
-        return adoptRef(new HitTestingTransformState(p, quad));
+        return adoptRef(new HitTestingTransformState(p, quad, area));
     }
 
     static PassRefPtr<HitTestingTransformState> create(const HitTestingTransformState& other)
@@ -58,18 +58,21 @@
 
     FloatPoint mappedPoint() const;
     FloatQuad mappedQuad() const;
-    LayoutRect boundsOfMappedQuad() const;
+    FloatQuad mappedArea() const;
+    LayoutRect boundsOfMappedArea() const;
     void flatten();
 
     FloatPoint m_lastPlanarPoint;
     FloatQuad m_lastPlanarQuad;
+    FloatQuad m_lastPlanarArea;
     TransformationMatrix m_accumulatedTransform;
     bool m_accumulatingTransform;
 
 private:
-    HitTestingTransformState(const FloatPoint& p, const FloatQuad& quad)
+    HitTestingTransformState(const FloatPoint& p, const FloatQuad& quad, const FloatQuad& area)
         : m_lastPlanarPoint(p)
         , m_lastPlanarQuad(quad)
+        , m_lastPlanarArea(area)
         , m_accumulatingTransform(false)
     {
     }
@@ -78,6 +81,7 @@
         : RefCounted<HitTestingTransformState>()
         , m_lastPlanarPoint(other.m_lastPlanarPoint)
         , m_lastPlanarQuad(other.m_lastPlanarQuad)
+        , m_lastPlanarArea(other.m_lastPlanarArea)
         , m_accumulatedTransform(other.m_accumulatedTransform)
         , m_accumulatingTransform(other.m_accumulatingTransform)
     {

Modified: trunk/Source/WebCore/rendering/RenderFlowThread.cpp (123623 => 123624)


--- trunk/Source/WebCore/rendering/RenderFlowThread.cpp	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/RenderFlowThread.cpp	2012-07-25 16:09:53 UTC (rev 123624)
@@ -315,17 +315,16 @@
     } else
         renderFlowThreadOffset = accumulatedOffset - regionRect.location();
 
-    LayoutPoint transformedPoint = pointInContainer.point() - renderFlowThreadOffset;
-
     // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView.
     HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping);
 
     RenderRegion* oldRegion = result.region();
     result.setRegion(region);
-    LayoutPoint oldPoint = result.point();
-    result.setPoint(transformedPoint);
-    bool isPointInsideFlowThread = layer()->hitTest(newRequest, result);
-    result.setPoint(oldPoint);
+
+    HitTestPoint newHitTestPoint(pointInContainer);
+    newHitTestPoint.move(-renderFlowThreadOffset);
+
+    bool isPointInsideFlowThread = layer()->hitTest(newRequest, newHitTestPoint, result);
     result.setRegion(oldRegion);
 
     // FIXME: Should we set result.m_localPoint back to the RenderRegion's coordinate space or leave it in the RenderFlowThread's coordinate

Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (123623 => 123624)


--- trunk/Source/WebCore/rendering/RenderLayer.cpp	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp	2012-07-25 16:09:53 UTC (rev 123624)
@@ -3386,13 +3386,18 @@
 
 bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result)
 {
+    return hitTest(request, result.hitTestPoint(), result);
+}
+
+bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestPoint& hitTestPoint, HitTestResult& result)
+{
     renderer()->document()->updateLayout();
     
     LayoutRect hitTestArea = renderer()->isRenderFlowThread() ? toRenderFlowThread(renderer())->borderBoxRect() : renderer()->view()->documentRect();
     if (!request.ignoreClipping())
         hitTestArea.intersect(frameVisibleRect(renderer()));
 
-    RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestArea, result.hitTestPoint(), false);
+    RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestArea, hitTestPoint, false);
     if (!insideLayer) {
         // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, 
         // return ourselves. We do this so mouse events continue getting delivered after a drag has 
@@ -3456,7 +3461,7 @@
     } else {
         // If this is the first time we need to make transform state, then base it off of hitTestPoint,
         // which is relative to rootLayer.
-        transformState = HitTestingTransformState::create(hitTestPoint.point(), FloatQuad(hitTestRect));
+        transformState = HitTestingTransformState::create(hitTestPoint.transformedPoint(), hitTestPoint.transformedRect(), FloatQuad(hitTestRect));
         convertToLayerCoords(rootLayer, offset);
     }
     
@@ -3536,10 +3541,14 @@
         //
         // We can't just map hitTestPoint and hitTestRect because they may have been flattened (losing z)
         // by our container.
-        LayoutPoint localPoint = roundedLayoutPoint(newTransformState->mappedPoint());
-        LayoutRect localHitTestRect = newTransformState->boundsOfMappedQuad();
-        HitTestPoint newHitTestPoint(result.hitTestPoint());
-        newHitTestPoint.setPoint(localPoint);
+        FloatPoint localPoint = newTransformState->mappedPoint();
+        FloatQuad localPointQuad = newTransformState->mappedQuad();
+        LayoutRect localHitTestRect = newTransformState->boundsOfMappedArea();
+        HitTestPoint newHitTestPoint;
+        if (hitTestPoint.isRectBasedTest())
+            newHitTestPoint = HitTestPoint(localPoint, localPointQuad);
+        else
+            newHitTestPoint = HitTestPoint(localPoint);
 
         // Now do a hit test with the root layer shifted to be us.
         return hitTestLayer(this, containerLayer, request, result, localHitTestRect, newHitTestPoint, true, newTransformState.get(), zOffset);
@@ -3835,10 +3844,14 @@
                 RenderLayer* nextLayer = columnLayers[columnIndex - 1];
                 RefPtr<HitTestingTransformState> newTransformState = nextLayer->createLocalTransformState(rootLayer, nextLayer, localClipRect, hitTestPoint, transformState);
                 newTransformState->translate(offset.width(), offset.height(), HitTestingTransformState::AccumulateTransform);
-                LayoutPoint localPoint = roundedLayoutPoint(newTransformState->mappedPoint());
-                LayoutRect localHitTestRect = newTransformState->mappedQuad().enclosingBoundingBox();
-                HitTestPoint newHitTestPoint(result.hitTestPoint());
-                newHitTestPoint.setPoint(localPoint);
+                FloatPoint localPoint = newTransformState->mappedPoint();
+                FloatQuad localPointQuad = newTransformState->mappedQuad();
+                LayoutRect localHitTestRect = newTransformState->mappedArea().enclosingBoundingBox();
+                HitTestPoint newHitTestPoint;
+                if (hitTestPoint.isRectBasedTest())
+                    newHitTestPoint = HitTestPoint(localPoint, localPointQuad);
+                else
+                    newHitTestPoint = HitTestPoint(localPoint);
                 newTransformState->flatten();
 
                 hitLayer = hitTestChildLayerColumns(childLayer, columnLayers[columnIndex - 1], request, result, localHitTestRect, newHitTestPoint,

Modified: trunk/Source/WebCore/rendering/RenderLayer.h (123623 => 123624)


--- trunk/Source/WebCore/rendering/RenderLayer.h	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/RenderLayer.h	2012-07-25 16:09:53 UTC (rev 123624)
@@ -512,6 +512,7 @@
     void paint(GraphicsContext*, const LayoutRect& damageRect, PaintBehavior = PaintBehaviorNormal, RenderObject* paintingRoot = 0,
         RenderRegion* = 0, PaintLayerFlags = 0);
     bool hitTest(const HitTestRequest&, HitTestResult&);
+    bool hitTest(const HitTestRequest&, const HitTestPoint&, HitTestResult&);
     void paintOverlayScrollbars(GraphicsContext*, const LayoutRect& damageRect, PaintBehavior, RenderObject* paintingRoot);
 
     // This method figures out our layerBounds in coordinates relative to

Modified: trunk/Source/WebCore/rendering/svg/RenderSVGText.cpp (123623 => 123624)


--- trunk/Source/WebCore/rendering/svg/RenderSVGText.cpp	2012-07-25 15:51:26 UTC (rev 123623)
+++ trunk/Source/WebCore/rendering/svg/RenderSVGText.cpp	2012-07-25 16:09:53 UTC (rev 123624)
@@ -451,7 +451,7 @@
             if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
                 return false;       
 
-            HitTestPoint hitTestPoint(flooredIntPoint(localPoint));
+            HitTestPoint hitTestPoint(LayoutPoint(flooredIntPoint(localPoint)));
             return RenderBlock::nodeAtPoint(request, result, hitTestPoint, LayoutPoint(), hitTestAction);
         }
     }
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to