Get basic hit testing right for transforms.
authorhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Oct 2007 15:52:26 +0000 (15:52 +0000)
committerhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Oct 2007 15:52:26 +0000 (15:52 +0000)
        Reviewed by ollliej

        * platform/graphics/AffineTransform.cpp:
        (WebCore::AffineTransform::mapPoint):
        * platform/graphics/AffineTransform.h:
        * rendering/InlineBox.cpp:
        (WebCore::InlineBox::nodeAtPoint):
        * rendering/RenderBlock.cpp:
        (WebCore::RenderBlock::nodeAtPoint):
        * rendering/RenderLayer.cpp:
        (WebCore::RenderLayer::paintLayer):
        (WebCore::RenderLayer::hitTest):
        (WebCore::RenderLayer::hitTestLayer):
        * rendering/RenderLayer.h:
        * rendering/RenderObject.cpp:
        (WebCore::RenderObject::hitTest):
        * rendering/RenderObject.h:

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

WebCore/ChangeLog
WebCore/platform/graphics/AffineTransform.cpp
WebCore/platform/graphics/AffineTransform.h
WebCore/rendering/InlineBox.cpp
WebCore/rendering/RenderBlock.cpp
WebCore/rendering/RenderLayer.cpp
WebCore/rendering/RenderLayer.h
WebCore/rendering/RenderObject.cpp
WebCore/rendering/RenderObject.h

index 8e2a431..6cb2db6 100644 (file)
@@ -1,3 +1,25 @@
+2007-10-23  David Hyatt  <hyatt@apple.com>
+
+        Get basic hit testing right for transforms.
+
+        Reviewed by ollliej
+
+        * platform/graphics/AffineTransform.cpp:
+        (WebCore::AffineTransform::mapPoint):
+        * platform/graphics/AffineTransform.h:
+        * rendering/InlineBox.cpp:
+        (WebCore::InlineBox::nodeAtPoint):
+        * rendering/RenderBlock.cpp:
+        (WebCore::RenderBlock::nodeAtPoint):
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::paintLayer):
+        (WebCore::RenderLayer::hitTest):
+        (WebCore::RenderLayer::hitTestLayer):
+        * rendering/RenderLayer.h:
+        * rendering/RenderObject.cpp:
+        (WebCore::RenderObject::hitTest):
+        * rendering/RenderObject.h:
+
 2007-10-23  Eric Seidel  <eric@webkit.org>
 
         Reviewed by Mark Rowe.
index 5ecc7c5..55f17d4 100644 (file)
@@ -83,4 +83,13 @@ AffineTransform& AffineTransform::skewY(double angle)
     return shear(0.0f, tan(deg2rad(angle)));
 }
 
+IntPoint AffineTransform::mapPoint(const IntPoint& point) const
+{
+    double x2, y2;
+    map(point.x(), point.y(), &x2, &y2);
+    
+    // Round the point.
+    return IntPoint(lround(x2), lround(y2));
+}
+
 }
index b51d513..7775278 100644 (file)
@@ -36,6 +36,7 @@
 
 namespace WebCore {
 
+class IntPoint;
 class IntRect;
 class FloatRect;
 
@@ -53,6 +54,7 @@ public:
 
     void setMatrix(double a, double b, double c, double d, double e, double f);
     void map(double x, double y, double *x2, double *y2) const;
+    IntPoint mapPoint(const IntPoint&) const;
     IntRect mapRect(const IntRect&) const;
     FloatRect mapRect(const FloatRect&) const;
     
index 63ac321..b7e8016 100644 (file)
@@ -162,7 +162,7 @@ bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result
     // Hit test all phases of replaced elements atomically, as though the replaced element established its
     // own stacking context.  (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
     // specification.)
-    return object()->hitTest(request, result, x, y, tx, ty);
+    return object()->hitTest(request, result, IntPoint(x, y), tx, ty);
 }
 
 RootInlineBox* InlineBox::root()
