Bug 1012740 - Change as not to abort in GrallocTextureHostOGL::GrallocTextureHostOGL...
[gecko.git] / layout / svg / nsSVGFilterInstance.cpp
blobb6c6be4d6239d165f3048336d4809ee52a334785
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/. */
6 // Main header first:
7 #include "nsSVGFilterInstance.h"
9 // Keep others in (case-insensitive) order:
10 #include "gfxPlatform.h"
11 #include "gfxUtils.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) :
32 mFilter(aFilter),
33 mTargetFrame(aTargetFrame),
34 mTargetBBox(aTargetBBox),
35 mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale),
36 mFilterSpaceToUserSpaceScale(aFilterSpaceToUserSpaceScale),
37 mInitialized(false) {
39 // Get the filter frame.
40 mFilterFrame = GetFilterFrame();
41 if (!mFilterFrame) {
42 return;
45 // Get the filter element.
46 mFilterElement = mFilterFrame->GetFilterContent();
47 if (!mFilterElement) {
48 NS_NOTREACHED("filter frame should have a related element");
49 return;
52 mPrimitiveUnits =
53 mFilterFrame->GetEnumValue(SVGFilterElement::PRIMITIVEUNITS);
55 nsresult rv = ComputeBounds();
56 if (NS_FAILED(rv)) {
57 return;
60 mInitialized = true;
63 nsresult
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).
77 nsSVGLength2 XYWH[4];
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);
111 return NS_OK;
114 nsSVGFilterFrame*
115 nsSVGFilterInstance::GetFilterFrame()
117 if (mFilter.GetType() != NS_STYLE_FILTER_URL) {
118 // The filter is not an SVG reference filter.
119 return nullptr;
122 nsIURI* url = mFilter.GetURL();
123 if (!url) {
124 NS_NOTREACHED("an nsStyleFilter of type URL should have a non-null URL");
125 return nullptr;
128 // Get the target element to use as a point of reference for looking up the
129 // filter element.
130 nsIContent* targetElement = mTargetFrame->GetContent();
131 if (!targetElement) {
132 // There is no element associated with the target frame.
133 return nullptr;
136 // Look up the filter element by URL.
137 nsReferencedElement filterElement;
138 bool watch = false;
139 filterElement.Reset(targetElement, url, watch);
140 Element* element = filterElement.get();
141 if (!element) {
142 // The URL points to no element.
143 return nullptr;
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.
150 return nullptr;
153 return static_cast<nsSVGFilterFrame*>(frame);
156 float
157 nsSVGFilterInstance::GetPrimitiveNumber(uint8_t aCtxType, float aValue) const
159 nsSVGLength2 val;
160 val.Init(aCtxType, 0xff, aValue,
161 nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
163 float value;
164 if (mPrimitiveUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
165 value = nsSVGUtils::ObjectSpace(mTargetBBox, &val);
166 } else {
167 value = nsSVGUtils::UserSpace(mTargetFrame, &val);
170 switch (aCtxType) {
171 case SVGContentUtils::X:
172 return value * mUserSpaceToFilterSpaceScale.width;
173 case SVGContentUtils::Y:
174 return value * mUserSpaceToFilterSpaceScale.height;
175 case SVGContentUtils::XY:
176 default:
177 return value * SVGContentUtils::ComputeNormalizedHypotenuse(
178 mUserSpaceToFilterSpaceScale.width,
179 mUserSpaceToFilterSpaceScale.height);
183 Point3D
184 nsSVGFilterInstance::ConvertLocation(const Point3D& aPoint) const
186 nsSVGLength2 val[4];
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));
203 gfxRect
204 nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
206 gfxRect filterSpaceRect = aUserSpaceRect;
207 filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
208 mUserSpaceToFilterSpaceScale.height);
209 return filterSpaceRect;
212 gfxRect
213 nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const
215 gfxRect userSpaceRect = aFilterSpaceRect;
216 userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width,
217 mFilterSpaceToUserSpaceScale.height);
218 return userSpaceRect;
221 IntRect
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);
238 } else {
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
257 // in the region.
258 region.RoundOut();
260 return RoundedToInt(region);
263 void
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);
273 } else {
274 aOutInputsAreTainted.AppendElement(aPrimitiveDescrs[inputIndex].IsTainted());
279 static int32_t
280 GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
282 uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length();
283 return !numPrimitiveDescrs ?
284 FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
285 numPrimitiveDescrs - 1;
288 nsresult
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++) {
298 nsAutoString str;
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);
315 } else {
316 bool inputExists = aImageTable.Get(str, &sourceIndex);
317 if (!inputExists)
318 return NS_ERROR_FAILURE;
321 aSourceIndices.AppendElement(sourceIndex);
323 return NS_OK;
326 nsresult
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();
335 child;
336 child = child->GetNextSibling()) {
337 nsRefPtr<nsSVGFE> primitive;
338 CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
339 if (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);
357 if (NS_FAILED(rv)) {
358 return rv;
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);
383 if (i == 0) {
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;
396 nsAutoString str;
397 filter->GetResultImageName().GetAnimValue(str, filter);
398 imageTable.Put(str, primitiveDescrIndex);
401 return NS_OK;