Bug 810470. Part 8: Limit the BasicLayers background-caching optimization to cases where the destination context has an integer translation only, and snap the image rectangle. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 14 Nov 2012 17:35:18 -0800
changeset 113434 7a530abf45befb679a04767b50417ff4cb2bb025
parent 113433 ea62a0ee450cd721989590973538dab950f98f4f
child 113435 e8c599817e977fb4c6b7bc07f9a942518b38db5a
push id18151
push userrocallahan@mozilla.com
push dateFri, 16 Nov 2012 00:03:18 +0000
treeherdermozilla-inbound@e8c599817e97 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs810470, 402807
milestone19.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 810470. Part 8: Limit the BasicLayers background-caching optimization to cases where the destination context has an integer translation only, and snap the image rectangle. r=mattwoodrow Previous patches in this bug enabled the nsDisplayCanvasBackground background-caching optimization for the test 402807-1.html. That exposed an existing bug where we don't snap the background image to pixel boundaries when drawing through that path. This patch fixes it. This patch also stops the IsSingleFixedPositionImage path from using mDestRect, returning the rect as an out-parameter instead.
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/generic/nsCanvasFrame.cpp
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1642,25 +1642,26 @@ RoundedBorderIntersectsRect(nsIFrame* aF
 
 // Returns TRUE if aContainedRect is guaranteed to be contained in
 // the rounded rect defined by aRoundedRect and aRadii. Complex cases are
 // handled conservatively by returning FALSE in some situations where
 // a more thorough analysis could return TRUE.
 //
 // See also RoundedRectIntersectsRect.
 static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
-                                      const nscoord aRadii[8],
-                                      const nsRect& aContainedRect) {
+                                    const nscoord aRadii[8],
+                                    const nsRect& aContainedRect) {
   nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii, aContainedRect);
   return rgn.Contains(aContainedRect);
 }
 
 bool
 nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
-                                                     const nsRect& aClipRect)
+                                                     const nsRect& aClipRect,
+                                                     gfxRect* aDestRect)
 {
   if (mIsThemed || !mBackgroundStyle)
     return false;
 
   nsPresContext* presContext = mFrame->PresContext();
   uint32_t flags = aBuilder->GetBackgroundPaintFlags();
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
   const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer];
@@ -1678,17 +1679,17 @@ nsDisplayBackgroundImage::IsSingleFixedP
                                            layer);
 
   nsImageRenderer* imageRenderer = &state.mImageRenderer;
   // We only care about images here, not gradients.
   if (!imageRenderer->IsRasterImage())
     return false;
 
   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
-  mDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
+  *aDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel);
 
   return true;
 }
 
 bool
 nsDisplayBackgroundImage::TryOptimizeToImageLayer(nsDisplayListBuilder* aBuilder)
 {
   if (mIsThemed || !mBackgroundStyle)
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1861,17 +1861,19 @@ public:
 
   static nsRegion GetInsideClipRegion(nsDisplayItem* aItem, nsPresContext* aPresContext, uint8_t aClip,
                                       const nsRect& aRect, bool* aSnap);
 protected:
   typedef class mozilla::layers::ImageContainer ImageContainer;
   typedef class mozilla::layers::ImageLayer ImageLayer;
 
   bool TryOptimizeToImageLayer(nsDisplayListBuilder* aBuilder);
-  bool IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder, const nsRect& aClipRect);
+  bool IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
+                                  const nsRect& aClipRect,
+                                  gfxRect* aDestRect);
   void ConfigureLayer(ImageLayer* aLayer);
 
   // Cache the result of nsCSSRendering::FindBackground. Always null if
   // mIsThemed is true or if FindBackground returned false.
   const nsStyleBackground* mBackgroundStyle;
   /* If this background can be a simple image layer, we store the format here. */
   nsRefPtr<ImageContainer> mImageContainer;
   gfxRect mDestRect;
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -201,40 +201,47 @@ nsDisplayCanvasBackground::Paint(nsDispl
   }
 
   bool snap;
   nsRect bounds = GetBounds(aBuilder, &snap);
   nsRenderingContext context;
   nsRefPtr<gfxContext> dest = aCtx->ThebesContext();
   nsRefPtr<gfxASurface> surf;
   nsRefPtr<gfxContext> ctx;
+  gfxRect destRect;
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
-  if (IsSingleFixedPositionImage(aBuilder, bgClipRect) && aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap()) {
+  if (IsSingleFixedPositionImage(aBuilder, bgClipRect, &destRect) &&
+      aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap() &&
+      !dest->CurrentMatrix().HasNonIntegerTranslation()) {
+    // Snap image rectangle to nearest pixel boundaries. This is the right way
+    // to snap for this context, because we checked HasNonIntegerTranslation above.
+    destRect.Round();
     surf = static_cast<gfxASurface*>(GetUnderlyingFrame()->Properties().Get(nsIFrame::CachedBackgroundImage()));
     nsRefPtr<gfxASurface> destSurf = dest->CurrentSurface();
     if (surf && surf->GetType() == destSurf->GetType()) {
-      BlitSurface(dest, mDestRect, surf);
+      BlitSurface(dest, destRect, surf);
       return;
     }
-    surf = destSurf->CreateSimilarSurface(gfxASurface::CONTENT_COLOR_ALPHA, gfxIntSize(ceil(mDestRect.width), ceil(mDestRect.height)));
+    surf = destSurf->CreateSimilarSurface(gfxASurface::CONTENT_COLOR_ALPHA,
+        gfxIntSize(destRect.width, destRect.height));
     if (surf) {
       ctx = new gfxContext(surf);
-      ctx->Translate(-gfxPoint(mDestRect.x, mDestRect.y));
+      ctx->Translate(-gfxPoint(destRect.x, destRect.y));
       context.Init(aCtx->DeviceContext(), ctx);
     }
   }
 #endif
 
   nsCSSRendering::PaintBackground(mFrame->PresContext(), surf ? context : *aCtx, mFrame,
                                   surf ? bounds : mVisibleRect,
                                   nsRect(offset, mFrame->GetSize()),
                                   aBuilder->GetBackgroundPaintFlags(),
                                   &bgClipRect, mLayer);
   if (surf) {
-    BlitSurface(dest, mDestRect, surf);
+    BlitSurface(dest, destRect, surf);
 
     GetUnderlyingFrame()->Properties().Set(nsIFrame::CachedBackgroundImage(), surf.forget().get());
     GetUnderlyingFrame()->AddStateBits(NS_FRAME_HAS_CACHED_BACKGROUND);
   }
 }
 
 /**
  * A display item to paint the focus ring for the document.