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/. */
7 #include "FilterSupport.h"
8 #include "FilterDescription.h"
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"
16 #include "gfxContext.h"
17 #include "gfxPattern.h"
18 #include "gfxPlatform.h"
20 #include "gfx2DGlue.h"
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
};
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
};
93 // Some convenience FilterNode creation functions.
95 namespace FilterWrappers
{
97 static already_AddRefed
<FilterNode
> Unpremultiply(DrawTarget
* aDT
,
99 RefPtr
<FilterNode
> filter
= aDT
->CreateFilter(FilterType::UNPREMULTIPLY
);
101 filter
->SetInput(IN_UNPREMULTIPLY_IN
, aInput
);
102 return filter
.forget();
107 static already_AddRefed
<FilterNode
> Premultiply(DrawTarget
* aDT
,
108 FilterNode
* aInput
) {
109 RefPtr
<FilterNode
> filter
= aDT
->CreateFilter(FilterType::PREMULTIPLY
);
111 filter
->SetInput(IN_PREMULTIPLY_IN
, aInput
);
112 return filter
.forget();
117 static already_AddRefed
<FilterNode
> LinearRGBToSRGB(DrawTarget
* aDT
,
118 FilterNode
* aInput
) {
119 RefPtr
<FilterNode
> transfer
=
120 aDT
->CreateFilter(FilterType::DISCRETE_TRANSFER
);
122 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R
, false);
123 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R
, glinearRGBTosRGBMap
,
125 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G
, false);
126 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G
, glinearRGBTosRGBMap
,
128 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B
, false);
129 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B
, glinearRGBTosRGBMap
,
131 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A
, true);
132 transfer
->SetInput(IN_DISCRETE_TRANSFER_IN
, aInput
);
133 return transfer
.forget();
138 static already_AddRefed
<FilterNode
> SRGBToLinearRGB(DrawTarget
* aDT
,
139 FilterNode
* aInput
) {
140 RefPtr
<FilterNode
> transfer
=
141 aDT
->CreateFilter(FilterType::DISCRETE_TRANSFER
);
143 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R
, false);
144 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R
, gsRGBToLinearRGBMap
,
146 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G
, false);
147 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G
, gsRGBToLinearRGBMap
,
149 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B
, false);
150 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B
, gsRGBToLinearRGBMap
,
152 transfer
->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A
, true);
153 transfer
->SetInput(IN_DISCRETE_TRANSFER_IN
, aInput
);
154 return transfer
.forget();
159 static already_AddRefed
<FilterNode
> Crop(DrawTarget
* aDT
,
160 FilterNode
* aInputFilter
,
161 const IntRect
& aRect
) {
162 RefPtr
<FilterNode
> filter
= aDT
->CreateFilter(FilterType::CROP
);
164 filter
->SetAttribute(ATT_CROP_RECT
, Rect(aRect
));
165 filter
->SetInput(IN_CROP_IN
, aInputFilter
);
166 return filter
.forget();
171 static already_AddRefed
<FilterNode
> Offset(DrawTarget
* aDT
,
172 FilterNode
* aInputFilter
,
173 const IntPoint
& aOffset
) {
174 RefPtr
<FilterNode
> filter
= aDT
->CreateFilter(FilterType::TRANSFORM
);
176 filter
->SetAttribute(ATT_TRANSFORM_MATRIX
,
177 Matrix::Translation(aOffset
.x
, aOffset
.y
));
178 filter
->SetInput(IN_TRANSFORM_IN
, aInputFilter
);
179 return filter
.forget();
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
));
190 RefPtr
<FilterNode
> filter
= aDT
->CreateFilter(FilterType::GAUSSIAN_BLUR
);
192 filter
->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION
, stdX
);
193 filter
->SetInput(IN_GAUSSIAN_BLUR_IN
, aInputFilter
);
194 return filter
.forget();
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();
214 already_AddRefed
<FilterNode
> Clear(DrawTarget
* aDT
) {
215 RefPtr
<FilterNode
> filter
= aDT
->CreateFilter(FilterType::FLOOD
);
217 filter
->SetAttribute(ATT_FLOOD_COLOR
, DeviceColor());
218 return filter
.forget();
223 already_AddRefed
<FilterNode
> ForSurface(DrawTarget
* aDT
,
224 SourceSurface
* aSurface
,
225 const IntPoint
& aSurfacePosition
) {
226 RefPtr
<FilterNode
> filter
= aDT
->CreateFilter(FilterType::TRANSFORM
);
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();
237 static already_AddRefed
<FilterNode
> ToAlpha(DrawTarget
* aDT
,
238 FilterNode
* aInput
) {
240 RefPtr
<FilterNode
> transfer
=
241 aDT
->CreateFilter(FilterType::DISCRETE_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();
256 } // namespace FilterWrappers
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
{
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
);
274 // Get a FilterNode for the specified color model, guaranteed to be non-null.
275 already_AddRefed
<FilterNode
> ForColorModel(ColorModel aColorModel
);
277 AlphaModel
OriginalAlphaModel() const {
278 return mOriginalColorModel
.mAlphaModel
;
282 // Create the required FilterNode that will be cached by ForColorModel.
283 already_AddRefed
<FilterNode
> WrapForColorModel(ColorModel aColorModel
);
285 RefPtr
<DrawTarget
> mDT
;
286 ColorModel mOriginalColorModel
;
288 // This array is indexed by ColorModel::ToIndex.
289 RefPtr
<FilterNode
> mFilterForColorModel
[4];
291 ~FilterCachedColorModels() = default;
294 FilterCachedColorModels::FilterCachedColorModels(DrawTarget
* aDT
,
296 ColorModel aOriginalColorModel
)
297 : mDT(aDT
), mOriginalColorModel(aOriginalColorModel
) {
299 mFilterForColorModel
[aOriginalColorModel
.ToIndex()] = aFilter
;
301 RefPtr
<FilterNode
> clear
= FilterWrappers::Clear(aDT
);
302 mFilterForColorModel
[0] = clear
;
303 mFilterForColorModel
[1] = clear
;
304 mFilterForColorModel
[2] = clear
;
305 mFilterForColorModel
[3] = clear
;
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();
319 if (!mFilterForColorModel
[aColorModel
.ToIndex()]) {
320 mFilterForColorModel
[aColorModel
.ToIndex()] =
321 WrapForColorModel(aColorModel
);
323 RefPtr
<FilterNode
> filter(mFilterForColorModel
[aColorModel
.ToIndex()]);
324 return filter
.forget();
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.
335 if (aColorModel
.mAlphaModel
== AlphaModel::Premultiplied
) {
336 RefPtr
<FilterNode
> unpre
= ForColorModel(
337 ColorModel(aColorModel
.mColorSpace
, AlphaModel::Unpremultiplied
));
338 return FilterWrappers::Premultiply(mDT
, unpre
);
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
);
348 RefPtr
<FilterNode
> unpremultipliedOriginal
= ForColorModel(
349 ColorModel(mOriginalColorModel
.mColorSpace
, AlphaModel::Unpremultiplied
));
350 if (aColorModel
.mColorSpace
== ColorSpace::LinearRGB
) {
351 return FilterWrappers::SRGBToLinearRGB(mDT
, unpremultipliedOriginal
);
353 return FilterWrappers::LinearRGBToSRGB(mDT
, unpremultipliedOriginal
);
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};
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
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:
371 static void InterpolateFromIdentityMatrix(const float aToMatrix
[20],
372 float aAmount
, float aOutMatrix
[20]) {
373 PodCopy(aOutMatrix
, identityMatrix
, 20);
375 float oneMinusAmount
= 1 - aAmount
;
377 aOutMatrix
[0] = aAmount
* aToMatrix
[0] + oneMinusAmount
;
378 aOutMatrix
[1] = aAmount
* aToMatrix
[1];
379 aOutMatrix
[2] = aAmount
* aToMatrix
[2];
381 aOutMatrix
[5] = aAmount
* aToMatrix
[5];
382 aOutMatrix
[6] = aAmount
* aToMatrix
[6] + oneMinusAmount
;
383 aOutMatrix
[7] = aAmount
* aToMatrix
[7];
385 aOutMatrix
[10] = aAmount
* aToMatrix
[10];
386 aOutMatrix
[11] = aAmount
* aToMatrix
[11];
387 aOutMatrix
[12] = aAmount
* aToMatrix
[12] + oneMinusAmount
;
390 // Create a 4x5 color matrix for the different ways to specify color matrices
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
;
399 static const float oneMinusLumR
= 1 - lumR
;
400 static const float oneMinusLumG
= 1 - lumG
;
401 static const float oneMinusLumB
= 1 - lumB
;
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};
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};
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};
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
;
419 switch (aMatrixAttributes
.mType
) {
420 case SVG_FECOLORMATRIX_TYPE_MATRIX
: {
421 if (aMatrixAttributes
.mValues
.Length() != 20) {
425 PodCopy(aOutMatrix
, aMatrixAttributes
.mValues
.Elements(), 20);
429 case SVG_FECOLORMATRIX_TYPE_SATURATE
: {
430 if (aMatrixAttributes
.mValues
.Length() != 1) {
434 float s
= aMatrixAttributes
.mValues
[0];
440 InterpolateFromIdentityMatrix(saturateMatrix
, 1 - s
, aOutMatrix
);
444 case SVG_FECOLORMATRIX_TYPE_HUE_ROTATE
: {
445 if (aMatrixAttributes
.mValues
.Length() != 1) {
449 PodCopy(aOutMatrix
, identityMatrix
, 20);
451 float hueRotateValue
= aMatrixAttributes
.mValues
[0];
453 float c
= static_cast<float>(cos(hueRotateValue
* M_PI
/ 180));
454 float s
= static_cast<float>(sin(hueRotateValue
* M_PI
/ 180));
456 aOutMatrix
[0] = lumR
+ oneMinusLumR
* c
- lumR
* s
;
457 aOutMatrix
[1] = lumG
- lumG
* c
- lumG
* s
;
458 aOutMatrix
[2] = lumB
- lumB
* c
+ oneMinusLumB
* s
;
460 aOutMatrix
[5] = lumR
- lumR
* c
+ hueRotateR
* s
;
461 aOutMatrix
[6] = lumG
+ oneMinusLumG
* c
+ hueRotateG
* s
;
462 aOutMatrix
[7] = lumB
- lumB
* c
- hueRotateB
* s
;
464 aOutMatrix
[10] = lumR
- lumR
* c
- oneMinusLumR
* s
;
465 aOutMatrix
[11] = lumG
- lumG
* c
+ lumG
* s
;
466 aOutMatrix
[12] = lumB
+ oneMinusLumB
* c
+ lumB
* s
;
471 case SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA
: {
472 PodCopy(aOutMatrix
, luminanceToAlphaMatrix
, 20);
476 case SVG_FECOLORMATRIX_TYPE_SEPIA
: {
477 if (aMatrixAttributes
.mValues
.Length() != 1) {
481 float amount
= aMatrixAttributes
.mValues
[0];
483 if (amount
< 0 || amount
> 1) {
487 InterpolateFromIdentityMatrix(sepiaMatrix
, amount
, aOutMatrix
);
496 return !ArrayEqual(aOutMatrix
, identityMatrix
, 20);
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);
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
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.
515 // @param aFunctionAttributes The attributes of the transfer function for this
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.
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
};
533 RefPtr
<FilterNode
> filter
;
535 uint32_t type
= aFunctionAttributes
.mTypes
[aInChannel
];
538 case SVG_FECOMPONENTTRANSFER_TYPE_TABLE
: {
539 const nsTArray
<float>& tableValues
=
540 aFunctionAttributes
.mValues
[aInChannel
];
541 if (tableValues
.Length() < 2) return;
543 if (!aTableTransfer
) {
544 aTableTransfer
= aDT
->CreateFilter(FilterType::TABLE_TRANSFER
);
545 if (!aTableTransfer
) {
548 DisableAllTransfers(aTableTransfer
);
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());
560 case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE
: {
561 const nsTArray
<float>& tableValues
=
562 aFunctionAttributes
.mValues
[aInChannel
];
563 if (tableValues
.Length() < 1) return;
565 if (!aDiscreteTransfer
) {
566 aDiscreteTransfer
= aDT
->CreateFilter(FilterType::DISCRETE_TRANSFER
);
567 if (!aDiscreteTransfer
) {
570 DisableAllTransfers(aDiscreteTransfer
);
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());
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
) {
595 DisableAllTransfers(aLinearTransfer
);
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
);
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
) {
623 DisableAllTransfers(aGammaTransfer
);
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
);
638 case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY
:
644 const int32_t kMorphologyMaxRadius
= 100000;
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.
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
,
660 nsTArray
<RefPtr
<FilterNode
>>& aSources
,
661 nsTArray
<IntRect
>& aSourceRegions
,
662 nsTArray
<RefPtr
<SourceSurface
>>& aInputImages
)
663 : mDescription(aDescription
),
666 mSourceRegions(aSourceRegions
),
667 mInputImages(aInputImages
) {}
669 const FilterPrimitiveDescription
& mDescription
;
671 nsTArray
<RefPtr
<FilterNode
>>& mSources
;
672 nsTArray
<IntRect
>& mSourceRegions
;
673 nsTArray
<RefPtr
<SourceSurface
>>& mInputImages
;
675 already_AddRefed
<FilterNode
> operator()(
676 const EmptyAttributes
& aEmptyAttributes
) {
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
) {
686 if (mode
== SVG_FEBLEND_MODE_NORMAL
) {
687 filter
= mDT
->CreateFilter(FilterType::COMPOSITE
);
691 filter
->SetInput(IN_COMPOSITE_IN_START
, mSources
[1]);
692 filter
->SetInput(IN_COMPOSITE_IN_START
+ 1, mSources
[0]);
694 filter
= mDT
->CreateFilter(FilterType::BLEND
);
698 static const uint8_t blendModes
[SVG_FEBLEND_MODE_LUMINOSITY
+ 1] = {
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
,
713 BLEND_MODE_SATURATION
,
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]);
722 return filter
.forget();
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();
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]);
740 RefPtr
<FilterNode
> filter
= mDT
->CreateFilter(FilterType::COLOR_MATRIX
);
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();
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
;
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();
763 // Clamp radii to prevent completely insane values:
764 rx
= std::min(rx
, kMorphologyMaxRadius
);
765 ry
= std::min(ry
, kMorphologyMaxRadius
);
767 MorphologyOperator op
= aMorphology
.mOperator
== SVG_OPERATOR_ERODE
768 ? MORPHOLOGY_OPERATOR_ERODE
769 : MORPHOLOGY_OPERATOR_DILATE
;
771 RefPtr
<FilterNode
> filter
= mDT
->CreateFilter(FilterType::MORPHOLOGY
);
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();
781 already_AddRefed
<FilterNode
> operator()(const FloodAttributes
& aFlood
) {
782 DeviceColor color
= ToDeviceColor(aFlood
.mColor
);
783 RefPtr
<FilterNode
> filter
= mDT
->CreateFilter(FilterType::FLOOD
);
787 filter
->SetAttribute(ATT_FLOOD_COLOR
, color
);
788 return filter
.forget();
791 already_AddRefed
<FilterNode
> operator()(const TileAttributes
& aTile
) {
792 RefPtr
<FilterNode
> filter
= mDT
->CreateFilter(FilterType::TILE
);
796 filter
->SetAttribute(ATT_TILE_SOURCE_RECT
, mSourceRegions
[0]);
797 filter
->SetInput(IN_TILE_IN
, mSources
[0]);
798 return filter
.forget();
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
);
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
) &&
815 ConvertComponentTransferFunctionToFilter(aComponentTransfer
, inputIndex
,
816 i
, mDT
, filters
[0], filters
[1],
817 filters
[2], filters
[3]);
820 // Connect all used filters nodes.
821 RefPtr
<FilterNode
> lastFilter
= mSources
[0];
822 for (int32_t i
= 0; i
< 4; i
++) {
824 filters
[i
]->SetInput(0, lastFilter
);
825 lastFilter
= filters
[i
];
829 return lastFilter
.forget();
832 already_AddRefed
<FilterNode
> operator()(const OpacityAttributes
& aOpacity
) {
833 RefPtr
<FilterNode
> filter
= mDT
->CreateFilter(FilterType::OPACITY
);
837 filter
->SetAttribute(ATT_OPACITY_VALUE
, aOpacity
.mOpacity
);
838 filter
->SetInput(IN_OPACITY_IN
, mSources
[0]);
839 return filter
.forget();
842 already_AddRefed
<FilterNode
> operator()(
843 const ConvolveMatrixAttributes
& aConvolveMatrix
) {
844 RefPtr
<FilterNode
> filter
=
845 mDT
->CreateFilter(FilterType::CONVOLVE_MATRIX
);
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(),
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
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();
876 already_AddRefed
<FilterNode
> operator()(const OffsetAttributes
& aOffset
) {
877 return FilterWrappers::Offset(mDT
, mSources
[0], aOffset
.mValue
);
880 already_AddRefed
<FilterNode
> operator()(
881 const DisplacementMapAttributes
& aDisplacementMap
) {
882 RefPtr
<FilterNode
> filter
=
883 mDT
->CreateFilter(FilterType::DISPLACEMENT_MAP
);
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
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();
904 already_AddRefed
<FilterNode
> operator()(
905 const TurbulenceAttributes
& aTurbulence
) {
906 RefPtr
<FilterNode
> filter
= mDT
->CreateFilter(FilterType::TURBULENCE
);
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
920 filter
->SetAttribute(ATT_TURBULENCE_TYPE
,
921 (uint32_t)type
[aTurbulence
.mType
]);
922 filter
->SetAttribute(
924 mDescription
.PrimitiveSubregion() - aTurbulence
.mOffset
);
925 return FilterWrappers::Offset(mDT
, filter
, aTurbulence
.mOffset
);
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
)))) {
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]);
947 filter
= mDT
->CreateFilter(FilterType::COMPOSITE
);
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
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]);
965 return filter
.forget();
968 already_AddRefed
<FilterNode
> operator()(const MergeAttributes
& aMerge
) {
969 if (mSources
.Length() == 0) {
972 if (mSources
.Length() == 1) {
973 RefPtr
<FilterNode
> filter(mSources
[0]);
974 return filter
.forget();
976 RefPtr
<FilterNode
> filter
= mDT
->CreateFilter(FilterType::COMPOSITE
);
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
]);
985 return filter
.forget();
988 already_AddRefed
<FilterNode
> operator()(
989 const GaussianBlurAttributes
& aGaussianBlur
) {
990 return FilterWrappers::GaussianBlur(mDT
, mSources
[0],
991 aGaussianBlur
.mStdDeviation
);
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
);
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
);
1011 flood
->SetAttribute(ATT_FLOOD_COLOR
, ToDeviceColor(color
));
1013 RefPtr
<FilterNode
> composite
= mDT
->CreateFilter(FilterType::COMPOSITE
);
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
);
1022 RefPtr
<FilterNode
> filter
= mDT
->CreateFilter(FilterType::COMPOSITE
);
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();
1033 already_AddRefed
<FilterNode
> operator()(
1034 const SpecularLightingAttributes
& aLighting
) {
1036 *(static_cast<const DiffuseLightingAttributes
*>(&aLighting
)));
1039 already_AddRefed
<FilterNode
> operator()(
1040 const DiffuseLightingAttributes
& aLighting
) {
1042 mDescription
.Attributes().is
<SpecularLightingAttributes
>();
1044 if (aLighting
.mLightType
== LightType::None
) {
1048 enum { POINT
= 0, SPOT
, DISTANT
} lightType
= POINT
;
1050 switch (aLighting
.mLightType
) {
1051 case LightType::Point
:
1054 case LightType::Spot
:
1057 case LightType::Distant
:
1058 lightType
= DISTANT
;
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
]);
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
);
1081 filter
->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT
,
1082 aLighting
.mLightingConstant
);
1083 filter
->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
,
1084 aLighting
.mSpecularExponent
);
1086 filter
->SetAttribute(ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
,
1087 aLighting
.mLightingConstant
);
1090 switch (lightType
) {
1092 Point3D
position(aLighting
.mLightValues
[kPointLightPositionXIndex
],
1093 aLighting
.mLightValues
[kPointLightPositionYIndex
],
1094 aLighting
.mLightValues
[kPointLightPositionZIndex
]);
1095 filter
->SetAttribute(ATT_POINT_LIGHT_POSITION
, position
);
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
]);
1115 filter
->SetAttribute(
1116 ATT_DISTANT_LIGHT_AZIMUTH
,
1117 aLighting
.mLightValues
[kDistantLightAzimuthIndex
]);
1118 filter
->SetAttribute(
1119 ATT_DISTANT_LIGHT_ELEVATION
,
1120 aLighting
.mLightValues
[kDistantLightElevationIndex
]);
1125 filter
->SetInput(IN_LIGHTING_IN
, mSources
[0]);
1127 return filter
.forget();
1130 already_AddRefed
<FilterNode
> operator()(const ImageAttributes
& aImage
) {
1131 const Matrix
& TM
= aImage
.mTransform
;
1132 if (!TM
.Determinant()) {
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
];
1140 RefPtr
<FilterNode
> transform
= mDT
->CreateFilter(FilterType::TRANSFORM
);
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();
1150 already_AddRefed
<FilterNode
> operator()(const ToAlphaAttributes
& aToAlpha
) {
1151 return FilterWrappers::ToAlpha(mDT
, mSources
[0]);
1155 return aDescription
.Attributes().match(PrimitiveAttributesMatcher(
1156 aDescription
, aDT
, aSources
, aSourceRegions
, aInputImages
));
1159 template <typename T
>
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
) {
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
;
1174 MOZ_ASSERT(aIndex
>= 0, "bad index");
1175 return aPrimitiveElements
[aIndex
];
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
;
1187 if (atts
.is
<ColorMatrixAttributes
>() ||
1188 atts
.is
<ComponentTransferAttributes
>()) {
1189 return AlphaModel::Unpremultiplied
;
1191 if (atts
.is
<DisplacementMapAttributes
>()) {
1192 return aInputIndex
== 0 ? AlphaModel::Premultiplied
1193 : AlphaModel::Unpremultiplied
;
1195 if (atts
.is
<ConvolveMatrixAttributes
>()) {
1196 return atts
.as
<ConvolveMatrixAttributes
>().mPreserveAlpha
1197 ? AlphaModel::Unpremultiplied
1198 : AlphaModel::Premultiplied
;
1200 return AlphaModel::Premultiplied
;
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]);
1212 // All filters without inputs produce premultiplied alpha.
1213 return AlphaModel::Premultiplied
;
1216 // Returns the output FilterNode, in premultiplied sRGB space.
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());
1226 RefPtr
<FilterCachedColorModels
> sourceFilters
[4];
1227 nsTArray
<RefPtr
<FilterCachedColorModels
>> primitiveFilters
;
1229 for (size_t i
= 0; i
< primitives
.Length(); ++i
) {
1230 const FilterPrimitiveDescription
& descr
= primitives
[i
];
1232 nsTArray
<RefPtr
<FilterNode
>> inputFilterNodes
;
1233 nsTArray
<IntRect
> inputSourceRects
;
1234 nsTArray
<AlphaModel
> inputAlphaModels
;
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());
1241 inputSourceRects
.AppendElement(
1242 primitives
[inputIndex
].PrimitiveSubregion());
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
];
1252 "Referred to input filter that comes after the current one?");
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
];
1259 RefPtr
<FilterNode
> sourceFilterNode
;
1261 nsTArray
<FilterNode
*> primitiveFilters
;
1262 RefPtr
<FilterNode
> filt
=
1263 ElementForIndex(inputIndex
, primitiveFilters
, aSourceGraphic
,
1264 aFillPaint
, aStrokePaint
);
1266 sourceFilterNode
= filt
;
1268 // Clip the original SourceGraphic to the first filter region if the
1269 // surface isn't already sized appropriately.
1271 FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic
||
1273 FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha
) &&
1274 !descr
.FilterSpaceBounds().Contains(aSourceGraphicRect
)) {
1275 sourceFilterNode
= FilterWrappers::Crop(
1276 aDT
, sourceFilterNode
, descr
.FilterSpaceBounds());
1280 FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha
) {
1281 sourceFilterNode
= FilterWrappers::ToAlpha(aDT
, sourceFilterNode
);
1285 inputFilter
= new FilterCachedColorModels(aDT
, sourceFilterNode
,
1286 ColorModel::PremulSRGB());
1287 sourceFilters
[sourceIndex
] = inputFilter
;
1290 MOZ_ASSERT(inputFilter
);
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
));
1300 RefPtr
<FilterNode
> primitiveFilterNode
= FilterNodeFromPrimitiveDescription(
1301 descr
, aDT
, inputFilterNodes
, inputSourceRects
, aAdditionalImages
);
1303 if (primitiveFilterNode
) {
1304 primitiveFilterNode
= FilterWrappers::Crop(aDT
, primitiveFilterNode
,
1305 descr
.PrimitiveSubregion());
1308 ColorModel
outputColorModel(
1309 descr
.OutputColorSpace(),
1310 OutputAlphaModelForPrimitive(descr
, inputAlphaModels
));
1311 RefPtr
<FilterCachedColorModels
> primitiveFilter
=
1312 new FilterCachedColorModels(aDT
, primitiveFilterNode
, outputColorModel
);
1314 primitiveFilters
.AppendElement(primitiveFilter
);
1317 MOZ_RELEASE_ASSERT(!primitiveFilters
.IsEmpty());
1318 return primitiveFilters
.LastElement()->ForColorModel(
1319 ColorModel::PremulSRGB());
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());
1338 FilterWrappers::ForSurface(aDT
, aFillPaint
, aFillPaintRect
.TopLeft());
1341 strokePaint
= FilterWrappers::ForSurface(aDT
, aStrokePaint
,
1342 aStrokePaintRect
.TopLeft());
1344 RefPtr
<FilterNode
> resultFilter
= FilterNodeGraphFromDescription(
1345 aDT
, aFilter
, aRenderRect
, sourceGraphic
, aSourceGraphicRect
, fillPaint
,
1346 strokePaint
, aAdditionalImages
);
1347 if (!resultFilter
) {
1348 gfxWarning() << "Filter is NULL.";
1351 aDT
->DrawFilter(resultFilter
, aRenderRect
, aDestPoint
, aOptions
);
1354 static nsIntRegion
UnionOfRegions(const nsTArray
<nsIntRegion
>& aRegions
) {
1356 for (size_t i
= 0; i
< aRegions
.Length(); i
++) {
1357 result
.Or(result
, aRegions
[i
]);
1362 static int32_t InflateSizeForBlurStdDev(float aStdDev
) {
1364 std::min(aStdDev
, kMaxStdDeviation
) * (3 * sqrt(2 * M_PI
) / 4) * 1.5;
1365 return uint32_t(floor(size
+ 0.5));
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
) {}
1377 const FilterPrimitiveDescription
& mDescription
;
1378 const nsTArray
<nsIntRegion
>& mInputChangeRegions
;
1380 nsIntRegion
operator()(const EmptyAttributes
& aEmptyAttributes
) {
1381 return nsIntRegion();
1384 nsIntRegion
operator()(const BlendAttributes
& aBlend
) {
1385 return UnionOfRegions(mInputChangeRegions
);
1388 nsIntRegion
operator()(const ColorMatrixAttributes
& aColorMatrix
) {
1389 return mInputChangeRegions
[0];
1392 nsIntRegion
operator()(const MorphologyAttributes
& aMorphology
) {
1393 Size radii
= aMorphology
.mRadii
;
1394 int32_t rx
= clamped(int32_t(ceil(radii
.width
)), 0, kMorphologyMaxRadius
);
1396 clamped(int32_t(ceil(radii
.height
)), 0, kMorphologyMaxRadius
);
1397 return mInputChangeRegions
[0].Inflated(nsIntMargin(ry
, rx
, ry
, rx
));
1400 nsIntRegion
operator()(const FloodAttributes
& aFlood
) {
1401 return nsIntRegion();
1404 nsIntRegion
operator()(const TileAttributes
& aTile
) {
1405 return mDescription
.PrimitiveSubregion();
1408 nsIntRegion
operator()(
1409 const ComponentTransferAttributes
& aComponentTransfer
) {
1410 return mInputChangeRegions
[0];
1413 nsIntRegion
operator()(const OpacityAttributes
& aOpacity
) {
1414 return UnionOfRegions(mInputChangeRegions
);
1417 nsIntRegion
operator()(const ConvolveMatrixAttributes
& aConvolveMatrix
) {
1418 if (aConvolveMatrix
.mEdgeMode
!= EDGE_MODE_NONE
) {
1419 return mDescription
.PrimitiveSubregion();
1421 Size kernelUnitLength
= aConvolveMatrix
.mKernelUnitLength
;
1422 IntSize kernelSize
= aConvolveMatrix
.mKernelSize
;
1423 IntPoint target
= aConvolveMatrix
.mTarget
;
1425 static_cast<int32_t>(ceil(kernelUnitLength
.width
* (target
.x
))),
1426 static_cast<int32_t>(ceil(kernelUnitLength
.height
* (target
.y
))),
1427 static_cast<int32_t>(
1428 ceil(kernelUnitLength
.width
* (kernelSize
.width
- target
.x
- 1))),
1429 static_cast<int32_t>(ceil(kernelUnitLength
.height
*
1430 (kernelSize
.height
- target
.y
- 1))));
1431 return mInputChangeRegions
[0].Inflated(m
);
1434 nsIntRegion
operator()(const OffsetAttributes
& aOffset
) {
1435 IntPoint offset
= aOffset
.mValue
;
1436 return mInputChangeRegions
[0].MovedBy(offset
.x
, offset
.y
);
1439 nsIntRegion
operator()(const DisplacementMapAttributes
& aDisplacementMap
) {
1440 int32_t scale
= ceil(std::abs(aDisplacementMap
.mScale
));
1441 return mInputChangeRegions
[0].Inflated(
1442 nsIntMargin(scale
, scale
, scale
, scale
));
1445 nsIntRegion
operator()(const TurbulenceAttributes
& aTurbulence
) {
1446 return nsIntRegion();
1449 nsIntRegion
operator()(const CompositeAttributes
& aComposite
) {
1450 return UnionOfRegions(mInputChangeRegions
);
1453 nsIntRegion
operator()(const MergeAttributes
& aMerge
) {
1454 return UnionOfRegions(mInputChangeRegions
);
1457 nsIntRegion
operator()(const GaussianBlurAttributes
& aGaussianBlur
) {
1458 const Size
& stdDeviation
= aGaussianBlur
.mStdDeviation
;
1459 int32_t dx
= InflateSizeForBlurStdDev(stdDeviation
.width
);
1460 int32_t dy
= InflateSizeForBlurStdDev(stdDeviation
.height
);
1461 return mInputChangeRegions
[0].Inflated(nsIntMargin(dy
, dx
, dy
, dx
));
1464 nsIntRegion
operator()(const DropShadowAttributes
& aDropShadow
) {
1465 IntPoint offset
= IntPoint::Truncate(aDropShadow
.mOffset
);
1466 nsIntRegion offsetRegion
=
1467 mInputChangeRegions
[0].MovedBy(offset
.x
, offset
.y
);
1468 Size stdDeviation
= aDropShadow
.mStdDeviation
;
1469 int32_t dx
= InflateSizeForBlurStdDev(stdDeviation
.width
);
1470 int32_t dy
= InflateSizeForBlurStdDev(stdDeviation
.height
);
1471 nsIntRegion blurRegion
=
1472 offsetRegion
.Inflated(nsIntMargin(dy
, dx
, dy
, dx
));
1473 blurRegion
.Or(blurRegion
, mInputChangeRegions
[0]);
1477 nsIntRegion
operator()(const SpecularLightingAttributes
& aLighting
) {
1479 *(static_cast<const DiffuseLightingAttributes
*>(&aLighting
)));
1482 nsIntRegion
operator()(const DiffuseLightingAttributes
& aLighting
) {
1483 Size kernelUnitLength
= aLighting
.mKernelUnitLength
;
1484 int32_t dx
= ceil(kernelUnitLength
.width
);
1485 int32_t dy
= ceil(kernelUnitLength
.height
);
1486 return mInputChangeRegions
[0].Inflated(nsIntMargin(dy
, dx
, dy
, dx
));
1489 nsIntRegion
operator()(const ImageAttributes
& aImage
) {
1490 return nsIntRegion();
1493 nsIntRegion
operator()(const ToAlphaAttributes
& aToAlpha
) {
1494 return mInputChangeRegions
[0];
1498 return aDescription
.Attributes().match(
1499 PrimitiveAttributesMatcher(aDescription
, aInputChangeRegions
));
1503 nsIntRegion
FilterSupport::ComputeResultChangeRegion(
1504 const FilterDescription
& aFilter
, const nsIntRegion
& aSourceGraphicChange
,
1505 const nsIntRegion
& aFillPaintChange
,
1506 const nsIntRegion
& aStrokePaintChange
) {
1507 const nsTArray
<FilterPrimitiveDescription
>& primitives
= aFilter
.mPrimitives
;
1508 MOZ_RELEASE_ASSERT(!primitives
.IsEmpty());
1510 nsTArray
<nsIntRegion
> resultChangeRegions
;
1512 for (int32_t i
= 0; i
< int32_t(primitives
.Length()); ++i
) {
1513 const FilterPrimitiveDescription
& descr
= primitives
[i
];
1515 nsTArray
<nsIntRegion
> inputChangeRegions
;
1516 for (size_t j
= 0; j
< descr
.NumberOfInputs(); j
++) {
1517 int32_t inputIndex
= descr
.InputPrimitiveIndex(j
);
1518 MOZ_ASSERT(inputIndex
< i
, "bad input index");
1519 nsIntRegion inputChangeRegion
=
1520 ElementForIndex(inputIndex
, resultChangeRegions
, aSourceGraphicChange
,
1521 aFillPaintChange
, aStrokePaintChange
);
1522 inputChangeRegions
.AppendElement(inputChangeRegion
);
1524 nsIntRegion changeRegion
=
1525 ResultChangeRegionForPrimitive(descr
, inputChangeRegions
);
1526 changeRegion
.And(changeRegion
, descr
.PrimitiveSubregion());
1527 resultChangeRegions
.AppendElement(changeRegion
);
1530 MOZ_RELEASE_ASSERT(!resultChangeRegions
.IsEmpty());
1531 return resultChangeRegions
[resultChangeRegions
.Length() - 1];
1534 static float ResultOfZeroUnderTransferFunction(
1535 const ComponentTransferAttributes
& aFunctionAttributes
, int32_t channel
) {
1536 switch (aFunctionAttributes
.mTypes
[channel
]) {
1537 case SVG_FECOMPONENTTRANSFER_TYPE_TABLE
: {
1538 const nsTArray
<float>& tableValues
= aFunctionAttributes
.mValues
[channel
];
1539 if (tableValues
.Length() < 2) {
1542 return tableValues
[0];
1545 case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE
: {
1546 const nsTArray
<float>& tableValues
= aFunctionAttributes
.mValues
[channel
];
1547 if (tableValues
.Length() < 1) {
1550 return tableValues
[0];
1553 case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR
: {
1554 const nsTArray
<float>& values
= aFunctionAttributes
.mValues
[channel
];
1555 return values
[kComponentTransferInterceptIndex
];
1558 case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA
: {
1559 const nsTArray
<float>& values
= aFunctionAttributes
.mValues
[channel
];
1560 return values
[kComponentTransferOffsetIndex
];
1563 case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY
:
1569 nsIntRegion
FilterSupport::PostFilterExtentsForPrimitive(
1570 const FilterPrimitiveDescription
& aDescription
,
1571 const nsTArray
<nsIntRegion
>& aInputExtents
) {
1572 struct PrimitiveAttributesMatcher
{
1573 PrimitiveAttributesMatcher(const FilterPrimitiveDescription
& aDescription
,
1574 const nsTArray
<nsIntRegion
>& aInputExtents
)
1575 : mDescription(aDescription
), mInputExtents(aInputExtents
) {}
1577 const FilterPrimitiveDescription
& mDescription
;
1578 const nsTArray
<nsIntRegion
>& mInputExtents
;
1580 nsIntRegion
operator()(const EmptyAttributes
& aEmptyAttributes
) {
1584 nsIntRegion
operator()(const BlendAttributes
& aBlend
) {
1585 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1588 nsIntRegion
operator()(const ColorMatrixAttributes
& aColorMatrix
) {
1589 if (aColorMatrix
.mType
== (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX
) {
1590 const nsTArray
<float>& values
= aColorMatrix
.mValues
;
1591 if (values
.Length() == 20 && values
[19] > 0.0f
) {
1592 return mDescription
.PrimitiveSubregion();
1595 return mInputExtents
[0];
1598 nsIntRegion
operator()(const MorphologyAttributes
& aMorphology
) {
1599 uint32_t op
= aMorphology
.mOperator
;
1600 if (op
== SVG_OPERATOR_ERODE
) {
1601 return mInputExtents
[0];
1603 Size radii
= aMorphology
.mRadii
;
1604 int32_t rx
= clamped(int32_t(ceil(radii
.width
)), 0, kMorphologyMaxRadius
);
1606 clamped(int32_t(ceil(radii
.height
)), 0, kMorphologyMaxRadius
);
1607 return mInputExtents
[0].Inflated(nsIntMargin(ry
, rx
, ry
, rx
));
1610 nsIntRegion
operator()(const FloodAttributes
& aFlood
) {
1611 if (aFlood
.mColor
.a
== 0.0f
) {
1614 return mDescription
.PrimitiveSubregion();
1617 nsIntRegion
operator()(const TileAttributes
& aTile
) {
1618 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1621 nsIntRegion
operator()(
1622 const ComponentTransferAttributes
& aComponentTransfer
) {
1623 if (ResultOfZeroUnderTransferFunction(aComponentTransfer
, kChannelA
) >
1625 return mDescription
.PrimitiveSubregion();
1627 return mInputExtents
[0];
1630 nsIntRegion
operator()(const OpacityAttributes
& aOpacity
) {
1631 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1634 nsIntRegion
operator()(const ConvolveMatrixAttributes
& aConvolveMatrix
) {
1635 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1638 nsIntRegion
operator()(const OffsetAttributes
& aOffset
) {
1639 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1642 nsIntRegion
operator()(const DisplacementMapAttributes
& aDisplacementMap
) {
1643 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1646 nsIntRegion
operator()(const TurbulenceAttributes
& aTurbulence
) {
1647 return mDescription
.PrimitiveSubregion();
1650 nsIntRegion
operator()(const CompositeAttributes
& aComposite
) {
1651 uint32_t op
= aComposite
.mOperator
;
1652 if (op
== SVG_FECOMPOSITE_OPERATOR_ARITHMETIC
) {
1653 // The arithmetic composite primitive can draw outside the bounding
1654 // box of its source images.
1655 const nsTArray
<float>& coefficients
= aComposite
.mCoefficients
;
1656 MOZ_ASSERT(coefficients
.Length() == 4);
1658 // The calculation is:
1659 // r = c[0] * in[0] * in[1] + c[1] * in[0] + c[2] * in[1] + c[3]
1661 if (coefficients
[0] > 0.0f
) {
1662 region
= mInputExtents
[0].Intersect(mInputExtents
[1]);
1664 if (coefficients
[1] > 0.0f
) {
1665 region
.Or(region
, mInputExtents
[0]);
1667 if (coefficients
[2] > 0.0f
) {
1668 region
.Or(region
, mInputExtents
[1]);
1670 if (coefficients
[3] > 0.0f
) {
1671 region
= mDescription
.PrimitiveSubregion();
1675 if (op
== SVG_FECOMPOSITE_OPERATOR_IN
) {
1676 return mInputExtents
[0].Intersect(mInputExtents
[1]);
1678 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1681 nsIntRegion
operator()(const MergeAttributes
& aMerge
) {
1682 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1685 nsIntRegion
operator()(const GaussianBlurAttributes
& aGaussianBlur
) {
1686 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1689 nsIntRegion
operator()(const DropShadowAttributes
& aDropShadow
) {
1690 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1693 nsIntRegion
operator()(const DiffuseLightingAttributes
& aDiffuseLighting
) {
1694 return mDescription
.PrimitiveSubregion();
1697 nsIntRegion
operator()(
1698 const SpecularLightingAttributes
& aSpecularLighting
) {
1699 return mDescription
.PrimitiveSubregion();
1702 nsIntRegion
operator()(const ImageAttributes
& aImage
) {
1703 return mDescription
.PrimitiveSubregion();
1706 nsIntRegion
operator()(const ToAlphaAttributes
& aToAlpha
) {
1707 return ResultChangeRegionForPrimitive(mDescription
, mInputExtents
);
1711 return aDescription
.Attributes().match(
1712 PrimitiveAttributesMatcher(aDescription
, aInputExtents
));
1716 nsIntRegion
FilterSupport::ComputePostFilterExtents(
1717 const FilterDescription
& aFilter
,
1718 const nsIntRegion
& aSourceGraphicExtents
) {
1719 const nsTArray
<FilterPrimitiveDescription
>& primitives
= aFilter
.mPrimitives
;
1720 MOZ_RELEASE_ASSERT(!primitives
.IsEmpty());
1721 nsTArray
<nsIntRegion
> postFilterExtents
;
1723 for (int32_t i
= 0; i
< int32_t(primitives
.Length()); ++i
) {
1724 const FilterPrimitiveDescription
& descr
= primitives
[i
];
1725 nsIntRegion filterSpace
= descr
.FilterSpaceBounds();
1727 nsTArray
<nsIntRegion
> inputExtents
;
1728 for (size_t j
= 0; j
< descr
.NumberOfInputs(); j
++) {
1729 int32_t inputIndex
= descr
.InputPrimitiveIndex(j
);
1730 MOZ_ASSERT(inputIndex
< i
, "bad input index");
1731 nsIntRegion inputExtent
=
1732 ElementForIndex(inputIndex
, postFilterExtents
, aSourceGraphicExtents
,
1733 filterSpace
, filterSpace
);
1734 inputExtents
.AppendElement(inputExtent
);
1736 nsIntRegion extent
= PostFilterExtentsForPrimitive(descr
, inputExtents
);
1737 extent
.And(extent
, descr
.PrimitiveSubregion());
1738 postFilterExtents
.AppendElement(extent
);
1741 MOZ_RELEASE_ASSERT(!postFilterExtents
.IsEmpty());
1742 return postFilterExtents
[postFilterExtents
.Length() - 1];
1745 static nsIntRegion
SourceNeededRegionForPrimitive(
1746 const FilterPrimitiveDescription
& aDescription
,
1747 const nsIntRegion
& aResultNeededRegion
, int32_t aInputIndex
) {
1748 struct PrimitiveAttributesMatcher
{
1749 PrimitiveAttributesMatcher(const FilterPrimitiveDescription
& aDescription
,
1750 const nsIntRegion
& aResultNeededRegion
,
1751 int32_t aInputIndex
)
1752 : mDescription(aDescription
),
1753 mResultNeededRegion(aResultNeededRegion
),
1754 mInputIndex(aInputIndex
) {}
1756 const FilterPrimitiveDescription
& mDescription
;
1757 const nsIntRegion
& mResultNeededRegion
;
1758 const int32_t mInputIndex
;
1760 nsIntRegion
operator()(const EmptyAttributes
& aEmptyAttributes
) {
1761 return nsIntRegion();
1764 nsIntRegion
operator()(const BlendAttributes
& aBlend
) {
1765 return mResultNeededRegion
;
1768 nsIntRegion
operator()(const ColorMatrixAttributes
& aColorMatrix
) {
1769 return mResultNeededRegion
;
1772 nsIntRegion
operator()(const MorphologyAttributes
& aMorphology
) {
1773 Size radii
= aMorphology
.mRadii
;
1774 int32_t rx
= clamped(int32_t(ceil(radii
.width
)), 0, kMorphologyMaxRadius
);
1776 clamped(int32_t(ceil(radii
.height
)), 0, kMorphologyMaxRadius
);
1777 return mResultNeededRegion
.Inflated(nsIntMargin(ry
, rx
, ry
, rx
));
1780 nsIntRegion
operator()(const FloodAttributes
& aFlood
) {
1781 MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
1782 return nsIntRegion();
1785 nsIntRegion
operator()(const TileAttributes
& aTile
) {
1786 return IntRect(INT32_MIN
/ 2, INT32_MIN
/ 2, INT32_MAX
, INT32_MAX
);
1789 nsIntRegion
operator()(
1790 const ComponentTransferAttributes
& aComponentTransfer
) {
1791 return mResultNeededRegion
;
1794 nsIntRegion
operator()(const OpacityAttributes
& aOpacity
) {
1795 return mResultNeededRegion
;
1798 nsIntRegion
operator()(const ConvolveMatrixAttributes
& aConvolveMatrix
) {
1799 Size kernelUnitLength
= aConvolveMatrix
.mKernelUnitLength
;
1800 IntSize kernelSize
= aConvolveMatrix
.mKernelSize
;
1801 IntPoint target
= aConvolveMatrix
.mTarget
;
1803 static_cast<int32_t>(
1804 ceil(kernelUnitLength
.width
* (kernelSize
.width
- target
.x
- 1))),
1805 static_cast<int32_t>(ceil(kernelUnitLength
.height
*
1806 (kernelSize
.height
- target
.y
- 1))),
1807 static_cast<int32_t>(ceil(kernelUnitLength
.width
* (target
.x
))),
1808 static_cast<int32_t>(ceil(kernelUnitLength
.height
* (target
.y
))));
1809 return mResultNeededRegion
.Inflated(m
);
1812 nsIntRegion
operator()(const OffsetAttributes
& aOffset
) {
1813 IntPoint offset
= aOffset
.mValue
;
1814 return mResultNeededRegion
.MovedBy(-nsIntPoint(offset
.x
, offset
.y
));
1817 nsIntRegion
operator()(const DisplacementMapAttributes
& aDisplacementMap
) {
1818 if (mInputIndex
== 1) {
1819 return mResultNeededRegion
;
1821 int32_t scale
= ceil(std::abs(aDisplacementMap
.mScale
));
1822 return mResultNeededRegion
.Inflated(
1823 nsIntMargin(scale
, scale
, scale
, scale
));
1826 nsIntRegion
operator()(const TurbulenceAttributes
& aTurbulence
) {
1827 MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
1828 return nsIntRegion();
1831 nsIntRegion
operator()(const CompositeAttributes
& aComposite
) {
1832 return mResultNeededRegion
;
1835 nsIntRegion
operator()(const MergeAttributes
& aMerge
) {
1836 return mResultNeededRegion
;
1839 nsIntRegion
operator()(const GaussianBlurAttributes
& aGaussianBlur
) {
1840 const Size
& stdDeviation
= aGaussianBlur
.mStdDeviation
;
1841 int32_t dx
= InflateSizeForBlurStdDev(stdDeviation
.width
);
1842 int32_t dy
= InflateSizeForBlurStdDev(stdDeviation
.height
);
1843 return mResultNeededRegion
.Inflated(nsIntMargin(dy
, dx
, dy
, dx
));
1846 nsIntRegion
operator()(const DropShadowAttributes
& aDropShadow
) {
1847 IntPoint offset
= IntPoint::Truncate(aDropShadow
.mOffset
);
1848 nsIntRegion offsetRegion
=
1849 mResultNeededRegion
.MovedBy(-nsIntPoint(offset
.x
, offset
.y
));
1850 Size stdDeviation
= aDropShadow
.mStdDeviation
;
1851 int32_t dx
= InflateSizeForBlurStdDev(stdDeviation
.width
);
1852 int32_t dy
= InflateSizeForBlurStdDev(stdDeviation
.height
);
1853 nsIntRegion blurRegion
=
1854 offsetRegion
.Inflated(nsIntMargin(dy
, dx
, dy
, dx
));
1855 blurRegion
.Or(blurRegion
, mResultNeededRegion
);
1859 nsIntRegion
operator()(const SpecularLightingAttributes
& aLighting
) {
1861 *(static_cast<const DiffuseLightingAttributes
*>(&aLighting
)));
1864 nsIntRegion
operator()(const DiffuseLightingAttributes
& aLighting
) {
1865 Size kernelUnitLength
= aLighting
.mKernelUnitLength
;
1866 int32_t dx
= ceil(kernelUnitLength
.width
);
1867 int32_t dy
= ceil(kernelUnitLength
.height
);
1868 return mResultNeededRegion
.Inflated(nsIntMargin(dy
, dx
, dy
, dx
));
1871 nsIntRegion
operator()(const ImageAttributes
& aImage
) {
1872 MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
1873 return nsIntRegion();
1876 nsIntRegion
operator()(const ToAlphaAttributes
& aToAlpha
) {
1877 return mResultNeededRegion
;
1881 return aDescription
.Attributes().match(PrimitiveAttributesMatcher(
1882 aDescription
, aResultNeededRegion
, aInputIndex
));
1886 void FilterSupport::ComputeSourceNeededRegions(
1887 const FilterDescription
& aFilter
, const nsIntRegion
& aResultNeededRegion
,
1888 nsIntRegion
& aSourceGraphicNeededRegion
,
1889 nsIntRegion
& aFillPaintNeededRegion
,
1890 nsIntRegion
& aStrokePaintNeededRegion
) {
1891 const nsTArray
<FilterPrimitiveDescription
>& primitives
= aFilter
.mPrimitives
;
1892 MOZ_ASSERT(!primitives
.IsEmpty());
1893 if (primitives
.IsEmpty()) {
1897 nsTArray
<nsIntRegion
> primitiveNeededRegions
;
1898 primitiveNeededRegions
.AppendElements(primitives
.Length());
1900 primitiveNeededRegions
[primitives
.Length() - 1] = aResultNeededRegion
;
1902 for (int32_t i
= primitives
.Length() - 1; i
>= 0; --i
) {
1903 const FilterPrimitiveDescription
& descr
= primitives
[i
];
1904 nsIntRegion neededRegion
= primitiveNeededRegions
[i
];
1905 neededRegion
.And(neededRegion
, descr
.PrimitiveSubregion());
1907 for (size_t j
= 0; j
< descr
.NumberOfInputs(); j
++) {
1908 int32_t inputIndex
= descr
.InputPrimitiveIndex(j
);
1909 MOZ_ASSERT(inputIndex
< i
, "bad input index");
1910 nsIntRegion
* inputNeededRegion
=
1911 const_cast<nsIntRegion
*>(&ElementForIndex(
1912 inputIndex
, primitiveNeededRegions
, aSourceGraphicNeededRegion
,
1913 aFillPaintNeededRegion
, aStrokePaintNeededRegion
));
1914 inputNeededRegion
->Or(*inputNeededRegion
, SourceNeededRegionForPrimitive(
1915 descr
, neededRegion
, j
));
1919 // Clip original SourceGraphic to first filter region.
1920 const FilterPrimitiveDescription
& firstDescr
= primitives
[0];
1921 aSourceGraphicNeededRegion
.And(aSourceGraphicNeededRegion
,
1922 firstDescr
.FilterSpaceBounds());
1925 // FilterPrimitiveDescription
1927 FilterPrimitiveDescription::FilterPrimitiveDescription()
1928 : mAttributes(EmptyAttributes()),
1929 mOutputColorSpace(ColorSpace::SRGB
),
1930 mIsTainted(false) {}
1932 FilterPrimitiveDescription::FilterPrimitiveDescription(
1933 PrimitiveAttributes
&& aAttributes
)
1934 : mAttributes(std::move(aAttributes
)),
1935 mOutputColorSpace(ColorSpace::SRGB
),
1936 mIsTainted(false) {}
1938 bool FilterPrimitiveDescription::operator==(
1939 const FilterPrimitiveDescription
& aOther
) const {
1940 return mFilterPrimitiveSubregion
.IsEqualInterior(
1941 aOther
.mFilterPrimitiveSubregion
) &&
1942 mFilterSpaceBounds
.IsEqualInterior(aOther
.mFilterSpaceBounds
) &&
1943 mOutputColorSpace
== aOther
.mOutputColorSpace
&&
1944 mIsTainted
== aOther
.mIsTainted
&&
1945 mInputPrimitives
== aOther
.mInputPrimitives
&&
1946 mInputColorSpaces
== aOther
.mInputColorSpaces
&&
1947 mAttributes
== aOther
.mAttributes
;
1950 // FilterDescription
1952 bool FilterDescription::operator==(const FilterDescription
& aOther
) const {
1953 return mPrimitives
== aOther
.mPrimitives
;
1957 } // namespace mozilla