зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
82980add96
Коммит
ed283d3861
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче