--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7161,24 +7161,17 @@ ComputeMaskGeometry(PaintFramesParams& a
nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
if (maskFrames.Length() == 0) {
return;
}
gfxContext& ctx = aParams.ctx;
nsIFrame* frame = aParams.frame;
-
nsPoint offsetToUserSpace = ComputeOffsetToUserSpace(aParams);
- gfxPoint devPixelOffsetToUserSpace =
- nsLayoutUtils::PointToGfxPoint(offsetToUserSpace,
- frame->PresContext()->AppUnitsPerDevPixel());
-
- gfxContextMatrixAutoSaveRestore matSR(&ctx);
- ctx.SetMatrix(ctx.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
// Convert boaderArea and dirtyRect to user space.
int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
// Union all mask layer rectangles in user space.
gfxRect maskInUserSpace;
@@ -7199,26 +7192,26 @@ ComputeMaskGeometry(PaintFramesParams& a
appUnitsPerDevPixel,
&clipState);
currentMaskSurfaceRect = clipState.mDirtyRectGfx;
}
maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
}
- ctx.Save();
-
+ gfxContextAutoSaveRestore autoSR(&ctx);
+ gfxPoint devPixelOffsetToUserSpace =
+ nsLayoutUtils::PointToGfxPoint(offsetToUserSpace,
+ frame->PresContext()->AppUnitsPerDevPixel());
+ ctx.SetMatrix(ctx.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
if (!maskInUserSpace.IsEmpty()) {
ctx.Clip(maskInUserSpace);
}
- IntRect result = ComputeClipExtsInDeviceSpace(ctx);
- ctx.Restore();
-
- aParams.maskRect = result;
+ aParams.maskRect = ComputeClipExtsInDeviceSpace(ctx);
}
nsDisplayMask::nsDisplayMask(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayList* aList,
bool aHandleOpacity,
const DisplayItemScrollClip* aScrollClip)
: nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity, aScrollClip)
{
@@ -7618,16 +7611,17 @@ nsDisplayFilter::PaintAsLayer(nsDisplayL
nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
nsSVGIntegrationUtils::PaintFramesParams params(*aCtx->ThebesContext(),
mFrame, mVisibleRect,
borderArea, aBuilder,
aManager,
mHandleOpacity);
image::DrawResult result = nsSVGIntegrationUtils::PaintFilter(params);
+
nsDisplayFilterGeometry::UpdateDrawResult(this, result);
}
#ifdef MOZ_DUMP_PAINTING
void
nsDisplayFilter::PrintEffects(nsACString& aTo)
{
nsIFrame* firstFrame =
--- a/layout/reftests/svg/filter-scaled-02.html
+++ b/layout/reftests/svg/filter-scaled-02.html
@@ -17,9 +17,9 @@
</filter>
<rect fill="green" x="120" y="100" width="100" height="120"
filter="url(#filter)"/>
<rect fill="none" stroke="black" stroke-width="4"
x="120" y="100" width="100" height="120"/>
</svg>
</div>
</body>
-</html>
+</html>
\ No newline at end of file
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -53,35 +53,67 @@ static UniquePtr<UserSpaceMetrics>
UserSpaceMetricsForFrame(nsIFrame* aFrame)
{
if (aFrame->GetContent()->IsSVGElement()) {
nsSVGElement* element = static_cast<nsSVGElement*>(aFrame->GetContent());
return MakeUnique<SVGElementMetrics>(element);
}
return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
}
+/*
+// Move to nsXXXXUtils!!
+static IntRect
+ComputeClipExtsInDeviceSpace(gfxContext& aCtx)
+{
+ gfxContextMatrixAutoSaveRestore matRestore(&aCtx);
+ // Get the clip extents in device space.
+ aCtx.SetMatrix(gfxMatrix());
+ gfxRect clippedFrameSurfaceRect = aCtx.GetClipExtents();
+ clippedFrameSurfaceRect.RoundOut();
+
+ IntRect result;
+ ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
+ return mozilla::gfx::Factory::CheckSurfaceSize(result.Size()) ? result
+ : IntRect();
+}*/
nsresult
nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
- DrawTarget* aDrawTarget,
- const gfxMatrix& aTransform,
+ gfxContext* aContext,
+ const gfxMatrix& aPaintSourceTransform,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRegion *aDirtyArea)
{
auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+
+ //IntRect intBounds = ComputeClipExtsInDeviceSpace(*aContext);
+ //int32_t A2D = aFilteredFrame->PresContext()->AppUnitsPerDevPixel();
+ //nsRect bounds = ToAppUnits(intBounds, A2D);
+
+ // What I changed here
+ // 1. aDirtyArea: I do not pass this parameter from nsSVGIntegrationUtils,
+ // so that this mPostFilterDirtyRegion and mPreFilterDirtyRegion will
+ // be computed from bounds.
+ // 2. bounds is in device space.
+ // 3. aTransform passed from nsSVGIntegrationUtils is aContext's
+ // CurrentMatrix
+
// 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, aPaintSourceTransform,
+ aDirtyArea, nullptr, nullptr,
+ nullptr);
if (!instance.IsInitialized()) {
return NS_OK;
}
- return instance.Render(aDrawTarget);
+ return instance.Render(aContext);
}
nsRegion
nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsRegion& aPreFilterDirtyRegion)
{
if (aPreFilterDirtyRegion.IsEmpty()) {
return nsRegion();
@@ -153,32 +185,46 @@ nsFilterInstance::GetPostFilterBounds(ns
aOverrideBBox);
if (!instance.IsInitialized()) {
return nsRect();
}
return instance.ComputePostFilterExtents();
}
+/* static */
+gfxMatrix
+GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
+{
+ int32_t appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
+ float devPxPerCSSPx =
+ 1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
+
+ return gfxMatrix(devPxPerCSSPx, 0.0,
+ 0.0, devPxPerCSSPx,
+ 0.0, 0.0);
+}
+
+
nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
nsIContent* aTargetContent,
const UserSpaceMetrics& aMetrics,
const nsTArray<nsStyleFilter>& aFilterChain,
bool aFilterInputIsTainted,
nsSVGFilterPaintCallback *aPaintCallback,
- const gfxMatrix& aPaintTransform,
+ const gfxMatrix& aPaintSourceTransform,
const nsRegion *aPostFilterDirtyRegion,
const nsRegion *aPreFilterDirtyRegion,
const nsRect *aPreFilterVisualOverflowRectOverride,
const gfxRect *aOverrideBBox)
: mTargetFrame(aTargetFrame)
, mTargetContent(aTargetContent)
, mMetrics(aMetrics)
, mPaintCallback(aPaintCallback)
- , mPaintTransform(aPaintTransform)
+ , mPaintTransform(aPaintSourceTransform)
, mInitialized(false)
{
if (aOverrideBBox) {
mTargetBBox = *aOverrideBBox;
} else {
MOZ_ASSERT(mTargetFrame, "Need to supply a frame when there's no aOverrideBBox");
mTargetBBox = nsSVGUtils::GetBBox(mTargetFrame);
}
@@ -199,17 +245,17 @@ 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;
+ mFilterSpaceToDeviceSpaceTransform = GetCSSPxToDevPxMatrix(mTargetFrame);
}
mFilterSpaceToFrameSpaceInCSSPxTransform =
filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform();
// mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible
mFrameSpaceInCSSPxToFilterSpaceTransform =
mFilterSpaceToFrameSpaceInCSSPxTransform;
mFrameSpaceInCSSPxToFilterSpaceTransform.Invert();
@@ -240,34 +286,30 @@ nsFilterInstance::nsFilterInstance(nsIFr
mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion);
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,
@@ -361,79 +403,81 @@ nsFilterInstance::ComputeNeededBoxes()
sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, mTargetBounds);
mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds();
mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds();
mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds();
}
nsresult
-nsFilterInstance::BuildSourcePaint(SourceInfo *aSource,
- DrawTarget* aTargetDT)
+nsFilterInstance::BuildSourcePaint(SourceInfo *aSource)
{
MOZ_ASSERT(mTargetFrame);
nsIntRect neededRect = aSource->mNeededBounds;
RefPtr<DrawTarget> offscreenDT =
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
neededRect.Size(), SurfaceFormat::B8G8R8A8);
if (!offscreenDT || !offscreenDT->IsValid()) {
return NS_ERROR_OUT_OF_MEMORY;
}
gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
if (!deviceToFilterSpace.Invert()) {
return NS_ERROR_FAILURE;
}
- if (!mPaintTransform.IsSingular()) {
- RefPtr<gfxContext> gfx = gfxContext::CreateOrNull(offscreenDT);
- MOZ_ASSERT(gfx); // already checked the draw target above
- gfx->Save();
- gfx->Multiply(mPaintTransform *
- deviceToFilterSpace *
- gfxMatrix::Translation(-neededRect.TopLeft()));
+ if (!deviceToFilterSpace.IsSingular()) {
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(offscreenDT);
+ MOZ_ASSERT(ctx); // already checked the draw target above
+ //gfx->Multiply(mPaintTransform *
+ // deviceToFilterSpace *
+ // gfxMatrix::Translation(-neededRect.TopLeft()));
+ gfxContextAutoSaveRestore saver(ctx);
+
+ // deviceToFilterSpace = filterToUserSpace * mPaintTransform
+ ctx->SetMatrix(mPaintTransform *
+ gfxMatrix::Translation(-neededRect.TopLeft()));
GeneralPattern pattern;
if (aSource == &mFillPaint) {
- nsSVGUtils::MakeFillPatternFor(mTargetFrame, gfx, &pattern);
+ nsSVGUtils::MakeFillPatternFor(mTargetFrame, ctx, &pattern);
} else if (aSource == &mStrokePaint) {
- nsSVGUtils::MakeStrokePatternFor(mTargetFrame, gfx, &pattern);
+ nsSVGUtils::MakeStrokePatternFor(mTargetFrame, ctx, &pattern);
}
if (pattern.GetPattern()) {
offscreenDT->FillRect(ToRect(FilterSpaceToUserSpace(ThebesRect(neededRect))),
pattern);
}
- gfx->Restore();
}
aSource->mSourceSurface = offscreenDT->Snapshot();
aSource->mSurfaceRect = neededRect;
return NS_OK;
}
nsresult
-nsFilterInstance::BuildSourcePaints(DrawTarget* aTargetDT)
+nsFilterInstance::BuildSourcePaints()
{
nsresult rv = NS_OK;
if (!mFillPaint.mNeededBounds.IsEmpty()) {
- rv = BuildSourcePaint(&mFillPaint, aTargetDT);
+ rv = BuildSourcePaint(&mFillPaint);
NS_ENSURE_SUCCESS(rv, rv);
}
if (!mStrokePaint.mNeededBounds.IsEmpty()) {
- rv = BuildSourcePaint(&mStrokePaint, aTargetDT);
+ rv = BuildSourcePaint(&mStrokePaint);
NS_ENSURE_SUCCESS(rv, rv);
}
return rv;
}
nsresult
-nsFilterInstance::BuildSourceImage(DrawTarget* aTargetDT)
+nsFilterInstance::BuildSourceImage()
{
MOZ_ASSERT(mTargetFrame);
nsIntRect neededRect = mSourceGraphic.mNeededBounds;
if (neededRect.IsEmpty()) {
return NS_OK;
}
@@ -456,64 +500,71 @@ nsFilterInstance::BuildSourceImage(DrawT
//
// (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();
- if (!deviceToFilterSpace.Invert()) {
- return NS_ERROR_FAILURE;
- }
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 deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
+ deviceToFilterSpace.Invert();
+ // ctx->SetMatrix(
+ // ctx->CurrentMatrix().Translate(-neededRect.TopLeft()).
+ // PreMultiply(deviceToFilterSpace));*/
+ gfxMatrix toBBoxTM =
+ deviceToFilterSpace * mPaintTransform * gfxMatrix::Translation(-neededRect.TopLeft());
+ ctx->SetMatrix(toBBoxTM);
DrawResult result =
mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty);
mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
mSourceGraphic.mSurfaceRect = neededRect;
return (result == DrawResult::SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
-nsFilterInstance::Render(DrawTarget* aDrawTarget)
+nsFilterInstance::Render(gfxContext* aContext)
{
MOZ_ASSERT(mTargetFrame, "Need a frame for rendering");
nsIntRect filterRect =
mPostFilterDirtyRegion.GetBounds().Intersect(OutputFilterSpaceBounds());
gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform();
-
if (filterRect.IsEmpty() || ctm.IsSingular()) {
return NS_OK;
}
- AutoRestoreTransform autoRestoreTransform(aDrawTarget);
- Matrix newTM = ToMatrix(ctm).PreTranslate(filterRect.x, filterRect.y) *
- aDrawTarget->GetTransform();
- aDrawTarget->SetTransform(newTM);
-
ComputeNeededBoxes();
- nsresult rv = BuildSourceImage(aDrawTarget);
+ nsresult rv = BuildSourceImage();
+ if (NS_FAILED(rv))
+ return rv;
+ rv = BuildSourcePaints();
if (NS_FAILED(rv))
return rv;
- rv = BuildSourcePaints(aDrawTarget);
- if (NS_FAILED(rv))
- return rv;
+ gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
+
+ gfxContextMatrixAutoSaveRestore saver(aContext);
+ gfxMatrix filterTM = mPaintTransform;
+ filterTM.Invert();
+
+ gfxMatrix toBoundingBox =
+ gfxMatrix::Translation(filterRect.x, filterRect.y) *
+ deviceToFilterSpace *
+ filterTM *
+ aContext->CurrentMatrix();
+ aContext->SetMatrix(toBoundingBox);
FilterSupport::RenderFilterDescription(
- aDrawTarget, mFilterDescription, IntRectToRect(filterRect),
+ aContext->GetDrawTarget(), mFilterDescription, IntRectToRect(filterRect),
mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect,
mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect,
mInputImages, Point(0, 0));
return NS_OK;
}
@@ -564,18 +615,27 @@ nsFilterInstance::OutputFilterSpaceBound
nsIntRect
nsFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const
{
nsIntRect rect = OutputFilterSpaceBounds();
if (aRect) {
if (aRect->IsEmpty()) {
return nsIntRect();
}
+
+ int32_t A2D = mTargetFrame->PresContext()->AppUnitsPerDevPixel();
+ gfxRect devBounds =
+ mFilterSpaceToDeviceSpaceTransform.Transform(
+ nsLayoutUtils::RectToGfxRect(*aRect, A2D));
+
+ nsRect devRect = nsRect(devBounds.x * A2D, devBounds.y * A2D,
+ devBounds.width * A2D, devBounds.height * A2D);
+
gfxRect rectInCSSPx =
- nsLayoutUtils::RectToGfxRect(*aRect, nsPresContext::AppUnitsPerCSSPixel());
+ nsLayoutUtils::RectToGfxRect(devRect, nsPresContext::AppUnitsPerCSSPixel());
gfxRect rectInFilterSpace =
mFrameSpaceInCSSPxToFilterSpaceTransform.TransformBounds(rectInCSSPx);
rectInFilterSpace.RoundOut();
nsIntRect intRect;
if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) {
rect = intRect;
}
}
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -79,18 +79,18 @@ 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 nsresult PaintFilteredFrame(nsIFrame *aFilteredFrame,
- DrawTarget* aDrawTarget,
- const gfxMatrix& aTransform,
+ gfxContext* aContext,
+ const gfxMatrix& aPaintSourceTransform,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRegion* aDirtyArea);
/**
* 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.
@@ -150,28 +150,16 @@ public:
bool aFilterInputIsTainted,
nsSVGFilterPaintCallback *aPaintCallback,
const gfxMatrix& aPaintTransform,
const nsRegion *aPostFilterDirtyRegion = nullptr,
const nsRegion *aPreFilterDirtyRegion = nullptr,
const nsRect *aOverridePreFilterVisualOverflowRect = nullptr,
const gfxRect *aOverrideBBox = nullptr);
- /**
- * Returns true if the filter instance was created successfully.
- */
- 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.
- */
- nsresult Render(DrawTarget* aDrawTarget);
const FilterDescription& ExtractDescriptionAndAdditionalImages(nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages)
{
mInputImages.SwapElements(aOutAdditionalImages);
return mFilterDescription;
}
/**
@@ -205,16 +193,29 @@ public:
/**
* Returns the transform from filter space to outer-<svg> device space.
*/
gfxMatrix GetFilterSpaceToDeviceSpaceTransform() const {
return mFilterSpaceToDeviceSpaceTransform;
}
private:
+ /**
+ * Returns true if the filter instance was created successfully.
+ */
+ 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.
+ */
+ nsresult Render(gfxContext* aContext);
+
struct SourceInfo {
// Specifies which parts of the source need to be rendered.
// Set by ComputeNeededBoxes().
nsIntRect mNeededBounds;
// The surface that contains the input rendering.
// Set by BuildSourceImage / BuildSourcePaint.
RefPtr<SourceSurface> mSourceSurface;
@@ -223,31 +224,30 @@ private:
// Set by BuildSourceImage / BuildSourcePaint.
IntRect mSurfaceRect;
};
/**
* Creates a SourceSurface for either the FillPaint or StrokePaint graph
* nodes
*/
- nsresult BuildSourcePaint(SourceInfo *aPrimitive,
- DrawTarget* aTargetDT);
+ nsresult BuildSourcePaint(SourceInfo *aPrimitive);
/**
* Creates a SourceSurface for either the FillPaint and StrokePaint graph
* nodes, fills its contents and assigns it to mFillPaint.mSourceSurface and
* mStrokePaint.mSourceSurface respectively.
*/
- nsresult BuildSourcePaints(DrawTarget* aTargetDT);
+ nsresult BuildSourcePaints();
/**
* Creates the SourceSurface for the SourceGraphic graph node, paints its
* contents, and assigns it to mSourceGraphic.mSourceSurface.
*/
- nsresult BuildSourceImage(DrawTarget* aTargetDT);
+ nsresult BuildSourceImage();
/**
* Build the list of FilterPrimitiveDescriptions that describes the filter's
* filter primitives and their connections. This populates
* mPrimitiveDescriptions and mInputImages. aFilterInputIsTainted describes
* whether the SourceGraphic is tainted.
*/
nsresult BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
@@ -374,17 +374,18 @@ private:
SourceInfo mSourceGraphic;
SourceInfo mFillPaint;
SourceInfo mStrokePaint;
/**
* The transform to the SVG user space of mTargetFrame.
*/
- gfxMatrix mPaintTransform;
+ const gfxMatrix mPaintTransform;
+ const gfxMatrix mFilterTransform;
nsTArray<RefPtr<SourceSurface>> mInputImages;
nsTArray<FilterPrimitiveDescription> mPrimitiveDescriptions;
FilterDescription mFilterDescription;
bool mInitialized;
};
#endif
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -398,42 +398,42 @@ nsSVGIntegrationUtils::HitTestFrameForEf
return nsSVGUtils::HitTestClip(firstFrame, userSpacePt);
}
class RegularFramePaintCallback : public nsSVGFilterPaintCallback
{
public:
RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
- const nsPoint& aOffset)
+ const gfxPoint& aUserSpaceToFrameSpace)
: mBuilder(aBuilder), mLayerManager(aManager),
- mOffset(aOffset) {}
+ mUserSpaceToFrameSpace(aUserSpaceToFrameSpace)
+ {}
virtual DrawResult Paint(gfxContext& aContext, nsIFrame *aTarget,
const gfxMatrix& aTransform,
const nsIntRect* aDirtyRect) override
{
BasicLayerManager* basic = mLayerManager->AsBasicLayerManager();
+ RefPtr<gfxContext> oldCtx = basic->GetTarget();
basic->SetTarget(&aContext);
- gfxPoint devPixelOffset =
- nsLayoutUtils::PointToGfxPoint(-mOffset,
- aTarget->PresContext()->AppUnitsPerDevPixel());
+ gfxContextMatrixAutoSaveRestore autoSR(&aContext);
+ aContext.SetMatrix(aContext.CurrentMatrix().Translate(-mUserSpaceToFrameSpace));
- gfxContextMatrixAutoSaveRestore autoSR(&aContext);
- aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffset));
-
- mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder);
+ mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
+ mBuilder);
+ basic->SetTarget(oldCtx);
return DrawResult::SUCCESS;
}
private:
nsDisplayListBuilder* mBuilder;
LayerManager* mLayerManager;
- nsPoint mOffset;
+ gfxPoint mUserSpaceToFrameSpace;
};
typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams;
/**
* Paint css-positioned-mask onto a given target(aMaskDT).
*/
static DrawResult
@@ -648,27 +648,88 @@ ValidateSVGFrame(nsIFrame* aFrame)
// The SVG spec says not to draw _anything_
return false;
}
}
return true;
}
+struct OffsetValues {
+ nsPoint offsetToBoundingBox;
+ gfxPoint offsetToBoundingBoxInDevPx;
+ nsPoint offsetToUserSpace;
+ gfxPoint offsetToUserSpaceInDevPx;
+};
+
+OffsetValues
+ComputeEffectOffset(nsIFrame* aFrame, const PaintFramesParams& aParams)
+{
+ OffsetValues result;
+ result.offsetToBoundingBox =
+ aParams.builder->ToReferenceFrame(aFrame) -
+ nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
+ if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
+ /* Snap the offset if the reference frame is not a SVG frame,
+ * since other frames will be snapped to pixel when rendering. */
+ result.offsetToBoundingBox =
+ nsPoint(
+ aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.x),
+ aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.y));
+ }
+
+ // After applying only "aOffsetToBoundingBox", aParams.ctx would have its
+ // origin at the top left corner of frame's bounding box (over all
+ // continuations).
+ // However, SVG painting needs the origin to be located at the origin of the
+ // SVG frame's "user space", i.e. the space in which, for example, the
+ // frame's BBox lives.
+ // SVG geometry frames and foreignObject frames apply their own offsets, so
+ // their position is relative to their user space. So for these frame types,
+ // if we want aParams.ctx to be in user space, we first need to subtract the
+ // frame's position so that SVG painting can later add it again and the
+ // frame is painted in the right place.
+
+ gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
+ nsPoint toUserSpace =
+ nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
+ nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
+
+ result.offsetToUserSpace = result.offsetToBoundingBox - toUserSpace;
+
+#ifdef DEBUG
+ bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
+ NS_ASSERTION(hasSVGLayout ||
+ result.offsetToBoundingBox == result.offsetToUserSpace,
+ "For non-SVG frames there shouldn't be any additional offset");
+#endif
+
+ result.offsetToUserSpaceInDevPx =
+ nsLayoutUtils::PointToGfxPoint(result.offsetToUserSpace,
+ aFrame->PresContext()->AppUnitsPerDevPixel());
+ result.offsetToBoundingBoxInDevPx =
+ nsLayoutUtils::PointToGfxPoint(result.offsetToBoundingBox,
+ aFrame->PresContext()->AppUnitsPerDevPixel());
+
+ return result;
+}
+
/**
* Setup transform matrix of a gfx context by a specific frame. Depend on
* aClipCtx, this function may clip that context by the visual overflow area
* of aFrame.
*
* @param aFrame is the target frame.
* @param aOffsetToBoundingBox returns the offset between the reference frame
* and the bounding box of aFrame.
* @oaram aOffsetToUserSpace returns the offset between the reference frame and
* the user space coordinate of aFrame.
*/
+// TODO: rename FrameSapceToUserSpace, ,move context.SetMatrix( out?
+
static void
SetupContextMatrix(nsIFrame* aFrame, const PaintFramesParams& aParams,
nsPoint& aOffsetToBoundingBox, nsPoint& aOffsetToUserSpace)
{
aOffsetToBoundingBox = aParams.builder->ToReferenceFrame(aFrame) -
nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
/* Snap the offset if the reference frame is not a SVG frame,
@@ -1093,35 +1154,44 @@ nsSVGIntegrationUtils::PaintFilter(const
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
if (effectProperties.HasInvalidFilter()) {
return DrawResult::NOT_READY;
}
gfxContext& context = aParams.ctx;
+ OffsetValues offsets = ComputeEffectOffset(firstFrame, aParams);
+ gfxPoint filterPosInDevPx =
+ context.CurrentMatrix().Transform(offsets.offsetToUserSpaceInDevPx);
+
nsPoint offsetToBoundingBox;
nsPoint offsetToUserSpace;
-
gfxContextAutoSaveRestore autoSR(&context);
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace);
if (opacity != 1.0f) {
context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
nullptr, Matrix());
}
/* Paint the child and apply filters */
+ gfxMatrix cssTpDevPxMatrix =
+ nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
+ gfxMatrix filterMatrix =
+ context.CurrentMatrix() * gfxMatrix::Translation(-filterPosInDevPx) * cssTpDevPxMatrix;
+
+ //gfxPoint xx =
+ // cssTpDevPxMatrix.Transform(offsets.offsetToUserSpaceInDevPx);
RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
- offsetToUserSpace);
- nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox;
- gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
- nsFilterInstance::PaintFilteredFrame(frame, context.GetDrawTarget(),
- tm, &callback, &dirtyRegion);
+ offsets.offsetToUserSpaceInDevPx);
+
+ nsFilterInstance::PaintFilteredFrame(frame, &context, filterMatrix,
+ &callback, nullptr);
if (opacity != 1.0f) {
context.PopGroupAndBlend();
}
return DrawResult::SUCCESS;
}
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -854,19 +854,18 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
aDirtyRect->width, aDirtyRect->height));
tmpDirtyRegion =
nsLayoutUtils::RoundGfxRectToAppRect(
dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
aFrame->GetPosition();
dirtyRegion = &tmpDirtyRegion;
}
SVGPaintCallback paintCallback;
- nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
- aTransform, &paintCallback,
- dirtyRegion);
+ nsFilterInstance::PaintFilteredFrame(aFrame, target, aTransform,
+ &paintCallback, dirtyRegion);
} else {
result = svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
}
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
aContext.PopClip();
}