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