Bug 1372458 - Fold opacity into filter drawing rather than using a temporary surface. r=bas,mstange
authorMatt Woodrow <mwoodrow@mozilla.com>
Sat, 14 Jul 2018 15:25:49 +1200
changeset 426597 d48e40cba0b4
parent 426596 ae6e73727685
child 426598 ff25d41b504f
push id34276
push userncsoregi@mozilla.com
push dateSat, 14 Jul 2018 09:41:08 +0000
treeherdermozilla-central@04dd259d71db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas, mstange
bugs1372458
milestone63.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 1372458 - Fold opacity into filter drawing rather than using a temporary surface. r=bas,mstange MozReview-Commit-ID: GOBTUhN7fcC
gfx/2d/DrawTargetD2D1.cpp
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
layout/svg/nsSVGIntegrationUtils.cpp
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -250,17 +250,34 @@ DrawTargetD2D1::DrawFilter(FilterNode *a
 
   PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
 
   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
 
   FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode);
   node->WillDraw(this);
 
-  mDC->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
+  if (aOptions.mAlpha == 1.0f) {
+    mDC->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
+  } else {
+    RefPtr<ID2D1Image> image;
+    node->OutputEffect()->GetOutput(getter_AddRefs(image));
+
+    Matrix mat = Matrix::Translation(aDestPoint);
+
+    RefPtr<ID2D1ImageBrush> imageBrush;
+    mDC->CreateImageBrush(image,
+                          D2D1::ImageBrushProperties(D2DRect(aSourceRect)),
+                          D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(mat)),
+                          getter_AddRefs(imageBrush));
+    mDC->FillRectangle(D2D1::RectF(aDestPoint.x, aDestPoint.y,
+                                   aDestPoint.x + aSourceRect.width,
+                                   aDestPoint.y + aSourceRect.height),
+                       imageBrush);
+  }
 
   FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
 }
 
 void
 DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface,
                                       const Point &aDest,
                                       const Color &aColor,
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -60,17 +60,18 @@ UserSpaceMetricsForFrame(nsIFrame* aFram
   return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
 }
 
 void
 nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
                                      gfxContext* aCtx,
                                      nsSVGFilterPaintCallback *aPaintCallback,
                                      const nsRegion *aDirtyArea,
-                                     imgDrawingParams& aImgParams)
+                                     imgDrawingParams& aImgParams,
+                                     float aOpacity)
 {
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
 
   gfxContextMatrixAutoSaveRestore autoSR(aCtx);
   gfxSize scaleFactors = aCtx->CurrentMatrixDouble().ScaleFactors(true);
   if (scaleFactors.IsEmpty()) {
     return;
@@ -92,17 +93,17 @@ nsFilterInstance::PaintFilteredFrame(nsI
 
   // 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, scaleMatrixInDevUnits,
                             aDirtyArea, nullptr, nullptr, nullptr);
   if (instance.IsInitialized()) {
-    instance.Render(aCtx, aImgParams);
+    instance.Render(aCtx, aImgParams, aOpacity);
   }
 }
 
 nsRegion
 nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
                                          const nsRegion& aPreFilterDirtyRegion)
 {
   if (aPreFilterDirtyRegion.IsEmpty()) {
@@ -488,17 +489,17 @@ nsFilterInstance::BuildSourceImage(DrawT
 
   mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty, aImgParams);
 
   mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
   mSourceGraphic.mSurfaceRect = neededRect;
 }
 
 void
-nsFilterInstance::Render(gfxContext* aCtx, imgDrawingParams& aImgParams)
+nsFilterInstance::Render(gfxContext* aCtx, imgDrawingParams& aImgParams, float aOpacity)
 {
   MOZ_ASSERT(mTargetFrame, "Need a frame for rendering");
 
   if (mPrimitiveDescriptions.IsEmpty()) {
     // An filter without any primitive. Treat it as success and paint nothing.
     return;
   }
 
@@ -516,17 +517,17 @@ nsFilterInstance::Render(gfxContext* aCt
   BuildSourceImage(aCtx->GetDrawTarget(), aImgParams);
   BuildSourcePaints(aImgParams);
 
   FilterSupport::RenderFilterDescription(
     aCtx->GetDrawTarget(), mFilterDescription, IntRectToRect(filterRect),
     mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect,
     mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
     mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect,
-    mInputImages, Point(0, 0));
+    mInputImages, Point(0, 0), DrawOptions(aOpacity));
 }
 
 nsRegion
 nsFilterInstance::ComputePostFilterDirtyRegion()
 {
   if (mPreFilterDirtyRegion.IsEmpty() || mPrimitiveDescriptions.IsEmpty()) {
     return nsRegion();
   }
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -83,17 +83,18 @@ public:
    * @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 void PaintFilteredFrame(nsIFrame *aFilteredFrame,
                                  gfxContext* aCtx,
                                  nsSVGFilterPaintCallback *aPaintCallback,
                                  const nsRegion* aDirtyArea,
-                                 imgDrawingParams& aImgParams);
+                                 imgDrawingParams& aImgParams,
+                                 float aOpacity = 1.0f);
 
   /**
    * 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,
@@ -163,17 +164,17 @@ private:
   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.
    */
-  void Render(gfxContext* aCtx, imgDrawingParams& aImgParams);
+  void Render(gfxContext* aCtx, imgDrawingParams& aImgParams, float aOpacity = 1.0f);
 
   const FilterDescription& ExtractDescriptionAndAdditionalImages(nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages)
   {
     mInputImages.SwapElements(aOutAdditionalImages);
     return mFilterDescription;
   }
 
   /**
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -1084,32 +1084,23 @@ nsSVGIntegrationUtils::PaintFilter(const
     return;
   }
 
   gfxContext& context = aParams.ctx;
 
   gfxContextAutoSaveRestore autoSR(&context);
   EffectOffsets offsets = MoveContextOriginToUserSpace(firstFrame, aParams);
 
-  if (opacity != 1.0f) {
-    context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
-                                  nullptr, Matrix());
-  }
-
   /* Paint the child and apply filters */
   RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
                                      offsets.offsetToUserSpaceInDevPx);
   nsRegion dirtyRegion = aParams.dirtyRect - offsets.offsetToBoundingBox;
 
   nsFilterInstance::PaintFilteredFrame(frame, &context, &callback,
-                                       &dirtyRegion, aParams.imgParams);
-
-  if (opacity != 1.0f) {
-    context.PopGroupAndBlend();
-  }
+                                       &dirtyRegion, aParams.imgParams, opacity);
 }
 
 class PaintFrameCallback : public gfxDrawingCallback {
 public:
   PaintFrameCallback(nsIFrame* aFrame,
                      const nsSize aPaintServerSize,
                      const IntSize aRenderSize,
                      uint32_t aFlags)