Bug 414784. Lots of SVG filter fixes plus a framework for analyzing the filter primitive graph, which we use to compute smaller surface sizes than the filter effects region. r=rlongson,sr=pavlov

This commit is contained in:
roc+@cs.cmu.edu 2008-02-17 17:10:14 -08:00
Родитель 82980add96
Коммит ed283d3861
7 изменённых файлов: 1073 добавлений и 603 удалений

Просмотреть файл

@ -153,7 +153,6 @@ FORCE_STATIC_LIB = 1
EXPORTS = \
nsIDOMSVGListener.h \
nsIDOMSVGZoomListener.h \
nsISVGFilter.h \
nsISVGTextContentMetrics.h \
nsISVGValue.h \
nsISVGValueObserver.h \

Просмотреть файл

@ -1,67 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Mozilla SVG project.
*
* The Initial Developer of the Original Code is IBM Corporation.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NS_ISVGFILTER_H__
#define __NS_ISVGFILTER_H__
#define NS_ISVGFILTER_IID \
{ 0xbc1bf81a, 0x9765, 0x43c0, { 0xb3, 0x17, 0xd6, 0x91, 0x66, 0xcd, 0x3a, 0x30 } }
// Bitfields used by nsISVGFilter::GetRequirements()
#define NS_FE_SOURCEGRAPHIC 0x01
#define NS_FE_SOURCEALPHA 0x02
#define NS_FE_BACKGROUNDIMAGE 0x04
#define NS_FE_BACKGROUNDALPHA 0x08
#define NS_FE_FILLPAINT 0x10
#define NS_FE_STROKEPAINT 0x20
class nsSVGFilterInstance;
class nsISVGFilter : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISVGFILTER_IID)
// Perform the filter operation on the images/regions
// specified by 'instance'
NS_IMETHOD Filter(nsSVGFilterInstance *instance) = 0;
// Get the standard image source requirements of this filter.
NS_IMETHOD GetRequirements(PRUint32 *aRequirements) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsISVGFilter, NS_ISVGFILTER_IID)
#endif

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -46,6 +46,10 @@ class nsIDOMSVGAnimatedString;
typedef nsSVGStylableElement nsSVGFEBase;
#define NS_SVG_FE_CID \
{ 0x60483958, 0xd229, 0x4a77, \
{ 0x96, 0xb2, 0x62, 0x3e, 0x69, 0x95, 0x1e, 0x0e } }
class nsSVGFE : public nsSVGFEBase
//, public nsIDOMSVGFilterPrimitiveStandardAttributes
{
@ -60,7 +64,7 @@ protected:
nsRefPtr<gfxImageSurface> mRealTarget;
nsRefPtr<gfxImageSurface> mSource;
nsRefPtr<gfxImageSurface> mTarget;
nsRect mRect;
nsRect mRect; // rect in mSource and mTarget to operate on
PRPackedBool mRescaling;
};
@ -89,10 +93,44 @@ public:
// See http://www.w3.org/TR/SVG/filters.html#FilterPrimitiveSubRegion
virtual PRBool SubregionIsUnionOfRegions() { return PR_TRUE; }
NS_DECLARE_STATIC_IID_ACCESSOR(NS_SVG_FE_CID)
// interfaces:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES
nsIDOMSVGAnimatedString* GetResultImageName() { return mResult; }
// Return a list of all image names used as sources. Default is to
// return no sources.
virtual void GetSourceImageNames(nsTArray<nsIDOMSVGAnimatedString*>* aSources);
// Compute the bounding-box of the filter output. The default is just the
// union of the source bounding-boxes. The caller is
// responsible for clipping this to the filter primitive subregion, so
// 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,
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
// aSourceBBoxes contains the bounding box of what's rendered by
// each source; this function should change those boxes to indicate
// 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;
static nsRect 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);
}
operator nsISupports*() { return static_cast<nsIContent*>(this); }
protected:
virtual PRBool OperatesOnPremultipledAlpha() { return PR_TRUE; }

Просмотреть файл

