Bug 505115 - Part 8c - Use ray tracing to untransform 2d points on a 3d plane. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 03 Aug 2011 15:04:21 +1200
changeset 73737 08b756f93436c48e5c32a01a24dd3633ee2b0593
parent 73736 74eac3a0904356bc20ce3613c715959c554d152c
child 73738 96941ed69aeb5d33faddea7d13d338f9ac87c49a
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersroc
bugs505115
milestone8.0a1
Bug 505115 - Part 8c - Use ray tracing to untransform 2d points on a 3d plane. r=roc
layout/base/FrameLayerBuilder.cpp
layout/base/nsDisplayList.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsFrame.cpp
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -916,26 +916,22 @@ AppUnitsPerDevPixel(nsDisplayItem* aItem
  * about whether CONTENT_OPAQUE is set; if layer was opauqe in the old
  * visible region, it will still be opaque in the new one.
  * @param aItemVisible the visible region of the display item (that is,
  * after any layer transform has been applied)
  */
 static void
 RestrictVisibleRegionForLayer(Layer* aLayer, const nsIntRect& aItemVisible)
 {
-  gfxMatrix transform;
-  if (!aLayer->GetTransform().Is2D(&transform))
-    return;
+  gfx3DMatrix transform = aLayer->GetTransform();
 
   // if 'transform' is not invertible, then nothing will be displayed
   // for the layer, so it doesn't really matter what we do here
-  gfxMatrix inverse = transform;
-  inverse.Invert();
   gfxRect itemVisible(aItemVisible.x, aItemVisible.y, aItemVisible.width, aItemVisible.height);
-  gfxRect layerVisible = inverse.TransformBounds(itemVisible);
+  gfxRect layerVisible = transform.Inverse().ProjectRectBounds(itemVisible);
   layerVisible.RoundOut();
 
   nsIntRect visibleRect;
   if (!gfxUtils::GfxRectToIntRect(layerVisible, &visibleRect))
     return;
 
   nsIntRegion rgn = aLayer->GetVisibleRegion();
   if (!visibleRect.Contains(rgn.GetBounds())) {
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -446,29 +446,26 @@ TreatAsOpaque(nsDisplayItem* aItem, nsDi
   return opaque;
 }
 
 static nsRect GetDisplayPortBounds(nsDisplayListBuilder* aBuilder,
                                    nsDisplayItem* aItem,
                                    PRBool aIgnoreTransform)
 {
   nsIFrame* frame = aItem->GetUnderlyingFrame();
-  nscoord auPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
-  gfx3DMatrix transform;
+  const nsRect* displayport = aBuilder->GetDisplayPort();
 
-  if (!aIgnoreTransform) {
-    transform = nsLayoutUtils::GetTransformToAncestor(frame,
-                  aBuilder->ReferenceFrame());
-    transform.Invert();
+  if (aIgnoreTransform) {
+    return *displayport;
   }
 
-  const nsRect* displayport = aBuilder->GetDisplayPort();
-  return nsLayoutUtils::MatrixTransformRect(
+  return nsLayoutUtils::TransformRectToBoundsInAncestor(
+           frame,
            nsRect(0, 0, displayport->width, displayport->height),
-           transform, auPerDevPixel);
+           aBuilder->ReferenceFrame());
 }
 
 PRBool
 nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion,
                                            const nsRect& aListVisibleBounds,
                                            const nsRect& aAllowVisibleRegionExpansion) {
 #ifdef DEBUG
@@ -2477,35 +2474,35 @@ void nsDisplayTransform::HitTest(nsDispl
 
   if (matrix.IsSingular())
     return;
 
   /* We want to go from transformed-space to regular space.
    * Thus we have to invert the matrix, which normally does
    * the reverse operation (e.g. regular->transformed)
    */
-  matrix.Invert();
 
   /* Now, apply the transform and pass it down the channel. */
   nsRect resultingRect;
   if (aRect.width == 1 && aRect.height == 1) {
-    gfxPoint point = matrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor),
-                                               NSAppUnitsToFloatPixels(aRect.y, factor)));
+    gfxPoint point = matrix.Inverse().ProjectPoint(
+                       gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor),
+                                NSAppUnitsToFloatPixels(aRect.y, factor)));
 
     resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor),
                            NSFloatPixelsToAppUnits(float(point.y), factor),
                            1, 1);
 
   } else {
     gfxRect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
                          NSAppUnitsToFloatPixels(aRect.y, factor),
                          NSAppUnitsToFloatPixels(aRect.width, factor),
                          NSAppUnitsToFloatPixels(aRect.height, factor));
 
