Bug 948265 - Connect the filter graphs of chained SVG filters. r=roc

This commit is contained in:
Max Vujovic 2014-02-28 16:40:00 -05:00
parent 7f24b975f1
commit 4ea33adaa9
8 changed files with 134 additions and 21 deletions

View File

@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="flood-with-yellow">
<!-- Turn the black rect into a yellow rect. -->
<feFlood x="20" y="20" width="100" height="100" flood-color="#ffff00"/>
</filter>
<filter id="extract-red-channel">
<!-- Turn the yellow rect into a red rect. -->
<feComponentTransfer x="0" y="0" width="120" height="120">
<feFuncR type="identity"/>
<feFuncG type="table" tableValues="0 0"/>
<feFuncB type="table" tableValues="0 0"/>
<feFuncA type="identity"/>
</feComponentTransfer>
</filter>
<filter id="blur">
<!-- Blur the red rect. -->
<feGaussianBlur stdDeviation="3" x="10" y="10" width="120" height="120"/>
</filter>
<filter id="hue-rotate">
<!-- Turn the red rect into a green rect. -->
<feColorMatrix type="hueRotate" values="90"/>
</filter>
<rect x="20" y="20" width="100" height="100" filter="url(#flood-with-yellow) url(#extract-red-channel) url(#blur) url(#hue-rotate)"/>
</svg>

After

Width:  |  Height:  |  Size: 992 B

View File

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg">
<!-- Test multiple SVG filters chained together against a single SVG filter. -->
<filter id="blur-and-hue-rotate">
<!-- Blur the red rect. -->
<feGaussianBlur stdDeviation="3"/>
<!-- Turn the red rect into a green rect. -->
<feColorMatrix type="hueRotate" values="90"/>
</filter>
<rect x="20" y="20" width="100" height="100" fill="#ff0000" filter="url(#blur-and-hue-rotate)"/>
</svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="blur">
<!-- Blur the red rect. -->
<feGaussianBlur stdDeviation="3"/>
</filter>
<filter id="hue-rotate">
<feFlood flood-color="#0000ff"/>
<!-- Turn the red rect into a green rect. feColorMatrix should use the
result of the #blur filter, not feFlood, as its SourceGraphic. -->
<feColorMatrix in="SourceGraphic" type="hueRotate" values="90"/>
</filter>
<rect x="20" y="20" width="100" height="100" fill="#ff0000" filter="url(#blur) url(#hue-rotate)"/>
</svg>

After

Width:  |  Height:  |  Size: 549 B

View File

@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="flood-with-red">
<!-- Turn the black rect into a yellow rect. -->
<feFlood x="20" y="20" width="100" height="100" flood-color="#ffff00"/>
<!-- Turn the yellow rect into a red rect. -->
<feComponentTransfer x="0" y="0" width="120" height="120">
<feFuncR type="identity"/>
<feFuncG type="table" tableValues="0 0"/>
<feFuncB type="table" tableValues="0 0"/>
<feFuncA type="identity"/>
</feComponentTransfer>
</filter>
<filter id="blur-and-hue-rotate">
<!-- Blur the red rect. -->
<feGaussianBlur stdDeviation="3" x="10" y="10" width="120" height="120"/>
<!-- Turn the red rect into a green rect. -->
<feColorMatrix type="hueRotate" values="90"/>
</filter>
<rect x="20" y="20" width="100" height="100" filter="url(#flood-with-red) url(#blur-and-hue-rotate)"/>
</svg>

After

Width:  |  Height:  |  Size: 894 B

View File

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="blur">
<!-- Blur the red rect. -->
<feGaussianBlur stdDeviation="3"/>
</filter>
<filter id="hue-rotate">
<!-- Turn the red rect into a green rect. -->
<feColorMatrix type="hueRotate" values="90"/>
</filter>
<rect x="20" y="20" width="100" height="100" fill="#ff0000" filter="url(#blur) url(#hue-rotate)"/>
</svg>

After

Width:  |  Height:  |  Size: 392 B

View File

