Bug 1039883 - release Tiled layer's gralloc when an application is background r=nical
[gecko.git] / layout / svg / nsSVGFilterInstance.cpp
blobed5eb59488df5c323176d894d0a4c9f750f16d2a
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 bool isStandardInput = inputIndex < 0 || inputIndex == mSourceGraphicIndex;
233 IntRect inputSubregion = isStandardInput ?
234 ToIntRect(mFilterSpaceBounds) :
235 aPrimitiveDescrs[inputIndex].PrimitiveSubregion();
237 defaultFilterSubregion = defaultFilterSubregion.Union(inputSubregion);
239 } else {
240 defaultFilterSubregion = ToIntRect(mFilterSpaceBounds);
243 gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
244 &fE->mLengthAttributes[nsSVGFE::ATTR_X], mTargetBBox, mTargetFrame);
245 Rect region = ToRect(UserSpaceToFilterSpace(feArea));
247 if (!fE->mLengthAttributes[nsSVGFE::ATTR_X].IsExplicitlySet())
248 region.x = defaultFilterSubregion.X();
249 if (!fE->mLengthAttributes[nsSVGFE::ATTR_Y].IsExplicitlySet())
250 region.y = defaultFilterSubregion.Y();
251 if (!fE->mLengthAttributes[nsSVGFE::ATTR_WIDTH].IsExplicitlySet())
252 region.width = defaultFilterSubregion.Width();
253 if (!fE->mLengthAttributes[nsSVGFE::ATTR_HEIGHT].IsExplicitlySet())
254 region.height = defaultFilterSubregion.Height();
256 // We currently require filter primitive subregions to be pixel-aligned.
257 // Following the spec, any pixel partially in the region is included
258 // in the region.
259 region.RoundOut();
260 IntRect regionInt = RoundedToInt(region);
262 // Clip the primitive subregion to this filter's filter region.
263 return regionInt.Intersect(ToIntRect(mFilterSpaceBounds));
266 void
267 nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
268 const nsTArray<int32_t>& aInputIndices,
269 nsTArray<bool>& aOutInputsAreTainted)
271 for (uint32_t i = 0; i < aInputIndices.Length(); i++) {
272 int32_t inputIndex = aInputIndices[i];
273 if (inputIndex < 0) {
274 // SourceGraphic, SourceAlpha, FillPaint and StrokePaint are tainted.
275 aOutInputsAreTainted.AppendElement(true);
276 } else {
277 aOutInputsAreTainted.AppendElement(aPrimitiveDescrs[inputIndex].IsTainted());
282 static int32_t
283 GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
285 uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length();
286 return !numPrimitiveDescrs ?
287 FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
288 numPrimitiveDescrs - 1;
291 nsresult
292 nsSVGFilterInstance::GetSourceIndices(nsSVGFE* aPrimitiveElement,
293 const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
294 const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
295 nsTArray<int32_t>& aSourceIndices)
297 nsAutoTArray<nsSVGStringInfo,2> sources;
298 aPrimitiveElement->GetSourceImageNames(sources);
300 for (uint32_t j = 0; j < sources.Length(); j++) {
301 nsAutoString str;
302 sources[j].mString->GetAnimValue(str, sources[j].mElement);
304 int32_t sourceIndex = 0;
305 if (str.EqualsLiteral("SourceGraphic")) {
306 sourceIndex = mSourceGraphicIndex;
307 } else if (str.EqualsLiteral("SourceAlpha")) {
308 sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha;
309 } else if (str.EqualsLiteral("FillPaint")) {
310 sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexFillPaint;
311 } else if (str.EqualsLiteral("StrokePaint")) {
312 sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexStrokePaint;
313 } else if (str.EqualsLiteral("BackgroundImage") ||
314 str.EqualsLiteral("BackgroundAlpha")) {
315 return NS_ERROR_NOT_IMPLEMENTED;
316 } else if (str.EqualsLiteral("")) {
317 sourceIndex = GetLastResultIndex(aPrimitiveDescrs);
318 } else {
319 bool inputExists = aImageTable.Get(str, &sourceIndex);
320 if (!inputExists)
321 return NS_ERROR_FAILURE;
324 aSourceIndices.AppendElement(sourceIndex);
326 return NS_OK;
329 nsresult
330 nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
331 nsTArray<mozilla::RefPtr<SourceSurface>>& aInputImages)
333 mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs);
335 // Get the filter primitive elements.
336 nsTArray<nsRefPtr<nsSVGFE> > primitives;
337 for (nsIContent* child = mFilterElement->nsINode::GetFirstChild();
338 child;
339 child = child->GetNextSibling()) {
340 nsRefPtr<nsSVGFE> primitive;
341 CallQueryInterface(child, (nsSVGFE**)getter_AddRefs(primitive));
342 if (primitive) {
343 primitives.AppendElement(primitive);
347 // Maps source image name to source index.
348 nsDataHashtable<nsStringHashKey, int32_t> imageTable(10);
350 // The principal that we check principals of any loaded images against.
351 nsCOMPtr<nsIPrincipal> principal = mTargetFrame->GetContent()->NodePrincipal();
353 for (uint32_t primitiveElementIndex = 0;
354 primitiveElementIndex < primitives.Length();
355 ++primitiveElementIndex) {
356 nsSVGFE* filter = primitives[primitiveElementIndex];
358 nsAutoTArray<int32_t,2> sourceIndices;
359 nsresult rv = GetSourceIndices(filter, aPrimitiveDescrs, imageTable, sourceIndices);
360 if (NS_FAILED(rv)) {
361 return rv;
364 IntRect primitiveSubregion =
365 ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices);
367 nsTArray<bool> sourcesAreTainted;
368 GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, sourcesAreTainted);
370 FilterPrimitiveDescription descr =
371 filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages);
373 descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal));
374 descr.SetPrimitiveSubregion(primitiveSubregion);
376 for (uint32_t i = 0; i < sourceIndices.Length(); i++) {
377 int32_t inputIndex = sourceIndices[i];
378 descr.SetInputPrimitive(i, inputIndex);
380 ColorSpace inputColorSpace = inputIndex >= 0
381 ? aPrimitiveDescrs[inputIndex].OutputColorSpace()
382 : ColorSpace(ColorSpace::SRGB);
384 ColorSpace desiredInputColorSpace = filter->GetInputColorSpace(i, inputColorSpace);
385 descr.SetInputColorSpace(i, desiredInputColorSpace);
386 if (i == 0) {
387 // the output color space is whatever in1 is if there is an in1
388 descr.SetOutputColorSpace(desiredInputColorSpace);
392 if (sourceIndices.Length() == 0) {
393 descr.SetOutputColorSpace(filter->GetOutputColorSpace());
396 aPrimitiveDescrs.AppendElement(descr);
397 uint32_t primitiveDescrIndex = aPrimitiveDescrs.Length() - 1;
399 nsAutoString str;
400 filter->GetResultImageName().GetAnimValue(str, filter);
401 imageTable.Put(str, primitiveDescrIndex);
404 return NS_OK;