tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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