Bug 363861. Part 4: Track rectangles of opaque content in RGBA surfaces and use them to make PushGroupAndCopyBackground work in more situations. r=jrmuizel,sr=vlad,a=blocking
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 03 Jan 2011 14:48:08 +1300
changeset 59765 b7d1bb50e829a88eafa856e2253ff1d70eb12c09
parent 59764 5c0c6d6f11e7bc37a30500f58ac359ec67e35908
child 59766 ccba8826be1451d0e61d0df38363dadffb20ba48
push id17781
push userrocallahan@mozilla.com
push dateMon, 03 Jan 2011 01:49:17 +0000
treeherdermozilla-central@836e01a2a6dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, vlad, blocking
bugs363861
milestone2.0b9pre
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 363861. Part 4: Track rectangles of opaque content in RGBA surfaces and use them to make PushGroupAndCopyBackground work in more situations. r=jrmuizel,sr=vlad,a=blocking
gfx/layers/basic/BasicLayers.cpp
gfx/thebes/gfxASurface.h
gfx/thebes/gfxContext.cpp
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -1309,16 +1309,32 @@ BasicLayerManager::PaintLayer(Layer* aLa
   gfxMatrix transform;
   // XXX we need to add some kind of 3D transform support, possibly
   // using pixman?
   NS_ASSERTION(effectiveTransform.Is2D(),
                "Only 2D transforms supported currently");
   effectiveTransform.Is2D(&transform);
   mTarget->SetMatrix(transform);
 
+  PRBool pushedTargetOpaqueRect = PR_FALSE;
+  const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion();
+  nsRefPtr<gfxASurface> currentSurface = mTarget->CurrentSurface();
+  const gfxRect& targetOpaqueRect = currentSurface->GetOpaqueRect();
+
+  // Try to annotate currentSurface with a region of pixels that have been
+  // (or will be) painted opaque, if no such region is currently set.
+  if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
+      (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+      !transform.HasNonAxisAlignedTransform()) {
+    const nsIntRect& bounds = visibleRegion.GetBounds();
+    currentSurface->SetOpaqueRect(
+        mTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)));
+    pushedTargetOpaqueRect = PR_TRUE;
+  }
+
   if (needsGroup) {
     // If we need to call PushGroup, we should clip to the smallest possible
     // area first to minimize the size of the temporary surface.
     ClipToContain(mTarget, aLayer->GetEffectiveVisibleRegion().GetBounds());
 
     gfxASurface::gfxContentType type = aLayer->CanUseOpaqueSurface()
         ? gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA;
     mTarget->PushGroupAndCopyBackground(type);
@@ -1347,16 +1363,20 @@ BasicLayerManager::PaintLayer(Layer* aLa
     mTarget->PopGroupToSource();
     // If the layer is opaque in its visible region we pushed a CONTENT_COLOR
     // group. We need to make sure that only pixels inside the layer's visible
     // region are copied back to the destination.
     gfxUtils::ClipToRegionSnapped(mTarget, aLayer->GetEffectiveVisibleRegion());
     mTarget->Paint(aLayer->GetEffectiveOpacity());
   }
 
+  if (pushedTargetOpaqueRect) {
+    currentSurface->SetOpaqueRect(gfxRect(0, 0, 0, 0));
+  }
+
   if (needsSaveRestore) {
     mTarget->Restore();
   } else {
     mTarget->SetMatrix(savedMatrix);
   }
 }
 
 void
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -197,16 +197,32 @@ public:
     void RecordMemoryFreed();
 
     PRInt32 KnownMemoryUsed() { return mBytesRecorded; }
 
     static PRInt32 BytePerPixelFromFormat(gfxImageFormat format);
 
     virtual const gfxIntSize GetSize() const { return gfxIntSize(-1, -1); }
 
+    void SetOpaqueRect(const gfxRect& aRect) {
+        if (aRect.IsEmpty()) {
+            mOpaqueRect = nsnull;
+        } else if (mOpaqueRect) {
+            *mOpaqueRect = aRect;
+        } else {
+            mOpaqueRect = new gfxRect(aRect);
+        }
+    }
+    const gfxRect& GetOpaqueRect() {
+        if (mOpaqueRect)
+            return *mOpaqueRect;
+        static const gfxRect empty(0, 0, 0, 0);
+        return empty;
+    }
+
     virtual PRBool SupportsSelfCopy() { return PR_TRUE; }
 
 protected:
     gfxASurface() : mSurface(nsnull), mFloatingRefs(0), mBytesRecorded(0), mSurfaceValid(PR_FALSE)
     {
         MOZ_COUNT_CTOR(gfxASurface);
     }
 
@@ -218,16 +234,17 @@ protected:
     virtual ~gfxASurface()
     {
         RecordMemoryFreed();
 
         MOZ_COUNT_DTOR(gfxASurface);
     }
 
     cairo_surface_t *mSurface;
+    nsAutoPtr<gfxRect> mOpaqueRect;
 
 private:
     static void SurfaceDestroyFunc(void *data);
 
     PRInt32 mFloatingRefs;
     PRInt32 mBytesRecorded;
 
 protected:
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -741,30 +741,42 @@ gfxContext::Paint(gfxFloat alpha)
 // groups
 
 void
 gfxContext::PushGroup(gfxASurface::gfxContentType content)
 {
     cairo_push_group_with_content(mCairo, (cairo_content_t) content);
 }
 
+static gfxRect
+GetRoundOutDeviceClipExtents(gfxContext* aCtx)
+{
+    gfxContextMatrixAutoSaveRestore save(aCtx);
+    aCtx->IdentityMatrix();
+    gfxRect r = aCtx->GetClipExtents();
+    r.RoundOut();
+    return r;
+}
+
 void
 gfxContext::PushGroupAndCopyBackground(gfxASurface::gfxContentType content)
 {
     if (content == gfxASurface::CONTENT_COLOR_ALPHA) {
-        cairo_surface_t *s = cairo_get_group_target(mCairo);
-        if (cairo_surface_get_content(s) == CAIRO_CONTENT_COLOR) {
+        nsRefPtr<gfxASurface> s = CurrentSurface();
+        if (s->GetContentType() == gfxASurface::CONTENT_COLOR ||
+            s->GetOpaqueRect().Contains(GetRoundOutDeviceClipExtents(this))) {
             cairo_push_group_with_content(mCairo, CAIRO_CONTENT_COLOR);
-            cairo_surface_t *d = cairo_get_group_target(mCairo);
+            nsRefPtr<gfxASurface> d = CurrentSurface();
 
-            cairo_t *cr = cairo_create(d);
-            cairo_set_source_surface(cr, s, 0, 0);
+            cairo_t *cr = cairo_create(d->CairoSurface());
+            cairo_set_source_surface(cr, s->CairoSurface(), 0, 0);
             cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
             cairo_paint(cr);
             cairo_destroy(cr);
+            d->SetOpaqueRect(s->GetOpaqueRect());
             return;
         }
     }
     cairo_push_group_with_content(mCairo, (cairo_content_t) content);
 }
 
 already_AddRefed<gfxPattern>
 gfxContext::PopGroup()