Bug 607653 - avoid temporary fbos/textures on transformed layers, when possible. r=roc a=approval2.0
authorOleg Romashin <romaxa@gmail.com>
Wed, 24 Nov 2010 11:35:21 +0200
changeset 58191 8fff1c186b30d3985d068f84af7cfff6c695efae
parent 58190 436ae4ff202beca009d72ea41aa913036b2ebca9
child 58192 15f8aa161de53233d941536bf55b461fac60fb10
push idunknown
push userunknown
push dateunknown
reviewersroc, approval2
bugs607653
milestone2.0b8pre
Bug 607653 - avoid temporary fbos/textures on transformed layers, when possible. r=roc a=approval2.0
content/svg/content/src/nsSVGFilters.cpp
gfx/layers/Layers.cpp
gfx/layers/d3d10/ContainerLayerD3D10.cpp
gfx/layers/d3d9/ContainerLayerD3D9.cpp
gfx/layers/opengl/ContainerLayerOGL.cpp
gfx/thebes/Makefile.in
gfx/thebes/gfxBlur.cpp
gfx/thebes/gfxBlur.h
gfx/thebes/gfxMatrix.h
gfx/thebes/gfxThebesUtils.cpp
gfx/thebes/gfxThebesUtils.h
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.h
layout/base/FrameLayerBuilder.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/svg/base/src/nsSVGFilterFrame.cpp
layout/svg/base/src/nsSVGFilterInstance.cpp
layout/svg/base/src/nsSVGUtils.cpp
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -62,16 +62,17 @@
 #include "nsImageLoadingContent.h"
 #include "imgIContainer.h"
 #include "nsNetUtil.h"
 #include "nsSVGPreserveAspectRatio.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsSVGFilterElement.h"
 #include "nsSVGString.h"
 #include "nsSVGEffects.h"
+#include "gfxUtils.h"
 
 #if defined(XP_WIN) 
 // Prevent Windows redefining LoadImage
 #ifndef WINCE
 #undef LoadImage
 #endif
 #endif
 