index 85713c1..f192e68 100644 (file)
@@ -2865,7 +2865,7 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu
                 if (!o->noPaint && !o->node->hasLayer()) {
                     int xoffset = scrolledX + o->left + o->node->marginLeft() - o->node->xPos();
                     int yoffset =  scrolledY + o->startY + o->node->marginTop() - o->node->yPos();
-                    if (o->node->hitTest(request, result, _x, _y, xoffset, yoffset)) {
+                    if (o->node->hitTest(request, result, IntPoint(_x, _y), xoffset, yoffset)) {
                         updateHitTestResult(result, IntPoint(_x - xoffset, _y - yoffset));
                         return true;
                     }
index 5fc53f8..df1bfb9 100644 (file)
@@ -1502,7 +1502,6 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p,
         p->concatCTM(transform);
 
         // Now do a paint with the root layer shifted to be us.
-        rootLayer = this;
         paintLayer(this, p, transform.inverse().mapRect(paintDirtyRect), haveTransparency, paintRestriction, paintingRoot);
         
         p->restore();
@@ -1632,7 +1631,7 @@ bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result)
     IntRect boundsRect(m_x, m_y, width(), height());
     boundsRect.intersect(frameVisibleRect(renderer()));
 
-    RenderLayer* insideLayer = hitTestLayer(this, request, result, boundsRect);
+    RenderLayer* insideLayer = hitTestLayer(this, request, result, boundsRect, result.point());
 
     // Now determine if the result is inside an anchor; make sure an image map wins if
     // it already set URLElement and only use the innermost.
@@ -1662,9 +1661,35 @@ Node* RenderLayer::enclosingElement() const
     return 0;
 }
 
-RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequest& request,
-    HitTestResult& result, const IntRect& hitTestRect)
+RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, const IntRect& hitTestRect, const IntPoint& hitTestPoint)
 {
+    // Apply a transform if we have one.
+    if (m_transform && rootLayer != this) {
+        // If the transform can't be inverted, then don't hit test this layer at all.
+        if (!m_transform->isInvertible())
+            return 0;
+  
+        // Make sure the parent's clip rects have been calculated.
+        parent()->calculateClipRects(rootLayer);
+        
+        // Go ahead and test the enclosing clip now.
+        IntRect clipRect = parent()->clipRects()->overflowClipRect();
+        if (!clipRect.contains(hitTestPoint))
+            return 0;
+
+        // Adjust the transform such that the renderer's upper left corner is at (0,0) in user space.
+        // This involves subtracting out the position of the layer in our current coordinate space.
+        int x = 0;
+        int y = 0;
+        convertToLayerCoords(rootLayer, x, y);
+        AffineTransform transform;
+        transform.translate(x, y);
+        transform = *m_transform * transform;
+        
+        // Map the hit test point into the transformed space and then do a hit test with the root layer shifted to be us.
+        return hitTestLayer(this, request, result, transform.inverse().mapRect(hitTestRect), transform.inverse().mapPoint(hitTestPoint));
+    }
+
     // Calculate the clip rects we should use.
     IntRect layerBounds;
     IntRect bgRect;
@@ -1684,7 +1709,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequ
     // z-index.
     if (m_posZOrderList) {
         for (int i = m_posZOrderList->size() - 1; i >= 0; --i) {
-            insideLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect);
+            insideLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint);
             if (insideLayer)
                 return insideLayer;
         }
@@ -1693,15 +1718,15 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequ
     // Now check our overflow objects.
     if (m_overflowList) {
         for (int i = m_overflowList->size() - 1; i >= 0; --i) {
-            insideLayer = m_overflowList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect);
+            insideLayer = m_overflowList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint);
             if (insideLayer)
                 return insideLayer;
         }
     }
 
     // Next we want to see if the mouse pos is inside the child RenderObjects of the layer.
