Bug 355249 - implement feMorphology.

Patch by amenzie@us.ibm.com, r=tor, sr=roc
This commit is contained in:
tor%cs.brown.edu 2006-11-02 16:30:20 +00:00
parent df572a2205
commit 38a296c7ab
8 changed files with 392 additions and 28 deletions

View File

@ -895,6 +895,7 @@ GK_ATOM(defs, "defs")
GK_ATOM(definition_src, "definition-src")
GK_ATOM(deg, "deg")
GK_ATOM(desc, "desc")
GK_ATOM(dilate, "dilate")
GK_ATOM(direction, "direction")
GK_ATOM(disable, "disable")
GK_ATOM(discrete, "discrete")
@ -902,6 +903,7 @@ GK_ATOM(dominant_baseline, "dominant-baseline")
GK_ATOM(dx, "dx")
GK_ATOM(dy, "dy")
GK_ATOM(ellipse, "ellipse")
GK_ATOM(erode, "erode")
GK_ATOM(ex, "ex")
GK_ATOM(exact, "exact")
GK_ATOM(exponent, "exponent")
@ -997,6 +999,7 @@ GK_ATOM(onSVGUnload, "onSVGUnload")
GK_ATOM(onSVGZoom, "onSVGZoom")
GK_ATOM(onzoom, "onzoom")
GK_ATOM(opacity, "opacity")
GK_ATOM(_operator, "operator")
GK_ATOM(pad, "pad")
GK_ATOM(path, "path")
GK_ATOM(pathLength, "pathLength")
@ -1015,6 +1018,7 @@ GK_ATOM(px, "px")
GK_ATOM(r, "r")
GK_ATOM(rad, "rad")
GK_ATOM(radialGradient, "radialGradient")
GK_ATOM(radius, "radius")
GK_ATOM(reflect, "reflect")
GK_ATOM(refX, "refX")
GK_ATOM(refY, "refY")

View File

