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,a=akeybl
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 14 Nov 2012 17:35:18 -0800
changeset 117011 f9cb68fee5c7a09805b08f3cf610b9628e8cfe8a
parent 117010 dab37cb0f43f265242ccee1d14ab40a67f2866bb
child 117012 f0833ab13aef06c38919e289562b108046549fa2
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, akeybl
bugs810470, 402807
milestone18.0a2
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,a=akeybl 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
@@ -1633,25 +1633,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];
@@ -1669,17 +1670,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
@@ -1860,17 +1860,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
@@ -202,40 +202,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.