Bug 1275450 - Part 1. Generate a transparent black mask layer when there is no resolvable mask source or image r=mstange
authorcku <cku@mozilla.com>
Tue, 31 May 2016 15:45:42 +0800
changeset 338681 619441944e8a9ca62bcab61bca61b78599f7da6c
parent 338680 373e67c2caaddfec3d3901e038804543c3da9afb
child 338682 5abd097ad9e02ab20edf5eadccfb4902c177c1ed
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1275450
milestone49.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 1275450 - Part 1. Generate a transparent black mask layer when there is no resolvable mask source or image r=mstange MozReview-Commit-ID: I2QlZnz07TL
layout/svg/nsSVGIntegrationUtils.cpp
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -404,91 +404,76 @@ public:
   }
 
 private:
   nsDisplayListBuilder* mBuilder;
   LayerManager* mLayerManager;
   nsPoint mOffset;
 };
 
-static bool
-HasMaskToDraw(const nsStyleSVGReset* aSVGReset,
-              nsSVGEffects::EffectProperties& aEffectProperties)
-{
-  nsTArray<nsSVGMaskFrame*> svgMaskFrames = aEffectProperties.GetMaskFrames();
-  for (int i = svgMaskFrames.Length() - 1; i >= 0 ; i--) {
-    nsSVGMaskFrame *maskFrame = svgMaskFrames[i];
-
-    // We found a SVG mask or an image mask.
-    if (maskFrame || !aSVGReset->mMask.mLayers[i].mImage.IsEmpty()) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 static void
 GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
                     float aOpacity, nsStyleContext* aSC,
-                    nsSVGEffects::EffectProperties& aEffectProperties,
+                    const nsTArray<nsSVGMaskFrame *>& aMaskFrames,
                     const gfxPoint& aOffest, Matrix& aOutMaskTransform,
                     RefPtr<SourceSurface>& aOutMaskSurface)
 {
   const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
-  MOZ_ASSERT(HasMaskToDraw(svgReset, aEffectProperties));
-
-  nsTArray<nsSVGMaskFrame *> svgMaskFrames = aEffectProperties.GetMaskFrames();
-  MOZ_ASSERT(svgMaskFrames.Length() == svgReset->mMask.mImageCount);
+  MOZ_ASSERT(aMaskFrames.Length() > 0);
 
   gfxMatrix cssPxToDevPxMatrix =
     nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame);
 
   gfxContext& ctx = aParams.ctx;
 
-  // There is only one mask. And that mask is a SVG mask.
-  if ((svgMaskFrames.Length() == 1) && svgMaskFrames[0]) {
+  // There is only one SVG mask.
+  if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) {
     aOutMaskSurface =
-      svgMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame,
-                                              cssPxToDevPxMatrix, aOpacity,
-                                              &aOutMaskTransform,
-                                              svgReset->mMask.mLayers[0].mMaskMode);
+      aMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame,
+                                            cssPxToDevPxMatrix, aOpacity,
+                                            &aOutMaskTransform,
+                                            svgReset->mMask.mLayers[0].mMaskMode);
     return;
   }
 
   ctx.Save();
   ctx.SetMatrix(gfxMatrix());
   gfxRect clipExtents = ctx.GetClipExtents();
   IntRect maskSurfaceRect = RoundedOut(ToRect(clipExtents));
   ctx.Restore();
 
+  if (maskSurfaceRect.IsEmpty()) {
+    return;
+  }
+
   // Mask composition result on CoreGraphic::A8 surface is not correct
   // when mask-mode is not add(source over). Switch to skia when CG backend
   // detected.
   RefPtr<DrawTarget> maskDT =
     (ctx.GetDrawTarget()->GetBackendType() == BackendType::COREGRAPHICS ||
      ctx.GetDrawTarget()->GetBackendType() == BackendType::DIRECT2D1_1)
     ? Factory::CreateDrawTarget(BackendType::SKIA, maskSurfaceRect.Size(),
                                 SurfaceFormat::A8)
     : ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(),
                                                    SurfaceFormat::A8);
+
   RefPtr<gfxContext> maskContext = gfxContext::ForDrawTarget(maskDT);
 
   // Set ctx's matrix on maskContext, offset by the maskSurfaceRect's position.
   // This makes sure that we combine the masks in device space.
   gfxMatrix maskSurfaceMatrix =
     ctx.CurrentMatrix() * gfxMatrix::Translation(-maskSurfaceRect.TopLeft());
   maskContext->SetMatrix(maskSurfaceMatrix);
 
   // Multiple SVG masks interleave with image mask. Paint each layer onto maskDT
   // one at a time.
