Bug 647560. Add support for compositing BasicLayers with OPERATOR_SOURCE. r=tnikkel
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 30 May 2011 15:48:13 +1200
changeset 70315 0b6e26a3230401d50166ecec09117ba615c9e0d2
parent 70314 0b8a78fc0b1827927bb970ae57bc9f8dd887dd67
child 70316 07b0336196cc19d5bbe3c7ca8fa8c0ea58de40e2
push idunknown
push userunknown
push dateunknown
reviewerstnikkel
bugs647560
milestone7.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 647560. Add support for compositing BasicLayers with OPERATOR_SOURCE. r=tnikkel We'll need this for the improved backbuffer code in the next patch. When a layer tree's leaf layers don't overlap and cover the window, the next patch will avoid double-buffering by blitting those layers directly to the window. If the window has transparent parts (e.g. with Aero Glass), we need to draw the layers for those transparent parts using OPERATOR_SOURCE so that the alpha values in the window are reset.
gfx/layers/basic/BasicLayers.cpp
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -87,17 +87,17 @@ class ShadowableLayer;
  *  |    +-> BasicThebesLayer <---------+   |
  *  |                                       |
  *  +-> ImageLayer                          |
  *       |                                  |
  *       +-> BasicImageLayer <--------------+
  */
 class BasicImplData {
 public:
-  BasicImplData() : mHidden(PR_FALSE)
+  BasicImplData() : mHidden(PR_FALSE), mOperator(gfxContext::OPERATOR_OVER)
   {
     MOZ_COUNT_CTOR(BasicImplData);
   }
   virtual ~BasicImplData()
   {
     MOZ_COUNT_DTOR(BasicImplData);
   }
 
@@ -131,37 +131,69 @@ public:
   /**
    * Layers will get this call when their layer manager is destroyed, this
    * indicates they should clear resources they don't really need after their
    * LayerManager ceases to exist.
    */
   virtual void ClearCachedResources() {}
 
   /**
-   * This variable is set by MarkLeafLayersHidden() before painting.
+   * This variable is set by MarkLayersHidden() before painting. It indicates
+   * that the layer should not be composited during this transaction.
    */
   void SetHidden(PRBool aCovered) { mHidden = aCovered; }
   PRBool IsHidden() const { return PR_FALSE; }
+  /**
+   * This variable is set by MarkLayersHidden() before painting. This is
+   * the operator to be used when compositing the layer in this transaction. It must
+   * be OVER or SOURCE.
+   */
+  void SetOperator(gfxContext::GraphicsOperator aOperator)
+  {
+    NS_ASSERTION(aOperator == gfxContext::OPERATOR_OVER ||
+                 aOperator == gfxContext::OPERATOR_SOURCE,
+                 "Bad composition operator");
+    mOperator = aOperator;
+  }
+  gfxContext::GraphicsOperator GetOperator() const { return mOperator; }
 
 protected:
   PRPackedBool mHidden;
+  gfxContext::GraphicsOperator mOperator;
+};
+
+class AutoSetOperator {
+public:
+  AutoSetOperator(gfxContext* aContext, gfxContext::GraphicsOperator aOperator) {
+    if (aOperator != gfxContext::OPERATOR_OVER) {
+      aContext->SetOperator(aOperator);
+      mContext = aContext;
+    }
+  }
+  ~AutoSetOperator() {
+    if (mContext) {
+      mContext->SetOperator(gfxContext::OPERATOR_OVER);
+    }
+  }
+private:
+  nsRefPtr<gfxContext> mContext;
 };
 
 static BasicImplData*
 ToData(Layer* aLayer)
 {
   return static_cast<BasicImplData*>(aLayer->ImplData());
 }
 
 template<class Container>
 static void ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer);
 template<class Container>
 static void ContainerRemoveChild(Layer* aChild, Container* aContainer);
 
