Bug 1385239 - Part 2. Remove aTransform parameter from PaintFilteredFrame.
There are two callers of nsFilterInstance::PaintFilteredFrame:
1. nsSVGUtils::PaintFrameWithEffects at [1]
This function is used while painting a filtered element on a path which
display item painting is not supported yet, such as drawing elements inside a
indirect painted SVG object, such as a mask, a clipPath or a gradient object.
Let's say we have a masked element, which refers to an SVG mask, and there is
a filtered element inside that SVG mask.
Using nsFilterInstance::PaintFilteredFrame to paint that filtered frame in
the mask, we have to pass a gfxContext and a transform matrix to it. The
transform of the gfxContext 'target' that we pass in consists of a transform
from the referenced frame, of the masked frame, to the masked frame. We also
pass in a transform matrix 'aTransform', this matrix contains a transform
from the the masked frame to the filtered frame in *device units*, which
means it contains css-to-dev-px scaling factor.
2. nsSVGIntegrationUtils::PaintFilter at [2]
This function is used by normal display item painting.
The same, we pass a gfxContext 'context' and a transform matrix 'tm' into
nsFilterInstance::PaintFilteredFrame. The transform matrix of 'context'
consists of a transform from the referenced frame, of the filtered frame,
to this filtered frame, but the scale factor was taken out . The transform
matrix 'tm' we pass in contains scale transform from the referenced frame to
the filtered frame in *device unit*.
Inside nsFilterInstance::PaintFilteredFrame, we treat the transform matrix of
'aCtx' and 'aTransform' as parameters we pass in in #2 caller. So it can be
failed in #1. For example, if the filtered frame inside a masked frame has a
translation transform applied, since that translation was put in 'aTransfrom',
but we only use the scale factor of 'aTransform' in
nsFilterInstance::PaintFilteredFrame, translation factor disappears.
In this patch, I unified the definition of parameters of
nsFilterInstance::PaintFilteredFrame:
1. nsFilterInstance::PaintFilteredFrame(aCtx): the transform matrix of aCtx
should be a transform from the referenced frame to the filtered frame in
*css units*. Originally, the aCtx we passed in #1 is in device units, which
should be fixed; the aCtx we passed in #2 does not even include css scaling
factor, need be fixed too.
2. nsFilterInstance::PaintFilteredFrame(aTransform): this transform matrix
should contain only scaling factor in device units. And I removed it in the
end since I found we can get this value easily right inside the callee.
[1]
https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGUtils.cpp#l857
[2]
https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGIntegrationUtils.cpp#l1114
MozReview-Commit-ID: gRV128NyQv
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -57,29 +57,45 @@ UserSpaceMetricsForFrame(nsIFrame* aFram
return MakeUnique<SVGElementMetrics>(element);
}
return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
}
void
nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
gfxContext* aCtx,
- const gfxMatrix& aTransform,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRegion *aDirtyArea,
imgDrawingParams& aImgParams)
{
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+
+ gfxContextMatrixAutoSaveRestore autoSR(aCtx);
+ gfxSize scaleFactors = aCtx->CurrentMatrix().ScaleFactors(true);
+ gfxMatrix scaleMatrix(scaleFactors.width, 0.0f,
+ 0.0f, scaleFactors.height,
+ 0.0f, 0.0f);
+
+ gfxMatrix reverseScaleMatrix = scaleMatrix;
+ DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
+ MOZ_ASSERT(invertible);
+ // Pull scale vector out of aCtx's transform, put all scale factors, which
+ // includes css and css-to-dev-px scale, into scaleMatrixInDevUnits.
+ aCtx->SetMatrix(reverseScaleMatrix * aCtx->CurrentMatrix());
+
+ gfxMatrix scaleMatrixInDevUnits =
+ scaleMatrix * nsSVGUtils::GetCSSPxToDevPxMatrix(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);
+ aPaintCallback, scaleMatrixInDevUnits,
+ aDirtyArea, nullptr, nullptr, nullptr);
if (instance.IsInitialized()) {
instance.Render(aCtx, aImgParams);
}
}
nsRegion
nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsRegion& aPreFilterDirtyRegion)
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -80,17 +80,16 @@ public:
/**
* 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 void PaintFilteredFrame(nsIFrame *aFilteredFrame,
gfxContext* aCtx,
- const gfxMatrix& aTransform,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRegion* aDirtyArea,
imgDrawingParams& aImgParams);
/**
* 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
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -1089,30 +1089,19 @@ nsSVGIntegrationUtils::PaintFilter(const
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;
- gfxSize scaleFactors = context.CurrentMatrix().ScaleFactors(true);
- gfxMatrix scaleMatrix(scaleFactors.width, 0.0f,
- 0.0f, scaleFactors.height,
- 0.0f, 0.0f);
- gfxMatrix reverseScaleMatrix = scaleMatrix;
- DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
- MOZ_ASSERT(invertible);
- context.SetMatrix(reverseScaleMatrix * context.CurrentMatrix());
- gfxMatrix tm =
- scaleMatrix * nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
- nsFilterInstance::PaintFilteredFrame(frame, &context,
- tm, &callback, &dirtyRegion,
- aParams.imgParams);
+ nsFilterInstance::PaintFilteredFrame(frame, &context, &callback,
+ &dirtyRegion, aParams.imgParams);
if (opacity != 1.0f) {
context.PopGroupAndBlend();
}
}
class PaintFrameCallback : public gfxDrawingCallback {
public:
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -848,19 +848,29 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
aDirtyRect->width, aDirtyRect->height));
tmpDirtyRegion =
nsLayoutUtils::RoundGfxRectToAppRect(
dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
aFrame->GetPosition();
dirtyRegion = &tmpDirtyRegion;
}
+ gfxContextMatrixAutoSaveRestore autoSR(target);
+
+ // 'target' is currently scaled such that its user space units are CSS
+ // pixels (SVG user space units). But PaintFilteredFrame expects it to be
+ // scaled in such a way that its user space units are device pixels. So we
+ // have to adjust the scale.
+ gfxMatrix reverseScaleMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(aFrame);
+ DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
+ target->SetMatrix(reverseScaleMatrix * aTransform *
+ target->CurrentMatrix());
+
SVGPaintCallback paintCallback;
- nsFilterInstance::PaintFilteredFrame(aFrame, target,
- aTransform, &paintCallback,
+ nsFilterInstance::PaintFilteredFrame(aFrame, target, &paintCallback,
dirtyRegion, aImgParams);
} else {
svgFrame->PaintSVG(*target, aTransform, aImgParams, aDirtyRect);
}
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
aContext.PopClip();
}