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 idunknown
push userunknown
push dateunknown
reviewersroc
bugs573829
milestone2.0b3pre
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;