Bug 1372458 - Fold opacity into filter drawing rather than using a temporary surface. r?mstange,Bas draft
authorMatt Woodrow <mwoodrow@mozilla.com>
Sat, 14 Jul 2018 15:25:49 +1200
changeset 818488 0036c609af9d4ef19804e206039ac5ccac1bd5f4
parent 816164 9302fd8c95c05e5a5cd295dde3bbdac2d58d6256
push id116270
push usermwoodrow@mozilla.com
push dateSat, 14 Jul 2018 03:26:16 +0000
reviewersmstange, Bas
bugs1372458
milestone63.0a1
Bug 1372458 - Fold opacity into filter drawing rather than using a temporary surface. r?mstange,Bas 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)