Bug 1334554 - Handle the return value of nsFilterInstance::PaintFilteredFrame
MozReview-Commit-ID: 79gddAmRjnp
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -54,33 +54,34 @@ UserSpaceMetricsForFrame(nsIFrame* aFram
{
if (aFrame->GetContent()->IsSVGElement()) {
nsSVGElement* element = static_cast<nsSVGElement*>(aFrame->GetContent());
return MakeUnique<SVGElementMetrics>(element);
}
return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
}
-nsresult
+DrawResult
nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
DrawTarget* aDrawTarget,
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);
if (!instance.IsInitialized()) {
- return NS_OK;
+ return DrawResult::BAD_IMAGE;
}
+
return instance.Render(aDrawTarget);
}
nsRegion
nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsRegion& aPreFilterDirtyRegion)
{
if (aPreFilterDirtyRegion.IsEmpty()) {
@@ -225,21 +226,16 @@ nsFilterInstance::nsFilterInstance(nsIFr
mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds);
// Build the filter graph.
rv = BuildPrimitives(aFilterChain, aTargetFrame, aFilterInputIsTainted);
if (NS_FAILED(rv)) {
return;
}
- if (mPrimitiveDescriptions.IsEmpty()) {
- // Nothing should be rendered.
- return;
- }
-
// Convert the passed in rects from frame space to filter space:
mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion);
mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion);
mInitialized = true;
}
nsresult
@@ -360,34 +356,37 @@ nsFilterInstance::ComputeNeededBoxes()
sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, mTargetBounds);
mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds();
mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds();
mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds();
}
-nsresult
+DrawResult
nsFilterInstance::BuildSourcePaint(SourceInfo *aSource,
DrawTarget* aTargetDT)
{
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;
+ return DrawResult::TEMPORARY_ERROR;
}
gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
- if (!deviceToFilterSpace.Invert()) {
- return NS_ERROR_FAILURE;
- }
+ 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)");
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()));
@@ -402,142 +401,160 @@ nsFilterInstance::BuildSourcePaint(Sourc
pattern);
}
gfx->Restore();
}
aSource->mSourceSurface = offscreenDT->Snapshot();
aSource->mSurfaceRect = neededRect;
- return NS_OK;
+ return DrawResult::SUCCESS;
}
-nsresult
+DrawResult
nsFilterInstance::BuildSourcePaints(DrawTarget* aTargetDT)
{
- nsresult rv = NS_OK;
-
if (!mFillPaint.mNeededBounds.IsEmpty()) {
- rv = BuildSourcePaint(&mFillPaint, aTargetDT);
- NS_ENSURE_SUCCESS(rv, rv);
+ DrawResult result = BuildSourcePaint(&mFillPaint, aTargetDT);
+ if (result != DrawResult::SUCCESS) {
+ return result;
+ }
}
if (!mStrokePaint.mNeededBounds.IsEmpty()) {
- rv = BuildSourcePaint(&mStrokePaint, aTargetDT);
- NS_ENSURE_SUCCESS(rv, rv);
+ DrawResult result = BuildSourcePaint(&mStrokePaint, aTargetDT);
+ if (result != DrawResult::SUCCESS) {
+ return result;
+ }
}
- return rv;
+
+ return DrawResult::SUCCESS;
}
-nsresult
+DrawResult
nsFilterInstance::BuildSourceImage(DrawTarget* aTargetDT)
{
MOZ_ASSERT(mTargetFrame);
nsIntRect neededRect = mSourceGraphic.mNeededBounds;
if (neededRect.IsEmpty()) {
- return NS_OK;
+ return DrawResult::SUCCESS;
}
RefPtr<DrawTarget> offscreenDT =
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
neededRect.Size(), SurfaceFormat::B8G8R8A8);
if (!offscreenDT || !offscreenDT->IsValid()) {
- return NS_ERROR_OUT_OF_MEMORY;
+ return DrawResult::TEMPORARY_ERROR;
}
gfxRect r = FilterSpaceToUserSpace(ThebesRect(neededRect));
r.RoundOut();
nsIntRect dirty;
- if (!gfxUtils::GfxRectToIntRect(r, &dirty))
- return NS_ERROR_FAILURE;
+ if (!gfxUtils::GfxRectToIntRect(r, &dirty)){
+ return DrawResult::SUCCESS;
+ }
// SVG graphics paint to device space, so we need to set an initial device
// space to filter space transform on the gfxContext that SourceGraphic
// and SourceAlpha will paint to.
//
// (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;
- }
+ 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));
DrawResult result =
mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty);
mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
mSourceGraphic.mSurfaceRect = neededRect;
- return (result == DrawResult::SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+ return result;
}
-nsresult
+DrawResult
nsFilterInstance::Render(DrawTarget* aDrawTarget)
{
MOZ_ASSERT(mTargetFrame, "Need a frame for rendering");
+ 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()) {
- return NS_OK;
+ return DrawResult::SUCCESS;
}
AutoRestoreTransform autoRestoreTransform(aDrawTarget);
Matrix newTM = ToMatrix(ctm).PreTranslate(filterRect.x, filterRect.y) *
aDrawTarget->GetTransform();
aDrawTarget->SetTransform(newTM);
ComputeNeededBoxes();
-
- nsresult rv = BuildSourceImage(aDrawTarget);
- if (NS_FAILED(rv))
- return rv;
- rv = BuildSourcePaints(aDrawTarget);
- if (NS_FAILED(rv))
- return rv;
+ DrawResult result = BuildSourceImage(aDrawTarget);
+ if (result != DrawResult::SUCCESS){
+ return result;
+ }
+ result = BuildSourcePaints(aDrawTarget);
+ if (result != DrawResult::SUCCESS){
+ return result;
+ }
FilterSupport::RenderFilterDescription(
aDrawTarget, mFilterDescription, IntRectToRect(filterRect),
mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect,
mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect,
mInputImages, Point(0, 0));
- return NS_OK;
+ return DrawResult::SUCCESS;
}
nsRegion
nsFilterInstance::ComputePostFilterDirtyRegion()
{
- if (mPreFilterDirtyRegion.IsEmpty()) {
+ if (mPreFilterDirtyRegion.IsEmpty() || mPrimitiveDescriptions.IsEmpty()) {
return nsRegion();
}
nsIntRegion resultChangeRegion =
FilterSupport::ComputeResultChangeRegion(mFilterDescription,
mPreFilterDirtyRegion, nsIntRegion(), nsIntRegion());
return FilterSpaceToFrameSpace(resultChangeRegion);
}
nsRect
nsFilterInstance::ComputePostFilterExtents()
{
+ if (mPrimitiveDescriptions.IsEmpty()) {
+ return nsRect();
+ }
+
nsIntRegion postFilterExtents =
FilterSupport::ComputePostFilterExtents(mFilterDescription, mTargetBounds);
return FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
}
nsRect
nsFilterInstance::ComputeSourceNeededRect()
{
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -50,17 +50,17 @@ class UserSpaceMetrics;
class nsFilterInstance
{
typedef mozilla::gfx::IntRect IntRect;
typedef mozilla::gfx::SourceSurface SourceSurface;
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
typedef mozilla::gfx::FilterDescription FilterDescription;
typedef mozilla::dom::UserSpaceMetrics UserSpaceMetrics;
-
+ typedef mozilla::image::DrawResult DrawResult;
public:
/**
* Create a FilterDescription for the supplied filter. All coordinates in
* the description are in filter space.
* @param aFilterInputIsTainted Describes whether the SourceImage / SourceAlpha
* input is tainted. This affects whether feDisplacementMap will respect
* the filter input as its map input, and it affects the IsTainted() state
* on the filter primitives in the FilterDescription. "Tainted" is a term
@@ -78,21 +78,21 @@ public:
nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages);
/**
* 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,
- nsSVGFilterPaintCallback *aPaintCallback,
- const nsRegion* aDirtyArea);
+ static DrawResult PaintFilteredFrame(nsIFrame *aFilteredFrame,
+ DrawTarget* aDrawTarget,
+ const gfxMatrix& aTransform,
+ 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.
*/
static nsRegion GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
@@ -161,17 +161,17 @@ public:
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);
+ DrawResult Render(DrawTarget* aDrawTarget);
const FilterDescription& ExtractDescriptionAndAdditionalImages(nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages)
{
mInputImages.SwapElements(aOutAdditionalImages);
return mFilterDescription;
}
/**
@@ -223,31 +223,31 @@ private:
// Set by BuildSourceImage / BuildSourcePaint.
IntRect mSurfaceRect;
};
/**
* Creates a SourceSurface for either the FillPaint or StrokePaint graph
* nodes
*/
- nsresult BuildSourcePaint(SourceInfo *aPrimitive,
+ DrawResult BuildSourcePaint(SourceInfo *aPrimitive,
DrawTarget* aTargetDT);
/**
* 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);
+ DrawResult BuildSourcePaints(DrawTarget* aTargetDT);
/**
* Creates the SourceSurface for the SourceGraphic graph node, paints its
* contents, and assigns it to mSourceGraphic.mSourceSurface.
*/
- nsresult BuildSourceImage(DrawTarget* aTargetDT);
+ DrawResult BuildSourceImage(DrawTarget* aTargetDT);
/**
* 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,
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -1110,24 +1110,25 @@ nsSVGIntegrationUtils::PaintFilter(const
nullptr, Matrix());
}
/* Paint the child and apply filters */
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);
+ DrawResult result =
+ nsFilterInstance::PaintFilteredFrame(frame, context.GetDrawTarget(),
+ tm, &callback, &dirtyRegion);
if (opacity != 1.0f) {
context.PopGroupAndBlend();
}
- return DrawResult::SUCCESS;
+ return result;
}
gfxMatrix
nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
{
int32_t appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
float devPxPerCSSPx =
1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -855,19 +855,20 @@ 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);
+ result =
+ nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
+ aTransform, &paintCallback,
+ dirtyRegion);
} else {
result = svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
}
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
aContext.PopClip();
}