-    gfxRect rect = matrix.TransformBounds(originalRect);
+    gfxRect rect = matrix.Inverse().ProjectRectBounds(originalRect);;
 
     resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
                            NSFloatPixelsToAppUnits(float(rect.Y()), factor),
                            NSFloatPixelsToAppUnits(float(rect.Width()), factor),
                            NSFloatPixelsToAppUnits(float(rect.Height()), factor));
   }
   
 
@@ -2688,24 +2685,31 @@ PRBool nsDisplayTransform::UntransformRe
                   "Cannot transform a rectangle if there's no transformation!");
 
 
   /* Grab the matrix.  If the transform is degenerate, just hand back the
    * empty rect.
    */
   float factor = nsPresContext::AppUnitsPerCSSPixel();
   gfx3DMatrix matrix = GetResultingTransformMatrix(aFrame, aOrigin, factor, nsnull);
-  if (matrix.IsSingular() || !matrix.Is2D())
+  if (matrix.IsSingular())
     return PR_FALSE;
 
+  gfxRect result(NSAppUnitsToFloatPixels(aUntransformedBounds.x, factor),
+                 NSAppUnitsToFloatPixels(aUntransformedBounds.y, factor),
+                 NSAppUnitsToFloatPixels(aUntransformedBounds.width, factor),
+                 NSAppUnitsToFloatPixels(aUntransformedBounds.height, factor));
+
   /* We want to untransform the matrix, so invert the transformation first! */
-  matrix.Invert();
+  result = matrix.Inverse().ProjectRectBounds(result);
 
-  *aOutRect = nsLayoutUtils::MatrixTransformRect(aUntransformedBounds, matrix,
-                                                 factor);
+  *aOutRect = nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
+                     NSFloatPixelsToAppUnits(float(result.y), factor),
+                     NSFloatPixelsToAppUnits(float(result.width), factor),
+                     NSFloatPixelsToAppUnits(float(result.height), factor));
 
   return PR_TRUE;
 }
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList)
     : nsDisplayWrapList(aBuilder, aFrame, aList), mEffectsFrame(aFrame),
       mBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -72,16 +72,17 @@
 #include "nsContentUtils.h"
 #include "nsThemeConstants.h"
 #include "nsPIDOMWindow.h"
 #include "nsIBaseWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIWidget.h"
 #include "gfxMatrix.h"
+#include "gfxPoint3D.h"
 #include "gfxTypes.h"
 #include "gfxUserFontSet.h"
 #include "nsTArray.h"
 #include "nsHTMLCanvasElement.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "gfxPlatform.h"
 #include "nsClientRect.h"
 #ifdef MOZ_MEDIA
@@ -1106,53 +1107,91 @@ nsLayoutUtils::MatrixTransformPoint(cons
                                     const gfx3DMatrix &aMatrix, float aFactor)
 {
   gfxPoint image = aMatrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
                                               NSAppUnitsToFloatPixels(aPoint.y, aFactor)));
   return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor),
                  NSFloatPixelsToAppUnits(float(image.y), aFactor));
 }
 
-gfx3DMatrix nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame,
-                                                  nsIFrame* aStopAtAncestor)
+static gfxPoint 
+InvertTransformsToAncestor(nsIFrame *aFrame,
+                           const gfxPoint &aPoint,
+                           nsIFrame *aStopAtAncestor = nsnull)
 {
-  gfx3DMatrix ctm;
-
-  /* Starting at the specified frame, we'll use the GetTransformMatrix
-   * function of the frame, which gives us a matrix from this frame up
-   * to some other ancestor frame. If aStopAtAncestor frame is not reached, 
-   * we stop at root. We get the CTM by simply accumulating all of these
-   * matrices together.
+  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
+
+  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
+   * the point.
    */
-  while (aFrame && aFrame != aStopAtAncestor) {
-    ctm *= aFrame->GetTransformMatrix(&aFrame);
+  nsIFrame *parent = nsnull;
+  gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent);
+  gfxPoint result = aPoint;
+  
+  if (parent && parent != aStopAtAncestor) {
+      result = InvertTransformsToAncestor(parent, aPoint, aStopAtAncestor);
   }
-  NS_ASSERTION(aFrame == aStopAtAncestor, "How did we manage to miss the ancestor?");
-  return ctm;
+
+  result = ctm.Inverse().ProjectPoint(result);
+  return result;
+}
+
+static gfxRect
+InvertGfxRectToAncestor(nsIFrame *aFrame,
+                     const gfxRect &aRect,
+                     nsIFrame *aStopAtAncestor = nsnull)
+{
+  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
+
+  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
+   * the point.
+   */
+  nsIFrame *parent = nsnull;
+  gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent);
+  gfxRect result = aRect;
+  
+  if (parent && parent != aStopAtAncestor) {
+      result = InvertGfxRectToAncestor(parent, aRect, aStopAtAncestor);
+  }
+
+  result = ctm.Inverse().ProjectRectBounds(result);
+  return result;
 }
 
 nsPoint
 nsLayoutUtils::InvertTransformsToRoot(nsIFrame *aFrame,
                                       const nsPoint &aPoint)
 {
-  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
-
-  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
-   * the point.
-   */
-  gfx3DMatrix ctm = GetTransformToAncestor(aFrame);
-
-  /* If the ctm is singular, hand back (0, 0) as a sentinel. */
-  if (ctm.IsSingular())
-    return nsPoint(0, 0);
-
-  /* TODO: Correctly handle 3d transforms when they start being used */
-
-  /* Otherwise, invert the CTM and use it to transform the point. */
-  return MatrixTransformPoint(aPoint, ctm.Invert(), aFrame->PresContext()->AppUnitsPerDevPixel());
+    float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
+    gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor),
+                    NSAppUnitsToFloatPixels(aPoint.y, factor));
+    
+    result = InvertTransformsToAncestor(aFrame, result);
+   
+    return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
+                   NSFloatPixelsToAppUnits(float(result.y), factor));
+}
+
+nsRect 
+nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame,
+                                               const nsRect &aRect,
+                                               nsIFrame* aStopAtAncestor)
+{
+    float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
+    gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor),
+                   NSAppUnitsToFloatPixels(aRect.y, factor),
+                   NSAppUnitsToFloatPixels(aRect.width, factor),
+                   NSAppUnitsToFloatPixels(aRect.height, factor));
+
+    result = InvertGfxRectToAncestor(aFrame, result, aStopAtAncestor);
+
+    return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
+                  NSFloatPixelsToAppUnits(float(result.y), factor),
+                  NSFloatPixelsToAppUnits(float(result.width), factor),
+                  NSFloatPixelsToAppUnits(float(result.height), factor));
 }
 
 static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) {
   nsIntPoint offset(0, 0);
   nsIWidget* parent = aWidget->GetParent();
   while (parent) {
     nsIntRect bounds;
     aWidget->GetBounds(bounds);
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -505,41 +505,34 @@ public:
    * @param aIgnoreRootScrollFrame whether or not the display list builder
    * should ignore the root scroll frame.
    */
   static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
                                    nsTArray<nsIFrame*> &aOutFrames,
                                    PRBool aShouldIgnoreSuppression = PR_FALSE,
                                    PRBool aIgnoreRootScrollFrame = PR_FALSE);
 
-  /**
-   * Returns the CTM at the specified frame. This matrix can be used to map
-   * coordinates from aFrame's to aStopAtAncestor's coordinate system.
-   *
-   * @param aFrame The frame at which we should calculate the CTM.
-   * @param aStopAtAncestor is an ancestor frame to stop at. If it's nsnull,
-   * matrix accumulating stops at root.
-   * @return The CTM at the specified frame.
-   */
-  static gfx3DMatrix GetTransformToAncestor(nsIFrame *aFrame,
-                                            nsIFrame* aStopAtAncestor = nsnull);
+  
+
+  static nsRect TransformRectToBoundsInAncestor(nsIFrame* aFrame,
+                                                const nsRect& aRect,
+                                                nsIFrame* aStopAtAncestor);
 
   /**
    * Given a point in the global coordinate space, returns that point expressed
    * in the coordinate system of aFrame.  This effectively inverts all transforms
    * between this point and the root frame.
    *
    * @param aFrame The frame that acts as the coordinate space container.
    * @param aPoint The point, in the global space, to get in the frame-local space.
    * @return aPoint, expressed in aFrame's canonical coordinate space.
    */
   static nsPoint InvertTransformsToRoot(nsIFrame* aFrame,
                                         const nsPoint &aPt);
 
-
   /**
    * Helper function that, given a rectangle and a matrix, returns the smallest
    * rectangle containing the image of the source rectangle.
    *
    * @param aBounds The rectangle to transform.
    * @param aMatrix The matrix to transform it with.
    * @param aFactor The number of app units per graphics unit.
    * @return The smallest rect that contains the image of aBounds.
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4539,17 +4539,17 @@ nsIFrame::GetTransformMatrix(nsIFrame **
     NS_ASSERTION(*aOutAncestor, "Cannot transform the viewport frame!");
     nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
     PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel();
 
     gfx3DMatrix result =
       nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0),
                                                       scaleFactor);
     /* Combine the raw transform with a translation to our parent. */
-    result = result * gfx3DMatrix::Translation
+    result *= gfx3DMatrix::Translation
       (NSAppUnitsToFloatPixels(delta.x, scaleFactor),
        NSAppUnitsToFloatPixels(delta.y, scaleFactor),
        0.0f);
     return result;
   }
   
   /* Otherwise, we're not transformed.  In that case, we'll walk up the frame
    * tree until we either hit the root frame or something that may be