--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -34,20 +34,20 @@ using namespace mozilla::image;
FilterDescription
nsFilterInstance::GetFilterDescription(nsIContent* aFilteredElement,
const nsTArray<nsStyleFilter>& aFilterChain,
bool aFilterInputIsTainted,
const UserSpaceMetrics& aMetrics,
const gfxRect& aBBox,
nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages)
{
- gfxMatrix unused; // aPaintTransform arg not used since we're not painting
+ gfxMatrix identity;
nsFilterInstance instance(nullptr, aFilteredElement, aMetrics,
aFilterChain, aFilterInputIsTainted, nullptr,
- unused, nullptr, nullptr, nullptr, &aBBox);
+ identity, nullptr, nullptr, nullptr, &aBBox);
if (!instance.IsInitialized()) {
return FilterDescription();
}
return instance.ExtractDescriptionAndAdditionalImages(aOutAdditionalImages);
}
static UniquePtr<UserSpaceMetrics>
UserSpaceMetricsForFrame(nsIFrame* aFrame)
@@ -65,64 +65,65 @@ nsFilterInstance::PaintFilteredFrame(nsI
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);
+ nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
+ *metrics, filterChain, /* InputIsTainted */ true,
+ aPaintCallback, aTransform, aDirtyArea, nullptr,
+ nullptr, nullptr);
if (!instance.IsInitialized()) {
return DrawResult::BAD_IMAGE;
}
return instance.Render(aDrawTarget);
}
nsRegion
nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsRegion& aPreFilterDirtyRegion)
{
if (aPreFilterDirtyRegion.IsEmpty()) {
return nsRegion();
}
- gfxMatrix unused; // aPaintTransform arg not used since we're not painting
+ gfxMatrix tm = nsSVGUtils::GetCanvasTM(aFilteredFrame);
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, nullptr, unused,
- nullptr, &aPreFilterDirtyRegion);
+ nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
+ *metrics, filterChain, /* InputIsTainted */ true,
+ nullptr, tm, nullptr, &aPreFilterDirtyRegion);
if (!instance.IsInitialized()) {
return nsRegion();
}
// We've passed in the source's dirty area so the instance knows about it.
// Now we can ask the instance to compute the area of the filter output
// that's dirty.
return instance.ComputePostFilterDirtyRegion();
}
nsRegion
nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
const nsRegion& aPostFilterDirtyRegion)
{
- gfxMatrix unused; // aPaintTransform arg not used since we're not painting
+ gfxMatrix tm = nsSVGUtils::GetCanvasTM(aFilteredFrame);
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, nullptr, unused,
- &aPostFilterDirtyRegion);
+ nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
+ *metrics, filterChain, /* InputIsTainted */ true,
+ nullptr, tm, &aPostFilterDirtyRegion);
if (!instance.IsInitialized()) {
return nsRect();
}
// Now we can ask the instance to compute the area of the source
// that's needed.
return instance.ComputeSourceNeededRect();
}
@@ -138,25 +139,25 @@ nsFilterInstance::GetPostFilterBounds(ns
nsRegion preFilterRegion;
nsRegion* preFilterRegionPtr = nullptr;
if (aPreFilterBounds) {
preFilterRegion = *aPreFilterBounds;
preFilterRegionPtr = &preFilterRegion;
}
- gfxMatrix unused; // aPaintTransform arg not used since we're not painting
+ gfxMatrix tm = nsSVGUtils::GetCanvasTM(aFilteredFrame);
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, nullptr, unused,
- nullptr, preFilterRegionPtr, aPreFilterBounds,
- aOverrideBBox);
+ nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
+ *metrics, filterChain, /* InputIsTainted */ true,
+ nullptr, tm, nullptr, preFilterRegionPtr,
+ aPreFilterBounds, aOverrideBBox);
if (!instance.IsInitialized()) {
return nsRect();
}
return instance.ComputePostFilterExtents();
}
nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
@@ -198,21 +199,16 @@ nsFilterInstance::nsFilterInstance(nsIFr
}
// Get various transforms:
gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.width, 0.0f,
0.0f, mFilterSpaceToUserSpaceScale.height,
0.0f, 0.0f);
- // Only used (so only set) when we paint:
- if (mPaintCallback) {
- mFilterSpaceToDeviceSpaceTransform = filterToUserSpace * mPaintTransform;
- }
-
mFilterSpaceToFrameSpaceInCSSPxTransform =
filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform();
// mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible
mFrameSpaceInCSSPxToFilterSpaceTransform =
mFilterSpaceToFrameSpaceInCSSPxTransform;
mFrameSpaceInCSSPxToFilterSpaceTransform.Invert();
nsIntRect targetBounds;
@@ -238,32 +234,29 @@ nsFilterInstance::nsFilterInstance(nsIFr
mInitialized = true;
}
nsresult
nsFilterInstance::ComputeUserSpaceToFilterSpaceScale()
{
gfxMatrix canvasTransform;
if (mTargetFrame) {
- canvasTransform = nsSVGUtils::GetCanvasTM(mTargetFrame);
- if (canvasTransform.IsSingular()) {
+ mUserSpaceToFilterSpaceScale = mPaintTransform.ScaleFactors(true);
+ if (mUserSpaceToFilterSpaceScale.width <= 0.0f ||
+ mUserSpaceToFilterSpaceScale.height <= 0.0f) {
// Nothing should be rendered.
return NS_ERROR_FAILURE;
}
+ } else {
+ mUserSpaceToFilterSpaceScale = gfxSize(1.0, 1.0);
}
- mUserSpaceToFilterSpaceScale = canvasTransform.ScaleFactors(true);
- if (mUserSpaceToFilterSpaceScale.width <= 0.0f ||
- mUserSpaceToFilterSpaceScale.height <= 0.0f) {
- // Nothing should be rendered.
- return NS_ERROR_FAILURE;
- }
-
- mFilterSpaceToUserSpaceScale = gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width,
- 1.0f / mUserSpaceToFilterSpaceScale.height);
+ mFilterSpaceToUserSpaceScale =
+ gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width,
+ 1.0f / mUserSpaceToFilterSpaceScale.height);
return NS_OK;
}
gfxRect
nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
{
gfxRect filterSpaceRect = aUserSpaceRect;
filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
@@ -369,40 +362,32 @@ nsFilterInstance::BuildSourcePaint(Sourc
RefPtr<DrawTarget> offscreenDT =
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
neededRect.Size(), SurfaceFormat::B8G8R8A8);
if (!offscreenDT || !offscreenDT->IsValid()) {
return DrawResult::TEMPORARY_ERROR;
}
- gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
- 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
+ gfxContextAutoSaveRestore saver(ctx);
- if (!mPaintTransform.IsSingular()) {
- RefPtr<gfxContext> sourceCtx = gfxContext::CreateOrNull(offscreenDT);
- MOZ_ASSERT(sourceCtx); // already checked the draw target above
- sourceCtx->Multiply(mPaintTransform *
- deviceToFilterSpace *
- gfxMatrix::Translation(-neededRect.TopLeft()));
- GeneralPattern pattern;
- if (aSource == &mFillPaint) {
- nsSVGUtils::MakeFillPatternFor(mTargetFrame, sourceCtx, &pattern);
- } else if (aSource == &mStrokePaint) {
- nsSVGUtils::MakeStrokePatternFor(mTargetFrame, sourceCtx, &pattern);
- }
- if (pattern.GetPattern()) {
- offscreenDT->FillRect(ToRect(FilterSpaceToUserSpace(ThebesRect(neededRect))),
- pattern);
- }
+ ctx->SetMatrix(mPaintTransform *
+ gfxMatrix::Translation(-neededRect.TopLeft()));
+ GeneralPattern pattern;
+ if (aSource == &mFillPaint) {
+ nsSVGUtils::MakeFillPatternFor(mTargetFrame, ctx, &pattern);
+ } else if (aSource == &mStrokePaint) {
+ nsSVGUtils::MakeStrokePatternFor(mTargetFrame, ctx, &pattern);
+ }
+
+ if (pattern.GetPattern()) {
+ offscreenDT->FillRect(ToRect(FilterSpaceToUserSpace(ThebesRect(neededRect))),
+ pattern);
}
aSource->mSourceSurface = offscreenDT->Snapshot();
aSource->mSurfaceRect = neededRect;
return DrawResult::SUCCESS;
}
@@ -456,29 +441,23 @@ nsFilterInstance::BuildSourceImage()
//
// (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();
- 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));
+ gfxMatrix devPxToCssPxTM = nsSVGUtils::GetCSSPxToDevPxMatrix(mTargetFrame);
+ DebugOnly<bool> invertible = devPxToCssPxTM.Invert();
+ MOZ_ASSERT(invertible);
+ ctx->SetMatrix(devPxToCssPxTM * mPaintTransform *
+ gfxMatrix::Translation(-neededRect.TopLeft()));
DrawResult result =
mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty);
mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
mSourceGraphic.mSurfaceRect = neededRect;
return result;
@@ -491,28 +470,32 @@ nsFilterInstance::Render(DrawTarget* aDr
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()) {
+ if (filterRect.IsEmpty() || mPaintTransform.IsSingular()) {
return DrawResult::SUCCESS;
}
AutoRestoreTransform autoRestoreTransform(aDrawTarget);
- Matrix newTM = ToMatrix(ctm).PreTranslate(filterRect.x, filterRect.y) *
- aDrawTarget->GetTransform();
- aDrawTarget->SetTransform(newTM);
+ gfxMatrix filterSpaceToUserSpace = mPaintTransform;
+ DebugOnly<bool> invertible = filterSpaceToUserSpace.Invert();
+ MOZ_ASSERT(invertible);
+ filterSpaceToUserSpace *= nsSVGUtils::GetCSSPxToDevPxMatrix(mTargetFrame);
+
+ aDrawTarget->SetTransform(ToMatrix(filterSpaceToUserSpace) *
+ aDrawTarget->GetTransform() *
+ Matrix::Translation(filterRect.TopLeft()));
ComputeNeededBoxes();
+
DrawResult result = BuildSourceImage();
if (result != DrawResult::SUCCESS){
return result;
}
result = BuildSourcePaints();
if (result != DrawResult::SUCCESS){
return result;
}