Bug 1334554 - Handle the return value of nsFilterInstance::PaintFilteredFrame r=mstange
authorcku <cku@mozilla.com>
Sat, 28 Jan 2017 02:54:38 +0800
changeset 341114 ddceecc1921e2eb2b28e9d104604750c095b2d0d
parent 341113 3660b57435a38d00299c024fbf070f47a73423a1
child 341115 55a8ad127517bb1438bb3a58f592d6930de44ef5
push id86634
push usercbook@mozilla.com
push dateTue, 07 Feb 2017 13:14:58 +0000
treeherdermozilla-inbound@9dbd2d9b334e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1334554
milestone54.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 1334554 - Handle the return value of nsFilterInstance::PaintFilteredFrame r=mstange MozReview-Commit-ID: 79gddAmRjnp
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGUtils.cpp
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -54,33 +54,34 @@ UserSpaceMetricsForFrame(nsIFrame* aFram
 {
   if (aFrame->GetContent()->IsSVGElement()) {
     nsSVGElement* element = static_cast<nsSVGElement*>(aFrame->GetContent());
     return MakeUnique<SVGElementMetrics>(element);
   }
   return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
 }
 
-nsresult
+DrawResult
 nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
                                      DrawTarget* aDrawTarget,
                                      const gfxMatrix& aTransform,
                                      nsSVGFilterPaintCallback *aPaintCallback,
                                      const nsRegion *aDirtyArea)
 {
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
   // Hardcode InputIsTainted to true because we don't want JS to be able to
   // read the rendered contents of aFilteredFrame.
   nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(), *metrics,
                             filterChain, /* InputIsTainted */ true, aPaintCallback,
                             aTransform, aDirtyArea, nullptr, nullptr, nullptr);
   if (!instance.IsInitialized()) {
-    return NS_OK;
+    return DrawResult::BAD_IMAGE;
   }
+
   return instance.Render(aDrawTarget);
 }
 
 nsRegion
 nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
                                          const nsRegion& aPreFilterDirtyRegion)
 {
   if (aPreFilterDirtyRegion.IsEmpty()) {
@@ -225,21 +226,16 @@ nsFilterInstance::nsFilterInstance(nsIFr
   mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds);
 
   // Build the filter graph.
   rv = BuildPrimitives(aFilterChain, aTargetFrame, aFilterInputIsTainted);
   if (NS_FAILED(rv)) {
     return;
   }
 
-  if (mPrimitiveDescriptions.IsEmpty()) {
-    // Nothing should be rendered.
-    return;
-  }
-
   // Convert the passed in rects from frame space to filter space:
   mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion);
   mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion);
 
   mInitialized = true;
 }
 
 nsresult
@@ -360,34 +356,37 @@ nsFilterInstance::ComputeNeededBoxes()
 
   sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, mTargetBounds);
 
   mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds();
   mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds();
   mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds();
 }
 
-nsresult
+DrawResult
 nsFilterInstance::BuildSourcePaint(SourceInfo *aSource,
                                    DrawTarget* aTargetDT)
 {
   MOZ_ASSERT(mTargetFrame);
   nsIntRect neededRect = aSource->mNeededBounds;
 
   RefPtr<DrawTarget> offscreenDT =
     gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
       neededRect.Size(), SurfaceFormat::B8G8R8A8);
   if (!offscreenDT || !offscreenDT->IsValid()) {
-    return NS_ERROR_OUT_OF_MEMORY;
+    return DrawResult::TEMPORARY_ERROR;
   }
 
   gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
-  if (!deviceToFilterSpace.Invert()) {
-    return NS_ERROR_FAILURE;
-  }
+  DebugOnly<bool> invertible = deviceToFilterSpace.Invert();
+  MOZ_ASSERT(invertible,
+             "The returning matix of GetFilterSpaceToDeviceSpaceTransform must"
+             "be an invertible matrix(not a singular one), since we already"
+             "checked it and early return if it's not from the caller side"
+             "(nsFilterInstance::Render)");
 
   if (!mPaintTransform.IsSingular()) {
     RefPtr<gfxContext> gfx = gfxContext::CreateOrNull(offscreenDT);
     MOZ_ASSERT(gfx); // already checked the draw target above
     gfx->Save();
     gfx->Multiply(mPaintTransform *
                   deviceToFilterSpace *
                   gfxMatrix::Translation(-neededRect.TopLeft()));
@@ -402,142 +401,160 @@ nsFilterInstance::BuildSourcePaint(Sourc
                             pattern);
     }
     gfx->Restore();
   }
 
   aSource->mSourceSurface = offscreenDT->Snapshot();
   aSource->mSurfaceRect = neededRect;
 
