Bug 1234485 - Part 8. Implement ContainerState::SetupMaskLayerForCSSMask. draft
authorcku <cku@mozilla.com>
Mon, 07 Nov 2016 22:32:43 +0800
changeset 435839 a9357a72eb8981895a165950a69abcc6ac145042
parent 435838 46662fa346b7f24ed2e9e350025432c23d97b93f
child 435840 876f83fcc3251e6fa6cff1978d6218e146ef886d
push id35137
push userbmo:cku@mozilla.com
push dateWed, 09 Nov 2016 09:16:18 +0000
bugs1234485
milestone52.0a1
Bug 1234485 - Part 8. Implement ContainerState::SetupMaskLayerForCSSMask. MozReview-Commit-ID: Gu1u4WvL2Cy
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1253,17 +1253,19 @@ protected:
    * a layer doesn't exist.
    *
    * Since mask layers can exist either on the layer directly, or as a side-
    * attachment to FrameMetrics (for ancestor scrollframe clips), we key the
    * recycle operation on both the originating layer and the mask layer's
    * index in the layer, if any.
    */
   struct MaskLayerKey;
-  already_AddRefed<ImageLayer> CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey);
+  already_AddRefed<ImageLayer>
+  CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey,
+                                   mozilla::function<void(Layer* aLayer)> aSetUserData);
   /**
    * Grabs all PaintedLayers and ColorLayers from the ContainerLayer and makes them
    * available for recycling.
    */
   void CollectOldLayers();
   /**
    * If aItem used to belong to a PaintedLayer, invalidates the area of
    * aItem in that layer. If aNewLayer is a PaintedLayer, invalidates the area of
@@ -1346,16 +1348,22 @@ protected:
    * add it to |aLayer|'s ancestor mask layers, returning an index into
    * the array of ancestor mask layers. Returns an empty Maybe if
    * |aClip| does not have rounded corners, or if no mask layer could
    * be created.
    */
   Maybe<size_t> SetupMaskLayerForScrolledClip(Layer* aLayer,
                                               const DisplayItemClip& aClip);
 
+  /*
+   * Create/find a mask layer with suitable size for aMaskItem to paint
+   * css-positioned-masking onto.
+   */
+  void SetupMaskLayerForCSSMask(Layer* aLayer, nsDisplayMask* aMaskItem);
+
   already_AddRefed<Layer> CreateMaskLayer(
     Layer *aLayer, const DisplayItemClip& aClip,
     const Maybe<size_t>& aForAncestorMaskLayer,
     uint32_t aRoundedRectClipCount = UINT32_MAX);
 
   bool ChooseAnimatedGeometryRoot(const nsDisplayList& aList,
                                   AnimatedGeometryRoot **aAnimatedGeometryRoot);
 
@@ -2222,29 +2230,30 @@ ContainerState::CreateOrRecycleImageLaye
 
     // Remove other layer types we might have stored for this PaintedLayer
     data->mColorLayer = nullptr;
   }
   return layer.forget();
 }
 
 already_AddRefed<ImageLayer>