-class BasicContainerLayer : public ContainerLayer, BasicImplData {
+class BasicContainerLayer : public ContainerLayer, public BasicImplData {
   template<class Container>
   friend void ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer);
   template<class Container>
   friend void ContainerRemoveChild(Layer* aChild, Container* aContainer);
 
 public:
   BasicContainerLayer(BasicLayerManager* aManager) :
     ContainerLayer(aManager, static_cast<BasicImplData*>(this))
@@ -353,17 +385,17 @@ private:
   {
     gfxIntSize sz = aBuffer->GetSize();
     SetBuffer(aBuffer, nsIntSize(sz.width, sz.height), aRect, aRotation);
   }
 
   BasicThebesLayer* mLayer;
 };
 
-class BasicThebesLayer : public ThebesLayer, BasicImplData {
+class BasicThebesLayer : public ThebesLayer, public BasicImplData {
 public:
   typedef BasicThebesLayerBuffer Buffer;
 
   BasicThebesLayer(BasicLayerManager* aLayerManager) :
     ThebesLayer(aLayerManager, static_cast<BasicImplData*>(this)),
     mBuffer(this)
   {
     MOZ_COUNT_CTOR(BasicThebesLayer);
@@ -545,36 +577,40 @@ BasicThebesLayer::PaintThebes(gfxContext
       (!canUseOpaqueSurface &&
        (mContentFlags & CONTENT_COMPONENT_ALPHA) &&
        !MustRetainContent())) {
     NS_ASSERTION(readbackUpdates.IsEmpty(), "Can't do readback for non-retained layer");
 
     mValidRegion.SetEmpty();
     mBuffer.Clear();
 
-    nsIntRegion toDraw = IntersectWithClip(mVisibleRegion, aContext);
+    nsIntRegion toDraw = IntersectWithClip(GetEffectiveVisibleRegion(), aContext);
     if (!toDraw.IsEmpty() && !IsHidden()) {
       if (!aCallback) {
         BasicManager()->SetTransactionIncomplete();
         return;
       }
 
       aContext->Save();
 
       PRBool needsClipToVisibleRegion = PR_FALSE;
-      if (opacity != 1.0) {
-        needsClipToVisibleRegion = PushGroupForLayer(aContext, this, toDraw);
+      PRBool needsGroup =
+          opacity != 1.0 || GetOperator() != gfxContext::OPERATOR_OVER;
+      if (needsGroup) {
+        needsClipToVisibleRegion = PushGroupForLayer(aContext, this, toDraw) ||
+                GetOperator() != gfxContext::OPERATOR_OVER;
       }
       SetAntialiasingFlags(this, aContext);
       aCallback(this, aContext, toDraw, nsIntRegion(), aCallbackData);
-      if (opacity != 1.0) {
+      if (needsGroup) {
         aContext->PopGroupToSource();
         if (needsClipToVisibleRegion) {
           gfxUtils::ClipToRegion(aContext, toDraw);
         }
+        AutoSetOperator setOperator(aContext, GetOperator());
         aContext->Paint(opacity);
       }
 
       aContext->Restore();
     }
     return;
   }
 
@@ -593,17 +629,18 @@ BasicThebesLayer::PaintThebes(gfxContext
       mBuffer.BeginPaint(this, contentType, paintXRes, paintYRes, flags);
     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);
+      state.mRegionToInvalidate.And(state.mRegionToInvalidate,
+                                    GetEffectiveVisibleRegion());
       nsIntRegion extendedDrawRegion = state.mRegionToDraw;
       extendedDrawRegion.ExtendForScaling(paintXRes, paintYRes);
       mXResolution = paintXRes;
       mYResolution = paintYRes;
       SetAntialiasingFlags(this, state.mContext);
       PaintBuffer(state.mContext,
                   state.mRegionToDraw, extendedDrawRegion, state.mRegionToInvalidate,
                   state.mDidSelfCopy,
@@ -613,16 +650,17 @@ BasicThebesLayer::PaintThebes(gfxContext
       // 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");
     }
   }
 
   if (!IsHidden()) {
+    AutoSetOperator setOperator(aContext, GetOperator());
     mBuffer.DrawTo(this, aContext, opacity);
   }
 
   for (PRUint32 i = 0; i < readbackUpdates.Length(); ++i) {
     ReadbackProcessor::Update& update = readbackUpdates[i];
     nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
     nsRefPtr<gfxContext> ctx =
       update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset,
@@ -650,23 +688,23 @@ BasicThebesLayerBuffer::DrawTo(ThebesLay
                                gfxContext* aTarget,
                                float aOpacity)
 {
   aTarget->Save();
   // If the entire buffer is valid, we can just draw the whole thing,
   // no need to clip. But we'll still clip if clipping is cheap ---
   // that might let us copy a smaller region of the buffer.
   if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
-      IsClippingCheap(aTarget, aLayer->GetVisibleRegion())) {
+      IsClippingCheap(aTarget, aLayer->GetEffectiveVisibleRegion())) {
     // We don't want to draw invalid stuff, so we need to clip. Might as
     // well clip to the smallest area possible --- the visible region.
     // Bug 599189 if there is a non-integer-translation transform in aTarget,
-    // we might sample pixels outside GetVisibleRegion(), which is wrong
+    // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong
     // and may cause gray lines.
-    gfxUtils::ClipToRegionSnapped(aTarget, aLayer->GetVisibleRegion());
+    gfxUtils::ClipToRegionSnapped(aTarget, aLayer->GetEffectiveVisibleRegion());
   }
   DrawBufferWithRotation(aTarget, aOpacity,
                          aLayer->GetXResolution(), aLayer->GetYResolution());
   aTarget->Restore();
 }
 
 already_AddRefed<gfxASurface>
 BasicThebesLayerBuffer::CreateBuffer(ContentType aType, 
@@ -689,17 +727,17 @@ BasicThebesLayerBuffer::SetBackingBuffer
   if (IsClippingCheap(destCtx, aUpdateRegion)) {
     gfxUtils::ClipToRegion(destCtx, aUpdateRegion);
   }
 
   BasicThebesLayerBuffer srcBuffer(aSource, aRect, aRotation);
   srcBuffer.DrawBufferWithRotation(destCtx, 1.0, aXResolution, aYResolution);
 }
 
-class BasicImageLayer : public ImageLayer, BasicImplData {
+class BasicImageLayer : public ImageLayer, public BasicImplData {
 public:
   BasicImageLayer(BasicLayerManager* aLayerManager) :
     ImageLayer(aLayerManager, static_cast<BasicImplData*>(this)),
     mSize(-1, -1)
   {
     MOZ_COUNT_CTOR(BasicImageLayer);
   }
   virtual ~BasicImageLayer()
@@ -764,16 +802,17 @@ BasicImageLayer::GetAndPaintCurrentImage
   }
 
   pat->SetFilter(mFilter);
 
   // The visible region can extend outside the image.  If we're not
   // tiling, we don't want to draw into that area, so just draw within
   // the image bounds.
   const nsIntRect* tileSrcRect = GetTileSourceRect();
+  AutoSetOperator setOperator(aContext, GetOperator());
   PaintContext(pat,
                tileSrcRect ? GetVisibleRegion() : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)),
                tileSrcRect,
                aOpacity, aContext);
 
   GetContainer()->NotifyPaintedImage(image);
 
   return pat.forget();
@@ -825,17 +864,17 @@ BasicImageLayer::PaintContext(gfxPattern
     aContext->SetPattern(aPattern);
     aContext->FillWithOpacity(aOpacity);
   }
 
   // Reset extend mode for callers that need to reuse the pattern
   aPattern->SetExtend(extend);
 }
 
-class BasicColorLayer : public ColorLayer, BasicImplData {
+class BasicColorLayer : public ColorLayer, public BasicImplData {
 public:
   BasicColorLayer(BasicLayerManager* aLayerManager) :
     ColorLayer(aLayerManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicColorLayer);
   }
   virtual ~BasicColorLayer()
   {
@@ -848,16 +887,17 @@ public:
                  "Can only set properties in construction phase");
     ColorLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void Paint(gfxContext* aContext)
   {
     if (IsHidden())
       return;
+    AutoSetOperator setOperator(aContext, GetOperator());
     PaintColorTo(mColor, GetEffectiveOpacity(), aContext);
   }
 
   static void PaintColorTo(gfxRGBA aColor, float aOpacity,
                            gfxContext* aContext);
 
 protected:
   BasicLayerManager* BasicManager()
@@ -870,17 +910,17 @@ protected:
 BasicColorLayer::PaintColorTo(gfxRGBA aColor, float aOpacity,
                               gfxContext* aContext)
 {
   aContext->SetColor(aColor);
   aContext->Paint(aOpacity);
 }
 
 class BasicCanvasLayer : public CanvasLayer,
-                         BasicImplData
+                         public BasicImplData
 {
 public:
   BasicCanvasLayer(BasicLayerManager* aLayerManager) :
     CanvasLayer(aLayerManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicCanvasLayer);
   }
   virtual ~BasicCanvasLayer()
@@ -1018,29 +1058,30 @@ BasicCanvasLayer::PaintWithOpacity(gfxCo
 
   gfxMatrix m;
   if (mNeedsYFlip) {
     m = aContext->CurrentMatrix();
     aContext->Translate(gfxPoint(0.0, mBounds.height));
     aContext->Scale(1.0, -1.0);
   }
 
+  AutoSetOperator setOperator(aContext, GetOperator());
   aContext->NewPath();
   // No need to snap here; our transform is already set up to snap our rect
   aContext->Rectangle(gfxRect(0, 0, mBounds.width, mBounds.height));
   aContext->SetPattern(pat);
   aContext->FillWithOpacity(aOpacity);
 
   if (mNeedsYFlip) {
     aContext->SetMatrix(m);
   }
 }
 
 class BasicReadbackLayer : public ReadbackLayer,
-                           BasicImplData
+                           public BasicImplData
 {
 public:
   BasicReadbackLayer(BasicLayerManager* aLayerManager) :
     ReadbackLayer(aLayerManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicReadbackLayer);
   }
   virtual ~BasicReadbackLayer()
@@ -1482,18 +1523,23 @@ BasicLayerManager::SetRoot(Layer* aLayer
 void
 BasicLayerManager::PaintLayer(Layer* aLayer,
                               DrawThebesLayerCallback aCallback,
                               void* aCallbackData,
                               ReadbackProcessor* aReadback)
 {
   const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
   const gfx3DMatrix& effectiveTransform = aLayer->GetEffectiveTransform();
+  BasicContainerLayer* container = static_cast<BasicContainerLayer*>(aLayer);
   PRBool needsGroup = aLayer->GetFirstChild() &&
-      static_cast<BasicContainerLayer*>(aLayer)->UseIntermediateSurface();
+    container->UseIntermediateSurface();
+  NS_ASSERTION(needsGroup || !aLayer->GetFirstChild() ||
+               container->GetOperator() == gfxContext::OPERATOR_OVER,
+               "non-OVER operator should have forced UseIntermediateSurface");
+
   // If needsSaveRestore is false, we should still save and restore
   // the CTM
   PRBool needsSaveRestore = needsGroup || clipRect;
 
   gfxMatrix savedMatrix;
   if (needsSaveRestore) {
     mTarget->Save();
 
@@ -1563,16 +1609,17 @@ BasicLayerManager::PaintLayer(Layer* aLa
     }
   }
 
   if (needsGroup) {
     mTarget->PopGroupToSource();
     if (needsClipToVisibleRegion) {
       gfxUtils::ClipToRegion(mTarget, aLayer->GetEffectiveVisibleRegion());
     }
+    AutoSetOperator setOperator(mTarget, container->GetOperator());
     mTarget->Paint(aLayer->GetEffectiveOpacity());
   }
 
   if (pushedTargetOpaqueRect) {
     currentSurface->SetOpaqueRect(gfxRect(0, 0, 0, 0));
   }
 
   if (needsSaveRestore) {
@@ -2334,17 +2381,17 @@ protected:
   CreateBuffer(ContentType aType, const nsIntSize& aSize)
   {
     NS_RUNTIMEABORT("ShadowThebesLayer can't paint content");
     return nsnull;
   }
 };
 
 
-class BasicShadowThebesLayer : public ShadowThebesLayer, BasicImplData {
+class BasicShadowThebesLayer : public ShadowThebesLayer, public BasicImplData {
 public:
   BasicShadowThebesLayer(BasicShadowLayerManager* aLayerManager)
     : ShadowThebesLayer(aLayerManager, static_cast<BasicImplData*>(this))
     , mOldXResolution(1.0)
     , mOldYResolution(1.0)
   {
     MOZ_COUNT_CTOR(BasicShadowThebesLayer);
   }
@@ -2505,17 +2552,17 @@ BasicShadowThebesLayer::PaintThebes(gfxC
 
   if (!mFrontBuffer.GetBuffer()) {
     return;
   }
 
   mFrontBuffer.DrawTo(this, aContext, GetEffectiveOpacity());
 }
 
-class BasicShadowContainerLayer : public ShadowContainerLayer, BasicImplData {
+class BasicShadowContainerLayer : public ShadowContainerLayer, public BasicImplData {
   template<class Container>
   friend void ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer);
   template<class Container>
   friend void ContainerRemoveChild(Layer* aChild, Container* aContainer);
 
 public:
   BasicShadowContainerLayer(BasicShadowLayerManager* aLayerManager) :
     ShadowContainerLayer(aLayerManager, static_cast<BasicImplData*>(this))
@@ -2551,17 +2598,17 @@ public:
      * otherwise we need a PushGroup and we need to mark ourselves as using
      * an intermediate surface so our children don't inherit our opacity
      * via GetEffectiveOpacity.
      */
     mUseIntermediateSurface = GetEffectiveOpacity() != 1.0 && HasMultipleChildren();
   }
 };
 
-class BasicShadowImageLayer : public ShadowImageLayer, BasicImplData {
+class BasicShadowImageLayer : public ShadowImageLayer, public BasicImplData {
 public:
   BasicShadowImageLayer(BasicShadowLayerManager* aLayerManager) :
     ShadowImageLayer(aLayerManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicShadowImageLayer);
   }
   virtual ~BasicShadowImageLayer()
   {
@@ -2624,44 +2671,46 @@ BasicShadowImageLayer::Paint(gfxContext*
     BasicManager()->OpenDescriptor(mFrontBuffer);
   nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
   pat->SetFilter(mFilter);
 
   // The visible region can extend outside the image.  If we're not
   // tiling, we don't want to draw into that area, so just draw within
   // the image bounds.
   const nsIntRect* tileSrcRect = GetTileSourceRect();
+  AutoSetOperator setOperator(aContext, GetOperator());
   BasicImageLayer::PaintContext(pat,
                                 tileSrcRect ? GetEffectiveVisibleRegion() : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)),
                                 tileSrcRect,
                                 GetEffectiveOpacity(), aContext);
 }
 
 class BasicShadowColorLayer : public ShadowColorLayer,
-                              BasicImplData
+                              public BasicImplData
 {
 public:
   BasicShadowColorLayer(BasicShadowLayerManager* aLayerManager) :
     ShadowColorLayer(aLayerManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicShadowColorLayer);
   }
   virtual ~BasicShadowColorLayer()
   {
     MOZ_COUNT_DTOR(BasicShadowColorLayer);
   }
 
   virtual void Paint(gfxContext* aContext)
   {
+    AutoSetOperator setOperator(aContext, GetOperator());
     BasicColorLayer::PaintColorTo(mColor, GetEffectiveOpacity(), aContext);
   }
 };
 
 class BasicShadowCanvasLayer : public ShadowCanvasLayer,
-                               BasicImplData
+                               public BasicImplData
 {
 public:
   BasicShadowCanvasLayer(BasicShadowLayerManager* aLayerManager) :
     ShadowCanvasLayer(aLayerManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicShadowCanvasLayer);
   }
   virtual ~BasicShadowCanvasLayer()