@ -191,16 +191,13 @@ protected:
virtual LengthAttributesInfo GetLengthInfo();
virtual NumberAttributesInfo GetNumberInfo();
static nsresult ReportAttributeParseFailure(nsIDocument* aDocument,
nsIAtom* aAttribute,
const nsAString& aValue);
nsCOMPtr<nsICSSStyleRule> mContentStyleRule;
nsAttrAndChildArray mMappedAttributes;
PRPackedBool mSuppressNotification;
private:
static nsresult
ReportAttributeParseFailure(nsIDocument* aDocument,
nsIAtom* aAttribute,
const nsAString& aValue);
};
/**

View File

@ -121,6 +121,8 @@ NS_NewSVGFEMergeElement(nsIContent **aResult, nsINodeInfo *aNodeInfo);
nsresult
NS_NewSVGFEMergeNodeElement(nsIContent **aResult, nsINodeInfo *aNodeInfo);
nsresult
NS_NewSVGFEMorphologyElement(nsIContent **aResult, nsINodeInfo *aNodeInfo);
nsresult
NS_NewSVGFEOffsetElement(nsIContent **aResult, nsINodeInfo *aNodeInfo);
nsresult
NS_NewSVGFEUnimplementedMOZElement(nsIContent **aResult, nsINodeInfo *aNodeInfo);
@ -218,6 +220,8 @@ NS_NewSVGElement(nsIContent** aResult, nsINodeInfo *aNodeInfo)
return NS_NewSVGFEMergeElement(aResult, aNodeInfo);
if (name == nsSVGAtoms::feMergeNode)
return NS_NewSVGFEMergeNodeElement(aResult, aNodeInfo);
if (name == nsSVGAtoms::feMorphology)
return NS_NewSVGFEMorphologyElement(aResult, aNodeInfo);
if (name == nsSVGAtoms::feOffset)
return NS_NewSVGFEOffsetElement(aResult, aNodeInfo);
if (name == nsSVGAtoms::feBlend ||
@ -228,7 +232,6 @@ NS_NewSVGElement(nsIContent** aResult, nsINodeInfo *aNodeInfo)
name == nsSVGAtoms::feDisplacementMap ||
name == nsSVGAtoms::feFlood ||
name == nsSVGAtoms::feImage ||
name == nsSVGAtoms::feMorphology ||
name == nsSVGAtoms::feSpecularLighting ||
name == nsSVGAtoms::feTile ||
name == nsSVGAtoms::feTurbulence)

View File

@ -55,6 +55,7 @@
#include "nsISVGValueUtils.h"
#include "nsSVGFilters.h"
#include "nsSVGUtils.h"
#include "prdtoa.h"
nsSVGElement::LengthInfo nsSVGFE::sLengthInfo[4] =
{
@ -119,6 +120,47 @@ nsSVGFE::DidModifySVGObservable (nsISVGValue* observable,
return nsSVGFEBase::DidModifySVGObservable(observable, aModType);
}
PRBool
nsSVGFE::ScanDualValueAttribute(const nsAString& aValue, nsIAtom* aAttribute,
nsSVGNumber2* aNum1, nsSVGNumber2* aNum2,
NumberInfo* aInfo1, NumberInfo* aInfo2,
nsAttrValue& aResult)
{
float x = 0.0f, y = 0.0f;
char *rest;
PRBool parseError = PR_FALSE;
NS_ConvertUTF16toUTF8 value(aValue);
value.CompressWhitespace(PR_FALSE, PR_TRUE);
const char *str = value.get();
x = NS_STATIC_CAST(float, PR_strtod(str, &rest));
if (str == rest) {
//first value was illformed
parseError = PR_TRUE;
} else {
if (*rest == '\0') {
//second value was not supplied
y = x;
} else {
y = NS_STATIC_CAST(float, PR_strtod(rest, &rest));
if (*rest != '\0') {
//second value was illformed or there was trailing content
parseError = PR_TRUE;
}
}
}
if (parseError) {
ReportAttributeParseFailure(GetOwnerDoc(), aAttribute, aValue);
x = aInfo1->mDefaultValue;
y = aInfo2->mDefaultValue;
return PR_FALSE;
}
aNum1->SetBaseValue(x, this, PR_FALSE);
aNum2->SetBaseValue(y, this, PR_FALSE);
aResult.SetTo(aValue);
return PR_TRUE;
}
//----------------------------------------------------------------------
// nsIDOMSVGFilterPrimitiveStandardAttributes methods
@ -409,9 +451,9 @@ public:
NS_FORWARD_NSIDOMNODE(nsSVGFEGaussianBlurElementBase::)
NS_FORWARD_NSIDOMELEMENT(nsSVGFEGaussianBlurElementBase::)
virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
PRBool aNotify);
virtual PRBool ParseAttribute(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAString& aValue,
nsAttrValue& aResult);
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
protected:
@ -511,27 +553,21 @@ nsSVGFEGaussianBlurElement::SetStdDeviation(float stdDeviationX, float stdDeviat
return NS_OK;
}
nsresult
nsSVGFEGaussianBlurElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
PRBool aNotify)
PRBool
nsSVGFEGaussianBlurElement::ParseAttribute(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAString& aValue,
nsAttrValue& aResult)
{
nsresult rv = nsSVGFEGaussianBlurElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
aValue, aNotify);
if (aName == nsSVGAtoms::stdDeviation && aNameSpaceID == kNameSpaceID_None) {
float stdX = 0.0f, stdY = 0.0f;
char *str;
str = ToNewCString(aValue);
int num = sscanf(str, "%f %f\n", &stdX, &stdY);
if (num == 1)
stdY = stdX;
mNumberAttributes[STD_DEV_X].SetBaseValue(stdX, this, PR_FALSE);
mNumberAttributes[STD_DEV_Y].SetBaseValue(stdY, this, PR_FALSE);
nsMemory::Free(str);
return ScanDualValueAttribute(aValue, nsGkAtoms::stdDeviation,
&mNumberAttributes[STD_DEV_X],
&mNumberAttributes[STD_DEV_Y],
&sNumberInfo[STD_DEV_X],
&sNumberInfo[STD_DEV_Y],
aResult);
}
return rv;
return nsSVGFEGaussianBlurElementBase::ParseAttribute(aNameSpaceID, aName,
aValue, aResult);
}
static void
@ -1899,6 +1935,301 @@ nsSVGFEOffsetElement::GetNumberInfo()
NS_ARRAY_LENGTH(sNumberInfo));
}
//---------------------Morphology------------------------
typedef nsSVGFE nsSVGFEMorphologyElementBase;
class nsSVGFEMorphologyElement : public nsSVGFEMorphologyElementBase,
public nsIDOMSVGFEMorphologyElement,
public nsISVGFilter
{
protected:
friend nsresult NS_NewSVGFEMorphologyElement(nsIContent **aResult,
nsINodeInfo *aNodeInfo);
nsSVGFEMorphologyElement(nsINodeInfo* aNodeInfo);
nsresult Init();
public:
// interfaces:
NS_DECL_ISUPPORTS_INHERITED
// FE Base
NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEMorphologyElementBase::)
// nsISVGFilter
NS_IMETHOD Filter(nsSVGFilterInstance *instance);
NS_IMETHOD GetRequirements(PRUint32 *aRequirements);
// Morphology
NS_DECL_NSIDOMSVGFEMORPHOLOGYELEMENT
NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEMorphologyElementBase::)
NS_FORWARD_NSIDOMNODE(nsSVGFEMorphologyElementBase::)
NS_FORWARD_NSIDOMELEMENT(nsSVGFEMorphologyElementBase::)
virtual PRBool ParseAttribute(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAString& aValue,
nsAttrValue& aResult);
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
protected:
virtual NumberAttributesInfo GetNumberInfo();
enum { RADIUS_X, RADIUS_Y };
nsSVGNumber2 mNumberAttributes[2];
static NumberInfo sNumberInfo[2];
nsCOMPtr<nsIDOMSVGAnimatedString> mIn1;
nsCOMPtr<nsIDOMSVGAnimatedEnumeration> mOperator;
};
nsSVGElement::NumberInfo nsSVGFEMorphologyElement::sNumberInfo[2] =
{
{ &nsGkAtoms::radius, 0 },
{ &nsGkAtoms::radius, 0 }
};
NS_IMPL_NS_NEW_SVG_ELEMENT(FEMorphology)
//----------------------------------------------------------------------
// nsISupports methods
NS_IMPL_ADDREF_INHERITED(nsSVGFEMorphologyElement,nsSVGFEMorphologyElementBase)
NS_IMPL_RELEASE_INHERITED(nsSVGFEMorphologyElement,nsSVGFEMorphologyElementBase)
NS_INTERFACE_MAP_BEGIN(nsSVGFEMorphologyElement)
NS_INTERFACE_MAP_ENTRY(nsIDOMNode)
NS_INTERFACE_MAP_ENTRY(nsIDOMElement)
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGElement)
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGFilterPrimitiveStandardAttributes)
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGFEMorphologyElement)
NS_INTERFACE_MAP_ENTRY(nsISVGFilter)
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(SVGFEMorphologyElement)
NS_INTERFACE_MAP_END_INHERITING(nsSVGFEMorphologyElementBase)
//----------------------------------------------------------------------
// Implementation
nsSVGFEMorphologyElement::nsSVGFEMorphologyElement(nsINodeInfo *aNodeInfo)
: nsSVGFEMorphologyElementBase(aNodeInfo)
{
}
nsresult
nsSVGFEMorphologyElement::Init()
{
nsresult rv = nsSVGFEMorphologyElementBase::Init();
NS_ENSURE_SUCCESS(rv,rv);
static struct nsSVGEnumMapping gOperatorTypes[] = {
{&nsSVGAtoms::erode, nsSVGFEMorphologyElement::SVG_OPERATOR_ERODE},
{&nsSVGAtoms::dilate, nsSVGFEMorphologyElement::SVG_OPERATOR_DILATE},
{nsnull, 0}
};
// Create mapped properties:
// DOM property: operator, #IMPLIED attrib: operator
{
nsCOMPtr<nsISVGEnum> operators;
rv = NS_NewSVGEnum(getter_AddRefs(operators),
nsSVGFEMorphologyElement::SVG_OPERATOR_DILATE,
gOperatorTypes);
NS_ENSURE_SUCCESS(rv,rv);
rv = NS_NewSVGAnimatedEnumeration(getter_AddRefs(mOperator), operators);
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsSVGAtoms::_operator, mOperator);
NS_ENSURE_SUCCESS(rv,rv);
}
// DOM property: in1 , #IMPLIED attrib: in
{
rv = NS_NewSVGAnimatedString(getter_AddRefs(mIn1));
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsSVGAtoms::in, mIn1);
NS_ENSURE_SUCCESS(rv,rv);
}
return rv;
}
//----------------------------------------------------------------------
// nsIDOMNode methods
NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEMorphologyElement)
//----------------------------------------------------------------------
// nsSVGFEMorphologyElement methods
/* readonly attribute nsIDOMSVGAnimatedString in1; */
NS_IMETHODIMP nsSVGFEMorphologyElement::GetIn1(nsIDOMSVGAnimatedString * *aIn)
{
*aIn = mIn1;
NS_IF_ADDREF(*aIn);
return NS_OK;
}
/* readonly attribute nsIDOMSVGAnimatedEnumeration operator; */
NS_IMETHODIMP nsSVGFEMorphologyElement::GetOperator(nsIDOMSVGAnimatedEnumeration * *aOperator)
{
*aOperator = mOperator;
NS_IF_ADDREF(*aOperator);
return NS_OK;
}
/* readonly attribute nsIDOMSVGAnimatedNumber radiusX; */
NS_IMETHODIMP nsSVGFEMorphologyElement::GetRadiusX(nsIDOMSVGAnimatedNumber * *aX)
{
return mNumberAttributes[RADIUS_X].ToDOMAnimatedNumber(aX, this);
}
/* readonly attribute nsIDOMSVGAnimatedNumber radiusY; */
NS_IMETHODIMP nsSVGFEMorphologyElement::GetRadiusY(nsIDOMSVGAnimatedNumber * *aY)
{
return mNumberAttributes[RADIUS_Y].ToDOMAnimatedNumber(aY, this);
}
NS_IMETHODIMP
nsSVGFEMorphologyElement::SetRadius(float rx, float ry)
{
mNumberAttributes[RADIUS_X].SetBaseValue(rx, this, PR_TRUE);
mNumberAttributes[RADIUS_Y].SetBaseValue(ry, this, PR_TRUE);
return NS_OK;
}
PRBool
nsSVGFEMorphologyElement::ParseAttribute(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAString& aValue,
nsAttrValue& aResult)
{
if (aName == nsSVGAtoms::radius && aNameSpaceID == kNameSpaceID_None) {
return ScanDualValueAttribute(aValue, nsGkAtoms::radius,
&mNumberAttributes[RADIUS_X],
&mNumberAttributes[RADIUS_Y],
&sNumberInfo[RADIUS_X],
&sNumberInfo[RADIUS_Y],
aResult);
}
return nsSVGFEMorphologyElementBase::ParseAttribute(aNameSpaceID, aName,
aValue, aResult);
}
NS_IMETHODIMP
nsSVGFEMorphologyElement::GetRequirements(PRUint32 *aRequirements)
{
*aRequirements = CheckStandardNames(mIn1);
return NS_OK;
}
//----------------------------------------------------------------------
// nsSVGElement methods
nsSVGElement::NumberAttributesInfo
nsSVGFEMorphologyElement::GetNumberInfo()
{
return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
NS_ARRAY_LENGTH(sNumberInfo));
}
NS_IMETHODIMP
nsSVGFEMorphologyElement::Filter(nsSVGFilterInstance *instance)
{
nsresult rv;
PRUint8 *sourceData, *targetData;
nsSVGFilterResource fr(instance);
rv = fr.AcquireSourceImage(mIn1, this, &sourceData);
NS_ENSURE_SUCCESS(rv, rv);
rv = fr.AcquireTargetImage(mResult, &targetData);
NS_ENSURE_SUCCESS(rv, rv);
nsRect rect = fr.GetRect();
#ifdef DEBUG_tor
fprintf(stderr, "FILTER MORPH rect: %d,%d %dx%d\n",
rect.x, rect.y, rect.width, rect.height);
#endif
float rx, ry;
nsSVGLength2 val;
GetAnimatedNumberValues(&rx, &ry, nsnull);
val.Init(nsSVGUtils::X, 0xff, rx, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
rx = instance->GetPrimitiveLength(&val);
val.Init(nsSVGUtils::Y, 0xff, ry, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
ry = instance->GetPrimitiveLength(&val);
PRInt32 stride = fr.GetDataStride();
PRUint32 xExt[4], yExt[4]; // X, Y indices of RGBA extrema
PRUint8 extrema[4]; // RGBA magnitude of extrema
PRUint16 op;
mOperator->GetAnimVal(&op);
if (rx == 0 && ry == 0) {
memcpy(targetData, sourceData, rect.height * stride);
return NS_OK;
}
/* Scan the kernel for each pixel to determine max/min RGBA values. Note that
* as we advance in the x direction, each kernel overlaps the previous kernel.
* Thus, we can avoid iterating over the entire kernel by comparing the
* leading edge of the new kernel against the extrema found in the previous
* kernel. We must still scan the entire kernel if the previous extrema do
* not fall within the current kernel or if we are starting a new row.
*/
for (PRInt32 y = rect.y; y < rect.y + rect.height; y++) {
PRUint32 startY = PR_MAX(0, (int)(y - ry));
PRUint32 endY = PR_MIN((int)(y + ry), rect.y + rect.height - 1);
for (PRInt32 x = rect.x; x < rect.x + rect.width; x++) {
PRUint32 startX = PR_MAX(0, (int)(x - rx));
PRUint32 endX = PR_MIN((int)(x + rx), rect.x + rect.width - 1);
PRUint32 targIndex = y * stride + 4 * x;
// We need to scan the entire kernel
if (x == rect.x || xExt[0] <= startX || xExt[1] <= startX ||
xExt[2] <= startX || xExt[3] <= startX) {
PRUint32 i;
for (i = 0; i < 4; i++) {
extrema[i] = sourceData[targIndex + i];
}
for (PRUint32 y1 = startY; y1 <= endY; y1++) {
for (PRUint32 x1 = startX; x1 <= endX; x1++) {
for (i = 0; i < 4; i++) {
PRUint8 pixel = sourceData[y1 * stride + 4 * x1 + i];
if ((extrema[i] >= pixel &&
op == nsSVGFEMorphologyElement::SVG_OPERATOR_ERODE) ||
(extrema[i] <= pixel &&
op == nsSVGFEMorphologyElement::SVG_OPERATOR_DILATE)) {
extrema[i] = pixel;
xExt[i] = x1;
yExt[i] = y1;
}
}
}
}
} else { // We only need to look at the newest column
for (PRUint32 y1 = startY; y1 <= endY; y1++) {
for (PRUint32 i = 0; i < 4; i++) {
PRUint8 pixel = sourceData[y1 * stride + 4 * endX + i];
if ((extrema[i] >= pixel &&
op == nsSVGFEMorphologyElement::SVG_OPERATOR_ERODE) ||
(extrema[i] <= pixel &&
op == nsSVGFEMorphologyElement::SVG_OPERATOR_DILATE)) {
extrema[i] = pixel;
xExt[i] = endX;
yExt[i] = y1;
}
}
}
}
targetData[targIndex ] = extrema[0];
targetData[targIndex+1] = extrema[1];
targetData[targIndex+2] = extrema[2];
targetData[targIndex+3] = extrema[3];
}
}
return NS_OK;
}
//---------------------UnimplementedMOZ------------------------

View File

@ -56,6 +56,10 @@ protected:
nsISVGValue::modificationType aModType);
NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
nsISVGValue::modificationType aModType);
PRBool ScanDualValueAttribute(const nsAString& aValue, nsIAtom* aAttribute,
nsSVGNumber2* aNum1, nsSVGNumber2* aNum2,
NumberInfo* aInfo1, NumberInfo* aInfo2,
nsAttrValue& aResult);
public:
// interfaces:

