Bug 1157984 - Part 3: Correct bounding box transformations to support projections and correct clipping when transforming behind the camera,r=vlad
authorKearwood (Kip) Gilbert <kgilbert@mozilla.com>
Thu, 06 Aug 2015 17:26:09 -0700
changeset 260434 9d8134f02763653040aa35520a8012a141c92a42
parent 260433 3baf8fb8d1f0e27a110c7a60c252539a90d86616
child 260435 821a65601cc0ce5a4961bd11e10a083f61c16f9e
push id29311
push usercbook@mozilla.com
push dateWed, 02 Sep 2015 11:53:18 +0000
treeherdermozilla-central@1b687fcb5213 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvlad
bugs1157984
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1157984 - Part 3: Correct bounding box transformations to support projections and correct clipping when transforming behind the camera,r=vlad - Update callsites of Matrix4x4::TransformBounds to use Matrix4x4::TransformAndClipBounds.
gfx/2d/Rect.h
gfx/layers/LayerTreeInvalidation.cpp
gfx/layers/basic/BasicCompositor.cpp
gfx/layers/basic/BasicLayerManager.cpp
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
gfx/layers/opengl/CompositorOGL.cpp
gfx/src/nsRegion.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
--- a/gfx/2d/Rect.h
+++ b/gfx/2d/Rect.h
@@ -138,16 +138,32 @@ struct RectTyped :
     RectTyped(const PointTyped<units, F>& aPos, const SizeTyped<units, F>& aSize) :
         Super(aPos, aSize) {}
     RectTyped(F _x, F _y, F _width, F _height) :
         Super(_x, _y, _width, _height) {}
     explicit RectTyped(const IntRectTyped<units>& rect) :
         Super(F(rect.x), F(rect.y),
               F(rect.width), F(rect.height)) {}
 
+    // Returns the largest rectangle that can be represented with 32-bit
+    // signed integers, centered around a point at 0,0.  As BaseRect's represent
+    // the dimensions as a top-left point with a width and height, the width
+    // and height will be the largest positive 32-bit value.  The top-left
+    // position coordinate is divided by two to center the rectangle around a
+    // point at 0,0.
+    static RectTyped<units, F> MaxIntRect()
+    {
+      return RectTyped<units, F>(
+        -std::numeric_limits<int32_t>::max() * 0.5,
+        -std::numeric_limits<int32_t>::max() * 0.5,
+        std::numeric_limits<int32_t>::max(),
+        std::numeric_limits<int32_t>::max()
+      );
+    };
+
     void NudgeToIntegers()
     {
       NudgeToInteger(&(this->x));
       NudgeToInteger(&(this->y));
       NudgeToInteger(&(this->width));
       NudgeToInteger(&(this->height));
     }
 
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -38,17 +38,17 @@ UniquePtr<LayerPropertiesBase> CloneLaye
 static IntRect
 TransformRect(const IntRect& aRect, const Matrix4x4& aTransform)
 {
   if (aRect.IsEmpty()) {
     return IntRect();
   }
 
   Rect rect(aRect.x, aRect.y, aRect.width, aRect.height);
-  rect = aTransform.TransformBounds(rect);
+  rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect());
   rect.RoundOut();
 
   IntRect intRect;
   if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) {
     return IntRect();
   }
 
   return intRect;
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -353,30 +353,25 @@ BasicCompositor::DrawQuad(const gfx::Rec
     dest = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(RoundOut(aRect).Size(), SurfaceFormat::B8G8R8A8);
     if (!dest) {
       return;
     }
 
     dest->SetTransform(Matrix::Translation(-aRect.x, -aRect.y));
 
     // Get the bounds post-transform.
-    new3DTransform = aTransform;
-    gfxRect bounds = ThebesRect(aRect);
-    bounds.TransformBounds(new3DTransform);
-    bounds.IntersectRect(bounds, gfxRect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height));
-
-    transformBounds = ToRect(bounds);
+    transformBounds = aTransform.TransformAndClipBounds(aRect, Rect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height));
     transformBounds.RoundOut();
 
     // Propagate the coordinate offset to our 2D draw target.
     newTransform = Matrix::Translation(transformBounds.x, transformBounds.y);
 
     // When we apply the 3D transformation, we do it against a temporary
     // surface, so undo the coordinate offset.
