Bumping manifests a=b2g-bump
[gecko.git] / gfx / src / FilterSupport.cpp
bloba747e8446266ef30219473d6fbb78381836f3a77
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "FilterSupport.h"
8 #include "mozilla/gfx/2D.h"
9 #include "mozilla/gfx/Filters.h"
10 #include "mozilla/PodOperations.h"
12 #include "gfxContext.h"
13 #include "gfxPattern.h"
14 #include "gfxPlatform.h"
15 #include "gfx2DGlue.h"
17 #include "nsMargin.h"
19 // c = n / 255
20 // c <= 0.0031308f ? c * 12.92f : 1.055f * powf(c, 1 / 2.4f) - 0.055f
21 static const float glinearRGBTosRGBMap[256] = {
22 0.000f, 0.050f, 0.085f, 0.111f, 0.132f, 0.150f, 0.166f, 0.181f,
23 0.194f, 0.207f, 0.219f, 0.230f, 0.240f, 0.250f, 0.260f, 0.269f,
24 0.278f, 0.286f, 0.295f, 0.303f, 0.310f, 0.318f, 0.325f, 0.332f,
25 0.339f, 0.346f, 0.352f, 0.359f, 0.365f, 0.371f, 0.378f, 0.383f,
26 0.389f, 0.395f, 0.401f, 0.406f, 0.412f, 0.417f, 0.422f, 0.427f,
27 0.433f, 0.438f, 0.443f, 0.448f, 0.452f, 0.457f, 0.462f, 0.466f,
28 0.471f, 0.476f, 0.480f, 0.485f, 0.489f, 0.493f, 0.498f, 0.502f,
29 0.506f, 0.510f, 0.514f, 0.518f, 0.522f, 0.526f, 0.530f, 0.534f,
30 0.538f, 0.542f, 0.546f, 0.549f, 0.553f, 0.557f, 0.561f, 0.564f,
31 0.568f, 0.571f, 0.575f, 0.579f, 0.582f, 0.586f, 0.589f, 0.592f,
32 0.596f, 0.599f, 0.603f, 0.606f, 0.609f, 0.613f, 0.616f, 0.619f,
33 0.622f, 0.625f, 0.629f, 0.632f, 0.635f, 0.638f, 0.641f, 0.644f,
34 0.647f, 0.650f, 0.653f, 0.656f, 0.659f, 0.662f, 0.665f, 0.668f,
35 0.671f, 0.674f, 0.677f, 0.680f, 0.683f, 0.685f, 0.688f, 0.691f,
36 0.694f, 0.697f, 0.699f, 0.702f, 0.705f, 0.708f, 0.710f, 0.713f,
37 0.716f, 0.718f, 0.721f, 0.724f, 0.726f, 0.729f, 0.731f, 0.734f,
38 0.737f, 0.739f, 0.742f, 0.744f, 0.747f, 0.749f, 0.752f, 0.754f,
39 0.757f, 0.759f, 0.762f, 0.764f, 0.767f, 0.769f, 0.772f, 0.774f,
40 0.776f, 0.779f, 0.781f, 0.784f, 0.786f, 0.788f, 0.791f, 0.793f,
41 0.795f, 0.798f, 0.800f, 0.802f, 0.805f, 0.807f, 0.809f, 0.812f,
42 0.814f, 0.816f, 0.818f, 0.821f, 0.823f, 0.825f, 0.827f, 0.829f,
43 0.832f, 0.834f, 0.836f, 0.838f, 0.840f, 0.843f, 0.845f, 0.847f,
44 0.849f, 0.851f, 0.853f, 0.855f, 0.857f, 0.860f, 0.862f, 0.864f,
45 0.866f, 0.868f, 0.870f, 0.872f, 0.874f, 0.876f, 0.878f, 0.880f,
46 0.882f, 0.884f, 0.886f, 0.888f, 0.890f, 0.892f, 0.894f, 0.896f,
47 0.898f, 0.900f, 0.902f, 0.904f, 0.906f, 0.908f, 0.910f, 0.912f,
48 0.914f, 0.916f, 0.918f, 0.920f, 0.922f, 0.924f, 0.926f, 0.928f,
49 0.930f, 0.931f, 0.933f, 0.935f, 0.937f, 0.939f, 0.941f, 0.943f,
50 0.945f, 0.946f, 0.948f, 0.950f, 0.952f, 0.954f, 0.956f, 0.957f,
51 0.959f, 0.961f, 0.963f, 0.965f, 0.967f, 0.968f, 0.970f, 0.972f,
52 0.974f, 0.975f, 0.977f, 0.979f, 0.981f, 0.983f, 0.984f, 0.986f,
53 0.988f, 0.990f, 0.991f, 0.993f, 0.995f, 0.997f, 0.998f, 1.000f
56 // c = n / 255
57 // c <= 0.04045f ? c / 12.92f : powf((c + 0.055f) / 1.055f, 2.4f)
58 static const float gsRGBToLinearRGBMap[256] = {
59 0.000f, 0.000f, 0.001f, 0.001f, 0.001f, 0.002f, 0.002f, 0.002f,
60 0.002f, 0.003f, 0.003f, 0.003f, 0.004f, 0.004f, 0.004f, 0.005f,
61 0.005f, 0.006f, 0.006f, 0.007f, 0.007f, 0.007f, 0.008f, 0.009f,
62 0.009f, 0.010f, 0.010f, 0.011f, 0.012f, 0.012f, 0.013f, 0.014f,
63 0.014f, 0.015f, 0.016f, 0.017f, 0.018f, 0.019f, 0.019f, 0.020f,
64 0.021f, 0.022f, 0.023f, 0.024f, 0.025f, 0.026f, 0.027f, 0.028f,
65 0.030f, 0.031f, 0.032f, 0.033f, 0.034f, 0.036f, 0.037f, 0.038f,
66 0.040f, 0.041f, 0.042f, 0.044f, 0.045f, 0.047f, 0.048f, 0.050f,
67 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,
69 0.080f, 0.082f, 0.084f, 0.087f, 0.089f, 0.091f, 0.093f, 0.095f,
70 0.098f, 0.100f, 0.102f, 0.105f, 0.107f, 0.109f, 0.112f, 0.114f,
71 0.117f, 0.120f, 0.122f, 0.125f, 0.127f, 0.130f, 0.133f, 0.136f,
72 0.138f, 0.141f, 0.144f, 0.147f, 0.150f, 0.153f, 0.156f, 0.159f,
73 0.162f, 0.165f, 0.168f, 0.171f, 0.175f, 0.178f, 0.181f, 0.184f,
74 0.188f, 0.191f, 0.195f, 0.198f, 0.202f, 0.205f, 0.209f, 0.212f,
75 0.216f, 0.220f, 0.223f, 0.227f, 0.231f, 0.235f, 0.238f, 0.242f,
76 0.246f, 0.250f, 0.254f, 0.258f, 0.262f, 0.266f, 0.270f, 0.275f,
77 0.279f, 0.283f, 0.287f, 0.292f, 0.296f, 0.301f, 0.305f, 0.309f,
78 0.314f, 0.319f, 0.323f, 0.328f, 0.332f, 0.337f, 0.342f, 0.347f,
79 0.352f, 0.356f, 0.361f, 0.366f, 0.371f, 0.376f, 0.381f, 0.386f,
80 0.392f, 0.397f, 0.402f, 0.407f, 0.413f, 0.418f, 0.423f, 0.429f,
81 0.434f, 0.440f, 0.445f, 0.451f, 0.456f, 0.462f, 0.468f, 0.474f,
82 0.479f, 0.485f, 0.491f, 0.497f, 0.503f, 0.509f, 0.515f, 0.521f,
83 0.527f, 0.533f, 0.539f, 0.546f, 0.552f, 0.558f, 0.565f, 0.571f,
84 0.578f, 0.584f, 0.591f, 0.597f, 0.604f, 0.610f, 0.617f, 0.624f,
85 0.631f, 0.638f, 0.644f, 0.651f, 0.658f, 0.665f, 0.672f, 0.680f,
86 0.687f, 0.694f, 0.701f, 0.708f, 0.716f, 0.723f, 0.730f, 0.738f,
87 0.745f, 0.753f, 0.761f, 0.768f, 0.776f, 0.784f, 0.791f, 0.799f,
88 0.807f, 0.815f, 0.823f, 0.831f, 0.839f, 0.847f, 0.855f, 0.863f,
89 0.871f, 0.880f, 0.888f, 0.896f, 0.905f, 0.913f, 0.922f, 0.930f,
90 0.939f, 0.947f, 0.956f, 0.965f, 0.973f, 0.982f, 0.991f, 1.000f
93 namespace mozilla {
94 namespace gfx {
96 // Some convenience FilterNode creation functions.
98 namespace FilterWrappers {
100 static TemporaryRef<FilterNode>
101 Unpremultiply(DrawTarget* aDT, FilterNode* aInput)
103 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::UNPREMULTIPLY);
104 filter->SetInput(IN_UNPREMULTIPLY_IN, aInput);
105 return filter;
108 static TemporaryRef<FilterNode>
109 Premultiply(DrawTarget* aDT, FilterNode* aInput)
111 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::PREMULTIPLY);
112 filter->SetInput(IN_PREMULTIPLY_IN, aInput);
113 return filter;
116 static TemporaryRef<FilterNode>
117 LinearRGBToSRGB(DrawTarget* aDT, FilterNode* aInput)
119 RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
120 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
121 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, glinearRGBTosRGBMap, 256);
122 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
123 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, glinearRGBTosRGBMap, 256);
124 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
125 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, glinearRGBTosRGBMap, 256);
126 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
127 transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
128 return transfer;
131 static TemporaryRef<FilterNode>
132 SRGBToLinearRGB(DrawTarget* aDT, FilterNode* aInput)
134 RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
135 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
136 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, gsRGBToLinearRGBMap, 256);
137 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
138 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, gsRGBToLinearRGBMap, 256);
139 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
140 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, gsRGBToLinearRGBMap, 256);
141 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
142 transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
143 return transfer;
146 static TemporaryRef<FilterNode>
147 Crop(DrawTarget* aDT, FilterNode* aInputFilter, const IntRect& aRect)
149 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CROP);
150 filter->SetAttribute(ATT_CROP_RECT, Rect(aRect));
151 filter->SetInput(IN_CROP_IN, aInputFilter);
152 return filter;
155 static TemporaryRef<FilterNode>
156 Offset(DrawTarget* aDT, FilterNode* aInputFilter, const IntPoint& aOffset)
158 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
159 filter->SetAttribute(ATT_TRANSFORM_MATRIX, Matrix::Translation(aOffset.x, aOffset.y));
160 filter->SetInput(IN_TRANSFORM_IN, aInputFilter);
161 return filter;
164 static TemporaryRef<FilterNode>
165 GaussianBlur(DrawTarget* aDT, FilterNode* aInputFilter, const Size& aStdDeviation)
167 float stdX = float(std::min(aStdDeviation.width, kMaxStdDeviation));
168 float stdY = float(std::min(aStdDeviation.height, kMaxStdDeviation));
169 if (stdX == stdY) {
170 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::GAUSSIAN_BLUR);
171 filter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, stdX);
172 filter->SetInput(IN_GAUSSIAN_BLUR_IN, aInputFilter);
173 return filter;
175 RefPtr<FilterNode> filterH = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
176 RefPtr<FilterNode> filterV = aDT->CreateFilter(FilterType::DIRECTIONAL_BLUR);
177 filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_X);
178 filterH->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdX);
179 filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_DIRECTION, (uint32_t)BLUR_DIRECTION_Y);
180 filterV->SetAttribute(ATT_DIRECTIONAL_BLUR_STD_DEVIATION, stdY);
181 filterH->SetInput(IN_DIRECTIONAL_BLUR_IN, aInputFilter);
182 filterV->SetInput(IN_DIRECTIONAL_BLUR_IN, filterH);
183 return filterV;
186 static TemporaryRef<FilterNode>
187 Clear(DrawTarget* aDT)
189 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
190 filter->SetAttribute(ATT_FLOOD_COLOR, Color(0,0,0,0));
191 return filter;
194 static TemporaryRef<FilterNode>
195 ForSurface(DrawTarget* aDT, SourceSurface* aSurface,
196 const IntPoint& aSurfacePosition)
198 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TRANSFORM);
199 filter->SetAttribute(ATT_TRANSFORM_MATRIX,
200 Matrix::Translation(aSurfacePosition.x, aSurfacePosition.y));
201 filter->SetInput(IN_TRANSFORM_IN, aSurface);
202 return filter;
205 static TemporaryRef<FilterNode>
206 ToAlpha(DrawTarget* aDT, FilterNode* aInput)
208 float zero = 0.0f;
209 RefPtr<FilterNode> transfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
210 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_R, false);
211 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_R, &zero, 1);
212 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_G, false);
213 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_G, &zero, 1);
214 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_B, false);
215 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_TABLE_B, &zero, 1);
216 transfer->SetAttribute(ATT_DISCRETE_TRANSFER_DISABLE_A, true);
217 transfer->SetInput(IN_DISCRETE_TRANSFER_IN, aInput);
218 return transfer;
223 // A class that wraps a FilterNode and handles conversion between different
224 // color models. Create FilterCachedColorModels with your original filter and
225 // the color model that this filter outputs in natively, and then call
226 // ->ForColorModel(colorModel) in order to get a FilterNode which outputs to
227 // the specified colorModel.
228 // Internally, this is achieved by wrapping the original FilterNode with
229 // conversion FilterNodes. These filter nodes are cached in such a way that no
230 // repeated or back-and-forth conversions happen.
231 class FilterCachedColorModels
233 public:
234 NS_INLINE_DECL_REFCOUNTING(FilterCachedColorModels)
235 // aFilter can be null. In that case, ForColorModel will return a non-null
236 // completely transparent filter for all color models.
237 FilterCachedColorModels(DrawTarget* aDT,
238 FilterNode* aFilter,
239 ColorModel aOriginalColorModel);
241 // Get a FilterNode for the specified color model, guaranteed to be non-null.
242 TemporaryRef<FilterNode> ForColorModel(ColorModel aColorModel);
244 AlphaModel OriginalAlphaModel() const { return mOriginalColorModel.mAlphaModel; }
246 private:
247 // Create the required FilterNode that will be cached by ForColorModel.
248 TemporaryRef<FilterNode> WrapForColorModel(ColorModel aColorModel);
250 RefPtr<DrawTarget> mDT;
251 ColorModel mOriginalColorModel;
253 // This array is indexed by ColorModel::ToIndex.
254 RefPtr<FilterNode> mFilterForColorModel[4];
256 ~FilterCachedColorModels() {}
259 FilterCachedColorModels::FilterCachedColorModels(DrawTarget* aDT,
260 FilterNode* aFilter,
261 ColorModel aOriginalColorModel)
262 : mDT(aDT)
263 , mOriginalColorModel(aOriginalColorModel)
265 if (aFilter) {
266 mFilterForColorModel[aOriginalColorModel.ToIndex()] = aFilter;
267 } else {
268 RefPtr<FilterNode> clear = FilterWrappers::Clear(aDT);
269 mFilterForColorModel[0] = clear;
270 mFilterForColorModel[1] = clear;
271 mFilterForColorModel[2] = clear;
272 mFilterForColorModel[3] = clear;
276 TemporaryRef<FilterNode>
277 FilterCachedColorModels::ForColorModel(ColorModel aColorModel)
279 if (!mFilterForColorModel[aColorModel.ToIndex()]) {
280 mFilterForColorModel[aColorModel.ToIndex()] = WrapForColorModel(aColorModel);
282 return mFilterForColorModel[aColorModel.ToIndex()];
285 TemporaryRef<FilterNode>
286 FilterCachedColorModels::WrapForColorModel(ColorModel aColorModel)
288 // Convert one aspect at a time and recurse.
289 // Conversions between premultiplied / unpremultiplied color channels for the
290 // same color space can happen directly.
291 // Conversions between different color spaces can only happen on
292 // unpremultiplied color channels.
294 if (aColorModel.mAlphaModel == AlphaModel::Premultiplied) {
295 RefPtr<FilterNode> unpre =
296 ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Unpremultiplied));
297 return FilterWrappers::Premultiply(mDT, unpre);
300 MOZ_ASSERT(aColorModel.mAlphaModel == AlphaModel::Unpremultiplied);
301 if (aColorModel.mColorSpace == mOriginalColorModel.mColorSpace) {
302 RefPtr<FilterNode> premultiplied =
303 ForColorModel(ColorModel(aColorModel.mColorSpace, AlphaModel::Premultiplied));
304 return FilterWrappers::Unpremultiply(mDT, premultiplied);
307 RefPtr<FilterNode> unpremultipliedOriginal =
308 ForColorModel(ColorModel(mOriginalColorModel.mColorSpace, AlphaModel::Unpremultiplied));
309 if (aColorModel.mColorSpace == ColorSpace::LinearRGB) {
310 return FilterWrappers::SRGBToLinearRGB(mDT, unpremultipliedOriginal);
312 return FilterWrappers::LinearRGBToSRGB(mDT, unpremultipliedOriginal);
315 // Create a 4x5 color matrix for the different ways to specify color matrices
316 // in SVG.
317 static nsresult
318 ComputeColorMatrix(uint32_t aColorMatrixType, const nsTArray<float>& aValues,
319 float aOutMatrix[20])
321 static const float identityMatrix[] =
322 { 1, 0, 0, 0, 0,
323 0, 1, 0, 0, 0,
324 0, 0, 1, 0, 0,
325 0, 0, 0, 1, 0 };
327 static const float luminanceToAlphaMatrix[] =
328 { 0, 0, 0, 0, 0,
329 0, 0, 0, 0, 0,
330 0, 0, 0, 0, 0,
331 0.2125f, 0.7154f, 0.0721f, 0, 0 };
333 switch (aColorMatrixType) {
335 case SVG_FECOLORMATRIX_TYPE_MATRIX:
337 if (aValues.Length() != 20)
338 return NS_ERROR_FAILURE;
340 PodCopy(aOutMatrix, aValues.Elements(), 20);
341 break;
344 case SVG_FECOLORMATRIX_TYPE_SATURATE:
346 if (aValues.Length() != 1)
347 return NS_ERROR_FAILURE;
349 float s = aValues[0];
351 if (s < 0)
352 return NS_ERROR_FAILURE;
354 PodCopy(aOutMatrix, identityMatrix, 20);
356 aOutMatrix[0] = 0.213f + 0.787f * s;
357 aOutMatrix[1] = 0.715f - 0.715f * s;
358 aOutMatrix[2] = 0.072f - 0.072f * s;
360 aOutMatrix[5] = 0.213f - 0.213f * s;
361 aOutMatrix[6] = 0.715f + 0.285f * s;
362 aOutMatrix[7] = 0.072f - 0.072f * s;
364 aOutMatrix[10] = 0.213f - 0.213f * s;
365 aOutMatrix[11] = 0.715f - 0.715f * s;
366 aOutMatrix[12] = 0.072f + 0.928f * s;
368 break;
371 case SVG_FECOLORMATRIX_TYPE_HUE_ROTATE:
373 if (aValues.Length() != 1)
374 return NS_ERROR_FAILURE;
376 PodCopy(aOutMatrix, identityMatrix, 20);
378 float hueRotateValue = aValues[0];
380 float c = static_cast<float>(cos(hueRotateValue * M_PI / 180));
381 float s = static_cast<float>(sin(hueRotateValue * M_PI / 180));
383 aOutMatrix[0] = 0.213f + 0.787f * c - 0.213f * s;
384 aOutMatrix[1] = 0.715f - 0.715f * c - 0.715f * s;
385 aOutMatrix[2] = 0.072f - 0.072f * c + 0.928f * s;
387 aOutMatrix[5] = 0.213f - 0.213f * c + 0.143f * s;
388 aOutMatrix[6] = 0.715f + 0.285f * c + 0.140f * s;
389 aOutMatrix[7] = 0.072f - 0.072f * c - 0.283f * s;
391 aOutMatrix[10] = 0.213f - 0.213f * c - 0.787f * s;
392 aOutMatrix[11] = 0.715f - 0.715f * c + 0.715f * s;
393 aOutMatrix[12] = 0.072f + 0.928f * c + 0.072f * s;
395 break;
398 case SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA:
400 PodCopy(aOutMatrix, luminanceToAlphaMatrix, 20);
401 break;
404 case SVG_FECOLORMATRIX_TYPE_SEPIA:
406 if (aValues.Length() != 1)
407 return NS_ERROR_FAILURE;
409 float amount = aValues[0];
411 if (amount < 0 || amount > 1)
412 return NS_ERROR_FAILURE;
414 PodCopy(aOutMatrix, identityMatrix, 20);
416 float s = 1 - amount;
418 aOutMatrix[0] = 0.393f + 0.607f * s;
419 aOutMatrix[1] = 0.769f - 0.769f * s;
420 aOutMatrix[2] = 0.189f - 0.189f * s;
422 aOutMatrix[5] = 0.349f - 0.349f * s;
423 aOutMatrix[6] = 0.686f + 0.314f * s;
424 aOutMatrix[7] = 0.168f - 0.168f * s;
426 aOutMatrix[10] = 0.272f - 0.272f * s;
427 aOutMatrix[11] = 0.534f - 0.534f * s;
428 aOutMatrix[12] = 0.131f + 0.869f * s;
430 break;
433 default:
434 return NS_ERROR_FAILURE;
438 return NS_OK;
441 static void
442 DisableAllTransfers(FilterNode* aTransferFilterNode)
444 aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_R, true);
445 aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_G, true);
446 aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_B, true);
447 aTransferFilterNode->SetAttribute(ATT_TRANSFER_DISABLE_A, true);
450 // Called for one channel at a time.
451 // This function creates the required FilterNodes on demand and tries to
452 // merge conversions of different channels into the same FilterNode if
453 // possible.
454 // There's a mismatch between the way SVG and the Moz2D API handle transfer
455 // functions: In SVG, it's possible to specify a different transfer function
456 // type for each color channel, but in Moz2D, a given transfer function type
457 // applies to all color channels.
459 // @param aFunctionAttributes The attributes of the transfer function for this
460 // channel.
461 // @param aChannel The color channel that this function applies to, where
462 // 0 = red, 1 = green, 2 = blue, 3 = alpha
463 // @param aDT The DrawTarget that the FilterNodes should be created for.
464 // @param aTableTransfer Existing FilterNode holders (which may still be
465 // null) that the resulting FilterNodes from this
466 // function will be stored in.
468 static void
469 ConvertComponentTransferFunctionToFilter(const AttributeMap& aFunctionAttributes,
470 int32_t aChannel,
471 DrawTarget* aDT,
472 RefPtr<FilterNode>& aTableTransfer,
473 RefPtr<FilterNode>& aDiscreteTransfer,
474 RefPtr<FilterNode>& aLinearTransfer,
475 RefPtr<FilterNode>& aGammaTransfer)
477 static const TransferAtts disableAtt[4] = {
478 ATT_TRANSFER_DISABLE_R,
479 ATT_TRANSFER_DISABLE_G,
480 ATT_TRANSFER_DISABLE_B,
481 ATT_TRANSFER_DISABLE_A
484 RefPtr<FilterNode> filter;
486 uint32_t type = aFunctionAttributes.GetUint(eComponentTransferFunctionType);
488 switch (type) {
489 case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
491 const nsTArray<float>& tableValues =
492 aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
493 if (tableValues.Length() < 2)
494 return;
496 if (!aTableTransfer) {
497 aTableTransfer = aDT->CreateFilter(FilterType::TABLE_TRANSFER);
498 DisableAllTransfers(aTableTransfer);
500 filter = aTableTransfer;
501 static const TableTransferAtts tableAtt[4] = {
502 ATT_TABLE_TRANSFER_TABLE_R,
503 ATT_TABLE_TRANSFER_TABLE_G,
504 ATT_TABLE_TRANSFER_TABLE_B,
505 ATT_TABLE_TRANSFER_TABLE_A
507 filter->SetAttribute(disableAtt[aChannel], false);
508 filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
509 break;
512 case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
514 const nsTArray<float>& tableValues =
515 aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
516 if (tableValues.Length() < 1)
517 return;
519 if (!aDiscreteTransfer) {
520 aDiscreteTransfer = aDT->CreateFilter(FilterType::DISCRETE_TRANSFER);
521 DisableAllTransfers(aDiscreteTransfer);
523 filter = aDiscreteTransfer;
524 static const DiscreteTransferAtts tableAtt[4] = {
525 ATT_DISCRETE_TRANSFER_TABLE_R,
526 ATT_DISCRETE_TRANSFER_TABLE_G,
527 ATT_DISCRETE_TRANSFER_TABLE_B,
528 ATT_DISCRETE_TRANSFER_TABLE_A
530 filter->SetAttribute(disableAtt[aChannel], false);
531 filter->SetAttribute(tableAtt[aChannel], &tableValues[0], tableValues.Length());
533 break;
536 case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
538 static const LinearTransferAtts slopeAtt[4] = {
539 ATT_LINEAR_TRANSFER_SLOPE_R,
540 ATT_LINEAR_TRANSFER_SLOPE_G,
541 ATT_LINEAR_TRANSFER_SLOPE_B,
542 ATT_LINEAR_TRANSFER_SLOPE_A
544 static const LinearTransferAtts interceptAtt[4] = {
545 ATT_LINEAR_TRANSFER_INTERCEPT_R,
546 ATT_LINEAR_TRANSFER_INTERCEPT_G,
547 ATT_LINEAR_TRANSFER_INTERCEPT_B,
548 ATT_LINEAR_TRANSFER_INTERCEPT_A
550 if (!aLinearTransfer) {
551 aLinearTransfer = aDT->CreateFilter(FilterType::LINEAR_TRANSFER);
552 DisableAllTransfers(aLinearTransfer);
554 filter = aLinearTransfer;
555 filter->SetAttribute(disableAtt[aChannel], false);
556 float slope = aFunctionAttributes.GetFloat(eComponentTransferFunctionSlope);
557 float intercept = aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept);
558 filter->SetAttribute(slopeAtt[aChannel], slope);
559 filter->SetAttribute(interceptAtt[aChannel], intercept);
560 break;
563 case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
565 static const GammaTransferAtts amplitudeAtt[4] = {
566 ATT_GAMMA_TRANSFER_AMPLITUDE_R,
567 ATT_GAMMA_TRANSFER_AMPLITUDE_G,
568 ATT_GAMMA_TRANSFER_AMPLITUDE_B,
569 ATT_GAMMA_TRANSFER_AMPLITUDE_A
571 static const GammaTransferAtts exponentAtt[4] = {
572 ATT_GAMMA_TRANSFER_EXPONENT_R,
573 ATT_GAMMA_TRANSFER_EXPONENT_G,
574 ATT_GAMMA_TRANSFER_EXPONENT_B,
575 ATT_GAMMA_TRANSFER_EXPONENT_A
577 static const GammaTransferAtts offsetAtt[4] = {
578 ATT_GAMMA_TRANSFER_OFFSET_R,
579 ATT_GAMMA_TRANSFER_OFFSET_G,
580 ATT_GAMMA_TRANSFER_OFFSET_B,
581 ATT_GAMMA_TRANSFER_OFFSET_A
583 if (!aGammaTransfer) {
584 aGammaTransfer = aDT->CreateFilter(FilterType::GAMMA_TRANSFER);
585 DisableAllTransfers(aGammaTransfer);
587 filter = aGammaTransfer;
588 filter->SetAttribute(disableAtt[aChannel], false);
589 float amplitude = aFunctionAttributes.GetFloat(eComponentTransferFunctionAmplitude);
590 float exponent = aFunctionAttributes.GetFloat(eComponentTransferFunctionExponent);
591 float offset = aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset);
592 filter->SetAttribute(amplitudeAtt[aChannel], amplitude);
593 filter->SetAttribute(exponentAtt[aChannel], exponent);
594 filter->SetAttribute(offsetAtt[aChannel], offset);
595 break;
598 case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
599 default:
600 break;
604 const int32_t kMorphologyMaxRadius = 100000;
606 // Handle the different primitive description types and create the necessary
607 // FilterNode(s) for each.
608 // Returns nullptr for invalid filter primitives. This should be interpreted as
609 // transparent black by the caller.
610 // aSourceRegions contains the filter primitive subregions of the source
611 // primitives; only needed for eTile primitives.
612 // aInputImages carries additional surfaces that are used by eImage primitives.
613 static TemporaryRef<FilterNode>
614 FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescription,
615 DrawTarget* aDT,
616 nsTArray<RefPtr<FilterNode> >& aSources,
617 nsTArray<IntRect>& aSourceRegions,
618 nsTArray<RefPtr<SourceSurface>>& aInputImages)
620 const AttributeMap& atts = aDescription.Attributes();
621 switch (aDescription.Type()) {
623 case PrimitiveType::Empty:
624 return nullptr;
626 case PrimitiveType::Blend:
628 uint32_t mode = atts.GetUint(eBlendBlendmode);
629 RefPtr<FilterNode> filter;
630 if (mode == SVG_FEBLEND_MODE_UNKNOWN) {
631 return nullptr;
633 if (mode == SVG_FEBLEND_MODE_NORMAL) {
634 filter = aDT->CreateFilter(FilterType::COMPOSITE);
635 filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
636 filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
637 } else {
638 filter = aDT->CreateFilter(FilterType::BLEND);
639 static const uint8_t blendModes[SVG_FEBLEND_MODE_LIGHTEN + 1] = {
642 BLEND_MODE_MULTIPLY,
643 BLEND_MODE_SCREEN,
644 BLEND_MODE_DARKEN,
645 BLEND_MODE_LIGHTEN
647 filter->SetAttribute(ATT_BLEND_BLENDMODE, (uint32_t)blendModes[mode]);
648 filter->SetInput(IN_BLEND_IN, aSources[0]);
649 filter->SetInput(IN_BLEND_IN2, aSources[1]);
651 return filter;
654 case PrimitiveType::ColorMatrix:
656 float colorMatrix[20];
657 uint32_t type = atts.GetUint(eColorMatrixType);
658 const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
659 if (NS_FAILED(ComputeColorMatrix(type, values, colorMatrix))) {
660 return aSources[0];
662 Matrix5x4 matrix(colorMatrix[0], colorMatrix[5], colorMatrix[10], colorMatrix[15],
663 colorMatrix[1], colorMatrix[6], colorMatrix[11], colorMatrix[16],
664 colorMatrix[2], colorMatrix[7], colorMatrix[12], colorMatrix[17],
665 colorMatrix[3], colorMatrix[8], colorMatrix[13], colorMatrix[18],
666 colorMatrix[4], colorMatrix[9], colorMatrix[14], colorMatrix[19]);
667 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COLOR_MATRIX);
668 filter->SetAttribute(ATT_COLOR_MATRIX_MATRIX, matrix);
669 filter->SetAttribute(ATT_COLOR_MATRIX_ALPHA_MODE, (uint32_t)ALPHA_MODE_STRAIGHT);
670 filter->SetInput(IN_COLOR_MATRIX_IN, aSources[0]);
671 return filter;
674 case PrimitiveType::Morphology:
676 Size radii = atts.GetSize(eMorphologyRadii);
677 int32_t rx = radii.width;
678 int32_t ry = radii.height;
679 if (rx < 0 || ry < 0) {
680 // XXX SVGContentUtils::ReportToConsole()
681 return nullptr;
683 if (rx == 0 && ry == 0) {
684 return nullptr;
687 // Clamp radii to prevent completely insane values:
688 rx = std::min(rx, kMorphologyMaxRadius);
689 ry = std::min(ry, kMorphologyMaxRadius);
691 MorphologyOperator op = atts.GetUint(eMorphologyOperator) == SVG_OPERATOR_ERODE ?
692 MORPHOLOGY_OPERATOR_ERODE : MORPHOLOGY_OPERATOR_DILATE;
694 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::MORPHOLOGY);
695 filter->SetAttribute(ATT_MORPHOLOGY_RADII, IntSize(rx, ry));
696 filter->SetAttribute(ATT_MORPHOLOGY_OPERATOR, (uint32_t)op);
697 filter->SetInput(IN_MORPHOLOGY_IN, aSources[0]);
698 return filter;
701 case PrimitiveType::Flood:
703 Color color = atts.GetColor(eFloodColor);
704 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::FLOOD);
705 filter->SetAttribute(ATT_FLOOD_COLOR, color);
706 return filter;
709 case PrimitiveType::Tile:
711 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TILE);
712 filter->SetAttribute(ATT_TILE_SOURCE_RECT, aSourceRegions[0]);
713 filter->SetInput(IN_TILE_IN, aSources[0]);
714 return filter;
717 case PrimitiveType::ComponentTransfer:
719 RefPtr<FilterNode> filters[4]; // one for each FILTER_*_TRANSFER type
720 static const AttributeName componentFunctionNames[4] = {
721 eComponentTransferFunctionR,
722 eComponentTransferFunctionG,
723 eComponentTransferFunctionB,
724 eComponentTransferFunctionA
726 for (int32_t i = 0; i < 4; i++) {
727 AttributeMap functionAttributes =
728 atts.GetAttributeMap(componentFunctionNames[i]);
729 ConvertComponentTransferFunctionToFilter(functionAttributes, i, aDT,
730 filters[0], filters[1], filters[2], filters[3]);
733 // Connect all used filters nodes.
734 RefPtr<FilterNode> lastFilter = aSources[0];
735 for (int32_t i = 0; i < 4; i++) {
736 if (filters[i]) {
737 filters[i]->SetInput(0, lastFilter);
738 lastFilter = filters[i];
742 return lastFilter;
745 case PrimitiveType::ConvolveMatrix:
747 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::CONVOLVE_MATRIX);
748 filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_SIZE, atts.GetIntSize(eConvolveMatrixKernelSize));
749 const nsTArray<float>& matrix = atts.GetFloats(eConvolveMatrixKernelMatrix);
750 filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_MATRIX,
751 matrix.Elements(), matrix.Length());
752 filter->SetAttribute(ATT_CONVOLVE_MATRIX_DIVISOR,
753 atts.GetFloat(eConvolveMatrixDivisor));
754 filter->SetAttribute(ATT_CONVOLVE_MATRIX_BIAS,
755 atts.GetFloat(eConvolveMatrixBias));
756 filter->SetAttribute(ATT_CONVOLVE_MATRIX_TARGET,
757 atts.GetIntPoint(eConvolveMatrixTarget));
758 filter->SetAttribute(ATT_CONVOLVE_MATRIX_SOURCE_RECT,
759 aSourceRegions[0]);
760 uint32_t edgeMode = atts.GetUint(eConvolveMatrixEdgeMode);
761 static const uint8_t edgeModes[SVG_EDGEMODE_NONE+1] = {
762 EDGE_MODE_NONE, // SVG_EDGEMODE_UNKNOWN
763 EDGE_MODE_DUPLICATE, // SVG_EDGEMODE_DUPLICATE
764 EDGE_MODE_WRAP, // SVG_EDGEMODE_WRAP
765 EDGE_MODE_NONE // SVG_EDGEMODE_NONE
767 filter->SetAttribute(ATT_CONVOLVE_MATRIX_EDGE_MODE, (uint32_t)edgeModes[edgeMode]);
768 filter->SetAttribute(ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH,
769 atts.GetSize(eConvolveMatrixKernelUnitLength));
770 filter->SetAttribute(ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA,
771 atts.GetBool(eConvolveMatrixPreserveAlpha));
772 filter->SetInput(IN_CONVOLVE_MATRIX_IN, aSources[0]);
773 return filter;
776 case PrimitiveType::Offset:
778 return FilterWrappers::Offset(aDT, aSources[0],
779 atts.GetIntPoint(eOffsetOffset));
782 case PrimitiveType::DisplacementMap:
784 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::DISPLACEMENT_MAP);
785 filter->SetAttribute(ATT_DISPLACEMENT_MAP_SCALE,
786 atts.GetFloat(eDisplacementMapScale));
787 static const uint8_t channel[SVG_CHANNEL_A+1] = {
788 COLOR_CHANNEL_R, // SVG_CHANNEL_UNKNOWN
789 COLOR_CHANNEL_R, // SVG_CHANNEL_R
790 COLOR_CHANNEL_G, // SVG_CHANNEL_G
791 COLOR_CHANNEL_B, // SVG_CHANNEL_B
792 COLOR_CHANNEL_A // SVG_CHANNEL_A
794 filter->SetAttribute(ATT_DISPLACEMENT_MAP_X_CHANNEL,
795 (uint32_t)channel[atts.GetUint(eDisplacementMapXChannel)]);
796 filter->SetAttribute(ATT_DISPLACEMENT_MAP_Y_CHANNEL,
797 (uint32_t)channel[atts.GetUint(eDisplacementMapYChannel)]);
798 filter->SetInput(IN_DISPLACEMENT_MAP_IN, aSources[0]);
799 filter->SetInput(IN_DISPLACEMENT_MAP_IN2, aSources[1]);
800 return filter;
803 case PrimitiveType::Turbulence:
805 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::TURBULENCE);
806 filter->SetAttribute(ATT_TURBULENCE_BASE_FREQUENCY,
807 atts.GetSize(eTurbulenceBaseFrequency));
808 filter->SetAttribute(ATT_TURBULENCE_NUM_OCTAVES,
809 atts.GetUint(eTurbulenceNumOctaves));
810 filter->SetAttribute(ATT_TURBULENCE_STITCHABLE,
811 atts.GetBool(eTurbulenceStitchable));
812 filter->SetAttribute(ATT_TURBULENCE_SEED,
813 (uint32_t)atts.GetFloat(eTurbulenceSeed));
814 static const uint8_t type[SVG_TURBULENCE_TYPE_TURBULENCE+1] = {
815 TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_UNKNOWN
816 TURBULENCE_TYPE_FRACTAL_NOISE, // SVG_TURBULENCE_TYPE_FRACTALNOISE
817 TURBULENCE_TYPE_TURBULENCE // SVG_TURBULENCE_TYPE_TURBULENCE
819 filter->SetAttribute(ATT_TURBULENCE_TYPE,
820 (uint32_t)type[atts.GetUint(eTurbulenceType)]);
821 filter->SetAttribute(ATT_TURBULENCE_RECT,
822 aDescription.PrimitiveSubregion() - atts.GetIntPoint(eTurbulenceOffset));
823 return FilterWrappers::Offset(aDT, filter, atts.GetIntPoint(eTurbulenceOffset));
826 case PrimitiveType::Composite:
828 RefPtr<FilterNode> filter;
829 uint32_t op = atts.GetUint(eCompositeOperator);
830 if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
831 filter = aDT->CreateFilter(FilterType::ARITHMETIC_COMBINE);
832 const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
833 filter->SetAttribute(ATT_ARITHMETIC_COMBINE_COEFFICIENTS,
834 coefficients.Elements(), coefficients.Length());
835 filter->SetInput(IN_ARITHMETIC_COMBINE_IN, aSources[0]);
836 filter->SetInput(IN_ARITHMETIC_COMBINE_IN2, aSources[1]);
837 } else {
838 filter = aDT->CreateFilter(FilterType::COMPOSITE);
839 static const uint8_t operators[SVG_FECOMPOSITE_OPERATOR_ARITHMETIC] = {
840 COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_UNKNOWN
841 COMPOSITE_OPERATOR_OVER, // SVG_FECOMPOSITE_OPERATOR_OVER
842 COMPOSITE_OPERATOR_IN, // SVG_FECOMPOSITE_OPERATOR_IN
843 COMPOSITE_OPERATOR_OUT, // SVG_FECOMPOSITE_OPERATOR_OUT
844 COMPOSITE_OPERATOR_ATOP, // SVG_FECOMPOSITE_OPERATOR_ATOP
845 COMPOSITE_OPERATOR_XOR // SVG_FECOMPOSITE_OPERATOR_XOR
847 filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)operators[op]);
848 filter->SetInput(IN_COMPOSITE_IN_START, aSources[1]);
849 filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
851 return filter;
854 case PrimitiveType::Merge:
856 if (aSources.Length() == 0) {
857 return nullptr;
859 if (aSources.Length() == 1) {
860 return aSources[0];
862 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
863 filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
864 for (size_t i = 0; i < aSources.Length(); i++) {
865 filter->SetInput(IN_COMPOSITE_IN_START + i, aSources[i]);
867 return filter;
870 case PrimitiveType::GaussianBlur:
872 return FilterWrappers::GaussianBlur(aDT, aSources[0],
873 atts.GetSize(eGaussianBlurStdDeviation));
876 case PrimitiveType::DropShadow:
878 RefPtr<FilterNode> alpha = FilterWrappers::ToAlpha(aDT, aSources[0]);
879 RefPtr<FilterNode> blur = FilterWrappers::GaussianBlur(aDT, alpha,
880 atts.GetSize(eDropShadowStdDeviation));
881 RefPtr<FilterNode> offsetBlur = FilterWrappers::Offset(aDT, blur,
882 atts.GetIntPoint(eDropShadowOffset));
883 RefPtr<FilterNode> flood = aDT->CreateFilter(FilterType::FLOOD);
884 Color color = atts.GetColor(eDropShadowColor);
885 if (aDescription.InputColorSpace(0) == ColorSpace::LinearRGB) {
886 color = Color(gsRGBToLinearRGBMap[uint8_t(color.r * 255)],
887 gsRGBToLinearRGBMap[uint8_t(color.g * 255)],
888 gsRGBToLinearRGBMap[uint8_t(color.b * 255)],
889 color.a);
891 flood->SetAttribute(ATT_FLOOD_COLOR, color);
893 RefPtr<FilterNode> composite = aDT->CreateFilter(FilterType::COMPOSITE);
894 composite->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_IN);
895 composite->SetInput(IN_COMPOSITE_IN_START, offsetBlur);
896 composite->SetInput(IN_COMPOSITE_IN_START + 1, flood);
898 RefPtr<FilterNode> filter = aDT->CreateFilter(FilterType::COMPOSITE);
899 filter->SetAttribute(ATT_COMPOSITE_OPERATOR, (uint32_t)COMPOSITE_OPERATOR_OVER);
900 filter->SetInput(IN_COMPOSITE_IN_START, composite);
901 filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
902 return filter;
905 case PrimitiveType::DiffuseLighting:
906 case PrimitiveType::SpecularLighting:
908 bool isSpecular =
909 aDescription.Type() == PrimitiveType::SpecularLighting;
911 AttributeMap lightAttributes = atts.GetAttributeMap(eLightingLight);
913 if (lightAttributes.GetUint(eLightType) == eLightTypeNone) {
914 return nullptr;
917 enum { POINT = 0, SPOT, DISTANT } lightType = POINT;
919 switch (lightAttributes.GetUint(eLightType)) {
920 case eLightTypePoint: lightType = POINT; break;
921 case eLightTypeSpot: lightType = SPOT; break;
922 case eLightTypeDistant: lightType = DISTANT; break;
925 static const FilterType filterType[2][DISTANT+1] = {
926 { FilterType::POINT_DIFFUSE, FilterType::SPOT_DIFFUSE, FilterType::DISTANT_DIFFUSE },
927 { FilterType::POINT_SPECULAR, FilterType::SPOT_SPECULAR, FilterType::DISTANT_SPECULAR }
929 RefPtr<FilterNode> filter =
930 aDT->CreateFilter(filterType[isSpecular][lightType]);
932 filter->SetAttribute(ATT_LIGHTING_COLOR,
933 atts.GetColor(eLightingColor));
934 filter->SetAttribute(ATT_LIGHTING_SURFACE_SCALE,
935 atts.GetFloat(eLightingSurfaceScale));
936 filter->SetAttribute(ATT_LIGHTING_KERNEL_UNIT_LENGTH,
937 atts.GetSize(eLightingKernelUnitLength));
939 if (isSpecular) {
940 filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT,
941 atts.GetFloat(eSpecularLightingSpecularConstant));
942 filter->SetAttribute(ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT,
943 atts.GetFloat(eSpecularLightingSpecularExponent));
944 } else {
945 filter->SetAttribute(ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT,
946 atts.GetFloat(eDiffuseLightingDiffuseConstant));
949 switch (lightType) {
950 case POINT:
951 filter->SetAttribute(ATT_POINT_LIGHT_POSITION,
952 lightAttributes.GetPoint3D(ePointLightPosition));
953 break;
954 case SPOT:
955 filter->SetAttribute(ATT_SPOT_LIGHT_POSITION,
956 lightAttributes.GetPoint3D(eSpotLightPosition));
957 filter->SetAttribute(ATT_SPOT_LIGHT_POINTS_AT,
958 lightAttributes.GetPoint3D(eSpotLightPointsAt));
959 filter->SetAttribute(ATT_SPOT_LIGHT_FOCUS,
960 lightAttributes.GetFloat(eSpotLightFocus));
961 filter->SetAttribute(ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE,
962 lightAttributes.GetFloat(eSpotLightLimitingConeAngle));
963 break;
964 case DISTANT:
965 filter->SetAttribute(ATT_DISTANT_LIGHT_AZIMUTH,
966 lightAttributes.GetFloat(eDistantLightAzimuth));
967 filter->SetAttribute(ATT_DISTANT_LIGHT_ELEVATION,
968 lightAttributes.GetFloat(eDistantLightElevation));
969 break;
972 filter->SetInput(IN_LIGHTING_IN, aSources[0]);
974 return filter;
977 case PrimitiveType::Image:
979 Matrix TM = atts.GetMatrix(eImageTransform);
980 if (!TM.Determinant()) {
981 return nullptr;
984 // Pull the image from the additional image list using the index that's
985 // stored in the primitive description.
986 RefPtr<SourceSurface> inputImage =
987 aInputImages[atts.GetUint(eImageInputIndex)];
989 RefPtr<FilterNode> transform = aDT->CreateFilter(FilterType::TRANSFORM);
990 transform->SetInput(IN_TRANSFORM_IN, inputImage);
991 transform->SetAttribute(ATT_TRANSFORM_MATRIX, TM);
992 transform->SetAttribute(ATT_TRANSFORM_FILTER, atts.GetUint(eImageFilter));
993 return transform;
996 case PrimitiveType::ToAlpha:
998 return FilterWrappers::ToAlpha(aDT, aSources[0]);
1001 default:
1002 return nullptr;
1006 template<typename T>
1007 static const T&
1008 ElementForIndex(int32_t aIndex,
1009 const nsTArray<T>& aPrimitiveElements,
1010 const T& aSourceGraphicElement,
1011 const T& aFillPaintElement,
1012 const T& aStrokePaintElement)
1014 switch (aIndex) {
1015 case FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic:
1016 case FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha:
1017 return aSourceGraphicElement;
1018 case FilterPrimitiveDescription::kPrimitiveIndexFillPaint:
1019 return aFillPaintElement;
1020 case FilterPrimitiveDescription::kPrimitiveIndexStrokePaint:
1021 return aStrokePaintElement;
1022 default:
1023 MOZ_ASSERT(aIndex >= 0, "bad index");
1024 return aPrimitiveElements[aIndex];
1028 static AlphaModel
1029 InputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr,
1030 int32_t aInputIndex,
1031 AlphaModel aOriginalAlphaModel)
1033 switch (aDescr.Type()) {
1034 case PrimitiveType::Tile:
1035 case PrimitiveType::Offset:
1036 case PrimitiveType::ToAlpha:
1037 return aOriginalAlphaModel;
1039 case PrimitiveType::ColorMatrix:
1040 case PrimitiveType::ComponentTransfer:
1041 return AlphaModel::Unpremultiplied;
1043 case PrimitiveType::DisplacementMap:
1044 return aInputIndex == 0 ?
1045 AlphaModel::Premultiplied : AlphaModel::Unpremultiplied;
1047 case PrimitiveType::ConvolveMatrix:
1048 return aDescr.Attributes().GetBool(eConvolveMatrixPreserveAlpha) ?
1049 AlphaModel::Unpremultiplied : AlphaModel::Premultiplied;
1051 default:
1052 return AlphaModel::Premultiplied;
1056 static AlphaModel
1057 OutputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr,
1058 const nsTArray<AlphaModel>& aInputAlphaModels)
1060 if (aInputAlphaModels.Length()) {
1061 // For filters with inputs, the output is premultiplied if and only if the
1062 // first input is premultiplied.
1063 return InputAlphaModelForPrimitive(aDescr, 0, aInputAlphaModels[0]);
1066 // All filters without inputs produce premultiplied alpha.
1067 return AlphaModel::Premultiplied;
1070 // Returns the output FilterNode, in premultiplied sRGB space.
1071 static TemporaryRef<FilterNode>
1072 FilterNodeGraphFromDescription(DrawTarget* aDT,
1073 const FilterDescription& aFilter,
1074 const Rect& aResultNeededRect,
1075 SourceSurface* aSourceGraphic,
1076 const IntRect& aSourceGraphicRect,
1077 SourceSurface* aFillPaint,
1078 const IntRect& aFillPaintRect,
1079 SourceSurface* aStrokePaint,
1080 const IntRect& aStrokePaintRect,
1081 nsTArray<RefPtr<SourceSurface>>& aAdditionalImages)
1083 const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
1085 Rect resultNeededRect(aResultNeededRect);
1086 resultNeededRect.RoundOut();
1088 RefPtr<FilterCachedColorModels> sourceFilters[4];
1089 nsTArray<RefPtr<FilterCachedColorModels> > primitiveFilters;
1091 for (size_t i = 0; i < primitives.Length(); ++i) {
1092 const FilterPrimitiveDescription& descr = primitives[i];
1094 nsTArray<RefPtr<FilterNode> > inputFilterNodes;
1095 nsTArray<IntRect> inputSourceRects;
1096 nsTArray<AlphaModel> inputAlphaModels;
1098 for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
1100 int32_t inputIndex = descr.InputPrimitiveIndex(j);
1101 if (inputIndex < 0) {
1102 inputSourceRects.AppendElement(descr.FilterSpaceBounds());
1103 } else {
1104 inputSourceRects.AppendElement(primitives[inputIndex].PrimitiveSubregion());
1107 RefPtr<FilterCachedColorModels> inputFilter;
1108 if (inputIndex >= 0) {
1109 MOZ_ASSERT(inputIndex < (int64_t)primitiveFilters.Length(), "out-of-bounds input index!");
1110 inputFilter = primitiveFilters[inputIndex];
1111 MOZ_ASSERT(inputFilter, "Referred to input filter that comes after the current one?");
1112 } else {
1113 int32_t sourceIndex = -inputIndex - 1;
1114 MOZ_ASSERT(sourceIndex >= 0, "invalid source index");
1115 MOZ_ASSERT(sourceIndex < 4, "invalid source index");
1116 inputFilter = sourceFilters[sourceIndex];
1117 if (!inputFilter) {
1118 RefPtr<FilterNode> sourceFilterNode;
1120 nsTArray<SourceSurface*> primitiveSurfaces;
1121 nsTArray<IntRect> primitiveSurfaceRects;
1122 RefPtr<SourceSurface> surf =
1123 ElementForIndex(inputIndex, primitiveSurfaces,
1124 aSourceGraphic, aFillPaint, aStrokePaint);
1125 IntRect surfaceRect =
1126 ElementForIndex(inputIndex, primitiveSurfaceRects,
1127 aSourceGraphicRect, aFillPaintRect, aStrokePaintRect);
1128 if (surf) {
1129 IntPoint offset = surfaceRect.TopLeft();
1130 sourceFilterNode = FilterWrappers::ForSurface(aDT, surf, offset);
1132 // Clip the original SourceGraphic to the first filter region if the
1133 // surface isn't already sized appropriately.
1134 if ((inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic ||
1135 inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) &&
1136 !descr.FilterSpaceBounds().Contains(aSourceGraphicRect)) {
1137 sourceFilterNode =
1138 FilterWrappers::Crop(aDT, sourceFilterNode, descr.FilterSpaceBounds());
1141 if (inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) {
1142 sourceFilterNode = FilterWrappers::ToAlpha(aDT, sourceFilterNode);
1146 inputFilter = new FilterCachedColorModels(aDT, sourceFilterNode,
1147 ColorModel::PremulSRGB());
1148 sourceFilters[sourceIndex] = inputFilter;
1151 MOZ_ASSERT(inputFilter);
1153 AlphaModel inputAlphaModel =
1154 InputAlphaModelForPrimitive(descr, j, inputFilter->OriginalAlphaModel());
1155 inputAlphaModels.AppendElement(inputAlphaModel);
1156 ColorModel inputColorModel(descr.InputColorSpace(j), inputAlphaModel);
1157 inputFilterNodes.AppendElement(inputFilter->ForColorModel(inputColorModel));
1160 RefPtr<FilterNode> primitiveFilterNode =
1161 FilterNodeFromPrimitiveDescription(descr, aDT, inputFilterNodes,
1162 inputSourceRects, aAdditionalImages);
1164 if (primitiveFilterNode) {
1165 primitiveFilterNode =
1166 FilterWrappers::Crop(aDT, primitiveFilterNode, descr.PrimitiveSubregion());
1169 ColorModel outputColorModel(descr.OutputColorSpace(),
1170 OutputAlphaModelForPrimitive(descr, inputAlphaModels));
1171 RefPtr<FilterCachedColorModels> primitiveFilter =
1172 new FilterCachedColorModels(aDT, primitiveFilterNode, outputColorModel);
1174 primitiveFilters.AppendElement(primitiveFilter);
1177 return primitiveFilters.LastElement()->ForColorModel(ColorModel::PremulSRGB());
1180 // FilterSupport
1182 void
1183 FilterSupport::RenderFilterDescription(DrawTarget* aDT,
1184 const FilterDescription& aFilter,
1185 const Rect& aRenderRect,
1186 SourceSurface* aSourceGraphic,
1187 const IntRect& aSourceGraphicRect,
1188 SourceSurface* aFillPaint,
1189 const IntRect& aFillPaintRect,
1190 SourceSurface* aStrokePaint,
1191 const IntRect& aStrokePaintRect,
1192 nsTArray<RefPtr<SourceSurface>>& aAdditionalImages)
1194 RefPtr<FilterNode> resultFilter =
1195 FilterNodeGraphFromDescription(aDT, aFilter, aRenderRect,
1196 aSourceGraphic, aSourceGraphicRect, aFillPaint, aFillPaintRect,
1197 aStrokePaint, aStrokePaintRect, aAdditionalImages);
1199 aDT->DrawFilter(resultFilter, aRenderRect, Point(0, 0));
1202 static nsIntRegion
1203 UnionOfRegions(const nsTArray<nsIntRegion>& aRegions)
1205 nsIntRegion result;
1206 for (size_t i = 0; i < aRegions.Length(); i++) {
1207 result.Or(result, aRegions[i]);
1209 return result;
1212 static int32_t
1213 InflateSizeForBlurStdDev(float aStdDev)
1215 double size = std::min(aStdDev, kMaxStdDeviation) * (3 * sqrt(2 * M_PI) / 4) * 1.5;
1216 return uint32_t(floor(size + 0.5));
1219 static nsIntRegion
1220 ResultChangeRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
1221 const nsTArray<nsIntRegion>& aInputChangeRegions)
1223 const AttributeMap& atts = aDescription.Attributes();
1224 switch (aDescription.Type()) {
1226 case PrimitiveType::Empty:
1227 case PrimitiveType::Flood:
1228 case PrimitiveType::Turbulence:
1229 case PrimitiveType::Image:
1230 return nsIntRegion();
1232 case PrimitiveType::Blend:
1233 case PrimitiveType::Composite:
1234 case PrimitiveType::Merge:
1235 return UnionOfRegions(aInputChangeRegions);
1237 case PrimitiveType::ColorMatrix:
1238 case PrimitiveType::ComponentTransfer:
1239 case PrimitiveType::ToAlpha:
1240 return aInputChangeRegions[0];
1242 case PrimitiveType::Morphology:
1244 Size radii = atts.GetSize(eMorphologyRadii);
1245 int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
1246 int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
1247 return aInputChangeRegions[0].Inflated(nsIntMargin(ry, rx, ry, rx));
1250 case PrimitiveType::Tile:
1251 return ThebesIntRect(aDescription.PrimitiveSubregion());
1253 case PrimitiveType::ConvolveMatrix:
1255 Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
1256 IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
1257 IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
1258 nsIntMargin m(ceil(kernelUnitLength.width * (target.x)),
1259 ceil(kernelUnitLength.height * (target.y)),
1260 ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
1261 ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)));
1262 return aInputChangeRegions[0].Inflated(m);
1265 case PrimitiveType::Offset:
1267 IntPoint offset = atts.GetIntPoint(eOffsetOffset);
1268 return aInputChangeRegions[0].MovedBy(offset.x, offset.y);
1271 case PrimitiveType::DisplacementMap:
1273 int32_t scale = ceil(abs(atts.GetFloat(eDisplacementMapScale)));
1274 return aInputChangeRegions[0].Inflated(nsIntMargin(scale, scale, scale, scale));
1277 case PrimitiveType::GaussianBlur:
1279 Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
1280 int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
1281 int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
1282 return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
1285 case PrimitiveType::DropShadow:
1287 IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
1288 nsIntRegion offsetRegion = aInputChangeRegions[0].MovedBy(offset.x, offset.y);
1289 Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
1290 int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
1291 int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
1292 nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
1293 blurRegion.Or(blurRegion, aInputChangeRegions[0]);
1294 return blurRegion;
1297 case PrimitiveType::DiffuseLighting:
1298 case PrimitiveType::SpecularLighting:
1300 Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
1301 int32_t dx = ceil(kernelUnitLength.width);
1302 int32_t dy = ceil(kernelUnitLength.height);
1303 return aInputChangeRegions[0].Inflated(nsIntMargin(dy, dx, dy, dx));
1306 default:
1307 return nsIntRegion();
1311 /* static */ nsIntRegion
1312 FilterSupport::ComputeResultChangeRegion(const FilterDescription& aFilter,
1313 const nsIntRegion& aSourceGraphicChange,
1314 const nsIntRegion& aFillPaintChange,
1315 const nsIntRegion& aStrokePaintChange)
1317 const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
1318 nsTArray<nsIntRegion> resultChangeRegions;
1320 for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
1321 const FilterPrimitiveDescription& descr = primitives[i];
1323 nsTArray<nsIntRegion> inputChangeRegions;
1324 for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
1325 int32_t inputIndex = descr.InputPrimitiveIndex(j);
1326 MOZ_ASSERT(inputIndex < i, "bad input index");
1327 nsIntRegion inputChangeRegion =
1328 ElementForIndex(inputIndex, resultChangeRegions,
1329 aSourceGraphicChange, aFillPaintChange,
1330 aStrokePaintChange);
1331 inputChangeRegions.AppendElement(inputChangeRegion);
1333 nsIntRegion changeRegion =
1334 ResultChangeRegionForPrimitive(descr, inputChangeRegions);
1335 changeRegion.And(changeRegion, ThebesIntRect(descr.PrimitiveSubregion()));
1336 resultChangeRegions.AppendElement(changeRegion);
1339 return resultChangeRegions[resultChangeRegions.Length() - 1];
1342 nsIntRegion
1343 FilterSupport::PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription,
1344 const nsTArray<nsIntRegion>& aInputExtents)
1346 const AttributeMap& atts = aDescription.Attributes();
1347 switch (aDescription.Type()) {
1349 case PrimitiveType::Empty:
1350 return nsIntRect();
1352 case PrimitiveType::Composite:
1354 uint32_t op = atts.GetUint(eCompositeOperator);
1355 if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
1356 // The arithmetic composite primitive can draw outside the bounding
1357 // box of its source images.
1358 const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
1359 MOZ_ASSERT(coefficients.Length() == 4);
1361 // The calculation is:
1362 // r = c[0] * in[0] * in[1] + c[1] * in[0] + c[2] * in[1] + c[3]
1363 nsIntRegion region;
1364 if (coefficients[0] > 0.0f) {
1365 region = aInputExtents[0].Intersect(aInputExtents[1]);
1367 if (coefficients[1] > 0.0f) {
1368 region.Or(region, aInputExtents[0]);
1370 if (coefficients[2] > 0.0f) {
1371 region.Or(region, aInputExtents[1]);
1373 if (coefficients[3] > 0.0f) {
1374 region = ThebesIntRect(aDescription.PrimitiveSubregion());
1376 return region;
1378 if (op == SVG_FECOMPOSITE_OPERATOR_IN) {
1379 return aInputExtents[0].Intersect(aInputExtents[1]);
1381 return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
1384 case PrimitiveType::Flood:
1386 if (atts.GetColor(eFloodColor).a == 0.0f) {
1387 return nsIntRect();
1389 return ThebesIntRect(aDescription.PrimitiveSubregion());
1392 case PrimitiveType::Turbulence:
1393 case PrimitiveType::Image:
1395 return ThebesIntRect(aDescription.PrimitiveSubregion());
1398 case PrimitiveType::Morphology:
1400 uint32_t op = atts.GetUint(eMorphologyOperator);
1401 if (op == SVG_OPERATOR_ERODE) {
1402 return aInputExtents[0];
1404 Size radii = atts.GetSize(eMorphologyRadii);
1405 int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
1406 int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
1407 return aInputExtents[0].Inflated(nsIntMargin(ry, rx, ry, rx));
1410 default:
1411 return ResultChangeRegionForPrimitive(aDescription, aInputExtents);
1415 /* static */ nsIntRegion
1416 FilterSupport::ComputePostFilterExtents(const FilterDescription& aFilter,
1417 const nsIntRegion& aSourceGraphicExtents)
1419 const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
1420 nsTArray<nsIntRegion> postFilterExtents;
1422 for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
1423 const FilterPrimitiveDescription& descr = primitives[i];
1424 nsIntRegion filterSpace = ThebesIntRect(descr.FilterSpaceBounds());
1426 nsTArray<nsIntRegion> inputExtents;
1427 for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
1428 int32_t inputIndex = descr.InputPrimitiveIndex(j);
1429 MOZ_ASSERT(inputIndex < i, "bad input index");
1430 nsIntRegion inputExtent =
1431 ElementForIndex(inputIndex, postFilterExtents,
1432 aSourceGraphicExtents, filterSpace, filterSpace);
1433 inputExtents.AppendElement(inputExtent);
1435 nsIntRegion extent = PostFilterExtentsForPrimitive(descr, inputExtents);
1436 extent.And(extent, ThebesIntRect(descr.PrimitiveSubregion()));
1437 postFilterExtents.AppendElement(extent);
1440 return postFilterExtents[postFilterExtents.Length() - 1];
1443 static nsIntRegion
1444 SourceNeededRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
1445 const nsIntRegion& aResultNeededRegion,
1446 int32_t aInputIndex)
1448 const AttributeMap& atts = aDescription.Attributes();
1449 switch (aDescription.Type()) {
1451 case PrimitiveType::Flood:
1452 case PrimitiveType::Turbulence:
1453 case PrimitiveType::Image:
1454 MOZ_CRASH("this shouldn't be called for filters without inputs");
1455 return nsIntRegion();
1457 case PrimitiveType::Empty:
1458 return nsIntRegion();
1460 case PrimitiveType::Blend:
1461 case PrimitiveType::Composite:
1462 case PrimitiveType::Merge:
1463 case PrimitiveType::ColorMatrix:
1464 case PrimitiveType::ComponentTransfer:
1465 case PrimitiveType::ToAlpha:
1466 return aResultNeededRegion;
1468 case PrimitiveType::Morphology:
1470 Size radii = atts.GetSize(eMorphologyRadii);
1471 int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
1472 int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
1473 return aResultNeededRegion.Inflated(nsIntMargin(ry, rx, ry, rx));
1476 case PrimitiveType::Tile:
1477 return nsIntRect(INT32_MIN/2, INT32_MIN/2, INT32_MAX, INT32_MAX);
1479 case PrimitiveType::ConvolveMatrix:
1481 Size kernelUnitLength = atts.GetSize(eConvolveMatrixKernelUnitLength);
1482 IntSize kernelSize = atts.GetIntSize(eConvolveMatrixKernelSize);
1483 IntPoint target = atts.GetIntPoint(eConvolveMatrixTarget);
1484 nsIntMargin m(ceil(kernelUnitLength.width * (kernelSize.width - target.x - 1)),
1485 ceil(kernelUnitLength.height * (kernelSize.height - target.y - 1)),
1486 ceil(kernelUnitLength.width * (target.x)),
1487 ceil(kernelUnitLength.height * (target.y)));
1488 return aResultNeededRegion.Inflated(m);
1491 case PrimitiveType::Offset:
1493 IntPoint offset = atts.GetIntPoint(eOffsetOffset);
1494 return aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
1497 case PrimitiveType::DisplacementMap:
1499 if (aInputIndex == 1) {
1500 return aResultNeededRegion;
1502 int32_t scale = ceil(abs(atts.GetFloat(eDisplacementMapScale)));
1503 return aResultNeededRegion.Inflated(nsIntMargin(scale, scale, scale, scale));
1506 case PrimitiveType::GaussianBlur:
1508 Size stdDeviation = atts.GetSize(eGaussianBlurStdDeviation);
1509 int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
1510 int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
1511 return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
1514 case PrimitiveType::DropShadow:
1516 IntPoint offset = atts.GetIntPoint(eDropShadowOffset);
1517 nsIntRegion offsetRegion =
1518 aResultNeededRegion.MovedBy(-nsIntPoint(offset.x, offset.y));
1519 Size stdDeviation = atts.GetSize(eDropShadowStdDeviation);
1520 int32_t dx = InflateSizeForBlurStdDev(stdDeviation.width);
1521 int32_t dy = InflateSizeForBlurStdDev(stdDeviation.height);
1522 nsIntRegion blurRegion = offsetRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
1523 blurRegion.Or(blurRegion, aResultNeededRegion);
1524 return blurRegion;
1527 case PrimitiveType::DiffuseLighting:
1528 case PrimitiveType::SpecularLighting:
1530 Size kernelUnitLength = atts.GetSize(eLightingKernelUnitLength);
1531 int32_t dx = ceil(kernelUnitLength.width);
1532 int32_t dy = ceil(kernelUnitLength.height);
1533 return aResultNeededRegion.Inflated(nsIntMargin(dy, dx, dy, dx));
1536 default:
1537 return nsIntRegion();
1542 /* static */ void
1543 FilterSupport::ComputeSourceNeededRegions(const FilterDescription& aFilter,
1544 const nsIntRegion& aResultNeededRegion,
1545 nsIntRegion& aSourceGraphicNeededRegion,
1546 nsIntRegion& aFillPaintNeededRegion,
1547 nsIntRegion& aStrokePaintNeededRegion)
1549 const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
1550 nsTArray<nsIntRegion> primitiveNeededRegions;
1551 primitiveNeededRegions.AppendElements(primitives.Length());
1553 primitiveNeededRegions[primitives.Length() - 1] = aResultNeededRegion;
1555 for (int32_t i = primitives.Length() - 1; i >= 0; --i) {
1556 const FilterPrimitiveDescription& descr = primitives[i];
1557 nsIntRegion neededRegion = primitiveNeededRegions[i];
1558 neededRegion.And(neededRegion, ThebesIntRect(descr.PrimitiveSubregion()));
1560 for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
1561 int32_t inputIndex = descr.InputPrimitiveIndex(j);
1562 MOZ_ASSERT(inputIndex < i, "bad input index");
1563 nsIntRegion* inputNeededRegion = const_cast<nsIntRegion*>(
1564 &ElementForIndex(inputIndex, primitiveNeededRegions,
1565 aSourceGraphicNeededRegion,
1566 aFillPaintNeededRegion, aStrokePaintNeededRegion));
1567 inputNeededRegion->Or(*inputNeededRegion,
1568 SourceNeededRegionForPrimitive(descr, neededRegion, j));
1572 // Clip original SourceGraphic to first filter region.
1573 if (primitives.Length() > 0) {
1574 const FilterPrimitiveDescription& firstDescr = primitives[0];
1575 aSourceGraphicNeededRegion.And(aSourceGraphicNeededRegion,
1576 ThebesIntRect(firstDescr.FilterSpaceBounds()));
1580 // FilterPrimitiveDescription
1582 FilterPrimitiveDescription::FilterPrimitiveDescription()
1583 : mType(PrimitiveType::Empty)
1584 , mOutputColorSpace(ColorSpace::SRGB)
1585 , mIsTainted(false)
1589 FilterPrimitiveDescription::FilterPrimitiveDescription(PrimitiveType aType)
1590 : mType(aType)
1591 , mOutputColorSpace(ColorSpace::SRGB)
1592 , mIsTainted(false)
1596 FilterPrimitiveDescription::FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther)
1597 : mType(aOther.mType)
1598 , mAttributes(aOther.mAttributes)
1599 , mInputPrimitives(aOther.mInputPrimitives)
1600 , mFilterPrimitiveSubregion(aOther.mFilterPrimitiveSubregion)
1601 , mFilterSpaceBounds(aOther.mFilterSpaceBounds)
1602 , mInputColorSpaces(aOther.mInputColorSpaces)
1603 , mOutputColorSpace(aOther.mOutputColorSpace)
1604 , mIsTainted(aOther.mIsTainted)
1608 FilterPrimitiveDescription&
1609 FilterPrimitiveDescription::operator=(const FilterPrimitiveDescription& aOther)
1611 if (this != &aOther) {
1612 mType = aOther.mType;
1613 mAttributes = aOther.mAttributes;
1614 mInputPrimitives = aOther.mInputPrimitives;
1615 mFilterPrimitiveSubregion = aOther.mFilterPrimitiveSubregion;
1616 mFilterSpaceBounds = aOther.mFilterSpaceBounds;
1617 mInputColorSpaces = aOther.mInputColorSpaces;
1618 mOutputColorSpace = aOther.mOutputColorSpace;
1619 mIsTainted = aOther.mIsTainted;
1621 return *this;
1624 bool
1625 FilterPrimitiveDescription::operator==(const FilterPrimitiveDescription& aOther) const
1627 return mType == aOther.mType &&
1628 mFilterPrimitiveSubregion.IsEqualInterior(aOther.mFilterPrimitiveSubregion) &&
1629 mFilterSpaceBounds.IsEqualInterior(aOther.mFilterSpaceBounds) &&
1630 mOutputColorSpace == aOther.mOutputColorSpace &&
1631 mIsTainted == aOther.mIsTainted &&
1632 mInputPrimitives == aOther.mInputPrimitives &&
1633 mInputColorSpaces == aOther.mInputColorSpaces &&
1634 mAttributes == aOther.mAttributes;
1637 // FilterDescription
1639 bool
1640 FilterDescription::operator==(const FilterDescription& aOther) const
1642 return mPrimitives == aOther.mPrimitives;
1645 // AttributeMap
1647 // A class that wraps different types for easy storage in a hashtable. Only
1648 // used by AttributeMap.
1649 struct FilterAttribute {
1650 FilterAttribute(const FilterAttribute& aOther);
1651 ~FilterAttribute();
1653 bool operator==(const FilterAttribute& aOther) const;
1654 bool operator!=(const FilterAttribute& aOther) const
1656 return !(*this == aOther);
1659 AttributeType Type() const { return mType; }
1661 #define MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(type, typeLabel) \
1662 explicit FilterAttribute(type aValue) \
1663 : mType(AttributeType::e##typeLabel), m##typeLabel(aValue) \
1664 {} \
1665 type As##typeLabel() { \
1666 MOZ_ASSERT(mType == AttributeType::e##typeLabel); \
1667 return m##typeLabel; \
1670 #define MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(className) \
1671 explicit FilterAttribute(const className& aValue) \
1672 : mType(AttributeType::e##className), m##className(new className(aValue)) \
1673 {} \
1674 className As##className() { \
1675 MOZ_ASSERT(mType == AttributeType::e##className); \
1676 return *m##className; \
1679 MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(bool, Bool)
1680 MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(uint32_t, Uint)
1681 MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC(float, Float)
1682 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Size)
1683 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntSize)
1684 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(IntPoint)
1685 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix)
1686 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Matrix5x4)
1687 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Point3D)
1688 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(Color)
1689 MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS(AttributeMap)
1691 #undef MAKE_CONSTRUCTOR_AND_ACCESSOR_BASIC
1692 #undef MAKE_CONSTRUCTOR_AND_ACCESSOR_CLASS
1694 FilterAttribute(const float* aValue, uint32_t aLength)
1695 : mType(AttributeType::eFloats)
1697 mFloats = new nsTArray<float>();
1698 mFloats->AppendElements(aValue, aLength);
1701 const nsTArray<float>& AsFloats() const {
1702 MOZ_ASSERT(mType == AttributeType::eFloats);
1703 return *mFloats;
1706 private:
1707 const AttributeType mType;
1709 union {
1710 bool mBool;
1711 uint32_t mUint;
1712 float mFloat;
1713 Size* mSize;
1714 IntSize* mIntSize;
1715 IntPoint* mIntPoint;
1716 Matrix* mMatrix;
1717 Matrix5x4* mMatrix5x4;
1718 Point3D* mPoint3D;
1719 Color* mColor;
1720 AttributeMap* mAttributeMap;
1721 nsTArray<float>* mFloats;
1725 FilterAttribute::FilterAttribute(const FilterAttribute& aOther)
1726 : mType(aOther.mType)
1728 switch (mType) {
1729 case AttributeType::eBool:
1730 mBool = aOther.mBool;
1731 break;
1732 case AttributeType::eUint:
1733 mUint = aOther.mUint;
1734 break;
1735 case AttributeType::eFloat:
1736 mFloat = aOther.mFloat;
1737 break;
1739 #define HANDLE_CLASS(className) \
1740 case AttributeType::e##className: \
1741 m##className = new className(*aOther.m##className); \
1742 break;
1744 HANDLE_CLASS(Size)
1745 HANDLE_CLASS(IntSize)
1746 HANDLE_CLASS(IntPoint)
1747 HANDLE_CLASS(Matrix)
1748 HANDLE_CLASS(Matrix5x4)
1749 HANDLE_CLASS(Point3D)
1750 HANDLE_CLASS(Color)
1751 HANDLE_CLASS(AttributeMap)
1753 #undef HANDLE_CLASS
1755 case AttributeType::eFloats:
1756 mFloats = new nsTArray<float>(*aOther.mFloats);
1757 break;
1758 case AttributeType::Max:
1759 break;
1763 FilterAttribute::~FilterAttribute() {
1764 switch (mType) {
1765 case AttributeType::Max:
1766 case AttributeType::eBool:
1767 case AttributeType::eUint:
1768 case AttributeType::eFloat:
1769 break;
1771 #define HANDLE_CLASS(className) \
1772 case AttributeType::e##className: \
1773 delete m##className; \
1774 break;
1776 HANDLE_CLASS(Size)
1777 HANDLE_CLASS(IntSize)
1778 HANDLE_CLASS(IntPoint)
1779 HANDLE_CLASS(Matrix)
1780 HANDLE_CLASS(Matrix5x4)
1781 HANDLE_CLASS(Point3D)
1782 HANDLE_CLASS(Color)
1783 HANDLE_CLASS(AttributeMap)
1785 #undef HANDLE_CLASS
1787 case AttributeType::eFloats:
1788 delete mFloats;
1789 break;
1793 bool
1794 FilterAttribute::operator==(const FilterAttribute& aOther) const
1796 if (mType != aOther.mType) {
1797 return false;
1800 switch (mType) {
1802 #define HANDLE_TYPE(typeName) \
1803 case AttributeType::e##typeName: \
1804 return m##typeName == aOther.m##typeName;
1806 HANDLE_TYPE(Bool)
1807 HANDLE_TYPE(Uint)
1808 HANDLE_TYPE(Float)
1809 HANDLE_TYPE(Size)
1810 HANDLE_TYPE(IntSize)
1811 HANDLE_TYPE(IntPoint)
1812 HANDLE_TYPE(Matrix)
1813 HANDLE_TYPE(Matrix5x4)
1814 HANDLE_TYPE(Point3D)
1815 HANDLE_TYPE(Color)
1816 HANDLE_TYPE(AttributeMap)
1817 HANDLE_TYPE(Floats)
1819 #undef HANDLE_TYPE
1821 default:
1822 return false;
1826 typedef FilterAttribute Attribute;
1828 AttributeMap::AttributeMap()
1832 AttributeMap::~AttributeMap()
1836 static PLDHashOperator
1837 CopyAttribute(const uint32_t& aAttributeName,
1838 Attribute* aAttribute,
1839 void* aAttributes)
1841 typedef nsClassHashtable<nsUint32HashKey, Attribute> Map;
1842 Map* map = static_cast<Map*>(aAttributes);
1843 map->Put(aAttributeName, new Attribute(*aAttribute));
1844 return PL_DHASH_NEXT;
1847 AttributeMap::AttributeMap(const AttributeMap& aOther)
1849 aOther.mMap.EnumerateRead(CopyAttribute, &mMap);
1852 AttributeMap&
1853 AttributeMap::operator=(const AttributeMap& aOther)
1855 if (this != &aOther) {
1856 mMap.Clear();
1857 aOther.mMap.EnumerateRead(CopyAttribute, &mMap);
1859 return *this;
1862 namespace {
1863 struct MatchingMap {
1864 typedef nsClassHashtable<nsUint32HashKey, Attribute> Map;
1865 const Map& map;
1866 bool matches;
1870 static PLDHashOperator
1871 CheckAttributeEquality(const uint32_t& aAttributeName,
1872 Attribute* aAttribute,
1873 void* aMatchingMap)
1875 MatchingMap& matchingMap = *static_cast<MatchingMap*>(aMatchingMap);
1876 Attribute* matchingAttribute = matchingMap.map.Get(aAttributeName);
1877 if (!matchingAttribute ||
1878 *matchingAttribute != *aAttribute) {
1879 matchingMap.matches = false;
1880 return PL_DHASH_STOP;
1882 return PL_DHASH_NEXT;
1885 bool
1886 AttributeMap::operator==(const AttributeMap& aOther) const
1888 if (mMap.Count() != aOther.mMap.Count()) {
1889 return false;
1892 MatchingMap matchingMap = { mMap, true };
1893 aOther.mMap.EnumerateRead(CheckAttributeEquality, &matchingMap);
1894 return matchingMap.matches;
1897 namespace {
1898 struct HandlerWithUserData
1900 AttributeMap::AttributeHandleCallback handler;
1901 void* userData;
1905 static PLDHashOperator
1906 PassAttributeToHandleCallback(const uint32_t& aAttributeName,
1907 Attribute* aAttribute,
1908 void* aHandlerWithUserData)
1910 HandlerWithUserData* handlerWithUserData =
1911 static_cast<HandlerWithUserData*>(aHandlerWithUserData);
1912 return handlerWithUserData->handler(AttributeName(aAttributeName),
1913 aAttribute->Type(),
1914 handlerWithUserData->userData) ?
1915 PL_DHASH_NEXT : PL_DHASH_STOP;
1918 void
1919 AttributeMap::EnumerateRead(AttributeMap::AttributeHandleCallback aCallback, void* aUserData) const
1921 HandlerWithUserData handlerWithUserData = { aCallback, aUserData };
1922 mMap.EnumerateRead(PassAttributeToHandleCallback, &handlerWithUserData);
1925 uint32_t
1926 AttributeMap::Count() const
1928 return mMap.Count();
1931 #define MAKE_ATTRIBUTE_HANDLERS_BASIC(type, typeLabel, defaultValue) \
1932 type \
1933 AttributeMap::Get##typeLabel(AttributeName aName) const { \
1934 Attribute* value = mMap.Get(aName); \
1935 return value ? value->As##typeLabel() : defaultValue; \
1937 void \
1938 AttributeMap::Set(AttributeName aName, type aValue) { \
1939 mMap.Remove(aName); \
1940 mMap.Put(aName, new Attribute(aValue)); \
1943 #define MAKE_ATTRIBUTE_HANDLERS_CLASS(className) \
1944 className \
1945 AttributeMap::Get##className(AttributeName aName) const { \
1946 Attribute* value = mMap.Get(aName); \
1947 return value ? value->As##className() : className(); \
1949 void \
1950 AttributeMap::Set(AttributeName aName, const className& aValue) { \
1951 mMap.Remove(aName); \
1952 mMap.Put(aName, new Attribute(aValue)); \
1955 MAKE_ATTRIBUTE_HANDLERS_BASIC(bool, Bool, false)
1956 MAKE_ATTRIBUTE_HANDLERS_BASIC(uint32_t, Uint, 0)
1957 MAKE_ATTRIBUTE_HANDLERS_BASIC(float, Float, 0)
1958 MAKE_ATTRIBUTE_HANDLERS_CLASS(Size)
1959 MAKE_ATTRIBUTE_HANDLERS_CLASS(IntSize)
1960 MAKE_ATTRIBUTE_HANDLERS_CLASS(IntPoint)
1961 MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix)
1962 MAKE_ATTRIBUTE_HANDLERS_CLASS(Matrix5x4)
1963 MAKE_ATTRIBUTE_HANDLERS_CLASS(Point3D)
1964 MAKE_ATTRIBUTE_HANDLERS_CLASS(Color)
1965 MAKE_ATTRIBUTE_HANDLERS_CLASS(AttributeMap)
1967 #undef MAKE_ATTRIBUTE_HANDLERS_BASIC
1968 #undef MAKE_ATTRIBUTE_HANDLERS_CLASS
1970 const nsTArray<float>&
1971 AttributeMap::GetFloats(AttributeName aName) const
1973 Attribute* value = mMap.Get(aName);
1974 if (!value) {
1975 value = new Attribute(nullptr, 0);
1976 mMap.Put(aName, value);
1978 return value->AsFloats();
1981 void
1982 AttributeMap::Set(AttributeName aName, const float* aValues, int32_t aLength)
1984 mMap.Remove(aName);
1985 mMap.Put(aName, new Attribute(aValues, aLength));