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/. */
6 
7 #include "SVGFilters.h"
8 
9 #include <algorithm>
10 #include "DOMSVGAnimatedNumberList.h"
11 #include "DOMSVGAnimatedLength.h"
12 #include "nsGkAtoms.h"
13 #include "nsCOMPtr.h"
14 #include "nsIFrame.h"
15 #include "nsLayoutUtils.h"
16 #include "SVGAnimatedEnumeration.h"
17 #include "SVGAnimatedNumberPair.h"
18 #include "SVGAnimatedString.h"
19 #include "nsSVGFilterInstance.h"
20 #include "SVGNumberList.h"
21 #include "mozilla/ArrayUtils.h"
22 #include "mozilla/ComputedStyle.h"
23 #include "mozilla/SVGContentUtils.h"
24 #include "nsSVGUtils.h"
25 #include "mozilla/dom/SVGComponentTransferFunctionElement.h"
26 #include "mozilla/dom/SVGElement.h"
27 #include "mozilla/dom/SVGFEDistantLightElement.h"
28 #include "mozilla/dom/SVGFEFuncAElementBinding.h"
29 #include "mozilla/dom/SVGFEFuncBElementBinding.h"
30 #include "mozilla/dom/SVGFEFuncGElementBinding.h"
31 #include "mozilla/dom/SVGFEFuncRElementBinding.h"
32 #include "mozilla/dom/SVGFEPointLightElement.h"
33 #include "mozilla/dom/SVGFESpotLightElement.h"
34 #include "mozilla/dom/SVGFilterElement.h"
35 #include "mozilla/dom/SVGLengthBinding.h"
36 
37 #if defined(XP_WIN)
38 // Prevent Windows redefining LoadImage
39 #  undef LoadImage
40 #endif
41 
42 using namespace mozilla::gfx;
43 
44 namespace mozilla {
45 namespace dom {
46 
47 //--------------------Filter Element Base Class-----------------------
48 
49 SVGElement::LengthInfo SVGFE::sLengthInfo[4] = {
50     {nsGkAtoms::x, 0, SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE,
51      SVGContentUtils::X},
52     {nsGkAtoms::y, 0, SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE,
53      SVGContentUtils::Y},
54     {nsGkAtoms::width, 100, SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE,
55      SVGContentUtils::X},
56     {nsGkAtoms::height, 100, SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE,
57      SVGContentUtils::Y}};
58 
59 //----------------------------------------------------------------------
60 // nsISupports methods
61 
NS_IMPL_ADDREF_INHERITED(SVGFE,SVGFEBase)62 NS_IMPL_ADDREF_INHERITED(SVGFE, SVGFEBase)
63 NS_IMPL_RELEASE_INHERITED(SVGFE, SVGFEBase)
64 
65 NS_INTERFACE_MAP_BEGIN(SVGFE)
66   NS_INTERFACE_MAP_ENTRY_CONCRETE(SVGFE)
67 NS_INTERFACE_MAP_END_INHERITING(SVGFEBase)
68 
69 //----------------------------------------------------------------------
70 // Implementation
71 
72 void SVGFE::GetSourceImageNames(nsTArray<SVGStringInfo>& aSources) {}
73 
OutputIsTainted(const nsTArray<bool> & aInputsAreTainted,nsIPrincipal * aReferencePrincipal)74 bool SVGFE::OutputIsTainted(const nsTArray<bool>& aInputsAreTainted,
75                             nsIPrincipal* aReferencePrincipal) {
76   // This is the default implementation for OutputIsTainted.
77   // Our output is tainted if we have at least one tainted input.
78   for (uint32_t i = 0; i < aInputsAreTainted.Length(); i++) {
79     if (aInputsAreTainted[i]) {
80       return true;
81     }
82   }
83   return false;
84 }
85 
AttributeAffectsRendering(int32_t aNameSpaceID,nsAtom * aAttribute) const86 bool SVGFE::AttributeAffectsRendering(int32_t aNameSpaceID,
87                                       nsAtom* aAttribute) const {
88   return aNameSpaceID == kNameSpaceID_None &&
89          (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
90           aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
91           aAttribute == nsGkAtoms::result);
92 }
93 
X()94 already_AddRefed<DOMSVGAnimatedLength> SVGFE::X() {
95   return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
96 }
97 
Y()98 already_AddRefed<DOMSVGAnimatedLength> SVGFE::Y() {
99   return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
100 }
101 
Width()102 already_AddRefed<DOMSVGAnimatedLength> SVGFE::Width() {
103   return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
104 }
105 
Height()106 already_AddRefed<DOMSVGAnimatedLength> SVGFE::Height() {
107   return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
108 }
109 
Result()110 already_AddRefed<DOMSVGAnimatedString> SVGFE::Result() {
111   return GetResultImageName().ToDOMAnimatedString(this);
112 }
113 
114 //----------------------------------------------------------------------
115 // nsIContent methods
116 
NS_IMETHODIMP_(bool)117 NS_IMETHODIMP_(bool)
118 SVGFE::IsAttributeMapped(const nsAtom* name) const {
119   static const MappedAttributeEntry* const map[] = {sFiltersMap};
120 
121   return FindAttributeDependence(name, map) ||
122          SVGFEBase::IsAttributeMapped(name);
123 }
124 
125 //----------------------------------------------------------------------
126 // SVGElement methods
127 
StyleIsSetToSRGB()128 bool SVGFE::StyleIsSetToSRGB() {
129   nsIFrame* frame = GetPrimaryFrame();
130   if (!frame) return false;
131 
132   ComputedStyle* style = frame->Style();
133   return style->StyleSVG()->mColorInterpolationFilters ==
134          StyleColorInterpolation::Srgb;
135 }
136 
137 /* virtual */
HasValidDimensions() const138 bool SVGFE::HasValidDimensions() const {
139   return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
140           mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
141          (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
142           mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0);
143 }
144 
GetKernelUnitLength(nsSVGFilterInstance * aInstance,SVGAnimatedNumberPair * aKernelUnitLength)145 Size SVGFE::GetKernelUnitLength(nsSVGFilterInstance* aInstance,
146                                 SVGAnimatedNumberPair* aKernelUnitLength) {
147   if (!aKernelUnitLength->IsExplicitlySet()) {
148     return Size(1, 1);
149   }
150 
151   float kernelX = aInstance->GetPrimitiveNumber(
152       SVGContentUtils::X, aKernelUnitLength, SVGAnimatedNumberPair::eFirst);
153   float kernelY = aInstance->GetPrimitiveNumber(
154       SVGContentUtils::Y, aKernelUnitLength, SVGAnimatedNumberPair::eSecond);
155   return Size(kernelX, kernelY);
156 }
157 
GetLengthInfo()158 SVGElement::LengthAttributesInfo SVGFE::GetLengthInfo() {
159   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
160                               ArrayLength(sLengthInfo));
161 }
162 
163 SVGElement::NumberListInfo
164     SVGComponentTransferFunctionElement::sNumberListInfo[1] = {
165         {nsGkAtoms::tableValues}};
166 
167 SVGElement::NumberInfo SVGComponentTransferFunctionElement::sNumberInfo[5] = {
168     {nsGkAtoms::slope, 1, false},
169     {nsGkAtoms::intercept, 0, false},
170     {nsGkAtoms::amplitude, 1, false},
171     {nsGkAtoms::exponent, 1, false},
172     {nsGkAtoms::offset, 0, false}};
173 
174 SVGEnumMapping SVGComponentTransferFunctionElement::sTypeMap[] = {
175     {nsGkAtoms::identity, SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY},
176     {nsGkAtoms::table, SVG_FECOMPONENTTRANSFER_TYPE_TABLE},
177     {nsGkAtoms::discrete, SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE},
178     {nsGkAtoms::linear, SVG_FECOMPONENTTRANSFER_TYPE_LINEAR},
179     {nsGkAtoms::gamma, SVG_FECOMPONENTTRANSFER_TYPE_GAMMA},
180     {nullptr, 0}};
181 
182 SVGElement::EnumInfo SVGComponentTransferFunctionElement::sEnumInfo[1] = {
183     {nsGkAtoms::type, sTypeMap, SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY}};
184 
185 //----------------------------------------------------------------------
186 // nsISupports methods
187 
NS_IMPL_ADDREF_INHERITED(SVGComponentTransferFunctionElement,SVGComponentTransferFunctionElementBase)188 NS_IMPL_ADDREF_INHERITED(SVGComponentTransferFunctionElement,
189                          SVGComponentTransferFunctionElementBase)
190 NS_IMPL_RELEASE_INHERITED(SVGComponentTransferFunctionElement,
191                           SVGComponentTransferFunctionElementBase)
192 
193 NS_INTERFACE_MAP_BEGIN(SVGComponentTransferFunctionElement)
194   NS_INTERFACE_MAP_ENTRY_CONCRETE(SVGComponentTransferFunctionElement)
195 NS_INTERFACE_MAP_END_INHERITING(SVGComponentTransferFunctionElementBase)
196 
197 //----------------------------------------------------------------------
198 // nsFEUnstyledElement methods
199 
200 bool SVGComponentTransferFunctionElement::AttributeAffectsRendering(
201     int32_t aNameSpaceID, nsAtom* aAttribute) const {
202   return aNameSpaceID == kNameSpaceID_None &&
203          (aAttribute == nsGkAtoms::tableValues ||
204           aAttribute == nsGkAtoms::slope ||
205           aAttribute == nsGkAtoms::intercept ||
206           aAttribute == nsGkAtoms::amplitude ||
207           aAttribute == nsGkAtoms::exponent ||
208           aAttribute == nsGkAtoms::offset || aAttribute == nsGkAtoms::type);
209 }
210 
211 //----------------------------------------------------------------------
212 
213 already_AddRefed<DOMSVGAnimatedEnumeration>
Type()214 SVGComponentTransferFunctionElement::Type() {
215   return mEnumAttributes[TYPE].ToDOMAnimatedEnum(this);
216 }
217 
218 already_AddRefed<DOMSVGAnimatedNumberList>
TableValues()219 SVGComponentTransferFunctionElement::TableValues() {
220   return DOMSVGAnimatedNumberList::GetDOMWrapper(
221       &mNumberListAttributes[TABLEVALUES], this, TABLEVALUES);
222 }
223 
224 already_AddRefed<DOMSVGAnimatedNumber>
Slope()225 SVGComponentTransferFunctionElement::Slope() {
226   return mNumberAttributes[SLOPE].ToDOMAnimatedNumber(this);
227 }
228 
229 already_AddRefed<DOMSVGAnimatedNumber>
Intercept()230 SVGComponentTransferFunctionElement::Intercept() {
231   return mNumberAttributes[INTERCEPT].ToDOMAnimatedNumber(this);
232 }
233 
234 already_AddRefed<DOMSVGAnimatedNumber>
Amplitude()235 SVGComponentTransferFunctionElement::Amplitude() {
236   return mNumberAttributes[AMPLITUDE].ToDOMAnimatedNumber(this);
237 }
238 
239 already_AddRefed<DOMSVGAnimatedNumber>
Exponent()240 SVGComponentTransferFunctionElement::Exponent() {
241   return mNumberAttributes[EXPONENT].ToDOMAnimatedNumber(this);
242 }
243 
244 already_AddRefed<DOMSVGAnimatedNumber>
Offset()245 SVGComponentTransferFunctionElement::Offset() {
246   return mNumberAttributes[OFFSET].ToDOMAnimatedNumber(this);
247 }
248 
ComputeAttributes(int32_t aChannel,ComponentTransferAttributes & aAttributes)249 void SVGComponentTransferFunctionElement::ComputeAttributes(
250     int32_t aChannel, ComponentTransferAttributes& aAttributes) {
251   uint32_t type = mEnumAttributes[TYPE].GetAnimValue();
252 
253   float slope, intercept, amplitude, exponent, offset;
254   GetAnimatedNumberValues(&slope, &intercept, &amplitude, &exponent, &offset,
255                           nullptr);
256 
257   const SVGNumberList& tableValues =
258       mNumberListAttributes[TABLEVALUES].GetAnimValue();
259 
260   aAttributes.mTypes[aChannel] = (uint8_t)type;
261   switch (type) {
262     case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR: {
263       aAttributes.mValues[aChannel].SetLength(2);
264       aAttributes.mValues[aChannel][kComponentTransferSlopeIndex] = slope;
265       aAttributes.mValues[aChannel][kComponentTransferInterceptIndex] =
266           intercept;
267       break;
268     }
269     case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA: {
270       aAttributes.mValues[aChannel].SetLength(3);
271       aAttributes.mValues[aChannel][kComponentTransferAmplitudeIndex] =
272           amplitude;
273       aAttributes.mValues[aChannel][kComponentTransferExponentIndex] = exponent;
274       aAttributes.mValues[aChannel][kComponentTransferOffsetIndex] = offset;
275       break;
276     }
277     case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
278     case SVG_FECOMPONENTTRANSFER_TYPE_TABLE: {
279       if (!tableValues.IsEmpty()) {
280         aAttributes.mValues[aChannel].AppendElements(&tableValues[0],
281                                                      tableValues.Length());
282       }
283       break;
284     }
285   }
286 }
287 
288 //----------------------------------------------------------------------
289 // SVGElement methods
290 
291 SVGElement::NumberListAttributesInfo
GetNumberListInfo()292 SVGComponentTransferFunctionElement::GetNumberListInfo() {
293   return NumberListAttributesInfo(mNumberListAttributes, sNumberListInfo,
294                                   ArrayLength(sNumberListInfo));
295 }
296 
297 SVGElement::EnumAttributesInfo
GetEnumInfo()298 SVGComponentTransferFunctionElement::GetEnumInfo() {
299   return EnumAttributesInfo(mEnumAttributes, sEnumInfo, ArrayLength(sEnumInfo));
300 }
301 
302 SVGElement::NumberAttributesInfo
GetNumberInfo()303 SVGComponentTransferFunctionElement::GetNumberInfo() {
304   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
305                               ArrayLength(sNumberInfo));
306 }
307 
308 /* virtual */
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)309 JSObject* SVGFEFuncRElement::WrapNode(JSContext* aCx,
310                                       JS::Handle<JSObject*> aGivenProto) {
311   return SVGFEFuncRElement_Binding::Wrap(aCx, this, aGivenProto);
312 }
313 
314 }  // namespace dom
315 }  // namespace mozilla
316 
317 NS_IMPL_NS_NEW_SVG_ELEMENT(FEFuncR)
318 
319 namespace mozilla {
320 namespace dom {
321 
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncRElement)322 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncRElement)
323 
324 /* virtual */
325 JSObject* SVGFEFuncGElement::WrapNode(JSContext* aCx,
326                                       JS::Handle<JSObject*> aGivenProto) {
327   return SVGFEFuncGElement_Binding::Wrap(aCx, this, aGivenProto);
328 }
329 
330 }  // namespace dom
331 }  // namespace mozilla
332 
333 NS_IMPL_NS_NEW_SVG_ELEMENT(FEFuncG)
334 
335 namespace mozilla {
336 namespace dom {
337 
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncGElement)338 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncGElement)
339 
340 /* virtual */
341 JSObject* SVGFEFuncBElement::WrapNode(JSContext* aCx,
342                                       JS::Handle<JSObject*> aGivenProto) {
343   return SVGFEFuncBElement_Binding::Wrap(aCx, this, aGivenProto);
344 }
345 
346 }  // namespace dom
347 }  // namespace mozilla
348 
349 NS_IMPL_NS_NEW_SVG_ELEMENT(FEFuncB)
350 
351 namespace mozilla {
352 namespace dom {
353 
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncBElement)354 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncBElement)
355 
356 /* virtual */
357 JSObject* SVGFEFuncAElement::WrapNode(JSContext* aCx,
358                                       JS::Handle<JSObject*> aGivenProto) {
359   return SVGFEFuncAElement_Binding::Wrap(aCx, this, aGivenProto);
360 }
361 
362 }  // namespace dom
363 }  // namespace mozilla
364 
365 NS_IMPL_NS_NEW_SVG_ELEMENT(FEFuncA)
366 
367 namespace mozilla {
368 namespace dom {
369 
370 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncAElement)
371 
372 //--------------------------------------------------------------------
373 //
374 SVGElement::NumberInfo SVGFELightingElement::sNumberInfo[4] = {
375     {nsGkAtoms::surfaceScale, 1, false},
376     {nsGkAtoms::diffuseConstant, 1, false},
377     {nsGkAtoms::specularConstant, 1, false},
378     {nsGkAtoms::specularExponent, 1, false}};
379 
380 SVGElement::NumberPairInfo SVGFELightingElement::sNumberPairInfo[1] = {
381     {nsGkAtoms::kernelUnitLength, 0, 0}};
382 
383 SVGElement::StringInfo SVGFELightingElement::sStringInfo[2] = {
384     {nsGkAtoms::result, kNameSpaceID_None, true},
385     {nsGkAtoms::in, kNameSpaceID_None, true}};
386 
387 //----------------------------------------------------------------------
388 // Implementation
389 
NS_IMETHODIMP_(bool)390 NS_IMETHODIMP_(bool)
391 SVGFELightingElement::IsAttributeMapped(const nsAtom* name) const {
392   static const MappedAttributeEntry* const map[] = {sColorMap,
393                                                     sLightingEffectsMap};
394 
395   return FindAttributeDependence(name, map) ||
396          SVGFELightingElementBase::IsAttributeMapped(name);
397 }
398 
GetSourceImageNames(nsTArray<SVGStringInfo> & aSources)399 void SVGFELightingElement::GetSourceImageNames(
400     nsTArray<SVGStringInfo>& aSources) {
401   aSources.AppendElement(SVGStringInfo(&mStringAttributes[IN1], this));
402 }
403 
ComputeLightAttributes(nsSVGFilterInstance * aInstance,nsTArray<float> & aFloatAttributes)404 LightType SVGFELightingElement::ComputeLightAttributes(
405     nsSVGFilterInstance* aInstance, nsTArray<float>& aFloatAttributes) {
406   // find specified light
407   for (nsCOMPtr<nsIContent> child = nsINode::GetFirstChild(); child;
408        child = child->GetNextSibling()) {
409     if (child->IsAnyOfSVGElements(nsGkAtoms::feDistantLight,
410                                   nsGkAtoms::fePointLight,
411                                   nsGkAtoms::feSpotLight)) {
412       return static_cast<SVGFELightElement*>(child.get())
413           ->ComputeLightAttributes(aInstance, aFloatAttributes);
414     }
415   }
416 
417   return LightType::None;
418 }
419 
AddLightingAttributes(mozilla::gfx::DiffuseLightingAttributes * aAttributes,nsSVGFilterInstance * aInstance)420 bool SVGFELightingElement::AddLightingAttributes(
421     mozilla::gfx::DiffuseLightingAttributes* aAttributes,
422     nsSVGFilterInstance* aInstance) {
423   nsIFrame* frame = GetPrimaryFrame();
424   if (!frame) {
425     return false;
426   }
427 
428   const nsStyleSVGReset* styleSVGReset = frame->Style()->StyleSVGReset();
429   sRGBColor color(
430       sRGBColor::FromABGR(styleSVGReset->mLightingColor.CalcColor(frame)));
431   color.a = 1.f;
432   float surfaceScale = mNumberAttributes[SURFACE_SCALE].GetAnimValue();
433   Size kernelUnitLength = GetKernelUnitLength(
434       aInstance, &mNumberPairAttributes[KERNEL_UNIT_LENGTH]);
435 
436   if (kernelUnitLength.width <= 0 || kernelUnitLength.height <= 0) {
437     // According to spec, A negative or zero value is an error. See link below
438     // for details.
439     // https://www.w3.org/TR/SVG/filters.html#feSpecularLightingKernelUnitLengthAttribute
440     return false;
441   }
442 
443   aAttributes->mLightType =
444       ComputeLightAttributes(aInstance, aAttributes->mLightValues);
445   aAttributes->mSurfaceScale = surfaceScale;
446   aAttributes->mKernelUnitLength = kernelUnitLength;
447   aAttributes->mColor = color;
448 
449   return true;
450 }
451 
AttributeAffectsRendering(int32_t aNameSpaceID,nsAtom * aAttribute) const452 bool SVGFELightingElement::AttributeAffectsRendering(int32_t aNameSpaceID,
453                                                      nsAtom* aAttribute) const {
454   return SVGFELightingElementBase::AttributeAffectsRendering(aNameSpaceID,
455                                                              aAttribute) ||
456          (aNameSpaceID == kNameSpaceID_None &&
457           (aAttribute == nsGkAtoms::in ||
458            aAttribute == nsGkAtoms::surfaceScale ||
459            aAttribute == nsGkAtoms::kernelUnitLength));
460 }
461 
462 //----------------------------------------------------------------------
463 // SVGElement methods
464 
GetNumberInfo()465 SVGElement::NumberAttributesInfo SVGFELightingElement::GetNumberInfo() {
466   return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
467                               ArrayLength(sNumberInfo));
468 }
469 
GetNumberPairInfo()470 SVGElement::NumberPairAttributesInfo SVGFELightingElement::GetNumberPairInfo() {
471   return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
472                                   ArrayLength(sNumberPairInfo));
473 }
474 
GetStringInfo()475 SVGElement::StringAttributesInfo SVGFELightingElement::GetStringInfo() {
476   return StringAttributesInfo(mStringAttributes, sStringInfo,
477                               ArrayLength(sStringInfo));
478 }
479 
480 }  // namespace dom
481 }  // namespace mozilla
482