Bug 1314536 - Part 2. Implement MixModeBlender to simplify nsSVGUtils::PaintFrameWithEffects.
MozReview-Commit-ID: JBlj7nApsbT
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -559,70 +559,120 @@ nsSVGUtils::DetermineMaskUsage(nsIFrame*
!aUsage.shouldApplyClipPath && !aUsage.shouldApplyBasicShape);
break;
default:
MOZ_ASSERT_UNREACHABLE("Unsupported clip-path type.");
break;
}
}
-static IntRect
-ComputeClipExtsInDeviceSpace(gfxContext& aCtx)
-{
- gfxContextMatrixAutoSaveRestore matRestore(&aCtx);
+class MixModeBlender {
+public:
+ typedef mozilla::gfx::Factory Factory;
- // Get the clip extents in device space.
- aCtx.SetMatrix(gfxMatrix());
- gfxRect clippedFrameSurfaceRect = aCtx.GetClipExtents();
- clippedFrameSurfaceRect.RoundOut();
+ MixModeBlender(nsIFrame *aFrame, gfxContext* aContext)
+ : mFrame(aFrame), mSourceCtx(aContext)
+ {
+ MOZ_ASSERT(mFrame && mSourceCtx);
+ }
+
+ bool ShouldCreateDrawTargetForBlend() const
+ {
+ return mFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
+ }
+
+ gfxContext* CreateBlendTarget(const gfxMatrix& aTransform)
+ {
+ MOZ_ASSERT(ShouldCreateDrawTargetForBlend());
- IntRect result;
- ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
- return mozilla::gfx::Factory::CheckSurfaceSize(result.Size()) ? result
- : IntRect();
-}
+ // Create a temporary context to draw to so we can blend it back with
+ // another operator.
+ IntRect drawRect = ComputeClipExtsInDeviceSpace(aTransform);
+
+ RefPtr<DrawTarget> targetDT =
+ mSourceCtx->GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(),
+ SurfaceFormat::B8G8R8A8);
+ if (!targetDT || !targetDT->IsValid()) {
+ return nullptr;
+ }
-static already_AddRefed<gfxContext>
-CreateBlendTarget(gfxContext* aContext, IntPoint& aTargetOffset)
-{
- // Create a temporary context to draw to so we can blend it back with
- // another operator.
- IntRect drawRect = ComputeClipExtsInDeviceSpace(*aContext);
+ MOZ_ASSERT(!mTargetCtx,
+ "CreateBlendTarget is designed to be used once only.");
- RefPtr<DrawTarget> targetDT =
- aContext->GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(),
- SurfaceFormat::B8G8R8A8);
- if (!targetDT || !targetDT->IsValid()) {
- return nullptr;
+ mTargetCtx = gfxContext::CreateOrNull(targetDT);
+ MOZ_ASSERT(mTargetCtx); // already checked the draw target above
+ mTargetCtx->SetMatrix(mSourceCtx->CurrentMatrix() *
+ gfxMatrix::Translation(-drawRect.TopLeft()));
+
+ mTargetOffset = drawRect.TopLeft();
+
+ return mTargetCtx;
}
- RefPtr<gfxContext> target = gfxContext::CreateOrNull(targetDT);
- MOZ_ASSERT(target); // already checked the draw target above
- target->SetMatrix(aContext->CurrentMatrix() *
- gfxMatrix::Translation(-drawRect.TopLeft()));
- aTargetOffset = drawRect.TopLeft();
+ void BlendToTarget()
+ {
+ MOZ_ASSERT(ShouldCreateDrawTargetForBlend());
+ MOZ_ASSERT(mTargetCtx,
+ "BlendToTarget should be used after CreateBlendTarget.");
+
+ RefPtr<SourceSurface> targetSurf = mTargetCtx->GetDrawTarget()->Snapshot();
- return target.forget();
-}
+ gfxContextAutoSaveRestore save(mSourceCtx);
+ mSourceCtx->SetMatrix(gfxMatrix()); // This will be restored right after.
+ RefPtr<gfxPattern> pattern =
+ new gfxPattern(targetSurf,
+ Matrix::Translation(mTargetOffset.x, mTargetOffset.y));
+ mSourceCtx->SetPattern(pattern);
+ mSourceCtx->Paint();
+ }
+
+private:
+ MixModeBlender() = delete;
+
+ IntRect ComputeClipExtsInDeviceSpace(const gfxMatrix& aTransform)
+ {
+ // These are used if we require a temporary surface for a custom blend
+ // mode. Clip the source context first, so that we can generate a smaller
+ // temporary surface. (Since we will clip this context in
+ // SetupContextMatrix, a pair of save/restore is needed.)
+ gfxContextAutoSaveRestore saver(mSourceCtx);
-static void
-BlendToTarget(nsIFrame* aFrame, gfxContext* aSource, gfxContext* aTarget,
- const IntPoint& aTargetOffset)
-{
- MOZ_ASSERT(aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL);
+ if (!(mFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
+ // aFrame has a valid visual overflow rect, so clip to it before calling
+ // PushGroup() to minimize the size of the surfaces we'll composite:
+ gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(mSourceCtx);
+ mSourceCtx->Multiply(aTransform);
+ nsRect overflowRect = mFrame->GetVisualOverflowRectRelativeToSelf();
+ if (mFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
+ mFrame->IsSVGText()) {
+ // Unlike containers, leaf frames do not include GetPosition() in
+ // GetCanvasTM().
+ overflowRect = overflowRect + mFrame->GetPosition();
+ }
+ mSourceCtx->Clip(NSRectToSnappedRect(overflowRect,
+ mFrame->PresContext()->AppUnitsPerDevPixel(),
+ *mSourceCtx->GetDrawTarget()));
+ }
- RefPtr<DrawTarget> targetDT = aTarget->GetDrawTarget();
- RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();
+ // Get the clip extents in device space.
+ mSourceCtx->SetMatrix(gfxMatrix());
+ gfxRect clippedFrameSurfaceRect = mSourceCtx->GetClipExtents();
+ clippedFrameSurfaceRect.RoundOut();
+
+ IntRect result;
+ ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
- gfxContextAutoSaveRestore save(aSource);
- aSource->SetMatrix(gfxMatrix()); // This will be restored right after.
- RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(aTargetOffset.x, aTargetOffset.y));
- aSource->SetPattern(pattern);
- aSource->Paint();
-}
+ return Factory::CheckSurfaceSize(result.Size()) ? result : IntRect();
+ }
+
+ nsIFrame* mFrame;
+ gfxContext* mSourceCtx;
+ RefPtr<gfxContext> mTargetCtx;
+ IntPoint mTargetOffset;
+};
DrawResult
nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
gfxContext& aContext,
const gfxMatrix& aTransform,
const nsIntRect *aDirtyRect)
{
NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
@@ -704,56 +754,31 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
nsSVGMaskFrame *maskFrame = effectProperties.GetFirstMaskFrame(&isOK);
if (!isOK) {
// Some resource is invalid. We shouldn't paint anything.
return DrawResult::SUCCESS;
}
- // These are used if we require a temporary surface for a custom blend mode.
- // Clip the source context first, so that we can generate a smaller temporary
- // surface. (Since we will clip this context in SetupContextMatrix, a pair
- // of save/restore is needed.)
- aContext.Save();
- if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
- // aFrame has a valid visual overflow rect, so clip to it before calling
- // PushGroup() to minimize the size of the surfaces we'll composite:
- gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&aContext);
- aContext.Multiply(aTransform);
- nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
- if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
- aFrame->IsSVGText()) {
- // Unlike containers, leaf frames do not include GetPosition() in
- // GetCanvasTM().
- overflowRect = overflowRect + aFrame->GetPosition();
- }
- aContext.Clip(NSRectToSnappedRect(overflowRect,
- aFrame->PresContext()->AppUnitsPerDevPixel(),
- *aContext.GetDrawTarget()));
- }
- IntPoint targetOffset;
- RefPtr<gfxContext> target =
- (aFrame->StyleEffects()->mMixBlendMode == NS_STYLE_BLEND_NORMAL)
- ? RefPtr<gfxContext>(&aContext).forget()
- : CreateBlendTarget(&aContext, targetOffset);
- aContext.Restore();
+ MixModeBlender blender(aFrame, &aContext);
+ gfxContext* target = blender.ShouldCreateDrawTargetForBlend()
+ ? blender.CreateBlendTarget(aTransform) : &aContext;
if (!target) {
return DrawResult::TEMPORARY_ERROR;
}
DrawResult result = DrawResult::SUCCESS;
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
maskUsage.shouldGenerateClipMaskLayer ||
- maskUsage.shouldGenerateMaskLayer ||
- aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL);
+ maskUsage.shouldGenerateMaskLayer);
if (shouldGenerateMask) {
Matrix maskTransform;
RefPtr<SourceSurface> maskSurface;
if (maskUsage.shouldGenerateMaskLayer) {
uint8_t maskMode =
aFrame->StyleSVGReset()->mMask.mLayers[0].mMaskMode;
@@ -837,19 +862,19 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
aContext.PopClip();
}
if (shouldGenerateMask) {
target->PopGroupAndBlend();
}
- if (aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
+ if (blender.ShouldCreateDrawTargetForBlend()) {
MOZ_ASSERT(target != &aContext);
- BlendToTarget(aFrame, &aContext, target, targetOffset);
+ blender.BlendToTarget();
}
return result;
}
bool
nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint)
{