tor-browser

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

FilterInstance.cpp (83712B)


      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 // Main header first:
      8 #include "FilterInstance.h"
      9 
     10 // MFBT headers next:
     11 #include "mozilla/UniquePtr.h"
     12 
     13 // Keep others in (case-insensitive) order:
     14 #include "CSSFilterInstance.h"
     15 #include "FilterSupport.h"
     16 #include "ImgDrawResult.h"
     17 #include "SVGContentUtils.h"
     18 #include "SVGIntegrationUtils.h"
     19 #include "gfx2DGlue.h"
     20 #include "gfxContext.h"
     21 #include "gfxPlatform.h"
     22 #include "gfxUtils.h"
     23 #include "mozilla/ISVGDisplayableFrame.h"
     24 #include "mozilla/SVGFilterInstance.h"
     25 #include "mozilla/SVGObserverUtils.h"
     26 #include "mozilla/SVGUtils.h"
     27 #include "mozilla/StaticPrefs_gfx.h"
     28 #include "mozilla/dom/Document.h"
     29 #include "mozilla/gfx/Filters.h"
     30 #include "mozilla/gfx/Helpers.h"
     31 #include "mozilla/gfx/Logging.h"
     32 #include "mozilla/gfx/PatternHelpers.h"
     33 #include "nsLayoutUtils.h"
     34 
     35 using namespace mozilla::dom;
     36 using namespace mozilla::gfx;
     37 using namespace mozilla::image;
     38 
     39 namespace mozilla {
     40 
     41 FilterDescription FilterInstance::GetFilterDescription(
     42    nsIContent* aFilteredElement, Span<const StyleFilter> aFilterChain,
     43    ISVGFilterObserverList* aFiltersObserverList, bool aFilterInputIsTainted,
     44    const UserSpaceMetrics& aMetrics, const gfxRect& aBBox,
     45    nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages) {
     46  gfxMatrix identity;
     47 
     48  nsTArray<SVGFilterFrame*> filterFrames;
     49  if (SVGObserverUtils::GetAndObserveFilters(aFiltersObserverList,
     50                                             &filterFrames) ==
     51      SVGObserverUtils::eHasRefsSomeInvalid) {
     52    return FilterDescription();
     53  }
     54 
     55  FilterInstance instance(nullptr, aFilteredElement, aMetrics, aFilterChain,
     56                          filterFrames, aFilterInputIsTainted, nullptr,
     57                          identity, nullptr, nullptr, nullptr, &aBBox);
     58  if (!instance.IsInitialized()) {
     59    return FilterDescription();
     60  }
     61  return instance.ExtractDescriptionAndAdditionalImages(aOutAdditionalImages);
     62 }
     63 
     64 static UniquePtr<UserSpaceMetrics> UserSpaceMetricsForFrame(nsIFrame* aFrame) {
     65  if (auto* element = SVGElement::FromNodeOrNull(aFrame->GetContent())) {
     66    return MakeUnique<SVGElementMetrics>(element);
     67  }
     68  return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
     69 }
     70 
     71 void FilterInstance::PaintFilteredFrame(
     72    nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilterChain,
     73    const nsTArray<SVGFilterFrame*>& aFilterFrames, gfxContext* aCtx,
     74    const SVGFilterPaintCallback& aPaintCallback, const nsRegion* aDirtyArea,
     75    imgDrawingParams& aImgParams, float aOpacity,
     76    const gfxRect* aOverrideBBox) {
     77  UniquePtr<UserSpaceMetrics> metrics =
     78      UserSpaceMetricsForFrame(aFilteredFrame);
     79 
     80  gfxContextMatrixAutoSaveRestore autoSR(aCtx);
     81  auto scaleFactors = aCtx->CurrentMatrixDouble().ScaleFactors();
     82  if (scaleFactors.xScale == 0 || scaleFactors.yScale == 0) {
     83    return;
     84  }
     85 
     86  gfxMatrix scaleMatrix(scaleFactors.xScale, 0.0f, 0.0f, scaleFactors.yScale,
     87                        0.0f, 0.0f);
     88 
     89  gfxMatrix reverseScaleMatrix = scaleMatrix;
     90  DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
     91  MOZ_ASSERT(invertible);
     92 
     93  gfxMatrix scaleMatrixInDevUnits =
     94      scaleMatrix * SVGUtils::GetCSSPxToDevPxMatrix(aFilteredFrame);
     95 
     96  // Hardcode InputIsTainted to true because we don't want JS to be able to
     97  // read the rendered contents of aFilteredFrame.
     98  FilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
     99                          *metrics, aFilterChain, aFilterFrames,
    100                          /* InputIsTainted */ true, aPaintCallback,
    101                          scaleMatrixInDevUnits, aDirtyArea, nullptr, nullptr,
    102                          aOverrideBBox);
    103  if (instance.IsInitialized()) {
    104    // Pull scale vector out of aCtx's transform, put all scale factors, which
    105    // includes css and css-to-dev-px scale, into scaleMatrixInDevUnits.
    106    aCtx->SetMatrixDouble(reverseScaleMatrix * aCtx->CurrentMatrixDouble());
    107 
    108    instance.Render(aCtx, aImgParams, aOpacity);
    109  } else {
    110    // Render the unfiltered contents.
    111    aPaintCallback(*aCtx, aImgParams, nullptr, nullptr);
    112  }
    113 }
    114 
    115 static mozilla::wr::ComponentTransferFuncType FuncTypeToWr(uint8_t aFuncType) {
    116  MOZ_ASSERT(aFuncType != SVG_FECOMPONENTTRANSFER_SAME_AS_R);
    117  switch (aFuncType) {
    118    case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
    119      return mozilla::wr::ComponentTransferFuncType::Table;
    120    case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
    121      return mozilla::wr::ComponentTransferFuncType::Discrete;
    122    case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
    123      return mozilla::wr::ComponentTransferFuncType::Linear;
    124    case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
    125      return mozilla::wr::ComponentTransferFuncType::Gamma;
    126    case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
    127    default:
    128      return mozilla::wr::ComponentTransferFuncType::Identity;
    129  }
    130  MOZ_ASSERT_UNREACHABLE("all func types not handled?");
    131  return mozilla::wr::ComponentTransferFuncType::Identity;
    132 }
    133 
    134 WrFiltersStatus FilterInstance::BuildWebRenderFilters(
    135    nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilters,
    136    StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters,
    137    const nsPoint& aOffsetForSVGFilters) {
    138  WrFiltersStatus status = WrFiltersStatus::BLOB_FALLBACK;
    139  if (StaticPrefs::gfx_webrender_svg_filter_effects()) {
    140    status =
    141        BuildWebRenderSVGFiltersImpl(aFilteredFrame, aFilters, aStyleFilterType,
    142                                     aWrFilters, aOffsetForSVGFilters);
    143  }
    144  if (status == WrFiltersStatus::BLOB_FALLBACK) {
    145    status = BuildWebRenderFiltersImpl(aFilteredFrame, aFilters,
    146                                       aStyleFilterType, aWrFilters);
    147  }
    148  if (status == WrFiltersStatus::BLOB_FALLBACK) {
    149    aFilteredFrame->PresContext()->Document()->SetUseCounter(
    150        eUseCounter_custom_WrFilterFallback);
    151  }
    152 
    153  return status;
    154 }
    155 
    156 WrFiltersStatus FilterInstance::BuildWebRenderFiltersImpl(
    157    nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilters,
    158    StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters) {
    159  aWrFilters.filters.Clear();
    160  aWrFilters.filter_datas.Clear();
    161  aWrFilters.values.Clear();
    162  aWrFilters.post_filters_clip = Nothing();
    163 
    164  nsIFrame* firstFrame =
    165      nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFilteredFrame);
    166 
    167  nsTArray<SVGFilterFrame*> filterFrames;
    168  if (SVGObserverUtils::GetAndObserveFilters(firstFrame, &filterFrames,
    169                                             aStyleFilterType) ==
    170      SVGObserverUtils::eHasRefsSomeInvalid) {
    171    return WrFiltersStatus::UNSUPPORTED;
    172  }
    173 
    174  UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(firstFrame);
    175 
    176  // TODO: simply using an identity matrix here, was pulling the scale from a
    177  // gfx context for the non-wr path.
    178  gfxMatrix scaleMatrix;
    179  gfxMatrix scaleMatrixInDevUnits =
    180      scaleMatrix * SVGUtils::GetCSSPxToDevPxMatrix(firstFrame);
    181 
    182  // Hardcode inputIsTainted to true because we don't want JS to be able to
    183  // read the rendered contents of aFilteredFrame.
    184  FilterInstance instance(firstFrame, firstFrame->GetContent(), *metrics,
    185                          aFilters, filterFrames, /* inputIsTainted */ true,
    186                          nullptr, scaleMatrixInDevUnits, nullptr, nullptr,
    187                          nullptr, nullptr);
    188 
    189  if (!instance.IsInitialized()) {
    190    return WrFiltersStatus::UNSUPPORTED;
    191  }
    192 
    193  // If there are too many filters to render, then just pretend that we
    194  // succeeded, and don't render any of them.
    195  if (instance.mFilterDescription.mPrimitives.Length() >
    196      StaticPrefs::gfx_webrender_max_filter_ops_per_chain()) {
    197    return WrFiltersStatus::DISABLED_FOR_PERFORMANCE;
    198  }
    199 
    200  Maybe<IntRect> finalClip;
    201  bool srgb = true;
    202  // We currently apply the clip on the stacking context after applying filters,
    203  // but primitive subregions imply clipping after each filter and not just the
    204  // end of the chain. For some types of filter it doesn't matter, but for those
    205  // which sample outside of the location of the destination pixel like blurs,
    206  // only clipping after could produce incorrect results, so we bail out in this
    207  // case.
    208  // We can lift this restriction once we have added support for primitive
    209  // subregions to WebRender's filters.
    210  for (uint32_t i = 0; i < instance.mFilterDescription.mPrimitives.Length();
    211       i++) {
    212    const auto& primitive = instance.mFilterDescription.mPrimitives[i];
    213 
    214    // WebRender only supports filters with one input.
    215    if (primitive.NumberOfInputs() != 1) {
    216      return WrFiltersStatus::BLOB_FALLBACK;
    217    }
    218    // The first primitive must have the source graphic as the input, all
    219    // other primitives must have the prior primitive as the input, otherwise
    220    // it's not supported by WebRender.
    221    if (i == 0) {
    222      if (primitive.InputPrimitiveIndex(0) !=
    223          FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic) {
    224        return WrFiltersStatus::BLOB_FALLBACK;
    225      }
    226    } else if (primitive.InputPrimitiveIndex(0) != int32_t(i - 1)) {
    227      return WrFiltersStatus::BLOB_FALLBACK;
    228    }
    229 
    230    bool previousSrgb = srgb;
    231    bool primNeedsSrgb = primitive.InputColorSpace(0) == gfx::ColorSpace::SRGB;
    232    if (srgb && !primNeedsSrgb) {
    233      aWrFilters.filters.AppendElement(wr::FilterOp::SrgbToLinear());
    234    } else if (!srgb && primNeedsSrgb) {
    235      aWrFilters.filters.AppendElement(wr::FilterOp::LinearToSrgb());
    236    }
    237    srgb = primitive.OutputColorSpace() == gfx::ColorSpace::SRGB;
    238 
    239    const PrimitiveAttributes& attr = primitive.Attributes();
    240 
    241    bool filterIsNoop = false;
    242 
    243    if (attr.is<OpacityAttributes>()) {
    244      float opacity = attr.as<OpacityAttributes>().mOpacity;
    245      aWrFilters.filters.AppendElement(wr::FilterOp::Opacity(
    246          wr::PropertyBinding<float>::Value(opacity), opacity));
    247    } else if (attr.is<ColorMatrixAttributes>()) {
    248      const ColorMatrixAttributes& attributes =
    249          attr.as<ColorMatrixAttributes>();
    250 
    251      float transposed[20];
    252      if (gfx::ComputeColorMatrix(attributes, transposed)) {
    253        float matrix[20] = {
    254            transposed[0], transposed[5], transposed[10], transposed[15],
    255            transposed[1], transposed[6], transposed[11], transposed[16],
    256            transposed[2], transposed[7], transposed[12], transposed[17],
    257            transposed[3], transposed[8], transposed[13], transposed[18],
    258            transposed[4], transposed[9], transposed[14], transposed[19]};
    259 
    260        aWrFilters.filters.AppendElement(wr::FilterOp::ColorMatrix(matrix));
    261      } else {
    262        filterIsNoop = true;
    263      }
    264    } else if (attr.is<GaussianBlurAttributes>()) {
    265      if (finalClip) {
    266        // There's a clip that needs to apply before the blur filter, but
    267        // WebRender only lets us apply the clip at the end of the filter
    268        // chain. Clipping after a blur is not equivalent to clipping before
    269        // a blur, so bail out.
    270        return WrFiltersStatus::BLOB_FALLBACK;
    271      }
    272 
    273      const GaussianBlurAttributes& blur = attr.as<GaussianBlurAttributes>();
    274 
    275      const Size& stdDev = blur.mStdDeviation;
    276      if (stdDev.width != 0.0 || stdDev.height != 0.0) {
    277        aWrFilters.filters.AppendElement(
    278            wr::FilterOp::Blur(stdDev.width, stdDev.height));
    279      } else {
    280        filterIsNoop = true;
    281      }
    282    } else if (attr.is<DropShadowAttributes>()) {
    283      if (finalClip) {
    284        // We have to bail out for the same reason we would with a blur filter.
    285        return WrFiltersStatus::BLOB_FALLBACK;
    286      }
    287 
    288      const DropShadowAttributes& shadow = attr.as<DropShadowAttributes>();
    289 
    290      const Size& stdDev = shadow.mStdDeviation;
    291      if (stdDev.width != stdDev.height) {
    292        return WrFiltersStatus::BLOB_FALLBACK;
    293      }
    294 
    295      sRGBColor color = shadow.mColor;
    296      if (!primNeedsSrgb) {
    297        color = FilterWrappers::SRGBToLinearRGB(color);
    298      }
    299      wr::Shadow wrShadow;
    300      wrShadow.offset = {shadow.mOffset.x, shadow.mOffset.y};
    301      wrShadow.color = wr::ToColorF(ToDeviceColor(color));
    302      wrShadow.blur_radius = stdDev.width;
    303      wr::FilterOp filterOp = wr::FilterOp::DropShadow(wrShadow);
    304 
    305      aWrFilters.filters.AppendElement(filterOp);
    306    } else if (attr.is<ComponentTransferAttributes>()) {
    307      const ComponentTransferAttributes& attributes =
    308          attr.as<ComponentTransferAttributes>();
    309 
    310      size_t numValues =
    311          attributes.mValues[0].Length() + attributes.mValues[1].Length() +
    312          attributes.mValues[2].Length() + attributes.mValues[3].Length();
    313      if (numValues > 1024) {
    314        // Depending on how the wr shaders are implemented we may need to
    315        // limit the total number of values.
    316        return WrFiltersStatus::BLOB_FALLBACK;
    317      }
    318 
    319      wr::FilterOp filterOp = {wr::FilterOp::Tag::ComponentTransfer};
    320      wr::WrFilterData filterData;
    321      aWrFilters.values.AppendElement(nsTArray<float>());
    322      nsTArray<float>* values = &aWrFilters.values.LastElement();
    323      values->SetCapacity(numValues);
    324 
    325      filterData.funcR_type = FuncTypeToWr(attributes.mTypes[0]);
    326      size_t R_startindex = values->Length();
    327      values->AppendElements(attributes.mValues[0]);
    328      filterData.R_values_count = attributes.mValues[0].Length();
    329 
    330      size_t indexToUse =
    331          attributes.mTypes[1] == SVG_FECOMPONENTTRANSFER_SAME_AS_R ? 0 : 1;
    332      filterData.funcG_type = FuncTypeToWr(attributes.mTypes[indexToUse]);
    333      size_t G_startindex = values->Length();
    334      values->AppendElements(attributes.mValues[indexToUse]);
    335      filterData.G_values_count = attributes.mValues[indexToUse].Length();
    336 
    337      indexToUse =
    338          attributes.mTypes[2] == SVG_FECOMPONENTTRANSFER_SAME_AS_R ? 0 : 2;
    339      filterData.funcB_type = FuncTypeToWr(attributes.mTypes[indexToUse]);
    340      size_t B_startindex = values->Length();
    341      values->AppendElements(attributes.mValues[indexToUse]);
    342      filterData.B_values_count = attributes.mValues[indexToUse].Length();
    343 
    344      filterData.funcA_type = FuncTypeToWr(attributes.mTypes[3]);
    345      size_t A_startindex = values->Length();
    346      values->AppendElements(attributes.mValues[3]);
    347      filterData.A_values_count = attributes.mValues[3].Length();
    348 
    349      filterData.R_values =
    350          filterData.R_values_count > 0 ? &((*values)[R_startindex]) : nullptr;
    351      filterData.G_values =
    352          filterData.G_values_count > 0 ? &((*values)[G_startindex]) : nullptr;
    353      filterData.B_values =
    354          filterData.B_values_count > 0 ? &((*values)[B_startindex]) : nullptr;
    355      filterData.A_values =
    356          filterData.A_values_count > 0 ? &((*values)[A_startindex]) : nullptr;
    357 
    358      aWrFilters.filters.AppendElement(filterOp);
    359      aWrFilters.filter_datas.AppendElement(filterData);
    360    } else {
    361      return WrFiltersStatus::BLOB_FALLBACK;
    362    }
    363 
    364    if (filterIsNoop && !aWrFilters.filters.IsEmpty() &&
    365        (aWrFilters.filters.LastElement().tag ==
    366             wr::FilterOp::Tag::SrgbToLinear ||
    367         aWrFilters.filters.LastElement().tag ==
    368             wr::FilterOp::Tag::LinearToSrgb)) {
    369      // We pushed a color space conversion filter in prevision of applying
    370      // another filter which turned out to be a no-op, so the conversion is
    371      // unnecessary. Remove it from the filter list.
    372      // This is both an optimization and a way to pass the wptest
    373      // css/filter-effects/filter-scale-001.html for which the needless
    374      // sRGB->linear->no-op->sRGB roundtrip introduces a slight error and we
    375      // cannot add fuzziness to the test.
    376      (void)aWrFilters.filters.PopLastElement();
    377      srgb = previousSrgb;
    378    }
    379 
    380    if (!filterIsNoop) {
    381      if (finalClip.isNothing()) {
    382        finalClip = Some(primitive.PrimitiveSubregion());
    383      } else {
    384        finalClip =
    385            Some(primitive.PrimitiveSubregion().Intersect(finalClip.value()));
    386      }
    387    }
    388  }
    389 
    390  if (!srgb) {
    391    aWrFilters.filters.AppendElement(wr::FilterOp::LinearToSrgb());
    392  }
    393 
    394  if (finalClip) {
    395    aWrFilters.post_filters_clip =
    396        Some(instance.FilterSpaceToFrameSpace(finalClip.value()));
    397  }
    398  return WrFiltersStatus::CHAIN;
    399 }
    400 
    401 static WrFiltersStatus WrSVGFEInputBuild(wr::FilterOpGraphPictureReference& pic,
    402                                         int32_t aSource, int16_t aNodeOutput,
    403                                         int16_t aSourceGraphic,
    404                                         int16_t aSourceAlpha,
    405                                         const int16_t aBufferIdMapping[]) {
    406  switch (aSource) {
    407    case FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic:
    408      pic.buffer_id =
    409          wr::FilterOpGraphPictureBufferId::BufferId(aSourceGraphic);
    410      break;
    411    case FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha:
    412      pic.buffer_id = wr::FilterOpGraphPictureBufferId::BufferId(aSourceAlpha);
    413      break;
    414    case FilterPrimitiveDescription::kPrimitiveIndexFillPaint:
    415    case FilterPrimitiveDescription::kPrimitiveIndexStrokePaint:
    416      // https://bugzilla.mozilla.org/show_bug.cgi?id=1897878
    417      // Fill and Stroke paints are not yet supported by WebRender, they may be
    418      // a color (most common) or pattern fill, so we could implement them with
    419      // feFlood or feImage + feTile depending on the nature of the fill.
    420      return WrFiltersStatus::BLOB_FALLBACK;
    421    default:
    422      MOZ_RELEASE_ASSERT(
    423          aSource >= 0,
    424          "Unrecognized SVG filter primitive enum value - added another?");
    425      MOZ_RELEASE_ASSERT(aSource < aNodeOutput,
    426                         "Invalid DAG - nodes can only refer to earlier nodes");
    427      if (aSource < 0 || aSource >= aNodeOutput) {
    428        return WrFiltersStatus::UNSUPPORTED;
    429      }
    430      // Look up the node we remapped this id to.
    431      // This can't overflow because aSource < aNodeOutput and the table is the
    432      // same size.
    433      pic.buffer_id =
    434          wr::FilterOpGraphPictureBufferId::BufferId(aBufferIdMapping[aSource]);
    435      break;
    436  }
    437  return WrFiltersStatus::SVGFE;
    438 }
    439 
    440 static WrFiltersStatus WrFilterOpSVGFEOpacity(
    441    WrFiltersHolder& aWrFilters, const wr::FilterOpGraphNode& aGraphNode,
    442    const OpacityAttributes& aAttributes) {
    443  // CSS opacity
    444  // This is the only CSS property that is has no direct analog in SVG, although
    445  // technically it can be represented as SVGFEComponentTransfer or
    446  // SVGFEColorMatrix or SVGFECompositeArithmetic, those would be inefficient
    447  // approaches.
    448  if (!StaticPrefs::gfx_webrender_svg_filter_effects_opacity()) {
    449    // Fallback if pref is disabled
    450    return WrFiltersStatus::BLOB_FALLBACK;
    451  }
    452  float opacity = aAttributes.mOpacity;
    453  if (opacity != 1.0f) {
    454    aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEOpacity(
    455        aGraphNode, wr::PropertyBinding<float>::Value(opacity), opacity));
    456  } else {
    457    // If it's a no-op, we still have to generate a graph node
    458    aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEIdentity(aGraphNode));
    459  }
    460  return WrFiltersStatus::SVGFE;
    461 }
    462 
    463 static WrFiltersStatus WrFilterOpSVGFEToAlpha(
    464    WrFiltersHolder& aWrFilters, const wr::FilterOpGraphNode& aGraphNode,
    465    const ToAlphaAttributes& aAttributes) {
    466  // Convert a color image to an alpha channel - internal use; generated by
    467  // SVGFilterInstance::GetOrCreateSourceAlphaIndex().
    468  if (!StaticPrefs::GetPrefName_gfx_webrender_svg_filter_effects_toalpha()) {
    469    // Fallback if pref is disabled
    470    return WrFiltersStatus::BLOB_FALLBACK;
    471  }
    472  aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEToAlpha(aGraphNode));
    473  return WrFiltersStatus::SVGFE;
    474 }
    475 
    476 static WrFiltersStatus WrFilterOpSVGFEBlend(
    477    WrFiltersHolder& aWrFilters, const wr::FilterOpGraphNode& aGraphNode,
    478    const BlendAttributes& aAttributes) {
    479  // SVGFEBlend - common
    480  if (!StaticPrefs::gfx_webrender_svg_filter_effects_feblend()) {
    481    // Fallback if pref is disabled
    482    return WrFiltersStatus::BLOB_FALLBACK;
    483  }
    484  switch (aAttributes.mBlendMode) {
    485    case SVG_FEBLEND_MODE_COLOR:
    486      aWrFilters.filters.AppendElement(
    487          wr::FilterOp::SVGFEBlendColor(aGraphNode));
    488      return WrFiltersStatus::SVGFE;
    489    case SVG_FEBLEND_MODE_COLOR_BURN:
    490      aWrFilters.filters.AppendElement(
    491          wr::FilterOp::SVGFEBlendColorBurn(aGraphNode));
    492      return WrFiltersStatus::SVGFE;
    493    case SVG_FEBLEND_MODE_COLOR_DODGE:
    494      aWrFilters.filters.AppendElement(
    495          wr::FilterOp::SVGFEBlendColorDodge(aGraphNode));
    496      return WrFiltersStatus::SVGFE;
    497    case SVG_FEBLEND_MODE_DARKEN:
    498      aWrFilters.filters.AppendElement(
    499          wr::FilterOp::SVGFEBlendDarken(aGraphNode));
    500      return WrFiltersStatus::SVGFE;
    501    case SVG_FEBLEND_MODE_DIFFERENCE:
    502      aWrFilters.filters.AppendElement(
    503          wr::FilterOp::SVGFEBlendDifference(aGraphNode));
    504      return WrFiltersStatus::SVGFE;
    505    case SVG_FEBLEND_MODE_EXCLUSION:
    506      aWrFilters.filters.AppendElement(
    507          wr::FilterOp::SVGFEBlendExclusion(aGraphNode));
    508      return WrFiltersStatus::SVGFE;
    509    case SVG_FEBLEND_MODE_HARD_LIGHT:
    510      aWrFilters.filters.AppendElement(
    511          wr::FilterOp::SVGFEBlendHardLight(aGraphNode));
    512      return WrFiltersStatus::SVGFE;
    513    case SVG_FEBLEND_MODE_HUE:
    514      aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEBlendHue(aGraphNode));
    515      return WrFiltersStatus::SVGFE;
    516    case SVG_FEBLEND_MODE_LIGHTEN:
    517      aWrFilters.filters.AppendElement(
    518          wr::FilterOp::SVGFEBlendLighten(aGraphNode));
    519      return WrFiltersStatus::SVGFE;
    520    case SVG_FEBLEND_MODE_LUMINOSITY:
    521      aWrFilters.filters.AppendElement(
    522          wr::FilterOp::SVGFEBlendLuminosity(aGraphNode));
    523      return WrFiltersStatus::SVGFE;
    524    case SVG_FEBLEND_MODE_MULTIPLY:
    525      aWrFilters.filters.AppendElement(
    526          wr::FilterOp::SVGFEBlendMultiply(aGraphNode));
    527      return WrFiltersStatus::SVGFE;
    528    case SVG_FEBLEND_MODE_NORMAL:
    529      aWrFilters.filters.AppendElement(
    530          wr::FilterOp::SVGFEBlendNormal(aGraphNode));
    531      return WrFiltersStatus::SVGFE;
    532    case SVG_FEBLEND_MODE_OVERLAY:
    533      aWrFilters.filters.AppendElement(
    534          wr::FilterOp::SVGFEBlendOverlay(aGraphNode));
    535      return WrFiltersStatus::SVGFE;
    536    case SVG_FEBLEND_MODE_SATURATION:
    537      aWrFilters.filters.AppendElement(
    538          wr::FilterOp::SVGFEBlendSaturation(aGraphNode));
    539      return WrFiltersStatus::SVGFE;
    540    case SVG_FEBLEND_MODE_SCREEN:
    541      aWrFilters.filters.AppendElement(
    542          wr::FilterOp::SVGFEBlendScreen(aGraphNode));
    543      return WrFiltersStatus::SVGFE;
    544    case SVG_FEBLEND_MODE_SOFT_LIGHT:
    545      aWrFilters.filters.AppendElement(
    546          wr::FilterOp::SVGFEBlendSoftLight(aGraphNode));
    547      return WrFiltersStatus::SVGFE;
    548    default:
    549      break;
    550  }
    551  MOZ_CRASH("Unrecognized SVG_FEBLEND_MODE");
    552  return WrFiltersStatus::BLOB_FALLBACK;
    553 }
    554 
    555 static WrFiltersStatus WrFilterOpSVGFEComposite(
    556    WrFiltersHolder& aWrFilters, const wr::FilterOpGraphNode& aGraphNode,
    557    const CompositeAttributes& aAttributes) {
    558  // SVGFEComposite - common
    559  if (!StaticPrefs::gfx_webrender_svg_filter_effects_fecomposite()) {
    560    // Fallback if pref is disabled
    561    return WrFiltersStatus::BLOB_FALLBACK;
    562  }
    563  switch (aAttributes.mOperator) {
    564    case SVG_FECOMPOSITE_OPERATOR_ARITHMETIC:
    565      aWrFilters.filters.AppendElement(wr::FilterOp::SVGFECompositeArithmetic(
    566          aGraphNode, aAttributes.mCoefficients[0],
    567          aAttributes.mCoefficients[1], aAttributes.mCoefficients[2],
    568          aAttributes.mCoefficients[3]));
    569      return WrFiltersStatus::SVGFE;
    570    case SVG_FECOMPOSITE_OPERATOR_ATOP:
    571      aWrFilters.filters.AppendElement(
    572          wr::FilterOp::SVGFECompositeATop(aGraphNode));
    573      return WrFiltersStatus::SVGFE;
    574    case SVG_FECOMPOSITE_OPERATOR_IN:
    575      aWrFilters.filters.AppendElement(
    576          wr::FilterOp::SVGFECompositeIn(aGraphNode));
    577      return WrFiltersStatus::SVGFE;
    578    case SVG_FECOMPOSITE_OPERATOR_LIGHTER:
    579      aWrFilters.filters.AppendElement(
    580          wr::FilterOp::SVGFECompositeLighter(aGraphNode));
    581      return WrFiltersStatus::SVGFE;
    582    case SVG_FECOMPOSITE_OPERATOR_OUT:
    583      aWrFilters.filters.AppendElement(
    584          wr::FilterOp::SVGFECompositeOut(aGraphNode));
    585      return WrFiltersStatus::SVGFE;
    586    case SVG_FECOMPOSITE_OPERATOR_OVER:
    587      aWrFilters.filters.AppendElement(
    588          wr::FilterOp::SVGFECompositeOver(aGraphNode));
    589      return WrFiltersStatus::SVGFE;
    590    case SVG_FECOMPOSITE_OPERATOR_XOR:
    591      aWrFilters.filters.AppendElement(
    592          wr::FilterOp::SVGFECompositeXOR(aGraphNode));
    593      return WrFiltersStatus::SVGFE;
    594    default:
    595      break;
    596  }
    597  MOZ_CRASH("Unrecognized SVG_FECOMPOSITE_OPERATOR");
    598  return WrFiltersStatus::BLOB_FALLBACK;
    599 }
    600 
    601 static WrFiltersStatus WrFilterOpSVGFEColorMatrix(
    602    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
    603    const ColorMatrixAttributes& aAttributes) {
    604  // SVGFEColorMatrix - common
    605  if (!StaticPrefs::gfx_webrender_svg_filter_effects_fecolormatrix()) {
    606    // Fallback if pref is disabled
    607    return WrFiltersStatus::BLOB_FALLBACK;
    608  }
    609  float transposed[20];
    610  if (gfx::ComputeColorMatrix(aAttributes, transposed)) {
    611    float matrix[20] = {
    612        transposed[0], transposed[5], transposed[10], transposed[15],
    613        transposed[1], transposed[6], transposed[11], transposed[16],
    614        transposed[2], transposed[7], transposed[12], transposed[17],
    615        transposed[3], transposed[8], transposed[13], transposed[18],
    616        transposed[4], transposed[9], transposed[14], transposed[19]};
    617    aWrFilters.filters.AppendElement(
    618        wr::FilterOp::SVGFEColorMatrix(aGraphNode, matrix));
    619  } else {
    620    // If it's a no-op, we still have to generate a graph node
    621    aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEIdentity(aGraphNode));
    622  }
    623  return WrFiltersStatus::SVGFE;
    624 }
    625 
    626 static WrFiltersStatus WrFilterOpSVGFEComponentTransfer(
    627    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
    628    const ComponentTransferAttributes& aAttributes) {
    629  // SVGFEComponentTransfer - common
    630  if (!StaticPrefs::gfx_webrender_svg_filter_effects_fecomponenttransfer()) {
    631    // Fallback if pref is disabled
    632    return WrFiltersStatus::BLOB_FALLBACK;
    633  }
    634  // We ensure that there are at least 256 values for each channel so that
    635  // the shader can skip interpolation math for simplicity.
    636  size_t stops = 256;
    637  for (const auto& v : aAttributes.mValues) {
    638    if (stops < v.Length()) {
    639      stops = v.Length();
    640    }
    641  }
    642  aWrFilters.values.AppendElement(nsTArray<float>());
    643  nsTArray<float>& values = aWrFilters.values.LastElement();
    644  values.SetCapacity(stops * 4);
    645 
    646  // Set the FilterData funcs for whether or not to interpolate the values
    647  // between stops, although we use enough stops that it may not matter.
    648  // The only type that doesn't use interpolation is discrete.
    649  wr::WrFilterData filterData{};
    650  filterData.funcR_type =
    651      aAttributes.mTypes[0] != SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE
    652          ? mozilla::wr::ComponentTransferFuncType::Table
    653          : mozilla::wr::ComponentTransferFuncType::Discrete;
    654  filterData.funcG_type =
    655      aAttributes.mTypes[1] != SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE
    656          ? mozilla::wr::ComponentTransferFuncType::Table
    657          : mozilla::wr::ComponentTransferFuncType::Discrete;
    658  filterData.funcB_type =
    659      aAttributes.mTypes[2] != SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE
    660          ? mozilla::wr::ComponentTransferFuncType::Table
    661          : mozilla::wr::ComponentTransferFuncType::Discrete;
    662  filterData.funcA_type =
    663      aAttributes.mTypes[3] != SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE
    664          ? mozilla::wr::ComponentTransferFuncType::Table
    665          : mozilla::wr::ComponentTransferFuncType::Discrete;
    666 
    667  // This is a bit of a strange way to store the table, it is an interleaved
    668  // array of RGBA values that we want to store in a single gpucache array
    669  // of raw pixels, so it's easiest to send it to WebRender as a single
    670  // channel, but FilterData requires it to be 4 channels, so we send it as
    671  // 4 groups of values but the data is interleaved.
    672  values.SetLength(stops * 4);
    673  filterData.R_values = &(values[0]);
    674  filterData.R_values_count = stops;
    675  filterData.G_values = &(values[stops]);
    676  filterData.G_values_count = stops;
    677  filterData.B_values = &(values[stops * 2]);
    678  filterData.B_values_count = stops;
    679  filterData.A_values = &(values[stops * 3]);
    680  filterData.A_values_count = stops;
    681 
    682  // This builds a single interleaved RGBA table as it is well suited to GPU
    683  // texture fetches without any dynamic component indexing in the shader which
    684  // can confuse buggy shader compilers.
    685  for (size_t c = 0; c < 4; c++) {
    686    auto f = aAttributes.mTypes[c];
    687    // Check if there's no data (we have crashtests for this).
    688    if (aAttributes.mValues[c].IsEmpty() &&
    689        f != SVG_FECOMPONENTTRANSFER_SAME_AS_R) {
    690      f = SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
    691    }
    692    // Check for misuse of SVG_FECOMPONENTTRANSFER_SAME_AS_R.
    693    if (c == 0 && f == SVG_FECOMPONENTTRANSFER_SAME_AS_R) {
    694      f = SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
    695    }
    696    switch (f) {
    697      case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE: {
    698        size_t length = (size_t)aAttributes.mValues[c].Length();
    699        size_t length1 = length - 1;
    700        float step = (float)length / (float)stops;
    701        for (size_t i = 0; i < stops; i++) {
    702          // find the corresponding color in the table
    703          // this can not overflow due to the length check
    704          float kf = (float)i * step;
    705          float floorkf = floor(kf);
    706          size_t k = (size_t)floorkf;
    707          k = std::min(k, length1);
    708          float v = aAttributes.mValues[c][k];
    709          v = std::clamp(v, 0.0f, 1.0f);
    710          values[i * 4 + c] = v;
    711        }
    712        break;
    713      }
    714      case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA: {
    715        float step = 1.0f / (float)(stops - 1);
    716        float amplitude = aAttributes.mValues[c][0];
    717        float exponent = aAttributes.mValues[c][1];
    718        float offset = aAttributes.mValues[c][2];
    719        for (size_t i = 0; i < stops; i++) {
    720          float v = amplitude * pow((float)i * step, exponent) + offset;
    721          v = std::clamp(v, 0.0f, 1.0f);
    722          values[i * 4 + c] = v;
    723        }
    724        break;
    725      }
    726      case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY: {
    727        float step = 1.0f / (float)(stops - 1);
    728        for (size_t i = 0; i < stops; i++) {
    729          float v = (float)i * step;
    730          v = std::clamp(v, 0.0f, 1.0f);
    731          values[i * 4 + c] = v;
    732        }
    733        break;
    734      }
    735      case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR: {
    736        float step = aAttributes.mValues[c][0] / (float)(stops - 1);
    737        float intercept = aAttributes.mValues[c][1];
    738        for (size_t i = 0; i < stops; i++) {
    739          float v = (float)i * step + intercept;
    740          v = std::clamp(v, 0.0f, 1.0f);
    741          values[i * 4 + c] = v;
    742        }
    743        break;
    744      }
    745      case SVG_FECOMPONENTTRANSFER_TYPE_TABLE: {
    746        size_t length1 = (size_t)aAttributes.mValues[c].Length() - 1;
    747        float step = (float)length1 / (float)(stops - 1);
    748        for (size_t i = 0; i < stops; i++) {
    749          // Find the corresponding color in the table and interpolate
    750          float kf = (float)i * step;
    751          float floorkf = floor(kf);
    752          size_t k = (size_t)floorkf;
    753          float v1 = aAttributes.mValues[c][k];
    754          float v2 = aAttributes.mValues[c][(k + 1 <= length1) ? k + 1 : k];
    755          float v = v1 + (v2 - v1) * (kf - floorkf);
    756          v = std::clamp(v, 0.0f, 1.0f);
    757          values[i * 4 + c] = v;
    758        }
    759        break;
    760      }
    761      case SVG_FECOMPONENTTRANSFER_SAME_AS_R: {
    762        // We already checked c > 0 above.
    763        for (size_t i = 0; i < stops; i++) {
    764          values[i * 4 + c] = values[i * 4];
    765        }
    766        break;
    767      }
    768      default: {
    769        MOZ_CRASH("Unrecognized feComponentTransfer type");
    770        return WrFiltersStatus::BLOB_FALLBACK;
    771      }
    772    }
    773  }
    774  aWrFilters.filters.AppendElement(
    775      wr::FilterOp::SVGFEComponentTransfer(aGraphNode));
    776  aWrFilters.filter_datas.AppendElement(filterData);
    777  return WrFiltersStatus::SVGFE;
    778 }
    779 
    780 static WrFiltersStatus WrFilterOpSVGFEConvolveMatrix(
    781    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
    782    const ConvolveMatrixAttributes& aAttributes) {
    783  // SVGFEConvolveMatrix - extremely rare
    784  if (!StaticPrefs::gfx_webrender_svg_filter_effects_feconvolvematrix()) {
    785    // Fallback if pref is disabled
    786    return WrFiltersStatus::BLOB_FALLBACK;
    787  }
    788  // Limited kernel size for performance reasons - spec allows us to drop
    789  // the whole filter graph if anything is unreasonable, so we only support
    790  // up to 5x5 kernel as that is pretty fast in hardware
    791  static constexpr int32_t width = 5;
    792  static constexpr int32_t height = 5;
    793  if (aAttributes.mKernelSize.Width() < 1 ||
    794      aAttributes.mKernelSize.Width() > width ||
    795      aAttributes.mKernelSize.Height() < 1 ||
    796      aAttributes.mKernelSize.Height() > height ||
    797      (size_t)aAttributes.mKernelSize.Width() *
    798              (size_t)aAttributes.mKernelSize.Height() >
    799          width * height) {
    800    return WrFiltersStatus::BLOB_FALLBACK;
    801  }
    802  // Reject kernel matrix if it is fewer values than dimensions suggest
    803  if (aAttributes.mKernelMatrix.Length() <
    804      (size_t)aAttributes.mKernelSize.Width() *
    805          (size_t)aAttributes.mKernelSize.Height()) {
    806    return WrFiltersStatus::UNSUPPORTED;
    807  }
    808  // Arrange the values in the order the shader expects
    809  float matrix[width * height];
    810  for (size_t y = 0; y < height; y++) {
    811    for (size_t x = 0; x < width; x++) {
    812      if (x < (size_t)aAttributes.mKernelSize.Width() &&
    813          y < (size_t)aAttributes.mKernelSize.Height()) {
    814        matrix[y * width + x] =
    815            aAttributes.mKernelMatrix[y * aAttributes.mKernelSize.Width() + x];
    816      } else {
    817        matrix[y * width + x] = 0.0f;
    818      }
    819    }
    820  }
    821  switch (aAttributes.mEdgeMode) {
    822    case SVG_EDGEMODE_UNKNOWN:
    823    case SVG_EDGEMODE_DUPLICATE:
    824      aWrFilters.filters.AppendElement(
    825          wr::FilterOp::SVGFEConvolveMatrixEdgeModeDuplicate(
    826              aGraphNode, aAttributes.mKernelSize.Width(),
    827              aAttributes.mKernelSize.Height(), matrix, aAttributes.mDivisor,
    828              aAttributes.mBias, aAttributes.mTarget.x.value,
    829              aAttributes.mTarget.y.value,
    830              aAttributes.mKernelUnitLength.Width(),
    831              aAttributes.mKernelUnitLength.Height(),
    832              aAttributes.mPreserveAlpha));
    833      return WrFiltersStatus::SVGFE;
    834    case SVG_EDGEMODE_NONE:
    835      aWrFilters.filters.AppendElement(
    836          wr::FilterOp::SVGFEConvolveMatrixEdgeModeNone(
    837              aGraphNode, aAttributes.mKernelSize.Width(),
    838              aAttributes.mKernelSize.Height(), matrix, aAttributes.mDivisor,
    839              aAttributes.mBias, aAttributes.mTarget.x.value,
    840              aAttributes.mTarget.y.value,
    841              aAttributes.mKernelUnitLength.Width(),
    842              aAttributes.mKernelUnitLength.Height(),
    843              aAttributes.mPreserveAlpha));
    844      return WrFiltersStatus::SVGFE;
    845    case SVG_EDGEMODE_WRAP:
    846      aWrFilters.filters.AppendElement(
    847          wr::FilterOp::SVGFEConvolveMatrixEdgeModeWrap(
    848              aGraphNode, aAttributes.mKernelSize.Width(),
    849              aAttributes.mKernelSize.Height(), matrix, aAttributes.mDivisor,
    850              aAttributes.mBias, aAttributes.mTarget.x.value,
    851              aAttributes.mTarget.y.value,
    852              aAttributes.mKernelUnitLength.Width(),
    853              aAttributes.mKernelUnitLength.Height(),
    854              aAttributes.mPreserveAlpha));
    855      return WrFiltersStatus::SVGFE;
    856    default:
    857      break;
    858  }
    859  MOZ_CRASH("Unrecognized SVG_EDGEMODE");
    860  return WrFiltersStatus::BLOB_FALLBACK;
    861 }
    862 
    863 static WrFiltersStatus WrFilterOpSVGFEDiffuseLighting(
    864    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
    865    const DiffuseLightingAttributes& aAttributes,
    866    const LayoutDevicePoint& aUserspaceOffset) {
    867  // SVGFEDiffuseLighting - extremely rare
    868  if (!StaticPrefs::gfx_webrender_svg_filter_effects_fediffuselighting()) {
    869    // Fallback if pref is disabled
    870    return WrFiltersStatus::BLOB_FALLBACK;
    871  }
    872  switch (aAttributes.mLightType) {
    873    case LightType::Distant:
    874      aWrFilters.filters.AppendElement(
    875          wr::FilterOp::SVGFEDiffuseLightingDistant(
    876              aGraphNode, aAttributes.mSurfaceScale,
    877              aAttributes.mLightingConstant,
    878              aAttributes.mKernelUnitLength.width,
    879              aAttributes.mKernelUnitLength.height, aAttributes.mLightValues[0],
    880              aAttributes.mLightValues[1]));
    881      return WrFiltersStatus::SVGFE;
    882    case LightType::Point:
    883      aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEDiffuseLightingPoint(
    884          aGraphNode, aAttributes.mSurfaceScale, aAttributes.mLightingConstant,
    885          aAttributes.mKernelUnitLength.width,
    886          aAttributes.mKernelUnitLength.height,
    887          aAttributes.mLightValues[0] + aUserspaceOffset.x.value,
    888          aAttributes.mLightValues[1] + aUserspaceOffset.y.value,
    889          aAttributes.mLightValues[2]));
    890      return WrFiltersStatus::SVGFE;
    891    case LightType::Spot:
    892      aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEDiffuseLightingSpot(
    893          aGraphNode, aAttributes.mSurfaceScale, aAttributes.mLightingConstant,
    894          aAttributes.mKernelUnitLength.width,
    895          aAttributes.mKernelUnitLength.height,
    896          aAttributes.mLightValues[0] + aUserspaceOffset.x.value,
    897          aAttributes.mLightValues[1] + aUserspaceOffset.y.value,
    898          aAttributes.mLightValues[2],
    899          aAttributes.mLightValues[3] + aUserspaceOffset.x.value,
    900          aAttributes.mLightValues[4] + aUserspaceOffset.y.value,
    901          aAttributes.mLightValues[5], aAttributes.mLightValues[6],
    902          aAttributes.mLightValues[7]));
    903      return WrFiltersStatus::SVGFE;
    904    case LightType::None:
    905    case LightType::Max:
    906      // No default case, so that the compiler will warn if new enums are added
    907      break;
    908  }
    909  MOZ_CRASH("Unrecognized LightType");
    910  return WrFiltersStatus::BLOB_FALLBACK;
    911 }
    912 
    913 static WrFiltersStatus WrFilterOpSVGFEDisplacementMap(
    914    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
    915    const DisplacementMapAttributes& aAttributes) {
    916  // SVGFEDisplacementMap - extremely rare
    917  if (!StaticPrefs::gfx_webrender_svg_filter_effects_fedisplacementmap()) {
    918    // Fallback if pref is disabled
    919    return WrFiltersStatus::BLOB_FALLBACK;
    920  }
    921  aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEDisplacementMap(
    922      aGraphNode, aAttributes.mScale, aAttributes.mXChannel,
    923      aAttributes.mYChannel));
    924  return WrFiltersStatus::SVGFE;
    925 }
    926 
    927 static WrFiltersStatus WrFilterOpSVGFEDropShadow(
    928    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
    929    const DropShadowAttributes& aAttributes) {
    930  // SVGFEDropShadow - extremely rare
    931  if (!StaticPrefs::gfx_webrender_svg_filter_effects_fedropshadow()) {
    932    // Fallback if pref is disabled
    933    return WrFiltersStatus::BLOB_FALLBACK;
    934  }
    935  // This color is used in a shader coefficient that is in sRGB color space,
    936  // so it needs to go through the regular device color transformation.
    937  // This does not premultiply the alpha - the shader will do that.
    938  aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEDropShadow(
    939      aGraphNode, wr::ToColorF(ToDeviceColor(aAttributes.mColor)),
    940      aAttributes.mOffset.x, aAttributes.mOffset.y,
    941      aAttributes.mStdDeviation.width, aAttributes.mStdDeviation.height));
    942  return WrFiltersStatus::SVGFE;
    943 }
    944 
    945 static WrFiltersStatus WrFilterOpSVGFEFlood(
    946    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
    947    const FloodAttributes& aAttributes) {
    948  // SVGFEFlood - common
    949  if (!StaticPrefs::gfx_webrender_svg_filter_effects_feflood()) {
    950    // Fallback if pref is disabled
    951    return WrFiltersStatus::BLOB_FALLBACK;
    952  }
    953  // This color is used in a shader coefficient that is in sRGB color space,
    954  // so it needs to go through the regular device color transformation.
    955  // This does not premultiply the alpha - the shader will do that.
    956  aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEFlood(
    957      aGraphNode, wr::ToColorF(ToDeviceColor(aAttributes.mColor))));
    958  return WrFiltersStatus::SVGFE;
    959 }
    960 
    961 static WrFiltersStatus WrFilterOpSVGFEGaussianBlur(
    962    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
    963    const GaussianBlurAttributes& aAttributes) {
    964  // SVGFEGaussianBlur - common
    965  if (!StaticPrefs::gfx_webrender_svg_filter_effects_fegaussianblur()) {
    966    // Fallback if pref is disabled
    967    return WrFiltersStatus::BLOB_FALLBACK;
    968  }
    969  aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEGaussianBlur(
    970      aGraphNode, aAttributes.mStdDeviation.width,
    971      aAttributes.mStdDeviation.height));
    972  return WrFiltersStatus::SVGFE;
    973 }
    974 
    975 static WrFiltersStatus WrFilterOpSVGFEImage(
    976    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
    977    const ImageAttributes& aAttributes,
    978    const LayoutDevicePoint& aUserspaceOffset) {
    979  // SVGFEImage - Extremely rare
    980  if (!StaticPrefs::gfx_webrender_svg_filter_effects_feimage()) {
    981    // Fallback if pref is disabled
    982    return WrFiltersStatus::BLOB_FALLBACK;
    983  }
    984  float matrix[6];
    985  matrix[0] = aAttributes.mTransform.components[0];
    986  matrix[1] = aAttributes.mTransform.components[1];
    987  matrix[2] = aAttributes.mTransform.components[2];
    988  matrix[3] = aAttributes.mTransform.components[3];
    989  matrix[4] = aAttributes.mTransform.components[4] + aUserspaceOffset.x.value;
    990  matrix[5] = aAttributes.mTransform.components[5] + aUserspaceOffset.y.value;
    991  // TODO: We need to resolve aAttributes.mInputIndex to an actual image
    992  // somehow.
    993  aWrFilters.filters.AppendElement(
    994      wr::FilterOp::SVGFEImage(aGraphNode, aAttributes.mFilter, matrix));
    995  return WrFiltersStatus::SVGFE;
    996 }
    997 
    998 static WrFiltersStatus WrFilterOpSVGFEMerge(
    999    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
   1000    const MergeAttributes& aAttributes, FilterPrimitiveDescription& aPrimitive,
   1001    int16_t aNodeOutput, int16_t aSourceGraphic, int16_t aSourceAlpha,
   1002    const int16_t aBufferIdMapping[], size_t aMaxFilters) {
   1003  // SVGFEMerge - common
   1004  if (!StaticPrefs::gfx_webrender_svg_filter_effects_femerge()) {
   1005    // Fallback if pref is disabled
   1006    return WrFiltersStatus::BLOB_FALLBACK;
   1007  }
   1008  // There is no SVGFEMerge, so we need to expand the provided inputs to a
   1009  // chain of SVGFECompositeOver ops before handing it to WebRender.
   1010  if (aPrimitive.NumberOfInputs() >= 2) {
   1011    wr::FilterOpGraphPictureReference previous{};
   1012    for (size_t index = 0; index < aPrimitive.NumberOfInputs(); index++) {
   1013      wr::FilterOpGraphPictureReference current{};
   1014      WrFiltersStatus status = WrSVGFEInputBuild(
   1015          current, aPrimitive.InputPrimitiveIndex(index), aNodeOutput,
   1016          aSourceGraphic, aSourceAlpha, aBufferIdMapping);
   1017      if (status != WrFiltersStatus::SVGFE) {
   1018        // If the input is an invalid ref, we have to disable filters on this.
   1019        return status;
   1020      }
   1021      aGraphNode.input = current;
   1022      aGraphNode.input2 = previous;
   1023      if (aWrFilters.filters.Length() >= aMaxFilters) {
   1024        // Reject the graph if it has too many filters to even process
   1025        return WrFiltersStatus::DISABLED_FOR_PERFORMANCE;
   1026      }
   1027      if (index >= 1) {
   1028        // Emit a node that composites this pic over the previous pics.
   1029        aWrFilters.filters.AppendElement(
   1030            wr::FilterOp::SVGFECompositeOver(aGraphNode));
   1031        // Use this graph node as input2 (background) on the next node.
   1032        previous.buffer_id = wr::FilterOpGraphPictureBufferId::BufferId(
   1033            (int16_t)(aWrFilters.filters.Length() - 1));
   1034      } else {
   1035        // Conceptually the first pic is composited over transparent black
   1036        // which is a no-op, so we just use the first pic as a direct input
   1037        // on the first node we actually emit.
   1038        previous.buffer_id = current.buffer_id;
   1039      }
   1040    }
   1041  } else if (aPrimitive.NumberOfInputs() == 1) {
   1042    // If we only got a single feMergeNode pic, we still want to apply
   1043    // the subregion clip, so make an SVGFEIdentity op.
   1044    aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEIdentity(aGraphNode));
   1045  } else {
   1046    // feMerge with no feMergeNodes is just blank.
   1047    wr::ColorF blank = {0.0f, 0.0f, 0.0f, 0.0f};
   1048    aWrFilters.filters.AppendElement(
   1049        wr::FilterOp::SVGFEFlood(aGraphNode, blank));
   1050  }
   1051  return WrFiltersStatus::SVGFE;
   1052 }
   1053 
   1054 static WrFiltersStatus WrFilterOpSVGFEMorphology(
   1055    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
   1056    const MorphologyAttributes& aAttributes) {
   1057  // SVGFEMorphology - Rare
   1058  if (!StaticPrefs::gfx_webrender_svg_filter_effects_femorphology()) {
   1059    // Fallback if pref is disabled
   1060    return WrFiltersStatus::BLOB_FALLBACK;
   1061  }
   1062  switch (aAttributes.mOperator) {
   1063    case SVG_OPERATOR_DILATE:
   1064      aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEMorphologyDilate(
   1065          aGraphNode, aAttributes.mRadii.width, aAttributes.mRadii.height));
   1066      return WrFiltersStatus::SVGFE;
   1067    case SVG_OPERATOR_ERODE:
   1068      aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEMorphologyErode(
   1069          aGraphNode, aAttributes.mRadii.width, aAttributes.mRadii.height));
   1070      return WrFiltersStatus::SVGFE;
   1071    default:
   1072      break;
   1073  }
   1074  MOZ_CRASH("Unrecognized SVG_OPERATOR");
   1075  return WrFiltersStatus::BLOB_FALLBACK;
   1076 }
   1077 
   1078 static WrFiltersStatus WrFilterOpSVGFEOffset(
   1079    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
   1080    const OffsetAttributes& aAttributes) {
   1081  // SVGFEOffset - Common
   1082  if (!StaticPrefs::gfx_webrender_svg_filter_effects_feoffset()) {
   1083    // Fallback if pref is disabled
   1084    return WrFiltersStatus::BLOB_FALLBACK;
   1085  }
   1086  aWrFilters.filters.AppendElement(wr::FilterOp::SVGFEOffset(
   1087      aGraphNode, (float)aAttributes.mValue.x, (float)aAttributes.mValue.y));
   1088  return WrFiltersStatus::SVGFE;
   1089 }
   1090 
   1091 static WrFiltersStatus WrFilterOpSVGFETile(WrFiltersHolder& aWrFilters,
   1092                                           wr::FilterOpGraphNode& aGraphNode,
   1093                                           const TileAttributes& aAttributes) {
   1094  // SVGFETile - Extremely rare
   1095  if (!StaticPrefs::gfx_webrender_svg_filter_effects_fetile()) {
   1096    // Fallback if pref is disabled
   1097    return WrFiltersStatus::BLOB_FALLBACK;
   1098  }
   1099  aWrFilters.filters.AppendElement(wr::FilterOp::SVGFETile(aGraphNode));
   1100  return WrFiltersStatus::SVGFE;
   1101 }
   1102 
   1103 static WrFiltersStatus WrFilterOpSVGFESpecularLighting(
   1104    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
   1105    const SpecularLightingAttributes& aAttributes,
   1106    const LayoutDevicePoint& aUserspaceOffset) {
   1107  // SVGFESpecularLighting - extremely rare
   1108  if (!StaticPrefs::gfx_webrender_svg_filter_effects_fespecularlighting()) {
   1109    // Fallback if pref is disabled
   1110    return WrFiltersStatus::BLOB_FALLBACK;
   1111  }
   1112  switch (aAttributes.mLightType) {
   1113    case LightType::Distant:
   1114      aWrFilters.filters.AppendElement(
   1115          wr::FilterOp::SVGFESpecularLightingDistant(
   1116              aGraphNode, aAttributes.mSurfaceScale,
   1117              aAttributes.mLightingConstant, aAttributes.mSpecularExponent,
   1118              aAttributes.mKernelUnitLength.width,
   1119              aAttributes.mKernelUnitLength.height, aAttributes.mLightValues[0],
   1120              aAttributes.mLightValues[1]));
   1121      return WrFiltersStatus::SVGFE;
   1122    case LightType::Point:
   1123      aWrFilters.filters.AppendElement(wr::FilterOp::SVGFESpecularLightingPoint(
   1124          aGraphNode, aAttributes.mSurfaceScale, aAttributes.mLightingConstant,
   1125          aAttributes.mSpecularExponent, aAttributes.mKernelUnitLength.width,
   1126          aAttributes.mKernelUnitLength.height,
   1127          aAttributes.mLightValues[0] + aUserspaceOffset.x.value,
   1128          aAttributes.mLightValues[1] + aUserspaceOffset.y.value,
   1129          aAttributes.mLightValues[2]));
   1130      return WrFiltersStatus::SVGFE;
   1131    case LightType::Spot:
   1132      aWrFilters.filters.AppendElement(wr::FilterOp::SVGFESpecularLightingSpot(
   1133          aGraphNode, aAttributes.mSurfaceScale, aAttributes.mLightingConstant,
   1134          aAttributes.mSpecularExponent, aAttributes.mKernelUnitLength.width,
   1135          aAttributes.mKernelUnitLength.height,
   1136          aAttributes.mLightValues[0] + aUserspaceOffset.x.value,
   1137          aAttributes.mLightValues[1] + aUserspaceOffset.y.value,
   1138          aAttributes.mLightValues[2],
   1139          aAttributes.mLightValues[3] + aUserspaceOffset.x.value,
   1140          aAttributes.mLightValues[4] + aUserspaceOffset.y.value,
   1141          aAttributes.mLightValues[5], aAttributes.mLightValues[6],
   1142          aAttributes.mLightValues[7]));
   1143      return WrFiltersStatus::SVGFE;
   1144    case LightType::None:
   1145    case LightType::Max:
   1146      // No default case, so that the compiler will warn if new enums are added
   1147      break;
   1148  }
   1149  MOZ_CRASH("Unrecognized LightType");
   1150  return WrFiltersStatus::BLOB_FALLBACK;
   1151 }
   1152 
   1153 static WrFiltersStatus WrFilterOpSVGFETurbulence(
   1154    WrFiltersHolder& aWrFilters, wr::FilterOpGraphNode& aGraphNode,
   1155    const TurbulenceAttributes& aAttributes,
   1156    const LayoutDevicePoint& aUserspaceOffset) {
   1157  // SVGFETurbulence - Rare
   1158  if (!StaticPrefs::gfx_webrender_svg_filter_effects_feturbulence()) {
   1159    // Fallback if pref is disabled
   1160    return WrFiltersStatus::BLOB_FALLBACK;
   1161  }
   1162  // The software implementation we use converts float to uint32_t and then
   1163  // to int32_t, so we do that here to get identical results to the prior
   1164  // implementation, in contrast to the spec which uses purely signed math
   1165  // for setting up the seed.
   1166  int32_t m1 = 2147483647 - 1;
   1167  int32_t seed = (int32_t)((uint32_t)aAttributes.mSeed);
   1168  if (seed <= 0) {
   1169    seed = -(seed % m1) + 1;
   1170  }
   1171  if (seed > m1) {
   1172    seed = m1;
   1173  }
   1174  switch (aAttributes.mType) {
   1175    case SVG_TURBULENCE_TYPE_FRACTALNOISE:
   1176      if (aAttributes.mStitchable) {
   1177        aWrFilters.filters.AppendElement(
   1178            wr::FilterOp::SVGFETurbulenceWithFractalNoiseWithStitching(
   1179                aGraphNode, aAttributes.mBaseFrequency.width,
   1180                aAttributes.mBaseFrequency.height, aAttributes.mOctaves, seed));
   1181      } else {
   1182        aWrFilters.filters.AppendElement(
   1183            wr::FilterOp::SVGFETurbulenceWithFractalNoiseWithNoStitching(
   1184                aGraphNode, aAttributes.mBaseFrequency.width,
   1185                aAttributes.mBaseFrequency.height, aAttributes.mOctaves, seed));
   1186      }
   1187      return WrFiltersStatus::SVGFE;
   1188    case SVG_TURBULENCE_TYPE_TURBULENCE:
   1189      if (aAttributes.mStitchable) {
   1190        aWrFilters.filters.AppendElement(
   1191            wr::FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching(
   1192                aGraphNode, aAttributes.mBaseFrequency.width,
   1193                aAttributes.mBaseFrequency.height, aAttributes.mOctaves, seed));
   1194      } else {
   1195        aWrFilters.filters.AppendElement(
   1196            wr::FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching(
   1197                aGraphNode, aAttributes.mBaseFrequency.width,
   1198                aAttributes.mBaseFrequency.height, aAttributes.mOctaves, seed));
   1199      }
   1200      return WrFiltersStatus::SVGFE;
   1201    default:
   1202      break;
   1203  }
   1204  MOZ_CRASH("Unrecognized SVG_TURBULENCE_TYPE");
   1205  return WrFiltersStatus::BLOB_FALLBACK;
   1206 }
   1207 
   1208 /// Builds filter DAG for fully accelerated rendering of SVG filter primitives
   1209 /// and CSS filter chains using SVG filter primitives
   1210 WrFiltersStatus FilterInstance::BuildWebRenderSVGFiltersImpl(
   1211    nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilters,
   1212    StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters,
   1213    const nsPoint& aOffsetForSVGFilters) {
   1214  // If we return without making a valid filter graph, we need to restore
   1215  // aInitialized before the fallback code is run.
   1216  aWrFilters.filters.Clear();
   1217  aWrFilters.filter_datas.Clear();
   1218  aWrFilters.values.Clear();
   1219  aWrFilters.post_filters_clip = Nothing();
   1220 
   1221  nsIFrame* firstFrame =
   1222      nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFilteredFrame);
   1223 
   1224  nsTArray<SVGFilterFrame*> filterFrames;
   1225  if (SVGObserverUtils::GetAndObserveFilters(firstFrame, &filterFrames,
   1226                                             aStyleFilterType) ==
   1227      SVGObserverUtils::eHasRefsSomeInvalid) {
   1228    return WrFiltersStatus::UNSUPPORTED;
   1229  }
   1230 
   1231  UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(firstFrame);
   1232 
   1233  gfxRect filterSpaceBoundsNotSnapped;
   1234 
   1235  // TODO: simply using an identity matrix here, was pulling the scale from a
   1236  // gfx context for the non-wr path.
   1237  gfxMatrix scaleMatrix;
   1238  gfxMatrix scaleMatrixInDevUnits =
   1239      scaleMatrix * SVGUtils::GetCSSPxToDevPxMatrix(firstFrame);
   1240 
   1241  // Hardcode inputIsTainted to true because we don't want JS to be able to
   1242  // read the rendered contents of aFilteredFrame.
   1243  FilterInstance instance(firstFrame, firstFrame->GetContent(), *metrics,
   1244                          aFilters, filterFrames, /* inputIsTainted */ true,
   1245                          nullptr, scaleMatrixInDevUnits, nullptr, nullptr,
   1246                          nullptr, nullptr, &filterSpaceBoundsNotSnapped);
   1247 
   1248  if (!instance.IsInitialized()) {
   1249    return WrFiltersStatus::UNSUPPORTED;
   1250  }
   1251 
   1252  // If there more filters than the limit pref allows, we can drop the entire
   1253  // filter graph and pretend we succeeded, the SVG spec allows us to drop any
   1254  // overly complex graph, very large graphs tend to OOM anyway.
   1255  if (instance.mFilterDescription.mPrimitives.Length() >
   1256      StaticPrefs::gfx_webrender_max_filter_ops_per_chain()) {
   1257    return WrFiltersStatus::DISABLED_FOR_PERFORMANCE;
   1258  }
   1259 
   1260  // We have to remap the input nodes to a possibly larger number of output
   1261  // nodes due to expanding feMerge.
   1262  static constexpr size_t maxFilters = wr::SVGFE_GRAPH_MAX;
   1263  int16_t bufferIdMapping[maxFilters];
   1264  // Just drop the graph if there are too many filters to process.
   1265  if (instance.mFilterDescription.mPrimitives.Length() > maxFilters) {
   1266    return WrFiltersStatus::DISABLED_FOR_PERFORMANCE;
   1267  }
   1268 
   1269  // For subregions and filter parameters, we need to transform into the SVG
   1270  // User Space coordinate system, which is the parent stacking context
   1271  // coordinate system, not to be confused with Filter Space (which is this
   1272  // stacking context's child coordinate system) or Frame Space.
   1273  //
   1274  // See nsLayoutUtils::ComputeOffsetToUserSpace for further explanation, and
   1275  // SVGIntegrationUtils.cpp EffectOffsets::ComputeEffectOffset is how this is
   1276  // done in the blob fallback.
   1277  //
   1278  // The display list we are building already puts the child elements' geometry
   1279  // (if any) in SVG User Space, so we want the filter region and primitive
   1280  // subregions to be in SVG User Space, so uerspaceOffset represents the offset
   1281  // from Filter to User Space, which is in LayoutDevice units.
   1282  //
   1283  // As a practical matter, things like regular view zoom change Filter Space
   1284  // scale so we don't have to do anything for that, pinch zoom in apz can be
   1285  // doing its own thing but doesn't affect the coordinate system we use here,
   1286  // as everything is multiplied by subregion and divided by filterRegion, so
   1287  // they only need to be matching scale from WebRender perspective.
   1288  LayoutDevicePoint userspaceOffset = LayoutDevicePoint::FromAppUnits(
   1289      aOffsetForSVGFilters,
   1290      aFilteredFrame->PresContext()->AppUnitsPerDevPixel());
   1291 
   1292  // The bounds of SourceGraphic are defined in the spec as being equal to the
   1293  // filter region, so we need to compute that, and while subregion bounds are
   1294  // always integer, the bounds of the filter element (and hence filter region)
   1295  // are not actually integer, so we need to account for the non-integer filter
   1296  // region clip by using filterSpaceBoundsNotSnapped, this matters in:
   1297  // ./mach reftest layout/reftests/svg/filter-scaled-01.svg
   1298  wr::LayoutRect filterRegion = {
   1299      {(float)(filterSpaceBoundsNotSnapped.TopLeft().x +
   1300               userspaceOffset.x.value),
   1301       (float)(filterSpaceBoundsNotSnapped.TopLeft().y +
   1302               userspaceOffset.y.value)},
   1303      {(float)(filterSpaceBoundsNotSnapped.BottomRight().x +
   1304               userspaceOffset.x.value),
   1305       (float)(filterSpaceBoundsNotSnapped.BottomRight().y +
   1306               userspaceOffset.y.value)}};
   1307 
   1308  // To enforce the filterRegion clipping SourceGraphic before it enters the
   1309  // graph we have to create a SourceGraphic node and SourceAlpha node, when we
   1310  // implement StrokePaint and FillPaint they will need to create nodes on
   1311  // demand however as they have custom colors (feFlood) and patterns (feTile).
   1312  auto sourceGraphicNode = (int16_t)aWrFilters.filters.Length();
   1313  auto sourceNode = wr::FilterOpGraphNode{};
   1314  sourceNode.subregion = filterRegion;
   1315  aWrFilters.filters.AppendElement(
   1316      wr::FilterOp::SVGFESourceGraphic(sourceNode));
   1317  auto sourceAlphaNode = (int16_t)aWrFilters.filters.Length();
   1318  aWrFilters.filters.AppendElement(wr::FilterOp::SVGFESourceAlpha(sourceNode));
   1319 
   1320  // We have some failure modes that can occur when processing the graph.
   1321  WrFiltersStatus status = WrFiltersStatus::SVGFE;
   1322 
   1323  for (uint32_t i = 0; i < instance.mFilterDescription.mPrimitives.Length();
   1324       i++) {
   1325    const auto& primitive = instance.mFilterDescription.mPrimitives[i];
   1326    const PrimitiveAttributes& attr = primitive.Attributes();
   1327    const bool linear = primitive.OutputColorSpace() == ColorSpace::LinearRGB;
   1328    const size_t inputs = primitive.NumberOfInputs();
   1329    wr::FilterOpGraphNode graphNode = wr::FilterOpGraphNode{};
   1330    // Physical (linear) colorspace is the default in SVG filters, whereas all
   1331    // CSS filters use sRGB (curved / naive) colorspace calculations for math,
   1332    // this is the color-interpolation-filter property in SVG spec.  Note that
   1333    // feFlood cares about the color-interpolation property on the color value
   1334    // provided, rather than the regular color-interpolation-filter property.
   1335    graphNode.linear = linear;
   1336    // Transform the subregion into SVG 'user space' which WebRender expects.
   1337    graphNode.subregion =
   1338        wr::ToLayoutRect(Rect(primitive.PrimitiveSubregion()) +
   1339                         userspaceOffset.ToUnknownPoint());
   1340    // We need to clip the final output node by the filterRegion, as it could
   1341    // be non-integer (whereas the subregions were computed by SVGFilterInstance
   1342    // code as integer only).
   1343    if (i == instance.mFilterDescription.mPrimitives.Length() - 1) {
   1344      if (graphNode.subregion.min.x < filterRegion.min.x) {
   1345        graphNode.subregion.min.x = filterRegion.min.x;
   1346      }
   1347      if (graphNode.subregion.min.y < filterRegion.min.y) {
   1348        graphNode.subregion.min.y = filterRegion.min.y;
   1349      }
   1350      if (graphNode.subregion.max.x > filterRegion.max.x) {
   1351        graphNode.subregion.max.x = filterRegion.max.x;
   1352      }
   1353      if (graphNode.subregion.max.y > filterRegion.max.y) {
   1354        graphNode.subregion.max.y = filterRegion.max.y;
   1355      }
   1356    }
   1357 
   1358    // Buffer ids are matched up later by WebRender to understand the DAG, we
   1359    // hold the following assumptions (and verify them regularly):
   1360    // * Inputs referencing buffer ids are always < node index
   1361    //   (This means the DAG can be walked sequentially as a flat array and
   1362    //    always evaluate correctly)
   1363    // * node index < maxFilters
   1364    graphNode.input.buffer_id = wr::FilterOpGraphPictureBufferId::None();
   1365    graphNode.input2.buffer_id = wr::FilterOpGraphPictureBufferId::None();
   1366    if (inputs >= 1) {
   1367      status = WrSVGFEInputBuild(
   1368          graphNode.input, primitive.InputPrimitiveIndex(0), (int16_t)i,
   1369          sourceGraphicNode, sourceAlphaNode, bufferIdMapping);
   1370      if (status != WrFiltersStatus::SVGFE) {
   1371        break;
   1372      }
   1373      if (inputs >= 2) {
   1374        status = WrSVGFEInputBuild(
   1375            graphNode.input2, primitive.InputPrimitiveIndex(1), (int16_t)i,
   1376            sourceGraphicNode, sourceAlphaNode, bufferIdMapping);
   1377        if (status != WrFiltersStatus::SVGFE) {
   1378          break;
   1379        }
   1380      }
   1381    }
   1382 
   1383    // If there are too many filters (after feMerge expansion) to keep track of
   1384    // in bufferIdMapping[] then we can just drop the entire graph, the SVG spec
   1385    // allows us to drop overly complex graphs and maxFilters is not a small
   1386    // quantity.
   1387    if (aWrFilters.filters.Length() >= maxFilters) {
   1388      status = WrFiltersStatus::DISABLED_FOR_PERFORMANCE;
   1389      break;
   1390    }
   1391 
   1392    if (attr.is<OpacityAttributes>()) {
   1393      status = WrFilterOpSVGFEOpacity(aWrFilters, graphNode,
   1394                                      attr.as<OpacityAttributes>());
   1395    } else if (attr.is<ToAlphaAttributes>()) {
   1396      status = WrFilterOpSVGFEToAlpha(aWrFilters, graphNode,
   1397                                      attr.as<ToAlphaAttributes>());
   1398    } else if (attr.is<BlendAttributes>()) {
   1399      status = WrFilterOpSVGFEBlend(aWrFilters, graphNode,
   1400                                    attr.as<BlendAttributes>());
   1401    } else if (attr.is<ColorMatrixAttributes>()) {
   1402      status = WrFilterOpSVGFEColorMatrix(aWrFilters, graphNode,
   1403                                          attr.as<ColorMatrixAttributes>());
   1404    } else if (attr.is<ComponentTransferAttributes>()) {
   1405      status = WrFilterOpSVGFEComponentTransfer(
   1406          aWrFilters, graphNode, attr.as<ComponentTransferAttributes>());
   1407    } else if (attr.is<CompositeAttributes>()) {
   1408      status = WrFilterOpSVGFEComposite(aWrFilters, graphNode,
   1409                                        attr.as<CompositeAttributes>());
   1410    } else if (attr.is<ConvolveMatrixAttributes>()) {
   1411      status = WrFilterOpSVGFEConvolveMatrix(
   1412          aWrFilters, graphNode, attr.as<ConvolveMatrixAttributes>());
   1413    } else if (attr.is<DiffuseLightingAttributes>()) {
   1414      status = WrFilterOpSVGFEDiffuseLighting(
   1415          aWrFilters, graphNode, attr.as<DiffuseLightingAttributes>(),
   1416          userspaceOffset);
   1417    } else if (attr.is<DisplacementMapAttributes>()) {
   1418      status = WrFilterOpSVGFEDisplacementMap(
   1419          aWrFilters, graphNode, attr.as<DisplacementMapAttributes>());
   1420    } else if (attr.is<DropShadowAttributes>()) {
   1421      status = WrFilterOpSVGFEDropShadow(aWrFilters, graphNode,
   1422                                         attr.as<DropShadowAttributes>());
   1423    } else if (attr.is<FloodAttributes>()) {
   1424      status = WrFilterOpSVGFEFlood(aWrFilters, graphNode,
   1425                                    attr.as<FloodAttributes>());
   1426    } else if (attr.is<GaussianBlurAttributes>()) {
   1427      status = WrFilterOpSVGFEGaussianBlur(aWrFilters, graphNode,
   1428                                           attr.as<GaussianBlurAttributes>());
   1429    } else if (attr.is<ImageAttributes>()) {
   1430      status = WrFilterOpSVGFEImage(
   1431          aWrFilters, graphNode, attr.as<ImageAttributes>(), userspaceOffset);
   1432    } else if (attr.is<MergeAttributes>()) {
   1433      status = WrFilterOpSVGFEMerge(
   1434          aWrFilters, graphNode, attr.as<MergeAttributes>(),
   1435          instance.mFilterDescription.mPrimitives[i], (int16_t)i,
   1436          sourceGraphicNode, sourceAlphaNode, bufferIdMapping, maxFilters);
   1437    } else if (attr.is<MorphologyAttributes>()) {
   1438      status = WrFilterOpSVGFEMorphology(aWrFilters, graphNode,
   1439                                         attr.as<MorphologyAttributes>());
   1440    } else if (attr.is<OffsetAttributes>()) {
   1441      status = WrFilterOpSVGFEOffset(aWrFilters, graphNode,
   1442                                     attr.as<OffsetAttributes>());
   1443    } else if (attr.is<SpecularLightingAttributes>()) {
   1444      status = WrFilterOpSVGFESpecularLighting(
   1445          aWrFilters, graphNode, attr.as<SpecularLightingAttributes>(),
   1446          userspaceOffset);
   1447    } else if (attr.is<TileAttributes>()) {
   1448      status =
   1449          WrFilterOpSVGFETile(aWrFilters, graphNode, attr.as<TileAttributes>());
   1450    } else if (attr.is<TurbulenceAttributes>()) {
   1451      status = WrFilterOpSVGFETurbulence(aWrFilters, graphNode,
   1452                                         attr.as<TurbulenceAttributes>(),
   1453                                         userspaceOffset);
   1454    } else {
   1455      // Unknown attributes type?
   1456      status = WrFiltersStatus::BLOB_FALLBACK;
   1457    }
   1458    if (status != WrFiltersStatus::SVGFE) {
   1459      break;
   1460    }
   1461    // Set the remapping table entry
   1462    bufferIdMapping[i] = (int16_t)(aWrFilters.filters.Length() - 1);
   1463  }
   1464  if (status != WrFiltersStatus::SVGFE) {
   1465    // If we couldn't handle this graph, clear the filters before returning.
   1466    aWrFilters.filters.Clear();
   1467    aWrFilters.filter_datas.Clear();
   1468    aWrFilters.values.Clear();
   1469    aWrFilters.post_filters_clip = Nothing();
   1470  }
   1471  return status;
   1472 }
   1473 
   1474 nsRegion FilterInstance::GetPreFilterNeededArea(
   1475    nsIFrame* aFilteredFrame, const nsTArray<SVGFilterFrame*>& aFilterFrames,
   1476    const nsRegion& aPostFilterDirtyRegion) {
   1477  gfxMatrix tm = SVGUtils::GetCanvasTM(aFilteredFrame);
   1478  auto filterChain = aFilteredFrame->StyleEffects()->mFilters.AsSpan();
   1479  UniquePtr<UserSpaceMetrics> metrics =
   1480      UserSpaceMetricsForFrame(aFilteredFrame);
   1481  // Hardcode InputIsTainted to true because we don't want JS to be able to
   1482  // read the rendered contents of aFilteredFrame.
   1483  FilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
   1484                          *metrics, filterChain, aFilterFrames,
   1485                          /* InputIsTainted */ true, nullptr, tm,
   1486                          &aPostFilterDirtyRegion);
   1487  if (!instance.IsInitialized()) {
   1488    return nsRect();
   1489  }
   1490 
   1491  // Now we can ask the instance to compute the area of the source
   1492  // that's needed.
   1493  return instance.ComputeSourceNeededRect();
   1494 }
   1495 
   1496 Maybe<nsRect> FilterInstance::GetPostFilterBounds(
   1497    nsIFrame* aFilteredFrame, const nsTArray<SVGFilterFrame*>& aFilterFrames,
   1498    const gfxRect* aOverrideBBox, const nsRect* aPreFilterBounds) {
   1499  MOZ_ASSERT(!aFilteredFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
   1500                 !aFilteredFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),
   1501             "Non-display SVG do not maintain ink overflow rects");
   1502 
   1503  nsRegion preFilterRegion;
   1504  nsRegion* preFilterRegionPtr = nullptr;
   1505  if (aPreFilterBounds) {
   1506    preFilterRegion = *aPreFilterBounds;
   1507    preFilterRegionPtr = &preFilterRegion;
   1508  }
   1509 
   1510  gfxMatrix tm = SVGUtils::GetCanvasTM(aFilteredFrame);
   1511  auto filterChain = aFilteredFrame->StyleEffects()->mFilters.AsSpan();
   1512  UniquePtr<UserSpaceMetrics> metrics =
   1513      UserSpaceMetricsForFrame(aFilteredFrame);
   1514  // Hardcode InputIsTainted to true because we don't want JS to be able to
   1515  // read the rendered contents of aFilteredFrame.
   1516  FilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
   1517                          *metrics, filterChain, aFilterFrames,
   1518                          /* InputIsTainted */ true, nullptr, tm, nullptr,
   1519                          preFilterRegionPtr, aPreFilterBounds, aOverrideBBox);
   1520  if (!instance.IsInitialized()) {
   1521    return Nothing();
   1522  }
   1523 
   1524  return Some(instance.ComputePostFilterExtents());
   1525 }
   1526 
   1527 FilterInstance::FilterInstance(
   1528    nsIFrame* aTargetFrame, nsIContent* aTargetContent,
   1529    const UserSpaceMetrics& aMetrics, Span<const StyleFilter> aFilterChain,
   1530    const nsTArray<SVGFilterFrame*>& aFilterFrames, bool aFilterInputIsTainted,
   1531    const SVGFilterPaintCallback& aPaintCallback,
   1532    const gfxMatrix& aPaintTransform, const nsRegion* aPostFilterDirtyRegion,
   1533    const nsRegion* aPreFilterDirtyRegion,
   1534    const nsRect* aPreFilterInkOverflowRectOverride,
   1535    const gfxRect* aOverrideBBox, gfxRect* aFilterSpaceBoundsNotSnapped)
   1536    : mTargetFrame(aTargetFrame),
   1537      mTargetContent(aTargetContent),
   1538      mMetrics(aMetrics),
   1539      mPaintCallback(aPaintCallback),
   1540      mPaintTransform(aPaintTransform),
   1541      mInitialized(false) {
   1542  if (aOverrideBBox) {
   1543    mTargetBBox = *aOverrideBBox;
   1544  } else {
   1545    MOZ_ASSERT(mTargetFrame,
   1546               "Need to supply a frame when there's no aOverrideBBox");
   1547    mTargetBBox =
   1548        SVGUtils::GetBBox(mTargetFrame, SVGUtils::eUseFrameBoundsForOuterSVG |
   1549                                            SVGUtils::eBBoxIncludeFillGeometry);
   1550  }
   1551 
   1552  // Compute user space to filter space transforms.
   1553  if (!ComputeUserSpaceToFilterSpaceScale()) {
   1554    return;
   1555  }
   1556 
   1557  if (!ComputeTargetBBoxInFilterSpace()) {
   1558    return;
   1559  }
   1560 
   1561  // Get various transforms:
   1562  gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.xScale, 0.0f, 0.0f,
   1563                              mFilterSpaceToUserSpaceScale.yScale, 0.0f, 0.0f);
   1564 
   1565  mFilterSpaceToFrameSpaceInCSSPxTransform =
   1566      filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform();
   1567  // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible
   1568  mFrameSpaceInCSSPxToFilterSpaceTransform =
   1569      mFilterSpaceToFrameSpaceInCSSPxTransform;
   1570  mFrameSpaceInCSSPxToFilterSpaceTransform.Invert();
   1571 
   1572  nsIntRect targetBounds;
   1573  if (aPreFilterInkOverflowRectOverride) {
   1574    targetBounds = FrameSpaceToFilterSpace(aPreFilterInkOverflowRectOverride);
   1575  } else if (mTargetFrame) {
   1576    nsRect preFilterVOR = mTargetFrame->PreEffectsInkOverflowRect();
   1577    targetBounds = FrameSpaceToFilterSpace(&preFilterVOR);
   1578  }
   1579  mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds);
   1580 
   1581  // Build the filter graph.
   1582  if (NS_FAILED(BuildPrimitives(aFilterChain, aFilterFrames,
   1583                                aFilterInputIsTainted))) {
   1584    return;
   1585  }
   1586 
   1587  // Convert the passed in rects from frame space to filter space:
   1588  mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion);
   1589  mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion);
   1590 
   1591  if (aFilterSpaceBoundsNotSnapped) {
   1592    *aFilterSpaceBoundsNotSnapped = mFilterSpaceBoundsNotSnapped;
   1593  }
   1594 
   1595  mInitialized = true;
   1596 }
   1597 
   1598 bool FilterInstance::ComputeTargetBBoxInFilterSpace() {
   1599  gfxRect targetBBoxInFilterSpace = UserSpaceToFilterSpace(mTargetBBox);
   1600  targetBBoxInFilterSpace.RoundOut();
   1601 
   1602  return gfxUtils::GfxRectToIntRect(targetBBoxInFilterSpace,
   1603                                    &mTargetBBoxInFilterSpace);
   1604 }
   1605 
   1606 bool FilterInstance::ComputeUserSpaceToFilterSpaceScale() {
   1607  if (mTargetFrame) {
   1608    mUserSpaceToFilterSpaceScale = mPaintTransform.ScaleFactors();
   1609    if (mUserSpaceToFilterSpaceScale.xScale <= 0.0f ||
   1610        mUserSpaceToFilterSpaceScale.yScale <= 0.0f) {
   1611      // Nothing should be rendered.
   1612      return false;
   1613    }
   1614  } else {
   1615    mUserSpaceToFilterSpaceScale = MatrixScalesDouble();
   1616  }
   1617 
   1618  mFilterSpaceToUserSpaceScale =
   1619      MatrixScalesDouble(1.0f / mUserSpaceToFilterSpaceScale.xScale,
   1620                         1.0f / mUserSpaceToFilterSpaceScale.yScale);
   1621 
   1622  return true;
   1623 }
   1624 
   1625 gfxRect FilterInstance::UserSpaceToFilterSpace(
   1626    const gfxRect& aUserSpaceRect) const {
   1627  gfxRect filterSpaceRect = aUserSpaceRect;
   1628  filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale);
   1629  return filterSpaceRect;
   1630 }
   1631 
   1632 gfxRect FilterInstance::FilterSpaceToUserSpace(
   1633    const gfxRect& aFilterSpaceRect) const {
   1634  gfxRect userSpaceRect = aFilterSpaceRect;
   1635  userSpaceRect.Scale(mFilterSpaceToUserSpaceScale);
   1636  return userSpaceRect;
   1637 }
   1638 
   1639 nsresult FilterInstance::BuildPrimitives(
   1640    Span<const StyleFilter> aFilterChain,
   1641    const nsTArray<SVGFilterFrame*>& aFilterFrames,
   1642    bool aFilterInputIsTainted) {
   1643  AutoTArray<FilterPrimitiveDescription, 8> primitiveDescriptions;
   1644 
   1645  uint32_t filterIndex = 0;
   1646 
   1647  for (const auto& filter : aFilterChain) {
   1648    if (filter.IsUrl() && aFilterFrames.IsEmpty()) {
   1649      return NS_ERROR_FAILURE;
   1650    }
   1651    auto* filterFrame = filter.IsUrl() ? aFilterFrames[filterIndex++] : nullptr;
   1652    bool inputIsTainted = primitiveDescriptions.IsEmpty()
   1653                              ? aFilterInputIsTainted
   1654                              : primitiveDescriptions.LastElement().IsTainted();
   1655    nsresult rv = BuildPrimitivesForFilter(filter, filterFrame, inputIsTainted,
   1656                                           primitiveDescriptions);
   1657    if (NS_FAILED(rv)) {
   1658      return rv;
   1659    }
   1660  }
   1661 
   1662  mFilterDescription = FilterDescription(std::move(primitiveDescriptions));
   1663 
   1664  return NS_OK;
   1665 }
   1666 
   1667 nsresult FilterInstance::BuildPrimitivesForFilter(
   1668    const StyleFilter& aFilter, SVGFilterFrame* aFilterFrame,
   1669    bool aInputIsTainted,
   1670    nsTArray<FilterPrimitiveDescription>& aPrimitiveDescriptions) {
   1671  NS_ASSERTION(mUserSpaceToFilterSpaceScale.xScale > 0.0f &&
   1672                   mFilterSpaceToUserSpaceScale.yScale > 0.0f,
   1673               "scale factors between spaces should be positive values");
   1674 
   1675  if (aFilter.IsUrl()) {
   1676    // Build primitives for an SVG filter.
   1677    SVGFilterInstance svgFilterInstance(
   1678        aFilter, aFilterFrame, mTargetContent, mMetrics, mTargetBBox,
   1679        mUserSpaceToFilterSpaceScale, mFilterSpaceBoundsNotSnapped);
   1680    if (!svgFilterInstance.IsInitialized()) {
   1681      return NS_ERROR_FAILURE;
   1682    }
   1683 
   1684    return svgFilterInstance.BuildPrimitives(aPrimitiveDescriptions,
   1685                                             mInputImages, aInputIsTainted);
   1686  }
   1687 
   1688  // Build primitives for a CSS filter.
   1689 
   1690  // If we don't have a frame, use opaque black for shadows with unspecified
   1691  // shadow colors.
   1692  nscolor shadowFallbackColor =
   1693      mTargetFrame ? mTargetFrame->StyleText()->mColor.ToColor()
   1694                   : NS_RGB(0, 0, 0);
   1695 
   1696  CSSFilterInstance cssFilterInstance(aFilter, shadowFallbackColor,
   1697                                      mTargetBounds,
   1698                                      mFrameSpaceInCSSPxToFilterSpaceTransform);
   1699  return cssFilterInstance.BuildPrimitives(aPrimitiveDescriptions,
   1700                                           aInputIsTainted);
   1701 }
   1702 
   1703 static void UpdateNeededBounds(const nsIntRegion& aRegion, nsIntRect& aBounds) {
   1704  aBounds = aRegion.GetBounds();
   1705 
   1706  if (aBounds.IsEmpty()) {
   1707    return;
   1708  }
   1709 
   1710  bool overflow;
   1711  IntSize surfaceSize =
   1712      SVGUtils::ConvertToSurfaceSize(SizeDouble(aBounds.Size()), &overflow);
   1713  if (overflow) {
   1714    aBounds.SizeTo(surfaceSize);
   1715  }
   1716 }
   1717 
   1718 void FilterInstance::ComputeNeededBoxes() {
   1719  if (mFilterDescription.mPrimitives.IsEmpty()) {
   1720    return;
   1721  }
   1722 
   1723  nsIntRegion sourceGraphicNeededRegion;
   1724  nsIntRegion fillPaintNeededRegion;
   1725  nsIntRegion strokePaintNeededRegion;
   1726 
   1727  FilterSupport::ComputeSourceNeededRegions(
   1728      mFilterDescription, mPostFilterDirtyRegion, sourceGraphicNeededRegion,
   1729      fillPaintNeededRegion, strokePaintNeededRegion);
   1730 
   1731  sourceGraphicNeededRegion.AndWith(mTargetBounds);
   1732 
   1733  UpdateNeededBounds(sourceGraphicNeededRegion, mSourceGraphic.mNeededBounds);
   1734  UpdateNeededBounds(fillPaintNeededRegion, mFillPaint.mNeededBounds);
   1735  UpdateNeededBounds(strokePaintNeededRegion, mStrokePaint.mNeededBounds);
   1736 }
   1737 
   1738 void FilterInstance::BuildSourcePaint(SourceInfo* aSource,
   1739                                      imgDrawingParams& aImgParams) {
   1740  MOZ_ASSERT(mTargetFrame);
   1741  nsIntRect neededRect = aSource->mNeededBounds;
   1742  if (neededRect.IsEmpty()) {
   1743    return;
   1744  }
   1745 
   1746  RefPtr<DrawTarget> offscreenDT =
   1747      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
   1748          neededRect.Size(), SurfaceFormat::B8G8R8A8);
   1749  if (!offscreenDT || !offscreenDT->IsValid()) {
   1750    return;
   1751  }
   1752 
   1753  gfxContext ctx(offscreenDT);
   1754  gfxContextAutoSaveRestore saver(&ctx);
   1755 
   1756  ctx.SetMatrixDouble(mPaintTransform *
   1757                      gfxMatrix::Translation(-neededRect.TopLeft()));
   1758  GeneralPattern pattern;
   1759  if (aSource == &mFillPaint) {
   1760    SVGUtils::MakeFillPatternFor(mTargetFrame, &ctx, &pattern, aImgParams);
   1761  } else if (aSource == &mStrokePaint) {
   1762    SVGUtils::MakeStrokePatternFor(mTargetFrame, &ctx, &pattern, aImgParams);
   1763  }
   1764 
   1765  if (pattern.GetPattern()) {
   1766    offscreenDT->FillRect(
   1767        ToRect(FilterSpaceToUserSpace(ThebesRect(neededRect))), pattern);
   1768  }
   1769 
   1770  aSource->mSourceSurface = offscreenDT->Snapshot();
   1771  aSource->mSurfaceRect = neededRect;
   1772 }
   1773 
   1774 void FilterInstance::BuildSourcePaints(imgDrawingParams& aImgParams) {
   1775  if (!mFillPaint.mNeededBounds.IsEmpty()) {
   1776    BuildSourcePaint(&mFillPaint, aImgParams);
   1777  }
   1778 
   1779  if (!mStrokePaint.mNeededBounds.IsEmpty()) {
   1780    BuildSourcePaint(&mStrokePaint, aImgParams);
   1781  }
   1782 }
   1783 
   1784 void FilterInstance::BuildSourceImage(DrawTarget* aDest,
   1785                                      imgDrawingParams& aImgParams,
   1786                                      FilterNode* aFilter, FilterNode* aSource,
   1787                                      const Rect& aSourceRect) {
   1788  MOZ_ASSERT(mTargetFrame);
   1789 
   1790  nsIntRect neededRect = mSourceGraphic.mNeededBounds;
   1791  if (neededRect.IsEmpty()) {
   1792    return;
   1793  }
   1794 
   1795  RefPtr<DrawTarget> offscreenDT;
   1796  SurfaceFormat format = SurfaceFormat::B8G8R8A8;
   1797  if (aDest->CanCreateSimilarDrawTarget(neededRect.Size(), format)) {
   1798    offscreenDT = aDest->CreateSimilarDrawTargetForFilter(
   1799        neededRect.Size(), format, aFilter, aSource, aSourceRect, Point(0, 0));
   1800  }
   1801  if (!offscreenDT || !offscreenDT->IsValid()) {
   1802    return;
   1803  }
   1804 
   1805  gfxRect r = FilterSpaceToUserSpace(ThebesRect(neededRect));
   1806  r.RoundOut();
   1807  nsIntRect dirty;
   1808  if (!gfxUtils::GfxRectToIntRect(r, &dirty)) {
   1809    return;
   1810  }
   1811 
   1812  // SVG graphics paint to device space, so we need to set an initial device
   1813  // space to filter space transform on the gfxContext that SourceGraphic
   1814  // and SourceAlpha will paint to.
   1815  //
   1816  // (In theory it would be better to minimize error by having filtered SVG
   1817  // graphics temporarily paint to user space when painting the sources and
   1818  // only set a user space to filter space transform on the gfxContext
   1819  // (since that would eliminate the transform multiplications from user
   1820  // space to device space and back again). However, that would make the
   1821  // code more complex while being hard to get right without introducing
   1822  // subtle bugs, and in practice it probably makes no real difference.)
   1823  gfxContext ctx(offscreenDT);
   1824  gfxMatrix devPxToCssPxTM = SVGUtils::GetCSSPxToDevPxMatrix(mTargetFrame);
   1825  DebugOnly<bool> invertible = devPxToCssPxTM.Invert();
   1826  MOZ_ASSERT(invertible);
   1827  ctx.SetMatrixDouble(devPxToCssPxTM * mPaintTransform *
   1828                      gfxMatrix::Translation(-neededRect.TopLeft()));
   1829 
   1830  auto imageFlags = aImgParams.imageFlags;
   1831  if (mTargetFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
   1832    // We're coming from a mask or pattern instance. Patterns
   1833    // are painted into a separate surface and it seems we can't
   1834    // handle the differently sized surface that might be returned
   1835    // with FLAG_HIGH_QUALITY_SCALING
   1836    imageFlags &= ~imgIContainer::FLAG_HIGH_QUALITY_SCALING;
   1837  }
   1838  imgDrawingParams imgParams(imageFlags);
   1839  mPaintCallback(ctx, imgParams, &mPaintTransform, &dirty);
   1840  aImgParams.result = imgParams.result;
   1841 
   1842  mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
   1843  mSourceGraphic.mSurfaceRect = neededRect;
   1844 }
   1845 
   1846 void FilterInstance::Render(gfxContext* aCtx, imgDrawingParams& aImgParams,
   1847                            float aOpacity) {
   1848  MOZ_ASSERT(mTargetFrame, "Need a frame for rendering");
   1849 
   1850  if (mFilterDescription.mPrimitives.IsEmpty()) {
   1851    // An filter without any primitive. Treat it as success and paint nothing.
   1852    return;
   1853  }
   1854 
   1855  nsIntRect filterRect =
   1856      mPostFilterDirtyRegion.GetBounds().Intersect(OutputFilterSpaceBounds());
   1857  if (filterRect.IsEmpty() || mPaintTransform.IsSingular()) {
   1858    return;
   1859  }
   1860 
   1861  gfxContextMatrixAutoSaveRestore autoSR(aCtx);
   1862  aCtx->SetMatrix(
   1863      aCtx->CurrentMatrix().PreTranslate(filterRect.x, filterRect.y));
   1864 
   1865  ComputeNeededBoxes();
   1866 
   1867  Rect renderRect = IntRectToRect(filterRect);
   1868  RefPtr<DrawTarget> dt = aCtx->GetDrawTarget();
   1869 
   1870  MOZ_ASSERT(dt);
   1871  if (!dt->IsValid()) {
   1872    return;
   1873  }
   1874 
   1875  BuildSourcePaints(aImgParams);
   1876  RefPtr<FilterNode> sourceGraphic, fillPaint, strokePaint;
   1877  if (mFillPaint.mSourceSurface) {
   1878    fillPaint = FilterWrappers::ForSurface(dt, mFillPaint.mSourceSurface,
   1879                                           mFillPaint.mSurfaceRect.TopLeft());
   1880  }
   1881  if (mStrokePaint.mSourceSurface) {
   1882    strokePaint = FilterWrappers::ForSurface(
   1883        dt, mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect.TopLeft());
   1884  }
   1885 
   1886  // We make the sourceGraphic filter but don't set its inputs until after so
   1887  // that we can make the sourceGraphic size depend on the filter chain
   1888  sourceGraphic = dt->CreateFilter(FilterType::TRANSFORM);
   1889  if (sourceGraphic) {
   1890    // Make sure we set the translation before calling BuildSourceImage
   1891    // so that CreateSimilarDrawTargetForFilter works properly
   1892    IntPoint offset = mSourceGraphic.mNeededBounds.TopLeft();
   1893    sourceGraphic->SetAttribute(ATT_TRANSFORM_MATRIX,
   1894                                Matrix::Translation(offset.x, offset.y));
   1895  }
   1896 
   1897  RefPtr<FilterNode> resultFilter = FilterNodeGraphFromDescription(
   1898      dt, mFilterDescription, renderRect, sourceGraphic,
   1899      mSourceGraphic.mSurfaceRect, fillPaint, strokePaint, mInputImages);
   1900 
   1901  if (!resultFilter) {
   1902    gfxWarning() << "Filter is NULL.";
   1903    return;
   1904  }
   1905 
   1906  BuildSourceImage(dt, aImgParams, resultFilter, sourceGraphic, renderRect);
   1907  if (sourceGraphic) {
   1908    if (mSourceGraphic.mSourceSurface) {
   1909      sourceGraphic->SetInput(IN_TRANSFORM_IN, mSourceGraphic.mSourceSurface);
   1910    } else {
   1911      RefPtr<FilterNode> clear = FilterWrappers::Clear(aCtx->GetDrawTarget());
   1912      sourceGraphic->SetInput(IN_TRANSFORM_IN, clear);
   1913    }
   1914  }
   1915 
   1916  dt->DrawFilter(resultFilter, renderRect, Point(0, 0), DrawOptions(aOpacity));
   1917 }
   1918 
   1919 nsRegion FilterInstance::ComputePostFilterDirtyRegion() {
   1920  if (mPreFilterDirtyRegion.IsEmpty() ||
   1921      mFilterDescription.mPrimitives.IsEmpty()) {
   1922    return nsRegion();
   1923  }
   1924 
   1925  nsIntRegion resultChangeRegion = FilterSupport::ComputeResultChangeRegion(
   1926      mFilterDescription, mPreFilterDirtyRegion, nsIntRegion(), nsIntRegion());
   1927  return FilterSpaceToFrameSpace(resultChangeRegion);
   1928 }
   1929 
   1930 nsRect FilterInstance::ComputePostFilterExtents() {
   1931  if (mFilterDescription.mPrimitives.IsEmpty()) {
   1932    return nsRect();
   1933  }
   1934 
   1935  nsIntRegion postFilterExtents = FilterSupport::ComputePostFilterExtents(
   1936      mFilterDescription, mTargetBounds);
   1937  return FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
   1938 }
   1939 
   1940 nsRect FilterInstance::ComputeSourceNeededRect() {
   1941  ComputeNeededBoxes();
   1942  return FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds);
   1943 }
   1944 
   1945 nsIntRect FilterInstance::OutputFilterSpaceBounds() const {
   1946  if (mFilterDescription.mPrimitives.IsEmpty()) {
   1947    return nsIntRect();
   1948  }
   1949 
   1950  return mFilterDescription.mPrimitives.LastElement().PrimitiveSubregion();
   1951 }
   1952 
   1953 nsIntRect FilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const {
   1954  nsIntRect rect = OutputFilterSpaceBounds();
   1955  if (aRect) {
   1956    if (aRect->IsEmpty()) {
   1957      return nsIntRect();
   1958    }
   1959    gfxRect rectInCSSPx =
   1960        nsLayoutUtils::RectToGfxRect(*aRect, AppUnitsPerCSSPixel());
   1961    gfxRect rectInFilterSpace =
   1962        mFrameSpaceInCSSPxToFilterSpaceTransform.TransformBounds(rectInCSSPx);
   1963    rectInFilterSpace.RoundOut();
   1964    nsIntRect intRect;
   1965    if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) {
   1966      rect = intRect;
   1967    }
   1968  }
   1969  return rect;
   1970 }
   1971 
   1972 nsRect FilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const {
   1973  if (aRect.IsEmpty()) {
   1974    return nsRect();
   1975  }
   1976  gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
   1977  r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r);
   1978  // nsLayoutUtils::RoundGfxRectToAppRect rounds out.
   1979  return nsLayoutUtils::RoundGfxRectToAppRect(r, AppUnitsPerCSSPixel());
   1980 }
   1981 
   1982 nsIntRegion FilterInstance::FrameSpaceToFilterSpace(
   1983    const nsRegion* aRegion) const {
   1984  if (!aRegion) {
   1985    return OutputFilterSpaceBounds();
   1986  }
   1987  nsIntRegion result;
   1988  for (auto iter = aRegion->RectIter(); !iter.Done(); iter.Next()) {
   1989    // FrameSpaceToFilterSpace rounds out, so this works.
   1990    nsRect rect = iter.Get();
   1991    result.OrWith(FrameSpaceToFilterSpace(&rect));
   1992  }
   1993  return result;
   1994 }
   1995 
   1996 nsRegion FilterInstance::FilterSpaceToFrameSpace(
   1997    const nsIntRegion& aRegion) const {
   1998  nsRegion result;
   1999  for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
   2000    // FilterSpaceToFrameSpace rounds out, so this works.
   2001    result.OrWith(FilterSpaceToFrameSpace(iter.Get()));
   2002  }
   2003  return result;
   2004 }
   2005 
   2006 gfxMatrix FilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const {
   2007  if (!mTargetFrame) {
   2008    return gfxMatrix();
   2009  }
   2010  return gfxMatrix::Translation(
   2011      -SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mTargetFrame));
   2012 }
   2013 
   2014 }  // namespace mozilla