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 "FilterSupport.h"
8 #include "FilterDescription.h"
9 
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/gfx/Filters.h"
12 #include "mozilla/gfx/Logging.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/PodOperations.h"
15 
16 #include "gfxContext.h"
17 #include "gfxPattern.h"
18 #include "gfxPlatform.h"
19 #include "gfxUtils.h"
20 #include "gfx2DGlue.h"
21 
22 #include "nsMargin.h"
23 
24 // c = n / 255
25 // c <= 0.0031308f ? c * 12.92f : 1.055f * powf(c, 1 / 2.4f) - 0.055f
26 static const float glinearRGBTosRGBMap[256] = {
27     0.000f, 0.050f, 0.085f, 0.111f, 0.132f, 0.150f, 0.166f, 0.181f, 0.194f,
28     0.207f, 0.219f, 0.230f, 0.240f, 0.250f, 0.260f, 0.269f, 0.278f, 0.286f,
29     0.295f, 0.303f, 0.310f, 0.318f, 0.325f, 0.332f, 0.339f, 0.346f, 0.352f,
30     0.359f, 0.365f, 0.371f, 0.378f, 0.383f, 0.389f, 0.395f, 0.401f, 0.406f,
31     0.412f, 0.417f, 0.422f, 0.427f, 0.433f, 0.438f, 0.443f, 0.448f, 0.452f,
32     0.457f, 0.462f, 0.466f, 0.471f, 0.476f, 0.480f, 0.485f, 0.489f, 0.493f,
33     0.498f, 0.502f, 0.506f, 0.510f, 0.514f, 0.518f, 0.522f, 0.526f, 0.530f,
34     0.534f, 0.538f, 0.542f, 0.546f, 0.549f, 0.553f, 0.557f, 0.561f, 0.564f,
35     0.568f, 0.571f, 0.575f, 0.579f, 0.582f, 0.586f, 0.589f, 0.592f, 0.596f,
36     0.599f, 0.603f, 0.606f, 0.609f, 0.613f, 0.616f, 0.619f, 0.622f, 0.625f,
37     0.629f, 0.632f, 0.635f, 0.638f, 0.641f, 0.644f, 0.647f, 0.650f, 0.653f,
38     0.656f, 0.659f, 0.662f, 0.665f, 0.668f, 0.671f, 0.674f, 0.677f, 0.680f,
39     0.683f, 0.685f, 0.688f, 0.691f, 0.694f, 0.697f, 0.699f, 0.702f, 0.705f,
40     0.708f, 0.710f, 0.713f, 0.716f, 0.718f, 0.721f, 0.724f, 0.726f, 0.729f,
41     0.731f, 0.734f, 0.737f, 0.739f, 0.742f, 0.744f, 0.747f, 0.749f, 0.752f,
42     0.754f, 0.757f, 0.759f, 0.762f, 0.764f, 0.767f, 0.769f, 0.772f, 0.774f,
43     0.776f, 0.779f, 0.781f, 0.784f, 0.786f, 0.788f, 0.791f, 0.793f, 0.795f,
44     0.798f, 0.800f, 0.802f, 0.805f, 0.807f, 0.809f, 0.812f, 0.814f, 0.816f,
45     0.818f, 0.821f, 0.823f, 0.825f, 0.827f, 0.829f, 0.832f, 0.834f, 0.836f,
46     0.838f, 0.840f, 0.843f, 0.845f, 0.847f, 0.849f, 0.851f, 0.853f, 0.855f,
47     0.857f, 0.860f, 0.862f, 0.864f, 0.866f, 0.868f, 0.870f, 0.872f, 0.874f,
48     0.876f, 0.878f, 0.880f, 0.882f, 0.884f, 0.886f, 0.888f, 0.890f, 0.892f,
49     0.894f, 0.896f, 0.898f, 0.900f, 0.902f, 0.904f, 0.906f, 0.908f, 0.910f,
50     0.912f, 0.914f, 0.916f, 0.918f, 0.920f, 0.922f, 0.924f, 0.926f, 0.928f,
51     0.930f, 0.931f, 0.933f, 0.935f, 0.937f, 0.939f, 0.941f, 0.943f, 0.945f,
52     0.946f, 0.948f, 0.950f, 0.952f, 0.954f, 0.956f, 0.957f, 0.959f, 0.961f,
53     0.963f, 0.965f, 0.967f, 0.968f, 0.970f, 0.972f, 0.974f, 0.975f, 0.977f,
54     0.979f, 0.981f, 0.983f, 0.984f, 0.986f, 0.988f, 0.990f, 0.991f, 0.993f,
55     0.995f, 0.997f, 0.998f, 1.000f};
56 
57 // c = n / 255
58 // c <= 0.04045f ? c / 12.92f : powf((c + 0.055f) / 1.055f, 2.4f)
59 extern const float gsRGBToLinearRGBMap[256] = {
60     0.000f, 0.000f, 0.001f, 0.001f, 0.001f, 0.002f, 0.002f, 0.002f, 0.002f,
61     0.003f, 0.003f, 0.003f, 0.004f, 0.004f, 0.004f, 0.005f, 0.005f, 0.006f,
62     0.006f, 0.007f, 0.007f, 0.007f, 0.008f, 0.009f, 0.009f, 0.010f, 0.010f,
63     0.011f, 0.012f, 0.012f, 0.013f, 0.014f, 0.014f, 0.015f, 0.016f, 0.017f,
64     0.018f, 0.019f, 0.019f, 0.020f, 0.021f, 0.022f, 0.023f, 0.024f, 0.025f,
65     0.026f, 0.027f, 0.028f, 0.030f, 0.031f, 0.032f, 0.033f, 0.034f, 0.036f,
66     0.037f, 0.038f, 0.040f, 0.041f, 0.042f, 0.044f, 0.045f, 0.047f, 0.048f,
67     0.050f, 0.051f, 0.053f, 0.054f, 0.056f, 0.058f, 0.060f, 0.061f, 0.063f,
68     0.065f, 0.067f, 0.068f, 0.070f, 0.072f, 0.074f, 0.076f, 0.078f, 0.080f,
69     0.082f, 0.084f, 0.087f, 0.089f, 0.091f, 0.093f, 0.095f, 0.098f, 0.100f,
70     0.102f, 0.105f, 0.107f, 0.109f, 0.112f, 0.114f, 0.117f, 0.120f, 0.122f,
71     0.125f, 0.127f, 0.130f, 0.133f, 0.136f, 0.138f, 0.141f, 0.144f, 0.147f,
72     0.150f, 0.153f, 0.156f, 0.159f, 0.162f, 0.165f, 0.168f, 0.171f, 0.175f,
73     0.178f, 0.181f, 0.184f, 0.188f, 0.191f, 0.195f, 0.198f, 0.202f, 0.205f,
74     0.209f, 0.212f, 0.216f, 0.220f, 0.223f, 0.227f, 0.231f, 0.235f, 0.238f,
75     0.242f, 0.246f, 0.250f, 0.254f, 0.258f, 0.262f, 0.266f, 0.270f, 0.275f,
76     0.279f, 0.283f, 0.287f, 0.292f, 0.296f, 0.301f, 0.305f, 0.309f, 0.314f,
77     0.319f, 0.323f, 0.328f, 0.332f, 0.337f, 0.342f, 0.347f, 0.352f, 0.356f,
78     0.361f, 0.366f, 0.371f, 0.376f, 0.381f, 0.386f, 0.392f, 0.397f, 0.402f,
79     0.407f, 0.413f, 0.418f, 0.423f, 0.429f, 0.434f, 0.440f, 0.445f, 0.451f,
80     0.456f, 0.462f, 0.468f, 0.474f, 0.479f, 0.485f, 0.491f, 0.497f, 0.503f,
81     0.509f, 0.515f, 0.521f, 0.527f, 0.533f, 0.539f, 0.546f, 0.552f, 0.558f,
82     0.565f, 0.571f, 0.578f, 0.584f, 0.591f, 0.597f, 0.604f, 0.610f, 0.617f,
83     0.624f, 0.631f, 0.638f, 0.644f, 0.651f, 0.658f, 0.665f, 0.672f, 0.680f,
84     0.687f, 0.694f, 0.701f, 0.708f, 0.716f, 0.723f, 0.730f, 0.738f, 0.745f,
85     0.753f, 0.761f, 0.768f, 0.776f, 0.784f, 0.791f, 0.799f, 0.807f, 0.815f,
86     0.823f, 0.831f, 0.839f, 0.847f, 0.855f, 0.863f, 0.871f, 0.880f, 0.888f,
87     0.896f, 0.905f, 0.913f, 0.922f, 0.930f, 0.939f, 0.947f, 0.956f, 0.965f,
88     0.973f, 0.982f, 0.991f, 1.000f};
89 
90 namespace mozilla {
91 namespace gfx {
92 
93 // Some convenience FilterNode creation functions.
94 
95 namespace FilterWrappers {
96 
Unpremultiply(DrawTarget * aDT,FilterNode * aInput)97 static already_AddRefed<FilterNode> Unpremultiply(DrawTarget* aDT,
98                                                   FilterNode* aInput) {
99   RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::UNPREMULTIPLY);
100   if (filter) {
101     filter->SetInput(IN_UNPREMULTIPLY_IN, aInput);
102     return filter.forget();
103   }
104   return nullptr;
105 }
106 
Premultiply(DrawTarget * aDT,FilterNode * aInput)107 static already_AddRefed<FilterNode> Premultiply(DrawTarget* aDT,
108                                                 FilterNode* aInput) {
109   RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::PREMULTIPLY);
110   if (filter) {
111     filter->SetInput(IN_PREMULTIPLY_IN, aInput);
112     return filter.forget();
113   }
114   return nullptr;
115 }
116 
LinearRGBToSRGB(DrawTarget * aDT,FilterNode * aInput)117 static already_AddRefed<FilterNode> LinearRGBToSRGB(DrawTarget* aDT,
118                                                     FilterNode* aInput) {
119   RefPtr<FilterNode> transfer =
120       aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
121   if (transfer) {
122     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
123     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, glinearRGBTosRGBMap,
124                            256);
125     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
126     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, glinearRGBTosRGBMap,
127                            256);
128     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
129     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, glinearRGBTosRGBMap,
130                            256);
131     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
132     transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
133     return transfer.forget();
134   }
135   return nullptr;
136 }
137 
SRGBToLinearRGB(DrawTarget * aDT,FilterNode * aInput)138 static already_AddRefed<FilterNode> SRGBToLinearRGB(DrawTarget* aDT,
139                                                     FilterNode* aInput) {
140   RefPtr<FilterNode> transfer =
141       aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
142   if (transfer) {
143     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
144     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, gsRGBToLinearRGBMap,
145                            256);
146     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
147     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, gsRGBToLinearRGBMap,
148                            256);
149     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
150     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, gsRGBToLinearRGBMap,
151                            256);
152     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
153     transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
154     return transfer.forget();
155   }
156   return nullptr;
157 }
158 
Crop(DrawTarget * aDT,FilterNode * aInputFilter,const IntRect & aRect)159 static already_AddRefed<FilterNode> Crop(DrawTarget* aDT,
160                                          FilterNode* aInputFilter,
161                                          const IntRect& aRect) {
162   RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CROP);
163   if (filter) {
164     filter->SetAttribute(ATT_CROP_RECT, Rect(aRect));
165     filter->SetInput(IN_CROP_IN, aInputFilter);
166     return filter.forget();
167   }
168   return nullptr;
169 }
170 
Offset(DrawTarget * aDT,FilterNode * aInputFilter,const IntPoint & aOffset)171 static already_AddRefed<FilterNode> Offset(DrawTarget* aDT,
172                                            FilterNode* aInputFilter,
173                                            const IntPoint& aOffset) {
174   RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
175   if (filter) {
176     filter->SetAttribute(ATT_TRANSFORM_MATRIX,
177                          Matrix::Translation(aOffset.x, aOffset.y));
178     filter->SetInput(IN_TRANSFORM_IN, aInputFilter);
179     return filter.forget();
180   }
181   return nullptr;
182 }
183 
GaussianBlur(DrawTarget * aDT,FilterNode * aInputFilter,const Size & aStdDeviation)184 static already_AddRefed<FilterNode> GaussianBlur(DrawTarget* aDT,
185                                                  FilterNode* aInputFilter,
186                                                  const Size& aStdDeviation) {
187   float stdX = float(std::min(aStdDeviation.width, kMaxStdDeviation));
188   float stdY = float(std::min(aStdDeviation.height, kMaxStdDeviation));
189   if (stdX == stdY) {
190     RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::GAUSSIAN_BLUR);
191     if (filter) {
192       filter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, stdX);
193       filter->SetInput(IN_GAUSSIAN_BLUR_IN, aInputFilter);
194       return filter.forget();
195     }
196     return nullptr;
197   }
198   RefPtr<FilterNode> filterH = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
199   RefPtr<FilterNode> filterV = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
200   if (filterH && filterV) {
201     filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION,
202                           (uint32_t)BLUR_DIRECTION_X);
203     filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdX);
204     filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION,
205                           (uint32_t)BLUR_DIRECTION_Y);
206     filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdY);
207     filterH->SetInput(IN_DIRECTIONAL_BLUR_IN, aInputFilter);
208     filterV->SetInput(IN_DIRECTIONAL_BLUR_IN, filterH);
209     return filterV.forget();
210   }
211   return nullptr;
212 }
213 
Clear(DrawTarget * aDT)214 already_AddRefed<FilterNode> Clear(DrawTarget* aDT) {
215   RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
216   if (filter) {
217     filter->SetAttribute(ATT_FLOOD_COLOR, DeviceColor());
218     return filter.forget();
219   }
220   return nullptr;
221 }
222 
ForSurface(DrawTarget * aDT,SourceSurface * aSurface,const IntPoint & aSurfacePosition)223 already_AddRefed<FilterNode> ForSurface(DrawTarget* aDT,
224                                         SourceSurface* aSurface,
225                                         const IntPoint& aSurfacePosition) {
226   RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
227   if (filter) {
228     filter->SetAttribute(
229         ATT_TRANSFORM_MATRIX,
230         Matrix::Translation(aSurfacePosition.x, aSurfacePosition.y));
231     filter->SetInput(IN_TRANSFORM_IN, aSurface);
232     return filter.forget();
233   }
234   return nullptr;
235 }
236 
ToAlpha(DrawTarget * aDT,FilterNode * aInput)237 static already_AddRefed<FilterNode> ToAlpha(DrawTarget* aDT,
238                                             FilterNode* aInput) {
239   float zero = 0.0f;
240   RefPtr<FilterNode> transfer =
241       aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
242   if (transfer) {
243     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
244     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, &zero, 1);
245     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
246     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, &zero, 1);
247     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
248     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, &zero, 1);
249     transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
250     transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
251     return transfer.forget();
252   }
253   return nullptr;
254 }
255 
256 }  // namespace FilterWrappers
257 
258 // A class that wraps a FilterNode and handles conversion between different
259 // color models. Create FilterCachedColorModels with your original filter and
260 // the color model that this filter outputs in natively, and then call
261 // ->ForColorModel(colorModel) in order to get a FilterNode which outputs to
262 // the specified colorModel.
263 // Internally, this is achieved by wrapping the original FilterNode with
264 // conversion FilterNodes. These filter nodes are cached in such a way that no
265 // repeated or back-and-forth conversions happen.
266 class FilterCachedColorModels {
267  public:
268   NS_INLINE_DECL_REFCOUNTING(FilterCachedColorModels)
269   // aFilter can be null. In that case, ForColorModel will return a non-null
270   // completely transparent filter for all color models.
271   FilterCachedColorModels(DrawTarget* aDT, FilterNode* aFilter,
272                           ColorModel aOriginalColorModel);
273 
274   // Get a FilterNode for the specified color model, guaranteed to be non-null.
275   already_AddRefed<FilterNode> ForColorModel(ColorModel aColorModel);
276 
OriginalAlphaModel() const277   AlphaModel OriginalAlphaModel() const {
278     return mOriginalColorModel.mAlphaModel;
279   }
280 
281  private:
282   // Create the required FilterNode that will be cached by ForColorModel.
283   already_AddRefed<FilterNode> WrapForColorModel(ColorModel aColorModel);
284 
285   RefPtr<DrawTarget> mDT;
286   ColorModel mOriginalColorModel;
287 
288   // This array is indexed by ColorModel::ToIndex.
289   RefPtr<FilterNode> mFilterForColorModel[4];
290 
291   ~FilterCachedColorModels() = default;
292 };
293 
FilterCachedColorModels(DrawTarget * aDT,FilterNode * aFilter,ColorModel aOriginalColorModel)294 FilterCachedColorModels::FilterCachedColorModels(DrawTarget* aDT,
295                                                  FilterNode* aFilter,
296                                                  ColorModel aOriginalColorModel)
297     : mDT(aDT), mOriginalColorModel(aOriginalColorModel) {
298   if (aFilter) {
299     mFilterForColorModel[aOriginalColorModel.ToIndex()] = aFilter;
300   } else {
301     RefPtr<FilterNode> clear = FilterWrappers::Clear(aDT);
302     mFilterForColorModel[0] = clear;
303     mFilterForColorModel[1] = clear;
304     mFilterForColorModel[2] = clear;
305     mFilterForColorModel[3] = clear;
306   }
307 }
308 
ForColorModel(ColorModel aColorModel)309 already_AddRefed<FilterNode> FilterCachedColorModels::ForColorModel(
310     ColorModel aColorModel) {
311   if (aColorModel == mOriginalColorModel) {
312     // Make sure to not call WrapForColorModel if our original filter node was
313     // null, because then we'd get an infinite recursion.
314     RefPtr<FilterNode> filter =
315         mFilterForColorModel[mOriginalColorModel.ToIndex()];
316     return filter.forget();
317   }
318 
319   if (!mFilterForColorModel[aColorModel.ToIndex()]) {
320     mFilterForColorModel[aColorModel.ToIndex()] =
321         WrapForColorModel(aColorModel);
322   }
323   RefPtr<FilterNode> filter(mFilterForColorModel[aColorModel.ToIndex()]);
324   return filter.forget();
325 }
326 
WrapForColorModel(ColorModel aColorModel)327 already_AddRefed<FilterNode> FilterCachedColorModels::WrapForColorModel(
328     ColorModel aColorModel) {
329   // Convert one aspect at a time and recurse.
330   // Conversions between premultiplied / unpremultiplied color channels for the
331   // same color space can happen directly.
332   // Conversions between different color spaces can only happen on
333   // unpremultiplied color channels.
334 
335   if (aColorModel.mAlphaModel == AlphaModel::Premultiplied) {
336     RefPtr<FilterNode> unpre = ForColorModel(
337         ColorModel(aColorModel.mColorSpace, AlphaModel::Unpremultiplied));
338     return FilterWrappers::Premultiply(mDT, unpre);
339   }
340 
341   MOZ_ASSERT(aColorModel.mAlphaModel == AlphaModel::Unpremultiplied);
342   if (aColorModel.mColorSpace == mOriginalColorModel.mColorSpace) {
343     RefPtr<FilterNode> premultiplied = ForColorModel(
344         ColorModel(aColorModel.mColorSpace, AlphaModel::Premultiplied));
345     return FilterWrappers::Unpremultiply(mDT, premultiplied);
346   }
347 
348   RefPtr<FilterNode> unpremultipliedOriginal = ForColorModel(
349       ColorModel(mOriginalColorModel.mColorSpace, AlphaModel::Unpremultiplied));
350   if (aColorModel.mColorSpace == ColorSpace::LinearRGB) {
351     return FilterWrappers::SRGBToLinearRGB(mDT, unpremultipliedOriginal);
352   }
353   return FilterWrappers::LinearRGBToSRGB(mDT, unpremultipliedOriginal);
354 }
355 
356 static const float identityMatrix[] = {1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
357                                        0, 0, 1, 0, 0, 0, 0, 0, 1, 0};
358 
359 // When aAmount == 0, the identity matrix is returned.
360 // When aAmount == 1, aToMatrix is returned.
361 // When aAmount > 1, an exaggerated version of aToMatrix is returned. This can
362 // be useful in certain cases, such as producing a color matrix to oversaturate
363 // an image.
364 //
365 // This function is a shortcut of a full matrix addition and a scalar multiply,
366 // and it assumes that the following elements in aToMatrix are 0 and 1:
367 //   x x x 0 0
368 //   x x x 0 0
369 //   x x x 0 0
370 //   0 0 0 1 0
InterpolateFromIdentityMatrix(const float aToMatrix[20],float aAmount,float aOutMatrix[20])371 static void InterpolateFromIdentityMatrix(const float aToMatrix[20],
372                                           float aAmount, float aOutMatrix[20]) {
373   PodCopy(aOutMatrix, identityMatrix, 20);
374 
375   float oneMinusAmount = 1 - aAmount;
376 
377   aOutMatrix[0] = aAmount * aToMatrix[0] + oneMinusAmount;
378   aOutMatrix[1] = aAmount * aToMatrix[1];
379   aOutMatrix[2] = aAmount * aToMatrix[2];
380 
381   aOutMatrix[5] = aAmount * aToMatrix[5];
382   aOutMatrix[6] = aAmount * aToMatrix[6] + oneMinusAmount;
383   aOutMatrix[7] = aAmount * aToMatrix[7];
384 
385   aOutMatrix[10] = aAmount * aToMatrix[10];
386   aOutMatrix[11] = aAmount * aToMatrix[11];
387   aOutMatrix[12] = aAmount * aToMatrix[12] + oneMinusAmount;
388 }
389 
390 // Create a 4x5 color matrix for the different ways to specify color matrices
391 // in SVG.
ComputeColorMatrix(const ColorMatrixAttributes & aMatrixAttributes,float aOutMatrix[20])392 bool ComputeColorMatrix(const ColorMatrixAttributes& aMatrixAttributes,
393                         float aOutMatrix[20]) {
394   // Luminance coefficients.
395   static const float lumR = 0.2126f;
396   static const float lumG = 0.7152f;
397   static const float lumB = 0.0722f;
398 
399   static const float oneMinusLumR = 1 - lumR;
400   static const float oneMinusLumG = 1 - lumG;
401   static const float oneMinusLumB = 1 - lumB;
402 
403   static const float luminanceToAlphaMatrix[] = {
404       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, lumR, lumG, lumB, 0, 0};
405 
406   static const float saturateMatrix[] = {
407       lumR, lumG, lumB, 0, 0, lumR, lumG, lumB, 0, 0,
408       lumR, lumG, lumB, 0, 0, 0,    0,    0,    1, 0};
409 
410   static const float sepiaMatrix[] = {
411       0.393f, 0.769f, 0.189f, 0, 0, 0.349f, 0.686f, 0.168f, 0, 0,
412       0.272f, 0.534f, 0.131f, 0, 0, 0,      0,      0,      1, 0};
413 
414   // Hue rotate specific coefficients.
415   static const float hueRotateR = 0.143f;
416   static const float hueRotateG = 0.140f;
417   static const float hueRotateB = 0.283f;
418 
419   switch (aMatrixAttributes.mType) {
420     case SVG_FECOLORMATRIX_TYPE_MATRIX: {
421       if (aMatrixAttributes.mValues.Length() != 20) {
422         return false;
423       }
424 
425       PodCopy(aOutMatrix, aMatrixAttributes.mValues.Elements(), 20);
426       break;
427     }
428 
429     case SVG_FECOLORMATRIX_TYPE_SATURATE: {
430       if (aMatrixAttributes.mValues.Length() != 1) {
431         return false;
432       }
433 
434       float s = aMatrixAttributes.mValues[0];
435 
436       if (s < 0) {
437         return false;
438       }
439 
440       InterpolateFromIdentityMatrix(saturateMatrix, 1 - s, aOutMatrix);
441       break;
442     }
443 
444     case SVG_FECOLORMATRIX_TYPE_HUE_ROTATE: {
445       if (aMatrixAttributes.mValues.Length() != 1) {
446         return false;
447       }
448 
449       PodCopy(aOutMatrix, identityMatrix, 20);
450 
451       float hueRotateValue = aMatrixAttributes.mValues[0];
452 
453       float c = static_cast<float>(cos(hueRotateValue * M_PI / 180));
454       float s = static_cast<float>(sin(hueRotateValue * M_PI / 180));
455 
456       aOutMatrix[0] = lumR + oneMinusLumR * c - lumR * s;
457       aOutMatrix[1] = lumG - lumG * c - lumG * s;
458       aOutMatrix[2] = lumB - lumB * c + oneMinusLumB * s;
459 
460       aOutMatrix[5] = lumR - lumR * c + hueRotateR * s;
461       aOutMatrix[6] = lumG + oneMinusLumG * c + hueRotateG * s;
462       aOutMatrix[7] = lumB - lumB * c - hueRotateB * s;
463 
464       aOutMatrix[10] = lumR - lumR * c - oneMinusLumR * s;
465       aOutMatrix[11] = lumG - lumG * c + lumG * s;
466       aOutMatrix[12] = lumB + oneMinusLumB * c + lumB * s;
467 
468       break;
469     }
470 
471     case SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA: {
472       PodCopy(aOutMatrix, luminanceToAlphaMatrix, 20);
473       break;
474     }
475 
476     case SVG_FECOLORMATRIX_TYPE_SEPIA: {
477       if (aMatrixAttributes.mValues.Length() != 1) {
478         return false;
479       }
480 
481       float amount = aMatrixAttributes.mValues[0];
482 
483       if (amount < 0 || amount > 1) {
484         return false;
485       }
486 
487       InterpolateFromIdentityMatrix(sepiaMatrix, amount, aOutMatrix);
488       break;
489     }
490 
491     default: {
492       return false;
493     }
494   }
495 
496   return !ArrayEqual(aOutMatrix, identityMatrix, 20);
497 }
498 
DisableAllTransfers(FilterNode * aTransferFilterNode)499 static void DisableAllTransfers(FilterNode* aTransferFilterNode) {
500   aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_R, true);
501   aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_G, true);
502   aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_B, true);
503   aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_A, true);
504 }
505 
506 // Called for one channel at a time.
507 // This function creates the required FilterNodes on demand and tries to
508 // merge conversions of different channels into the same FilterNode if
509 // possible.
510 // There's a mismatch between the way SVG and the Moz2D API handle transfer
511 // functions: In SVG, it's possible to specify a different transfer function
512 // type for each color channel, but in Moz2D, a given transfer function type
513 // applies to all color channels.
514 //
515 //  @param aFunctionAttributes The attributes of the transfer function for this
516 //                             channel.
517 //  @param aChannel The color channel that this function applies to, where
518 //                  0 = red, 1 = green, 2 = blue, 3 = alpha
519 //  @param aDT The DrawTarget that the FilterNodes should be created for.
520 //  @param aTableTransfer Existing FilterNode holders (which may still be
521 //                        null) that the resulting FilterNodes from this
522 //                        function will be stored in.
523 //
ConvertComponentTransferFunctionToFilter(const ComponentTransferAttributes & aFunctionAttributes,int32_t aInChannel,int32_t aOutChannel,DrawTarget * aDT,RefPtr<FilterNode> & aTableTransfer,RefPtr<FilterNode> & aDiscreteTransfer,RefPtr<FilterNode> & aLinearTransfer,RefPtr<FilterNode> & aGammaTransfer)524 static void ConvertComponentTransferFunctionToFilter(
525     const ComponentTransferAttributes& aFunctionAttributes, int32_t aInChannel,
526     int32_t aOutChannel, DrawTarget* aDT, RefPtr<FilterNode>& aTableTransfer,
527     RefPtr<FilterNode>& aDiscreteTransfer, RefPtr<FilterNode>& aLinearTransfer,
528     RefPtr<FilterNode>& aGammaTransfer) {
529   static const TransferAtts disableAtt[4] = {
530       ATT_TRANSFER_DISABLE_R, ATT_TRANSFER_DISABLE_G, ATT_TRANSFER_DISABLE_B,
531       ATT_TRANSFER_DISABLE_A};
532 
533   RefPtr<FilterNode> filter;
534 
535   uint32_t type = aFunctionAttributes.mTypes[aInChannel];
536 
537   switch (type) {
538     case SVG_FECOMPONENTTRANSFER_TYPE_TABLE: {
539       const nsTArray<float>& tableValues =
540           aFunctionAttributes.mValues[aInChannel];
541       if (tableValues.Length() < 2) return;
542 
543       if (!aTableTransfer) {
544         aTableTransfer = aDT->CreateFilter(FilterType::TABLE_TRANSFER);
545         if (!aTableTransfer) {
546           return;
547         }
548         DisableAllTransfers(aTableTransfer);
549       }
550       filter = aTableTransfer;
551       static const TableTransferAtts tableAtt[4] = {
552           ATT_TABLE_TRANSFER_TABLE_R, ATT_TABLE_TRANSFER_TABLE_G,
553           ATT_TABLE_TRANSFER_TABLE_B, ATT_TABLE_TRANSFER_TABLE_A};
554       filter->SetAttribute(disableAtt[aOutChannel], false);
555       filter->SetAttribute(tableAtt[aOutChannel], &tableValues[0],
556                            tableValues.Length());
557       break;
558     }
559 
560     case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE: {
561       const nsTArray<float>& tableValues =
562           aFunctionAttributes.mValues[aInChannel];
563       if (tableValues.Length() < 1) return;
564 
565       if (!aDiscreteTransfer) {
566         aDiscreteTransfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
567         if (!aDiscreteTransfer) {
568           return;
569         }
570         DisableAllTransfers(aDiscreteTransfer);
571       }
572       filter = aDiscreteTransfer;
573       static const DiscreteTransferAtts tableAtt[4] = {
574           ATT_DISCRETE_TRANSFER_TABLE_R, ATT_DISCRETE_TRANSFER_TABLE_G,
575           ATT_DISCRETE_TRANSFER_TABLE_B, ATT_DISCRETE_TRANSFER_TABLE_A};
576       filter->SetAttribute(disableAtt[aOutChannel], false);
577       filter->SetAttribute(tableAtt[aOutChannel], &tableValues[0],
578                            tableValues.Length());
579 
580       break;
581     }
582 
583     case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR: {
584       static const LinearTransferAtts slopeAtt[4] = {
585           ATT_LINEAR_TRANSFER_SLOPE_R, ATT_LINEAR_TRANSFER_SLOPE_G,
586           ATT_LINEAR_TRANSFER_SLOPE_B, ATT_LINEAR_TRANSFER_SLOPE_A};
587       static const LinearTransferAtts interceptAtt[4] = {
588           ATT_LINEAR_TRANSFER_INTERCEPT_R, ATT_LINEAR_TRANSFER_INTERCEPT_G,
589           ATT_LINEAR_TRANSFER_INTERCEPT_B, ATT_LINEAR_TRANSFER_INTERCEPT_A};
590       if (!aLinearTransfer) {
591         aLinearTransfer = aDT->CreateFilter(FilterType::LINEAR_TRANSFER);
592         if (!aLinearTransfer) {
593           return;
594         }
595         DisableAllTransfers(aLinearTransfer);
596       }
597       filter = aLinearTransfer;
598       filter->SetAttribute(disableAtt[aOutChannel], false);
599       const nsTArray<float>& slopeIntercept =
600           aFunctionAttributes.mValues[aInChannel];
601       float slope = slopeIntercept[kComponentTransferSlopeIndex];
602       float intercept = slopeIntercept[kComponentTransferInterceptIndex];
603       filter->SetAttribute(slopeAtt[aOutChannel], slope);
604       filter->SetAttribute(interceptAtt[aOutChannel], intercept);
605       break;
606     }
607 
608     case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA: {
609       static const GammaTransferAtts amplitudeAtt[4] = {
610           ATT_GAMMA_TRANSFER_AMPLITUDE_R, ATT_GAMMA_TRANSFER_AMPLITUDE_G,
611           ATT_GAMMA_TRANSFER_AMPLITUDE_B, ATT_GAMMA_TRANSFER_AMPLITUDE_A};
612       static const GammaTransferAtts exponentAtt[4] = {
613           ATT_GAMMA_TRANSFER_EXPONENT_R, ATT_GAMMA_TRANSFER_EXPONENT_G,
614           ATT_GAMMA_TRANSFER_EXPONENT_B, ATT_GAMMA_TRANSFER_EXPONENT_A};
615       static const GammaTransferAtts offsetAtt[4] = {
616           ATT_GAMMA_TRANSFER_OFFSET_R, ATT_GAMMA_TRANSFER_OFFSET_G,
617           ATT_GAMMA_TRANSFER_OFFSET_B, ATT_GAMMA_TRANSFER_OFFSET_A};
618       if (!aGammaTransfer) {
619         aGammaTransfer = aDT->CreateFilter(FilterType::GAMMA_TRANSFER);
620         if (!aGammaTransfer) {
621           return;
622         }
623         DisableAllTransfers(aGammaTransfer);
624       }
625       filter = aGammaTransfer;
626       filter->SetAttribute(disableAtt[aOutChannel], false);
627       const nsTArray<float>& gammaValues =
628           aFunctionAttributes.mValues[aInChannel];
629       float amplitude = gammaValues[kComponentTransferAmplitudeIndex];
630       float exponent = gammaValues[kComponentTransferExponentIndex];
631       float offset = gammaValues[kComponentTransferOffsetIndex];
632       filter->SetAttribute(amplitudeAtt[aOutChannel], amplitude);
633       filter->SetAttribute(exponentAtt[aOutChannel], exponent);
634       filter->SetAttribute(offsetAtt[aOutChannel], offset);
635       break;
636     }
637 
638     case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
639     default:
640       break;
641   }
642 }
643 
644 const int32_t kMorphologyMaxRadius = 100000;
645 
646 // Handle the different primitive description types and create the necessary
647 // FilterNode(s) for each.
648 // Returns nullptr for invalid filter primitives. This should be interpreted as
649 // transparent black by the caller.
650 // aSourceRegions contains the filter primitive subregions of the source
651 // primitives; only needed for eTile primitives.
652 // aInputImages carries additional surfaces that are used by eImage primitives.
FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription & aDescription,DrawTarget * aDT,nsTArray<RefPtr<FilterNode>> & aSources,nsTArray<IntRect> & aSourceRegions,nsTArray<RefPtr<SourceSurface>> & aInputImages)653 static already_AddRefed<FilterNode> FilterNodeFromPrimitiveDescription(
654     const FilterPrimitiveDescription& aDescription, DrawTarget* aDT,
655     nsTArray<RefPtr<FilterNode>>& aSources, nsTArray<IntRect>& aSourceRegions,
656     nsTArray<RefPtr<SourceSurface>>& aInputImages) {
657   struct PrimitiveAttributesMatcher {
658     PrimitiveAttributesMatcher(const FilterPrimitiveDescription& aDescription,
659                                DrawTarget* aDT,
660                                nsTArray<RefPtr<FilterNode>>& aSources,
661                                nsTArray<IntRect>& aSourceRegions,
662                                nsTArray<RefPtr<SourceSurface>>& aInputImages)
663         : mDescription(aDescription),
664           mDT(aDT),
665           mSources(aSources),
666           mSourceRegions(aSourceRegions),
667           mInputImages(aInputImages) {}
668 
669     const FilterPrimitiveDescription& mDescription;
670     DrawTarget* mDT;
671     nsTArray<RefPtr<FilterNode>>& mSources;
672     nsTArray<IntRect>& mSourceRegions;
673     nsTArray<RefPtr<SourceSurface>>& mInputImages;
674 
675     already_AddRefed<FilterNode> operator()(
676         const EmptyAttributes& aEmptyAttributes) {
677       return nullptr;
678     }
679 
680     already_AddRefed<FilterNode> operator()(const BlendAttributes& aBlend) {
681       uint32_t mode = aBlend.mBlendMode;
682       RefPtr<FilterNode> filter;
683       if (mode == SVG_FEBLEND_MODE_UNKNOWN) {
684         return nullptr;
685       }
686       if (mode == SVG_FEBLEND_MODE_NORMAL) {
687         filter = mDT->CreateFilter(FilterType::COMPOSITE);
688         if (!filter) {
689           return nullptr;
690         }
691         filter->SetInput(IN_COMPOSITE_IN_START, mSources[1]);
692         filter->SetInput(IN_COMPOSITE_IN_START + 1, mSources[0]);
693       } else {
694         filter = mDT->CreateFilter(FilterType::BLEND);
695         if (!filter) {
696           return nullptr;
697         }
698         static const uint8_t blendModes[SVG_FEBLEND_MODE_LUMINOSITY + 1] = {
699             0,
700             0,
701             BLEND_MODE_MULTIPLY,
702             BLEND_MODE_SCREEN,
703             BLEND_MODE_DARKEN,
704             BLEND_MODE_LIGHTEN,
705             BLEND_MODE_OVERLAY,
706             BLEND_MODE_COLOR_DODGE,
707             BLEND_MODE_COLOR_BURN,
708             BLEND_MODE_HARD_LIGHT,
709             BLEND_MODE_SOFT_LIGHT,
710             BLEND_MODE_DIFFERENCE,
711             BLEND_MODE_EXCLUSION,
712             BLEND_MODE_HUE,
713             BLEND_MODE_SATURATION,
714             BLEND_MODE_COLOR,
715             BLEND_MODE_LUMINOSITY};
716         filter->SetAttribute(ATT_BLEND_BLENDMODE, (uint32_t)blendModes[mode]);
717         // The correct input order for both software and D2D filters is flipped
718         // from our source order, so flip here.
719         filter->SetInput(IN_BLEND_IN, mSources[1]);
720         filter->SetInput(IN_BLEND_IN2, mSources[0]);
721       }
722       return filter.forget();
723     }
724 
725     already_AddRefed<FilterNode> operator()(
726         const ColorMatrixAttributes& aMatrixAttributes) {
727       float colorMatrix[20];
728       if (!ComputeColorMatrix(aMatrixAttributes, colorMatrix)) {
729         RefPtr<FilterNode> filter(mSources[0]);
730         return filter.forget();
731       }
732 
733       Matrix5x4 matrix(
734           colorMatrix[0], colorMatrix[5], colorMatrix[10], colorMatrix[15],
735           colorMatrix[1], colorMatrix[6], colorMatrix[11], colorMatrix[16],
736           colorMatrix[2], colorMatrix[7], colorMatrix[12], colorMatrix[17],
737           colorMatrix[3], colorMatrix[8], colorMatrix[13], colorMatrix[18],
738           colorMatrix[4], colorMatrix[9], colorMatrix[14], colorMatrix[19]);
739 
740       RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::COLOR_MATRIX);
741       if (!filter) {
742         return nullptr;
743       }
744       filter->SetAttribute(ATT_COLOR_MATRIX_MATRIX, matrix);
745       filter->SetAttribute(ATT_COLOR_MATRIX_ALPHA_MODE,
746                            (uint32_t)ALPHA_MODE_STRAIGHT);
747       filter->SetInput(IN_COLOR_MATRIX_IN, mSources[0]);
748       return filter.forget();
749     }
750 
751     already_AddRefed<FilterNode> operator()(
752         const MorphologyAttributes& aMorphology) {
753       Size radii = aMorphology.mRadii;
754       int32_t rx = radii.width;
755       int32_t ry = radii.height;
756 
757       // Is one of the radii zero or negative, return the input image
758       if (rx <= 0 || ry <= 0) {
759         RefPtr<FilterNode> filter(mSources[0]);
760         return filter.forget();
761       }
762 
763       // Clamp radii to prevent completely insane values:
764       rx = std::min(rx, kMorphologyMaxRadius);
765       ry = std::min(ry, kMorphologyMaxRadius);
766 
767       MorphologyOperator op = aMorphology.mOperator == SVG_OPERATOR_ERODE
768                                   ? MORPHOLOGY_OPERATOR_ERODE
769                                   : MORPHOLOGY_OPERATOR_DILATE;
770 
771       RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::MORPHOLOGY);
772       if (!filter) {
773         return nullptr;
774       }
775       filter->SetAttribute(ATT_MORPHOLOGY_RADII, IntSize(rx, ry));
776       filter->SetAttribute(ATT_MORPHOLOGY_OPERATOR, (uint32_t)op);
777       filter->SetInput(IN_MORPHOLOGY_IN, mSources[0]);
778       return filter.forget();
779     }
780 
781     already_AddRefed<FilterNode> operator()(const FloodAttributes& aFlood) {
782       DeviceColor color = ToDeviceColor(aFlood.mColor);
783       RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::FLOOD);
784       if (!filter) {
785         return nullptr;
786       }
787       filter->SetAttribute(ATT_FLOOD_COLOR, color);
788       return filter.forget();
789     }
790 
791     already_AddRefed<FilterNode> operator()(const TileAttributes& aTile) {
792       RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::TILE);
793       if (!filter) {
794         return nullptr;
795       }
796       filter->SetAttribute(ATT_TILE_SOURCE_RECT, mSourceRegions[0]);
797       filter->SetInput(IN_TILE_IN, mSources[0]);
798       return filter.forget();
799     }
800 
801     already_AddRefed<FilterNode> operator()(
802         const ComponentTransferAttributes& aComponentTransfer) {
803       MOZ_ASSERT(aComponentTransfer.mTypes[0] !=
804                  SVG_FECOMPONENTTRANSFER_SAME_AS_R);
805       MOZ_ASSERT(aComponentTransfer.mTypes[3] !=
806                  SVG_FECOMPONENTTRANSFER_SAME_AS_R);
807 
808       RefPtr<FilterNode> filters[4];  // one for each FILTER_*_TRANSFER type
809       for (int32_t i = 0; i < 4; i++) {
810         int32_t inputIndex = (aComponentTransfer.mTypes[i] ==
811                               SVG_FECOMPONENTTRANSFER_SAME_AS_R) &&
812                                      (i < 3)
813                                  ? 0
814                                  : i;
815         ConvertComponentTransferFunctionToFilter(aComponentTransfer, inputIndex,
816                                                  i, mDT, filters[0], filters[1],
817                                                  filters[2], filters[3]);
818       }
819 
820       // Connect all used filters nodes.
821       RefPtr<FilterNode> lastFilter = mSources[0];
822       for (int32_t i = 0; i < 4; i++) {
823         if (filters[i]) {
824           filters[i]->SetInput(0, lastFilter);
825           lastFilter = filters[i];
826         }
827       }
828 
829       return lastFilter.forget();
830     }
831 
832     already_AddRefed<FilterNode> operator()(const OpacityAttributes& aOpacity) {
833       RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::OPACITY);
834       if (!filter) {
835         return nullptr;
836       }
837       filter->SetAttribute(ATT_OPACITY_VALUE, aOpacity.mOpacity);
838       filter->SetInput(IN_OPACITY_IN, mSources[0]);
839       return filter.forget();
840     }
841 
842     already_AddRefed<FilterNode> operator()(
843         const ConvolveMatrixAttributes& aConvolveMatrix) {
844       RefPtr<FilterNode> filter =
845           mDT->CreateFilter(FilterType::CONVOLVE_MATRIX);
846       if (!filter) {
847         return nullptr;
848       }
849       filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_SIZE,
850                            aConvolveMatrix.mKernelSize);
851       const nsTArray<float>& matrix = aConvolveMatrix.mKernelMatrix;
852       filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_MATRIX, matrix.Elements(),
853                            matrix.Length());
854       filter->SetAttribute(ATT_CONVOLVE_MATRIX_DIVISOR,
855                            aConvolveMatrix.mDivisor);
856       filter->SetAttribute(ATT_CONVOLVE_MATRIX_BIAS, aConvolveMatrix.mBias);
857       filter->SetAttribute(ATT_CONVOLVE_MATRIX_TARGET, aConvolveMatrix.mTarget);
858       filter->SetAttribute(ATT_CONVOLVE_MATRIX_SOURCE_RECT, mSourceRegions[0]);
859       uint32_t edgeMode = aConvolveMatrix.mEdgeMode;
860       static const uint8_t edgeModes[SVG_EDGEMODE_NONE + 1] = {
861           EDGE_MODE_NONE,       // SVG_EDGEMODE_UNKNOWN
862           EDGE_MODE_DUPLICATE,  // SVG_EDGEMODE_DUPLICATE
863           EDGE_MODE_WRAP,       // SVG_EDGEMODE_WRAP
864           EDGE_MODE_NONE        // SVG_EDGEMODE_NONE
865       };
866       filter->SetAttribute(ATT_CONVOLVE_MATRIX_EDGE_MODE,
867                            (uint32_t)edgeModes[edgeMode]);
868       filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH,
869                            aConvolveMatrix.mKernelUnitLength);
870       filter->SetAttribute(ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA,
871                            aConvolveMatrix.mPreserveAlpha);
872       filter->SetInput(IN_CONVOLVE_MATRIX_IN, mSources[0]);
873       return filter.forget();
874     }
875 
876     already_AddRefed<FilterNode> operator()(const OffsetAttributes& aOffset) {
877       return FilterWrappers::Offset(mDT, mSources[0], aOffset.mValue);
878     }
879 
880     already_AddRefed<FilterNode> operator()(
881         const DisplacementMapAttributes& aDisplacementMap) {
882       RefPtr<FilterNode> filter =
883           mDT->CreateFilter(FilterType::DISPLACEMENT_MAP);
884       if (!filter) {
885         return nullptr;
886       }
887       filter->SetAttribute(ATT_DISPLACEMENT_MAP_SCALE, aDisplacementMap.mScale);
888       static const uint8_t channel[SVG_CHANNEL_A + 1] = {
889           COLOR_CHANNEL_R,  // SVG_CHANNEL_UNKNOWN
890           COLOR_CHANNEL_R,  // SVG_CHANNEL_R
891           COLOR_CHANNEL_G,  // SVG_CHANNEL_G
892           COLOR_CHANNEL_B,  // SVG_CHANNEL_B
893           COLOR_CHANNEL_A   // SVG_CHANNEL_A
894       };
895       filter->SetAttribute(ATT_DISPLACEMENT_MAP_X_CHANNEL,
896                            (uint32_t)channel[aDisplacementMap.mXChannel]);
897       filter->SetAttribute(ATT_DISPLACEMENT_MAP_Y_CHANNEL,
898                            (uint32_t)channel[aDisplacementMap.mYChannel]);
899       filter->SetInput(IN_DISPLACEMENT_MAP_IN, mSources[0]);
900       filter->SetInput(IN_DISPLACEMENT_MAP_IN2, mSources[1]);
901       return filter.forget();
902     }
903 
904     already_AddRefed<FilterNode> operator()(
905         const TurbulenceAttributes& aTurbulence) {
906       RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::TURBULENCE);
907       if (!filter) {
908         return nullptr;
909       }
910       filter->SetAttribute(ATT_TURBULENCE_BASE_FREQUENCY,
911                            aTurbulence.mBaseFrequency);
912       filter->SetAttribute(ATT_TURBULENCE_NUM_OCTAVES, aTurbulence.mOctaves);
913       filter->SetAttribute(ATT_TURBULENCE_STITCHABLE, aTurbulence.mStitchable);
914       filter->SetAttribute(ATT_TURBULENCE_SEED, (uint32_t)aTurbulence.mSeed);
915       static const uint8_t type[SVG_TURBULENCE_TYPE_TURBULENCE + 1] = {
916           TURBULENCE_TYPE_FRACTAL_NOISE,  // SVG_TURBULENCE_TYPE_UNKNOWN
917           TURBULENCE_TYPE_FRACTAL_NOISE,  // SVG_TURBULENCE_TYPE_FRACTALNOISE
918           TURBULENCE_TYPE_TURBULENCE      // SVG_TURBULENCE_TYPE_TURBULENCE
919       };
920       filter->SetAttribute(ATT_TURBULENCE_TYPE,
921                            (uint32_t)type[aTurbulence.mType]);
922       filter->SetAttribute(
923           ATT_TURBULENCE_RECT,
924           mDescription.PrimitiveSubregion() - aTurbulence.mOffset);
925       return FilterWrappers::Offset(mDT, filter, aTurbulence.mOffset);
926     }
927 
928     already_AddRefed<FilterNode> operator()(
929         const CompositeAttributes& aComposite) {
930       RefPtr<FilterNode> filter;
931       uint32_t op = aComposite.mOperator;
932       if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
933         const nsTArray<float>& coefficients = aComposite.mCoefficients;
934         static const float allZero[4] = {0, 0, 0, 0};
935         filter = mDT->CreateFilter(FilterType::ARITHMETIC_COMBINE);
936         // All-zero coefficients sometimes occur in junk filters.
937         if (!filter || (coefficients.Length() == ArrayLength(allZero) &&
938                         ArrayEqual(coefficients.Elements(), allZero,
939                                    ArrayLength(allZero)))) {
940           return nullptr;
941         }
942         filter->SetAttribute(ATT_ARITHMETIC_COMBINE_COEFFICIENTS,
943                              coefficients.Elements(), coefficients.Length());
944         filter->SetInput(IN_ARITHMETIC_COMBINE_IN, mSources[0]);
945         filter->SetInput(IN_ARITHMETIC_COMBINE_IN2, mSources[1]);
946       } else {
947         filter = mDT->CreateFilter(FilterType::COMPOSITE);
948         if (!filter) {
949           return nullptr;
950         }
951         static const uint8_t operators[SVG_FECOMPOSITE_OPERATOR_LIGHTER + 1] = {
952             COMPOSITE_OPERATOR_OVER,    // SVG_FECOMPOSITE_OPERATOR_UNKNOWN
953             COMPOSITE_OPERATOR_OVER,    // SVG_FECOMPOSITE_OPERATOR_OVER
954             COMPOSITE_OPERATOR_IN,      // SVG_FECOMPOSITE_OPERATOR_IN
955             COMPOSITE_OPERATOR_OUT,     // SVG_FECOMPOSITE_OPERATOR_OUT
956             COMPOSITE_OPERATOR_ATOP,    // SVG_FECOMPOSITE_OPERATOR_ATOP
957             COMPOSITE_OPERATOR_XOR,     // SVG_FECOMPOSITE_OPERATOR_XOR
958             COMPOSITE_OPERATOR_OVER,    // Unused, arithmetic is handled above
959             COMPOSITE_OPERATOR_LIGHTER  // SVG_FECOMPOSITE_OPERATOR_LIGHTER
960         };
961         filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)operators[op]);
962         filter->SetInput(IN_COMPOSITE_IN_START, mSources[1]);
963         filter->SetInput(IN_COMPOSITE_IN_START + 1, mSources[0]);
964       }
965       return filter.forget();
966     }
967 
968     already_AddRefed<FilterNode> operator()(const MergeAttributes& aMerge) {
969       if (mSources.Length() == 0) {
970         return nullptr;
971       }
972       if (mSources.Length() == 1) {
973         RefPtr<FilterNode> filter(mSources[0]);
974         return filter.forget();
975       }
976       RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::COMPOSITE);
977       if (!filter) {
978         return nullptr;
979       }
980       filter->SetAttribute(ATT_COMPOSITE_OPERATOR,
981                            (uint32_t)COMPOSITE_OPERATOR_OVER);
982       for (size_t i = 0; i < mSources.Length(); i++) {
983         filter->SetInput(IN_COMPOSITE_IN_START + i, mSources[i]);
984       }
985       return filter.forget();
986     }
987 
988     already_AddRefed<FilterNode> operator()(
989         const GaussianBlurAttributes& aGaussianBlur) {
990       return FilterWrappers::GaussianBlur(mDT, mSources[0],
991                                           aGaussianBlur.mStdDeviation);
992     }
993 
994     already_AddRefed<FilterNode> operator()(
995         const DropShadowAttributes& aDropShadow) {
996       RefPtr<FilterNode> alpha = FilterWrappers::ToAlpha(mDT, mSources[0]);
997       RefPtr<FilterNode> blur =
998           FilterWrappers::GaussianBlur(mDT, alpha, aDropShadow.mStdDeviation);
999       RefPtr<FilterNode> offsetBlur = FilterWrappers::Offset(
1000           mDT, blur, IntPoint::Truncate(aDropShadow.mOffset));
1001       RefPtr<FilterNode> flood = mDT->CreateFilter(FilterType::FLOOD);
1002       if (!flood) {
1003         return nullptr;
1004       }
1005       sRGBColor color = aDropShadow.mColor;
1006       if (mDescription.InputColorSpace(0) == ColorSpace::LinearRGB) {
1007         color = sRGBColor(gsRGBToLinearRGBMap[uint8_t(color.r * 255)],
1008                           gsRGBToLinearRGBMap[uint8_t(color.g * 255)],
1009                           gsRGBToLinearRGBMap[uint8_t(color.b * 255)], color.a);
1010       }
1011       flood->SetAttribute(ATT_FLOOD_COLOR, ToDeviceColor(color));
1012 
1013       RefPtr<FilterNode> composite = mDT->CreateFilter(FilterType::COMPOSITE);
1014       if (!composite) {
1015         return nullptr;
1016       }
1017       composite->SetAttribute(ATT_COMPOSITE_OPERATOR,
1018                               (uint32_t)COMPOSITE_OPERATOR_IN);
1019       composite->SetInput(IN_COMPOSITE_IN_START, offsetBlur);
1020       composite->SetInput(IN_COMPOSITE_IN_START + 1, flood);
1021 
1022       RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::COMPOSITE);
1023       if (!filter) {
1024         return nullptr;
1025       }
1026       filter->SetAttribute(ATT_COMPOSITE_OPERATOR,
1027                            (uint32_t)COMPOSITE_OPERATOR_OVER);
1028       filter->SetInput(IN_COMPOSITE_IN_START, composite);
1029       filter->SetInput(IN_COMPOSITE_IN_START + 1, mSources[0]);
1030       return filter.forget();
1031     }
1032 
1033     already_AddRefed<FilterNode> operator()(
1034         const SpecularLightingAttributes& aLighting) {
1035       return operator()(
1036           *(static_cast<const DiffuseLightingAttributes*>(&aLighting)));
1037     }
1038 
1039     already_AddRefed<FilterNode> operator()(
1040         const DiffuseLightingAttributes& aLighting) {
1041       bool isSpecular =
1042           mDescription.Attributes().is<SpecularLightingAttributes>();
1043 
1044       if (aLighting.mLightType == LightType::None) {
1045         return nullptr;
1046       }
1047 
1048       enum { POINT = 0, SPOT, DISTANT } lightType = POINT;
1049 
1050       switch (aLighting.mLightType) {
1051         case LightType::Point:
1052           lightType = POINT;
1053           break;
1054         case LightType::Spot:
1055           lightType = SPOT;
1056           break;
1057         case LightType::Distant:
1058           lightType = DISTANT;
1059           break;
1060         default:
1061           break;
1062       }
1063 
1064       static const FilterType filterType[2][DISTANT + 1] = {
1065           {FilterType::POINT_DIFFUSE, FilterType::SPOT_DIFFUSE,
1066            FilterType::DISTANT_DIFFUSE},
1067           {FilterType::POINT_SPECULAR, FilterType::SPOT_SPECULAR,
1068            FilterType::DISTANT_SPECULAR}};
1069       RefPtr<FilterNode> filter =
1070           mDT->CreateFilter(filterType[isSpecular][lightType]);
1071       if (!filter) {
1072         return nullptr;
1073       }
1074 
1075       filter->SetAttribute(ATT_LIGHTING_COLOR, ToDeviceColor(aLighting.mColor));
1076       filter->SetAttribute(ATT_LIGHTING_SURFACE_SCALE, aLighting.mSurfaceScale);
1077       filter->SetAttribute(ATT_LIGHTING_KERNEL_UNIT_LENGTH,
1078                            aLighting.mKernelUnitLength);
1079 
1080       if (isSpecular) {
1081         filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
1082                              aLighting.mLightingConstant);
1083         filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT,
1084                              aLighting.mSpecularExponent);
1085       } else {
1086         filter->SetAttribute(ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT,
1087                              aLighting.mLightingConstant);
1088       }
1089 
1090       switch (lightType) {
1091         case POINT: {
1092           Point3D position(aLighting.mLightValues[kPointLightPositionXIndex],
1093                            aLighting.mLightValues[kPointLightPositionYIndex],
1094                            aLighting.mLightValues[kPointLightPositionZIndex]);
1095           filter->SetAttribute(ATT_POINT_LIGHT_POSITION, position);
1096           break;
1097         }
1098         case SPOT: {
1099           Point3D position(aLighting.mLightValues[kSpotLightPositionXIndex],
1100                            aLighting.mLightValues[kSpotLightPositionYIndex],
1101                            aLighting.mLightValues[kSpotLightPositionZIndex]);
1102           filter->SetAttribute(ATT_SPOT_LIGHT_POSITION, position);
1103           Point3D pointsAt(aLighting.mLightValues[kSpotLightPointsAtXIndex],
1104                            aLighting.mLightValues[kSpotLightPointsAtYIndex],
1105                            aLighting.mLightValues[kSpotLightPointsAtZIndex]);
1106           filter->SetAttribute(ATT_SPOT_LIGHT_POINTS_AT, pointsAt);
1107           filter->SetAttribute(ATT_SPOT_LIGHT_FOCUS,
1108                                aLighting.mLightValues[kSpotLightFocusIndex]);
1109           filter->SetAttribute(
1110               ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
1111               aLighting.mLightValues[kSpotLightLimitingConeAngleIndex]);
1112           break;
1113         }
1114         case DISTANT: {
1115           filter->SetAttribute(
1116               ATT_DISTANT_LIGHT_AZIMUTH,
1117               aLighting.mLightValues[kDistantLightAzimuthIndex]);
1118           filter->SetAttribute(
1119               ATT_DISTANT_LIGHT_ELEVATION,
1120               aLighting.mLightValues[kDistantLightElevationIndex]);
1121           break;
1122         }
1123       }
1124 
1125       filter->SetInput(IN_LIGHTING_IN, mSources[0]);
1126 
1127       return filter.forget();
1128     }
1129 
1130     already_AddRefed<FilterNode> operator()(const ImageAttributes& aImage) {
1131       const Matrix& TM = aImage.mTransform;
1132       if (!TM.Determinant()) {
1133         return nullptr;
1134       }
1135 
1136       // Pull the image from the additional image list using the index that's
1137       // stored in the primitive description.
1138       RefPtr<SourceSurface> inputImage = mInputImages[aImage.mInputIndex];
1139 
1140       RefPtr<FilterNode> transform = mDT->CreateFilter(FilterType::TRANSFORM);
1141       if (!transform) {
1142         return nullptr;
1143       }
1144       transform->SetInput(IN_TRANSFORM_IN, inputImage);
1145       transform->SetAttribute(ATT_TRANSFORM_MATRIX, TM);
1146       transform->SetAttribute(ATT_TRANSFORM_FILTER, aImage.mFilter);
1147       return transform.forget();
1148     }
1149 
1150     already_AddRefed<FilterNode> operator()(const ToAlphaAttributes& aToAlpha) {
1151       return FilterWrappers::ToAlpha(mDT, mSources[0]);
1152     }
1153   };
1154 
1155   return aDescription.Attributes().match(PrimitiveAttributesMatcher(
1156       aDescription, aDT, aSources, aSourceRegions, aInputImages));
1157 }
1158 
1159 template <typename T>
ElementForIndex(int32_t aIndex,const nsTArray<T> & aPrimitiveElements,const T & aSourceGraphicElement,const T & aFillPaintElement,const T & aStrokePaintElement)1160 static const T& ElementForIndex(int32_t aIndex,
1161                                 const nsTArray<T>& aPrimitiveElements,
1162                                 const T& aSourceGraphicElement,
1163                                 const T& aFillPaintElement,
1164                                 const T& aStrokePaintElement) {
1165   switch (aIndex) {
1166     case FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic:
1167     case FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha:
1168       return aSourceGraphicElement;
1169     case FilterPrimitiveDescription::kPrimitiveIndexFillPaint:
1170       return aFillPaintElement;
1171     case FilterPrimitiveDescription::kPrimitiveIndexStrokePaint:
1172       return aStrokePaintElement;
1173     default:
1174       MOZ_ASSERT(aIndex >= 0, "bad index");
1175       return aPrimitiveElements[aIndex];
1176   }
1177 }
1178 
InputAlphaModelForPrimitive(const FilterPrimitiveDescription & aDescr,int32_t aInputIndex,AlphaModel aOriginalAlphaModel)1179 static AlphaModel InputAlphaModelForPrimitive(
1180     const FilterPrimitiveDescription& aDescr, int32_t aInputIndex,
1181     AlphaModel aOriginalAlphaModel) {
1182   const PrimitiveAttributes& atts = aDescr.Attributes();
1183   if (atts.is<TileAttributes>() || atts.is<OffsetAttributes>() ||
1184       atts.is<ToAlphaAttributes>()) {
1185     return aOriginalAlphaModel;
1186   }
1187   if (atts.is<ColorMatrixAttributes>() ||
1188       atts.is<ComponentTransferAttributes>()) {
1189     return AlphaModel::Unpremultiplied;
1190   }
1191   if (atts.is<DisplacementMapAttributes>()) {
1192     return aInputIndex == 0 ? AlphaModel::Premultiplied
1193                             : AlphaModel::Unpremultiplied;
1194   }
1195   if (atts.is<ConvolveMatrixAttributes>()) {
1196     return atts.as<ConvolveMatrixAttributes>().mPreserveAlpha
1197                ? AlphaModel::Unpremultiplied
1198                : AlphaModel::Premultiplied;
1199   }
1200   return AlphaModel::Premultiplied;
1201 }
1202 
OutputAlphaModelForPrimitive(const FilterPrimitiveDescription & aDescr,const nsTArray<AlphaModel> & aInputAlphaModels)1203 static AlphaModel OutputAlphaModelForPrimitive(
1204     const FilterPrimitiveDescription& aDescr,
1205     const nsTArray<AlphaModel>& aInputAlphaModels) {
1206   if (aInputAlphaModels.Length()) {
1207     // For filters with inputs, the output is premultiplied if and only if the
1208     // first input is premultiplied.
1209     return InputAlphaModelForPrimitive(aDescr, 0, aInputAlphaModels[0]);
1210   }
1211 
1212   // All filters without inputs produce premultiplied alpha.
1213   return AlphaModel::Premultiplied;
1214 }
1215 
1216 // Returns the output FilterNode, in premultiplied sRGB space.
FilterNodeGraphFromDescription(DrawTarget * aDT,const FilterDescription & aFilter,const Rect & aResultNeededRect,FilterNode * aSourceGraphic,const IntRect & aSourceGraphicRect,FilterNode * aFillPaint,FilterNode * aStrokePaint,nsTArray<RefPtr<SourceSurface>> & aAdditionalImages)1217 already_AddRefed<FilterNode> FilterNodeGraphFromDescription(
1218     DrawTarget* aDT, const FilterDescription& aFilter,
1219     const Rect& aResultNeededRect, FilterNode* aSourceGraphic,
1220     const IntRect& aSourceGraphicRect, FilterNode* aFillPaint,
1221     FilterNode* aStrokePaint,
1222     nsTArray<RefPtr<SourceSurface>>& aAdditionalImages) {
1223   const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
1224   MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
1225 
1226   RefPtr<FilterCachedColorModels> sourceFilters[4];
1227   nsTArray<RefPtr<FilterCachedColorModels>> primitiveFilters;
1228 
1229   for (size_t i = 0; i < primitives.Length(); ++i) {
1230     const FilterPrimitiveDescription& descr = primitives[i];
1231 
1232     nsTArray<RefPtr<FilterNode>> inputFilterNodes;
1233     nsTArray<IntRect> inputSourceRects;
1234     nsTArray<AlphaModel> inputAlphaModels;
1235 
1236     for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
1237       int32_t inputIndex = descr.InputPrimitiveIndex(j);
1238       if (inputIndex < 0) {
1239         inputSourceRects.AppendElement(descr.FilterSpaceBounds());
1240       } else {
1241         inputSourceRects.AppendElement(
1242             primitives[inputIndex].PrimitiveSubregion());
1243       }
1244 
1245       RefPtr<FilterCachedColorModels> inputFilter;
1246       if (inputIndex >= 0) {
1247         MOZ_ASSERT(inputIndex < (int64_t)primitiveFilters.Length(),
1248                    "out-of-bounds input index!");
1249         inputFilter = primitiveFilters[inputIndex];
1250         MOZ_ASSERT(
1251             inputFilter,
1252             "Referred to input filter that comes after the current one?");
1253       } else {
1254         int32_t sourceIndex = -inputIndex - 1;
1255         MOZ_ASSERT(sourceIndex >= 0, "invalid source index");
1256         MOZ_ASSERT(sourceIndex < 4, "invalid source index");
1257         inputFilter = sourceFilters[sourceIndex];
1258         if (!inputFilter) {
1259           RefPtr<FilterNode> sourceFilterNode;
1260 
1261           nsTArray<FilterNode*> primitiveFilters;
1262           RefPtr<FilterNode> filt =
1263               ElementForIndex(inputIndex, primitiveFilters, aSourceGraphic,
1264                               aFillPaint, aStrokePaint);
1265           if (filt) {
1266             sourceFilterNode = filt;
1267 
1268             // Clip the original SourceGraphic to the first filter region if the
1269             // surface isn't already sized appropriately.
1270             if ((inputIndex ==
1271                      FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic ||
1272                  inputIndex ==
1273                      FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) &&
1274                 !descr.FilterSpaceBounds().Contains(aSourceGraphicRect)) {
1275               sourceFilterNode = FilterWrappers::Crop(
1276                   aDT, sourceFilterNode, descr.FilterSpaceBounds());
1277             }
1278 
1279             if (inputIndex ==
1280                 FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) {
1281               sourceFilterNode = FilterWrappers::ToAlpha(aDT, sourceFilterNode);
1282             }
1283           }
1284 
1285           inputFilter = new FilterCachedColorModels(aDT, sourceFilterNode,
1286                                                     ColorModel::PremulSRGB());
1287           sourceFilters[sourceIndex] = inputFilter;
1288         }
1289       }
1290       MOZ_ASSERT(inputFilter);
1291 
1292       AlphaModel inputAlphaModel = InputAlphaModelForPrimitive(
1293           descr, j, inputFilter->OriginalAlphaModel());
1294       inputAlphaModels.AppendElement(inputAlphaModel);
1295       ColorModel inputColorModel(descr.InputColorSpace(j), inputAlphaModel);
1296       inputFilterNodes.AppendElement(
1297           inputFilter->ForColorModel(inputColorModel));
1298     }
1299 
1300     RefPtr<FilterNode> primitiveFilterNode = FilterNodeFromPrimitiveDescription(
1301         descr, aDT, inputFilterNodes, inputSourceRects, aAdditionalImages);
1302 
1303     if (primitiveFilterNode) {
1304       primitiveFilterNode = FilterWrappers::Crop(aDT, primitiveFilterNode,
1305                                                  descr.PrimitiveSubregion());
1306     }
1307 
1308     ColorModel outputColorModel(
1309         descr.OutputColorSpace(),
1310         OutputAlphaModelForPrimitive(descr, inputAlphaModels));
1311     RefPtr<FilterCachedColorModels> primitiveFilter =
1312         new FilterCachedColorModels(aDT, primitiveFilterNode, outputColorModel);
1313 
1314     primitiveFilters.AppendElement(primitiveFilter);
1315   }
1316 
1317   MOZ_RELEASE_ASSERT(!primitiveFilters.IsEmpty());
1318   return primitiveFilters.LastElement()->ForColorModel(
1319       ColorModel::PremulSRGB());
1320 }
1321 
1322 // FilterSupport
1323 
RenderFilterDescription(DrawTarget * aDT,const FilterDescription & aFilter,const Rect & aRenderRect,SourceSurface * aSourceGraphic,const IntRect & aSourceGraphicRect,SourceSurface * aFillPaint,const IntRect & aFillPaintRect,SourceSurface * aStrokePaint,const IntRect & aStrokePaintRect,nsTArray<RefPtr<SourceSurface>> & aAdditionalImages,const Point & aDestPoint,const DrawOptions & aOptions)1324 void FilterSupport::RenderFilterDescription(
1325     DrawTarget* aDT, const FilterDescription& aFilter, const Rect& aRenderRect,
1326     SourceSurface* aSourceGraphic, const IntRect& aSourceGraphicRect,
1327     SourceSurface* aFillPaint, const IntRect& aFillPaintRect,
1328     SourceSurface* aStrokePaint, const IntRect& aStrokePaintRect,
1329     nsTArray<RefPtr<SourceSurface>>& aAdditionalImages, const Point& aDestPoint,
1330     const DrawOptions& aOptions) {
1331   RefPtr<FilterNode> sourceGraphic, fillPaint, strokePaint;
1332   if (aSourceGraphic) {
1333     sourceGraphic = FilterWrappers::ForSurface(aDT, aSourceGraphic,
1334                                                aSourceGraphicRect.TopLeft());
1335   }
1336   if (aFillPaint) {
1337     fillPaint =
1338         FilterWrappers::ForSurface(aDT, aFillPaint, aFillPaintRect.TopLeft());
1339   }
1340   if (aStrokePaint) {
1341     strokePaint = FilterWrappers::ForSurface(aDT, aStrokePaint,
1342                                              aStrokePaintRect.TopLeft());
1343   }
1344   RefPtr<FilterNode> resultFilter = FilterNodeGraphFromDescription(
1345       aDT, aFilter, aRenderRect, sourceGraphic, aSourceGraphicRect, fillPaint,
1346       strokePaint, aAdditionalImages);
1347   if (!resultFilter) {
1348     gfxWarning() << "Filter is NULL.";
1349     return;
1350   }
1351   aDT->DrawFilter(resultFilter, aRenderRect, aDestPoint, aOptions);
1352 }
1353 
UnionOfRegions(const nsTArray<nsIntRegion> & aRegions)1354 static nsIntRegion UnionOfRegions(const nsTArray<nsIntRegion>& aRegions) {
1355   nsIntRegion result;
1356   for (size_t i = 0; i < aRegions.Length(); i++) {
1357     result.Or(result, aRegions[i]);
1358   }
1359   return result;
1360 }
1361 
InflateSizeForBlurStdDev(float aStdDev)1362 static int32_t InflateSizeForBlurStdDev(float aStdDev) {
1363   double size =
1364       std::min(aStdDev, kMaxStdDeviation) * (3 * sqrt(2 * M_PI) / 4) * 1.5;
1365   return uint32_t(floor(size + 0.5));
1366 }
1367 
ResultChangeRegionForPrimitive(const FilterPrimitiveDescription & aDescription,const nsTArray<nsIntRegion> & aInputChangeRegions)1368 static nsIntRegion ResultChangeRegionForPrimitive(
1369     const FilterPrimitiveDescription& aDescription,
1370     const nsTArray<nsIntRegion>& aInputChangeRegions) {
1371   struct PrimitiveAttributesMatcher {
1372     PrimitiveAttributesMatcher(const FilterPrimitiveDescription& aDescription,
1373                                const nsTArray<nsIntRegion>& aInputChangeRegions)
1374         : mDescription(aDescription),
1375           mInputChangeRegions(aInputChangeRegions) {}
1376 
1377     const FilterPrimitiveDescription& mDescription;
1378     const nsTArray<nsIntRegion>& mInputChangeRegions;
1379 
1380     nsIntRegion operator()(const EmptyAttributes& aEmptyAttributes) {
1381       return nsIntRegion();
1382     }
1383 
1384     nsIntRegion operator()(const BlendAttributes& aBlend) {
1385       return UnionOfRegions(mInputChangeRegions);
1386     }
1387 
1388     nsIntRegion operator()(const ColorMatrixAttributes& aColorMatrix) {
1389       return mInputChangeRegions[0];
1390     }
1391 
1392     nsIntRegion operator()(const MorphologyAttributes& aMorphology) {
1393       Size radii = aMorphology.mRadii;
1394       int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
1395       int32_t ry =
1396           clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
1397       return mInputChangeRegions[0].Inflated(nsIntMargin(ry, rx, ry, rx));
1398     }
1399 
1400     nsIntRegion operator()(const FloodAttributes& aFlood) {
1401       return nsIntRegion();
1402     }
1403 
1404     nsIntRegion operator()(const TileAttributes& aTile) {
1405       return mDescription.PrimitiveSubregion();
1406     }
1407 
1408     nsIntRegion operator()(
1409         const ComponentTransferAttributes& aComponentTransfer) {
1410       return mInputChangeRegions[0];
1411     }
1412 
1413     nsIntRegion operator()(const OpacityAttributes& aOpacity) {
1414       return UnionOfRegions(mInputChangeRegions);
1415     }
1416 
1417     nsIntRegion operator()(const ConvolveMatrixAttributes& aConvolveMatrix) {
1418       if (aConvolveMatrix.mEdgeMode != EDGE_MODE_NONE) {
1419         return mDescription.PrimitiveSubregion();
1420       }
1421       Size kernelUnitLength = aConvolveMatrix.mKernelUnitLength;
1422       IntSize kernelSize = aConvolveMatrix.mKernelSize;
1423       IntPoint target = aConvolveMatrix.mTarget;
1424       nsIntMargin m(
1425           ceil(kernelUnitLength.width * (target.x)),
1426           ceil(kernelUnitLength.height * (target.y)),
1427           ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
1428           ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)));
1429       return mInputChangeRegions[0].Inflated(m);
1430     }
1431 
1432     nsIntRegion operator()(const OffsetAttributes& aOffset) {
1433       IntPoint offset = aOffset.mValue;
1434       return mInputChangeRegions[0].MovedBy(offset.x, offset.y);
1435     }
1436 
1437     nsIntRegion operator()(const DisplacementMapAttributes& aDisplacementMap) {
1438       int32_t scale = ceil(std::abs(aDisplacementMap.mScale));
1439       return mInputChangeRegions[0].Inflated(
1440           nsIntMargin(scale, scale, scale, scale));
1441     }
1442 
1443     nsIntRegion operator()(const TurbulenceAttributes& aTurbulence) {
1444       return nsIntRegion();
1445     }
1446 
1447     nsIntRegion operator()(const CompositeAttributes& aComposite) {
1448       return UnionOfRegions(mInputChangeRegions);
1449     }
1450 
1451     nsIntRegion operator()(const MergeAttributes& aMerge) {
1452       return UnionOfRegions(mInputChangeRegions);
1453     }
1454 
1455     nsIntRegion operator()(const GaussianBlurAttributes& aGaussianBlur) {
1456       const Size& stdDeviation = aGaussianBlur.mStdDeviation;
1457       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
1458       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
1459       return mInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
1460     }
1461 
1462     nsIntRegion operator()(const DropShadowAttributes& aDropShadow) {
1463       IntPoint offset = IntPoint::Truncate(aDropShadow.mOffset);
1464       nsIntRegion offsetRegion =
1465           mInputChangeRegions[0].MovedBy(offset.x, offset.y);
1466       Size stdDeviation = aDropShadow.mStdDeviation;
1467       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
1468       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
1469       nsIntRegion blurRegion =
1470           offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
1471       blurRegion.Or(blurRegion, mInputChangeRegions[0]);
1472       return blurRegion;
1473     }
1474 
1475     nsIntRegion operator()(const SpecularLightingAttributes& aLighting) {
1476       return operator()(
1477           *(static_cast<const DiffuseLightingAttributes*>(&aLighting)));
1478     }
1479 
1480     nsIntRegion operator()(const DiffuseLightingAttributes& aLighting) {
1481       Size kernelUnitLength = aLighting.mKernelUnitLength;
1482       int32_t dx = ceil(kernelUnitLength.width);
1483       int32_t dy = ceil(kernelUnitLength.height);
1484       return mInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
1485     }
1486 
1487     nsIntRegion operator()(const ImageAttributes& aImage) {
1488       return nsIntRegion();
1489     }
1490 
1491     nsIntRegion operator()(const ToAlphaAttributes& aToAlpha) {
1492       return mInputChangeRegions[0];
1493     }
1494   };
1495 
1496   return aDescription.Attributes().match(
1497       PrimitiveAttributesMatcher(aDescription, aInputChangeRegions));
1498 }
1499 
1500 /* static */
ComputeResultChangeRegion(const FilterDescription & aFilter,const nsIntRegion & aSourceGraphicChange,const nsIntRegion & aFillPaintChange,const nsIntRegion & aStrokePaintChange)1501 nsIntRegion FilterSupport::ComputeResultChangeRegion(
1502     const FilterDescription& aFilter, const nsIntRegion& aSourceGraphicChange,
1503     const nsIntRegion& aFillPaintChange,
1504     const nsIntRegion& aStrokePaintChange) {
1505   const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
1506   MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
1507 
1508   nsTArray<nsIntRegion> resultChangeRegions;
1509 
1510   for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
1511     const FilterPrimitiveDescription& descr = primitives[i];
1512 
1513     nsTArray<nsIntRegion> inputChangeRegions;
1514     for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
1515       int32_t inputIndex = descr.InputPrimitiveIndex(j);
1516       MOZ_ASSERT(inputIndex < i, "bad input index");
1517       nsIntRegion inputChangeRegion =
1518           ElementForIndex(inputIndex, resultChangeRegions, aSourceGraphicChange,
1519                           aFillPaintChange, aStrokePaintChange);
1520       inputChangeRegions.AppendElement(inputChangeRegion);
1521     }
1522     nsIntRegion changeRegion =
1523         ResultChangeRegionForPrimitive(descr, inputChangeRegions);
1524     changeRegion.And(changeRegion, descr.PrimitiveSubregion());
1525     resultChangeRegions.AppendElement(changeRegion);
1526   }
1527 
1528   MOZ_RELEASE_ASSERT(!resultChangeRegions.IsEmpty());
1529   return resultChangeRegions[resultChangeRegions.Length() - 1];
1530 }
1531 
ResultOfZeroUnderTransferFunction(const ComponentTransferAttributes & aFunctionAttributes,int32_t channel)1532 static float ResultOfZeroUnderTransferFunction(
1533     const ComponentTransferAttributes& aFunctionAttributes, int32_t channel) {
1534   switch (aFunctionAttributes.mTypes[channel]) {
1535     case SVG_FECOMPONENTTRANSFER_TYPE_TABLE: {
1536       const nsTArray<float>& tableValues = aFunctionAttributes.mValues[channel];
1537       if (tableValues.Length() < 2) {
1538         return 0.0f;
1539       }
1540       return tableValues[0];
1541     }
1542 
1543     case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE: {
1544       const nsTArray<float>& tableValues = aFunctionAttributes.mValues[channel];
1545       if (tableValues.Length() < 1) {
1546         return 0.0f;
1547       }
1548       return tableValues[0];
1549     }
1550 
1551     case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR: {
1552       const nsTArray<float>& values = aFunctionAttributes.mValues[channel];
1553       return values[kComponentTransferInterceptIndex];
1554     }
1555 
1556     case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA: {
1557       const nsTArray<float>& values = aFunctionAttributes.mValues[channel];
1558       return values[kComponentTransferOffsetIndex];
1559     }
1560 
1561     case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
1562     default:
1563       return 0.0f;
1564   }
1565 }
1566 
PostFilterExtentsForPrimitive(const FilterPrimitiveDescription & aDescription,const nsTArray<nsIntRegion> & aInputExtents)1567 nsIntRegion FilterSupport::PostFilterExtentsForPrimitive(
1568     const FilterPrimitiveDescription& aDescription,
1569     const nsTArray<nsIntRegion>& aInputExtents) {
1570   struct PrimitiveAttributesMatcher {
1571     PrimitiveAttributesMatcher(const FilterPrimitiveDescription& aDescription,
1572                                const nsTArray<nsIntRegion>& aInputExtents)
1573         : mDescription(aDescription), mInputExtents(aInputExtents) {}
1574 
1575     const FilterPrimitiveDescription& mDescription;
1576     const nsTArray<nsIntRegion>& mInputExtents;
1577 
1578     nsIntRegion operator()(const EmptyAttributes& aEmptyAttributes) {
1579       return IntRect();
1580     }
1581 
1582     nsIntRegion operator()(const BlendAttributes& aBlend) {
1583       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1584     }
1585 
1586     nsIntRegion operator()(const ColorMatrixAttributes& aColorMatrix) {
1587       if (aColorMatrix.mType == (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX) {
1588         const nsTArray<float>& values = aColorMatrix.mValues;
1589         if (values.Length() == 20 && values[19] > 0.0f) {
1590           return mDescription.PrimitiveSubregion();
1591         }
1592       }
1593       return mInputExtents[0];
1594     }
1595 
1596     nsIntRegion operator()(const MorphologyAttributes& aMorphology) {
1597       uint32_t op = aMorphology.mOperator;
1598       if (op == SVG_OPERATOR_ERODE) {
1599         return mInputExtents[0];
1600       }
1601       Size radii = aMorphology.mRadii;
1602       int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
1603       int32_t ry =
1604           clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
1605       return mInputExtents[0].Inflated(nsIntMargin(ry, rx, ry, rx));
1606     }
1607 
1608     nsIntRegion operator()(const FloodAttributes& aFlood) {
1609       if (aFlood.mColor.a == 0.0f) {
1610         return IntRect();
1611       }
1612       return mDescription.PrimitiveSubregion();
1613     }
1614 
1615     nsIntRegion operator()(const TileAttributes& aTile) {
1616       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1617     }
1618 
1619     nsIntRegion operator()(
1620         const ComponentTransferAttributes& aComponentTransfer) {
1621       if (ResultOfZeroUnderTransferFunction(aComponentTransfer, kChannelA) >
1622           0.0f) {
1623         return mDescription.PrimitiveSubregion();
1624       }
1625       return mInputExtents[0];
1626     }
1627 
1628     nsIntRegion operator()(const OpacityAttributes& aOpacity) {
1629       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1630     }
1631 
1632     nsIntRegion operator()(const ConvolveMatrixAttributes& aConvolveMatrix) {
1633       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1634     }
1635 
1636     nsIntRegion operator()(const OffsetAttributes& aOffset) {
1637       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1638     }
1639 
1640     nsIntRegion operator()(const DisplacementMapAttributes& aDisplacementMap) {
1641       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1642     }
1643 
1644     nsIntRegion operator()(const TurbulenceAttributes& aTurbulence) {
1645       return mDescription.PrimitiveSubregion();
1646     }
1647 
1648     nsIntRegion operator()(const CompositeAttributes& aComposite) {
1649       uint32_t op = aComposite.mOperator;
1650       if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
1651         // The arithmetic composite primitive can draw outside the bounding
1652         // box of its source images.
1653         const nsTArray<float>& coefficients = aComposite.mCoefficients;
1654         MOZ_ASSERT(coefficients.Length() == 4);
1655 
1656         // The calculation is:
1657         // r = c[0] * in[0] * in[1] + c[1] * in[0] + c[2] * in[1] + c[3]
1658         nsIntRegion region;
1659         if (coefficients[0] > 0.0f) {
1660           region = mInputExtents[0].Intersect(mInputExtents[1]);
1661         }
1662         if (coefficients[1] > 0.0f) {
1663           region.Or(region, mInputExtents[0]);
1664         }
1665         if (coefficients[2] > 0.0f) {
1666           region.Or(region, mInputExtents[1]);
1667         }
1668         if (coefficients[3] > 0.0f) {
1669           region = mDescription.PrimitiveSubregion();
1670         }
1671         return region;
1672       }
1673       if (op == SVG_FECOMPOSITE_OPERATOR_IN) {
1674         return mInputExtents[0].Intersect(mInputExtents[1]);
1675       }
1676       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1677     }
1678 
1679     nsIntRegion operator()(const MergeAttributes& aMerge) {
1680       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1681     }
1682 
1683     nsIntRegion operator()(const GaussianBlurAttributes& aGaussianBlur) {
1684       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1685     }
1686 
1687     nsIntRegion operator()(const DropShadowAttributes& aDropShadow) {
1688       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1689     }
1690 
1691     nsIntRegion operator()(const DiffuseLightingAttributes& aDiffuseLighting) {
1692       return mDescription.PrimitiveSubregion();
1693     }
1694 
1695     nsIntRegion operator()(
1696         const SpecularLightingAttributes& aSpecularLighting) {
1697       return mDescription.PrimitiveSubregion();
1698     }
1699 
1700     nsIntRegion operator()(const ImageAttributes& aImage) {
1701       return mDescription.PrimitiveSubregion();
1702     }
1703 
1704     nsIntRegion operator()(const ToAlphaAttributes& aToAlpha) {
1705       return ResultChangeRegionForPrimitive(mDescription, mInputExtents);
1706     }
1707   };
1708 
1709   return aDescription.Attributes().match(
1710       PrimitiveAttributesMatcher(aDescription, aInputExtents));
1711 }
1712 
1713 /* static */
ComputePostFilterExtents(const FilterDescription & aFilter,const nsIntRegion & aSourceGraphicExtents)1714 nsIntRegion FilterSupport::ComputePostFilterExtents(
1715     const FilterDescription& aFilter,
1716     const nsIntRegion& aSourceGraphicExtents) {
1717   const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
1718   MOZ_RELEASE_ASSERT(!primitives.IsEmpty());
1719   nsTArray<nsIntRegion> postFilterExtents;
1720 
1721   for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
1722     const FilterPrimitiveDescription& descr = primitives[i];
1723     nsIntRegion filterSpace = descr.FilterSpaceBounds();
1724 
1725     nsTArray<nsIntRegion> inputExtents;
1726     for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
1727       int32_t inputIndex = descr.InputPrimitiveIndex(j);
1728       MOZ_ASSERT(inputIndex < i, "bad input index");
1729       nsIntRegion inputExtent =
1730           ElementForIndex(inputIndex, postFilterExtents, aSourceGraphicExtents,
1731                           filterSpace, filterSpace);
1732       inputExtents.AppendElement(inputExtent);
1733     }
1734     nsIntRegion extent = PostFilterExtentsForPrimitive(descr, inputExtents);
1735     extent.And(extent, descr.PrimitiveSubregion());
1736     postFilterExtents.AppendElement(extent);
1737   }
1738 
1739   MOZ_RELEASE_ASSERT(!postFilterExtents.IsEmpty());
1740   return postFilterExtents[postFilterExtents.Length() - 1];
1741 }
1742 
SourceNeededRegionForPrimitive(const FilterPrimitiveDescription & aDescription,const nsIntRegion & aResultNeededRegion,int32_t aInputIndex)1743 static nsIntRegion SourceNeededRegionForPrimitive(
1744     const FilterPrimitiveDescription& aDescription,
1745     const nsIntRegion& aResultNeededRegion, int32_t aInputIndex) {
1746   struct PrimitiveAttributesMatcher {
1747     PrimitiveAttributesMatcher(const FilterPrimitiveDescription& aDescription,
1748                                const nsIntRegion& aResultNeededRegion,
1749                                int32_t aInputIndex)
1750         : mDescription(aDescription),
1751           mResultNeededRegion(aResultNeededRegion),
1752           mInputIndex(aInputIndex) {}
1753 
1754     const FilterPrimitiveDescription& mDescription;
1755     const nsIntRegion& mResultNeededRegion;
1756     const int32_t mInputIndex;
1757 
1758     nsIntRegion operator()(const EmptyAttributes& aEmptyAttributes) {
1759       return nsIntRegion();
1760     }
1761 
1762     nsIntRegion operator()(const BlendAttributes& aBlend) {
1763       return mResultNeededRegion;
1764     }
1765 
1766     nsIntRegion operator()(const ColorMatrixAttributes& aColorMatrix) {
1767       return mResultNeededRegion;
1768     }
1769 
1770     nsIntRegion operator()(const MorphologyAttributes& aMorphology) {
1771       Size radii = aMorphology.mRadii;
1772       int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
1773       int32_t ry =
1774           clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
1775       return mResultNeededRegion.Inflated(nsIntMargin(ry, rx, ry, rx));
1776     }
1777 
1778     nsIntRegion operator()(const FloodAttributes& aFlood) {
1779       MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
1780       return nsIntRegion();
1781     }
1782 
1783     nsIntRegion operator()(const TileAttributes& aTile) {
1784       return IntRect(INT32_MIN / 2, INT32_MIN / 2, INT32_MAX, INT32_MAX);
1785     }
1786 
1787     nsIntRegion operator()(
1788         const ComponentTransferAttributes& aComponentTransfer) {
1789       return mResultNeededRegion;
1790     }
1791 
1792     nsIntRegion operator()(const OpacityAttributes& aOpacity) {
1793       return mResultNeededRegion;
1794     }
1795 
1796     nsIntRegion operator()(const ConvolveMatrixAttributes& aConvolveMatrix) {
1797       Size kernelUnitLength = aConvolveMatrix.mKernelUnitLength;
1798       IntSize kernelSize = aConvolveMatrix.mKernelSize;
1799       IntPoint target = aConvolveMatrix.mTarget;
1800       nsIntMargin m(
1801           ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
1802           ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)),
1803           ceil(kernelUnitLength.width * (target.x)),
1804           ceil(kernelUnitLength.height * (target.y)));
1805       return mResultNeededRegion.Inflated(m);
1806     }
1807 
1808     nsIntRegion operator()(const OffsetAttributes& aOffset) {
1809       IntPoint offset = aOffset.mValue;
1810       return mResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
1811     }
1812 
1813     nsIntRegion operator()(const DisplacementMapAttributes& aDisplacementMap) {
1814       if (mInputIndex == 1) {
1815         return mResultNeededRegion;
1816       }
1817       int32_t scale = ceil(std::abs(aDisplacementMap.mScale));
1818       return mResultNeededRegion.Inflated(
1819           nsIntMargin(scale, scale, scale, scale));
1820     }
1821 
1822     nsIntRegion operator()(const TurbulenceAttributes& aTurbulence) {
1823       MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
1824       return nsIntRegion();
1825     }
1826 
1827     nsIntRegion operator()(const CompositeAttributes& aComposite) {
1828       return mResultNeededRegion;
1829     }
1830 
1831     nsIntRegion operator()(const MergeAttributes& aMerge) {
1832       return mResultNeededRegion;
1833     }
1834 
1835     nsIntRegion operator()(const GaussianBlurAttributes& aGaussianBlur) {
1836       const Size& stdDeviation = aGaussianBlur.mStdDeviation;
1837       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
1838       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
1839       return mResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
1840     }
1841 
1842     nsIntRegion operator()(const DropShadowAttributes& aDropShadow) {
1843       IntPoint offset = IntPoint::Truncate(aDropShadow.mOffset);
1844       nsIntRegion offsetRegion =
1845           mResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
1846       Size stdDeviation = aDropShadow.mStdDeviation;
1847       int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
1848       int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
1849       nsIntRegion blurRegion =
1850           offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
1851       blurRegion.Or(blurRegion, mResultNeededRegion);
1852       return blurRegion;
1853     }
1854 
1855     nsIntRegion operator()(const SpecularLightingAttributes& aLighting) {
1856       return operator()(
1857           *(static_cast<const DiffuseLightingAttributes*>(&aLighting)));
1858     }
1859 
1860     nsIntRegion operator()(const DiffuseLightingAttributes& aLighting) {
1861       Size kernelUnitLength = aLighting.mKernelUnitLength;
1862       int32_t dx = ceil(kernelUnitLength.width);
1863       int32_t dy = ceil(kernelUnitLength.height);
1864       return mResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
1865     }
1866 
1867     nsIntRegion operator()(const ImageAttributes& aImage) {
1868       MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
1869       return nsIntRegion();
1870     }
1871 
1872     nsIntRegion operator()(const ToAlphaAttributes& aToAlpha) {
1873       return mResultNeededRegion;
1874     }
1875   };
1876 
1877   return aDescription.Attributes().match(PrimitiveAttributesMatcher(
1878       aDescription, aResultNeededRegion, aInputIndex));
1879 }
1880 
1881 /* static */
ComputeSourceNeededRegions(const FilterDescription & aFilter,const nsIntRegion & aResultNeededRegion,nsIntRegion & aSourceGraphicNeededRegion,nsIntRegion & aFillPaintNeededRegion,nsIntRegion & aStrokePaintNeededRegion)1882 void FilterSupport::ComputeSourceNeededRegions(
1883     const FilterDescription& aFilter, const nsIntRegion& aResultNeededRegion,
1884     nsIntRegion& aSourceGraphicNeededRegion,
1885     nsIntRegion& aFillPaintNeededRegion,
1886     nsIntRegion& aStrokePaintNeededRegion) {
1887   const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
1888   MOZ_ASSERT(!primitives.IsEmpty());
1889   if (primitives.IsEmpty()) {
1890     return;
1891   }
1892 
1893   nsTArray<nsIntRegion> primitiveNeededRegions;
1894   primitiveNeededRegions.AppendElements(primitives.Length());
1895 
1896   primitiveNeededRegions[primitives.Length() - 1] = aResultNeededRegion;
1897 
1898   for (int32_t i = primitives.Length() - 1; i >= 0; --i) {
1899     const FilterPrimitiveDescription& descr = primitives[i];
1900     nsIntRegion neededRegion = primitiveNeededRegions[i];
1901     neededRegion.And(neededRegion, descr.PrimitiveSubregion());
1902 
1903     for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
1904       int32_t inputIndex = descr.InputPrimitiveIndex(j);
1905       MOZ_ASSERT(inputIndex < i, "bad input index");
1906       nsIntRegion* inputNeededRegion =
1907           const_cast<nsIntRegion*>(&ElementForIndex(
1908               inputIndex, primitiveNeededRegions, aSourceGraphicNeededRegion,
1909               aFillPaintNeededRegion, aStrokePaintNeededRegion));
1910       inputNeededRegion->Or(*inputNeededRegion, SourceNeededRegionForPrimitive(
1911                                                     descr, neededRegion, j));
1912     }
1913   }
1914 
1915   // Clip original SourceGraphic to first filter region.
1916   const FilterPrimitiveDescription& firstDescr = primitives[0];
1917   aSourceGraphicNeededRegion.And(aSourceGraphicNeededRegion,
1918                                  firstDescr.FilterSpaceBounds());
1919 }
1920 
1921 // FilterPrimitiveDescription
1922 
FilterPrimitiveDescription()1923 FilterPrimitiveDescription::FilterPrimitiveDescription()
1924     : mAttributes(EmptyAttributes()),
1925       mOutputColorSpace(ColorSpace::SRGB),
1926       mIsTainted(false) {}
1927 
FilterPrimitiveDescription(PrimitiveAttributes && aAttributes)1928 FilterPrimitiveDescription::FilterPrimitiveDescription(
1929     PrimitiveAttributes&& aAttributes)
1930     : mAttributes(std::move(aAttributes)),
1931       mOutputColorSpace(ColorSpace::SRGB),
1932       mIsTainted(false) {}
1933 
operator ==(const FilterPrimitiveDescription & aOther) const1934 bool FilterPrimitiveDescription::operator==(
1935     const FilterPrimitiveDescription& aOther) const {
1936   return mFilterPrimitiveSubregion.IsEqualInterior(
1937              aOther.mFilterPrimitiveSubregion) &&
1938          mFilterSpaceBounds.IsEqualInterior(aOther.mFilterSpaceBounds) &&
1939          mOutputColorSpace == aOther.mOutputColorSpace &&
1940          mIsTainted == aOther.mIsTainted &&
1941          mInputPrimitives == aOther.mInputPrimitives &&
1942          mInputColorSpaces == aOther.mInputColorSpaces &&
1943          mAttributes == aOther.mAttributes;
1944 }
1945 
1946 // FilterDescription
1947 
operator ==(const FilterDescription & aOther) const1948 bool FilterDescription::operator==(const FilterDescription& aOther) const {
1949   return mPrimitives == aOther.mPrimitives;
1950 }
1951 
1952 }  // namespace gfx
1953 }  // namespace mozilla
1954