@@ -169,17 +170,17 @@ nsSVGFE::SetupScalingFilter(nsSVGFilterI
   // overflow) or zero size (large kernel unit)
   if (overflow || scaledSize.width <= 0 || scaledSize.height <= 0)
     return result;
 
   gfxRect r(aDataRect.x, aDataRect.y, aDataRect.width, aDataRect.height);
   r.Scale(gfxFloat(scaledSize.width)/aTarget->mImage->Width(),
           gfxFloat(scaledSize.height)/aTarget->mImage->Height());
   r.RoundOut();
-  if (NS_FAILED(nsLayoutUtils::GfxRectToIntRect(r, &result.mDataRect)))
+  if (!gfxUtils::GfxRectToIntRect(r, &result.mDataRect))
     return result;
   
 #ifdef DEBUG_tor
   fprintf(stderr, "scaled size %d %d\n", scaledSize.width, scaledSize.height);
 #endif
   result.mSource = new gfxImageSurface(scaledSize,
                                        gfxASurface::ImageFormatARGB32);
   result.mTarget = new gfxImageSurface(scaledSize,
@@ -2958,19 +2959,19 @@ nsSVGFETileElement::Filter(nsSVGFilterIn
           rect.x, rect.y, rect.width, rect.height);
 #endif
   // XXX This code depends on the surface rect containing the filter
   // primitive subregion. ComputeTargetBBox, ComputeNeededSourceBBoxes
   // and ComputeChangeBBox are all pessimal, so that will normally be OK,
   // but nothing clips mFilterPrimitiveSubregion so this should be changed.
 
   nsIntRect tile;
-  nsresult res = nsLayoutUtils::GfxRectToIntRect(aSources[0]->mFilterPrimitiveSubregion, &tile);
-
-  NS_ENSURE_SUCCESS(res, res); // asserts on failure (not 
+  PRBool res = gfxUtils::GfxRectToIntRect(aSources[0]->mFilterPrimitiveSubregion, &tile);
+
+  NS_ENSURE_TRUE(res, NS_ERROR_FAILURE); // asserts on failure (not 
   if (tile.IsEmpty())
     return NS_OK;
 
   const nsIntRect &surfaceRect = instance->GetSurfaceRect();
   if (!tile.Intersects(surfaceRect)) {
     // nothing to draw
     return NS_OK;
   }
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -328,17 +328,19 @@ ContainerLayer::DefaultComputeEffectiveT
   mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), &residual);
 
   PRBool useIntermediateSurface;
   float opacity = GetEffectiveOpacity();
   if (opacity != 1.0f && HasMultipleChildren()) {
     useIntermediateSurface = PR_TRUE;
   } else {
     useIntermediateSurface = PR_FALSE;
-    if (!mEffectiveTransform.IsIdentity()) {
+    gfxMatrix contTransform;
+    if (!mEffectiveTransform.Is2D(&contTransform) ||
+        !contTransform.PreservesAxisAlignedRectangles()) {
       for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
         const nsIntRect *clipRect = child->GetEffectiveClipRect();
         /* We can't (easily) forward our transform to children with a non-empty clip
          * rect since it would need to be adjusted for the transform.
          * TODO: This is easily solvable for translation/scaling transforms.
          */
         if (clipRect && !clipRect->IsEmpty() && !child->GetVisibleRegion().IsEmpty()) {
           useIntermediateSurface = PR_TRUE;
--- a/gfx/layers/d3d10/ContainerLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ContainerLayerD3D10.cpp
@@ -32,16 +32,18 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "ContainerLayerD3D10.h"
 #include "nsAlgorithm.h"
+#include "gfxUtils.h"
+#include "nsRect.h"
 
 namespace mozilla {
 namespace layers {
 
 ContainerLayerD3D10::ContainerLayerD3D10(LayerManagerD3D10 *aManager)
   : ContainerLayer(aManager, NULL)
   , LayerD3D10(aManager)
 {
@@ -138,16 +140,25 @@ LayerD3D10*
 ContainerLayerD3D10::GetFirstChildD3D10()
 {
   if (!mFirstChild) {
     return nsnull;
   }
   return static_cast<LayerD3D10*>(mFirstChild->ImplData());
 }
 
+static inline LayerD3D10*
+GetNextSiblingD3D10(LayerD3D10* aLayer)
+{
+   Layer* layer = aLayer->GetLayer()->GetNextSibling();
+   return layer ? static_cast<LayerD3D10*>(layer->
+                                           ImplData())
+                : nsnull;
+}
+
 void
 ContainerLayerD3D10::RenderLayer()
 {
   float renderTargetOffset[] = { 0, 0 };
 
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
   float opacity = GetEffectiveOpacity();
   PRBool useIntermediate = UseIntermediateSurface();
@@ -155,16 +166,17 @@ ContainerLayerD3D10::RenderLayer()
   nsRefPtr<ID3D10RenderTargetView> previousRTView;
   nsRefPtr<ID3D10Texture2D> renderTexture;
   nsRefPtr<ID3D10RenderTargetView> rtView;
   float previousRenderTargetOffset[2];
   nsIntSize previousViewportSize;
 
   gfx3DMatrix oldViewMatrix;
 
+  gfxMatrix contTransform;
   if (useIntermediate) {
     device()->OMGetRenderTargets(1, getter_AddRefs(previousRTView), NULL);
  
     D3D10_TEXTURE2D_DESC desc;
     memset(&desc, 0, sizeof(D3D10_TEXTURE2D_DESC));
     desc.ArraySize = 1;
     desc.MipLevels = 1;
     desc.Width = visibleRect.width;
@@ -187,24 +199,36 @@ ContainerLayerD3D10::RenderLayer()
 
     renderTargetOffset[0] = (float)visibleRect.x;
     renderTargetOffset[1] = (float)visibleRect.y;
     effect()->GetVariableByName("vRenderTargetOffset")->
       SetRawValue(renderTargetOffset, 0, 8);
 
     previousViewportSize = mD3DManager->GetViewport();
     mD3DManager->SetViewport(nsIntSize(visibleRect.Size()));
+  } else {
+#ifdef DEBUG
+    PRBool is2d =
+#endif
+    GetEffectiveTransform().Is2D(&contTransform);
+    NS_ASSERTION(is2d, "Transform must be 2D");
   }
 
   /*
    * Render this container's contents.
    */
-  LayerD3D10 *layerToRender = GetFirstChildD3D10();
-  while (layerToRender) {
-    const nsIntRect *clipRect = layerToRender->GetLayer()->GetClipRect();
+  for (LayerD3D10* layerToRender = GetFirstChildD3D10();
+       layerToRender != nsnull;
+       layerToRender = GetNextSiblingD3D10(layerToRender)) {
+
+    const nsIntRect* clipRect = layerToRender->GetLayer()->GetClipRect();
+    if ((clipRect && clipRect->IsEmpty()) ||
+        layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) {
+      continue;
+    }
 
     D3D10_RECT oldScissor;
     if (clipRect || useIntermediate) {
       UINT numRects = 1;
       device()->RSGetScissorRects(&numRects, &oldScissor);
 
       RECT r;
       if (clipRect) {
@@ -217,29 +241,43 @@ ContainerLayerD3D10::RenderLayer()
         r.left = 0;
         r.top = 0;
         r.right = visibleRect.width;
         r.bottom = visibleRect.height;
       }
 
       D3D10_RECT d3drect;
       if (!useIntermediate) {
+        if (clipRect) {
+          gfxRect cliprect(r.left, r.top, r.left + r.right, r.top + r.bottom);
+          gfxRect trScissor = contTransform.TransformBounds(cliprect);
+          trScissor.Round();
+          nsIntRect trIntScissor;
+          if (gfxUtils::GfxRectToIntRect(trScissor, &trIntScissor)) {
+            r.left = trIntScissor.x;
+            r.top = trIntScissor.y;
+            r.right = trIntScissor.XMost();
+            r.bottom = trIntScissor.YMost();
+          } else {
+            r.left = 0;
+            r.top = 0;
+            r.right = visibleRect.width;
+            r.bottom = visibleRect.height;
+            clipRect = nsnull;
+          }
+        }
         // Scissor rect should be an intersection of the old and current scissor.
         r.left = NS_MAX<PRInt32>(oldScissor.left, r.left);
         r.right = NS_MIN<PRInt32>(oldScissor.right, r.right);
         r.top = NS_MAX<PRInt32>(oldScissor.top, r.top);
         r.bottom = NS_MIN<PRInt32>(oldScissor.bottom, r.bottom);
       }
 
       if (r.left >= r.right || r.top >= r.bottom) {
         // Entire layer's clipped out, don't bother drawing.
-        Layer *nextSibling = layerToRender->GetLayer()->GetNextSibling();
-        layerToRender = nextSibling ? static_cast<LayerD3D10*>(nextSibling->
-                                                              ImplData())
-                                    : nsnull;
         continue;
       }
 
       d3drect.left = NS_MAX<PRInt32>(r.left, 0);
       d3drect.top = NS_MAX<PRInt32>(r.top, 0);
       d3drect.bottom = r.bottom;
       d3drect.right = r.right;
 
@@ -247,21 +285,16 @@ ContainerLayerD3D10::RenderLayer()
     }
 
     // SetScissorRect
     layerToRender->RenderLayer();
 
     if (clipRect || useIntermediate) {
       device()->RSSetScissorRects(1, &oldScissor);
     }
-
-    Layer *nextSibling = layerToRender->GetLayer()->GetNextSibling();
-    layerToRender = nextSibling ? static_cast<LayerD3D10*>(nextSibling->
-                                                          ImplData())
-                                : nsnull;
   }
 
   if (useIntermediate) {
     mD3DManager->SetViewport(previousViewportSize);
     ID3D10RenderTargetView *rtView = previousRTView;
     device()->OMSetRenderTargets(1, &rtView, NULL);
     effect()->GetVariableByName("vRenderTargetOffset")->
       SetRawValue(previousRenderTargetOffset, 0, 8);
--- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp
@@ -31,16 +31,18 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "ContainerLayerD3D9.h"
+#include "gfxUtils.h"
+#include "nsRect.h"
 
 namespace mozilla {
 namespace layers {
 
 ContainerLayerD3D9::ContainerLayerD3D9(LayerManagerD3D9 *aManager)
   : ContainerLayer(aManager, NULL)
   , LayerD3D9(aManager)
 {
@@ -137,29 +139,39 @@ LayerD3D9*
 ContainerLayerD3D9::GetFirstChildD3D9()
 {
   if (!mFirstChild) {
     return nsnull;
   }
   return static_cast<LayerD3D9*>(mFirstChild->ImplData());
 }
 
+static inline LayerD3D9*
+GetNextSiblingD3D9(LayerD3D9* aLayer)
+{
+   Layer* layer = aLayer->GetLayer()->GetNextSibling();
+   return layer ? static_cast<LayerD3D9*>(layer->
+                                          ImplData())
+                 : nsnull;
+}
+
 void
 ContainerLayerD3D9::RenderLayer()
 {
   nsRefPtr<IDirect3DSurface9> previousRenderTarget;
   nsRefPtr<IDirect3DTexture9> renderTexture;
   float previousRenderTargetOffset[4];
   RECT oldClipRect;
   float renderTargetOffset[] = { 0, 0, 0, 0 };
   float oldViewMatrix[4][4];
 
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
   PRBool useIntermediate = UseIntermediateSurface();
 
+  gfxMatrix contTransform;
   if (useIntermediate) {
     device()->GetRenderTarget(0, getter_AddRefs(previousRenderTarget));
     device()->CreateTexture(visibleRect.width, visibleRect.height, 1,
                             D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
                             D3DPOOL_DEFAULT, getter_AddRefs(renderTexture),
                             NULL);
     nsRefPtr<IDirect3DSurface9> renderSurface;
     renderTexture->GetSurfaceLevel(0, getter_AddRefs(renderSurface));
@@ -177,24 +189,37 @@ ContainerLayerD3D9::RenderLayer()
      */
     viewMatrix._11 = 2.0f / visibleRect.width;
     viewMatrix._22 = -2.0f / visibleRect.height;
     viewMatrix._41 = -1.0f;
     viewMatrix._42 = 1.0f;
 
     device()->GetVertexShaderConstantF(CBmProjection, &oldViewMatrix[0][0], 4);
     device()->SetVertexShaderConstantF(CBmProjection, &viewMatrix._11, 4);
+  } else {
+#ifdef DEBUG
+    PRBool is2d =
+#endif
+    GetEffectiveTransform().Is2D(&contTransform);
+    NS_ASSERTION(is2d, "Transform must be 2D");
   }
 
   /*
    * Render this container's contents.
    */
-  LayerD3D9 *layerToRender = GetFirstChildD3D9();
-  while (layerToRender) {
-    const nsIntRect *clipRect = layerToRender->GetLayer()->GetClipRect();
+  for (LayerD3D9* layerToRender = GetFirstChildD3D9();
+       layerToRender != nsnull;
+       layerToRender = GetNextSiblingD3D9(layerToRender)) {
+
+    const nsIntRect* clipRect = layerToRender->GetLayer()->GetClipRect();
+    if ((clipRect && clipRect->IsEmpty()) ||
+        layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) {
+      continue;
+    }
+
     if (clipRect || useIntermediate) {
       RECT r;
       device()->GetScissorRect(&oldClipRect);
       if (clipRect) {
         r.left = (LONG)(clipRect->x - renderTargetOffset[0]);
         r.top = (LONG)(clipRect->y - renderTargetOffset[1]);
         r.right = (LONG)(clipRect->x - renderTargetOffset[0] + clipRect->width);
         r.bottom = (LONG)(clipRect->y - renderTargetOffset[1] + clipRect->height);
@@ -207,16 +232,35 @@ ContainerLayerD3D9::RenderLayer()
 
       nsRefPtr<IDirect3DSurface9> renderSurface;
       device()->GetRenderTarget(0, getter_AddRefs(renderSurface));
 
       D3DSURFACE_DESC desc;
       renderSurface->GetDesc(&desc);
 
       if (!useIntermediate) {
+        // Transform clip rect
+        if (clipRect) {
+          gfxRect cliprect(r.left, r.top, r.left + r.right, r.top + r.bottom);
+          gfxRect trScissor = contTransform.TransformBounds(cliprect);
+          trScissor.Round();
+          nsIntRect trIntScissor;
+          if (gfxUtils::GfxRectToIntRect(trScissor, &trIntScissor)) {
+            r.left = trIntScissor.x;
+            r.top = trIntScissor.y;
+            r.right = trIntScissor.XMost();
+            r.bottom = trIntScissor.YMost();
+          } else {
+            r.left = 0;
+            r.top = 0;
+            r.right = visibleRect.width;
+            r.bottom = visibleRect.height;
+            clipRect = nsnull;
+          }
+        }
         // Intersect with current clip rect.
         r.left = NS_MAX<PRInt32>(oldClipRect.left, r.left);
         r.right = NS_MIN<PRInt32>(oldClipRect.right, r.right);
         r.top = NS_MAX<PRInt32>(oldClipRect.top, r.top);
         r.bottom = NS_MAX<PRInt32>(oldClipRect.bottom, r.bottom);
       } else {
         // > 0 is implied during the intersection when useIntermediate == true;
         r.left = NS_MAX<LONG>(0, r.left);
@@ -229,20 +273,17 @@ ContainerLayerD3D9::RenderLayer()
     }
 
     layerToRender->RenderLayer();
 
     if (clipRect || useIntermediate) {
       device()->SetScissorRect(&oldClipRect);
     }
 
-    Layer *nextSibling = layerToRender->GetLayer()->GetNextSibling();
-    layerToRender = nextSibling ? static_cast<LayerD3D9*>(nextSibling->
-                                                          ImplData())
-                                : nsnull;
+    continue;
   }
 
   if (useIntermediate) {
     device()->SetRenderTarget(0, previousRenderTarget);
     device()->SetVertexShaderConstantF(CBvRenderTargetOffset, previousRenderTargetOffset, 1);
     device()->SetVertexShaderConstantF(CBmProjection, &oldViewMatrix[0][0], 4);
 
     device()->SetVertexShaderConstantF(CBvLayerQuad,
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp
+++ b/gfx/layers/opengl/ContainerLayerOGL.cpp
@@ -31,16 +31,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "ContainerLayerOGL.h"
+#include "gfxUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 template<class Container>
 static void
 ContainerInsertAfter(Container* aContainer, Layer* aChild, Layer* aAfter)
 {
@@ -123,16 +124,25 @@ ContainerDestroy(Container* aContainer)
     while (aContainer->mFirstChild) {
       aContainer->GetFirstChildOGL()->Destroy();
       aContainer->RemoveChild(aContainer->mFirstChild);
     }
     aContainer->mDestroyed = PR_TRUE;
   }
 }
 
+static inline LayerOGL*
+GetNextSibling(LayerOGL* aLayer)
+{
+   Layer* layer = aLayer->GetLayer()->GetNextSibling();
+   return layer ? static_cast<LayerOGL*>(layer->
+                                         ImplData())
+                 : nsnull;
+}
+
 template<class Container>
 static void
 ContainerRender(Container* aContainer,
                 int aPreviousFrameBuffer,
                 const nsIntPoint& aOffset,
                 LayerManagerOGL* aManager)
 {
   /**
@@ -145,44 +155,67 @@ ContainerRender(Container* aContainer,
   nsIntRect visibleRect = aContainer->GetEffectiveVisibleRegion().GetBounds();
 
   nsIntRect cachedScissor = aContainer->gl()->ScissorRect();
   aContainer->gl()->PushScissorRect();
 
   float opacity = aContainer->GetEffectiveOpacity();
   const gfx3DMatrix& transform = aContainer->GetEffectiveTransform();
   bool needsFramebuffer = aContainer->UseIntermediateSurface();
+  gfxMatrix contTransform;
   if (needsFramebuffer) {
     aManager->CreateFBOWithTexture(visibleRect.width,
                                    visibleRect.height,
                                    &frameBuffer,
                                    &containerSurface);
     childOffset.x = visibleRect.x;
     childOffset.y = visibleRect.y;
 
     aContainer->gl()->PushViewportRect();
     aManager->SetupPipeline(visibleRect.width, visibleRect.height);
 
     aContainer->gl()->fScissor(0, 0, visibleRect.width, visibleRect.height);
     aContainer->gl()->fClearColor(0.0, 0.0, 0.0, 0.0);
     aContainer->gl()->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
   } else {
     frameBuffer = aPreviousFrameBuffer;
+#ifdef DEBUG
+    PRBool is2d =
+#endif
+    transform.Is2D(&contTransform);
+    NS_ASSERTION(is2d, "Transform must be 2D");
   }
 
   /**
    * Render this container's contents.
    */
-  LayerOGL *layerToRender = aContainer->GetFirstChildOGL();
-  while (layerToRender) {
+  for (LayerOGL* layerToRender = aContainer->GetFirstChildOGL();
+       layerToRender != nsnull;
+       layerToRender = GetNextSibling(layerToRender)) {
+
+    if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) {
+      continue;
+    }
+
     nsIntRect scissorRect(visibleRect);
 
     const nsIntRect *clipRect = layerToRender->GetLayer()->GetEffectiveClipRect();
     if (clipRect) {
+      if (clipRect->IsEmpty()) {
+        continue;
+      }
       scissorRect = *clipRect;
+      if (!needsFramebuffer) {
+        gfxRect r(scissorRect.x, scissorRect.y, scissorRect.width, scissorRect.height);
+        gfxRect trScissor = contTransform.TransformBounds(r);
+        trScissor.Round();
+        if (!gfxUtils::GfxRectToIntRect(trScissor, &scissorRect)) {
+          scissorRect = visibleRect;
+        }
+      }
     }
 
     if (needsFramebuffer) {
       scissorRect.MoveBy(- visibleRect.TopLeft());
     } else {
       if (!aPreviousFrameBuffer) {
         /**
          * glScissor coordinates are oriented with 0,0 being at the bottom left,
@@ -210,21 +243,16 @@ ContainerRender(Container* aContainer,
     } else {
       aContainer->gl()->fScissor(cachedScissor.x, 
                                  cachedScissor.y, 
                                  cachedScissor.width, 
                                  cachedScissor.height);
     }
 
     layerToRender->RenderLayer(frameBuffer, childOffset);
-
-    Layer *nextSibling = layerToRender->GetLayer()->GetNextSibling();
-    layerToRender = nextSibling ? static_cast<LayerOGL*>(nextSibling->
-                                                         ImplData())
-                                : nsnull;
   }
 
   aContainer->gl()->PopScissorRect();
 
   if (needsFramebuffer) {
     // Unbind the current framebuffer and rebind the previous one.
     
     // Restore the viewport
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -30,17 +30,16 @@ EXPORTS	= \
 	gfxPattern.h \
 	gfxPlatform.h \
 	gfxPoint.h \
 	gfxRect.h \
 	gfxSkipChars.h \
 	gfxTypes.h \
 	gfxTextRunCache.h \
 	gfxTextRunWordCache.h \
-	gfxThebesUtils.h \
 	gfxUtils.h \
 	gfxUserFontSet.h \
 	GLDefs.h \
 	GLContext.h \
 	GLContextSymbols.h \
 	GLContextProvider.h \
 	GLContextProviderImpl.h \
 	nsCoreAnimationSupport.h \
@@ -179,17 +178,16 @@ CPPSRCS	= \
 	gfxPath.cpp \
 	gfxPattern.cpp \
 	gfxPlatform.cpp \
 	gfxPlatformFontList.cpp \
 	gfxRect.cpp \
 	gfxSkipChars.cpp \
 	gfxTextRunCache.cpp \
 	gfxTextRunWordCache.cpp \
-	gfxThebesUtils.cpp \
 	gfxUserFontSet.cpp \
 	gfxUtils.cpp \
 	gfxUnicodeProperties.cpp \
 	gfxScriptItemizer.cpp \
 	gfxHarfBuzzShaper.cpp \
 	GLContext.cpp \
 	GLContextProviderOSMesa.cpp \
 	$(NULL)
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -83,18 +83,19 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRe
 
     if (aSkipRect) {
         // If we get passed a skip rect, we can lower the amount of
         // blurring/spreading we need to do. We convert it to nsIntRect to avoid
         // expensive int<->float conversions if we were to use gfxRect instead.
         gfxRect skipRect = *aSkipRect;
         skipRect.RoundIn();
         skipRect.Inset(aBlurRadius + aSpreadRadius);
-        mSkipRect = gfxThebesUtils::GfxRectToIntRect(skipRect);
-        nsIntRect shadowIntRect = gfxThebesUtils::GfxRectToIntRect(rect);
+        gfxUtils::GfxRectToIntRect(skipRect, &mSkipRect);
+        nsIntRect shadowIntRect;
+        gfxUtils::GfxRectToIntRect(rect, &shadowIntRect);
         mSkipRect.IntersectRect(mSkipRect, shadowIntRect);
         if (mSkipRect == shadowIntRect)
           return nsnull;
 
         mSkipRect -= shadowIntRect.TopLeft();
     } else {
         mSkipRect = nsIntRect(0, 0, 0, 0);
     }
--- a/gfx/thebes/gfxBlur.h
+++ b/gfx/thebes/gfxBlur.h
@@ -36,17 +36,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_BLUR_H
 #define GFX_BLUR_H
 
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "gfxTypes.h"
-#include "gfxThebesUtils.h"
+#include "gfxUtils.h"
+#include "nsRect.h"
 
 /**
  * Implementation of a triple box blur approximation of a Gaussian blur.
  *
  * A Gaussian blur is good for blurring because, when done independently
  * in the horizontal and vertical directions, it matches the result that
  * would be obtained using a different (rotated) set of axes.  A triple
  * box blur is a very close approximation of a Gaussian.
--- a/gfx/thebes/gfxMatrix.h
+++ b/gfx/thebes/gfxMatrix.h
@@ -260,15 +260,24 @@ public:
 
     /**
      * Snap matrix components that are close to integers
      * to integers. In particular, components that are integral when
      * converted to single precision are set to those integers.
      */
     void NudgeToIntegers(void);
 
+    /**
+     * Returns true if matrix is multiple of 90 degrees rotation with flipping,
+     * scaling and translation.
+     */
+    PRBool PreservesAxisAlignedRectangles() const {
+        return ((FuzzyEqual(xx, 0.0) && FuzzyEqual(yy, 0.0))
+            || (FuzzyEqual(xy, 0.0) && FuzzyEqual(yx, 0.0)));
+    }
+
 private:
     static PRBool FuzzyEqual(gfxFloat aV1, gfxFloat aV2) {
         return fabs(aV2 - aV1) < 1e-6;
     }
 };
 
 #endif /* GFX_MATRIX_H */
deleted file mode 100644
--- a/gfx/thebes/gfxThebesUtils.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is gfx thebes code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Michael Ventnor <m.ventnor@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "gfxThebesUtils.h"
-
-// Converts a gfxRect to an nsIntRect for speed
-nsIntRect
-gfxThebesUtils::GfxRectToIntRect(const gfxRect& aIn)
-{
-  nsIntRect result(PRInt32(aIn.X()), PRInt32(aIn.Y()),
-                   PRInt32(aIn.Width()), PRInt32(aIn.Height()));
-  NS_ASSERTION(gfxRect(result.x, result.y, result.width, result.height) == aIn,
-               "The given gfxRect isn't rounded properly!");
-  return result;
-}
deleted file mode 100644
--- a/gfx/thebes/gfxThebesUtils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is gfx thebes code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Michael Ventnor <m.ventnor@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "gfxRect.h"
-#include "nsRect.h"
-
-#ifndef GFX_THEBES_UTILS_H
-#define GFX_THEBES_UTILS_H
-
-class THEBES_API gfxThebesUtils
-{
-public:
-    /**
-     * Converts a gfxRect into nsIntRect for speed reasons.
-     */
-    static nsIntRect GfxRectToIntRect(const gfxRect& aIn);
-};
-
-#endif /* GFX_THEBES_UTILS_H */
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -453,8 +453,17 @@ gfxUtils::ClipToRegion(gfxContext* aCont
   ClipToRegionInternal(aContext, aRegion, PR_FALSE);
 }
 
 /*static*/ void
 gfxUtils::ClipToRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion)
 {
   ClipToRegionInternal(aContext, aRegion, PR_TRUE);
 }
+
+PRBool
+gfxUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut)
+{
+  *aOut = nsIntRect(PRInt32(aIn.X()), PRInt32(aIn.Y()),
+  PRInt32(aIn.Width()), PRInt32(aIn.Height()));
+  return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height) == aIn;
+}
+
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -39,16 +39,17 @@
 #define GFX_UTILS_H
 
 #include "gfxTypes.h"
 #include "gfxPattern.h"
 #include "gfxImageSurface.h"
 
 class gfxDrawable;
 class nsIntRegion;
+class nsIntRect;
 
 class THEBES_API gfxUtils {
 public:
     /*
      * Premultiply or Unpremultiply aSourceSurface, writing the result
      * to aDestSurface or back into aSourceSurface if aDestSurface is null.
      *
      * If aDestSurface is given, it must have identical format, dimensions, and
@@ -94,11 +95,18 @@ public:
      * Clip aContext to the region aRegion, snapping the rectangles.
      */
     static void ClipToRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion);
 
     /*
      * Convert image format to depth value
      */
     static int ImageFormatToDepth(gfxASurface::gfxImageFormat aFormat);
+
+    /**
+     * If aIn can be represented exactly using an nsIntRect (i.e.
+     * integer-aligned edges and coordinates in the PRInt32 range) then we
+     * set aOut to that rectangle, otherwise return failure.
+    */
+    static PRBool GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut);
 };
 
 #endif
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -40,16 +40,17 @@
 #include "nsDisplayList.h"
 #include "nsPresContext.h"
 #include "nsLayoutUtils.h"
 #include "Layers.h"
 #include "BasicLayers.h"
 #include "nsSubDocumentFrame.h"
 #include "nsCSSRendering.h"
 #include "nsCSSFrameConstructor.h"
+#include "gfxUtils.h"
 
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
 using namespace mozilla::layers;
 
 namespace mozilla {
@@ -742,17 +743,17 @@ SetVisibleRectForLayer(Layer* aLayer, co
   if (aLayer->GetTransform().Is2D(&transform)) {
     // if 'transform' is not invertible, then nothing will be displayed
     // for the layer, so it doesn't really matter what we do here
     transform.Invert();
     gfxRect layerVisible = transform.TransformBounds(
         gfxRect(aRect.x, aRect.y, aRect.width, aRect.height));
     layerVisible.RoundOut();
     nsIntRect visibleRect;
-    if (NS_FAILED(nsLayoutUtils::GfxRectToIntRect(layerVisible, &visibleRect))) {
+    if (!gfxUtils::GfxRectToIntRect(layerVisible, &visibleRect)) {
       visibleRect = nsIntRect(0, 0, 0, 0);
       NS_WARNING("Visible rect transformed out of bounds");
     }
     aLayer->SetVisibleRegion(visibleRect);
   } else {
     NS_ERROR("Only 2D transformations currently supported");
   }
 }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -980,25 +980,16 @@ nsLayoutUtils::InvertTransformsToRoot(ns
   /* If the ctm is singular, hand back (0, 0) as a sentinel. */
   if (ctm.IsSingular())
     return nsPoint(0, 0);
 
   /* Otherwise, invert the CTM and use it to transform the point. */
   return MatrixTransformPoint(aPoint, ctm.Invert(), aFrame->PresContext()->AppUnitsPerDevPixel());
 }
 
-nsresult
-nsLayoutUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut)
-{
-  *aOut = nsIntRect(PRInt32(aIn.X()), PRInt32(aIn.Y()),
-                    PRInt32(aIn.Width()), PRInt32(aIn.Height()));
-  return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height) == aIn
-    ? NS_OK : NS_ERROR_FAILURE;
-}
-
 static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) {
   nsIntPoint offset(0, 0);
   nsIWidget* parent = aWidget->GetParent();
   while (parent) {
     nsIntRect bounds;
     aWidget->GetBounds(bounds);
     offset += bounds.TopLeft();
     aWidget = parent;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -507,23 +507,16 @@ public:
    * app space that contains the graphics rectangle, rounding out as necessary.
    *
    * @param aRect The graphics rect to round outward.
    * @param aFactor The number of app units per graphics unit.
    * @return The smallest rectangle in app space that contains aRect.
    */
   static nsRect RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor);
 
-  /**
-   * If aIn can be represented exactly using an nsIntRect (i.e.
-   * integer-aligned edges and coordinates in the PRInt32 range) then we
-   * set aOut to that rectangle, otherwise return failure.
-   */
-  static nsresult GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut);
-
   enum {
     PAINT_IN_TRANSFORM = 0x01,
     PAINT_SYNC_DECODE_IMAGES = 0x02,
     PAINT_WIDGET_LAYERS = 0x04,
     PAINT_IGNORE_SUPPRESSION = 0x08,
     PAINT_DOCUMENT_RELATIVE = 0x10,
     PAINT_HIDE_CARET = 0x20,
     PAINT_ALL_CONTINUATIONS = 0x40,
--- a/layout/svg/base/src/nsSVGFilterFrame.cpp
+++ b/layout/svg/base/src/nsSVGFilterFrame.cpp
@@ -42,16 +42,17 @@
 #include "nsSVGFilterElement.h"
 #include "nsSVGFilters.h"
 #include "gfxASurface.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGRect.h"
 #include "nsSVGFilterInstance.h"
+#include "gfxUtils.h"
 
 nsIFrame*
 NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSVGFilterFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSVGFilterFrame)
@@ -62,17 +63,17 @@ MapDeviceRectToFilterSpace(const gfxMatr
                            const nsIntRect* aDeviceRect)
 {
   nsIntRect rect(0, 0, aFilterSize.width, aFilterSize.height);
   if (aDeviceRect) {
     gfxRect r = aMatrix.TransformBounds(gfxRect(aDeviceRect->x, aDeviceRect->y,
                                                 aDeviceRect->width, aDeviceRect->height));
     r.RoundOut();
     nsIntRect intRect;
-    if (NS_SUCCEEDED(nsLayoutUtils::GfxRectToIntRect(r, &intRect))) {
+    if (!gfxUtils::GfxRectToIntRect(r, &intRect)) {
       rect = intRect;
     }
   }
   return rect;
 }
 
 class NS_STACK_CLASS nsAutoFilterInstance {
 public:
@@ -231,19 +232,18 @@ nsSVGFilterFrame::FilterPaint(nsSVGRende
 static nsresult
 TransformFilterSpaceToDeviceSpace(nsSVGFilterInstance *aInstance, nsIntRect *aRect)
 {
   gfxMatrix m = aInstance->GetFilterSpaceToDeviceSpaceTransform();
   gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height);
   r = m.TransformBounds(r);
   r.RoundOut();
   nsIntRect deviceRect;
-  nsresult rv = nsLayoutUtils::GfxRectToIntRect(r, &deviceRect);
-  if (NS_FAILED(rv))
-    return rv;
+  if (!gfxUtils::GfxRectToIntRect(r, &deviceRect))
+    return NS_ERROR_FAILURE;
   *aRect = deviceRect;
   return NS_OK;
 }
 
 nsIntRect
 nsSVGFilterFrame::GetInvalidationBBox(nsIFrame *aTarget, const nsIntRect& aRect)
 {
   nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, &aRect, nsnull);
--- a/layout/svg/base/src/nsSVGFilterInstance.cpp
+++ b/layout/svg/base/src/nsSVGFilterInstance.cpp
@@ -36,16 +36,17 @@
 
 #include "nsSVGFilterInstance.h"
 #include "nsSVGUtils.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "gfxPlatform.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGFilterElement.h"
 #include "nsLayoutUtils.h"
+#include "gfxUtils.h"
 
 static double Square(double aX)
 {
   return aX*aX;
 }
 
 float
 nsSVGFilterInstance::GetPrimitiveLength(nsSVGLength2 *aLength) const
@@ -160,17 +161,17 @@ nsSVGFilterInstance::BuildSources()
   gfxRect filterRegion = gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
   mSourceColorAlpha.mImage.mFilterPrimitiveSubregion = filterRegion;
   mSourceAlpha.mImage.mFilterPrimitiveSubregion = filterRegion;
 
   nsIntRect sourceBoundsInt;
   gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox);
   sourceBounds.RoundOut();
   // Detect possible float->int overflow
-  if (NS_FAILED(nsLayoutUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt)))
+  if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt))
     return NS_ERROR_FAILURE;
 
   mSourceColorAlpha.mResultBoundingBox = sourceBoundsInt;
   mSourceAlpha.mResultBoundingBox = sourceBoundsInt;
   return NS_OK;
 }
 
 nsresult
@@ -351,19 +352,18 @@ nsSVGFilterInstance::BuildSourceImages()
     gfxMatrix userSpaceToFilterSpace = GetUserSpaceToFilterSpaceTransform();
 
     gfxRect r(neededRect.x, neededRect.y, neededRect.width, neededRect.height);
     gfxMatrix m = userSpaceToFilterSpace;
     m.Invert();
     r = m.TransformBounds(r);
     r.RoundOut();
     nsIntRect dirty;
-    nsresult rv = nsLayoutUtils::GfxRectToIntRect(r, &dirty);
-    if (NS_FAILED(rv))
-      return rv;
+    if (!gfxUtils::GfxRectToIntRect(r, &dirty))
+      return NS_ERROR_FAILURE;
 
     // SVG graphics paint to device space, so we need to set an initial device
     // space to filter space transform on the gfxContext that SourceGraphic
     // and SourceAlpha will paint to.
     //
     // (In theory it would be better to minimize error by having filtered SVG
     // graphics temporarily paint to user space when painting the sources and
     // only set a user space to filter space transform on the gfxContext
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -87,16 +87,17 @@
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGGeometryFrame.h"
 #include "nsComputedDOMStyle.h"
 #include "nsSVGPathGeometryFrame.h"
 #include "prdtoa.h"
 #include "mozilla/dom/Element.h"
 #include "nsIDOMSVGNumberList.h"
+#include "gfxUtils.h"
 
 using namespace mozilla::dom;
 
 gfxASurface *nsSVGUtils::gThebesComputationalSurface = nsnull;
 
 // c = n / 255
 // (c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1 / 2.4) - 0.055) * 255 + 0.5
 static const PRUint8 glinearRGBTosRGBMap[256] = {
@@ -632,17 +633,17 @@ nsSVGUtils::FindFilterInvalidation(nsIFr
       nsSVGDisplayContainerFrame* innerSvgParent = do_QueryFrame(viewportFrame->GetParent());
       float x, y, width, height;
       static_cast<nsSVGSVGElement*>(innerSvg->GetContent())->
         GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
       gfxRect bounds = nsSVGUtils::GetCanvasTM(innerSvgParent).
                          TransformBounds(gfxRect(x, y, width, height));
       bounds.RoundOut();
       nsIntRect r;
-      if (NS_SUCCEEDED(nsLayoutUtils::GfxRectToIntRect(bounds, &r))) {
+      if (!gfxUtils::GfxRectToIntRect(bounds, &r)) {
         rect = r;
       } else {
         NS_NOTREACHED("Not going to invalidate the correct area");
       }
       aFrame = viewportFrame;
     }
     aFrame = aFrame->GetParent();
   }
@@ -951,17 +952,17 @@ public:
     if (aDirtyRect) {
       gfxMatrix userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
       if (userToDeviceSpace.IsSingular()) {
         return;
       }
       gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(
         gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
       dirtyBounds.RoundOut();
-      if (NS_SUCCEEDED(nsLayoutUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect))) {
+      if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
         dirtyRect = &tmpDirtyRect;
       }
     }
 
     svgChildFrame->PaintSVG(aContext, dirtyRect);
   }
 };