Bug 1223736 - Part 2: Apply mask in correct coordinate space when drawing with 3d transforms with BasicCompositor. r=lsalzman
☠☠ backed out by b34c0c51ae70 ☠ ☠
authorMatt Woodrow <mwoodrow@mozilla.com>
Fri, 18 Mar 2016 18:45:50 +1300
changeset 342032 2b7b7ea014d8b429c2f2c1d0aeceb479d60d7cf0
parent 342031 ceb8532131fb64a9029f4ffeb146af8ea7f7537e
child 342033 b4a2342c8f5d781844dc26d9b504801d120910d6
push id13342
push userbmo:james@hoppipolla.co.uk
push dateFri, 18 Mar 2016 09:55:58 +0000
reviewerslsalzman
bugs1223736
milestone48.0a1
Bug 1223736 - Part 2: Apply mask in correct coordinate space when drawing with 3d transforms with BasicCompositor. r=lsalzman
gfx/layers/basic/BasicCompositor.cpp
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -392,16 +392,35 @@ Transform(DataSourceSurface* aDest,
 
 static inline IntRect
 RoundOut(Rect r)
 {
   r.RoundOut();
   return IntRect(r.x, r.y, r.width, r.height);
 }
 
+static void
+SetupMask(const EffectChain& aEffectChain,
+          DrawTarget* aDest,
+          const IntPoint& aOffset,
+          RefPtr<SourceSurface>& aMaskSurface,
+          Matrix& aMaskTransform)
+{
+  if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+    EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+    aMaskSurface = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(aDest);
+    if (!aMaskSurface) {
+      gfxWarning() << "Invalid sourceMask effect";
+    }
+    MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!");
+    aMaskTransform = effectMask->mMaskTransform.As2D();
+    aMaskTransform.PostTranslate(-aOffset.x, -aOffset.y);
+  }
+}
+
 void
 BasicCompositor::DrawQuad(const gfx::Rect& aRect,
                           const gfx::Rect& aClipRect,
                           const EffectChain &aEffectChain,
                           gfx::Float aOpacity,
                           const gfx::Matrix4x4& aTransform,
                           const gfx::Rect& aVisibleRect)
 {
@@ -437,36 +456,29 @@ BasicCompositor::DrawQuad(const gfx::Rec
       return;
     }
 
     // Propagate the coordinate offset to our 2D draw target.
     newTransform = Matrix::Translation(transformBounds.x, transformBounds.y);
 
     // When we apply the 3D transformation, we do it against a temporary
     // surface, so undo the coordinate offset.
-    new3DTransform = Matrix4x4::Translation(aRect.x, aRect.y, 0) * aTransform;
+    new3DTransform = aTransform;
+    new3DTransform.PreTranslate(aRect.x, aRect.y, 0);
   }
 
   buffer->PushClipRect(aClipRect);
 
   newTransform.PostTranslate(-offset.x, -offset.y);
   buffer->SetTransform(newTransform);
 
   RefPtr<SourceSurface> sourceMask;
   Matrix maskTransform;
-  if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
-    EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
-    sourceMask = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(dest);
-    if (!sourceMask) {
-      gfxWarning() << "Invalid sourceMask effect";
-    }
-    MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!");
-    MOZ_ASSERT(!effectMask->mIs3D);
-    maskTransform = effectMask->mMaskTransform.As2D();
-    maskTransform.PostTranslate(-offset.x, -offset.y);
+  if (aTransform.Is2D()) {
+    SetupMask(aEffectChain, dest, offset, sourceMask, maskTransform);
   }
 
   CompositionOp blendMode = CompositionOp::OP_OVER;
   if (Effect* effect = aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
     blendMode = static_cast<EffectBlendMode*>(effect)->mBlendMode;
   }
 
   switch (aEffectChain.mPrimaryEffect->mType) {
@@ -563,18 +575,50 @@ BasicCompositor::DrawQuad(const gfx::Rec
         );
     if (NS_WARN_IF(!temp)) {
       buffer->PopClip();
       return;
     }
 
     Transform(temp, source, new3DTransform, transformBounds.TopLeft());
 
+    SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform);
+
+    // Adjust for the fact that our content now start at 0,0 instead
+    // of the top left of transformBounds.
     transformBounds.MoveTo(0, 0);
-    buffer->DrawSurface(temp, transformBounds, transformBounds);
+    maskTransform.PostTranslate(-transformBounds.x, -transformBounds.y);
+
+    if (sourceMask) {
+      // Transform the source by it's normal transform, and then the inverse
+      // of the mask transform so that it's in the mask's untransformed
+      // coordinate space.
+      Matrix old = buffer->GetTransform();
+      Matrix sourceTransform = old;
+
+      Matrix inverseMask = maskTransform;
+      inverseMask.Invert();
+
+      sourceTransform *= inverseMask;
+
+      SurfacePattern source(temp, ExtendMode::CLAMP, sourceTransform);
+
+      buffer->PushClipRect(transformBounds);
+
+      // Mask in the untransformed coordinate space, and then transform
+      // by the mask transform to put the result back into destination
+      // coords.
+      buffer->SetTransform(maskTransform);
+      buffer->MaskSurface(source, sourceMask, Point(0, 0));
+      buffer->SetTransform(old);
+
+      buffer->PopClip();
+    } else {
+      buffer->DrawSurface(temp, transformBounds, transformBounds);
+    }
   }
 
   buffer->PopClip();
 }
 
 void
 BasicCompositor::ClearRect(const gfx::Rect& aRect)
 {