@ -39,7 +39,6 @@
#include "nsISVGValueUtils.h"
#include "nsSVGMatrix.h"
#include "nsSVGOuterSVGFrame.h"
#include "nsISVGFilter.h"
#include "nsGkAtoms.h"
#include "nsSVGUtils.h"
#include "nsSVGFilterElement.h"
@ -84,6 +83,227 @@ 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<nsIDOMSVGAnimatedString*,2> sources;
filter->GetSourceImageNames(&sources);
for (PRUint32 j=0; j<sources.Length(); ++j) {
nsAutoString str;
sources[j]->GetAnimVal(str);
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);
}
nsAutoString str;
filter->GetResultImageName()->GetAnimVal(str);
ImageAnalysisEntry* entry = imageTable.PutEntry(str);
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)
@ -91,34 +311,6 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
nsCOMPtr<nsIDOMSVGFilterElement> aFilter = do_QueryInterface(mContent);
NS_ASSERTION(aFilter, "Wrong content element (not filter)");
PRUint32 requirements = 0;
PRUint32 count = mContent->GetChildCount();
PRBool filterExists = PR_FALSE;
for (PRUint32 i=0; i<count; ++i) {
nsIContent* child = mContent->GetChildAt(i);
nsCOMPtr<nsISVGFilter> filter = do_QueryInterface(child);
if (filter) {
filterExists = PR_TRUE;
PRUint32 tmp;
filter->GetRequirements(&tmp);
requirements |= tmp;
}
}
if (!filterExists) {
return NS_OK;
}
// check for source requirements that we don't support yet
if (requirements & ~(NS_FE_SOURCEGRAPHIC | NS_FE_SOURCEALPHA)) {
#ifdef DEBUG_tor
if (requirements & ~(NS_FE_SOURCEGRAPHIC | NS_FE_SOURCEALPHA))
fprintf(stderr, "FilterFrame: unimplemented source requirement\n");
#endif
aTarget->PaintSVG(aContext, nsnull);
return NS_OK;
}
nsIFrame *frame;
CallQueryInterface(aTarget, &frame);
@ -145,6 +337,7 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
PRUint16 units =
filter->mEnumAttributes[nsSVGFilterElement::FILTERUNITS].GetAnimValue();
// Compute filter effects region as per spec
if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
if (!bbox) {
aTarget->SetMatrixPropagation(PR_TRUE);
@ -169,6 +362,7 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
PRBool resultOverflows;
gfxIntSize filterRes;
// Compute size of filter buffer
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::filterRes)) {
PRInt32 filterResX, filterResY;
filter->GetAnimatedIntegerValues(&filterResX, &filterResY, nsnull);
@ -210,71 +404,112 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
// paint the target geometry
nsRefPtr<gfxImageSurface> tmpSurface =
new gfxImageSurface(filterRes, gfxASurface::ImageFormatARGB32);
if (!tmpSurface || tmpSurface->CairoStatus()) {
FilterFailCleanup(aContext, aTarget);
return NS_OK;
}
gfxContext tmpContext(tmpSurface);
nsSVGRenderState tmpState(&tmpContext);
tmpContext.SetOperator(gfxContext::OPERATOR_CLEAR);
tmpContext.Paint();
tmpContext.SetOperator(gfxContext::OPERATOR_OVER);
aTarget->PaintSVG(&tmpState, nsnull);
PRUint16 primitiveUnits =
filter->mEnumAttributes[nsSVGFilterElement::PRIMITIVEUNITS].GetAnimValue();
nsSVGFilterInstance instance(target, bbox,
x, y, width, height,
filterRes.width, filterRes.height,
primitiveUnits);
nsSVGFilterInstance::ColorModel
colorModel(nsSVGFilterInstance::ColorModel::SRGB,
nsSVGFilterInstance::ColorModel::PREMULTIPLIED);
if (requirements & NS_FE_SOURCEALPHA) {
nsRefPtr<gfxImageSurface> alpha =
new gfxImageSurface(filterRes, gfxASurface::ImageFormatARGB32);
// Setup instance data
nsSVGFilterInstance instance(target, bbox,
x, y, width, height,
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);
if (!alpha || alpha->CairoStatus()) {
nsresult rv = analysis.SetupGraph(mContent);
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 (!analysis.GetSourceColorAlphaNeeded().IsEmpty() ||
!analysis.GetSourceAlphaNeeded().IsEmpty()) {
// paint the target geometry
nsRefPtr<gfxImageSurface> tmpSurface = instance.GetImage();
if (!tmpSurface) {
FilterFailCleanup(aContext, aTarget);
return NS_OK;
}
PRUint8 *data = tmpSurface->Data();
PRUint8 *alphaData = alpha->Data();
PRUint32 stride = tmpSurface->Stride();
// 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);
for (PRInt32 yy = 0; yy < filterRes.height; yy++)
for (PRInt32 xx = 0; xx < filterRes.width; 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];
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);
}
instance.DefineImage(NS_LITERAL_STRING("SourceAlpha"), alpha,
// 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);
}
// 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);
// Now invoke the components of the filter chain
PRUint32 count = mContent->GetChildCount();
for (PRUint32 k=0; k<count; ++k) {
nsIContent* child = mContent->GetChildAt(k);
nsCOMPtr<nsISVGFilter> filter = do_QueryInterface(child);
nsRefPtr<nsSVGFE> filter;
CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(filter));
if (filter && NS_FAILED(filter->Filter(&instance))) {
FilterFailCleanup(aContext, aTarget);
return NS_OK;
@ -410,7 +645,7 @@ nsSVGFilterFrame::GetType() const
// nsSVGFilterInstance
float
nsSVGFilterInstance::GetPrimitiveLength(nsSVGLength2 *aLength)
nsSVGFilterInstance::GetPrimitiveLength(nsSVGLength2 *aLength) const
{
float value;
if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
@ -446,10 +681,11 @@ nsSVGFilterInstance::GetFilterSubregion(
tmpHeight = &fE->mLengthAttributes[nsSVGFE::HEIGHT];
float x, y, width, height;
if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
x = nsSVGUtils::ObjectSpace(mTargetBBox, tmpX);
y = nsSVGUtils::ObjectSpace(mTargetBBox, tmpY);
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 {
@ -470,6 +706,7 @@ nsSVGFilterInstance::GetFilterSubregion(
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;
@ -480,14 +717,13 @@ nsSVGFilterInstance::GetFilterSubregion(
region.x, region.y, region.width, region.height);
#endif
nsCOMPtr<nsIContent> content = do_QueryInterface(aFilter);
if (!content->HasAttr(kNameSpaceID_None, nsGkAtoms::x))
if (!aFilter->HasAttr(kNameSpaceID_None, nsGkAtoms::x))
region.x = defaultRegion.x;
if (!content->HasAttr(kNameSpaceID_None, nsGkAtoms::y))
if (!aFilter->HasAttr(kNameSpaceID_None, nsGkAtoms::y))
region.y = defaultRegion.y;
if (!content->HasAttr(kNameSpaceID_None, nsGkAtoms::width))
if (!aFilter->HasAttr(kNameSpaceID_None, nsGkAtoms::width))
region.width = defaultRegion.width;
if (!content->HasAttr(kNameSpaceID_None, nsGkAtoms::height))
if (!aFilter->HasAttr(kNameSpaceID_None, nsGkAtoms::height))
region.height = defaultRegion.height;
result->IntersectRect(filter, region);
@ -502,13 +738,16 @@ already_AddRefed<gfxImageSurface>
nsSVGFilterInstance::GetImage()
{
nsRefPtr<gfxImageSurface> surface =
new gfxImageSurface(gfxIntSize(mFilterResX, mFilterResY),
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();
@ -526,42 +765,59 @@ nsSVGFilterInstance::LookupImage(const nsAString &aName,
{
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) {
*aImage = entry->mImage;
NS_ADDREF(*aImage);
*aRegion = entry->mRegion;
if (entry)
return entry->mRegion;
if (aRequiredColorModel == entry->mColorModel)
return;
// convert image to desired format
PRUint8 *data = (*aImage)->Data();
PRInt32 stride = (*aImage)->Stride();
if (entry->mColorModel.mAlphaChannel == ColorModel::PREMULTIPLIED)
nsSVGUtils::UnPremultiplyImageDataAlpha(data, stride, entry->mRegion);
if (aRequiredColorModel.mColorSpace != entry->mColorModel.mColorSpace) {
if (aRequiredColorModel.mColorSpace == ColorModel::LINEAR_RGB)
nsSVGUtils::ConvertImageDataToLinearRGB(data, stride, entry->mRegion);
else
nsSVGUtils::ConvertImageDataFromLinearRGB(data, stride, entry->mRegion);
}
if (aRequiredColorModel.mAlphaChannel == ColorModel::PREMULTIPLIED)
nsSVGUtils::PremultiplyImageDataAlpha(data, stride, entry->mRegion);
entry->mColorModel = aRequiredColorModel;
} else {
*aImage = nsnull;
aRegion->Empty();
}
return nsRect();
}
nsSVGFilterInstance::ColorModel

Просмотреть файл

@ -40,8 +40,9 @@
#include "nsRect.h"
#include "nsSVGContainerFrame.h"
typedef nsSVGContainerFrame nsSVGFilterFrameBase;
class nsSVGFilterInstance;
typedef nsSVGContainerFrame nsSVGFilterFrameBase;
class nsSVGFilterFrame : public nsSVGFilterFrameBase
{
friend nsIFrame*
@ -49,7 +50,7 @@ class nsSVGFilterFrame : public nsSVGFilterFrameBase
protected:
nsSVGFilterFrame(nsStyleContext* aContext) : nsSVGFilterFrameBase(aContext) {}
public:
public:
nsresult FilterPaint(nsSVGRenderState *aContext,
nsISVGChildFrame *aTarget);

Просмотреть файл

@ -66,26 +66,32 @@ public:
mAlphaChannel == aOther.mAlphaChannel;
}
ColorSpace mColorSpace;
PRPackedBool mAlphaChannel;
AlphaChannel mAlphaChannel;
};
float GetPrimitiveLength(nsSVGLength2 *aLength);
float GetPrimitiveLength(nsSVGLength2 *aLength) const;
void GetFilterSubregion(nsIContent *aFilter,
nsRect defaultRegion,
nsRect *result);
// 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 transprent black.
already_AddRefed<gfxImageSurface> GetImage();
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) {
void GetFilterBox(float *x, float *y, float *width, float *height) const {
*x = mFilterX;
*y = mFilterY;
*width = mFilterWidth;
@ -104,9 +110,20 @@ public:
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 {
@ -129,6 +146,8 @@ private:
float mFilterX, mFilterY, mFilterWidth, mFilterHeight;
PRUint32 mFilterResX, mFilterResY;
nsRect mSurfaceRect;
PRInt32 mSurfaceStride;
PRUint16 mPrimitiveUnits;
};