gecko/layout/svg/nsSVGFilterInstance.cpp

683 lines
22 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2012-05-21 04:12:37 -07:00
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Main header first:
#include "nsSVGFilterInstance.h"
// Keep others in (case-insensitive) order:
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "nsIDOMSVGUnitTypes.h"
#include "nsISVGChildFrame.h"
#include "nsRenderingContext.h"
#include "nsSVGFilterElement.h"
#include "nsSVGFilterPaintCallback.h"
#include "nsSVGUtils.h"
#include "SVGContentUtils.h"
float
nsSVGFilterInstance::GetPrimitiveNumber(uint8_t aCtxType, float aValue) const
{
nsSVGLength2 val;
val.Init(aCtxType, 0xff, aValue,
nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
float value;
if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
value = nsSVGUtils::ObjectSpace(mTargetBBox, &val);
} else {
value = nsSVGUtils::UserSpace(mTargetFrame, &val);
}
switch (aCtxType) {
case SVGContentUtils::X:
return value * mFilterSpaceSize.width / mFilterRegion.Width();
case SVGContentUtils::Y:
return value * mFilterSpaceSize.height / mFilterRegion.Height();
case SVGContentUtils::XY:
default:
return value * SVGContentUtils::ComputeNormalizedHypotenuse(
mFilterSpaceSize.width / mFilterRegion.Width(),
mFilterSpaceSize.height / mFilterRegion.Height());
}
}
void
nsSVGFilterInstance::ConvertLocation(float aValues[3]) const
{
nsSVGLength2 val[4];
val[0].Init(SVGContentUtils::X, 0xff, aValues[0],
nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
val[1].Init(SVGContentUtils::Y, 0xff, aValues[1],
nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
// Dummy width/height values
val[2].Init(SVGContentUtils::X, 0xff, 0,
nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
val[3].Init(SVGContentUtils::Y, 0xff, 0,
nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
val, mTargetBBox, mTargetFrame);
aValues[0] = feArea.X();
aValues[1] = feArea.Y();
aValues[2] = GetPrimitiveNumber(SVGContentUtils::XY, aValues[2]);
}
already_AddRefed<gfxImageSurface>
nsSVGFilterInstance::CreateImage()
{
nsRefPtr<gfxImageSurface> surface =
new gfxImageSurface(gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
gfxASurface::ImageFormatARGB32);
if (!surface || surface->CairoStatus())
return nullptr;
surface->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
gfxImageSurface *retval = nullptr;
surface.swap(retval);
return retval;
}
gfxRect
nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aRect) const
{
gfxRect r = aRect - mFilterRegion.TopLeft();
r.Scale(mFilterSpaceSize.width / mFilterRegion.Width(),
mFilterSpaceSize.height / mFilterRegion.Height());
return r;
}
gfxPoint
nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxPoint& aPt) const
{
return gfxPoint(aPt.x * mFilterRegion.Width() / mFilterSpaceSize.width + mFilterRegion.X(),
aPt.y * mFilterRegion.Height() / mFilterSpaceSize.height + mFilterRegion.Y());
}
gfxMatrix
nsSVGFilterInstance::GetUserSpaceToFilterSpaceTransform() const
{
gfxFloat widthScale = mFilterSpaceSize.width / mFilterRegion.Width();
gfxFloat heightScale = mFilterSpaceSize.height / mFilterRegion.Height();
return gfxMatrix(widthScale, 0.0f,
0.0f, heightScale,
-mFilterRegion.X() * widthScale, -mFilterRegion.Y() * heightScale);
}
void
nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(PrimitiveInfo* aPrimitive)
{
nsSVGFE* fE = aPrimitive->mFE;
gfxRect defaultFilterSubregion(0,0,0,0);
if (fE->SubregionIsUnionOfRegions()) {
for (uint32_t i = 0; i < aPrimitive->mInputs.Length(); ++i) {
defaultFilterSubregion =
defaultFilterSubregion.Union(
aPrimitive->mInputs[i]->mImage.mFilterPrimitiveSubregion);
}
} else {
defaultFilterSubregion =
gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
}
gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
&fE->mLengthAttributes[nsSVGFE::X], mTargetBBox, mTargetFrame);
gfxRect region = UserSpaceToFilterSpace(feArea);
if (!fE->mLengthAttributes[nsSVGFE::X].IsExplicitlySet())
region.x = defaultFilterSubregion.X();
if (!fE->mLengthAttributes[nsSVGFE::Y].IsExplicitlySet())
region.y = defaultFilterSubregion.Y();
if (!fE->mLengthAttributes[nsSVGFE::WIDTH].IsExplicitlySet())
region.width = defaultFilterSubregion.Width();
if (!fE->mLengthAttributes[nsSVGFE::HEIGHT].IsExplicitlySet())
region.height = defaultFilterSubregion.Height();
// We currently require filter primitive subregions to be pixel-aligned.
// Following the spec, any pixel partially in the region is included
// in the region.
region.RoundOut();
aPrimitive->mImage.mFilterPrimitiveSubregion = region;
}
nsresult
nsSVGFilterInstance::BuildSources()
{
gfxRect filterRegion = gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
mSourceColorAlpha.mImage.mFilterPrimitiveSubregion = filterRegion;
mSourceAlpha.mImage.mFilterPrimitiveSubregion = filterRegion;
mFillPaint.mImage.mFilterPrimitiveSubregion = filterRegion;
mStrokePaint.mImage.mFilterPrimitiveSubregion = filterRegion;
nsIntRect sourceBoundsInt;
gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox);
sourceBounds.RoundOut();
// Detect possible float->int overflow
if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt))
return NS_ERROR_FAILURE;
sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds);
mSourceColorAlpha.mResultBoundingBox = sourceBoundsInt;
mSourceAlpha.mResultBoundingBox = sourceBoundsInt;
mFillPaint.mResultBoundingBox = sourceBoundsInt;
mStrokePaint.mResultBoundingBox = sourceBoundsInt;
return NS_OK;
}
nsresult
nsSVGFilterInstance::BuildPrimitives()
{
// First build mFilterInfo. It's important that we don't change that
// array after we start storing pointers to its elements!
for (nsIContent* child = mFilterElement->nsINode::GetFirstChild();
child;
child = child->GetNextSibling()) {
nsRefPtr<nsSVGFE> primitive;
CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
if (!primitive)
continue;
PrimitiveInfo* info = mPrimitives.AppendElement();
info->mFE = primitive;
}
// Now fill in all the links
nsTHashtable<ImageAnalysisEntry> imageTable;
imageTable.Init(10);
for (uint32_t i = 0; i < mPrimitives.Length(); ++i) {
PrimitiveInfo* info = &mPrimitives[i];
nsSVGFE* filter = info->mFE;
nsAutoTArray<nsSVGStringInfo,2> sources;
filter->GetSourceImageNames(sources);
for (uint32_t j=0; j<sources.Length(); ++j) {
nsAutoString str;
sources[j].mString->GetAnimValue(str, sources[j].mElement);
PrimitiveInfo* sourceInfo;
if (str.EqualsLiteral("SourceGraphic")) {
sourceInfo = &mSourceColorAlpha;
} else if (str.EqualsLiteral("SourceAlpha")) {
sourceInfo = &mSourceAlpha;
} else if (str.EqualsLiteral("FillPaint")) {
sourceInfo = &mFillPaint;
} else if (str.EqualsLiteral("StrokePaint")) {
sourceInfo = &mStrokePaint;
} else if (str.EqualsLiteral("BackgroundImage") ||
str.EqualsLiteral("BackgroundAlpha")) {
return NS_ERROR_NOT_IMPLEMENTED;
} else if (str.EqualsLiteral("")) {
sourceInfo = i == 0 ? &mSourceColorAlpha : &mPrimitives[i - 1];
} else {
ImageAnalysisEntry* entry = imageTable.GetEntry(str);
if (!entry)
return NS_ERROR_FAILURE;
sourceInfo = entry->mInfo;
}
++sourceInfo->mImageUsers;
info->mInputs.AppendElement(sourceInfo);
}
ComputeFilterPrimitiveSubregion(info);
nsAutoString str;
filter->GetResultImageName().GetAnimValue(str, filter);
ImageAnalysisEntry* entry = imageTable.PutEntry(str);
if (entry) {
entry->mInfo = info;
}
// The last filter primitive is the filter result, so mark it used
if (i == mPrimitives.Length() - 1) {
++info->mImageUsers;
}
}
return NS_OK;
}
void
nsSVGFilterInstance::ComputeResultBoundingBoxes()
{
for (uint32_t i = 0; i < mPrimitives.Length(); ++i) {
PrimitiveInfo* info = &mPrimitives[i];
nsAutoTArray<nsIntRect,2> sourceBBoxes;
for (uint32_t j = 0; j < info->mInputs.Length(); ++j) {
sourceBBoxes.AppendElement(info->mInputs[j]->mResultBoundingBox);
}
nsIntRect resultBBox = info->mFE->ComputeTargetBBox(sourceBBoxes, *this);
ClipToFilterSpace(&resultBBox);
nsSVGUtils::ClipToGfxRect(&resultBBox, info->mImage.mFilterPrimitiveSubregion);
info->mResultBoundingBox = resultBBox;
}
}
void
nsSVGFilterInstance::ComputeResultChangeBoxes()
{
for (uint32_t i = 0; i < mPrimitives.Length(); ++i) {
PrimitiveInfo* info = &mPrimitives[i];
nsAutoTArray<nsIntRect,2> sourceChangeBoxes;
for (uint32_t j = 0; j < info->mInputs.Length(); ++j) {
sourceChangeBoxes.AppendElement(info->mInputs[j]->mResultChangeBox);
}
nsIntRect resultChangeBox = info->mFE->ComputeChangeBBox(sourceChangeBoxes, *this);
info->mResultChangeBox.IntersectRect(resultChangeBox, info->mResultBoundingBox);
}
}
void
nsSVGFilterInstance::ComputeNeededBoxes()
{
if (mPrimitives.IsEmpty())
return;
// In the end, we need whatever the final filter primitive will draw that
// intersects the destination dirty area.
mPrimitives[mPrimitives.Length() - 1].mResultNeededBox.IntersectRect(
mPrimitives[mPrimitives.Length() - 1].mResultBoundingBox, mPostFilterDirtyRect);
for (int32_t i = mPrimitives.Length() - 1; i >= 0; --i) {
PrimitiveInfo* info = &mPrimitives[i];
nsAutoTArray<nsIntRect,2> sourceBBoxes;
for (uint32_t j = 0; j < info->mInputs.Length(); ++j) {
sourceBBoxes.AppendElement(info->mInputs[j]->mResultBoundingBox);
}
info->mFE->ComputeNeededSourceBBoxes(
info->mResultNeededBox, sourceBBoxes, *this);
// Update each source with the rectangle we need
for (uint32_t j = 0; j < info->mInputs.Length(); ++j) {
nsIntRect* r = &info->mInputs[j]->mResultNeededBox;
r->UnionRect(*r, sourceBBoxes[j]);
// Keep everything within the filter effects region
ClipToFilterSpace(r);
nsSVGUtils::ClipToGfxRect(r, info->mInputs[j]->mImage.mFilterPrimitiveSubregion);
}
}
}
nsIntRect
nsSVGFilterInstance::ComputeUnionOfAllNeededBoxes()
{
nsIntRect r;
r.UnionRect(mSourceColorAlpha.mResultNeededBox,
mSourceAlpha.mResultNeededBox);
r.UnionRect(r, mFillPaint.mResultNeededBox);
r.UnionRect(r, mStrokePaint.mResultNeededBox);
for (uint32_t i = 0; i < mPrimitives.Length(); ++i) {
r.UnionRect(r, mPrimitives[i].mResultNeededBox);
}
return r;
}
nsresult
nsSVGFilterInstance::BuildSourcePaint(PrimitiveInfo *aPrimitive)
{
NS_ASSERTION(aPrimitive->mImageUsers > 0, "Some user must have needed this");
nsRefPtr<gfxImageSurface> image = CreateImage();
if (!image)
return NS_ERROR_OUT_OF_MEMORY;
nsRefPtr<gfxASurface> offscreen =
gfxPlatform::GetPlatform()->CreateOffscreenSurface(
gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
gfxASurface::CONTENT_COLOR_ALPHA);
if (!offscreen || offscreen->CairoStatus())
return NS_ERROR_OUT_OF_MEMORY;
offscreen->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
nsRenderingContext tmpCtx;
tmpCtx.Init(mTargetFrame->PresContext()->DeviceContext(), offscreen);
gfxRect r = aPrimitive->mImage.mFilterPrimitiveSubregion;
gfxMatrix m = GetUserSpaceToFilterSpaceTransform();
m.Invert();
r = m.TransformBounds(r);
gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert();
gfxContext *gfx = tmpCtx.ThebesContext();
gfx->Multiply(deviceToFilterSpace);
gfx->Save();
gfxMatrix matrix =
nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING);
if (!matrix.IsSingular()) {
gfx->Multiply(matrix);
gfx->Rectangle(r);
if ((aPrimitive == &mFillPaint &&
nsSVGUtils::SetupCairoFillPaint(mTargetFrame, gfx)) ||
(aPrimitive == &mStrokePaint &&
nsSVGUtils::SetupCairoStrokePaint(mTargetFrame, gfx))) {
gfx->Fill();
}
}
gfx->Restore();
gfxContext copyContext(image);
copyContext.SetSource(offscreen);
copyContext.Paint();
aPrimitive->mImage.mImage = image;
// color model is PREMULTIPLIED SRGB by default.
return NS_OK;
}
nsresult
nsSVGFilterInstance::BuildSourcePaints()
{
nsresult rv = NS_OK;
if (!mFillPaint.mResultNeededBox.IsEmpty()) {
rv = BuildSourcePaint(&mFillPaint);
NS_ENSURE_SUCCESS(rv, rv);
}
if (!mStrokePaint.mResultNeededBox.IsEmpty()) {
rv = BuildSourcePaint(&mStrokePaint);
NS_ENSURE_SUCCESS(rv, rv);
}
return rv;
}
nsresult
nsSVGFilterInstance::BuildSourceImages()
{
nsIntRect neededRect;
neededRect.UnionRect(mSourceColorAlpha.mResultNeededBox,
mSourceAlpha.mResultNeededBox);
if (neededRect.IsEmpty())
return NS_OK;
nsRefPtr<gfxImageSurface> sourceColorAlpha = CreateImage();
if (!sourceColorAlpha)
return NS_ERROR_OUT_OF_MEMORY;
{
// Paint to an offscreen surface first, then copy it to an image
// surface. This can be faster especially when the stuff we're painting
// contains native themes.
nsRefPtr<gfxASurface> offscreen =
gfxPlatform::GetPlatform()->CreateOffscreenSurface(
gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
gfxASurface::CONTENT_COLOR_ALPHA);
if (!offscreen || offscreen->CairoStatus())
return NS_ERROR_OUT_OF_MEMORY;
offscreen->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
nsRenderingContext tmpCtx;
tmpCtx.Init(mTargetFrame->PresContext()->DeviceContext(), offscreen);
gfxRect r(neededRect.x, neededRect.y, neededRect.width, neededRect.height);
gfxMatrix m = GetUserSpaceToFilterSpaceTransform();
m.Invert();
r = m.TransformBounds(r);
r.RoundOut();
nsIntRect dirty;
if (!gfxUtils::GfxRectToIntRect(r, &dirty))
return NS_ERROR_FAILURE;
// 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().Invert();
tmpCtx.ThebesContext()->Multiply(deviceToFilterSpace);
mPaintCallback->Paint(&tmpCtx, mTargetFrame, &dirty);
gfxContext copyContext(sourceColorAlpha);
copyContext.SetSource(offscreen);
copyContext.Paint();
}
if (!mSourceColorAlpha.mResultNeededBox.IsEmpty()) {
NS_ASSERTION(mSourceColorAlpha.mImageUsers > 0, "Some user must have needed this");
mSourceColorAlpha.mImage.mImage = sourceColorAlpha;
// color model is PREMULTIPLIED SRGB by default.
}
if (!mSourceAlpha.mResultNeededBox.IsEmpty()) {
NS_ASSERTION(mSourceAlpha.mImageUsers > 0, "Some user must have needed this");
mSourceAlpha.mImage.mImage = CreateImage();
if (!mSourceAlpha.mImage.mImage)
return NS_ERROR_OUT_OF_MEMORY;
// color model is PREMULTIPLIED SRGB by default.
// Clear the color channel
const uint32_t* src = reinterpret_cast<uint32_t*>(sourceColorAlpha->Data());
uint32_t* dest = reinterpret_cast<uint32_t*>(mSourceAlpha.mImage.mImage->Data());
for (int32_t y = 0; y < mSurfaceRect.height; y++) {
uint32_t rowOffset = (mSourceAlpha.mImage.mImage->Stride()*y) >> 2;
for (int32_t x = 0; x < mSurfaceRect.width; x++) {
dest[rowOffset + x] = src[rowOffset + x] & 0xFF000000U;
}
}
mSourceAlpha.mImage.mConstantColorChannels = true;
}
return NS_OK;
}
void
nsSVGFilterInstance::EnsureColorModel(PrimitiveInfo* aPrimitive,
ColorModel aColorModel)
{
ColorModel currentModel = aPrimitive->mImage.mColorModel;
if (aColorModel == currentModel)
return;
uint8_t* data = aPrimitive->mImage.mImage->Data();
int32_t stride = aPrimitive->mImage.mImage->Stride();
nsIntRect r = aPrimitive->mResultNeededBox - mSurfaceRect.TopLeft();
if (currentModel.mAlphaChannel == ColorModel::PREMULTIPLIED) {
nsSVGUtils::UnPremultiplyImageDataAlpha(data, stride, r);
}
if (aColorModel.mColorSpace != currentModel.mColorSpace) {
if (aColorModel.mColorSpace == ColorModel::LINEAR_RGB) {
nsSVGUtils::ConvertImageDataToLinearRGB(data, stride, r);
} else {
nsSVGUtils::ConvertImageDataFromLinearRGB(data, stride, r);
}
}
if (aColorModel.mAlphaChannel == ColorModel::PREMULTIPLIED) {
nsSVGUtils::PremultiplyImageDataAlpha(data, stride, r);
}
aPrimitive->mImage.mColorModel = aColorModel;
}
nsresult
nsSVGFilterInstance::Render(gfxASurface** aOutput)
{
*aOutput = nullptr;
nsresult rv = BuildSources();
if (NS_FAILED(rv))
return rv;
rv = BuildPrimitives();
if (NS_FAILED(rv))
return rv;
if (mPrimitives.IsEmpty()) {
// Nothing should be rendered.
return NS_OK;
}
ComputeResultBoundingBoxes();
ComputeNeededBoxes();
// For now, we make all surface sizes equal to the union of the
// bounding boxes needed for each temporary image
mSurfaceRect = ComputeUnionOfAllNeededBoxes();
rv = BuildSourceImages();
if (NS_FAILED(rv))
return rv;
rv = BuildSourcePaints();
if (NS_FAILED(rv))
return rv;
for (uint32_t i = 0; i < mPrimitives.Length(); ++i) {
PrimitiveInfo* primitive = &mPrimitives[i];
nsIntRect dataRect;
// Since mResultNeededBox is clipped to the filter primitive subregion,
// dataRect is also limited to the filter primitive subregion.
if (!dataRect.IntersectRect(primitive->mResultNeededBox, mSurfaceRect))
continue;
dataRect -= mSurfaceRect.TopLeft();
primitive->mImage.mImage = CreateImage();
if (!primitive->mImage.mImage)
return NS_ERROR_OUT_OF_MEMORY;
nsAutoTArray<const Image*,2> inputs;
for (uint32_t j = 0; j < primitive->mInputs.Length(); ++j) {
PrimitiveInfo* input = primitive->mInputs[j];
if (!input->mImage.mImage) {
// This image data is not really going to be used, but we'd better
// have an image object here so the filter primitive doesn't die.
input->mImage.mImage = CreateImage();
if (!input->mImage.mImage)
return NS_ERROR_OUT_OF_MEMORY;
}
ColorModel desiredColorModel =
primitive->mFE->GetInputColorModel(this, j, &input->mImage);
if (j == 0) {
// the output colour model is whatever in1 is if there is an in1
primitive->mImage.mColorModel = desiredColorModel;
}
EnsureColorModel(input, desiredColorModel);
NS_ASSERTION(input->mImage.mImage->Stride() == primitive->mImage.mImage->Stride(),
"stride mismatch");
inputs.AppendElement(&input->mImage);
}
if (primitive->mInputs.Length() == 0) {
primitive->mImage.mColorModel = primitive->mFE->GetOutputColorModel(this);
}
rv = primitive->mFE->Filter(this, inputs, &primitive->mImage, dataRect);
if (NS_FAILED(rv))
return rv;
for (uint32_t j = 0; j < primitive->mInputs.Length(); ++j) {
PrimitiveInfo* input = primitive->mInputs[j];
--input->mImageUsers;
NS_ASSERTION(input->mImageUsers >= 0, "Bad mImageUsers tracking");
if (input->mImageUsers == 0) {
// Release the image, it's no longer needed
input->mImage.mImage = nullptr;
}
}
}
PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
ColorModel premulSRGB; // default
EnsureColorModel(result, premulSRGB);
gfxImageSurface* surf = nullptr;
result->mImage.mImage.swap(surf);
*aOutput = surf;
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputePostFilterDirtyRect(nsIntRect* aPostFilterDirtyRect)
{
*aPostFilterDirtyRect = nsIntRect();
nsresult rv = BuildSources();
if (NS_FAILED(rv))
return rv;
rv = BuildPrimitives();
if (NS_FAILED(rv))
return rv;
if (mPrimitives.IsEmpty()) {
// Nothing should be rendered, so nothing can be dirty.
return NS_OK;
}
ComputeResultBoundingBoxes();
mSourceColorAlpha.mResultChangeBox = mPreFilterDirtyRect;
mSourceAlpha.mResultChangeBox = mPreFilterDirtyRect;
ComputeResultChangeBoxes();
PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
*aPostFilterDirtyRect = result->mResultChangeBox;
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty)
{
nsresult rv = BuildSources();
if (NS_FAILED(rv))
return rv;
rv = BuildPrimitives();
if (NS_FAILED(rv))
return rv;
if (mPrimitives.IsEmpty()) {
// Nothing should be rendered, so nothing is needed.
return NS_OK;
}
ComputeResultBoundingBoxes();
ComputeNeededBoxes();
aDirty->UnionRect(mSourceColorAlpha.mResultNeededBox,
mSourceAlpha.mResultNeededBox);
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputeOutputBBox(nsIntRect* aDirty)
{
nsresult rv = BuildSources();
if (NS_FAILED(rv))
return rv;
rv = BuildPrimitives();
if (NS_FAILED(rv))
return rv;
if (mPrimitives.IsEmpty()) {
// Nothing should be rendered.
*aDirty = nsIntRect();
return NS_OK;
}
ComputeResultBoundingBoxes();
PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
*aDirty = result->mResultBoundingBox;
return NS_OK;
}