View File

@ -129,6 +129,21 @@ interface nsIDOMSVGFEOffsetElement : nsIDOMSVGFilterPrimitiveStandardAttributes
readonly attribute nsIDOMSVGAnimatedNumber dy;
};
[scriptable, uuid(16154319-FB5F-4473-B360-5065B6096D33)]
interface nsIDOMSVGFEMorphologyElement : nsIDOMSVGFilterPrimitiveStandardAttributes
{
// Operator Types
const unsigned short SVG_OPERATOR_UNKNOWN = 0;
const unsigned short SVG_OPERATOR_ERODE = 1;
const unsigned short SVG_OPERATOR_DILATE = 2;
readonly attribute nsIDOMSVGAnimatedString in1;
readonly attribute nsIDOMSVGAnimatedNumber radiusX;
readonly attribute nsIDOMSVGAnimatedNumber radiusY;
readonly attribute nsIDOMSVGAnimatedEnumeration operator;
void setRadius ( in float rx, in float ry );
};
[scriptable, uuid(8698c635-26c7-468b-905e-494e8825d56b)]
interface nsIDOMSVGFEUnimplementedMOZElement : nsIDOMSVGFilterPrimitiveStandardAttributes

View File

@ -252,6 +252,7 @@ enum nsDOMClassInfoID {
eDOMClassInfo_SVGFEGaussianBlurElement_id,
eDOMClassInfo_SVGFEMergeElement_id,
eDOMClassInfo_SVGFEMergeNodeElement_id,
eDOMClassInfo_SVGFEMorphologyElement_id,
eDOMClassInfo_SVGFEOffsetElement_id,
eDOMClassInfo_SVGFEUnimplementedMOZElement_id,
eDOMClassInfo_SVGFilterElement_id,

View File

@ -917,6 +917,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
ELEMENT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(SVGFEMergeNodeElement, nsElementSH,
ELEMENT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(SVGFEMorphologyElement, nsElementSH,
ELEMENT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(SVGFEOffsetElement, nsElementSH,
ELEMENT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(SVGFEUnimplementedMOZElement, nsElementSH,
@ -2594,6 +2596,13 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_SVG_ELEMENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(SVGFEMorphologyElement, nsIDOMSVGFEMorphologyElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGFEMorphologyElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGFilterPrimitiveStandardAttributes)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGStylable)
DOM_CLASSINFO_SVG_ELEMENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(SVGFEMergeNodeElement, nsIDOMSVGFEMergeNodeElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGFEMergeNodeElement)
DOM_CLASSINFO_SVG_ELEMENT_MAP_ENTRIES