-  return NS_OK;
+  return DrawResult::SUCCESS;
 }
 
-nsresult
+DrawResult
 nsFilterInstance::BuildSourcePaints(DrawTarget* aTargetDT)
 {
-  nsresult rv = NS_OK;
-
   if (!mFillPaint.mNeededBounds.IsEmpty()) {
-    rv = BuildSourcePaint(&mFillPaint, aTargetDT);
-    NS_ENSURE_SUCCESS(rv, rv);
+    DrawResult result = BuildSourcePaint(&mFillPaint, aTargetDT);
+    if (result != DrawResult::SUCCESS) {
+      return result;
+    }
   }
 
   if (!mStrokePaint.mNeededBounds.IsEmpty()) {
-    rv = BuildSourcePaint(&mStrokePaint, aTargetDT);
-    NS_ENSURE_SUCCESS(rv, rv);
+    DrawResult result = BuildSourcePaint(&mStrokePaint, aTargetDT);
+    if (result != DrawResult::SUCCESS) {
+      return result;
+    }
   }
-  return  rv;
+
+  return  DrawResult::SUCCESS;
 }
 
-nsresult
+DrawResult
 nsFilterInstance::BuildSourceImage(DrawTarget* aTargetDT)
 {
   MOZ_ASSERT(mTargetFrame);
 
   nsIntRect neededRect = mSourceGraphic.mNeededBounds;
   if (neededRect.IsEmpty()) {
-    return NS_OK;
+    return DrawResult::SUCCESS;
   }
 
   RefPtr<DrawTarget> offscreenDT =
     gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
       neededRect.Size(), SurfaceFormat::B8G8R8A8);
   if (!offscreenDT || !offscreenDT->IsValid()) {
-    return NS_ERROR_OUT_OF_MEMORY;
+    return DrawResult::TEMPORARY_ERROR;
   }
 
   gfxRect r = FilterSpaceToUserSpace(ThebesRect(neededRect));
   r.RoundOut();
   nsIntRect dirty;
-  if (!gfxUtils::GfxRectToIntRect(r, &dirty))
-    return NS_ERROR_FAILURE;
+  if (!gfxUtils::GfxRectToIntRect(r, &dirty)){
+    return DrawResult::SUCCESS;
+  }
 
   // SVG graphics paint to device space, so we need to set an initial device
   // space to filter space transform on the gfxContext that SourceGraphic
   // and SourceAlpha will paint to.
   //
   // (In theory it would be better to minimize error by having filtered SVG
   // graphics temporarily paint to user space when painting the sources and
   // only set a user space to filter space transform on the gfxContext
   // (since that would eliminate the transform multiplications from user
   // space to device space and back again). However, that would make the
   // code more complex while being hard to get right without introducing
   // subtle bugs, and in practice it probably makes no real difference.)
   gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
-  if (!deviceToFilterSpace.Invert()) {
-    return NS_ERROR_FAILURE;
-  }
+  DebugOnly<bool> invertible = deviceToFilterSpace.Invert();
+  MOZ_ASSERT(invertible,
+             "The returning matix of GetFilterSpaceToDeviceSpaceTransform must"
+             "be an invertible matrix(not a singular one), since we already"
+             "checked it and early return if it's not from the caller side"
+             "(nsFilterInstance::Render)");
+
   RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(offscreenDT);
   MOZ_ASSERT(ctx); // already checked the draw target above
   ctx->SetMatrix(
     ctx->CurrentMatrix().Translate(-neededRect.TopLeft()).
                          PreMultiply(deviceToFilterSpace));
 
   DrawResult result =
     mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty);
 
   mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
   mSourceGraphic.mSurfaceRect = neededRect;
 
-  return (result == DrawResult::SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+  return result;
 }
 