@ -104,3 +104,7 @@ fuzzy(1,119) == feDiffuseLighting-1.svg feDiffuseLighting-1-ref.svg
skip-if(d2d) == feSpecularLighting-1.svg feSpecularLighting-1-ref.svg skip-if(d2d) == feSpecularLighting-1.svg feSpecularLighting-1-ref.svg
pref(layout.css.filters.enabled,true) == multiple-svg-filters.svg multiple-svg-filters-ref.svg
pref(layout.css.filters.enabled,true) == multiple-svg-filters-long-chain.svg multiple-svg-filters-ref.svg
pref(layout.css.filters.enabled,true) == multiple-svg-filters-second-uses-SourceGraphic.svg multiple-svg-filters-ref.svg
pref(layout.css.filters.enabled,true) == multiple-svg-filters-with-multiple-primitives.svg multiple-svg-filters-ref.svg

View File

@ -301,14 +301,23 @@ nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<FilterPrimitiveDescripti
} }
} }
static nsresult static int32_t
GetSourceIndices(nsSVGFE* aFilterElement, GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
int32_t aCurrentIndex, {
const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable, uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length();
nsTArray<int32_t>& aSourceIndices) return !numPrimitiveDescrs ?
FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
numPrimitiveDescrs - 1;
}
nsresult
nsSVGFilterInstance::GetSourceIndices(nsSVGFE* aPrimitiveElement,
const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
nsTArray<int32_t>& aSourceIndices)
{ {
nsAutoTArray<nsSVGStringInfo,2> sources; nsAutoTArray<nsSVGStringInfo,2> sources;
aFilterElement->GetSourceImageNames(sources); aPrimitiveElement->GetSourceImageNames(sources);
for (uint32_t j = 0; j < sources.Length(); j++) { for (uint32_t j = 0; j < sources.Length(); j++) {
nsAutoString str; nsAutoString str;
@ -316,7 +325,7 @@ GetSourceIndices(nsSVGFE* aFilterElement,
int32_t sourceIndex = 0; int32_t sourceIndex = 0;
if (str.EqualsLiteral("SourceGraphic")) { if (str.EqualsLiteral("SourceGraphic")) {
sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic; sourceIndex = mSourceGraphicIndex;
} else if (str.EqualsLiteral("SourceAlpha")) { } else if (str.EqualsLiteral("SourceAlpha")) {
sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha; sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha;
} else if (str.EqualsLiteral("FillPaint")) { } else if (str.EqualsLiteral("FillPaint")) {
@ -327,16 +336,13 @@ GetSourceIndices(nsSVGFE* aFilterElement,
str.EqualsLiteral("BackgroundAlpha")) { str.EqualsLiteral("BackgroundAlpha")) {
return NS_ERROR_NOT_IMPLEMENTED; return NS_ERROR_NOT_IMPLEMENTED;
} else if (str.EqualsLiteral("")) { } else if (str.EqualsLiteral("")) {
sourceIndex = aCurrentIndex == 0 ? sourceIndex = GetLastResultIndex(aPrimitiveDescrs);
FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
aCurrentIndex - 1;
} else { } else {
bool inputExists = aImageTable.Get(str, &sourceIndex); bool inputExists = aImageTable.Get(str, &sourceIndex);
if (!inputExists) if (!inputExists)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
MOZ_ASSERT(sourceIndex < aCurrentIndex);
aSourceIndices.AppendElement(sourceIndex); aSourceIndices.AppendElement(sourceIndex);
} }
return NS_OK; return NS_OK;
@ -346,6 +352,9 @@ nsresult
nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
nsTArray<mozilla::RefPtr<SourceSurface>>& aInputImages) nsTArray<mozilla::RefPtr<SourceSurface>>& aInputImages)
{ {
mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs);
// Get the filter primitive elements.
nsTArray<nsRefPtr<nsSVGFE> > primitives; nsTArray<nsRefPtr<nsSVGFE> > primitives;
for (nsIContent* child = mFilterElement->nsINode::GetFirstChild(); for (nsIContent* child = mFilterElement->nsINode::GetFirstChild();
child; child;
@ -363,11 +372,13 @@ nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrim
// The principal that we check principals of any loaded images against. // The principal that we check principals of any loaded images against.
nsCOMPtr<nsIPrincipal> principal = mTargetFrame->GetContent()->NodePrincipal(); nsCOMPtr<nsIPrincipal> principal = mTargetFrame->GetContent()->NodePrincipal();
for (uint32_t i = 0; i < primitives.Length(); ++i) { for (uint32_t primitiveElementIndex = 0;
nsSVGFE* filter = primitives[i]; primitiveElementIndex < primitives.Length();
++primitiveElementIndex) {
nsSVGFE* filter = primitives[primitiveElementIndex];
nsAutoTArray<int32_t,2> sourceIndices; nsAutoTArray<int32_t,2> sourceIndices;
nsresult rv = GetSourceIndices(filter, i, imageTable, sourceIndices); nsresult rv = GetSourceIndices(filter, aPrimitiveDescrs, imageTable, sourceIndices);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -384,14 +395,14 @@ nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrim
descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal)); descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal));
descr.SetPrimitiveSubregion(primitiveSubregion); descr.SetPrimitiveSubregion(primitiveSubregion);
for (uint32_t j = 0; j < sourceIndices.Length(); j++) { for (uint32_t i = 0; i < sourceIndices.Length(); i++) {
int32_t inputIndex = sourceIndices[j]; int32_t inputIndex = sourceIndices[i];
descr.SetInputPrimitive(j, inputIndex); descr.SetInputPrimitive(i, inputIndex);
ColorSpace inputColorSpace = ColorSpace inputColorSpace =
inputIndex < 0 ? SRGB : aPrimitiveDescrs[inputIndex].OutputColorSpace(); inputIndex < 0 ? SRGB : aPrimitiveDescrs[inputIndex].OutputColorSpace();
ColorSpace desiredInputColorSpace = filter->GetInputColorSpace(j, inputColorSpace); ColorSpace desiredInputColorSpace = filter->GetInputColorSpace(i, inputColorSpace);
descr.SetInputColorSpace(j, desiredInputColorSpace); descr.SetInputColorSpace(i, desiredInputColorSpace);
if (j == 0) { if (i == 0) {
// the output color space is whatever in1 is if there is an in1 // the output color space is whatever in1 is if there is an in1
descr.SetOutputColorSpace(desiredInputColorSpace); descr.SetOutputColorSpace(desiredInputColorSpace);
} }
@ -402,10 +413,11 @@ nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrim
} }
aPrimitiveDescrs.AppendElement(descr); aPrimitiveDescrs.AppendElement(descr);
uint32_t primitiveDescrIndex = aPrimitiveDescrs.Length() - 1;
nsAutoString str; nsAutoString str;
filter->GetResultImageName().GetAnimValue(str, filter); filter->GetResultImageName().GetAnimValue(str, filter);
imageTable.Put(str, i); imageTable.Put(str, primitiveDescrIndex);
} }
return NS_OK; return NS_OK;

View File

@ -139,6 +139,18 @@ private:
*/ */
gfxMatrix GetUserSpaceToFrameSpaceInCSSPxTransform() const; gfxMatrix GetUserSpaceToFrameSpaceInCSSPxTransform() const;
/**
* Finds the index in aPrimitiveDescrs of each input to aPrimitiveElement.
* For example, if aPrimitiveElement is:
* <feGaussianBlur in="another-primitive" .../>
* Then, the resulting aSourceIndices will contain the index of the
* FilterPrimitiveDescription representing "another-primitive".
*/
nsresult GetSourceIndices(nsSVGFE* aPrimitiveElement,
const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
nsTArray<int32_t>& aSourceIndices);
/** /**
* The SVG reference filter originally from the style system. * The SVG reference filter originally from the style system.
*/ */
@ -175,6 +187,13 @@ private:
*/ */
uint16_t mPrimitiveUnits; uint16_t mPrimitiveUnits;
/**
* The index of the FilterPrimitiveDescription that this SVG filter should use
* as its SourceGraphic, or the SourceGraphic keyword index if this is the
* first filter in a chain.
*/
int32_t mSourceGraphicIndex;
bool mInitialized; bool mInitialized;
}; };