mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Bug 416305. Part 1: restructure SVG filters to remove the image dictionary and pass Image objects along edges of the filter primtive graph; filter primitive analysis and coordination is moved to nsSVGFilterInstance. r=longsonr,sr=mats
This commit is contained in:
parent
b5fc0b40d0
commit
634a8a2e01
File diff suppressed because it is too large
Load Diff
@ -39,10 +39,12 @@
|
||||
|
||||
#include "nsSVGStylableElement.h"
|
||||
#include "nsSVGLength2.h"
|
||||
#include "nsSVGString.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "gfxRect.h"
|
||||
#include "gfxImageSurface.h"
|
||||
|
||||
class nsSVGFilterResource;
|
||||
class nsSVGString;
|
||||
|
||||
typedef nsSVGStylableElement nsSVGFEBase;
|
||||
|
||||
@ -55,38 +57,69 @@ class nsSVGFE : public nsSVGFEBase
|
||||
{
|
||||
friend class nsSVGFilterInstance;
|
||||
|
||||
public:
|
||||
class ColorModel {
|
||||
public:
|
||||
enum ColorSpace { SRGB, LINEAR_RGB };
|
||||
enum AlphaChannel { UNPREMULTIPLIED, PREMULTIPLIED };
|
||||
|
||||
ColorModel(ColorSpace aColorSpace, AlphaChannel aAlphaChannel) :
|
||||
mColorSpace(aColorSpace), mAlphaChannel(aAlphaChannel) {}
|
||||
ColorModel() :
|
||||
mColorSpace(SRGB), mAlphaChannel(PREMULTIPLIED) {}
|
||||
PRBool operator==(const ColorModel& aOther) const {
|
||||
return mColorSpace == aOther.mColorSpace &&
|
||||
mAlphaChannel == aOther.mAlphaChannel;
|
||||
}
|
||||
ColorSpace mColorSpace;
|
||||
AlphaChannel mAlphaChannel;
|
||||
};
|
||||
|
||||
struct Image {
|
||||
// The device offset of mImage makes it relative to filter space
|
||||
nsRefPtr<gfxImageSurface> mImage;
|
||||
// The filter primitive subregion bounding this image, in filter space
|
||||
gfxRect mFilterPrimitiveSubregion;
|
||||
ColorModel mColorModel;
|
||||
};
|
||||
|
||||
protected:
|
||||
nsSVGFE(nsINodeInfo *aNodeInfo) : nsSVGFEBase(aNodeInfo) {}
|
||||
|
||||
struct ScaleInfo {
|
||||
nsRefPtr<gfxImageSurface> mRealSource;
|
||||
nsRefPtr<gfxImageSurface> mRealTarget;
|
||||
nsRefPtr<gfxImageSurface> mSource;
|
||||
nsRefPtr<gfxImageSurface> mTarget;
|
||||
nsRect mRect; // rect in mSource and mTarget to operate on
|
||||
nsIntRect mDataRect; // rect in mSource and mTarget to operate on
|
||||
PRPackedBool mRescaling;
|
||||
};
|
||||
|
||||
nsresult SetupScalingFilter(nsSVGFilterInstance *aInstance,
|
||||
nsSVGFilterResource *aResource,
|
||||
nsSVGString *aIn,
|
||||
nsSVGNumber2 *aUnitX, nsSVGNumber2 *aUnitY,
|
||||
ScaleInfo *aScaleInfo);
|
||||
|
||||
void FinishScalingFilter(nsSVGFilterResource *aResource,
|
||||
ScaleInfo *aScaleInfo);
|
||||
ScaleInfo SetupScalingFilter(nsSVGFilterInstance *aInstance,
|
||||
const Image *aSource,
|
||||
const Image *aTarget,
|
||||
const nsIntRect& aDataRect,
|
||||
nsSVGNumber2 *aUnitX, nsSVGNumber2 *aUnitY);
|
||||
|
||||
void FinishScalingFilter(ScaleInfo *aScaleInfo);
|
||||
|
||||
public:
|
||||
nsSVGFilterInstance::ColorModel
|
||||
GetColorModel(nsSVGFilterInstance* aInstance, nsSVGString* aIn) {
|
||||
return nsSVGFilterInstance::ColorModel (
|
||||
(OperatesOnSRGB(aInstance, aIn) ?
|
||||
nsSVGFilterInstance::ColorModel::SRGB :
|
||||
nsSVGFilterInstance::ColorModel::LINEAR_RGB),
|
||||
ColorModel
|
||||
GetInputColorModel(nsSVGFilterInstance* aInstance, PRUint32 aInputIndex,
|
||||
Image* aImage) {
|
||||
return ColorModel(
|
||||
(OperatesOnSRGB(aInstance, aInputIndex, aImage) ?
|
||||
ColorModel::SRGB : ColorModel::LINEAR_RGB),
|
||||
(OperatesOnPremultipledAlpha() ?
|
||||
nsSVGFilterInstance::ColorModel::PREMULTIPLIED :
|
||||
nsSVGFilterInstance::ColorModel::UNPREMULTIPLIED));
|
||||
ColorModel::PREMULTIPLIED : ColorModel::UNPREMULTIPLIED));
|
||||
}
|
||||
|
||||
ColorModel
|
||||
GetOutputColorModel(nsSVGFilterInstance* aInstance) {
|
||||
return ColorModel(
|
||||
(OperatesOnSRGB(aInstance, 0, nsnull) ?
|
||||
ColorModel::SRGB : ColorModel::LINEAR_RGB),
|
||||
(OperatesOnPremultipledAlpha() ?
|
||||
ColorModel::PREMULTIPLIED : ColorModel::UNPREMULTIPLIED));
|
||||
}
|
||||
|
||||
// See http://www.w3.org/TR/SVG/filters.html#FilterPrimitiveSubRegion
|
||||
@ -98,7 +131,7 @@ public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES
|
||||
|
||||
virtual nsSVGString* GetResultImageName()=0;
|
||||
virtual nsSVGString* GetResultImageName() = 0;
|
||||
// Return a list of all image names used as sources. Default is to
|
||||
// return no sources.
|
||||
virtual void GetSourceImageNames(nsTArray<nsSVGString*>* aSources);
|
||||
@ -108,7 +141,7 @@ public:
|
||||
// if the filter fills its filter primitive subregion, it can just
|
||||
// return GetMaxRect() here.
|
||||
// The source bounding-boxes are ordered corresponding to GetSourceImageNames.
|
||||
virtual nsRect ComputeTargetBBox(const nsTArray<nsRect>& aSourceBBoxes,
|
||||
virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
|
||||
const nsSVGFilterInstance& aInstance);
|
||||
// Given a bounding-box for what we need to compute in the target,
|
||||
// compute which regions of the inputs are needed. On input
|
||||
@ -117,15 +150,27 @@ public:
|
||||
// which region of the source's output it needs.
|
||||
// The default implementation sets all the source bboxes to the
|
||||
// target bbox.
|
||||
virtual void ComputeNeededSourceBBoxes(const nsRect& aTargetBBox,
|
||||
nsTArray<nsRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
|
||||
// Perform the actual filter operation.
|
||||
virtual nsresult Filter(nsSVGFilterInstance* aInstance) = 0;
|
||||
virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
|
||||
nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
|
||||
|
||||
static nsRect GetMaxRect() {
|
||||
// Perform the actual filter operation.
|
||||
// We guarantee that every mImage from aSources and aTarget has the
|
||||
// same width, height, stride and device offset.
|
||||
// aTarget is already filled in. This function just needs to fill in the
|
||||
// pixels of aTarget->mImage (which have already been cleared).
|
||||
// @param aDataRect the destination rectangle that needs to be painted,
|
||||
// relative to aTarget's surface data. Output must be clipped to this
|
||||
// rectangle. This is the intersection of the filter primitive subregion
|
||||
// for this filter element and the temporary surface area.
|
||||
virtual nsresult Filter(nsSVGFilterInstance* aInstance,
|
||||
const nsTArray<const Image*>& aSources,
|
||||
const Image* aTarget,
|
||||
const nsIntRect& aDataRect) = 0;
|
||||
|
||||
static nsIntRect GetMaxRect() {
|
||||
// Try to avoid overflow errors dealing with this rect. It will
|
||||
// be intersected with some other reasonable-sized rect eventually.
|
||||
return nsRect(PR_INT32_MIN/2, PR_INT32_MIN/2, PR_INT32_MAX, PR_INT32_MAX);
|
||||
return nsIntRect(PR_INT32_MIN/2, PR_INT32_MIN/2, PR_INT32_MAX, PR_INT32_MAX);
|
||||
}
|
||||
|
||||
operator nsISupports*() { return static_cast<nsIContent*>(this); }
|
||||
@ -133,8 +178,11 @@ public:
|
||||
protected:
|
||||
virtual PRBool OperatesOnPremultipledAlpha() { return PR_TRUE; }
|
||||
|
||||
virtual PRBool OperatesOnSRGB(nsSVGFilterInstance*,
|
||||
nsSVGString*) {
|
||||
// Called either with aImage non-null, in which case this is
|
||||
// testing whether the input 'aInputIndex' should be SRGB, or
|
||||
// if aImage is null returns true if the output will be SRGB
|
||||
virtual PRBool OperatesOnSRGB(nsSVGFilterInstance* aInstance,
|
||||
PRUint32 aInputIndex, Image* aImage) {
|
||||
nsIFrame* frame = GetPrimaryFrame();
|
||||
if (!frame) return PR_FALSE;
|
||||
|
||||
|
@ -70,6 +70,7 @@ CPPSRCS = \
|
||||
nsSVGClipPathFrame.cpp \
|
||||
nsSVGContainerFrame.cpp \
|
||||
nsSVGFilterFrame.cpp \
|
||||
nsSVGFilterInstance.cpp \
|
||||
nsSVGGFrame.cpp \
|
||||
nsSVGGenericContainerFrame.cpp \
|
||||
nsSVGGeometryFrame.cpp \
|
||||
|
@ -83,225 +83,6 @@ nsSVGFilterFrame::FilterFailCleanup(nsSVGRenderState *aContext,
|
||||
aTarget->PaintSVG(aContext, nsnull);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class builds a graph of the filter image data flow, essentially
|
||||
* converting the filter graph to SSA. This lets us easily propagate
|
||||
* analysis data (such as bounding-boxes) over the filter primitive graph.
|
||||
* XXX In the future we could extend this to propagate other information
|
||||
* such as whether images are opaque, whether they're alpha-only,
|
||||
* and color models... We should also compute and store filter primitive
|
||||
* subregions here, and use this graph for drawing to
|
||||
* eliminate the need for an image dictionary at draw time.
|
||||
*/
|
||||
class FilterAnalysis {
|
||||
public:
|
||||
FilterAnalysis(const nsRect& aSourceBBox, const nsRect& aFilterEffectsRegion,
|
||||
const nsSVGFilterInstance& aInstance)
|
||||
: mFilterEffectsRegion(aFilterEffectsRegion), mInstance(&aInstance)
|
||||
{
|
||||
mSourceColorAlphaInfo.mResultBoundingBox = aSourceBBox;
|
||||
mSourceAlphaInfo.mResultBoundingBox = aSourceBBox;
|
||||
}
|
||||
|
||||
// Build graph of Info nodes describing filter primitives
|
||||
nsresult SetupGraph(nsIContent* aFilterElement);
|
||||
// Compute bounding boxes of the filter primitive outputs
|
||||
void ComputeResultBoundingBoxes();
|
||||
// Compute bounding boxes of what we actually *need* from the filter
|
||||
// primitive outputs
|
||||
void ComputeNeededBoxes();
|
||||
nsRect ComputeUnionOfAllNeededBoxes();
|
||||
const nsRect& GetSourceColorAlphaNeeded()
|
||||
{ return mSourceColorAlphaInfo.mResultNeededBox; }
|
||||
const nsRect& GetSourceAlphaNeeded()
|
||||
{ return mSourceAlphaInfo.mResultNeededBox; }
|
||||
|
||||
private:
|
||||
struct Info {
|
||||
nsSVGFE* mFE;
|
||||
nsRect mResultBoundingBox;
|
||||
nsRect mResultNeededBox;
|
||||
|
||||
// Can't use nsAutoTArray here, because these Info objects
|
||||
// live in nsTArrays themselves and nsTArray moves the elements
|
||||
// around in memory, which breaks nsAutoTArray.
|
||||
nsTArray<Info*> mInputs;
|
||||
|
||||
Info() : mFE(nsnull) {}
|
||||
};
|
||||
|
||||
class ImageAnalysisEntry : public nsStringHashKey {
|
||||
public:
|
||||
ImageAnalysisEntry(KeyTypePointer aStr) : nsStringHashKey(aStr) { }
|
||||
ImageAnalysisEntry(const ImageAnalysisEntry& toCopy) : nsStringHashKey(toCopy),
|
||||
mInfo(toCopy.mInfo) { }
|
||||
|
||||
Info* mInfo;
|
||||
};
|
||||
|
||||
nsRect mFilterEffectsRegion;
|
||||
const nsSVGFilterInstance* mInstance;
|
||||
|
||||
Info mSourceColorAlphaInfo;
|
||||
Info mSourceAlphaInfo;
|
||||
nsTArray<Info> mFilterInfo;
|
||||
};
|
||||
|
||||
nsresult
|
||||
FilterAnalysis::SetupGraph(nsIContent* aFilterElement)
|
||||
{
|
||||
// First build mFilterInfo. It's important that we don't change that
|
||||
// array after we start storing pointers to its elements!
|
||||
PRUint32 count = aFilterElement->GetChildCount();
|
||||
PRUint32 i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
nsIContent* child = aFilterElement->GetChildAt(i);
|
||||
nsRefPtr<nsSVGFE> filter;
|
||||
CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(filter));
|
||||
if (!filter)
|
||||
continue;
|
||||
|
||||
Info* info = mFilterInfo.AppendElement();
|
||||
info->mFE = filter;
|
||||
}
|
||||
|
||||
// Now fill in all the links
|
||||
nsTHashtable<ImageAnalysisEntry> imageTable;
|
||||
imageTable.Init(10);
|
||||
|
||||
for (i = 0; i < mFilterInfo.Length(); ++i) {
|
||||
Info* info = &mFilterInfo[i];
|
||||
nsSVGFE* filter = info->mFE;
|
||||
nsAutoTArray<nsSVGString*,2> sources;
|
||||
filter->GetSourceImageNames(&sources);
|
||||
|
||||
for (PRUint32 j=0; j<sources.Length(); ++j) {
|
||||
const nsString &str = sources[j]->GetAnimValue();
|
||||
Info* sourceInfo;
|
||||
|
||||
if (str.EqualsLiteral("SourceGraphic")) {
|
||||
sourceInfo = &mSourceColorAlphaInfo;
|
||||
} else if (str.EqualsLiteral("SourceAlpha")) {
|
||||
sourceInfo = &mSourceAlphaInfo;
|
||||
} else if (str.EqualsLiteral("BackgroundImage") ||
|
||||
str.EqualsLiteral("BackgroundAlpha") ||
|
||||
str.EqualsLiteral("FillPaint") ||
|
||||
str.EqualsLiteral("StrokePaint")) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
} else if (str.EqualsLiteral("")) {
|
||||
sourceInfo = i == 0 ? &mSourceColorAlphaInfo : &mFilterInfo[i - 1];
|
||||
} else {
|
||||
ImageAnalysisEntry* entry = imageTable.GetEntry(str);
|
||||
if (!entry)
|
||||
return NS_ERROR_FAILURE;
|
||||
sourceInfo = entry->mInfo;
|
||||
}
|
||||
|
||||
info->mInputs.AppendElement(sourceInfo);
|
||||
}
|
||||
|
||||
ImageAnalysisEntry* entry = imageTable.PutEntry(
|
||||
filter->GetResultImageName()->GetAnimValue());
|
||||
if (entry) {
|
||||
entry->mInfo = info;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FilterAnalysis::ComputeResultBoundingBoxes()
|
||||
{
|
||||
for (PRUint32 i = 0; i < mFilterInfo.Length(); ++i) {
|
||||
Info* info = &mFilterInfo[i];
|
||||
nsAutoTArray<nsRect,2> sourceBBoxes;
|
||||
for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
|
||||
sourceBBoxes.AppendElement(info->mInputs[j]->mResultBoundingBox);
|
||||
}
|
||||
|
||||
nsRect resultBBox = info->mFE->ComputeTargetBBox(sourceBBoxes, *mInstance);
|
||||
// XXX at some point we should clip this to the filter primitive subregion
|
||||
// as well
|
||||
resultBBox.IntersectRect(resultBBox, mFilterEffectsRegion);
|
||||
info->mResultBoundingBox = resultBBox;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FilterAnalysis::ComputeNeededBoxes()
|
||||
{
|
||||
if (mFilterInfo.IsEmpty())
|
||||
return;
|
||||
|
||||
// In the end, we need whatever the final filter primitive will draw.
|
||||
// XXX we could optimize this by intersecting with the dirty rect here!!!
|
||||
mFilterInfo[mFilterInfo.Length() - 1].mResultNeededBox
|
||||
= mFilterInfo[mFilterInfo.Length() - 1].mResultBoundingBox;
|
||||
|
||||
for (PRInt32 i = mFilterInfo.Length() - 1; i >= 0; --i) {
|
||||
Info* info = &mFilterInfo[i];
|
||||
nsAutoTArray<nsRect,2> sourceBBoxes;
|
||||
for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
|
||||
sourceBBoxes.AppendElement(info->mInputs[j]->mResultBoundingBox);
|
||||
}
|
||||
|
||||
info->mFE->ComputeNeededSourceBBoxes(
|
||||
mFilterInfo[i].mResultNeededBox, sourceBBoxes, *mInstance);
|
||||
// Update each source with the rectangle we need
|
||||
for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
|
||||
nsRect* r = &info->mInputs[j]->mResultNeededBox;
|
||||
r->UnionRect(*r, sourceBBoxes[j]);
|
||||
// Keep everything within the filter effects region
|
||||
// XXX at some point we should clip to the filter primitive subregion
|
||||
// as well
|
||||
r->IntersectRect(*r, mFilterEffectsRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsRect
|
||||
FilterAnalysis::ComputeUnionOfAllNeededBoxes()
|
||||
{
|
||||
nsRect r;
|
||||
r.UnionRect(mSourceColorAlphaInfo.mResultNeededBox,
|
||||
mSourceAlphaInfo.mResultNeededBox);
|
||||
for (PRUint32 i = 0; i < mFilterInfo.Length(); ++i) {
|
||||
r.UnionRect(r, mFilterInfo[i].mResultNeededBox);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// XXX only works with matrices that are a translation + scale!
|
||||
static nsRect
|
||||
TransformBBox(nsIDOMSVGRect *aBBox, nsIDOMSVGMatrix *aMatrix,
|
||||
const nsRect& aBounds)
|
||||
{
|
||||
if (!aBBox) {
|
||||
// No bbox means empty bbox (from nsSVGUseFrame at least); just
|
||||
// return an empty rect
|
||||
return nsRect();
|
||||
}
|
||||
|
||||
float bboxX, bboxY, bboxWidth, bboxHeight;
|
||||
aBBox->GetX(&bboxX);
|
||||
aBBox->GetY(&bboxY);
|
||||
aBBox->GetWidth(&bboxWidth);
|
||||
aBBox->GetHeight(&bboxHeight);
|
||||
|
||||
float bboxXMost = bboxX + bboxWidth;
|
||||
float bboxYMost = bboxY + bboxHeight;
|
||||
nsSVGUtils::TransformPoint(aMatrix, &bboxX, &bboxY);
|
||||
nsSVGUtils::TransformPoint(aMatrix, &bboxXMost, &bboxYMost);
|
||||
|
||||
nsRect r;
|
||||
r.x = NSToIntFloor(PR_MAX(aBounds.x, bboxX));
|
||||
r.y = NSToIntFloor(PR_MAX(aBounds.y, bboxY));
|
||||
r.width = NSToIntCeil(PR_MIN(aBounds.XMost(), bboxXMost)) - r.x;
|
||||
r.height = NSToIntCeil(PR_MIN(aBounds.YMost(), bboxYMost)) - r.y;
|
||||
return r;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
||||
nsISVGChildFrame *aTarget)
|
||||
@ -379,7 +160,6 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
||||
&resultOverflows);
|
||||
}
|
||||
|
||||
|
||||
// 0 disables rendering, < 0 is error
|
||||
if (filterRes.width <= 0 || filterRes.height <= 0) {
|
||||
aTarget->SetMatrixPropagation(PR_TRUE);
|
||||
@ -393,6 +173,7 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
||||
fprintf(stderr, "filterRes: %u %u\n", filterRes.width, filterRes.height);
|
||||
#endif
|
||||
|
||||
// Transformation from user space to filter space
|
||||
nsCOMPtr<nsIDOMSVGMatrix> filterTransform;
|
||||
NS_NewSVGMatrix(getter_AddRefs(filterTransform),
|
||||
filterRes.width / width, 0.0f,
|
||||
@ -402,140 +183,34 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
||||
aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
|
||||
nsISVGChildFrame::TRANSFORM_CHANGED);
|
||||
|
||||
// Setup instance data
|
||||
PRUint16 primitiveUnits =
|
||||
filter->mEnumAttributes[nsSVGFilterElement::PRIMITIVEUNITS].GetAnimValue();
|
||||
|
||||
nsSVGFilterInstance::ColorModel
|
||||
colorModel(nsSVGFilterInstance::ColorModel::SRGB,
|
||||
nsSVGFilterInstance::ColorModel::PREMULTIPLIED);
|
||||
|
||||
// Setup instance data
|
||||
nsSVGFilterInstance instance(target, bbox,
|
||||
x, y, width, height,
|
||||
filterRes.width, filterRes.height,
|
||||
nsSVGFilterInstance instance(aTarget, mContent, bbox,
|
||||
gfxRect(x, y, width, height),
|
||||
nsIntSize(filterRes.width, filterRes.height),
|
||||
primitiveUnits);
|
||||
|
||||
// Compute the smallest buffer size that can contain the rendering of
|
||||
// all filter components. We also compute whether we need
|
||||
// the source image and/or alpha (and which region of each we need,
|
||||
// XXX although we don't use that yet).
|
||||
nsRect filterBounds(0, 0, filterRes.width, filterRes.height);
|
||||
nsRect sourceBounds = TransformBBox(bbox, filterTransform, filterBounds);
|
||||
FilterAnalysis analysis(sourceBounds, filterBounds, instance);
|
||||
|
||||
nsresult rv = analysis.SetupGraph(mContent);
|
||||
nsRefPtr<gfxASurface> result;
|
||||
nsresult rv = instance.Render(getter_AddRefs(result));
|
||||
if (NS_FAILED(rv)) {
|
||||
FilterFailCleanup(aContext, aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
analysis.ComputeResultBoundingBoxes();
|
||||
analysis.ComputeNeededBoxes();
|
||||
|
||||
// set the dimensions for all surfaces to the bounding box of all needed
|
||||
// images.
|
||||
// These surfaces use device offsets to position themselves inside our
|
||||
// filter effects region.
|
||||
// XXX this isn't optimal, we really should be able to use different
|
||||
// sizes for different images!
|
||||
instance.SetSurfaceRect(analysis.ComputeUnionOfAllNeededBoxes());
|
||||
if (instance.GetSurfaceRect().IsEmpty())
|
||||
return NS_OK;
|
||||
if (result) {
|
||||
nsCOMPtr<nsIDOMSVGMatrix> scale, fini;
|
||||
NS_NewSVGMatrix(getter_AddRefs(scale),
|
||||
width / filterRes.width, 0.0f,
|
||||
0.0f, height / filterRes.height,
|
||||
x, y);
|
||||
|
||||
if (!analysis.GetSourceColorAlphaNeeded().IsEmpty() ||
|
||||
!analysis.GetSourceAlphaNeeded().IsEmpty()) {
|
||||
// paint the target geometry
|
||||
nsRefPtr<gfxImageSurface> tmpSurface = instance.GetImage();
|
||||
if (!tmpSurface) {
|
||||
FilterFailCleanup(aContext, aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
ctm->Multiply(scale, getter_AddRefs(fini));
|
||||
|
||||
// XXX now that we can compute which region of the source will
|
||||
// actually be needed, we could speed this up
|
||||
gfxContext tmpContext(tmpSurface);
|
||||
nsSVGRenderState tmpState(&tmpContext);
|
||||
tmpContext.SetOperator(gfxContext::OPERATOR_OVER);
|
||||
aTarget->PaintSVG(&tmpState, nsnull);
|
||||
|
||||
if (!analysis.GetSourceAlphaNeeded().IsEmpty()) {
|
||||
nsRefPtr<gfxImageSurface> alpha = instance.GetImage();
|
||||
if (!alpha) {
|
||||
FilterFailCleanup(aContext, aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUint8 *data = tmpSurface->Data();
|
||||
PRUint8 *alphaData = alpha->Data();
|
||||
PRUint32 stride = tmpSurface->Stride();
|
||||
|
||||
for (PRInt32 yy = 0; yy < instance.GetSurfaceHeight(); yy++)
|
||||
for (PRInt32 xx = 0; xx < instance.GetSurfaceWidth(); xx++) {
|
||||
alphaData[stride*yy + 4*xx + GFX_ARGB32_OFFSET_B] = 0;
|
||||
alphaData[stride*yy + 4*xx + GFX_ARGB32_OFFSET_G] = 0;
|
||||
alphaData[stride*yy + 4*xx + GFX_ARGB32_OFFSET_R] = 0;
|
||||
alphaData[stride*yy + 4*xx + GFX_ARGB32_OFFSET_A] =
|
||||
data[stride*yy + 4*xx + GFX_ARGB32_OFFSET_A];
|
||||
}
|
||||
|
||||
instance.DefineImage(NS_LITERAL_STRING("SourceAlpha"), alpha,
|
||||
nsRect(0, 0, filterRes.width, filterRes.height),
|
||||
colorModel);
|
||||
}
|
||||
|
||||
// this always needs to be defined last because the default image
|
||||
// for the first filter element is supposed to be SourceGraphic
|
||||
instance.DefineImage(NS_LITERAL_STRING("SourceGraphic"), tmpSurface,
|
||||
nsRect(0, 0, filterRes.width, filterRes.height),
|
||||
colorModel);
|
||||
} else {
|
||||
// XXX We shouldn't really need to set up a temporary surface here,
|
||||
// but we have to because all filter primitives currently need to
|
||||
// call AcquireSourceImage and find a source image, even if they don't
|
||||
// use it!
|
||||
nsRefPtr<gfxImageSurface> tmpSurface = instance.GetImage();
|
||||
if (!tmpSurface) {
|
||||
FilterFailCleanup(aContext, aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
instance.DefineImage(NS_LITERAL_STRING("SourceGraphic"), tmpSurface,
|
||||
nsRect(0, 0, filterRes.width, filterRes.height),
|
||||
colorModel);
|
||||
nsSVGUtils::CompositeSurfaceMatrix(aContext->GetGfxContext(),
|
||||
result, fini, 1.0);
|
||||
}
|
||||
|
||||
// Now invoke the components of the filter chain
|
||||
PRUint32 count = mContent->GetChildCount();
|
||||
for (PRUint32 k=0; k<count; ++k) {
|
||||
nsIContent* child = mContent->GetChildAt(k);
|
||||
nsRefPtr<nsSVGFE> filter;
|
||||
CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(filter));
|
||||
if (filter && NS_FAILED(filter->Filter(&instance))) {
|
||||
FilterFailCleanup(aContext, aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
nsRect filterRect;
|
||||
nsRefPtr<gfxImageSurface> filterSurface;
|
||||
|
||||
instance.LookupImage(NS_LITERAL_STRING(""),
|
||||
getter_AddRefs(filterSurface), &filterRect, colorModel);
|
||||
|
||||
if (!filterSurface) {
|
||||
FilterFailCleanup(aContext, aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMSVGMatrix> scale, fini;
|
||||
NS_NewSVGMatrix(getter_AddRefs(scale),
|
||||
width / filterRes.width, 0.0f,
|
||||
0.0f, height / filterRes.height,
|
||||
x, y);
|
||||
|
||||
ctm->Multiply(scale, getter_AddRefs(fini));
|
||||
|
||||
nsSVGUtils::CompositeSurfaceMatrix(aContext->GetGfxContext(),
|
||||
filterSurface, fini, 1.0);
|
||||
|
||||
aTarget->SetOverrideCTM(nsnull);
|
||||
aTarget->SetMatrixPropagation(PR_TRUE);
|
||||
aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
|
||||
@ -638,217 +313,3 @@ nsSVGFilterFrame::GetType() const
|
||||
{
|
||||
return nsGkAtoms::svgFilterFrame;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// nsSVGFilterInstance
|
||||
|
||||
float
|
||||
nsSVGFilterInstance::GetPrimitiveLength(nsSVGLength2 *aLength) const
|
||||
{
|
||||
float value;
|
||||
if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
|
||||
value = nsSVGUtils::ObjectSpace(mTargetBBox, aLength);
|
||||
else
|
||||
value = nsSVGUtils::UserSpace(mTarget, aLength);
|
||||
|
||||
switch (aLength->GetCtxType()) {
|
||||
case nsSVGUtils::X:
|
||||
return value * mFilterResX / mFilterWidth;
|
||||
case nsSVGUtils::Y:
|
||||
return value * mFilterResY / mFilterHeight;
|
||||
case nsSVGUtils::XY:
|
||||
default:
|
||||
return value *
|
||||
sqrt(float(mFilterResX * mFilterResX + mFilterResY * mFilterResY)) /
|
||||
sqrt(mFilterWidth * mFilterWidth + mFilterHeight * mFilterHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGFilterInstance::GetFilterSubregion(
|
||||
nsIContent *aFilter,
|
||||
nsRect defaultRegion,
|
||||
nsRect *result)
|
||||
{
|
||||
nsSVGFE *fE = static_cast<nsSVGFE*>(aFilter);
|
||||
nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight;
|
||||
|
||||
tmpX = &fE->mLengthAttributes[nsSVGFE::X];
|
||||
tmpY = &fE->mLengthAttributes[nsSVGFE::Y];
|
||||
tmpWidth = &fE->mLengthAttributes[nsSVGFE::WIDTH];
|
||||
tmpHeight = &fE->mLengthAttributes[nsSVGFE::HEIGHT];
|
||||
|
||||
float x, y, width, height;
|
||||
if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
|
||||
mTargetBBox->GetX(&x);
|
||||
x += nsSVGUtils::ObjectSpace(mTargetBBox, tmpX);
|
||||
mTargetBBox->GetY(&y);
|
||||
y += nsSVGUtils::ObjectSpace(mTargetBBox, tmpY);
|
||||
width = nsSVGUtils::ObjectSpace(mTargetBBox, tmpWidth);
|
||||
height = nsSVGUtils::ObjectSpace(mTargetBBox, tmpHeight);
|
||||
} else {
|
||||
x = nsSVGUtils::UserSpace(mTarget, tmpX);
|
||||
y = nsSVGUtils::UserSpace(mTarget, tmpY);
|
||||
width = nsSVGUtils::UserSpace(mTarget, tmpWidth);
|
||||
height = nsSVGUtils::UserSpace(mTarget, tmpHeight);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "GFS[1]: %f %f %f %f\n", x, y, width, height);
|
||||
#endif
|
||||
|
||||
nsRect filter, region;
|
||||
|
||||
filter.x = 0;
|
||||
filter.y = 0;
|
||||
filter.width = mFilterResX;
|
||||
filter.height = mFilterResY;
|
||||
|
||||
// XXX this needs to round out
|
||||
region.x = (x - mFilterX) * mFilterResX / mFilterWidth;
|
||||
region.y = (y - mFilterY) * mFilterResY / mFilterHeight;
|
||||
region.width = width * mFilterResX / mFilterWidth;
|
||||
region.height = height * mFilterResY / mFilterHeight;
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "GFS[2]: %d %d %d %d\n",
|
||||
region.x, region.y, region.width, region.height);
|
||||
#endif
|
||||
|
||||
if (!aFilter->HasAttr(kNameSpaceID_None, nsGkAtoms::x))
|
||||
region.x = defaultRegion.x;
|
||||
if (!aFilter->HasAttr(kNameSpaceID_None, nsGkAtoms::y))
|
||||
region.y = defaultRegion.y;
|
||||
if (!aFilter->HasAttr(kNameSpaceID_None, nsGkAtoms::width))
|
||||
region.width = defaultRegion.width;
|
||||
if (!aFilter->HasAttr(kNameSpaceID_None, nsGkAtoms::height))
|
||||
region.height = defaultRegion.height;
|
||||
|
||||
result->IntersectRect(filter, region);
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "GFS[3]: %d %d %d %d\n",
|
||||
result->x, result->y, result->width, result->height);
|
||||
#endif
|
||||
}
|
||||
|
||||
already_AddRefed<gfxImageSurface>
|
||||
nsSVGFilterInstance::GetImage()
|
||||
{
|
||||
nsRefPtr<gfxImageSurface> surface =
|
||||
new gfxImageSurface(gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
|
||||
if (!surface || surface->CairoStatus()) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
surface->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
|
||||
mSurfaceStride = surface->Stride();
|
||||
|
||||
gfxContext ctx(surface);
|
||||
ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
|
||||
ctx.Paint();
|
||||
|
||||
gfxImageSurface *retval = nsnull;
|
||||
surface.swap(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGFilterInstance::LookupImage(const nsAString &aName,
|
||||
gfxImageSurface **aImage,
|
||||
nsRect *aRegion,
|
||||
const ColorModel &aRequiredColorModel)
|
||||
{
|
||||
ImageEntry *entry;
|
||||
|
||||
if (aName.IsEmpty()) {
|
||||
entry = mLastImage;
|
||||
} else {
|
||||
mImageDictionary.Get(aName, &entry);
|
||||
if (!entry) {
|
||||
entry = mLastImage;
|
||||
}
|
||||
}
|
||||
|
||||
*aImage = entry->mImage;
|
||||
NS_ADDREF(*aImage);
|
||||
*aRegion = entry->mRegion;
|
||||
|
||||
if (aRequiredColorModel == entry->mColorModel)
|
||||
return;
|
||||
|
||||
// convert image to desired format
|
||||
PRUint8 *data = (*aImage)->Data();
|
||||
PRInt32 stride = (*aImage)->Stride();
|
||||
|
||||
nsRect r;
|
||||
r.IntersectRect(entry->mRegion, mSurfaceRect);
|
||||
r -= mSurfaceRect.TopLeft();
|
||||
|
||||
if (entry->mColorModel.mAlphaChannel == ColorModel::PREMULTIPLIED)
|
||||
nsSVGUtils::UnPremultiplyImageDataAlpha(data, stride, r);
|
||||
|
||||
if (aRequiredColorModel.mColorSpace != entry->mColorModel.mColorSpace) {
|
||||
if (aRequiredColorModel.mColorSpace == ColorModel::LINEAR_RGB)
|
||||
nsSVGUtils::ConvertImageDataToLinearRGB(data, stride, r);
|
||||
else
|
||||
nsSVGUtils::ConvertImageDataFromLinearRGB(data, stride, r);
|
||||
}
|
||||
if (aRequiredColorModel.mAlphaChannel == ColorModel::PREMULTIPLIED)
|
||||
nsSVGUtils::PremultiplyImageDataAlpha(data, stride, r);
|
||||
|
||||
entry->mColorModel = aRequiredColorModel;
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsSVGFilterInstance::LookupImageRegion(const nsAString &aName)
|
||||
{
|
||||
ImageEntry *entry;
|
||||
|
||||
if (aName.IsEmpty())
|
||||
entry = mLastImage;
|
||||
else
|
||||
mImageDictionary.Get(aName, &entry);
|
||||
|
||||
if (entry)
|
||||
return entry->mRegion;
|
||||
|
||||
return nsRect();
|
||||
}
|
||||
|
||||
nsSVGFilterInstance::ColorModel
|
||||
nsSVGFilterInstance::LookupImageColorModel(const nsAString &aName)
|
||||
{
|
||||
ImageEntry *entry;
|
||||
|
||||
if (aName.IsEmpty())
|
||||
entry = mLastImage;
|
||||
else
|
||||
mImageDictionary.Get(aName, &entry);
|
||||
|
||||
if (entry)
|
||||
return entry->mColorModel;
|
||||
|
||||
// We'll reach this point if someone specifies a nonexistent input
|
||||
// for a filter, as feDisplacementMap need to find the color model
|
||||
// before the filter element calls AcquireSourceImage() which both
|
||||
// uses the color model and tells us if the input exists.
|
||||
|
||||
return ColorModel(ColorModel::SRGB, ColorModel::PREMULTIPLIED);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGFilterInstance::DefineImage(const nsAString &aName,
|
||||
gfxImageSurface *aImage,
|
||||
const nsRect &aRegion,
|
||||
const ColorModel &aColorModel)
|
||||
{
|
||||
ImageEntry *entry = new ImageEntry(aImage, aRegion, aColorModel);
|
||||
|
||||
if (entry)
|
||||
mImageDictionary.Put(aName, entry);
|
||||
|
||||
mLastImage = entry;
|
||||
}
|
||||
|
@ -39,116 +39,135 @@
|
||||
|
||||
#include "nsIDOMSVGLength.h"
|
||||
#include "nsIDOMSVGRect.h"
|
||||
#include "nsInterfaceHashtable.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsIDOMSVGFilters.h"
|
||||
#include "nsRect.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsSVGFilters.h"
|
||||
#include "nsISVGChildFrame.h"
|
||||
#include "nsSVGString.h"
|
||||
|
||||
#include "gfxImageSurface.h"
|
||||
|
||||
class nsSVGLength2;
|
||||
class nsSVGElement;
|
||||
|
||||
/**
|
||||
* This class performs all filter processing.
|
||||
*
|
||||
* We build a graph of the filter image data flow, essentially
|
||||
* converting the filter graph to SSA. This lets us easily propagate
|
||||
* analysis data (such as bounding-boxes) over the filter primitive graph.
|
||||
*/
|
||||
class NS_STACK_CLASS nsSVGFilterInstance
|
||||
{
|
||||
public:
|
||||
class ColorModel {
|
||||
public:
|
||||
enum ColorSpace { SRGB, LINEAR_RGB };
|
||||
enum AlphaChannel { UNPREMULTIPLIED, PREMULTIPLIED };
|
||||
|
||||
ColorModel(ColorSpace aColorSpace, AlphaChannel aAlphaChannel) :
|
||||
mColorSpace(aColorSpace), mAlphaChannel(aAlphaChannel) {}
|
||||
PRBool operator==(const ColorModel& aOther) const {
|
||||
return mColorSpace == aOther.mColorSpace &&
|
||||
mAlphaChannel == aOther.mAlphaChannel;
|
||||
}
|
||||
ColorSpace mColorSpace;
|
||||
AlphaChannel mAlphaChannel;
|
||||
};
|
||||
|
||||
float GetPrimitiveLength(nsSVGLength2 *aLength) const;
|
||||
|
||||
void GetFilterSubregion(nsIContent *aFilter,
|
||||
nsRect defaultRegion,
|
||||
nsRect *result);
|
||||
nsSVGFilterInstance(nsISVGChildFrame *aTargetFrame,
|
||||
nsIContent* aFilterElement,
|
||||
nsIDOMSVGRect *aTargetBBox,
|
||||
const gfxRect& aFilterRect,
|
||||
const nsIntSize& aFilterSpaceSize,
|
||||
PRUint16 aPrimitiveUnits) :
|
||||
mTargetFrame(aTargetFrame),
|
||||
mFilterElement(aFilterElement),
|
||||
mTargetBBox(aTargetBBox),
|
||||
mFilterRect(aFilterRect),
|
||||
mFilterSpaceSize(aFilterSpaceSize),
|
||||
mSurfaceRect(nsIntPoint(0, 0), aFilterSpaceSize),
|
||||
mPrimitiveUnits(aPrimitiveUnits) {
|
||||
}
|
||||
|
||||
// The area covered by temporary images, in filter space
|
||||
void SetSurfaceRect(const nsIntRect& aRect) { mSurfaceRect = aRect; }
|
||||
|
||||
gfxRect GetFilterRect() const { return mFilterRect; }
|
||||
|
||||
const nsIntSize& GetFilterSpaceSize() { return mFilterSpaceSize; }
|
||||
PRUint32 GetFilterResX() const { return mFilterSpaceSize.width; }
|
||||
PRUint32 GetFilterResY() const { return mFilterSpaceSize.height; }
|
||||
|
||||
const nsIntRect& GetSurfaceRect() const { return mSurfaceRect; }
|
||||
PRInt32 GetSurfaceWidth() const { return mSurfaceRect.width; }
|
||||
PRInt32 GetSurfaceHeight() const { return mSurfaceRect.height; }
|
||||
|
||||
nsresult Render(gfxASurface** aOutput);
|
||||
|
||||
private:
|
||||
typedef nsSVGFE::Image Image;
|
||||
typedef nsSVGFE::ColorModel ColorModel;
|
||||
|
||||
struct PrimitiveInfo {
|
||||
nsSVGFE* mFE;
|
||||
nsIntRect mResultBoundingBox;
|
||||
nsIntRect mResultNeededBox;
|
||||
Image mImage;
|
||||
PRInt32 mImageUsers;
|
||||
|
||||
// Can't use nsAutoTArray here, because these Info objects
|
||||
// live in nsTArrays themselves and nsTArray moves the elements
|
||||
// around in memory, which breaks nsAutoTArray.
|
||||
nsTArray<PrimitiveInfo*> mInputs;
|
||||
|
||||
PrimitiveInfo() : mFE(nsnull), mImageUsers(0) {}
|
||||
};
|
||||
|
||||
class ImageAnalysisEntry : public nsStringHashKey {
|
||||
public:
|
||||
ImageAnalysisEntry(KeyTypePointer aStr) : nsStringHashKey(aStr) { }
|
||||
ImageAnalysisEntry(const ImageAnalysisEntry& toCopy) : nsStringHashKey(toCopy),
|
||||
mInfo(toCopy.mInfo) { }
|
||||
|
||||
PrimitiveInfo* mInfo;
|
||||
};
|
||||
|
||||
nsresult BuildSources();
|
||||
// Build graph of PrimitiveInfo nodes describing filter primitives
|
||||
nsresult BuildPrimitives();
|
||||
// Compute bounding boxes of the filter primitive outputs
|
||||
void ComputeResultBoundingBoxes();
|
||||
// Compute bounding boxes of what we actually *need* from the filter
|
||||
// primitive outputs
|
||||
void ComputeNeededBoxes();
|
||||
nsIntRect ComputeUnionOfAllNeededBoxes();
|
||||
nsresult BuildSourceImages();
|
||||
|
||||
// Allocates an image surface that covers mSurfaceRect (it uses
|
||||
// device offsets so that its origin is positioned at mSurfaceRect.TopLeft()
|
||||
// when using cairo to draw into the surface). The surface is cleared
|
||||
// to transparent black.
|
||||
already_AddRefed<gfxImageSurface> GetImage();
|
||||
already_AddRefed<gfxImageSurface> CreateImage();
|
||||
|
||||
void LookupImage(const nsAString &aName,
|
||||
gfxImageSurface **aImage,
|
||||
nsRect *aRegion,
|
||||
const ColorModel &aColorModel);
|
||||
nsRect LookupImageRegion(const nsAString &aName);
|
||||
ColorModel LookupImageColorModel(const nsAString &aName);
|
||||
void DefineImage(const nsAString &aName,
|
||||
gfxImageSurface *aImage,
|
||||
const nsRect &aRegion,
|
||||
const ColorModel &aColorModel);
|
||||
void GetFilterBox(float *x, float *y, float *width, float *height) const {
|
||||
*x = mFilterX;
|
||||
*y = mFilterY;
|
||||
*width = mFilterWidth;
|
||||
*height = mFilterHeight;
|
||||
void ComputeFilterPrimitiveSubregion(PrimitiveInfo* aInfo);
|
||||
void EnsureColorModel(PrimitiveInfo* aPrimitive,
|
||||
ColorModel aColorModel);
|
||||
|
||||
gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const;
|
||||
void ClipToFilterSpace(nsIntRect* aRect) const
|
||||
{
|
||||
nsIntRect filterSpace(nsIntPoint(0, 0), mFilterSpaceSize);
|
||||
aRect->IntersectRect(*aRect, filterSpace);
|
||||
}
|
||||
void ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfx) const;
|
||||
nsSVGElement* TargetElement() const
|
||||
{
|
||||
nsIFrame* f;
|
||||
CallQueryInterface(mTargetFrame, &f);
|
||||
return static_cast<nsSVGElement*>(f->GetContent());
|
||||
}
|
||||
|
||||
nsSVGFilterInstance(nsSVGElement *aTarget,
|
||||
nsIDOMSVGRect *aTargetBBox,
|
||||
float aFilterX, float aFilterY,
|
||||
float aFilterWidth, float aFilterHeight,
|
||||
PRUint32 aFilterResX, PRUint32 aFilterResY,
|
||||
PRUint16 aPrimitiveUnits) :
|
||||
mTarget(aTarget),
|
||||
mTargetBBox(aTargetBBox),
|
||||
mLastImage(nsnull),
|
||||
mFilterX(aFilterX), mFilterY(aFilterY),
|
||||
mFilterWidth(aFilterWidth), mFilterHeight(aFilterHeight),
|
||||
mFilterResX(aFilterResX), mFilterResY(aFilterResY),
|
||||
mSurfaceRect(0, 0, aFilterResX, aFilterResY),
|
||||
mPrimitiveUnits(aPrimitiveUnits) {
|
||||
mImageDictionary.Init();
|
||||
}
|
||||
|
||||
void SetSurfaceRect(const nsRect& aRect) { mSurfaceRect = aRect; }
|
||||
|
||||
const nsRect& GetSurfaceRect() const { return mSurfaceRect; }
|
||||
PRInt32 GetSurfaceWidth() const { return mSurfaceRect.width; }
|
||||
PRInt32 GetSurfaceHeight() const { return mSurfaceRect.height; }
|
||||
PRInt32 GetSurfaceStride() const { return mSurfaceStride; }
|
||||
|
||||
PRUint32 GetFilterResX() const { return mFilterResX; }
|
||||
PRUint32 GetFilterResY() const { return mFilterResY; }
|
||||
|
||||
private:
|
||||
class ImageEntry {
|
||||
public:
|
||||
ImageEntry(gfxImageSurface *aImage,
|
||||
const nsRect &aRegion,
|
||||
const ColorModel &aColorModel) :
|
||||
mImage(aImage), mRegion(aRegion), mColorModel(aColorModel) {
|
||||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> mImage;
|
||||
nsRect mRegion;
|
||||
ColorModel mColorModel;
|
||||
};
|
||||
|
||||
nsClassHashtable<nsStringHashKey,ImageEntry> mImageDictionary;
|
||||
nsRefPtr<nsSVGElement> mTarget;
|
||||
nsISVGChildFrame* mTargetFrame;
|
||||
nsIContent* mFilterElement;
|
||||
nsCOMPtr<nsIDOMSVGRect> mTargetBBox;
|
||||
ImageEntry *mLastImage;
|
||||
gfxRect mFilterRect;
|
||||
nsIntSize mFilterSpaceSize;
|
||||
nsIntRect mSurfaceRect;
|
||||
PRUint16 mPrimitiveUnits;
|
||||
|
||||
float mFilterX, mFilterY, mFilterWidth, mFilterHeight;
|
||||
PRUint32 mFilterResX, mFilterResY;
|
||||
nsRect mSurfaceRect;
|
||||
PRInt32 mSurfaceStride;
|
||||
PRUint16 mPrimitiveUnits;
|
||||
PrimitiveInfo mSourceColorAlpha;
|
||||
PrimitiveInfo mSourceAlpha;
|
||||
nsTArray<PrimitiveInfo> mPrimitives;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1682,6 +1682,26 @@ nsSVGUtils::SetClipRect(gfxContext *aContext,
|
||||
aContext->SetMatrix(oldMatrix);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGUtils::ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfxRect)
|
||||
{
|
||||
gfxRect r = aGfxRect;
|
||||
r.RoundOut();
|
||||
gfxRect r2(aRect->x, aRect->y, aRect->width, aRect->height);
|
||||
r = r.Intersect(r2);
|
||||
*aRect = nsIntRect(PRInt32(r.X()), PRInt32(r.Y()),
|
||||
PRInt32(r.Width()), PRInt32(r.Height()));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut)
|
||||
{
|
||||
*aOut = nsIntRect(PRInt32(aIn.X()), PRInt32(aIn.Y()),
|
||||
PRInt32(aIn.Width()), PRInt32(aIn.Height()));
|
||||
return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height) == aIn
|
||||
? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
|
||||
{
|
||||
|
@ -429,6 +429,18 @@ public:
|
||||
nsIDOMSVGMatrix *aCTM, float aX, float aY,
|
||||
float aWidth, float aHeight);
|
||||
|
||||
/**
|
||||
* If aIn can be represented exactly using an nsIntRect (i.e. integer-aligned edges and
|
||||
* coordinates in the PRInt32 range) then we set aOut to that rectangle, otherwise
|
||||
* return failure.
|
||||
*/
|
||||
static nsresult GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut);
|
||||
|
||||
/**
|
||||
* Restricts aRect to pixels that intersect aGfxRect.
|
||||
*/
|
||||
static void ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfxRect);
|
||||
|
||||
/* Using group opacity instead of fill or stroke opacity on a
|
||||
* geometry object seems to be a common authoring mistake. If we're
|
||||
* not applying filters and not both stroking and filling, we can
|
||||
|
29
layout/svg/crashtests/429774-1.svg
Normal file
29
layout/svg/crashtests/429774-1.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<svg width="7.5cm" height="5cm" viewBox="0 0 200 120"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<defs>
|
||||
<filter id="MyFilter" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="120">
|
||||
|
||||
<feOffset in="SourceAlpha" result="offset" dx="4" dy="4" y="76"/>
|
||||
|
||||
<feSpecularLighting in="offset" result="specOut"
|
||||
surfaceScale="5" specularConstant=".75" specularExponent="20">
|
||||
<fePointLight x="-5000" y="-10000" z="20000"/>
|
||||
</feSpecularLighting>
|
||||
|
||||
<feComposite in="SourceAlpha" in2="SourceAlpha" result="litPaint"
|
||||
operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
|
||||
|
||||
<feMerge>
|
||||
<feMergeNode in="offset"/>
|
||||
<feMergeNode in="litPaint"/>
|
||||
</feMerge>
|
||||
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<g filter="url(#MyFilter)"/>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 837 B |
@ -51,3 +51,4 @@ load 402408-1.svg
|
||||
load 404677-1.xhtml
|
||||
load 409565-1.xhtml
|
||||
load 409573-1.svg
|
||||
load 429774-1.svg
|
||||
|
Loading…
Reference in New Issue
Block a user