-nsresult
+DrawResult
 nsFilterInstance::Render(DrawTarget* aDrawTarget)
 {
   MOZ_ASSERT(mTargetFrame, "Need a frame for rendering");
 
+  if (mPrimitiveDescriptions.IsEmpty()) {
+    // An filter without any primitive. Treat it as success and paint nothing.
+    return DrawResult::SUCCESS;
+  }
+
   nsIntRect filterRect =
     mPostFilterDirtyRegion.GetBounds().Intersect(OutputFilterSpaceBounds());
   gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform();
 
   if (filterRect.IsEmpty() || ctm.IsSingular()) {
-    return NS_OK;
+    return DrawResult::SUCCESS;
   }
 
   AutoRestoreTransform autoRestoreTransform(aDrawTarget);
   Matrix newTM = ToMatrix(ctm).PreTranslate(filterRect.x, filterRect.y) *
                  aDrawTarget->GetTransform();
   aDrawTarget->SetTransform(newTM);
 
   ComputeNeededBoxes();
-
-  nsresult rv = BuildSourceImage(aDrawTarget);
-  if (NS_FAILED(rv))
-    return rv;
-  rv = BuildSourcePaints(aDrawTarget);
-  if (NS_FAILED(rv))
-    return rv;
+  DrawResult result = BuildSourceImage(aDrawTarget);
+  if (result != DrawResult::SUCCESS){
+    return result;
+  }
+  result = BuildSourcePaints(aDrawTarget);
+  if (result != DrawResult::SUCCESS){
+    return result;
+  }
 
   FilterSupport::RenderFilterDescription(
     aDrawTarget, mFilterDescription, IntRectToRect(filterRect),
     mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect,
     mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
     mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect,
     mInputImages, Point(0, 0));
 
-  return NS_OK;
+  return DrawResult::SUCCESS;
 }
 
 nsRegion
 nsFilterInstance::ComputePostFilterDirtyRegion()
 {
-  if (mPreFilterDirtyRegion.IsEmpty()) {
+  if (mPreFilterDirtyRegion.IsEmpty() || mPrimitiveDescriptions.IsEmpty()) {
     return nsRegion();
   }
 
   nsIntRegion resultChangeRegion =
     FilterSupport::ComputeResultChangeRegion(mFilterDescription,
       mPreFilterDirtyRegion, nsIntRegion(), nsIntRegion());
   return FilterSpaceToFrameSpace(resultChangeRegion);
 }
 
 nsRect
 nsFilterInstance::ComputePostFilterExtents()
 {
+  if (mPrimitiveDescriptions.IsEmpty()) {
+    return nsRect();
+  }
+
   nsIntRegion postFilterExtents =
     FilterSupport::ComputePostFilterExtents(mFilterDescription, mTargetBounds);
   return FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
 }
 
 nsRect
 nsFilterInstance::ComputeSourceNeededRect()
 {
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -50,17 +50,17 @@ class UserSpaceMetrics;
 class nsFilterInstance
 {
   typedef mozilla::gfx::IntRect IntRect;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
   typedef mozilla::gfx::FilterDescription FilterDescription;
   typedef mozilla::dom::UserSpaceMetrics UserSpaceMetrics;
-
+  typedef mozilla::image::DrawResult DrawResult;
 public:
   /**
    * Create a FilterDescription for the supplied filter. All coordinates in
    * the description are in filter space.
    * @param aFilterInputIsTainted Describes whether the SourceImage / SourceAlpha
    *   input is tainted. This affects whether feDisplacementMap will respect
    *   the filter input as its map input, and it affects the IsTainted() state
    *   on the filter primitives in the FilterDescription. "Tainted" is a term
@@ -78,21 +78,21 @@ public:
                                                 nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages);
 
   /**
    * Paint the given filtered frame.
    * @param aDirtyArea The area than needs to be painted, in aFilteredFrame's
    *   frame space (i.e. relative to its origin, the top-left corner of its
    *   border box).
    */
-  static nsresult PaintFilteredFrame(nsIFrame *aFilteredFrame,
-                                     DrawTarget* aDrawTarget,
-                                     const gfxMatrix& aTransform,
-                                     nsSVGFilterPaintCallback *aPaintCallback,
-                                     const nsRegion* aDirtyArea);
+  static DrawResult PaintFilteredFrame(nsIFrame *aFilteredFrame,
+                                       DrawTarget* aDrawTarget,
+                                       const gfxMatrix& aTransform,
+                                       nsSVGFilterPaintCallback *aPaintCallback,
+                                       const nsRegion* aDirtyArea);
 
   /**
    * Returns the post-filter area that could be dirtied when the given
    * pre-filter area of aFilteredFrame changes.
    * @param aPreFilterDirtyRegion The pre-filter area of aFilteredFrame that has
    *   changed, relative to aFilteredFrame, in app units.
    */
   static nsRegion GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
@@ -161,17 +161,17 @@ public:
   bool IsInitialized() const { return mInitialized; }
 
   /**
    * Draws the filter output into aDrawTarget. The area that
    * needs to be painted must have been specified before calling this method
    * by passing it as the aPostFilterDirtyRegion argument to the
    * nsFilterInstance constructor.
    */
-  nsresult Render(DrawTarget* aDrawTarget);
+  DrawResult Render(DrawTarget* aDrawTarget);
 
   const FilterDescription& ExtractDescriptionAndAdditionalImages(nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages)
   {
     mInputImages.SwapElements(aOutAdditionalImages);
     return mFilterDescription;
   }
 
   /**
@@ -223,31 +223,31 @@ private:
     // Set by BuildSourceImage / BuildSourcePaint.
     IntRect mSurfaceRect;
   };
 
   /**
    * Creates a SourceSurface for either the FillPaint or StrokePaint graph
    * nodes
    */
-  nsresult BuildSourcePaint(SourceInfo *aPrimitive,
+  DrawResult BuildSourcePaint(SourceInfo *aPrimitive,
                             DrawTarget* aTargetDT);
 
   /**
    * Creates a SourceSurface for either the FillPaint and StrokePaint graph
    * nodes, fills its contents and assigns it to mFillPaint.mSourceSurface and
    * mStrokePaint.mSourceSurface respectively.
    */
-  nsresult BuildSourcePaints(DrawTarget* aTargetDT);
+  DrawResult BuildSourcePaints(DrawTarget* aTargetDT);
 
   /**
    * Creates the SourceSurface for the SourceGraphic graph node, paints its
    * contents, and assigns it to mSourceGraphic.mSourceSurface.
    */
-  nsresult BuildSourceImage(DrawTarget* aTargetDT);
+  DrawResult BuildSourceImage(DrawTarget* aTargetDT);
 
   /**
    * Build the list of FilterPrimitiveDescriptions that describes the filter's
    * filter primitives and their connections. This populates
    * mPrimitiveDescriptions and mInputImages. aFilterInputIsTainted describes
    * whether the SourceGraphic is tainted.
    */
   nsresult BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -1110,24 +1110,25 @@ nsSVGIntegrationUtils::PaintFilter(const
                                   nullptr, Matrix());
   }
 
   /* Paint the child and apply filters */
   RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
                                      offsetToUserSpace);
   nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox;
   gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
-  nsFilterInstance::PaintFilteredFrame(frame, context.GetDrawTarget(),
-                                       tm, &callback, &dirtyRegion);
+  DrawResult result =
+    nsFilterInstance::PaintFilteredFrame(frame, context.GetDrawTarget(),
+                                         tm, &callback, &dirtyRegion);
 
   if (opacity != 1.0f) {
     context.PopGroupAndBlend();
   }
 
-  return DrawResult::SUCCESS;
+  return result;
 }
 
 gfxMatrix
 nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
 {
   int32_t appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
   float devPxPerCSSPx =
     1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -855,19 +855,20 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
                                       aDirtyRect->width, aDirtyRect->height));
       tmpDirtyRegion =
         nsLayoutUtils::RoundGfxRectToAppRect(
           dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
         aFrame->GetPosition();
       dirtyRegion = &tmpDirtyRegion;
     }
     SVGPaintCallback paintCallback;
-    nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
-                                         aTransform, &paintCallback,
-                                         dirtyRegion);
+    result =
+      nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
+                                           aTransform, &paintCallback,
+                                           dirtyRegion);
   } else {
     result = svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
   }
 
   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     aContext.PopClip();
   }