1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsSVGFilterInstance.h"
9 // Keep others in (case-insensitive) order:
10 #include "gfxPlatform.h"
12 #include "nsISVGChildFrame.h"
13 #include "nsRenderingContext.h"
14 #include "mozilla/dom/SVGFilterElement.h"
15 #include "nsReferencedElement.h"
16 #include "nsSVGFilterFrame.h"
17 #include "nsSVGFilterPaintCallback.h"
18 #include "nsSVGUtils.h"
19 #include "SVGContentUtils.h"
20 #include "FilterSupport.h"
21 #include "gfx2DGlue.h"
23 using namespace mozilla
;
24 using namespace mozilla::dom
;
25 using namespace mozilla::gfx
;
27 nsSVGFilterInstance::nsSVGFilterInstance(const nsStyleFilter
& aFilter
,
28 nsIFrame
*aTargetFrame
,
29 const gfxRect
& aTargetBBox
,
30 const gfxSize
& aUserSpaceToFilterSpaceScale
,
31 const gfxSize
& aFilterSpaceToUserSpaceScale
) :
33 mTargetFrame(aTargetFrame
),
34 mTargetBBox(aTargetBBox
),
35 mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale
),
36 mFilterSpaceToUserSpaceScale(aFilterSpaceToUserSpaceScale
),
39 // Get the filter frame.
40 mFilterFrame
= GetFilterFrame();
45 // Get the filter element.
46 mFilterElement
= mFilterFrame
->GetFilterContent();
47 if (!mFilterElement
) {
48 NS_NOTREACHED("filter frame should have a related element");
53 mFilterFrame
->GetEnumValue(SVGFilterElement::PRIMITIVEUNITS
);
55 nsresult rv
= ComputeBounds();
64 nsSVGFilterInstance::ComputeBounds()
66 // XXX if filterUnits is set (or has defaulted) to objectBoundingBox, we
67 // should send a warning to the error console if the author has used lengths
68 // with units. This is a common mistake and can result in the filter region
69 // being *massive* below (because we ignore the units and interpret the number
70 // as a factor of the bbox width/height). We should also send a warning if the
71 // user uses a number without units (a future SVG spec should really
72 // deprecate that, since it's too confusing for a bare number to be sometimes
73 // interpreted as a fraction of the bounding box and sometimes as user-space
74 // units). So really only percentage values should be used in this case.
76 // Set the user space bounds (i.e. the filter region in user space).
78 NS_ABORT_IF_FALSE(sizeof(mFilterElement
->mLengthAttributes
) == sizeof(XYWH
),
79 "XYWH size incorrect");
80 memcpy(XYWH
, mFilterElement
->mLengthAttributes
,
81 sizeof(mFilterElement
->mLengthAttributes
));
82 XYWH
[0] = *mFilterFrame
->GetLengthValue(SVGFilterElement::ATTR_X
);
83 XYWH
[1] = *mFilterFrame
->GetLengthValue(SVGFilterElement::ATTR_Y
);
84 XYWH
[2] = *mFilterFrame
->GetLengthValue(SVGFilterElement::ATTR_WIDTH
);
85 XYWH
[3] = *mFilterFrame
->GetLengthValue(SVGFilterElement::ATTR_HEIGHT
);
86 uint16_t filterUnits
=
87 mFilterFrame
->GetEnumValue(SVGFilterElement::FILTERUNITS
);
88 mUserSpaceBounds
= nsSVGUtils::GetRelativeRect(filterUnits
,
89 XYWH
, mTargetBBox
, mTargetFrame
);
91 // Temporarily transform the user space bounds to filter space, so we
92 // can align them with the pixel boundries of the offscreen surface.
93 // The offscreen surface has the same scale as filter space.
94 mUserSpaceBounds
= UserSpaceToFilterSpace(mUserSpaceBounds
);
95 mUserSpaceBounds
.RoundOut();
96 if (mUserSpaceBounds
.Width() <= 0 || mUserSpaceBounds
.Height() <= 0) {
97 // 0 disables rendering, < 0 is error. dispatch error console warning
98 // or error as appropriate.
99 return NS_ERROR_FAILURE
;
102 // Set the filter space bounds.
103 if (!gfxUtils::GfxRectToIntRect(mUserSpaceBounds
, &mFilterSpaceBounds
)) {
104 // The filter region is way too big if there is float -> int overflow.
105 return NS_ERROR_FAILURE
;
108 // Undo the temporary transformation of the user space bounds.
109 mUserSpaceBounds
= FilterSpaceToUserSpace(mUserSpaceBounds
);
115 nsSVGFilterInstance::GetFilterFrame()
117 if (mFilter
.GetType() != NS_STYLE_FILTER_URL
) {
118 // The filter is not an SVG reference filter.
122 nsIURI
* url
= mFilter
.GetURL();
124 NS_NOTREACHED("an nsStyleFilter of type URL should have a non-null URL");
128 // Get the target element to use as a point of reference for looking up the
130 nsIContent
* targetElement
= mTargetFrame
->GetContent();
131 if (!targetElement
) {
132 // There is no element associated with the target frame.
136 // Look up the filter element by URL.
137 nsReferencedElement filterElement
;
139 filterElement
.Reset(targetElement
, url
, watch
);
140 Element
* element
= filterElement
.get();
142 // The URL points to no element.
146 // Get the frame of the filter element.
147 nsIFrame
* frame
= element
->GetPrimaryFrame();
148 if (frame
->GetType() != nsGkAtoms::svgFilterFrame
) {
149 // The URL points to an element that's not an SVG filter element.
153 return static_cast<nsSVGFilterFrame
*>(frame
);
157 nsSVGFilterInstance::GetPrimitiveNumber(uint8_t aCtxType
, float aValue
) const
160 val
.Init(aCtxType
, 0xff, aValue
,
161 nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER
);
164 if (mPrimitiveUnits
== SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
165 value
= nsSVGUtils::ObjectSpace(mTargetBBox
, &val
);
167 value
= nsSVGUtils::UserSpace(mTargetFrame
, &val
);
171 case SVGContentUtils::X
:
172 return value
* mUserSpaceToFilterSpaceScale
.width
;
173 case SVGContentUtils::Y
:
174 return value
* mUserSpaceToFilterSpaceScale
.height
;
175 case SVGContentUtils::XY
:
177 return value
* SVGContentUtils::ComputeNormalizedHypotenuse(
178 mUserSpaceToFilterSpaceScale
.width
,
179 mUserSpaceToFilterSpaceScale
.height
);
184 nsSVGFilterInstance::ConvertLocation(const Point3D
& aPoint
) const
187 val
[0].Init(SVGContentUtils::X
, 0xff, aPoint
.x
,
188 nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER
);
189 val
[1].Init(SVGContentUtils::Y
, 0xff, aPoint
.y
,
190 nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER
);
191 // Dummy width/height values
192 val
[2].Init(SVGContentUtils::X
, 0xff, 0,
193 nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER
);
194 val
[3].Init(SVGContentUtils::Y
, 0xff, 0,
195 nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER
);
197 gfxRect feArea
= nsSVGUtils::GetRelativeRect(mPrimitiveUnits
,
198 val
, mTargetBBox
, mTargetFrame
);
199 gfxRect r
= UserSpaceToFilterSpace(feArea
);
200 return Point3D(r
.x
, r
.y
, GetPrimitiveNumber(SVGContentUtils::XY
, aPoint
.z
));
204 nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect
& aUserSpaceRect
) const
206 gfxRect filterSpaceRect
= aUserSpaceRect
;
207 filterSpaceRect
.Scale(mUserSpaceToFilterSpaceScale
.width
,
208 mUserSpaceToFilterSpaceScale
.height
);
209 return filterSpaceRect
;
213 nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxRect
& aFilterSpaceRect
) const
215 gfxRect userSpaceRect
= aFilterSpaceRect
;
216 userSpaceRect
.Scale(mFilterSpaceToUserSpaceScale
.width
,
217 mFilterSpaceToUserSpaceScale
.height
);
218 return userSpaceRect
;
222 nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(nsSVGFE
* aFilterElement
,
223 const nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
,
224 const nsTArray
<int32_t>& aInputIndices
)
226 nsSVGFE
* fE
= aFilterElement
;
228 IntRect
defaultFilterSubregion(0,0,0,0);
229 if (fE
->SubregionIsUnionOfRegions()) {
230 for (uint32_t i
= 0; i
< aInputIndices
.Length(); ++i
) {
231 int32_t inputIndex
= aInputIndices
[i
];
232 IntRect inputSubregion
= inputIndex
>= 0 ?
233 aPrimitiveDescrs
[inputIndex
].PrimitiveSubregion() :
234 ToIntRect(mFilterSpaceBounds
);
236 defaultFilterSubregion
= defaultFilterSubregion
.Union(inputSubregion
);
239 defaultFilterSubregion
= ToIntRect(mFilterSpaceBounds
);
242 gfxRect feArea
= nsSVGUtils::GetRelativeRect(mPrimitiveUnits
,
243 &fE
->mLengthAttributes
[nsSVGFE::ATTR_X
], mTargetBBox
, mTargetFrame
);
244 Rect region
= ToRect(UserSpaceToFilterSpace(feArea
));
246 if (!fE
->mLengthAttributes
[nsSVGFE::ATTR_X
].IsExplicitlySet())
247 region
.x
= defaultFilterSubregion
.X();
248 if (!fE
->mLengthAttributes
[nsSVGFE::ATTR_Y
].IsExplicitlySet())
249 region
.y
= defaultFilterSubregion
.Y();
250 if (!fE
->mLengthAttributes
[nsSVGFE::ATTR_WIDTH
].IsExplicitlySet())
251 region
.width
= defaultFilterSubregion
.Width();
252 if (!fE
->mLengthAttributes
[nsSVGFE::ATTR_HEIGHT
].IsExplicitlySet())
253 region
.height
= defaultFilterSubregion
.Height();
255 // We currently require filter primitive subregions to be pixel-aligned.
256 // Following the spec, any pixel partially in the region is included
260 return RoundedToInt(region
);
264 nsSVGFilterInstance::GetInputsAreTainted(const nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
,
265 const nsTArray
<int32_t>& aInputIndices
,
266 nsTArray
<bool>& aOutInputsAreTainted
)
268 for (uint32_t i
= 0; i
< aInputIndices
.Length(); i
++) {
269 int32_t inputIndex
= aInputIndices
[i
];
270 if (inputIndex
< 0) {
271 // SourceGraphic, SourceAlpha, FillPaint and StrokePaint are tainted.
272 aOutInputsAreTainted
.AppendElement(true);
274 aOutInputsAreTainted
.AppendElement(aPrimitiveDescrs
[inputIndex
].IsTainted());
280 GetLastResultIndex(const nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
)
282 uint32_t numPrimitiveDescrs
= aPrimitiveDescrs
.Length();
283 return !numPrimitiveDescrs
?
284 FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic
:
285 numPrimitiveDescrs
- 1;
289 nsSVGFilterInstance::GetSourceIndices(nsSVGFE
* aPrimitiveElement
,
290 const nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
,
291 const nsDataHashtable
<nsStringHashKey
, int32_t>& aImageTable
,
292 nsTArray
<int32_t>& aSourceIndices
)
294 nsAutoTArray
<nsSVGStringInfo
,2> sources
;
295 aPrimitiveElement
->GetSourceImageNames(sources
);
297 for (uint32_t j
= 0; j
< sources
.Length(); j
++) {
299 sources
[j
].mString
->GetAnimValue(str
, sources
[j
].mElement
);
301 int32_t sourceIndex
= 0;
302 if (str
.EqualsLiteral("SourceGraphic")) {
303 sourceIndex
= mSourceGraphicIndex
;
304 } else if (str
.EqualsLiteral("SourceAlpha")) {
305 sourceIndex
= FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha
;
306 } else if (str
.EqualsLiteral("FillPaint")) {
307 sourceIndex
= FilterPrimitiveDescription::kPrimitiveIndexFillPaint
;
308 } else if (str
.EqualsLiteral("StrokePaint")) {
309 sourceIndex
= FilterPrimitiveDescription::kPrimitiveIndexStrokePaint
;
310 } else if (str
.EqualsLiteral("BackgroundImage") ||
311 str
.EqualsLiteral("BackgroundAlpha")) {
312 return NS_ERROR_NOT_IMPLEMENTED
;
313 } else if (str
.EqualsLiteral("")) {
314 sourceIndex
= GetLastResultIndex(aPrimitiveDescrs
);
316 bool inputExists
= aImageTable
.Get(str
, &sourceIndex
);
318 return NS_ERROR_FAILURE
;
321 aSourceIndices
.AppendElement(sourceIndex
);
327 nsSVGFilterInstance::BuildPrimitives(nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
,
328 nsTArray
<mozilla::RefPtr
<SourceSurface
>>& aInputImages
)
330 mSourceGraphicIndex
= GetLastResultIndex(aPrimitiveDescrs
);
332 // Get the filter primitive elements.
333 nsTArray
<nsRefPtr
<nsSVGFE
> > primitives
;
334 for (nsIContent
* child
= mFilterElement
->nsINode::GetFirstChild();
336 child
= child
->GetNextSibling()) {
337 nsRefPtr
<nsSVGFE
> primitive
;
338 CallQueryInterface(child
, (nsSVGFE
**)getter_AddRefs(primitive
));
340 primitives
.AppendElement(primitive
);
344 // Maps source image name to source index.
345 nsDataHashtable
<nsStringHashKey
, int32_t> imageTable(10);
347 // The principal that we check principals of any loaded images against.
348 nsCOMPtr
<nsIPrincipal
> principal
= mTargetFrame
->GetContent()->NodePrincipal();
350 for (uint32_t primitiveElementIndex
= 0;
351 primitiveElementIndex
< primitives
.Length();
352 ++primitiveElementIndex
) {
353 nsSVGFE
* filter
= primitives
[primitiveElementIndex
];
355 nsAutoTArray
<int32_t,2> sourceIndices
;
356 nsresult rv
= GetSourceIndices(filter
, aPrimitiveDescrs
, imageTable
, sourceIndices
);
361 IntRect primitiveSubregion
=
362 ComputeFilterPrimitiveSubregion(filter
, aPrimitiveDescrs
, sourceIndices
);
364 nsTArray
<bool> sourcesAreTainted
;
365 GetInputsAreTainted(aPrimitiveDescrs
, sourceIndices
, sourcesAreTainted
);
367 FilterPrimitiveDescription descr
=
368 filter
->GetPrimitiveDescription(this, primitiveSubregion
, sourcesAreTainted
, aInputImages
);
370 descr
.SetIsTainted(filter
->OutputIsTainted(sourcesAreTainted
, principal
));
371 descr
.SetPrimitiveSubregion(primitiveSubregion
);
373 for (uint32_t i
= 0; i
< sourceIndices
.Length(); i
++) {
374 int32_t inputIndex
= sourceIndices
[i
];
375 descr
.SetInputPrimitive(i
, inputIndex
);
377 ColorSpace inputColorSpace
= inputIndex
>= 0
378 ? aPrimitiveDescrs
[inputIndex
].OutputColorSpace()
379 : ColorSpace(ColorSpace::SRGB
);
381 ColorSpace desiredInputColorSpace
= filter
->GetInputColorSpace(i
, inputColorSpace
);
382 descr
.SetInputColorSpace(i
, desiredInputColorSpace
);
384 // the output color space is whatever in1 is if there is an in1
385 descr
.SetOutputColorSpace(desiredInputColorSpace
);
389 if (sourceIndices
.Length() == 0) {
390 descr
.SetOutputColorSpace(filter
->GetOutputColorSpace());
393 aPrimitiveDescrs
.AppendElement(descr
);
394 uint32_t primitiveDescrIndex
= aPrimitiveDescrs
.Length() - 1;
397 filter
->GetResultImageName().GetAnimValue(str
, filter
);
398 imageTable
.Put(str
, primitiveDescrIndex
);