Bug 573829, part 1: Refactor ThebesLayerBuffer in preparation for its use in the OGL layers backend. r=roc
authorChris Jones <jones.chris.g@gmail.com>
Wed, 21 Jul 2010 13:06:33 -0500
changeset 48026 432bca3dd7bae26289e7f112ed2baf12459789ac
parent 48025 b0f37b33246e67dae92ecd04fc7e98f138867d81
child 48027 f6c676a1fbba6c89fdd780c8f19000d7dcbc48cd
child 49328 ae77ad8d4425b7e8489ae2f7d4435fc38007c396
push id14539
push usercjones@mozilla.com
push dateWed, 21 Jul 2010 18:06:46 +0000
treeherdermozilla-central@432bca3dd7ba [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs573829
milestone2.0b3pre
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 573829, part 1: Refactor ThebesLayerBuffer in preparation for its use in the OGL layers backend. r=roc
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/ThebesLayerBuffer.cpp
gfx/layers/ThebesLayerBuffer.h
gfx/layers/basic/BasicLayers.cpp
gfx/layers/basic/ThebesLayerBuffer.cpp
gfx/layers/basic/ThebesLayerBuffer.h
gfx/layers/opengl/ThebesLayerOGL.cpp
gfx/src/nsSize.h
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -133,20 +133,36 @@ AppendToString(nsACString& s, const nsIn
 
   return s += sfx;
 }
 
 } // namespace <anon>
 
 namespace mozilla {
 namespace layers {
- 
+
 //--------------------------------------------------
 // Layer
- 
+
+PRBool
+Layer::CanUseOpaqueSurface()
+{
+  // If the visible content in the layer is opaque, there is no need
+  // for an alpha channel.
+  if (IsOpaqueContent())
+    return PR_TRUE;
+  // Also, if this layer is the bottommost layer in a container which
+  // doesn't need an alpha channel, we can use an opaque surface for this
+  // layer too. Any transparent areas must be covered by something else
+  // in the container.
+  ContainerLayer* parent = GetParent();
+  return parent && parent->GetFirstChild() == this &&
+    parent->CanUseOpaqueSurface();
+}
+
 #ifdef MOZ_LAYERS_HAVE_LOG
 
 void
 Layer::Dump(FILE* aFile, const char* aPrefix)
 {
   DumpSelf(aFile, aPrefix);
 
   if (Layer* kid = GetFirstChild()) {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -401,16 +401,23 @@ public:
   PRBool IsOpaqueContent() { return mIsOpaqueContent; }
   const nsIntRegion& GetVisibleRegion() { return mVisibleRegion; }
   ContainerLayer* GetParent() { return mParent; }
   Layer* GetNextSibling() { return mNextSibling; }
   Layer* GetPrevSibling() { return mPrevSibling; }
   virtual Layer* GetFirstChild() { return nsnull; }
   const gfx3DMatrix& GetTransform() { return mTransform; }
 
+  // Returns true if it's OK to save the contents of aLayer in an
+  // opaque surface (a surface without an alpha channel).
+  // If we can use a surface without an alpha channel, we should, because
+  // it will often make painting of antialiased text faster and higher
+  // quality.
+  PRBool CanUseOpaqueSurface();
+
   // This setter and getter can be used anytime. The user data is initially
   // null.
   void SetUserData(void* aData) { mUserData = aData; }
   void* GetUserData() { return mUserData; }
 
   /**
    * Dynamic downcast to a Thebes layer. Returns null if this is not
    * a ThebesLayer.
rename from gfx/layers/basic/ThebesLayerBuffer.cpp
rename to gfx/layers/ThebesLayerBuffer.cpp
--- a/gfx/layers/basic/ThebesLayerBuffer.cpp
+++ b/gfx/layers/ThebesLayerBuffer.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * ***** 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/
  *
@@ -38,18 +38,19 @@
 #include "ThebesLayerBuffer.h"
 #include "Layers.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 
 namespace mozilla {
 namespace layers {
 
-static void
-ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion)
+/*static*/ void
+ThebesLayerBuffer::ClipToRegion(gfxContext* aContext,
+                                const nsIntRegion& aRegion)
 {
   aContext->NewPath();
   nsIntRegionRectIterator iter(aRegion);
   const nsIntRect* r;
   while ((r = iter.Next()) != nsnull) {
     aContext->Rectangle(gfxRect(r->x, r->y, r->width, r->height));
   }
   aContext->Clip();
@@ -119,56 +120,40 @@ WrapRotationAxis(PRInt32* aRotationPoint
 {
   if (*aRotationPoint < 0) {
     *aRotationPoint += aSize;
   } else if (*aRotationPoint >= aSize) {
     *aRotationPoint -= aSize;
   }
 }
 
-static already_AddRefed<gfxASurface>
-CreateBuffer(gfxASurface* aTargetSurface, gfxASurface::gfxContentType aType,
-             const nsIntSize& aSize)
-{
-  return aTargetSurface->CreateSimilarSurface(aType, gfxIntSize(aSize.width, aSize.height));
-}
-
 ThebesLayerBuffer::PaintState
-ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer,
-                              gfxASurface* aReferenceSurface,
-                              PRUint32 aFlags)
+ThebesLayerBuffer::BeginPaint(ThebesLayer* aLayer, ContentType aContentType)
 {
   PaintState result;
 
   result.mRegionToDraw.Sub(aLayer->GetVisibleRegion(), aLayer->GetValidRegion());
 
-  gfxASurface::gfxContentType desiredContentType = gfxASurface::CONTENT_COLOR_ALPHA;
-  if (aReferenceSurface->AreSimilarSurfacesSensitiveToContentType()) {
-    if (aFlags & OPAQUE_CONTENT) {
-      desiredContentType = gfxASurface::CONTENT_COLOR;
-    }
-    if (mBuffer && desiredContentType != mBuffer->GetContentType()) {
-      // We're effectively clearing the valid region, so we need to draw
-      // the entire visible region now.
-      result.mRegionToDraw = aLayer->GetVisibleRegion();
-      result.mRegionToInvalidate = aLayer->GetValidRegion();
-      Clear();
-    }
+  if (mBuffer && aContentType != mBuffer->GetContentType()) {
+    // We're effectively clearing the valid region, so we need to draw
+    // the entire visible region now.
+    result.mRegionToDraw = aLayer->GetVisibleRegion();
+    result.mRegionToInvalidate = aLayer->GetValidRegion();
+    Clear();
   }
 
   if (result.mRegionToDraw.IsEmpty())
     return result;
   nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
 
   nsIntRect visibleBounds = aLayer->GetVisibleRegion().GetBounds();
   nsRefPtr<gfxASurface> destBuffer;
   nsIntRect destBufferRect;
 
-  if (mBufferRect.width >= visibleBounds.width &&
-      mBufferRect.height >= visibleBounds.height) {
+  if (BufferSizeOkFor(visibleBounds.Size())) {
     // The current buffer is big enough to hold the visible area.
     if (mBufferRect.Contains(visibleBounds)) {
       // We don't need to adjust mBufferRect.
       destBufferRect = mBufferRect;
     } else {
       // The buffer's big enough but doesn't contain everything that's
       // going to be visible. We'll move it.
       destBufferRect = nsIntRect(visibleBounds.TopLeft(), mBufferRect.Size());
@@ -191,18 +176,17 @@ ThebesLayerBuffer::BeginPaint(ThebesLaye
         // The stuff we need to redraw will wrap around an edge of the
         // buffer, so we will need to do a self-copy
         if (mBufferRotation == nsIntPoint(0,0)) {
           destBuffer = mBuffer;
         } else {
           // We can't do a real self-copy because the buffer is rotated.
           // So allocate a new buffer for the destination.
           destBufferRect = visibleBounds;
-          destBuffer = CreateBuffer(aReferenceSurface, desiredContentType,
-                                    destBufferRect.Size());
+          destBuffer = CreateBuffer(aContentType, destBufferRect.Size());
           if (!destBuffer)
             return result;
         }
       } else {
         mBufferRect = destBufferRect;
         mBufferRotation = newRotation;
       }
     } else {
@@ -210,18 +194,17 @@ ThebesLayerBuffer::BeginPaint(ThebesLaye
       // will be redrawn, so we don't need to copy anything, so we don't
       // set destBuffer.
       mBufferRect = destBufferRect;
       mBufferRotation = nsIntPoint(0,0);
     }
   } else {
     // The buffer's not big enough, so allocate a new one
     destBufferRect = visibleBounds;
-    destBuffer = CreateBuffer(aReferenceSurface, desiredContentType,
-                              destBufferRect.Size());
+    destBuffer = CreateBuffer(aContentType, destBufferRect.Size());
     if (!destBuffer)
       return result;
   }
 
   // If we have no buffered data already, then destBuffer will be a fresh buffer
   // and we do not need to clear it below.
   PRBool isClear = mBuffer == nsnull;
 
@@ -251,31 +234,19 @@ ThebesLayerBuffer::BeginPaint(ThebesLaye
   PRInt32 yBoundary = mBufferRect.YMost() - mBufferRotation.y;
   XSide sideX = drawBounds.XMost() <= xBoundary ? RIGHT : LEFT;
   YSide sideY = drawBounds.YMost() <= yBoundary ? BOTTOM : TOP;
   nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
   NS_ASSERTION(quadrantRect.Contains(drawBounds), "Messed up quadrants");
   result.mContext->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y));
 
   ClipToRegion(result.mContext, result.mRegionToDraw);
-  if (desiredContentType == gfxASurface::CONTENT_COLOR_ALPHA && !isClear) {
+  if (aContentType == gfxASurface::CONTENT_COLOR_ALPHA && !isClear) {
     result.mContext->SetOperator(gfxContext::OPERATOR_CLEAR);
     result.mContext->Paint();
     result.mContext->SetOperator(gfxContext::OPERATOR_OVER);
   }
   return result;
 }
 
-void
-ThebesLayerBuffer::DrawTo(ThebesLayer* aLayer, PRUint32 aFlags, gfxContext* aTarget, float aOpacity)
-{
-  aTarget->Save();
-  ClipToRegion(aTarget, aLayer->GetVisibleRegion());
-  if (aFlags & OPAQUE_CONTENT) {
-    aTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
-  }
-  DrawBufferWithRotation(aTarget, aOpacity);
-  aTarget->Restore();
-}
-
 }
 }
 
rename from gfx/layers/basic/ThebesLayerBuffer.h
rename to gfx/layers/ThebesLayerBuffer.h
--- a/gfx/layers/basic/ThebesLayerBuffer.h
+++ b/gfx/layers/ThebesLayerBuffer.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * ***** 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/
  *
@@ -62,21 +62,37 @@ class ThebesLayer;
  * and paint content rows H to H+N-1 into rows 0 to N-1 of the buffer.
  * Then we can refresh the screen by painting rows N to H-1 of the buffer
  * at row 0 on the screen, and then painting rows 0 to N-1 of the buffer
  * at row H-N on the screen.
  * mBufferRotation.y would be N in this example.
  */
 class ThebesLayerBuffer {
 public:
-  ThebesLayerBuffer() : mBufferRotation(0,0)
+  typedef gfxASurface::gfxContentType ContentType;
+
+  /**
+   * Controls the size of the backing buffer of this.
+   * - SizedToVisibleBounds: the backing buffer is exactly the same
+   *   size as the bounds of ThebesLayer's visible region
+   * - ContainsVisibleBounds: the backing buffer is large enough to
+   *   fit visible bounds.  May be larger.
+   */
+  enum BufferSizePolicy {
+    SizedToVisibleBounds,
+    ContainsVisibleBounds
+  };
+
+  ThebesLayerBuffer(BufferSizePolicy aBufferSizePolicy)
+    : mBufferRotation(0,0)
+    , mBufferSizePolicy(aBufferSizePolicy)
   {
     MOZ_COUNT_CTOR(ThebesLayerBuffer);
   }
-  ~ThebesLayerBuffer()
+  virtual ~ThebesLayerBuffer()
   {
     MOZ_COUNT_DTOR(ThebesLayerBuffer);
   }
 
   /**
    * Wipe out all retained contents. Call this when the entire
    * buffer becomes invalid.
    */
@@ -94,72 +110,78 @@ public:
    * opaque to transparent or vice versa, since the details of rendering can
    * depend on the buffer type.
    */
   struct PaintState {
     nsRefPtr<gfxContext> mContext;
     nsIntRegion mRegionToDraw;
     nsIntRegion mRegionToInvalidate;
   };
-  /**
-   * Pass OPAQUE_CONTENT when we have determined that everything visible
-   * in the buffer will be rendered with opaque pixels.
-   */
-  enum {
-    OPAQUE_CONTENT = 0x01
-  };
+
   /**
    * Start a drawing operation. This returns a PaintState describing what
    * needs to be drawn to bring the buffer up to date in the visible region.
    * This queries aLayer to get the currently valid and visible regions.
    * The returned mContext may be null if mRegionToDraw is empty.
    * Otherwise it must not be null.
    * mRegionToInvalidate will contain mRegionToDraw.
-   * @param aReferenceSurface if we need to create a buffer, we'll create
-   * a surface that's similar to aReferenceSurface
    */
-  PaintState BeginPaint(ThebesLayer* aLayer, gfxASurface* aReferenceSurface,
-                        PRUint32 aFlags);
+  PaintState BeginPaint(ThebesLayer* aLayer, ContentType aContentType);
+
   /**
-   * Complete the drawing operation. The region to draw must have been drawn
-   * before this is called. The contents of the buffer are drawn to aTarget.
+   * Return a new surface of |aSize| and |aType|.
    */
-  void DrawTo(ThebesLayer* aLayer, PRUint32 aFlags, gfxContext* aTarget, float aOpacity);
+  virtual already_AddRefed<gfxASurface>
+  CreateBuffer(ContentType aType, const nsIntSize& aSize) = 0;
 
   /**
    * Get the underlying buffer, if any. This is useful because we can pass
    * in the buffer as the default "reference surface" if there is one.
    * Don't use it for anything else!
    */
   gfxASurface* GetBuffer() { return mBuffer; }
 
 protected:
+  // XXX make me a general utility
+  static void ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion);
+
   enum XSide {
     LEFT, RIGHT
   };
   enum YSide {
     TOP, BOTTOM
   };
   nsIntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide);
   void DrawBufferQuadrant(gfxContext* aTarget, XSide aXSide, YSide aYSide, float aOpacity);
   void DrawBufferWithRotation(gfxContext* aTarget, float aOpacity);
 
+  const nsIntRect& BufferRect() const { return mBufferRect; }
+  const nsIntPoint& BufferRotation() const { return mBufferRotation; }
+
 private:
+  PRBool BufferSizeOkFor(const nsIntSize& aSize)
+  {
+    return (aSize == mBufferRect.Size() ||
+            (SizedToVisibleBounds != mBufferSizePolicy &&
+             aSize < mBufferRect.Size()));
+  }
+
   nsRefPtr<gfxASurface> mBuffer;
   /** The area of the ThebesLayer that is covered by the buffer as a whole */
   nsIntRect             mBufferRect;
   /**
    * The x and y rotation of the buffer. Conceptually the buffer
    * has its origin translated to mBufferRect.TopLeft() - mBufferRotation,
    * is tiled to fill the plane, and the result is clipped to mBufferRect.
    * So the pixel at mBufferRotation within the buffer is what gets painted at
    * mBufferRect.TopLeft().
    * This is "rotation" in the sense of rotating items in a linear buffer,
    * where items falling off the end of the buffer are returned to the
    * buffer at the other end, not 2D rotation!
    */
   nsIntPoint            mBufferRotation;
+  BufferSizePolicy      mBufferSizePolicy;
 };
 
 }
 }
 
 #endif /* THEBESLAYERBUFFER_H_ */
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -209,42 +209,21 @@ BasicContainerLayer::RemoveChildInternal
 
   aChild->SetNextSibling(nsnull);
   aChild->SetPrevSibling(nsnull);
   aChild->SetParent(nsnull);
 
   NS_RELEASE(aChild);
 }
 
-// Returns true if it's OK to save the contents of aLayer in an
-// opaque surface (a surface without an alpha channel).
-// If we can use a surface without an alpha channel, we should, because
-// it will often make painting of antialiased text faster and higher
-// quality.
-static PRBool
-UseOpaqueSurface(Layer* aLayer)
-{
-  // If the visible content in the layer is opaque, there is no need
-  // for an alpha channel.
-  if (aLayer->IsOpaqueContent())
-    return PR_TRUE;
-  // Also, if this layer is the bottommost layer in a container which
-  // doesn't need an alpha channel, we can use an opaque surface for this
-  // layer too. Any transparent areas must be covered by something else
-  // in the container.
-  BasicContainerLayer* parent =
-    static_cast<BasicContainerLayer*>(aLayer->GetParent());
-  return parent && parent->GetFirstChild() == aLayer &&
-         UseOpaqueSurface(parent);
-}
-
 class BasicThebesLayer : public ThebesLayer, BasicImplData {
 public:
   BasicThebesLayer(BasicLayerManager* aLayerManager) :
-    ThebesLayer(aLayerManager, static_cast<BasicImplData*>(this))
+    ThebesLayer(aLayerManager, static_cast<BasicImplData*>(this)),
+    mBuffer(this)
   {
     MOZ_COUNT_CTOR(BasicThebesLayer);
   }
   virtual ~BasicThebesLayer()
   {
     MOZ_COUNT_DTOR(BasicThebesLayer);
   }
 
@@ -267,17 +246,62 @@ public:
                      float aOpacity);
 
 protected:
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
   }
 