-ContainerState::CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey)
+ContainerState::CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey,
+                                                 mozilla::function<void(Layer* aLayer)> aSetUserData)
 {
   RefPtr<ImageLayer> result = mRecycledMaskImageLayers.Get(aKey);
   if (result) {
     mRecycledMaskImageLayers.Remove(aKey);
     aKey.mLayer->ClearExtraDumpInfo();
     // XXX if we use clip on mask layers, null it out here
   } else {
     // Create a new layer
     result = mManager->CreateImageLayer();
     if (!result)
       return nullptr;
-    result->SetUserData(&gMaskLayerUserData, new MaskLayerUserData());
+    aSetUserData(result);
   }
 
   return result.forget();
 }
 
 static const double SUBPIXEL_OFFSET_EPSILON = 0.02;
 
 /**
@@ -3860,16 +3869,73 @@ ContainerState::SetupMaskLayerForScrolle
       aLayer->AddAncestorMaskLayer(maskLayer);
       return maskLayerIndex;
     }
     // Fall through to |return Nothing()|.
   }
   return Nothing();
 }
 
+void
+ContainerState::SetupMaskLayerForCSSMask(Layer* aLayer,
+                                         nsDisplayMask* aMaskItem)
+{
+  MOZ_ASSERT(mManager->IsCompositingCheap());
+
+  RefPtr<ImageLayer> maskLayer =
+    CreateOrRecycleMaskImageLayerFor(MaskLayerKey(aLayer, Nothing()),
+      [](Layer* aMaskLayer)
+      {
+        aMaskLayer->SetUserData(&gCSSMaskLayerUserData,
+                                new CSSMaskLayerUserData());
+      }
+    );
+
+  CSSMaskLayerUserData* oldUserData =
+    static_cast<CSSMaskLayerUserData*>(maskLayer->GetUserData(&gCSSMaskLayerUserData));
+
+  bool snap;
+  nsRect bounds = aMaskItem->GetBounds(mBuilder, &snap);
+  CSSMaskLayerUserData newUserData(aMaskItem->Frame(), bounds);
+  if (*oldUserData == newUserData) {
+    aLayer->SetMaskLayer(maskLayer);
+    return;
+  }
+
+  const nsIFrame* frame = aMaskItem->Frame();
+  int32_t A2D = frame->PresContext()->AppUnitsPerDevPixel();
+  Rect devBounds = NSRectToRect(bounds, A2D);
+  uint32_t maxSize = mManager->GetMaxTextureSize();
+  gfx::Size surfaceSize(std::min<gfx::Float>(devBounds.Width(), maxSize),
+                        std::min<gfx::Float>(devBounds.Height(), maxSize));
+  IntSize surfaceSizeInt(NSToIntCeil(surfaceSize.width),
+                         NSToIntCeil(surfaceSize.height));
+
+  if (surfaceSizeInt.IsEmpty()) {
+    return;
+  }
+
+  MaskImageData imageData(surfaceSizeInt, mManager);
+  RefPtr<DrawTarget> dt = imageData.CreateDrawTarget();
+  if (!dt || !dt->IsValid()) {
+    NS_WARNING("Could not create DrawTarget for mask layer.");
+    return;
+  }
+
+  RefPtr<ImageContainer> imgContainer =
+    imageData.CreateImageAndImageContainer();
+  if (!imgContainer) {
+    return;
+  }
+  maskLayer->SetContainer(imgContainer);
+
+  *oldUserData = newUserData;
+  aLayer->SetMaskLayer(maskLayer);
+}
+
 /*
  * Iterate through the non-clip items in aList and its descendants.
  * For each item we compute the effective clip rect. Each item is assigned
  * to a layer. We invalidate the areas in PaintedLayers where an item
  * has moved from one PaintedLayer to another. Also,
  * aState->mInvalidPaintedContent is invalidated in every PaintedLayer.
  * We set the clip rect for items that generated their own layer, and
  * create a mask layer to do any rounded rect clipping.
@@ -6141,19 +6207,33 @@ ContainerState::SetupMaskLayer(Layer *aL
 }
 
 already_AddRefed<Layer>
 ContainerState::CreateMaskLayer(Layer *aLayer,
                                const DisplayItemClip& aClip,
                                const Maybe<size_t>& aForAncestorMaskLayer,
                                uint32_t aRoundedRectClipCount)
 {
+  // aLayer will never be the container layer created by an nsDisplayMask
+  // because nsDisplayMask propagates the DisplayItemClip to its contents
+  // and is not clipped itself.
+  // This assertion will fail if that ever stops being the case.
+  MOZ_ASSERT(!aLayer->GetUserData(&gCSSMaskLayerUserData),
+             "A layer contains round clips should not have css-mask on it.");
+
   // check if we can re-use the mask layer
   MaskLayerKey recycleKey(aLayer, aForAncestorMaskLayer);
-  RefPtr<ImageLayer> maskLayer = CreateOrRecycleMaskImageLayerFor(recycleKey);
+  RefPtr<ImageLayer> maskLayer =
+    CreateOrRecycleMaskImageLayerFor(recycleKey,
+      [](Layer* aMaskLayer)
+      {
+        aMaskLayer->SetUserData(&gMaskLayerUserData,
+                                new MaskLayerUserData());
+      }
+    );
   MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer);
 
   MaskLayerUserData newData;
   aClip.AppendRoundedRects(&newData.mRoundedClipRects, aRoundedRectClipCount);
   newData.mScaleX = mParameters.mXScale;
   newData.mScaleY = mParameters.mYScale;
   newData.mOffset = mParameters.mOffset;
   newData.mAppUnitsPerDevPixel = mContainerFrame->PresContext()->AppUnitsPerDevPixel();
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -19,16 +19,17 @@
 #include "Layers.h"
 #include "LayerUserData.h"
 
 class nsDisplayListBuilder;
 class nsDisplayList;
 class nsDisplayItem;
 class gfxContext;
 class nsDisplayItemGeometry;
+class nsDisplayMask;
 
 namespace mozilla {
 class DisplayItemScrollClip;
 namespace layers {
 class ContainerLayer;
 class LayerManager;
 class BasicLayerManager;
 class PaintedLayer;