-  for (int i = svgMaskFrames.Length() - 1; i >= 0 ; i--) {
-    nsSVGMaskFrame *maskFrame = svgMaskFrames[i];
+  for (int i = aMaskFrames.Length() - 1; i >= 0 ; i--) {
+    nsSVGMaskFrame *maskFrame = aMaskFrames[i];
 
-    CompositionOp compositionOp = (i == int(svgMaskFrames.Length() - 1))
+    CompositionOp compositionOp = (i == int(aMaskFrames.Length() - 1))
       ? CompositionOp::OP_OVER
       : nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite);
 
     // maskFrame != nullptr means we get a SVG mask.
     // maskFrame == nullptr means we get an image mask.
     if (maskFrame) {
       Matrix svgMaskMatrix;
       RefPtr<SourceSurface> svgMask =
@@ -625,46 +610,58 @@ nsSVGIntegrationUtils::PaintFramesWithEf
   gfxPoint devPixelOffsetToUserSpace =
     nsLayoutUtils::PointToGfxPoint(offsetToUserSpace,
                                    frame->PresContext()->AppUnitsPerDevPixel());
   context.SetMatrix(context.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
 
   gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame);
 
   const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
-  bool hasMaskToDraw = HasMaskToDraw(svgReset, effectProperties);
+  nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
+  // For a HTML doc:
+  //   According to css-masking spec, always create a mask surface when we
+  //   have any item in maskFrame even if all of those items are
+  //   non-resolvable <mask-sources> or <images>, we still need to create a
+  //   transparent black mask layer under this condition.
+  // For a SVG doc:
+  //   SVG 1.1 say that  if we fail to resolve a mask, we should draw the
+  //   object unmasked.
+  nsIDocument* currentDoc = frame->PresContext()->Document();
+  bool shouldGenerateMaskLayer = currentDoc->IsSVGDocument()
+                                 ? maskFrames.Length() == 1 && maskFrames[0]
+                                 : maskFrames.Length() > 0;
 
   // These are used if we require a temporary surface for a custom blend mode.
   RefPtr<gfxContext> target = &aParams.ctx;
   IntPoint targetOffset;
 
   bool complexEffects = false;
   /* Check if we need to do additional operations on this child's
    * rendering, which necessitates rendering into another surface. */
   if (opacity != 1.0f ||  (clipPathFrame && !isTrivialClip)
       || frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL
-      || hasMaskToDraw) {
+      || shouldGenerateMaskLayer) {
     complexEffects = true;
 
     context.Save();
     nsRect clipRect =
       frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
     context.Clip(NSRectToSnappedRect(clipRect,
                                   frame->PresContext()->AppUnitsPerDevPixel(),
                                   *drawTarget));
     Matrix maskTransform;
     RefPtr<SourceSurface> maskSurface;
 
-    if (hasMaskToDraw) {
+    if (shouldGenerateMaskLayer) {
       GenerateMaskSurface(aParams, opacity, firstFrame->StyleContext(),
-                          effectProperties, devPixelOffsetToUserSpace,
+                          maskFrames, devPixelOffsetToUserSpace,
                           maskTransform, maskSurface);
     }
 
-    if (hasMaskToDraw && !maskSurface) {
+    if (shouldGenerateMaskLayer && !maskSurface) {
       // Entire surface is clipped out.
       context.Restore();
       return;
     }
 
     if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
       // Create a temporary context to draw to so we can blend it back with
       // another operator.
@@ -695,17 +692,18 @@ nsSVGIntegrationUtils::PaintFramesWithEf
                                                                          &clippedMaskTransform, maskSurface, maskTransform);
 
       if (clipMaskSurface) {
         maskSurface = clipMaskSurface;
         maskTransform = clippedMaskTransform;
       }
     }
 
-    if (opacity != 1.0f || hasMaskToDraw || (clipPathFrame && !isTrivialClip)) {
+    if (opacity != 1.0f || shouldGenerateMaskLayer ||
+        (clipPathFrame && !isTrivialClip)) {
       target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform);
     }
   }
 
   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
    * we can just do normal painting and get it clipped appropriately.
    */
   if (clipPathFrame && isTrivialClip) {
@@ -740,17 +738,18 @@ nsSVGIntegrationUtils::PaintFramesWithEf
     context.Restore();
   }
 
   /* No more effects, we're done. */
   if (!complexEffects) {
     return;
   }
 
-  if (opacity != 1.0f || hasMaskToDraw || (clipPathFrame && !isTrivialClip)) {
+  if (opacity != 1.0f || shouldGenerateMaskLayer ||
+      (clipPathFrame && !isTrivialClip)) {
     target->PopGroupAndBlend();
   }
 
   if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
     RefPtr<DrawTarget> targetDT = target->GetDrawTarget();
     target = nullptr;
     RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();