Bug 1891710: part 2) Enable <Element-outerHTML.html> WPT for Trusted Types. r=smaug
[gecko.git] / gfx / src / FilterSupport.cpp
blobd46de042f75b894e181d2fa475c16fff3e3f06b0
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"
19 #include "gfxUtils.h"
20 #include "gfx2DGlue.h"
22 #include "nsMargin.h"
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};
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};
90 namespace mozilla {
91 namespace gfx {
93 // Some convenience FilterNode creation functions.
95 namespace FilterWrappers {
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();
104 return nullptr;
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();
114 return nullptr;
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();
135 return nullptr;
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();
156 return nullptr;
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();
168 return nullptr;
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();
181 return nullptr;
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();
196 return nullptr;
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();
211 return nullptr;
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();
220 return nullptr;
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();
234 return nullptr;
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();
253 return nullptr;
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 {
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);
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;
281 private:
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,
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;
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
363 // an image.
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
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
391 // in SVG.
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) {
422 return false;
425 PodCopy(aOutMatrix, aMatrixAttributes.mValues.Elements(), 20);
426 break;
429 case SVG_FECOLORMATRIX_TYPE_SATURATE: {
430 if (aMatrixAttributes.mValues.Length() != 1) {
431 return false;
434 float s = aMatrixAttributes.mValues[0];
436 if (s < 0) {
437 return false;
440 InterpolateFromIdentityMatrix(saturateMatrix, 1 - s, aOutMatrix);
441 break;
444 case SVG_FECOLORMATRIX_TYPE_HUE_ROTATE: {
445 if (aMatrixAttributes.mValues.Length() != 1) {
446 return false;
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;
468 break;
471 case SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA: {
472 PodCopy(aOutMatrix, luminanceToAlphaMatrix, 20);
473 break;
476 case SVG_FECOLORMATRIX_TYPE_SEPIA: {
477 if (aMatrixAttributes.mValues.Length() != 1) {
478 return false;
481 float amount = aMatrixAttributes.mValues[0];
483 if (amount < 0 || amount > 1) {
484 return false;
487 InterpolateFromIdentityMatrix(sepiaMatrix, amount, aOutMatrix);
488 break;
491 default: {
492 return false;
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
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.
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.
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];
537 switch (type) {
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) {
546 return;
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());
557 break;
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) {
568 return;
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());
580 break;
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;
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);
605 break;
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;
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);
635 break;
638 case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
639 default:
640 break;
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,
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) {}
669 const FilterPrimitiveDescription& mDescription;
670 DrawTarget* mDT;
671 nsTArray<RefPtr<FilterNode>>& mSources;
672 nsTArray<IntRect>& mSourceRegions;
673 nsTArray<RefPtr<SourceSurface>>& mInputImages;
675 already_AddRefed<FilterNode> operator()(
676 const EmptyAttributes& aEmptyAttributes) {
677 return nullptr;
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;
686 if (mode == SVG_FEBLEND_MODE_NORMAL) {
687 filter = mDT->CreateFilter(FilterType::COMPOSITE);
688 if (!filter) {
689 return nullptr;
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;
698 static const uint8_t blendModes[SVG_FEBLEND_MODE_LUMINOSITY + 1] = {
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]);
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();
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]);
740 RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::COLOR_MATRIX);
741 if (!filter) {
742 return nullptr;
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);
772 if (!filter) {
773 return nullptr;
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);
784 if (!filter) {
785 return nullptr;
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);
793 if (!filter) {
794 return nullptr;
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) &&
812 (i < 3)
814 : i;
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++) {
823 if (filters[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);
834 if (!filter) {
835 return nullptr;
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);
846 if (!filter) {
847 return nullptr;
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
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);
884 if (!filter) {
885 return nullptr;
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);
907 if (!filter) {
908 return nullptr;
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(
923 ATT_TURBULENCE_RECT,
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)))) {
940 return nullptr;
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;
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) {
970 return nullptr;
972 if (mSources.Length() == 1) {
973 RefPtr<FilterNode> filter(mSources[0]);
974 return filter.forget();
976 RefPtr<FilterNode> filter = mDT->CreateFilter(FilterType::COMPOSITE);
977 if (!filter) {
978 return nullptr;
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);
1002 if (!flood) {
1003 return nullptr;
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);
1014 if (!composite) {
1015 return nullptr;
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);
1023 if (!filter) {
1024 return nullptr;
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) {
1035 return operator()(
1036 *(static_cast<const DiffuseLightingAttributes*>(&aLighting)));
1039 already_AddRefed<FilterNode> operator()(
1040 const DiffuseLightingAttributes& aLighting) {
1041 bool isSpecular =
1042 mDescription.Attributes().is<SpecularLightingAttributes>();
1044 if (aLighting.mLightType == LightType::None) {
1045 return nullptr;
1048 enum { POINT = 0, SPOT, DISTANT } lightType = POINT;
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;
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;
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);
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);
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;
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;
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;
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()) {
1133 return nullptr;
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);
1141 if (!transform) {
1142 return nullptr;
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) {
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];
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());
1240 } else {
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];
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;
1261 nsTArray<FilterNode*> primitiveFilters;
1262 RefPtr<FilterNode> filt =
1263 ElementForIndex(inputIndex, primitiveFilters, aSourceGraphic,
1264 aFillPaint, aStrokePaint);
1265 if (filt) {
1266 sourceFilterNode = filt;
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());
1279 if (inputIndex ==
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());
1322 // FilterSupport
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());
1336 if (aFillPaint) {
1337 fillPaint =
1338 FilterWrappers::ForSurface(aDT, aFillPaint, aFillPaintRect.TopLeft());
1340 if (aStrokePaint) {
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.";
1349 return;
1351 aDT->DrawFilter(resultFilter, aRenderRect, aDestPoint, aOptions);
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]);
1359 return result;
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));
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);
1395 int32_t ry =
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;
1424 nsIntMargin m(
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]);
1474 return blurRegion;
1477 nsIntRegion operator()(const SpecularLightingAttributes& aLighting) {
1478 return operator()(
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));
1502 /* static */
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) {
1540 return 0.0f;
1542 return tableValues[0];
1545 case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE: {
1546 const nsTArray<float>& tableValues = aFunctionAttributes.mValues[channel];
1547 if (tableValues.Length() < 1) {
1548 return 0.0f;
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:
1564 default:
1565 return 0.0f;
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) {
1581 return IntRect();
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);
1605 int32_t ry =
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) {
1612 return IntRect();
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) >
1624 0.0f) {
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]
1660 nsIntRegion region;
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();
1673 return region;
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));
1715 /* static */
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);
1775 int32_t ry =
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;
1802 nsIntMargin m(
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);
1856 return blurRegion;
1859 nsIntRegion operator()(const SpecularLightingAttributes& aLighting) {
1860 return operator()(
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));
1885 /* static */
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()) {
1894 return;
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;
1956 } // namespace gfx
1957 } // namespace mozilla