1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "SVGFilterInstance.h"
10 // Keep others in (case-insensitive) order:
11 #include "gfxPlatform.h"
13 #include "mozilla/ISVGDisplayableFrame.h"
14 #include "mozilla/SVGContentUtils.h"
15 #include "mozilla/SVGObserverUtils.h"
16 #include "mozilla/SVGUtils.h"
17 #include "mozilla/dom/HTMLCanvasElement.h"
18 #include "mozilla/dom/SVGLengthBinding.h"
19 #include "mozilla/dom/SVGUnitTypesBinding.h"
20 #include "mozilla/dom/SVGFilterElement.h"
21 #include "SVGFilterFrame.h"
22 #include "FilterSupport.h"
23 #include "gfx2DGlue.h"
25 using namespace mozilla::dom
;
26 using namespace mozilla::dom::SVGUnitTypes_Binding
;
27 using namespace mozilla::gfx
;
31 SVGFilterInstance::SVGFilterInstance(
32 const StyleFilter
& aFilter
, SVGFilterFrame
* aFilterFrame
,
33 nsIContent
* aTargetContent
, const UserSpaceMetrics
& aMetrics
,
34 const gfxRect
& aTargetBBox
,
35 const MatrixScalesDouble
& aUserSpaceToFilterSpaceScale
,
36 gfxRect
& aFilterSpaceBoundsNotSnapped
)
38 mTargetContent(aTargetContent
),
40 mFilterFrame(aFilterFrame
),
41 mTargetBBox(aTargetBBox
),
42 mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale
),
43 mSourceAlphaAvailable(false),
45 // Get the filter element.
46 mFilterElement
= mFilterFrame
->GetFilterContent();
47 if (!mFilterElement
) {
48 MOZ_ASSERT_UNREACHABLE("filter frame should have a related element");
53 mFilterFrame
->GetEnumValue(SVGFilterElement::PRIMITIVEUNITS
);
55 if (!ComputeBounds()) {
58 aFilterSpaceBoundsNotSnapped
= mFilterSpaceBoundsNotSnapped
;
63 bool SVGFilterInstance::ComputeBounds() {
64 // XXX if filterUnits is set (or has defaulted) to objectBoundingBox, we
65 // should send a warning to the error console if the author has used lengths
66 // with units. This is a common mistake and can result in the filter region
67 // being *massive* below (because we ignore the units and interpret the number
68 // as a factor of the bbox width/height). We should also send a warning if the
69 // user uses a number without units (a future SVG spec should really
70 // deprecate that, since it's too confusing for a bare number to be sometimes
71 // interpreted as a fraction of the bounding box and sometimes as user-space
72 // units). So really only percentage values should be used in this case.
74 // Set the user space bounds (i.e. the filter region in user space).
75 SVGAnimatedLength XYWH
[4];
76 static_assert(sizeof(mFilterElement
->mLengthAttributes
) == sizeof(XYWH
),
77 "XYWH size incorrect");
78 memcpy(XYWH
, mFilterElement
->mLengthAttributes
,
79 sizeof(mFilterElement
->mLengthAttributes
));
80 XYWH
[0] = *mFilterFrame
->GetLengthValue(SVGFilterElement::ATTR_X
);
81 XYWH
[1] = *mFilterFrame
->GetLengthValue(SVGFilterElement::ATTR_Y
);
82 XYWH
[2] = *mFilterFrame
->GetLengthValue(SVGFilterElement::ATTR_WIDTH
);
83 XYWH
[3] = *mFilterFrame
->GetLengthValue(SVGFilterElement::ATTR_HEIGHT
);
84 uint16_t filterUnits
=
85 mFilterFrame
->GetEnumValue(SVGFilterElement::FILTERUNITS
);
86 gfxRect userSpaceBounds
=
87 SVGUtils::GetRelativeRect(filterUnits
, XYWH
, mTargetBBox
, mMetrics
);
89 // Transform the user space bounds to filter space, so we
90 // can align them with the pixel boundaries of the offscreen surface.
91 // The offscreen surface has the same scale as filter space.
92 gfxRect filterSpaceBounds
= UserSpaceToFilterSpace(userSpaceBounds
);
93 mFilterSpaceBoundsNotSnapped
= filterSpaceBounds
;
94 filterSpaceBounds
.RoundOut();
95 if (filterSpaceBounds
.width
<= 0 || filterSpaceBounds
.height
<= 0) {
96 // 0 disables rendering, < 0 is error. dispatch error console warning
97 // or error as appropriate.
101 // Set the filter space bounds.
102 if (!gfxUtils::GfxRectToIntRect(filterSpaceBounds
, &mFilterSpaceBounds
)) {
103 // The filter region is way too big if there is float -> int overflow.
110 float SVGFilterInstance::GetPrimitiveNumber(uint8_t aCtxType
,
111 float aValue
) const {
112 SVGAnimatedLength val
;
113 val
.Init(aCtxType
, 0xff, aValue
, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
);
116 if (mPrimitiveUnits
== SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
117 value
= SVGUtils::ObjectSpace(mTargetBBox
, &val
);
119 value
= SVGUtils::UserSpace(mMetrics
, &val
);
123 case SVGContentUtils::X
:
124 return value
* static_cast<float>(mUserSpaceToFilterSpaceScale
.xScale
);
125 case SVGContentUtils::Y
:
126 return value
* static_cast<float>(mUserSpaceToFilterSpaceScale
.yScale
);
127 case SVGContentUtils::XY
:
129 return value
* SVGContentUtils::ComputeNormalizedHypotenuse(
130 mUserSpaceToFilterSpaceScale
.xScale
,
131 mUserSpaceToFilterSpaceScale
.yScale
);
135 Point3D
SVGFilterInstance::ConvertLocation(const Point3D
& aPoint
) const {
136 SVGAnimatedLength val
[4];
137 val
[0].Init(SVGContentUtils::X
, 0xff, aPoint
.x
,
138 SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
);
139 val
[1].Init(SVGContentUtils::Y
, 0xff, aPoint
.y
,
140 SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
);
141 // Dummy width/height values
142 val
[2].Init(SVGContentUtils::X
, 0xff, 0,
143 SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
);
144 val
[3].Init(SVGContentUtils::Y
, 0xff, 0,
145 SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
);
148 SVGUtils::GetRelativeRect(mPrimitiveUnits
, val
, mTargetBBox
, mMetrics
);
149 gfxRect r
= UserSpaceToFilterSpace(feArea
);
150 return Point3D(r
.x
, r
.y
, GetPrimitiveNumber(SVGContentUtils::XY
, aPoint
.z
));
153 gfxRect
SVGFilterInstance::UserSpaceToFilterSpace(
154 const gfxRect
& aUserSpaceRect
) const {
155 gfxRect filterSpaceRect
= aUserSpaceRect
;
156 filterSpaceRect
.Scale(mUserSpaceToFilterSpaceScale
);
157 return filterSpaceRect
;
160 IntRect
SVGFilterInstance::ComputeFilterPrimitiveSubregion(
161 SVGFilterPrimitiveElement
* aFilterElement
,
162 const nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
,
163 const nsTArray
<int32_t>& aInputIndices
) {
164 SVGFilterPrimitiveElement
* fE
= aFilterElement
;
166 IntRect
defaultFilterSubregion(0, 0, 0, 0);
167 if (fE
->SubregionIsUnionOfRegions()) {
168 for (const auto& inputIndex
: aInputIndices
) {
169 bool isStandardInput
=
170 inputIndex
< 0 || inputIndex
== mSourceGraphicIndex
;
171 IntRect inputSubregion
=
172 isStandardInput
? mFilterSpaceBounds
173 : aPrimitiveDescrs
[inputIndex
].PrimitiveSubregion();
175 defaultFilterSubregion
= defaultFilterSubregion
.Union(inputSubregion
);
178 defaultFilterSubregion
= mFilterSpaceBounds
;
181 gfxRect feArea
= SVGUtils::GetRelativeRect(
183 &fE
->mLengthAttributes
[SVGFilterPrimitiveElement::ATTR_X
], mTargetBBox
,
185 Rect region
= ToRect(UserSpaceToFilterSpace(feArea
));
187 if (!fE
->mLengthAttributes
[SVGFilterPrimitiveElement::ATTR_X
]
189 region
.x
= defaultFilterSubregion
.X();
190 if (!fE
->mLengthAttributes
[SVGFilterPrimitiveElement::ATTR_Y
]
192 region
.y
= defaultFilterSubregion
.Y();
193 if (!fE
->mLengthAttributes
[SVGFilterPrimitiveElement::ATTR_WIDTH
]
195 region
.width
= defaultFilterSubregion
.Width();
196 if (!fE
->mLengthAttributes
[SVGFilterPrimitiveElement::ATTR_HEIGHT
]
198 region
.height
= defaultFilterSubregion
.Height();
200 // We currently require filter primitive subregions to be pixel-aligned.
201 // Following the spec, any pixel partially in the region is included
204 return RoundedToInt(region
);
207 void SVGFilterInstance::GetInputsAreTainted(
208 const nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
,
209 const nsTArray
<int32_t>& aInputIndices
, bool aFilterInputIsTainted
,
210 nsTArray
<bool>& aOutInputsAreTainted
) {
211 for (const auto& inputIndex
: aInputIndices
) {
212 if (inputIndex
< 0) {
213 aOutInputsAreTainted
.AppendElement(aFilterInputIsTainted
);
215 aOutInputsAreTainted
.AppendElement(
216 aPrimitiveDescrs
[inputIndex
].IsTainted());
221 static int32_t GetLastResultIndex(
222 const nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
) {
223 uint32_t numPrimitiveDescrs
= aPrimitiveDescrs
.Length();
224 return !numPrimitiveDescrs
225 ? FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic
226 : numPrimitiveDescrs
- 1;
229 int32_t SVGFilterInstance::GetOrCreateSourceAlphaIndex(
230 nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
) {
231 // If the SourceAlpha index has already been determined or created for this
232 // SVG filter, just return it.
233 if (mSourceAlphaAvailable
) {
234 return mSourceAlphaIndex
;
237 // If this is the first filter in the chain, we can just use the
238 // kPrimitiveIndexSourceAlpha keyword to refer to the SourceAlpha of the
240 if (mSourceGraphicIndex
< 0) {
241 mSourceAlphaIndex
= FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha
;
242 mSourceAlphaAvailable
= true;
243 return mSourceAlphaIndex
;
246 // Otherwise, create a primitive description to turn the previous filter's
247 // output into a SourceAlpha input.
248 FilterPrimitiveDescription
descr(AsVariant(ToAlphaAttributes()));
249 descr
.SetInputPrimitive(0, mSourceGraphicIndex
);
251 const FilterPrimitiveDescription
& sourcePrimitiveDescr
=
252 aPrimitiveDescrs
[mSourceGraphicIndex
];
253 descr
.SetPrimitiveSubregion(sourcePrimitiveDescr
.PrimitiveSubregion());
254 descr
.SetIsTainted(sourcePrimitiveDescr
.IsTainted());
256 ColorSpace colorSpace
= sourcePrimitiveDescr
.OutputColorSpace();
257 descr
.SetInputColorSpace(0, colorSpace
);
258 descr
.SetOutputColorSpace(colorSpace
);
260 aPrimitiveDescrs
.AppendElement(std::move(descr
));
261 mSourceAlphaIndex
= aPrimitiveDescrs
.Length() - 1;
262 mSourceAlphaAvailable
= true;
263 return mSourceAlphaIndex
;
266 nsresult
SVGFilterInstance::GetSourceIndices(
267 SVGFilterPrimitiveElement
* aPrimitiveElement
,
268 nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
,
269 const nsTHashMap
<nsStringHashKey
, int32_t>& aImageTable
,
270 nsTArray
<int32_t>& aSourceIndices
) {
271 AutoTArray
<SVGStringInfo
, 2> sources
;
272 aPrimitiveElement
->GetSourceImageNames(sources
);
274 for (const auto& source
: sources
) {
276 source
.mString
->GetAnimValue(str
, source
.mElement
);
278 int32_t sourceIndex
= 0;
279 if (str
.EqualsLiteral("SourceGraphic")) {
280 sourceIndex
= mSourceGraphicIndex
;
281 } else if (str
.EqualsLiteral("SourceAlpha")) {
282 sourceIndex
= GetOrCreateSourceAlphaIndex(aPrimitiveDescrs
);
283 } else if (str
.EqualsLiteral("FillPaint")) {
284 sourceIndex
= FilterPrimitiveDescription::kPrimitiveIndexFillPaint
;
285 } else if (str
.EqualsLiteral("StrokePaint")) {
286 sourceIndex
= FilterPrimitiveDescription::kPrimitiveIndexStrokePaint
;
287 } else if (str
.EqualsLiteral("BackgroundImage") ||
288 str
.EqualsLiteral("BackgroundAlpha")) {
289 return NS_ERROR_NOT_IMPLEMENTED
;
290 } else if (str
.EqualsLiteral("")) {
291 sourceIndex
= GetLastResultIndex(aPrimitiveDescrs
);
293 bool inputExists
= aImageTable
.Get(str
, &sourceIndex
);
295 sourceIndex
= GetLastResultIndex(aPrimitiveDescrs
);
299 aSourceIndices
.AppendElement(sourceIndex
);
304 nsresult
SVGFilterInstance::BuildPrimitives(
305 nsTArray
<FilterPrimitiveDescription
>& aPrimitiveDescrs
,
306 nsTArray
<RefPtr
<SourceSurface
>>& aInputImages
, bool aInputIsTainted
) {
307 mSourceGraphicIndex
= GetLastResultIndex(aPrimitiveDescrs
);
309 // Clip previous filter's output to this filter's filter region.
310 if (mSourceGraphicIndex
>= 0) {
311 FilterPrimitiveDescription
& sourceDescr
=
312 aPrimitiveDescrs
[mSourceGraphicIndex
];
313 sourceDescr
.SetPrimitiveSubregion(
314 sourceDescr
.PrimitiveSubregion().Intersect(mFilterSpaceBounds
));
317 // Get the filter primitive elements.
318 AutoTArray
<RefPtr
<SVGFilterPrimitiveElement
>, 8> primitives
;
319 for (nsIContent
* child
= mFilterElement
->nsINode::GetFirstChild(); child
;
320 child
= child
->GetNextSibling()) {
321 if (auto* primitive
= SVGFilterPrimitiveElement::FromNode(child
)) {
322 primitives
.AppendElement(primitive
);
326 // Maps source image name to source index.
327 nsTHashMap
<nsStringHashKey
, int32_t> imageTable(8);
329 // The principal that we check principals of any loaded images against.
330 nsCOMPtr
<nsIPrincipal
> principal
= mTargetContent
->NodePrincipal();
332 for (uint32_t primitiveElementIndex
= 0;
333 primitiveElementIndex
< primitives
.Length(); ++primitiveElementIndex
) {
334 SVGFilterPrimitiveElement
* filter
= primitives
[primitiveElementIndex
];
336 AutoTArray
<int32_t, 2> sourceIndices
;
338 GetSourceIndices(filter
, aPrimitiveDescrs
, imageTable
, sourceIndices
);
343 IntRect primitiveSubregion
= ComputeFilterPrimitiveSubregion(
344 filter
, aPrimitiveDescrs
, sourceIndices
);
346 AutoTArray
<bool, 8> sourcesAreTainted
;
347 GetInputsAreTainted(aPrimitiveDescrs
, sourceIndices
, aInputIsTainted
,
350 FilterPrimitiveDescription descr
= filter
->GetPrimitiveDescription(
351 this, primitiveSubregion
, sourcesAreTainted
, aInputImages
);
353 descr
.SetIsTainted(filter
->OutputIsTainted(sourcesAreTainted
, principal
));
354 descr
.SetFilterSpaceBounds(mFilterSpaceBounds
);
355 descr
.SetPrimitiveSubregion(
356 primitiveSubregion
.Intersect(descr
.FilterSpaceBounds()));
358 for (uint32_t i
= 0; i
< sourceIndices
.Length(); i
++) {
359 int32_t inputIndex
= sourceIndices
[i
];
360 descr
.SetInputPrimitive(i
, inputIndex
);
362 ColorSpace inputColorSpace
=
363 inputIndex
>= 0 ? aPrimitiveDescrs
[inputIndex
].OutputColorSpace()
364 : ColorSpace(ColorSpace::SRGB
);
366 ColorSpace desiredInputColorSpace
=
367 filter
->GetInputColorSpace(i
, inputColorSpace
);
368 descr
.SetInputColorSpace(i
, desiredInputColorSpace
);
370 // the output color space is whatever in1 is if there is an in1
371 descr
.SetOutputColorSpace(desiredInputColorSpace
);
375 if (sourceIndices
.Length() == 0) {
376 descr
.SetOutputColorSpace(filter
->GetOutputColorSpace());
379 aPrimitiveDescrs
.AppendElement(std::move(descr
));
380 uint32_t primitiveDescrIndex
= aPrimitiveDescrs
.Length() - 1;
383 filter
->GetResultImageName().GetAnimValue(str
, filter
);
384 imageTable
.InsertOrUpdate(str
, primitiveDescrIndex
);
390 } // namespace mozilla