tor-browser

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

FilterNodeWebgl.cpp (36310B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "FilterNodeWebgl.h"
      8 
      9 #include <limits>
     10 
     11 #include "DrawTargetWebglInternal.h"
     12 #include "SourceSurfaceWebgl.h"
     13 #include "mozilla/PodOperations.h"
     14 #include "mozilla/gfx/Blur.h"
     15 #include "mozilla/gfx/DrawTargetSkia.h"
     16 #include "mozilla/gfx/FilterNodeSoftware.h"
     17 #include "mozilla/gfx/Helpers.h"
     18 #include "mozilla/gfx/Logging.h"
     19 
     20 namespace mozilla::gfx {
     21 
     22 FilterNodeWebgl::FilterNodeWebgl(FilterType aType)
     23    : mType(aType),
     24      mSoftwareFilter(
     25          FilterNodeSoftware::Create(aType).downcast<FilterNodeSoftware>()) {}
     26 
     27 FilterNodeWebgl::~FilterNodeWebgl() = default;
     28 
     29 already_AddRefed<FilterNodeWebgl> FilterNodeWebgl::Create(FilterType aType) {
     30  RefPtr<FilterNodeWebgl> filter;
     31  switch (aType) {
     32    case FilterType::CROP:
     33      filter = new FilterNodeCropWebgl;
     34      break;
     35    case FilterType::TRANSFORM:
     36      filter = new FilterNodeTransformWebgl;
     37      break;
     38    case FilterType::GAUSSIAN_BLUR:
     39      filter = new FilterNodeGaussianBlurWebgl;
     40      break;
     41    case FilterType::PREMULTIPLY:
     42      filter = new FilterNodePremultiplyWebgl;
     43      break;
     44    case FilterType::UNPREMULTIPLY:
     45      filter = new FilterNodeUnpremultiplyWebgl;
     46      break;
     47    case FilterType::COLOR_MATRIX:
     48      filter = new FilterNodeColorMatrixWebgl;
     49      break;
     50    case FilterType::LINEAR_TRANSFER:
     51      filter = new FilterNodeLinearTransferWebgl;
     52      break;
     53    case FilterType::TABLE_TRANSFER:
     54      filter = new FilterNodeTableTransferWebgl;
     55      break;
     56    case FilterType::OPACITY:
     57      filter = new FilterNodeOpacityWebgl;
     58      break;
     59    default:
     60      filter = new FilterNodeWebgl(aType);
     61      break;
     62  }
     63  return filter.forget();
     64 }
     65 
     66 int32_t FilterNodeWebgl::InputIndex(uint32_t aInputEnumIndex) const {
     67  if (mSoftwareFilter) {
     68    return mSoftwareFilter->InputIndex(aInputEnumIndex);
     69  }
     70  return -1;
     71 }
     72 
     73 bool FilterNodeWebgl::ReserveInputIndex(uint32_t aIndex) {
     74  size_t inputIndex = aIndex;
     75  if (std::numeric_limits<size_t>::max() - inputIndex < 1) {
     76    return false;
     77  }
     78  if (mInputSurfaces.size() <= inputIndex) {
     79    mInputSurfaces.resize(inputIndex + 1);
     80  }
     81  if (mInputFilters.size() <= inputIndex) {
     82    mInputFilters.resize(inputIndex + 1);
     83  }
     84  return true;
     85 }
     86 
     87 bool FilterNodeWebgl::SetInputAccel(uint32_t aIndex, SourceSurface* aSurface) {
     88  if (ReserveInputIndex(aIndex)) {
     89    mInputSurfaces[aIndex] = aSurface;
     90    mInputFilters[aIndex] = nullptr;
     91    return true;
     92  }
     93  return false;
     94 }
     95 
     96 bool FilterNodeWebgl::SetInputSoftware(uint32_t aIndex,
     97                                       SourceSurface* aSurface) {
     98  if (mSoftwareFilter) {
     99    mSoftwareFilter->SetInput(aIndex, aSurface);
    100  }
    101  mInputMask |= (1 << aIndex);
    102  return true;
    103 }
    104 
    105 void FilterNodeWebgl::SetInput(uint32_t aIndex, SourceSurface* aSurface) {
    106  int32_t inputIndex = InputIndex(aIndex);
    107  if (inputIndex < 0 || !SetInputAccel(inputIndex, aSurface) ||
    108      !SetInputSoftware(inputIndex, aSurface)) {
    109    gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
    110    return;
    111  }
    112 }
    113 
    114 void FilterNodeWebgl::SetInput(uint32_t aIndex, FilterNode* aFilter) {
    115  if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_WEBGL) {
    116    MOZ_ASSERT(false, "FilterNodeWebgl required as input");
    117    return;
    118  }
    119 
    120  int32_t inputIndex = InputIndex(aIndex);
    121  if (inputIndex < 0 || !ReserveInputIndex(inputIndex)) {
    122    gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
    123    return;
    124  }
    125 
    126  auto* webglFilter = static_cast<FilterNodeWebgl*>(aFilter);
    127  mInputFilters[inputIndex] = webglFilter;
    128  mInputSurfaces[inputIndex] = nullptr;
    129  if (mSoftwareFilter) {
    130    MOZ_ASSERT(!webglFilter || webglFilter->mSoftwareFilter);
    131    mSoftwareFilter->SetInput(
    132        aIndex, webglFilter ? webglFilter->mSoftwareFilter.get() : nullptr);
    133  }
    134 }
    135 
    136 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, bool aValue) {
    137  if (mSoftwareFilter) {
    138    mSoftwareFilter->SetAttribute(aIndex, aValue);
    139  }
    140 }
    141 
    142 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, uint32_t aValue) {
    143  if (mSoftwareFilter) {
    144    mSoftwareFilter->SetAttribute(aIndex, aValue);
    145  }
    146 }
    147 
    148 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, Float aValue) {
    149  if (mSoftwareFilter) {
    150    mSoftwareFilter->SetAttribute(aIndex, aValue);
    151  }
    152 }
    153 
    154 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Size& aValue) {
    155  if (mSoftwareFilter) {
    156    mSoftwareFilter->SetAttribute(aIndex, aValue);
    157  }
    158 }
    159 
    160 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const IntSize& aValue) {
    161  if (mSoftwareFilter) {
    162    mSoftwareFilter->SetAttribute(aIndex, aValue);
    163  }
    164 }
    165 
    166 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const IntPoint& aValue) {
    167  if (mSoftwareFilter) {
    168    mSoftwareFilter->SetAttribute(aIndex, aValue);
    169  }
    170 }
    171 
    172 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Rect& aValue) {
    173  if (mSoftwareFilter) {
    174    mSoftwareFilter->SetAttribute(aIndex, aValue);
    175  }
    176 }
    177 
    178 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const IntRect& aValue) {
    179  if (mSoftwareFilter) {
    180    mSoftwareFilter->SetAttribute(aIndex, aValue);
    181  }
    182 }
    183 
    184 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Point& aValue) {
    185  if (mSoftwareFilter) {
    186    mSoftwareFilter->SetAttribute(aIndex, aValue);
    187  }
    188 }
    189 
    190 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Matrix& aValue) {
    191  if (mSoftwareFilter) {
    192    mSoftwareFilter->SetAttribute(aIndex, aValue);
    193  }
    194 }
    195 
    196 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Matrix5x4& aValue) {
    197  if (mSoftwareFilter) {
    198    mSoftwareFilter->SetAttribute(aIndex, aValue);
    199  }
    200 }
    201 
    202 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Point3D& aValue) {
    203  if (mSoftwareFilter) {
    204    mSoftwareFilter->SetAttribute(aIndex, aValue);
    205  }
    206 }
    207 
    208 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const DeviceColor& aValue) {
    209  if (mSoftwareFilter) {
    210    mSoftwareFilter->SetAttribute(aIndex, aValue);
    211  }
    212 }
    213 
    214 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Float* aValues,
    215                                   uint32_t aSize) {
    216  if (mSoftwareFilter) {
    217    mSoftwareFilter->SetAttribute(aIndex, aValues, aSize);
    218  }
    219 }
    220 
    221 IntRect FilterNodeWebgl::MapRectToSource(const IntRect& aRect,
    222                                         const IntRect& aMax,
    223                                         FilterNode* aSourceNode) {
    224  if (mSoftwareFilter) {
    225    if (aSourceNode && aSourceNode->GetBackendType() == FILTER_BACKEND_WEBGL) {
    226      aSourceNode = static_cast<FilterNodeWebgl*>(aSourceNode)->mSoftwareFilter;
    227    }
    228    return mSoftwareFilter->MapRectToSource(aRect, aMax, aSourceNode);
    229  }
    230  return aMax;
    231 }
    232 
    233 void FilterNodeWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
    234                           const Point& aDestPoint, const DrawOptions& aOptions,
    235                           FilterNodeWebgl* aParent) {
    236  ResolveAllInputs(aDT, aParent);
    237 
    238  MOZ_ASSERT(mSoftwareFilter);
    239  aDT->DrawFilterFallback(mSoftwareFilter, aSourceRect, aDestPoint, aOptions);
    240 }
    241 
    242 already_AddRefed<SourceSurface> FilterNodeWebgl::DrawChild(
    243    FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect,
    244    const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) {
    245  ResolveAllInputs(aDT, aParent);
    246 
    247  MOZ_ASSERT(mSoftwareFilter);
    248  RefPtr<DrawTarget> swDT = aDT->mSkia->CreateSimilarDrawTarget(
    249      IntSize::Ceil(aSourceRect.Size()), aDT->GetFormat());
    250  if (!swDT) {
    251    return nullptr;
    252  }
    253  swDT->DrawFilter(mSoftwareFilter, aSourceRect, Point(0, 0), aOptions);
    254  aSurfaceOffset = aSourceRect.TopLeft();
    255  aColor = DeviceColor(1, 1, 1, 1);
    256  return swDT->Snapshot();
    257 }
    258 
    259 IntRect FilterNodeWebgl::MapInputRectToSource(uint32_t aInputEnumIndex,
    260                                              const IntRect& aRect,
    261                                              const IntRect& aMax,
    262                                              FilterNode* aSourceNode) {
    263  int32_t inputIndex = InputIndex(aInputEnumIndex);
    264  if (inputIndex < 0) {
    265    gfxDevCrash(LogReason::FilterInputError)
    266        << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
    267    return aMax;
    268  }
    269  if ((uint32_t)inputIndex < NumberOfSetInputs()) {
    270    if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIndex]) {
    271      return filter->MapRectToSource(aRect, aMax, aSourceNode);
    272    }
    273  }
    274  if (this == aSourceNode) {
    275    return aRect;
    276  }
    277  return IntRect();
    278 }
    279 
    280 void FilterNodeWebgl::ResolveAllInputs(DrawTargetWebgl* aDT,
    281                                       FilterNodeWebgl* aParent) {
    282  ResolveInputs(aDT, false, aParent);
    283  for (const auto& filter : mInputFilters) {
    284    if (filter) {
    285      filter->ResolveAllInputs(aDT, this);
    286    }
    287  }
    288 }
    289 
    290 int32_t FilterNodeCropWebgl::InputIndex(uint32_t aInputEnumIndex) const {
    291  switch (aInputEnumIndex) {
    292    case IN_CROP_IN:
    293      return 0;
    294    default:
    295      return -1;
    296  }
    297 }
    298 
    299 void FilterNodeCropWebgl::SetAttribute(uint32_t aIndex, const Rect& aValue) {
    300  MOZ_ASSERT(aIndex == ATT_CROP_RECT);
    301  Rect srcRect = aValue;
    302  srcRect.Round();
    303  if (!srcRect.ToIntRect(&mCropRect)) {
    304    mCropRect = IntRect();
    305  }
    306  FilterNodeWebgl::SetAttribute(aIndex, aValue);
    307 }
    308 
    309 IntRect FilterNodeCropWebgl::MapRectToSource(const IntRect& aRect,
    310                                             const IntRect& aMax,
    311                                             FilterNode* aSourceNode) {
    312  return MapInputRectToSource(IN_CROP_IN, aRect.Intersect(mCropRect), aMax,
    313                              aSourceNode);
    314 }
    315 
    316 void FilterNodeCropWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
    317                               const Point& aDestPoint,
    318                               const DrawOptions& aOptions,
    319                               FilterNodeWebgl* aParent) {
    320  ResolveInputs(aDT, true, aParent);
    321 
    322  uint32_t inputIdx = InputIndex(IN_CROP_IN);
    323  if (inputIdx < NumberOfSetInputs()) {
    324    Rect croppedSource = aSourceRect.Intersect(Rect(mCropRect));
    325    if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
    326      filter->Draw(aDT, croppedSource,
    327                   aDestPoint + croppedSource.TopLeft() - aSourceRect.TopLeft(),
    328                   aOptions, this);
    329    } else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
    330      aDT->DrawSurface(surface,
    331                       croppedSource - aSourceRect.TopLeft() + aDestPoint,
    332                       croppedSource, DrawSurfaceOptions(), aOptions);
    333    }
    334  }
    335 }
    336 
    337 bool FilterNodeCropWebgl::DrawAccel(DrawTargetWebgl* aDT,
    338                                    const Rect& aSourceRect,
    339                                    const Point& aDestPoint,
    340                                    const DrawOptions& aOptions,
    341                                    FilterNodeWebgl* aParent) {
    342  uint32_t inputIdx = InputIndex(IN_CROP_IN);
    343  if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) {
    344    Rect croppedSource = aSourceRect.Intersect(Rect(mCropRect));
    345    FilterNodeWebgl* filter = mInputFilters[inputIdx];
    346    switch (filter->GetType()) {
    347      case FilterType::COLOR_MATRIX:
    348      case FilterType::LINEAR_TRANSFER:
    349      case FilterType::TABLE_TRANSFER:
    350        // Crop filters are sometimes generated before evaluating a color matrix
    351        // filter.
    352        return filter->DrawAccel(
    353            aDT, croppedSource,
    354            aDestPoint + croppedSource.TopLeft() - aSourceRect.TopLeft(),
    355            aOptions, this);
    356      default:
    357        break;
    358    }
    359  }
    360  return false;
    361 }
    362 
    363 already_AddRefed<SourceSurface> FilterNodeCropWebgl::DrawChild(
    364    FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect,
    365    const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) {
    366  ResolveInputs(aDT, true, aParent);
    367 
    368  uint32_t inputIdx = InputIndex(IN_CROP_IN);
    369  if (inputIdx < NumberOfSetInputs()) {
    370    if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
    371      Rect croppedSource = aSourceRect.Intersect(Rect(mCropRect));
    372      return filter->DrawChild(this, aDT, croppedSource, aOptions,
    373                               aSurfaceOffset, aColor);
    374    }
    375    return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions,
    376                                      aSurfaceOffset, aColor);
    377  }
    378  return nullptr;
    379 }
    380 
    381 int32_t FilterNodeTransformWebgl::InputIndex(uint32_t aInputEnumIndex) const {
    382  switch (aInputEnumIndex) {
    383    case IN_TRANSFORM_IN:
    384      return 0;
    385    default:
    386      return -1;
    387  }
    388 }
    389 
    390 void FilterNodeTransformWebgl::SetAttribute(uint32_t aIndex, uint32_t aValue) {
    391  MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
    392  mSamplingFilter = static_cast<SamplingFilter>(aValue);
    393  FilterNodeWebgl::SetAttribute(aIndex, aValue);
    394 }
    395 
    396 void FilterNodeTransformWebgl::SetAttribute(uint32_t aIndex,
    397                                            const Matrix& aValue) {
    398  MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
    399  mMatrix = aValue;
    400  FilterNodeWebgl::SetAttribute(aIndex, aValue);
    401 }
    402 
    403 IntRect FilterNodeTransformWebgl::MapRectToSource(const IntRect& aRect,
    404                                                  const IntRect& aMax,
    405                                                  FilterNode* aSourceNode) {
    406  if (aRect.IsEmpty()) {
    407    return IntRect();
    408  }
    409  Matrix inv(mMatrix);
    410  if (!inv.Invert()) {
    411    return aMax;
    412  }
    413  Rect rect = inv.TransformBounds(Rect(aRect));
    414  rect.RoundOut();
    415  IntRect intRect;
    416  if (!rect.ToIntRect(&intRect)) {
    417    return aMax;
    418  }
    419  return MapInputRectToSource(IN_TRANSFORM_IN, intRect, aMax, aSourceNode);
    420 }
    421 
    422 void FilterNodeTransformWebgl::Draw(DrawTargetWebgl* aDT,
    423                                    const Rect& aSourceRect,
    424                                    const Point& aDestPoint,
    425                                    const DrawOptions& aOptions,
    426                                    FilterNodeWebgl* aParent) {
    427  if (!mMatrix.IsTranslation()) {
    428    FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions, aParent);
    429    return;
    430  }
    431 
    432  ResolveInputs(aDT, true, aParent);
    433 
    434  uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN);
    435  if (inputIdx < NumberOfSetInputs()) {
    436    if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
    437      filter->Draw(aDT, aSourceRect - mMatrix.GetTranslation(), aDestPoint,
    438                   aOptions, aParent);
    439    } else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
    440      aDT->DrawSurface(surface, Rect(aDestPoint, aSourceRect.Size()),
    441                       aSourceRect - mMatrix.GetTranslation(),
    442                       DrawSurfaceOptions(mSamplingFilter), aOptions);
    443    }
    444  }
    445 }
    446 
    447 already_AddRefed<SourceSurface> FilterNodeTransformWebgl::DrawChild(
    448    FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect,
    449    const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) {
    450  if (!mMatrix.IsIntegerTranslation()) {
    451    return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions,
    452                                      aSurfaceOffset, aColor);
    453  }
    454 
    455  ResolveInputs(aDT, true, aParent);
    456 
    457  uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN);
    458  if (inputIdx < NumberOfSetInputs()) {
    459    if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
    460      aSurfaceOffset = mMatrix.GetTranslation().Round();
    461      aColor = DeviceColor(1, 1, 1, aOptions.mAlpha);
    462      return surface.forget();
    463    }
    464    return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions,
    465                                      aSurfaceOffset, aColor);
    466  }
    467  return nullptr;
    468 }
    469 
    470 FilterNodeDeferInputWebgl::FilterNodeDeferInputWebgl(
    471    RefPtr<Path> aPath, const Pattern& aPattern, const IntRect& aSourceRect,
    472    const Matrix& aDestTransform, const DrawOptions& aOptions,
    473    const StrokeOptions* aStrokeOptions)
    474    : mPath(std::move(aPath)),
    475      mSourceRect(aSourceRect),
    476      mDestTransform(aDestTransform),
    477      mOptions(aOptions) {
    478  mPattern.Init(aPattern);
    479  if (aStrokeOptions) {
    480    mStrokeOptions = Some(*aStrokeOptions);
    481    if (aStrokeOptions->mDashLength > 0) {
    482      mDashPatternStorage.reset(new Float[aStrokeOptions->mDashLength]);
    483      PodCopy(mDashPatternStorage.get(), aStrokeOptions->mDashPattern,
    484              aStrokeOptions->mDashLength);
    485      mStrokeOptions->mDashPattern = mDashPatternStorage.get();
    486    }
    487  }
    488  SetAttribute(ATT_TRANSFORM_MATRIX,
    489               Matrix::Translation(mSourceRect.TopLeft()));
    490 }
    491 
    492 void FilterNodeDeferInputWebgl::ResolveInputs(DrawTargetWebgl* aDT, bool aAccel,
    493                                              FilterNodeWebgl* aParent) {
    494  uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN);
    495  bool hasAccel = false;
    496  if (inputIdx < NumberOfSetInputs() && mInputSurfaces[inputIdx]) {
    497    if (aAccel || (mInputMask & (1 << inputIdx))) {
    498      return;
    499    }
    500    hasAccel = true;
    501  }
    502  RefPtr<SourceSurface> surface;
    503  SurfaceFormat format = SurfaceFormat::B8G8R8A8;
    504  static const ColorPattern maskPattern(DeviceColor(1, 1, 1, 1));
    505  const Pattern* pattern = mPattern.GetPattern();
    506  if (aAccel) {
    507    // If using acceleration on a color pattern, attempt to blur solely on the
    508    // alpha values to significantly reduce data churn, as the color will only
    509    // vary linearly with alpha over the input surface. The color will be
    510    // incorporated on the final mask draw.
    511    if (mPattern.GetPattern()->GetType() == PatternType::COLOR) {
    512      format = SurfaceFormat::A8;
    513      pattern = &maskPattern;
    514    }
    515    surface = aDT->ResolveFilterInputAccel(
    516        mPath, *pattern, mSourceRect, mDestTransform, mOptions,
    517        mStrokeOptions.ptrOr(nullptr), format);
    518  }
    519  if (!surface) {
    520    surface = aDT->mSkia->ResolveFilterInput(
    521        mPath, *pattern, mSourceRect, mDestTransform, mOptions,
    522        mStrokeOptions.ptrOr(nullptr), format);
    523  }
    524  if (hasAccel) {
    525    SetInputSoftware(inputIdx, surface);
    526  } else if (surface && surface->GetFormat() == SurfaceFormat::A8) {
    527    SetInputAccel(inputIdx, surface);
    528  } else {
    529    SetInput(inputIdx, surface);
    530  }
    531 }
    532 
    533 void FilterNodeDeferInputWebgl::Draw(DrawTargetWebgl* aDT,
    534                                     const Rect& aSourceRect,
    535                                     const Point& aDestPoint,
    536                                     const DrawOptions& aOptions,
    537                                     FilterNodeWebgl* aParent) {
    538  const Pattern* pattern = mPattern.GetPattern();
    539  AutoRestoreTransform restore(aDT);
    540  aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size()));
    541  aDT->ConcatTransform(
    542      Matrix(mDestTransform).PostTranslate(aDestPoint - aSourceRect.TopLeft()));
    543  DrawOptions options(aOptions.mAlpha * mOptions.mAlpha,
    544                      aOptions.mCompositionOp, mOptions.mAntialiasMode);
    545  if (mStrokeOptions) {
    546    aDT->Stroke(mPath, *pattern, *mStrokeOptions, options);
    547  } else {
    548    aDT->Fill(mPath, *pattern, options);
    549  }
    550  aDT->PopClip();
    551 }
    552 
    553 already_AddRefed<SourceSurface> FilterNodeDeferInputWebgl::DrawChild(
    554    FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect,
    555    const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) {
    556  ResolveInputs(aDT, true, aParent);
    557 
    558  uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN);
    559  if (inputIdx < NumberOfSetInputs()) {
    560    if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
    561      aSurfaceOffset = mMatrix.GetTranslation().Round();
    562      // If the output will be a mask, then supply the color that should be
    563      // rendered with it.
    564      aColor =
    565          mPattern.GetPattern()->GetType() == PatternType::COLOR
    566              ? static_cast<const ColorPattern*>(mPattern.GetPattern())->mColor
    567              : DeviceColor(1, 1, 1, 1);
    568      aColor.a *= aOptions.mAlpha;
    569      return surface.forget();
    570    }
    571    return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions,
    572                                      aSurfaceOffset, aColor);
    573  }
    574  return nullptr;
    575 }
    576 
    577 int32_t FilterNodeGaussianBlurWebgl::InputIndex(
    578    uint32_t aInputEnumIndex) const {
    579  switch (aInputEnumIndex) {
    580    case IN_GAUSSIAN_BLUR_IN:
    581      return 0;
    582    default:
    583      return -1;
    584  }
    585 }
    586 
    587 void FilterNodeGaussianBlurWebgl::SetAttribute(uint32_t aIndex, float aValue) {
    588  MOZ_ASSERT(aIndex == ATT_GAUSSIAN_BLUR_STD_DEVIATION);
    589  // Match the FilterNodeSoftware blur limit.
    590  mStdDeviation = std::clamp(aValue, 0.0f, 100.0f);
    591  FilterNodeWebgl::SetAttribute(aIndex, aValue);
    592 }
    593 
    594 IntRect FilterNodeGaussianBlurWebgl::MapRectToSource(const IntRect& aRect,
    595                                                     const IntRect& aMax,
    596                                                     FilterNode* aSourceNode) {
    597  return MapInputRectToSource(IN_GAUSSIAN_BLUR_IN, aRect, aMax, aSourceNode);
    598 }
    599 
    600 void FilterNodeGaussianBlurWebgl::Draw(DrawTargetWebgl* aDT,
    601                                       const Rect& aSourceRect,
    602                                       const Point& aDestPoint,
    603                                       const DrawOptions& aOptions,
    604                                       FilterNodeWebgl* aParent) {
    605  ResolveInputs(aDT, true, aParent);
    606 
    607  uint32_t inputIdx = InputIndex(IN_GAUSSIAN_BLUR_IN);
    608  if (inputIdx < NumberOfSetInputs()) {
    609    bool success = false;
    610    Point surfaceOffset;
    611    DeviceColor color(1, 1, 1, 1);
    612    if (RefPtr<SourceSurface> surface =
    613            mInputFilters[inputIdx] ? mInputFilters[inputIdx]->DrawChild(
    614                                          this, aDT, aSourceRect, DrawOptions(),
    615                                          surfaceOffset, color)
    616                                    : mInputSurfaces[inputIdx]) {
    617      aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size()));
    618      IntRect surfRect = RoundedOut(
    619          Rect(surface->GetRect()).Intersect(aSourceRect - surfaceOffset));
    620      Point destOffset = aDestPoint + Point(surfRect.TopLeft()) +
    621                         surfaceOffset - aSourceRect.TopLeft();
    622      success = surfRect.IsEmpty() ||
    623                aDT->BlurSurface(mStdDeviation, surface, surfRect, destOffset,
    624                                 aOptions, color);
    625      aDT->PopClip();
    626    }
    627    if (!success) {
    628      FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions, aParent);
    629    }
    630  }
    631 }
    632 
    633 void FilterNodeColorMatrixWebgl::SetAttribute(uint32_t aIndex,
    634                                              const Matrix5x4& aValue) {
    635  MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
    636  mMatrix = aValue;
    637  FilterNodeWebgl::SetAttribute(aIndex, aValue);
    638 }
    639 
    640 void FilterNodeColorMatrixWebgl::SetAttribute(uint32_t aIndex,
    641                                              uint32_t aValue) {
    642  MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
    643  mAlphaMode = (AlphaMode)aValue;
    644  FilterNodeWebgl::SetAttribute(aIndex, aValue);
    645 }
    646 
    647 int32_t FilterNodeColorMatrixWebgl::InputIndex(uint32_t aInputEnumIndex) const {
    648  switch (aInputEnumIndex) {
    649    case IN_COLOR_MATRIX_IN:
    650      return 0;
    651    default:
    652      return -1;
    653  }
    654 }
    655 
    656 static bool DrawColorMatrixFilter(DrawTargetWebgl* aDT, const Point& aDestPoint,
    657                                  const DrawOptions& aOptions,
    658                                  const RefPtr<SourceSurface>& aSurface,
    659                                  const Rect& aSourceRect,
    660                                  const Point& aSurfaceOffset,
    661                                  const Matrix5x4& aMatrix,
    662                                  const DeviceColor& aColor) {
    663  IntRect surfRect = RoundedOut(
    664      Rect(aSurface->GetRect()).Intersect(aSourceRect - aSurfaceOffset));
    665  if (surfRect.IsEmpty()) {
    666    return true;
    667  }
    668  aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size()));
    669  Point destOffset = aDestPoint + Point(surfRect.TopLeft()) + aSurfaceOffset -
    670                     aSourceRect.TopLeft();
    671  bool success = true;
    672  if (aSurface->GetFormat() == SurfaceFormat::A8) {
    673    // Mask surfaces only use a solid color that is supplied outside the
    674    // surface. This color can be transformed without requiring a shader.
    675    Point4D outColor =
    676        Matrix4x4(aMatrix.components)
    677            .TransformPoint(Point4D(aColor.r, aColor.g, aColor.b, aColor.a)) +
    678        Point4D(aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54);
    679    SurfacePattern maskPattern(aSurface, ExtendMode::CLAMP,
    680                               Matrix::Translation(destOffset));
    681    if (!surfRect.IsEqualEdges(aSurface->GetRect())) {
    682      maskPattern.mSamplingRect = surfRect;
    683    }
    684    aDT->Mask(ColorPattern(
    685                  DeviceColor(outColor.x, outColor.y, outColor.z, outColor.w)),
    686              maskPattern, aOptions);
    687  } else {
    688    // For normal surfaces, try to use the color matrix filter shader.
    689    success =
    690        aDT->FilterSurface(aMatrix, aSurface, surfRect, destOffset, aOptions);
    691  }
    692  aDT->PopClip();
    693  return success;
    694 }
    695 
    696 bool FilterNodeColorMatrixWebgl::DrawAccel(DrawTargetWebgl* aDT,
    697                                           const Rect& aSourceRect,
    698                                           const Point& aDestPoint,
    699                                           const DrawOptions& aOptions,
    700                                           FilterNodeWebgl* aParent) {
    701  if (!aParent || mAlphaMode != ALPHA_MODE_STRAIGHT) {
    702    return false;
    703  }
    704  switch (aParent->GetType()) {
    705    case FilterType::PREMULTIPLY:
    706    case FilterType::CROP:
    707      break;
    708    default:
    709      return false;
    710  }
    711 
    712  ResolveInputs(aDT, true, aParent);
    713 
    714  uint32_t inputIdx = InputIndex(IN_COLOR_MATRIX_IN);
    715  if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) {
    716    FilterNodeWebgl* filter = mInputFilters[inputIdx];
    717    if (filter->GetType() == FilterType::UNPREMULTIPLY) {
    718      bool success = false;
    719      Point surfaceOffset;
    720      DeviceColor color;
    721      if (RefPtr<SourceSurface> surface = filter->DrawChild(
    722              this, aDT, aSourceRect, DrawOptions(), surfaceOffset, color)) {
    723        success =
    724            DrawColorMatrixFilter(aDT, aDestPoint, aOptions, surface,
    725                                  aSourceRect, surfaceOffset, mMatrix, color);
    726      }
    727      return success;
    728    }
    729  }
    730  return false;
    731 }
    732 
    733 void FilterNodeComponentTransferWebgl::SetAttribute(uint32_t aIndex,
    734                                                    bool aValue) {
    735  switch (aIndex) {
    736    case ATT_TRANSFER_DISABLE_R:
    737      mDisableR = aValue;
    738      break;
    739    case ATT_TRANSFER_DISABLE_G:
    740      mDisableG = aValue;
    741      break;
    742    case ATT_TRANSFER_DISABLE_B:
    743      mDisableB = aValue;
    744      break;
    745    case ATT_TRANSFER_DISABLE_A:
    746      mDisableA = aValue;
    747      break;
    748    default:
    749      gfxDevCrash(LogReason::FilterInputError)
    750          << "FilterNodeComponentTransferWebgl: Invalid attribute " << aIndex;
    751      break;
    752  }
    753  FilterNodeWebgl::SetAttribute(aIndex, aValue);
    754 }
    755 
    756 int32_t FilterNodeComponentTransferWebgl::InputIndex(
    757    uint32_t aInputEnumIndex) const {
    758  switch (aInputEnumIndex) {
    759    case IN_TRANSFER_IN:
    760      return 0;
    761    default:
    762      return -1;
    763  }
    764 }
    765 
    766 bool FilterNodeComponentTransferWebgl::DrawAccel(DrawTargetWebgl* aDT,
    767                                                 const Rect& aSourceRect,
    768                                                 const Point& aDestPoint,
    769                                                 const DrawOptions& aOptions,
    770                                                 FilterNodeWebgl* aParent) {
    771  if (!aParent) {
    772    return false;
    773  }
    774  switch (aParent->GetType()) {
    775    case FilterType::PREMULTIPLY:
    776    case FilterType::CROP:
    777      break;
    778    default:
    779      return false;
    780  }
    781 
    782  Matrix5x4 mat5x4;
    783  if (!ToColorMatrix(mat5x4)) {
    784    return false;
    785  }
    786 
    787  ResolveInputs(aDT, true, aParent);
    788 
    789  uint32_t inputIdx = InputIndex(IN_TRANSFER_IN);
    790  if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) {
    791    FilterNodeWebgl* filter = mInputFilters[inputIdx];
    792    if (filter->GetType() == FilterType::UNPREMULTIPLY) {
    793      bool success = false;
    794      Point surfaceOffset;
    795      DeviceColor color;
    796      if (RefPtr<SourceSurface> surface = filter->DrawChild(
    797              this, aDT, aSourceRect, DrawOptions(), surfaceOffset, color)) {
    798        success =
    799            DrawColorMatrixFilter(aDT, aDestPoint, aOptions, surface,
    800                                  aSourceRect, surfaceOffset, mat5x4, color);
    801      }
    802      return success;
    803    }
    804  }
    805  return false;
    806 }
    807 
    808 void FilterNodeLinearTransferWebgl::SetAttribute(uint32_t aIndex,
    809                                                 Float aValue) {
    810  switch (aIndex) {
    811    case ATT_LINEAR_TRANSFER_SLOPE_R:
    812      mSlope.r = aValue;
    813      break;
    814    case ATT_LINEAR_TRANSFER_INTERCEPT_R:
    815      mIntercept.r = aValue;
    816      break;
    817    case ATT_LINEAR_TRANSFER_SLOPE_G:
    818      mSlope.g = aValue;
    819      break;
    820    case ATT_LINEAR_TRANSFER_INTERCEPT_G:
    821      mIntercept.g = aValue;
    822      break;
    823    case ATT_LINEAR_TRANSFER_SLOPE_B:
    824      mSlope.b = aValue;
    825      break;
    826    case ATT_LINEAR_TRANSFER_INTERCEPT_B:
    827      mIntercept.b = aValue;
    828      break;
    829    case ATT_LINEAR_TRANSFER_SLOPE_A:
    830      mSlope.a = aValue;
    831      break;
    832    case ATT_LINEAR_TRANSFER_INTERCEPT_A:
    833      mIntercept.a = aValue;
    834      break;
    835    default:
    836      MOZ_ASSERT(false);
    837      break;
    838  }
    839  FilterNodeWebgl::SetAttribute(aIndex, aValue);
    840 }
    841 
    842 bool FilterNodeLinearTransferWebgl::ToColorMatrix(Matrix5x4& aMatrix) const {
    843  // Linear filters can be interpreted as a scale and translation matrix.
    844  aMatrix = Matrix5x4();
    845  if (!mDisableR) {
    846    aMatrix._11 = mSlope.r;
    847    aMatrix._51 = mIntercept.r;
    848  }
    849  if (!mDisableG) {
    850    aMatrix._22 = mSlope.g;
    851    aMatrix._52 = mIntercept.g;
    852  }
    853  if (!mDisableB) {
    854    aMatrix._33 = mSlope.b;
    855    aMatrix._53 = mIntercept.b;
    856  }
    857  if (!mDisableA) {
    858    aMatrix._44 = mSlope.a;
    859    aMatrix._54 = mIntercept.a;
    860  }
    861  return true;
    862 }
    863 
    864 void FilterNodeTableTransferWebgl::SetAttribute(uint32_t aIndex,
    865                                                const Float* aValues,
    866                                                uint32_t aSize) {
    867  std::vector<Float> table(aValues, aValues + aSize);
    868  switch (aIndex) {
    869    case ATT_TABLE_TRANSFER_TABLE_R:
    870      mTableR = table;
    871      break;
    872    case ATT_TABLE_TRANSFER_TABLE_G:
    873      mTableG = table;
    874      break;
    875    case ATT_TABLE_TRANSFER_TABLE_B:
    876      mTableB = table;
    877      break;
    878    case ATT_TABLE_TRANSFER_TABLE_A:
    879      mTableA = table;
    880      break;
    881    default:
    882      MOZ_ASSERT(false);
    883      break;
    884  }
    885  FilterNodeWebgl::SetAttribute(aIndex, aValues, aSize);
    886 }
    887 
    888 bool FilterNodeTableTransferWebgl::ToColorMatrix(Matrix5x4& aMatrix) const {
    889  // 2 element table transfers are effectively linear transfers. These can be
    890  // interpreted as a scale and translation matrix.
    891  if ((mDisableR || mTableR.size() == 2) &&
    892      (mDisableG || mTableG.size() == 2) &&
    893      (mDisableB || mTableB.size() == 2) &&
    894      (mDisableA || mTableA.size() == 2)) {
    895    aMatrix = Matrix5x4();
    896    if (!mDisableR) {
    897      aMatrix._11 = mTableR[1] - mTableR[0];
    898      aMatrix._51 = mTableR[0];
    899    }
    900    if (!mDisableG) {
    901      aMatrix._22 = mTableG[1] - mTableG[0];
    902      aMatrix._52 = mTableG[0];
    903    }
    904    if (!mDisableB) {
    905      aMatrix._33 = mTableB[1] - mTableB[0];
    906      aMatrix._53 = mTableB[0];
    907    }
    908    if (!mDisableA) {
    909      aMatrix._44 = mTableA[1] - mTableA[0];
    910      aMatrix._54 = mTableA[0];
    911    }
    912    return true;
    913  }
    914  return true;
    915 }
    916 
    917 int32_t FilterNodePremultiplyWebgl::InputIndex(uint32_t aInputEnumIndex) const {
    918  switch (aInputEnumIndex) {
    919    case IN_PREMULTIPLY_IN:
    920      return 0;
    921    default:
    922      return -1;
    923  }
    924 }
    925 
    926 void FilterNodePremultiplyWebgl::Draw(DrawTargetWebgl* aDT,
    927                                      const Rect& aSourceRect,
    928                                      const Point& aDestPoint,
    929                                      const DrawOptions& aOptions,
    930                                      FilterNodeWebgl* aParent) {
    931  uint32_t inputIdx = InputIndex(IN_PREMULTIPLY_IN);
    932  if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) {
    933    FilterNodeWebgl* filter = mInputFilters[inputIdx];
    934    switch (filter->GetType()) {
    935      case FilterType::CROP:
    936      case FilterType::COLOR_MATRIX:
    937      case FilterType::LINEAR_TRANSFER:
    938      case FilterType::TABLE_TRANSFER:
    939        // For color matrix filters, they will normally be preceded by a premul
    940        // filter. In certain cases, after the premul there is a crop before the
    941        // color matrix filter is actually evaluated. Here, we use DrawAccel to
    942        // only handle the filter if it can actually be accelerated, otherwise
    943        // falling back to a software color matrix filter below.
    944        if (filter->DrawAccel(aDT, aSourceRect, aDestPoint, aOptions, this)) {
    945          return;
    946        }
    947        break;
    948      default:
    949        break;
    950    }
    951  }
    952  FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions, aParent);
    953 }
    954 
    955 int32_t FilterNodeUnpremultiplyWebgl::InputIndex(
    956    uint32_t aInputEnumIndex) const {
    957  switch (aInputEnumIndex) {
    958    case IN_UNPREMULTIPLY_IN:
    959      return 0;
    960    default:
    961      return -1;
    962  }
    963 }
    964 
    965 already_AddRefed<SourceSurface> FilterNodeUnpremultiplyWebgl::DrawChild(
    966    FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect,
    967    const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) {
    968  switch (aParent->GetType()) {
    969    case FilterType::COLOR_MATRIX:
    970    case FilterType::LINEAR_TRANSFER:
    971    case FilterType::TABLE_TRANSFER:
    972      // Unpremul should always be the child of a color matrix filter.
    973      break;
    974    default:
    975      return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions,
    976                                        aSurfaceOffset, aColor);
    977  }
    978 
    979  ResolveInputs(aDT, true, aParent);
    980 
    981  uint32_t inputIdx = InputIndex(IN_UNPREMULTIPLY_IN);
    982  if (inputIdx < NumberOfSetInputs()) {
    983    if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
    984      if (RefPtr<SourceSurface> surface = filter->DrawChild(
    985              this, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor)) {
    986        return surface.forget();
    987      }
    988    } else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
    989      aColor = DeviceColor(1, 1, 1, aOptions.mAlpha);
    990      return surface.forget();
    991    }
    992    return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions,
    993                                      aSurfaceOffset, aColor);
    994  }
    995  return nullptr;
    996 }
    997 
    998 int32_t FilterNodeOpacityWebgl::InputIndex(uint32_t aInputEnumIndex) const {
    999  switch (aInputEnumIndex) {
   1000    case IN_OPACITY_IN:
   1001      return 0;
   1002    default:
   1003      return -1;
   1004  }
   1005 }
   1006 
   1007 void FilterNodeOpacityWebgl::SetAttribute(uint32_t aIndex, Float aValue) {
   1008  MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE);
   1009  mValue = aValue;
   1010  FilterNodeWebgl::SetAttribute(aIndex, aValue);
   1011 }
   1012 
   1013 void FilterNodeOpacityWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
   1014                                  const Point& aDestPoint,
   1015                                  const DrawOptions& aOptions,
   1016                                  FilterNodeWebgl* aParent) {
   1017  ResolveInputs(aDT, true, aParent);
   1018 
   1019  uint32_t inputIdx = InputIndex(IN_OPACITY_IN);
   1020  if (inputIdx < NumberOfSetInputs()) {
   1021    // Opacity filters need only modify the DrawOptions alpha value.
   1022    DrawOptions options(aOptions);
   1023    options.mAlpha *= mValue;
   1024    if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
   1025      filter->Draw(aDT, aSourceRect, aDestPoint, options, this);
   1026    } else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
   1027      aDT->DrawSurface(surface, Rect(aDestPoint, aSourceRect.Size()),
   1028                       aSourceRect, DrawSurfaceOptions(), options);
   1029    }
   1030  }
   1031 }
   1032 
   1033 already_AddRefed<SourceSurface> FilterNodeOpacityWebgl::DrawChild(
   1034    FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect,
   1035    const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) {
   1036  ResolveInputs(aDT, true, aParent);
   1037 
   1038  uint32_t inputIdx = InputIndex(IN_OPACITY_IN);
   1039  if (inputIdx < NumberOfSetInputs()) {
   1040    if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
   1041      // Opacity filters need only modify he DrawOptions alpha value.
   1042      DrawOptions options(aOptions);
   1043      options.mAlpha *= mValue;
   1044      return filter->DrawChild(this, aDT, aSourceRect, options, aSurfaceOffset,
   1045                               aColor);
   1046    }
   1047    return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions,
   1048                                      aSurfaceOffset, aColor);
   1049  }
   1050  return nullptr;
   1051 }
   1052 
   1053 }  // namespace mozilla::gfx