-  ThebesLayerBuffer mBuffer;
+  class Buffer : public ThebesLayerBuffer {
+  public:
+    Buffer(BasicThebesLayer* aLayer)
+      : ThebesLayerBuffer(ContainsVisibleBounds)
+      , mLayer(aLayer)
+    {}
+
+    /**
+     * Complete the drawing operation. The region to draw must have been
+     * drawn before this is called. The contents of the buffer are drawn
+     * to aTarget.
+     */
+    void DrawTo(PRBool aIsOpaqueContent, gfxContext* aTarget, float aOpacity);
+
+    virtual already_AddRefed<gfxASurface>
+    CreateBuffer(ContentType aType, const nsIntSize& aSize)
+    {
+      return mLayer->CreateBuffer(aType, aSize);
+    }
+
+  private:
+    BasicThebesLayer* mLayer;
+  };
+  
+  virtual already_AddRefed<gfxASurface>
+  CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize)
+  {
+    nsRefPtr<gfxASurface> referenceSurface = mBuffer.GetBuffer();
+    if (!referenceSurface) {
+      gfxContext* defaultTarget = BasicManager()->GetDefaultTarget();
+      if (defaultTarget) {
+        referenceSurface = defaultTarget->CurrentSurface();
+      } else {
+        nsIWidget* widget = BasicManager()->GetRetainerWidget();
+        if (widget) {
+          referenceSurface = widget->GetThebesSurface();
+        } else {
+          referenceSurface = BasicManager()->GetTarget()->CurrentSurface();
+        }
+      }
+    }
+    return referenceSurface->CreateSimilarSurface(
+      aType, gfxIntSize(aSize.width, aSize.height));
+  }
+
+  Buffer mBuffer;
 };
 
 static void
 ClipToContain(gfxContext* aContext, const nsIntRect& aRect)
 {
   gfxRect deviceRect =
     aContext->UserToDevice(gfxRect(aRect.x, aRect.y, aRect.width, aRect.height));
   deviceRect.RoundOut();
@@ -313,38 +337,26 @@ BasicThebesLayer::Paint(gfxContext* aCon
     if (aOpacity != 1.0) {
       target->PopGroupToSource();
       target->Paint(aOpacity);
       target->Restore();
     }
     return;
   }
 
-  PRUint32 flags = 0;
-  if (UseOpaqueSurface(this)) {
-    flags |= ThebesLayerBuffer::OPAQUE_CONTENT;
-  }
-
+  nsRefPtr<gfxASurface> targetSurface = aContext->CurrentSurface();
+  PRBool isOpaqueContent =
+    (targetSurface->AreSimilarSurfacesSensitiveToContentType() &&
+     aOpacity == 1.0 &&
+     CanUseOpaqueSurface());
   {
-    nsRefPtr<gfxASurface> referenceSurface = mBuffer.GetBuffer();
-    if (!referenceSurface) {
-      gfxContext* defaultTarget = BasicManager()->GetDefaultTarget();
-      if (defaultTarget) {
-        referenceSurface = defaultTarget->CurrentSurface();
-      } else {
-        nsIWidget* widget = BasicManager()->GetRetainerWidget();
-        if (widget) {
-          referenceSurface = widget->GetThebesSurface();
-        } else {
-          referenceSurface = aContext->CurrentSurface();
-        }
-      }
-    }
-    ThebesLayerBuffer::PaintState state =
-      mBuffer.BeginPaint(this, referenceSurface, flags);
+    Buffer::ContentType contentType =
+      isOpaqueContent ? gfxASurface::CONTENT_COLOR :
+                        gfxASurface::CONTENT_COLOR_ALPHA;
+    Buffer::PaintState state = mBuffer.BeginPaint(this, contentType);
     mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
 
     if (state.mContext) {
       // The area that became invalid and is visible needs to be repainted
       // (this could be the whole visible area if our buffer switched
       // from RGB to RGBA, because we might need to repaint with
       // subpixel AA)
       state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion);
@@ -354,17 +366,31 @@ BasicThebesLayer::Paint(gfxContext* aCon
     } else {
       // It's possible that state.mRegionToInvalidate is nonempty here,
       // if we are shrinking the valid region to nothing.
       NS_ASSERTION(state.mRegionToDraw.IsEmpty(),
                    "If we need to draw, we should have a context");
     }
   }
 
-  mBuffer.DrawTo(this, flags, target, aOpacity);
+  mBuffer.DrawTo(isOpaqueContent, target, aOpacity);
+}
+
+void
+BasicThebesLayer::Buffer::DrawTo(PRBool aIsOpaqueContent,
+                                 gfxContext* aTarget,
+                                 float aOpacity)
+{
+  aTarget->Save();
+  ClipToRegion(aTarget, mLayer->GetVisibleRegion());
+  if (aIsOpaqueContent) {
+    aTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
+  }
+  DrawBufferWithRotation(aTarget, aOpacity);
+  aTarget->Restore();
 }
 
 class BasicImageLayer : public ImageLayer, BasicImplData {
 public:
   BasicImageLayer(BasicLayerManager* aLayerManager) :
     ImageLayer(aLayerManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicImageLayer);
@@ -930,17 +956,17 @@ BasicLayerManager::PaintLayer(Layer* aLa
     aLayer->GetTransform().Is2D(&transform);
     mTarget->Multiply(transform);
 
     if (needsGroup && children > 1) {
       // 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->GetVisibleRegion().GetBounds());
 
-      gfxASurface::gfxContentType type = UseOpaqueSurface(aLayer)
+      gfxASurface::gfxContentType type = aLayer->CanUseOpaqueSurface()
           ? gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA;
       mTarget->PushGroup(type);
     }
   }
 
   /* Only paint ourself, or our children - This optimization relies on this! */
   if (!children) {
     ToData(aLayer)->Paint(mTarget, aCallback, aCallbackData, aOpacity);
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -45,39 +45,16 @@
 #endif
 #include "GLContextProvider.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gl;
 
-// Returns true if it's OK to save the contents of aLayer in an
-// opaque surface (a surface without an alpha channel).
-// If we can use a surface without an alpha channel, we should, because
-// it will often make painting of antialiased text faster and higher
-// quality.
-static PRBool
-UseOpaqueSurface(Layer* aLayer)
-{
-  // If the visible content in the layer is opaque, there is no need
-  // for an alpha channel.
-  if (aLayer->IsOpaqueContent())
-    return PR_TRUE;
-  // Also, if this layer is the bottommost layer in a container which
-  // doesn't need an alpha channel, we can use an opaque surface for this
-  // layer too. Any transparent areas must be covered by something else
-  // in the container.
-  ContainerLayerOGL* parent =
-    static_cast<ContainerLayerOGL*>(aLayer->GetParent());
-  return parent && parent->GetFirstChild() == aLayer &&
-         UseOpaqueSurface(parent);
-}
-
-
 ThebesLayerOGL::ThebesLayerOGL(LayerManagerOGL *aManager)
   : ThebesLayer(aManager, nsnull)
   , LayerOGL(aManager)
   , mTexImage(nsnull)
 {
   mImplData = static_cast<LayerOGL*>(this);
 }
 
@@ -87,18 +64,18 @@ ThebesLayerOGL::~ThebesLayerOGL()
   DEBUG_GL_ERROR_CHECK(gl());
 }
 
 PRBool
 ThebesLayerOGL::EnsureSurface()
 {
   nsIntSize visibleSize = mVisibleRegion.GetBounds().Size();
   TextureImage::ContentType contentType =
-    UseOpaqueSurface(this) ? gfxASurface::CONTENT_COLOR :
-                             gfxASurface::CONTENT_COLOR_ALPHA;
+    CanUseOpaqueSurface() ? gfxASurface::CONTENT_COLOR :
+                            gfxASurface::CONTENT_COLOR_ALPHA;
   if (!mTexImage ||
       mTexImage->GetSize() != visibleSize ||
       mTexImage->GetContentType() != contentType)
   {
     mValidRegion.SetEmpty();
     mTexImage = nsnull;
     DEBUG_GL_ERROR_CHECK(gl());
   }
@@ -184,17 +161,17 @@ ThebesLayerOGL::RenderLayer(int aPreviou
   }
 
   if (!textureBound)
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexImage->Texture());
 
   // Note BGR: Cairo's image surfaces are always in what
   // OpenGL and our shaders consider BGR format.
   ColorTextureLayerProgram *program =
-    UseOpaqueSurface(this)
+    CanUseOpaqueSurface()
     ? mOGLManager->GetBGRXLayerProgram()
     : mOGLManager->GetBGRALayerProgram();
 
   program->Activate();
   program->SetLayerQuadRect(mVisibleRegion.GetBounds());
   program->SetLayerOpacity(GetOpacity());
   program->SetLayerTransform(mTransform);
   program->SetRenderOffset(aOffset);
--- a/gfx/src/nsSize.h
+++ b/gfx/src/nsSize.h
@@ -84,16 +84,23 @@ struct nsIntSize {
   // Overloaded operators. Note that '=' isn't defined so we'll get the
   // compiler generated default assignment operator
   PRBool  operator==(const nsIntSize& aSize) const {
     return (PRBool) ((width == aSize.width) && (height == aSize.height));
   }
   PRBool  operator!=(const nsIntSize& aSize) const {
     return (PRBool) ((width != aSize.width) || (height != aSize.height));
   }
+  PRBool  operator<(const nsIntSize& aSize) const {
+    return (PRBool) (operator<=(aSize) &&
+                     (width < aSize.width || height < aSize.height));
+  }
+  PRBool  operator<=(const nsIntSize& aSize) const {
+    return (PRBool) ((width <= aSize.width) && (height <= aSize.height));
+  }
 
   void SizeTo(PRInt32 aWidth, PRInt32 aHeight) {width = aWidth; height = aHeight;}
 };
 
 inline nsSize
 nsSize::ConvertAppUnits(PRInt32 aFromAPP, PRInt32 aToAPP) const {
   if (aFromAPP != aToAPP) {
     nsSize size;