-    new3DTransform = Matrix4x4::Translation(aRect.x, aRect.y, 0) * new3DTransform;
+    new3DTransform = Matrix4x4::Translation(aRect.x, aRect.y, 0) * aTransform;
   }
 
   newTransform.PostTranslate(-offset.x, -offset.y);
   buffer->SetTransform(newTransform);
 
   RefPtr<SourceSurface> sourceMask;
   Matrix maskTransform;
   if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -756,24 +756,22 @@ Transform(const gfxImageSurface* aDest,
  */
 static already_AddRefed<gfxASurface>
 Transform3D(RefPtr<SourceSurface> aSource,
             gfxContext* aDest,
             const gfxRect& aBounds,
             const Matrix4x4& aTransform,
             gfxRect& aDestRect)
 {
-  // Find the transformed rectangle of our layer.
-  gfxRect offsetRect = aBounds;
-  offsetRect.TransformBounds(aTransform);
-
-  // Intersect the transformed layer with the destination rectangle.
+  // Find the transformed rectangle of our layer, intersected with the
+  // destination rectangle.
   // This is in device space since we have an identity transform set on aTarget.
-  aDestRect = aDest->GetClipExtents();
-  aDestRect.IntersectRect(aDestRect, offsetRect);
+  aDestRect = ThebesRect(aTransform.TransformAndClipBounds(
+                ToRectDouble(aBounds),
+                ToRectDouble(aDest->GetClipExtents())));
   aDestRect.RoundOut();
 
   // Create a surface the size of the transformed object.
   nsRefPtr<gfxASurface> dest = aDest->CurrentSurface();
   nsRefPtr<gfxImageSurface> destImage = new gfxImageSurface(IntSize(aDestRect.width,
                                                                     aDestRect.height),
                                                             gfxImageFormat::ARGB32);
   gfxPoint offset = aDestRect.TopLeft();
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -1014,17 +1014,18 @@ SubtractTransformedRegion(nsIntRegion& a
   if (aRegionToSubtract.IsEmpty()) {
     return;
   }
 
   // For each rect in the region, find out its bounds in screen space and
   // subtract it from the screen region.
   nsIntRegionRectIterator it(aRegionToSubtract);
   while (const IntRect* rect = it.Next()) {
-    Rect incompleteRect = aTransform.TransformBounds(ToRect(*rect));
+    Rect incompleteRect = aTransform.TransformAndClipBounds(ToRect(*rect),
+                                                            Rect::MaxIntRect());
     aRegion.Sub(aRegion, IntRect(incompleteRect.x,
                                    incompleteRect.y,
                                    incompleteRect.width,
                                    incompleteRect.height));
   }
 }
 
 /* static */ void
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -549,17 +549,17 @@ RenderWithAllMasks(Layer* aLayer, Compos
   // For parts (1) and (2) we need to allocate intermediate surfaces to render
   // into. The final mask gets rendered into the original render target.
 
   // Calculate the size of the intermediate surfaces.
   gfx::Rect visibleRect(aLayer->GetEffectiveVisibleRegion().GetBounds());
   gfx::Matrix4x4 transform = aLayer->GetEffectiveTransform();
   // TODO: Use RenderTargetIntRect and TransformTo<...> here
   gfx::IntRect surfaceRect =
-    RoundedOut(transform.TransformBounds(visibleRect)).Intersect(aClipRect);
+    RoundedOut(transform.TransformAndClipBounds(visibleRect, gfx::Rect(aClipRect)));
   if (surfaceRect.IsEmpty()) {
     return;
   }
 
   RefPtr<CompositingRenderTarget> originalTarget =
     aCompositor->GetCurrentRenderTarget();
 
   RefPtr<CompositingRenderTarget> firstTarget =
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -982,17 +982,17 @@ CompositorOGL::DrawQuad(const Rect& aRec
 
   if (aEffectChain.mPrimaryEffect->mType == EffectTypes::VR_DISTORTION) {
     DrawVRDistortion(aRect, aClipRect, aEffectChain, aOpacity, aTransform);
     return;
   }
 
   // XXX: This doesn't handle 3D transforms. It also doesn't handled rotated
   //      quads. Fix me.
-  Rect destRect = aTransform.TransformBounds(aRect);
+  Rect destRect = aTransform.TransformAndClipBounds(aRect, aClipRect);
   mPixelsFilled += destRect.width * destRect.height;
 
   IntPoint offset = mCurrentRenderTarget->GetOrigin();
 
   // Do a simple culling if this rect is out of target buffer.
   // Inflate a small size to avoid some numerical imprecision issue.
   destRect.Inflate(1, 1);
   destRect.MoveBy(-offset);
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -607,22 +607,22 @@ nsRegion& nsRegion::ScaleInverseRoundOut
 
 static mozilla::gfx::IntRect
 TransformRect(const mozilla::gfx::IntRect& aRect, const mozilla::gfx::Matrix4x4& aTransform)
 {
     if (aRect.IsEmpty()) {
         return mozilla::gfx::IntRect();
     }
 
-    gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height);
-    rect.TransformBounds(aTransform);
+    mozilla::gfx::RectDouble rect(aRect.x, aRect.y, aRect.width, aRect.height);
+    rect = aTransform.TransformAndClipBounds(rect, mozilla::gfx::RectDouble::MaxIntRect());
     rect.RoundOut();
 
     mozilla::gfx::IntRect intRect;
-    if (!gfxUtils::GfxRectToIntRect(rect, &intRect)) {
+    if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) {
         return mozilla::gfx::IntRect();
     }
 
     return intRect;
 }
 
 nsRegion& nsRegion::Transform (const mozilla::gfx::Matrix4x4 &aTransform)
 {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2323,32 +2323,44 @@ nsLayoutUtils::RoundedRectIntersectsRect
 }
 
 nsRect
 nsLayoutUtils::MatrixTransformRectOut(const nsRect &aBounds,
                                       const Matrix4x4 &aMatrix, float aFactor)
 {
   nsRect outside = aBounds;
   outside.ScaleRoundOut(1/aFactor);
-  gfxRect image = gfxRect(outside.x, outside.y, outside.width, outside.height);
-  image.TransformBounds(aMatrix);
-  return RoundGfxRectToAppRect(image, aFactor);
+  RectDouble image = RectDouble(outside.x, outside.y,
+                                outside.width, outside.height);
+
+  RectDouble maxBounds = RectDouble(double(nscoord_MIN) / aFactor * 0.5,
+                                    double(nscoord_MIN) / aFactor * 0.5,
+                                    double(nscoord_MAX) / aFactor,
+                                    double(nscoord_MAX) / aFactor);
+  image = aMatrix.TransformAndClipBounds(image, maxBounds);
+  return RoundGfxRectToAppRect(ThebesRect(image), aFactor);
 }
 
 nsRect
 nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds,
                                    const Matrix4x4 &aMatrix, float aFactor)
 {
-  gfxRect image = gfxRect(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
-                          NSAppUnitsToDoublePixels(aBounds.y, aFactor),
-                          NSAppUnitsToDoublePixels(aBounds.width, aFactor),
-                          NSAppUnitsToDoublePixels(aBounds.height, aFactor));
-  image.TransformBounds(aMatrix);
-
-  return RoundGfxRectToAppRect(image, aFactor);
+  RectDouble image = RectDouble(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
+                                NSAppUnitsToDoublePixels(aBounds.y, aFactor),
+                                NSAppUnitsToDoublePixels(aBounds.width, aFactor),
+                                NSAppUnitsToDoublePixels(aBounds.height, aFactor));
+
+  RectDouble maxBounds = RectDouble(double(nscoord_MIN) / aFactor * 0.5,
+                                    double(nscoord_MIN) / aFactor * 0.5,
+                                    double(nscoord_MAX) / aFactor,
+                                    double(nscoord_MAX) / aFactor);
+
+  image = aMatrix.TransformAndClipBounds(image, maxBounds);
+
+  return RoundGfxRectToAppRect(ThebesRect(image), aFactor);
 }
 
 nsPoint
 nsLayoutUtils::MatrixTransformPoint(const nsPoint &aPoint,
                                     const Matrix4x4 &aMatrix, float aFactor)
 {
   gfxPoint image = gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
                             NSAppUnitsToFloatPixels(aPoint.y, aFactor));
@@ -2682,17 +2694,21 @@ TransformGfxRectToAncestor(nsIFrame *aFr
                            bool* aPreservesAxisAlignedRectangles = nullptr)
 {
   Matrix4x4 ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
   if (aPreservesAxisAlignedRectangles) {
     Matrix matrix2d;
     *aPreservesAxisAlignedRectangles =
       ctm.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles();
   }
-  return ctm.TransformBounds(aRect);
+  Rect maxBounds = Rect(-std::numeric_limits<float>::max() * 0.5,
+                        -std::numeric_limits<float>::max() * 0.5,
+                        std::numeric_limits<float>::max(),
+                        std::numeric_limits<float>::max());
+  return ctm.TransformAndClipBounds(aRect, maxBounds);
 }
 
 static SVGTextFrame*
 GetContainingSVGTextFrame(nsIFrame* aFrame)
 {
   if (!aFrame->IsSVGText()) {
     return nullptr;
   }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -118,16 +118,17 @@ class nsLayoutUtils
   typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
   typedef mozilla::IntrinsicSize IntrinsicSize;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::gfx::Color Color;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Point Point;
   typedef mozilla::gfx::Rect Rect;
+  typedef mozilla::gfx::RectDouble RectDouble;
   typedef mozilla::gfx::Matrix4x4 Matrix4x4;
   typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
   typedef mozilla::gfx::StrokeOptions StrokeOptions;
   typedef mozilla::image::DrawResult DrawResult;
 
 public:
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef FrameMetrics::ViewID ViewID;