-    if (fgRect.contains(result.point()) && 
-        renderer()->hitTest(request, result, result.point().x(), result.point().y(),
+    if (fgRect.contains(hitTestPoint) && 
+        renderer()->hitTest(request, result, hitTestPoint,
                             layerBounds.x() - renderer()->xPos(),
                             layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(), 
                             HitTestDescendants)) {
@@ -1723,15 +1748,15 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequ
     // Now check our negative z-index children.
     if (m_negZOrderList) {
         for (int i = m_negZOrderList->size() - 1; i >= 0; --i) {
-            insideLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect);
+            insideLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint);
             if (insideLayer)
                 return insideLayer;
         }
     }
 
     // Next we want to see if the mouse is inside this layer but not any of its children.
-    if (bgRect.contains(result.point()) &&
-        renderer()->hitTest(request, result, result.point().x(), result.point().y(),
+    if (bgRect.contains(hitTestPoint) &&
+        renderer()->hitTest(request, result, hitTestPoint,
                             layerBounds.x() - renderer()->xPos(),
                             layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
                             HitTestSelf)) {
@@ -1750,7 +1775,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequ
     // return ourselves. We do this so mouse events continue getting delivered after a drag has 
     // exited the WebView, and so hit testing over a scrollbar hits the content document.
     if ((request.active || request.mouseUp) && renderer()->isRenderView()) {
-        renderer()->updateHitTestResult(result, result.point());
+        renderer()->updateHitTestResult(result, hitTestPoint);
         return this;
     }
 
index 6b20813..2cbb189 100644 (file)
@@ -375,7 +375,7 @@ private:
 
     void paintLayer(RenderLayer* rootLayer, GraphicsContext*, const IntRect& paintDirtyRect,
                     bool haveTransparency, PaintRestriction, RenderObject* paintingRoot);
-    RenderLayer* hitTestLayer(RenderLayer* rootLayer, const HitTestRequest&, HitTestResult&, const IntRect& hitTestRect);
+    RenderLayer* hitTestLayer(RenderLayer* rootLayer, const HitTestRequest&, HitTestResult&, const IntRect& hitTestRect, const IntPoint& hitTestPoint);
     void computeScrollDimensions(bool* needHBar = 0, bool* needVBar = 0);
 
     bool shouldBeOverflowOnly() const;
index 425b3cc..f082f94 100644 (file)
@@ -2565,25 +2565,25 @@ void RenderObject::updateDragState(bool dragOn)
         continuation()->updateDragState(dragOn);
 }
 
-bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestFilter hitTestFilter)
+bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const IntPoint& point, int tx, int ty, HitTestFilter hitTestFilter)
 {
     bool inside = false;
     if (hitTestFilter != HitTestSelf) {
         // First test the foreground layer (lines and inlines).
-        inside = nodeAtPoint(request, result, x, y, tx, ty, HitTestForeground);
+        inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestForeground);
 
         // Test floats next.
         if (!inside)
-            inside = nodeAtPoint(request, result, x, y, tx, ty, HitTestFloat);
+            inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestFloat);
 
         // Finally test to see if the mouse is in the background (within a child block's background).
         if (!inside)
-            inside = nodeAtPoint(request, result, x, y, tx, ty, HitTestChildBlockBackgrounds);
+            inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestChildBlockBackgrounds);
     }
 
     // See if the mouse is inside us but not any of our descendants
     if (hitTestFilter != HitTestDescendants && !inside)
-        inside = nodeAtPoint(request, result, x, y, tx, ty, HitTestBlockBackground);
+        inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestBlockBackground);
 
     return inside;
 }
index 900081c..237573b 100644 (file)
@@ -509,7 +509,7 @@ public:
         IntRect m_repaintRect;
     };
 
-    bool hitTest(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestFilter = HitTestAll);
+    bool hitTest(const HitTestRequest&, HitTestResult&, const IntPoint&, int tx, int ty, HitTestFilter = HitTestAll);
     virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
     void updateHitTestResult(HitTestResult&, const IntPoint&);