tor-browser

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

FilterNodeSoftware.cpp (123992B)


      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 <cmath>
      8 #include "DataSurfaceHelpers.h"
      9 #include "FilterNodeSoftware.h"
     10 #include "2D.h"
     11 #include "Tools.h"
     12 #include "Blur.h"
     13 #include <map>
     14 #include "FilterProcessing.h"
     15 #include "Logging.h"
     16 #include "mozilla/PodOperations.h"
     17 
     18 // #define DEBUG_DUMP_SURFACES
     19 
     20 #ifdef DEBUG_DUMP_SURFACES
     21 #  include "gfxUtils.h"  // not part of Moz2D
     22 #endif
     23 
     24 namespace mozilla {
     25 namespace gfx {
     26 
     27 namespace {
     28 
     29 /**
     30 * This class provides a way to get a pow() results in constant-time. It works
     31 * by caching 257 ((1 << sCacheIndexPrecisionBits) + 1) values for bases between
     32 * 0 and 1 and a fixed exponent.
     33 **/
     34 class PowCache {
     35 public:
     36  PowCache() : mNumPowTablePreSquares(-1) {}
     37 
     38  void CacheForExponent(Float aExponent) {
     39    // Since we are in the world where we only care about
     40    // input and results in [0,1], there is no point in
     41    // dealing with non-positive exponents.
     42    if (aExponent <= 0) {
     43      mNumPowTablePreSquares = -1;
     44      return;
     45    }
     46    int numPreSquares = 0;
     47    while (numPreSquares < 5 && aExponent > (1 << (numPreSquares + 2))) {
     48      numPreSquares++;
     49    }
     50    mNumPowTablePreSquares = numPreSquares;
     51    for (size_t i = 0; i < sCacheSize; i++) {
     52      // sCacheSize is chosen in such a way that a takes values
     53      // from 0.0 to 1.0 inclusive.
     54      Float a = i / Float(1 << sCacheIndexPrecisionBits);
     55      MOZ_ASSERT(0.0f <= a && a <= 1.0f,
     56                 "We only want to cache for bases between 0 and 1.");
     57 
     58      for (int j = 0; j < mNumPowTablePreSquares; j++) {
     59        a = sqrt(a);
     60      }
     61      uint32_t cachedInt = pow(a, aExponent) * (1 << sOutputIntPrecisionBits);
     62      MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)),
     63                 "mPowCache integer type too small");
     64 
     65      mPowTable[i] = cachedInt;
     66    }
     67  }
     68 
     69  // Only call Pow() if HasPowerTable() would return true, to avoid complicating
     70  // this code and having it just return (1 << sOutputIntPrecisionBits))
     71  uint16_t Pow(uint16_t aBase) {
     72    MOZ_ASSERT(HasPowerTable());
     73    // Results should be similar to what the following code would produce:
     74    // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
     75    // return uint16_t(pow(x, aExponent) * (1 << sOutputIntPrecisionBits));
     76 
     77    MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits),
     78               "aBase needs to be between 0 and 1!");
     79 
     80    uint32_t a = aBase;
     81    for (int j = 0; j < mNumPowTablePreSquares; j++) {
     82      a = a * a >> sInputIntPrecisionBits;
     83    }
     84    uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits);
     85    MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access");
     86    return mPowTable[i];
     87  }
     88 
     89  static const int sInputIntPrecisionBits = 15;
     90  static const int sOutputIntPrecisionBits = 15;
     91  static const int sCacheIndexPrecisionBits = 8;
     92 
     93  inline bool HasPowerTable() const { return mNumPowTablePreSquares >= 0; }
     94 
     95 private:
     96  static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
     97 
     98  int mNumPowTablePreSquares;
     99  uint16_t mPowTable[sCacheSize];
    100 };
    101 
    102 class PointLightSoftware {
    103 public:
    104  bool SetAttribute(uint32_t aIndex, Float) { return false; }
    105  bool SetAttribute(uint32_t aIndex, const Point3D&);
    106  void Prepare() {}
    107  Point3D GetVectorToLight(const Point3D& aTargetPoint);
    108  uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight);
    109 
    110 private:
    111  Point3D mPosition;
    112 };
    113 
    114 class SpotLightSoftware {
    115 public:
    116  SpotLightSoftware();
    117  bool SetAttribute(uint32_t aIndex, Float);
    118  bool SetAttribute(uint32_t aIndex, const Point3D&);
    119  void Prepare();
    120  Point3D GetVectorToLight(const Point3D& aTargetPoint);
    121  uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight);
    122 
    123 private:
    124  Point3D mPosition;
    125  Point3D mPointsAt;
    126  Point3D mVectorFromFocusPointToLight;
    127  Float mSpecularFocus;
    128  Float mLimitingConeAngle;
    129  Float mLimitingConeCos;
    130  PowCache mPowCache;
    131 };
    132 
    133 class DistantLightSoftware {
    134 public:
    135  DistantLightSoftware();
    136  bool SetAttribute(uint32_t aIndex, Float);
    137  bool SetAttribute(uint32_t aIndex, const Point3D&) { return false; }
    138  void Prepare();
    139  Point3D GetVectorToLight(const Point3D& aTargetPoint);
    140  uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight);
    141 
    142 private:
    143  Float mAzimuth;
    144  Float mElevation;
    145  Point3D mVectorToLight;
    146 };
    147 
    148 class DiffuseLightingSoftware {
    149 public:
    150  DiffuseLightingSoftware();
    151  bool SetAttribute(uint32_t aIndex, Float);
    152  void Prepare() {}
    153  uint32_t LightPixel(const Point3D& aNormal, const Point3D& aVectorToLight,
    154                      uint32_t aColor);
    155 
    156 private:
    157  Float mDiffuseConstant;
    158 };
    159 
    160 class SpecularLightingSoftware {
    161 public:
    162  SpecularLightingSoftware();
    163  bool SetAttribute(uint32_t aIndex, Float);
    164  void Prepare();
    165  uint32_t LightPixel(const Point3D& aNormal, const Point3D& aVectorToLight,
    166                      uint32_t aColor);
    167 
    168 private:
    169  Float mSpecularConstant;
    170  Float mSpecularExponent;
    171  uint32_t mSpecularConstantInt;
    172  PowCache mPowCache;
    173 };
    174 
    175 }  // unnamed namespace
    176 
    177 // from xpcom/ds/nsMathUtils.h
    178 static int32_t NS_lround(double x) {
    179  return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
    180 }
    181 
    182 static already_AddRefed<DataSourceSurface> CloneAligned(
    183    DataSourceSurface* aSource) {
    184  return CreateDataSourceSurfaceByCloning(aSource);
    185 }
    186 
    187 static void FillRectWithPixel(DataSourceSurface* aSurface,
    188                              const IntRect& aFillRect, IntPoint aPixelPos) {
    189  MOZ_ASSERT(!aFillRect.Overflows());
    190  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
    191             "aFillRect needs to be completely inside the surface");
    192  MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
    193             "aPixelPos needs to be inside the surface");
    194 
    195  DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
    196  if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
    197    return;
    198  }
    199  uint8_t* sourcePixelData =
    200      DataAtOffset(aSurface, surfMap.GetMappedSurface(), aPixelPos);
    201  uint8_t* data =
    202      DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
    203  int bpp = BytesPerPixel(aSurface->GetFormat());
    204 
    205  // Fill the first row by hand.
    206  if (bpp == 4) {
    207    uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
    208    for (int32_t x = 0; x < aFillRect.Width(); x++) {
    209      *((uint32_t*)data + x) = sourcePixel;
    210    }
    211  } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
    212    uint8_t sourcePixel = *sourcePixelData;
    213    memset(data, sourcePixel, aFillRect.Width());
    214  }
    215 
    216  // Copy the first row into the other rows.
    217  for (int32_t y = 1; y < aFillRect.Height(); y++) {
    218    PodCopy(data + y * surfMap.GetStride(), data, aFillRect.Width() * bpp);
    219  }
    220 }
    221 
    222 static void FillRectWithVerticallyRepeatingHorizontalStrip(
    223    DataSourceSurface* aSurface, const IntRect& aFillRect,
    224    const IntRect& aSampleRect) {
    225  MOZ_ASSERT(!aFillRect.Overflows());
    226  MOZ_ASSERT(!aSampleRect.Overflows());
    227  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
    228             "aFillRect needs to be completely inside the surface");
    229  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
    230             "aSampleRect needs to be completely inside the surface");
    231 
    232  DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
    233  if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
    234    return;
    235  }
    236 
    237  uint8_t* sampleData =
    238      DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
    239  uint8_t* data =
    240      DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
    241  if (BytesPerPixel(aSurface->GetFormat()) == 4) {
    242    for (int32_t y = 0; y < aFillRect.Height(); y++) {
    243      PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.Width());
    244      data += surfMap.GetStride();
    245    }
    246  } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
    247    for (int32_t y = 0; y < aFillRect.Height(); y++) {
    248      PodCopy(data, sampleData, aFillRect.Width());
    249      data += surfMap.GetStride();
    250    }
    251  }
    252 }
    253 
    254 static void FillRectWithHorizontallyRepeatingVerticalStrip(
    255    DataSourceSurface* aSurface, const IntRect& aFillRect,
    256    const IntRect& aSampleRect) {
    257  MOZ_ASSERT(!aFillRect.Overflows());
    258  MOZ_ASSERT(!aSampleRect.Overflows());
    259  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
    260             "aFillRect needs to be completely inside the surface");
    261  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
    262             "aSampleRect needs to be completely inside the surface");
    263 
    264  DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
    265  if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
    266    return;
    267  }
    268 
    269  uint8_t* sampleData =
    270      DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
    271  uint8_t* data =
    272      DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
    273  if (BytesPerPixel(aSurface->GetFormat()) == 4) {
    274    for (int32_t y = 0; y < aFillRect.Height(); y++) {
    275      int32_t sampleColor = *((uint32_t*)sampleData);
    276      for (int32_t x = 0; x < aFillRect.Width(); x++) {
    277        *((uint32_t*)data + x) = sampleColor;
    278      }
    279      data += surfMap.GetStride();
    280      sampleData += surfMap.GetStride();
    281    }
    282  } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
    283    for (int32_t y = 0; y < aFillRect.Height(); y++) {
    284      uint8_t sampleColor = *sampleData;
    285      memset(data, sampleColor, aFillRect.Width());
    286      data += surfMap.GetStride();
    287      sampleData += surfMap.GetStride();
    288    }
    289  }
    290 }
    291 
    292 static void DuplicateEdges(DataSourceSurface* aSurface,
    293                           const IntRect& aFromRect) {
    294  MOZ_ASSERT(!aFromRect.Overflows());
    295  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
    296             "aFromRect needs to be completely inside the surface");
    297 
    298  IntSize size = aSurface->GetSize();
    299  IntRect fill;
    300  IntRect sampleRect;
    301  for (int32_t ix = 0; ix < 3; ix++) {
    302    switch (ix) {
    303      case 0:
    304        fill.SetRectX(0, aFromRect.X());
    305        sampleRect.SetRectX(fill.XMost(), 1);
    306        break;
    307      case 1:
    308        fill.SetRectX(aFromRect.X(), aFromRect.Width());
    309        sampleRect.SetRectX(fill.X(), fill.Width());
    310        break;
    311      case 2:
    312        fill.MoveToX(aFromRect.XMost());
    313        fill.SetRightEdge(size.width);
    314        sampleRect.SetRectX(fill.X() - 1, 1);
    315        break;
    316    }
    317    if (fill.Width() <= 0) {
    318      continue;
    319    }
    320    bool xIsMiddle = (ix == 1);
    321    for (int32_t iy = 0; iy < 3; iy++) {
    322      switch (iy) {
    323        case 0:
    324          fill.SetRectY(0, aFromRect.Y());
    325          sampleRect.SetRectY(fill.YMost(), 1);
    326          break;
    327        case 1:
    328          fill.SetRectY(aFromRect.Y(), aFromRect.Height());
    329          sampleRect.SetRectY(fill.Y(), fill.Height());
    330          break;
    331        case 2:
    332          fill.MoveToY(aFromRect.YMost());
    333          fill.SetBottomEdge(size.height);
    334          sampleRect.SetRectY(fill.Y() - 1, 1);
    335          break;
    336      }
    337      if (fill.Height() <= 0) {
    338        continue;
    339      }
    340      bool yIsMiddle = (iy == 1);
    341      if (!xIsMiddle && !yIsMiddle) {
    342        // Corner
    343        FillRectWithPixel(aSurface, fill, sampleRect.TopLeft());
    344      }
    345      if (xIsMiddle && !yIsMiddle) {
    346        // Top middle or bottom middle
    347        FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill,
    348                                                       sampleRect);
    349      }
    350      if (!xIsMiddle && yIsMiddle) {
    351        // Left middle or right middle
    352        FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill,
    353                                                       sampleRect);
    354      }
    355    }
    356  }
    357 }
    358 
    359 static IntPoint TileIndex(const IntRect& aFirstTileRect,
    360                          const IntPoint& aPoint) {
    361  return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.X()) /
    362                                aFirstTileRect.Width())),
    363                  int32_t(floor(double(aPoint.y - aFirstTileRect.Y()) /
    364                                aFirstTileRect.Height())));
    365 }
    366 
    367 static void TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget,
    368                        const IntPoint& aOffset) {
    369  IntRect sourceRect(aOffset, aSource->GetSize());
    370  IntRect targetRect(IntPoint(0, 0), aTarget->GetSize());
    371  IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft());
    372  IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight());
    373 
    374  for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
    375    for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
    376      IntPoint destPoint(sourceRect.X() + ix * sourceRect.Width(),
    377                         sourceRect.Y() + iy * sourceRect.Height());
    378      IntRect destRect(destPoint, sourceRect.Size());
    379      destRect = destRect.Intersect(targetRect);
    380      IntRect srcRect = destRect - destPoint;
    381      CopyRect(aSource, aTarget, srcRect, destRect.TopLeft());
    382    }
    383  }
    384 }
    385 
    386 static already_AddRefed<DataSourceSurface> GetDataSurfaceInRect(
    387    SourceSurface* aSurface, const IntRect& aSurfaceRect,
    388    const IntRect& aDestRect, ConvolveMatrixEdgeMode aEdgeMode) {
    389  MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize()
    390                      : aSurfaceRect.IsEmpty());
    391 
    392  if (aSurfaceRect.Overflows() || aDestRect.Overflows()) {
    393    // We can't rely on the intersection calculations below to make sense when
    394    // XMost() or YMost() overflow. Bail out.
    395    return nullptr;
    396  }
    397 
    398  IntRect sourceRect = aSurfaceRect;
    399 
    400  if (sourceRect.IsEqualEdges(aDestRect)) {
    401    return aSurface ? aSurface->GetDataSurface() : nullptr;
    402  }
    403 
    404  IntRect intersect = sourceRect.Intersect(aDestRect);
    405 
    406  // create rects that are in surface local space.
    407  IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
    408  IntRect intersectInDestSpace = intersect - aDestRect.TopLeft();
    409  SurfaceFormat format =
    410      aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8);
    411 
    412  RefPtr<DataSourceSurface> target =
    413      Factory::CreateDataSourceSurface(aDestRect.Size(), format, true);
    414  if (MOZ2D_WARN_IF(!target)) {
    415    return nullptr;
    416  }
    417 
    418  if (!aSurface) {
    419    return target.forget();
    420  }
    421 
    422  RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
    423  MOZ_ASSERT(dataSource);
    424 
    425  if (aEdgeMode == EDGE_MODE_WRAP) {
    426    TileSurface(dataSource, target, intersectInDestSpace.TopLeft());
    427    return target.forget();
    428  }
    429 
    430  CopyRect(dataSource, target, intersectInSourceSpace,
    431           intersectInDestSpace.TopLeft());
    432 
    433  if (aEdgeMode == EDGE_MODE_DUPLICATE) {
    434    DuplicateEdges(target, intersectInDestSpace);
    435  }
    436 
    437  return target.forget();
    438 }
    439 
    440 /* static */
    441 already_AddRefed<FilterNode> FilterNodeSoftware::Create(FilterType aType) {
    442  RefPtr<FilterNodeSoftware> filter;
    443  switch (aType) {
    444    case FilterType::BLEND:
    445      filter = new FilterNodeBlendSoftware();
    446      break;
    447    case FilterType::TRANSFORM:
    448      filter = new FilterNodeTransformSoftware();
    449      break;
    450    case FilterType::MORPHOLOGY:
    451      filter = new FilterNodeMorphologySoftware();
    452      break;
    453    case FilterType::COLOR_MATRIX:
    454      filter = new FilterNodeColorMatrixSoftware();
    455      break;
    456    case FilterType::FLOOD:
    457      filter = new FilterNodeFloodSoftware();
    458      break;
    459    case FilterType::TILE:
    460      filter = new FilterNodeTileSoftware();
    461      break;
    462    case FilterType::TABLE_TRANSFER:
    463      filter = new FilterNodeTableTransferSoftware();
    464      break;
    465    case FilterType::DISCRETE_TRANSFER:
    466      filter = new FilterNodeDiscreteTransferSoftware();
    467      break;
    468    case FilterType::LINEAR_TRANSFER:
    469      filter = new FilterNodeLinearTransferSoftware();
    470      break;
    471    case FilterType::GAMMA_TRANSFER:
    472      filter = new FilterNodeGammaTransferSoftware();
    473      break;
    474    case FilterType::CONVOLVE_MATRIX:
    475      filter = new FilterNodeConvolveMatrixSoftware();
    476      break;
    477    case FilterType::DISPLACEMENT_MAP:
    478      filter = new FilterNodeDisplacementMapSoftware();
    479      break;
    480    case FilterType::TURBULENCE:
    481      filter = new FilterNodeTurbulenceSoftware();
    482      break;
    483    case FilterType::ARITHMETIC_COMBINE:
    484      filter = new FilterNodeArithmeticCombineSoftware();
    485      break;
    486    case FilterType::COMPOSITE:
    487      filter = new FilterNodeCompositeSoftware();
    488      break;
    489    case FilterType::GAUSSIAN_BLUR:
    490      filter = new FilterNodeGaussianBlurSoftware();
    491      break;
    492    case FilterType::DIRECTIONAL_BLUR:
    493      filter = new FilterNodeDirectionalBlurSoftware();
    494      break;
    495    case FilterType::CROP:
    496      filter = new FilterNodeCropSoftware();
    497      break;
    498    case FilterType::PREMULTIPLY:
    499      filter = new FilterNodePremultiplySoftware();
    500      break;
    501    case FilterType::UNPREMULTIPLY:
    502      filter = new FilterNodeUnpremultiplySoftware();
    503      break;
    504    case FilterType::OPACITY:
    505      filter = new FilterNodeOpacitySoftware();
    506      break;
    507    case FilterType::POINT_DIFFUSE:
    508      filter = new FilterNodeLightingSoftware<PointLightSoftware,
    509                                              DiffuseLightingSoftware>(
    510          "FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
    511      break;
    512    case FilterType::POINT_SPECULAR:
    513      filter = new FilterNodeLightingSoftware<PointLightSoftware,
    514                                              SpecularLightingSoftware>(
    515          "FilterNodeLightingSoftware<PointLight, SpecularLighting>");
    516      break;
    517    case FilterType::SPOT_DIFFUSE:
    518      filter = new FilterNodeLightingSoftware<SpotLightSoftware,
    519                                              DiffuseLightingSoftware>(
    520          "FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
    521      break;
    522    case FilterType::SPOT_SPECULAR:
    523      filter = new FilterNodeLightingSoftware<SpotLightSoftware,
    524                                              SpecularLightingSoftware>(
    525          "FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
    526      break;
    527    case FilterType::DISTANT_DIFFUSE:
    528      filter = new FilterNodeLightingSoftware<DistantLightSoftware,
    529                                              DiffuseLightingSoftware>(
    530          "FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
    531      break;
    532    case FilterType::DISTANT_SPECULAR:
    533      filter = new FilterNodeLightingSoftware<DistantLightSoftware,
    534                                              SpecularLightingSoftware>(
    535          "FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
    536      break;
    537  }
    538  return filter.forget();
    539 }
    540 
    541 void FilterNodeSoftware::Draw(DrawTarget* aDrawTarget, const Rect& aSourceRect,
    542                              const Point& aDestPoint,
    543                              const DrawOptions& aOptions) {
    544 #ifdef DEBUG_DUMP_SURFACES
    545  printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n",
    546         GetName());
    547 #endif
    548 
    549  Rect renderRect = aSourceRect;
    550  renderRect.RoundOut();
    551  IntRect renderIntRect;
    552  if (!renderRect.ToIntRect(&renderIntRect)) {
    553 #ifdef DEBUG_DUMP_SURFACES
    554    printf("render rect overflowed, not painting anything\n");
    555    printf("</pre>\n");
    556 #endif
    557    return;
    558  }
    559 
    560  IntRect outputRect = GetOutputRectInRect(renderIntRect);
    561  if (outputRect.Overflows()) {
    562 #ifdef DEBUG_DUMP_SURFACES
    563    printf("output rect overflowed, not painting anything\n");
    564    printf("</pre>\n");
    565 #endif
    566    return;
    567  }
    568 
    569  RefPtr<DataSourceSurface> result;
    570  if (!outputRect.IsEmpty()) {
    571    result = GetOutput(outputRect);
    572  }
    573 
    574  if (!result) {
    575    // Null results are allowed and treated as transparent. Don't draw anything.
    576 #ifdef DEBUG_DUMP_SURFACES
    577    printf("output returned null\n");
    578    printf("</pre>\n");
    579 #endif
    580    return;
    581  }
    582 
    583 #ifdef DEBUG_DUMP_SURFACES
    584  printf("output from %s:\n", GetName());
    585  printf("<img src='");
    586  gfxUtils::DumpAsDataURI(result);
    587  printf("'>\n");
    588  printf("</pre>\n");
    589 #endif
    590 
    591  Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft();
    592  Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect);
    593  Rect renderedDestRect = renderedSourceRect + sourceToDestOffset;
    594  if (result->GetFormat() == SurfaceFormat::A8) {
    595    // Interpret the result as having implicitly black color channels.
    596    aDrawTarget->PushClipRect(renderedDestRect);
    597    aDrawTarget->MaskSurface(
    598        ColorPattern(DeviceColor::MaskOpaqueBlack()), result,
    599        Point(outputRect.TopLeft()) + sourceToDestOffset, aOptions);
    600    aDrawTarget->PopClip();
    601  } else {
    602    aDrawTarget->DrawSurface(result, renderedDestRect,
    603                             renderedSourceRect - Point(outputRect.TopLeft()),
    604                             DrawSurfaceOptions(), aOptions);
    605  }
    606 }
    607 
    608 already_AddRefed<DataSourceSurface> FilterNodeSoftware::GetOutput(
    609    const IntRect& aRect) {
    610  MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
    611 
    612  if (aRect.Overflows()) {
    613    return nullptr;
    614  }
    615 
    616  IntRect cachedRect;
    617  IntRect requestedRect;
    618  RefPtr<DataSourceSurface> cachedOutput;
    619 
    620  // Retrieve a cached surface if we have one and it can
    621  // satisfy this request, or else request a rect we will compute and cache
    622  if (!mCachedRect.Contains(aRect)) {
    623    RequestRect(aRect);
    624    requestedRect = mRequestedRect;
    625  } else {
    626    MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
    627    cachedRect = mCachedRect;
    628    cachedOutput = mCachedOutput;
    629  }
    630 
    631  if (!cachedOutput) {
    632    // Compute the output
    633    cachedOutput = Render(requestedRect);
    634 
    635    // Update the cache for future requests
    636    mCachedOutput = cachedOutput;
    637    if (!mCachedOutput) {
    638      mCachedRect = IntRect();
    639      mRequestedRect = IntRect();
    640      return nullptr;
    641    }
    642    mCachedRect = requestedRect;
    643    mRequestedRect = IntRect();
    644 
    645    cachedRect = mCachedRect;
    646  }
    647 
    648  return GetDataSurfaceInRect(cachedOutput, cachedRect, aRect, EDGE_MODE_NONE);
    649 }
    650 
    651 void FilterNodeSoftware::RequestRect(const IntRect& aRect) {
    652  if (mRequestedRect.Contains(aRect)) {
    653    // Bail out now. Otherwise pathological filters can spend time exponential
    654    // in the number of primitives, e.g. if each primitive takes the
    655    // previous primitive as its two inputs.
    656    return;
    657  }
    658  mRequestedRect = mRequestedRect.Union(aRect);
    659  RequestFromInputsForRect(aRect);
    660 }
    661 
    662 IntRect FilterNodeSoftware::MapInputRectToSource(uint32_t aInputEnumIndex,
    663                                                 const IntRect& aRect,
    664                                                 const IntRect& aMax,
    665                                                 FilterNode* aSourceNode) {
    666  int32_t inputIndex = InputIndex(aInputEnumIndex);
    667  if (inputIndex < 0) {
    668    gfxDevCrash(LogReason::FilterInputError)
    669        << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
    670    return aMax;
    671  }
    672  if ((uint32_t)inputIndex < NumberOfSetInputs()) {
    673    RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
    674    // If we have any input filters call into them to do the mapping,
    675    // otherwise we can assume an input surface will be used
    676    // and just return aRect.
    677    if (filter) {
    678      return filter->MapRectToSource(aRect, aMax, aSourceNode);
    679    }
    680  }
    681  // We have an input surface instead of a filter
    682  // so check if we're the target node.
    683  if (this == aSourceNode) {
    684    return aRect;
    685  }
    686  return IntRect();
    687 }
    688 
    689 void FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex,
    690                                          const IntRect& aRect) {
    691  if (aRect.Overflows()) {
    692    return;
    693  }
    694 
    695  int32_t inputIndex = InputIndex(aInputEnumIndex);
    696  if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
    697    gfxDevCrash(LogReason::FilterInputError)
    698        << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
    699    return;
    700  }
    701  if (mInputSurfaces[inputIndex]) {
    702    return;
    703  }
    704  RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
    705  MOZ_ASSERT(filter, "missing input");
    706  if (filter) {
    707    filter->RequestRect(filter->GetOutputRectInRect(aRect));
    708  }
    709 }
    710 
    711 SurfaceFormat FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
    712                                                FormatHint aFormatHint) {
    713  if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
    714    return SurfaceFormat::A8;
    715  }
    716  return SurfaceFormat::B8G8R8A8;
    717 }
    718 
    719 already_AddRefed<DataSourceSurface>
    720 FilterNodeSoftware::GetInputDataSourceSurface(
    721    uint32_t aInputEnumIndex, const IntRect& aRect, FormatHint aFormatHint,
    722    ConvolveMatrixEdgeMode aEdgeMode,
    723    const IntRect* aTransparencyPaddedSourceRect) {
    724  if (aRect.Overflows()) {
    725    return nullptr;
    726  }
    727 
    728 #ifdef DEBUG_DUMP_SURFACES
    729  printf("<section><h1>GetInputDataSourceSurface with aRect: %s</h1>\n",
    730         ToString(aRect).c_str());
    731 #endif
    732  int32_t inputIndex = InputIndex(aInputEnumIndex);
    733  if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
    734    gfxDevCrash(LogReason::FilterInputData)
    735        << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs();
    736    return nullptr;
    737  }
    738 
    739  if (aRect.IsEmpty()) {
    740    return nullptr;
    741  }
    742 
    743  RefPtr<SourceSurface> surface;
    744  IntRect surfaceRect;
    745 
    746  if (mInputSurfaces[inputIndex]) {
    747    // Input from input surface
    748    surface = mInputSurfaces[inputIndex];
    749 #ifdef DEBUG_DUMP_SURFACES
    750    printf("input from input surface:\n");
    751 #endif
    752    surfaceRect = surface->GetRect();
    753  } else {
    754    // Input from input filter
    755    RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
    756    MOZ_ASSERT(filter, "missing input");
    757    if (!filter) {
    758      return nullptr;
    759    }
    760 #ifdef DEBUG_DUMP_SURFACES
    761    printf("getting input from input filter %s...\n", filter->GetName());
    762 #endif
    763    IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
    764    if (!inputFilterOutput.IsEmpty()) {
    765      surface = filter->GetOutput(inputFilterOutput);
    766    }
    767 #ifdef DEBUG_DUMP_SURFACES
    768    printf("input from input filter %s:\n", filter->GetName());
    769 #endif
    770    surfaceRect = inputFilterOutput;
    771    MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
    772  }
    773 
    774  if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
    775 #ifdef DEBUG_DUMP_SURFACES
    776    printf("wrong input format</section>\n\n");
    777 #endif
    778    return nullptr;
    779  }
    780 
    781  if (!surfaceRect.IsEmpty() && !surface) {
    782 #ifdef DEBUG_DUMP_SURFACES
    783    printf(" -- no input --</section>\n\n");
    784 #endif
    785    return nullptr;
    786  }
    787 
    788  if (aTransparencyPaddedSourceRect &&
    789      !aTransparencyPaddedSourceRect->IsEmpty()) {
    790    IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
    791    surface =
    792        GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
    793    if (surface) {
    794      surfaceRect = srcRect;
    795    } else {
    796      // Padding the surface with transparency failed, probably due to size
    797      // restrictions. Since |surface| is now null, set the surfaceRect to
    798      // empty so that we're consistent.
    799      surfaceRect.SetEmpty();
    800    }
    801  }
    802 
    803  RefPtr<DataSourceSurface> result =
    804      GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
    805 
    806  if (result) {
    807    // TODO: This isn't safe since we don't have a guarantee
    808    // that future Maps will have the same stride
    809    DataSourceSurface::MappedSurface map;
    810    if (result->Map(DataSourceSurface::READ, &map)) {
    811      // Unmap immediately since CloneAligned hasn't been updated
    812      // to use the Map API yet. We can still read the stride/data
    813      // values as long as we don't try to dereference them.
    814      result->Unmap();
    815      if (map.mStride != GetAlignedStride<16>(map.mStride, 1) ||
    816          reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) {
    817        // Align unaligned surface.
    818        result = CloneAligned(result);
    819      }
    820    } else {
    821      result = nullptr;
    822    }
    823  }
    824 
    825  if (!result) {
    826 #ifdef DEBUG_DUMP_SURFACES
    827    printf(" -- no input --</section>\n\n");
    828 #endif
    829    return nullptr;
    830  }
    831 
    832  SurfaceFormat currentFormat = result->GetFormat();
    833  if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
    834      currentFormat != SurfaceFormat::B8G8R8A8) {
    835    result = FilterProcessing::ConvertToB8G8R8A8(result);
    836  }
    837 
    838 #ifdef DEBUG_DUMP_SURFACES
    839  printf("<img src='");
    840  gfxUtils::DumpAsDataURI(result);
    841  printf("'></section>");
    842 #endif
    843 
    844  MOZ_ASSERT(!result || result->GetSize() == aRect.Size(),
    845             "wrong surface size");
    846 
    847  return result.forget();
    848 }
    849 
    850 IntRect FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
    851                                               const IntRect& aInRect) {
    852  if (aInRect.Overflows()) {
    853    return IntRect();
    854  }
    855 
    856  int32_t inputIndex = InputIndex(aInputEnumIndex);
    857  if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
    858    gfxDevCrash(LogReason::FilterInputRect)
    859        << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs();
    860    return IntRect();
    861  }
    862  if (mInputSurfaces[inputIndex]) {
    863    return aInRect.Intersect(mInputSurfaces[inputIndex]->GetRect());
    864  }
    865  RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
    866  MOZ_ASSERT(filter, "missing input");
    867  if (!filter) {
    868    return IntRect();
    869  }
    870  return filter->GetOutputRectInRect(aInRect);
    871 }
    872 
    873 size_t FilterNodeSoftware::NumberOfSetInputs() {
    874  return std::max(mInputSurfaces.size(), mInputFilters.size());
    875 }
    876 
    877 void FilterNodeSoftware::AddInvalidationListener(
    878    FilterInvalidationListener* aListener) {
    879  MOZ_ASSERT(aListener, "null listener");
    880  mInvalidationListeners.push_back(aListener);
    881 }
    882 
    883 void FilterNodeSoftware::RemoveInvalidationListener(
    884    FilterInvalidationListener* aListener) {
    885  MOZ_ASSERT(aListener, "null listener");
    886  std::vector<FilterInvalidationListener*>::iterator it = std::find(
    887      mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
    888  mInvalidationListeners.erase(it);
    889 }
    890 
    891 void FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter) {
    892  Invalidate();
    893 }
    894 
    895 void FilterNodeSoftware::Invalidate() {
    896  mCachedOutput = nullptr;
    897  mCachedRect = IntRect();
    898  for (std::vector<FilterInvalidationListener*>::iterator it =
    899           mInvalidationListeners.begin();
    900       it != mInvalidationListeners.end(); it++) {
    901    (*it)->FilterInvalidated(this);
    902  }
    903 }
    904 
    905 FilterNodeSoftware::FilterNodeSoftware() {}
    906 
    907 FilterNodeSoftware::~FilterNodeSoftware() {
    908  MOZ_ASSERT(
    909      mInvalidationListeners.empty(),
    910      "All invalidation listeners should have unsubscribed themselves by now!");
    911 
    912  for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it =
    913           mInputFilters.begin();
    914       it != mInputFilters.end(); it++) {
    915    if (*it) {
    916      (*it)->RemoveInvalidationListener(this);
    917    }
    918  }
    919 }
    920 
    921 void FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode* aFilter) {
    922  if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
    923    MOZ_ASSERT(false, "can only take software filters as inputs");
    924    return;
    925  }
    926  SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter));
    927 }
    928 
    929 void FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface* aSurface) {
    930  SetInput(aIndex, aSurface, nullptr);
    931 }
    932 
    933 void FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
    934                                  SourceSurface* aSurface,
    935                                  FilterNodeSoftware* aFilter) {
    936  int32_t inputIndex = InputIndex(aInputEnumIndex);
    937  if (inputIndex < 0) {
    938    gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
    939    return;
    940  }
    941  if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
    942    mInputSurfaces.resize(inputIndex + 1);
    943    mInputFilters.resize(inputIndex + 1);
    944  }
    945  mInputSurfaces[inputIndex] = aSurface;
    946  if (mInputFilters[inputIndex]) {
    947    mInputFilters[inputIndex]->RemoveInvalidationListener(this);
    948  }
    949  if (aFilter) {
    950    aFilter->AddInvalidationListener(this);
    951  }
    952  mInputFilters[inputIndex] = aFilter;
    953  if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
    954    mInputSurfaces.resize(inputIndex);
    955    mInputFilters.resize(inputIndex);
    956  }
    957  Invalidate();
    958 }
    959 
    960 FilterNodeBlendSoftware::FilterNodeBlendSoftware()
    961    : mBlendMode(BLEND_MODE_MULTIPLY) {}
    962 
    963 int32_t FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex) {
    964  switch (aInputEnumIndex) {
    965    case IN_BLEND_IN:
    966      return 0;
    967    case IN_BLEND_IN2:
    968      return 1;
    969    default:
    970      return -1;
    971  }
    972 }
    973 
    974 void FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex,
    975                                           uint32_t aBlendMode) {
    976  MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
    977  mBlendMode = static_cast<BlendMode>(aBlendMode);
    978  Invalidate();
    979 }
    980 
    981 static CompositionOp ToBlendOp(BlendMode aOp) {
    982  switch (aOp) {
    983    case BLEND_MODE_MULTIPLY:
    984      return CompositionOp::OP_MULTIPLY;
    985    case BLEND_MODE_SCREEN:
    986      return CompositionOp::OP_SCREEN;
    987    case BLEND_MODE_OVERLAY:
    988      return CompositionOp::OP_OVERLAY;
    989    case BLEND_MODE_DARKEN:
    990      return CompositionOp::OP_DARKEN;
    991    case BLEND_MODE_LIGHTEN:
    992      return CompositionOp::OP_LIGHTEN;
    993    case BLEND_MODE_COLOR_DODGE:
    994      return CompositionOp::OP_COLOR_DODGE;
    995    case BLEND_MODE_COLOR_BURN:
    996      return CompositionOp::OP_COLOR_BURN;
    997    case BLEND_MODE_HARD_LIGHT:
    998      return CompositionOp::OP_HARD_LIGHT;
    999    case BLEND_MODE_SOFT_LIGHT:
   1000      return CompositionOp::OP_SOFT_LIGHT;
   1001    case BLEND_MODE_DIFFERENCE:
   1002      return CompositionOp::OP_DIFFERENCE;
   1003    case BLEND_MODE_EXCLUSION:
   1004      return CompositionOp::OP_EXCLUSION;
   1005    case BLEND_MODE_HUE:
   1006      return CompositionOp::OP_HUE;
   1007    case BLEND_MODE_SATURATION:
   1008      return CompositionOp::OP_SATURATION;
   1009    case BLEND_MODE_COLOR:
   1010      return CompositionOp::OP_COLOR;
   1011    case BLEND_MODE_LUMINOSITY:
   1012      return CompositionOp::OP_LUMINOSITY;
   1013  }
   1014 
   1015  MOZ_ASSERT_UNREACHABLE("Unexpected BlendMode");
   1016  return CompositionOp::OP_OVER;
   1017 }
   1018 
   1019 already_AddRefed<DataSourceSurface> FilterNodeBlendSoftware::Render(
   1020    const IntRect& aRect) {
   1021  RefPtr<DataSourceSurface> input1 =
   1022      GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
   1023  RefPtr<DataSourceSurface> input2 =
   1024      GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
   1025 
   1026  // Null inputs need to be treated as transparent.
   1027 
   1028  // First case: both are transparent.
   1029  if (!input1 && !input2) {
   1030    // Then the result is transparent, too.
   1031    return nullptr;
   1032  }
   1033 
   1034  // Second case: one of them is transparent. Return the non-transparent one.
   1035  if (!input1 || !input2) {
   1036    return input1 ? input1.forget() : input2.forget();
   1037  }
   1038 
   1039  // Third case: both are non-transparent.
   1040  // Apply normal filtering.
   1041  RefPtr<DataSourceSurface> target =
   1042      FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
   1043  if (target != nullptr) {
   1044    return target.forget();
   1045  }
   1046 
   1047  IntSize size = input1->GetSize();
   1048  target = Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
   1049  if (MOZ2D_WARN_IF(!target)) {
   1050    return nullptr;
   1051  }
   1052 
   1053  CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
   1054 
   1055  // This needs to stay in scope until the draw target has been flushed.
   1056  DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
   1057  if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
   1058    return nullptr;
   1059  }
   1060 
   1061  RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
   1062      BackendType::SKIA, targetMap.GetData(), target->GetSize(),
   1063      targetMap.GetStride(), target->GetFormat());
   1064 
   1065  if (!dt) {
   1066    gfxWarning()
   1067        << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
   1068    return nullptr;
   1069  }
   1070 
   1071  Rect r(0, 0, size.width, size.height);
   1072  dt->DrawSurface(input2, r, r, DrawSurfaceOptions(),
   1073                  DrawOptions(1.0f, ToBlendOp(mBlendMode)));
   1074  dt->Flush();
   1075  return target.forget();
   1076 }
   1077 
   1078 void FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect& aRect) {
   1079  RequestInputRect(IN_BLEND_IN, aRect);
   1080  RequestInputRect(IN_BLEND_IN2, aRect);
   1081 }
   1082 
   1083 IntRect FilterNodeBlendSoftware::MapRectToSource(const IntRect& aRect,
   1084                                                 const IntRect& aMax,
   1085                                                 FilterNode* aSourceNode) {
   1086  IntRect result = MapInputRectToSource(IN_BLEND_IN, aRect, aMax, aSourceNode);
   1087  result.OrWith(MapInputRectToSource(IN_BLEND_IN2, aRect, aMax, aSourceNode));
   1088  return result;
   1089 }
   1090 
   1091 IntRect FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect) {
   1092  return GetInputRectInRect(IN_BLEND_IN, aRect)
   1093      .Union(GetInputRectInRect(IN_BLEND_IN2, aRect))
   1094      .Intersect(aRect);
   1095 }
   1096 
   1097 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
   1098    : mSamplingFilter(SamplingFilter::GOOD) {}
   1099 
   1100 int32_t FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex) {
   1101  switch (aInputEnumIndex) {
   1102    case IN_TRANSFORM_IN:
   1103      return 0;
   1104    default:
   1105      return -1;
   1106  }
   1107 }
   1108 
   1109 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex,
   1110                                               uint32_t aFilter) {
   1111  MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
   1112  mSamplingFilter = static_cast<SamplingFilter>(aFilter);
   1113  Invalidate();
   1114 }
   1115 
   1116 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex,
   1117                                               const Matrix& aMatrix) {
   1118  MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
   1119  mMatrix = aMatrix;
   1120  Invalidate();
   1121 }
   1122 
   1123 IntRect FilterNodeTransformSoftware::SourceRectForOutputRect(
   1124    const IntRect& aRect) {
   1125  if (aRect.IsEmpty()) {
   1126    return IntRect();
   1127  }
   1128 
   1129  Matrix inverted(mMatrix);
   1130  if (!inverted.Invert()) {
   1131    return IntRect();
   1132  }
   1133 
   1134  Rect neededRect = inverted.TransformBounds(Rect(aRect));
   1135  neededRect.RoundOut();
   1136  IntRect neededIntRect;
   1137  if (!neededRect.ToIntRect(&neededIntRect)) {
   1138    return IntRect();
   1139  }
   1140  return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
   1141 }
   1142 
   1143 IntRect FilterNodeTransformSoftware::MapRectToSource(const IntRect& aRect,
   1144                                                     const IntRect& aMax,
   1145                                                     FilterNode* aSourceNode) {
   1146  if (aRect.IsEmpty()) {
   1147    return IntRect();
   1148  }
   1149 
   1150  Matrix inverted(mMatrix);
   1151  if (!inverted.Invert()) {
   1152    return aMax;
   1153  }
   1154 
   1155  Rect neededRect = inverted.TransformBounds(Rect(aRect));
   1156  neededRect.RoundOut();
   1157  IntRect neededIntRect;
   1158  if (!neededRect.ToIntRect(&neededIntRect)) {
   1159    return aMax;
   1160  }
   1161  return MapInputRectToSource(IN_TRANSFORM_IN, neededIntRect, aMax,
   1162                              aSourceNode);
   1163 }
   1164 
   1165 already_AddRefed<DataSourceSurface> FilterNodeTransformSoftware::Render(
   1166    const IntRect& aRect) {
   1167  IntRect srcRect = SourceRectForOutputRect(aRect);
   1168 
   1169  RefPtr<DataSourceSurface> input =
   1170      GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
   1171 
   1172  if (!input) {
   1173    return nullptr;
   1174  }
   1175 
   1176  Matrix transform = Matrix::Translation(srcRect.X(), srcRect.Y()) * mMatrix *
   1177                     Matrix::Translation(-aRect.X(), -aRect.Y());
   1178  if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
   1179    return input.forget();
   1180  }
   1181 
   1182  RefPtr<DataSourceSurface> surf =
   1183      Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
   1184 
   1185  if (!surf) {
   1186    return nullptr;
   1187  }
   1188 
   1189  DataSourceSurface::MappedSurface mapping;
   1190  if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
   1191    gfxCriticalError()
   1192        << "FilterNodeTransformSoftware::Render failed to map surface";
   1193    return nullptr;
   1194  }
   1195 
   1196  RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
   1197      BackendType::SKIA, mapping.mData, surf->GetSize(), mapping.mStride,
   1198      surf->GetFormat());
   1199  if (!dt) {
   1200    gfxWarning() << "FilterNodeTransformSoftware::Render failed in "
   1201                    "CreateDrawTargetForData";
   1202    return nullptr;
   1203  }
   1204 
   1205  Rect r(0, 0, srcRect.Width(), srcRect.Height());
   1206  dt->SetTransform(transform);
   1207  dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter));
   1208 
   1209  dt->Flush();
   1210  surf->Unmap();
   1211  return surf.forget();
   1212 }
   1213 
   1214 void FilterNodeTransformSoftware::RequestFromInputsForRect(
   1215    const IntRect& aRect) {
   1216  RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
   1217 }
   1218 
   1219 IntRect FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect) {
   1220  IntRect srcRect = SourceRectForOutputRect(aRect);
   1221  if (srcRect.IsEmpty()) {
   1222    return IntRect();
   1223  }
   1224 
   1225  Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
   1226  outRect.RoundOut();
   1227  IntRect outIntRect;
   1228  if (!outRect.ToIntRect(&outIntRect)) {
   1229    return IntRect();
   1230  }
   1231  return outIntRect.Intersect(aRect);
   1232 }
   1233 
   1234 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
   1235    : mOperator(MORPHOLOGY_OPERATOR_ERODE) {}
   1236 
   1237 int32_t FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex) {
   1238  switch (aInputEnumIndex) {
   1239    case IN_MORPHOLOGY_IN:
   1240      return 0;
   1241    default:
   1242      return -1;
   1243  }
   1244 }
   1245 
   1246 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
   1247                                                const IntSize& aRadii) {
   1248  MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
   1249  mRadii.width = std::clamp(aRadii.width, 0, 100000);
   1250  mRadii.height = std::clamp(aRadii.height, 0, 100000);
   1251  Invalidate();
   1252 }
   1253 
   1254 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
   1255                                                uint32_t aOperator) {
   1256  MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
   1257  mOperator = static_cast<MorphologyOperator>(aOperator);
   1258  Invalidate();
   1259 }
   1260 
   1261 static already_AddRefed<DataSourceSurface> ApplyMorphology(
   1262    const IntRect& aSourceRect, DataSourceSurface* aInput,
   1263    const IntRect& aDestRect, int32_t rx, int32_t ry,
   1264    MorphologyOperator aOperator) {
   1265  IntRect srcRect = aSourceRect - aDestRect.TopLeft();
   1266  IntRect destRect = aDestRect - aDestRect.TopLeft();
   1267  IntRect tmpRect(destRect.X(), srcRect.Y(), destRect.Width(),
   1268                  srcRect.Height());
   1269 #ifdef DEBUG
   1270  IntMargin margin = srcRect - destRect;
   1271  MOZ_ASSERT(margin.top >= ry && margin.right >= rx && margin.bottom >= ry &&
   1272                 margin.left >= rx,
   1273             "insufficient margin");
   1274 #endif
   1275 
   1276  RefPtr<DataSourceSurface> tmp;
   1277  if (rx == 0) {
   1278    tmp = aInput;
   1279  } else {
   1280    tmp = Factory::CreateDataSourceSurface(tmpRect.Size(),
   1281                                           SurfaceFormat::B8G8R8A8);
   1282    if (MOZ2D_WARN_IF(!tmp)) {
   1283      return nullptr;
   1284    }
   1285 
   1286    DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
   1287    DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::WRITE);
   1288    if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !tmpMap.IsMapped())) {
   1289      return nullptr;
   1290    }
   1291    const uint8_t* sourceData =
   1292        DataAtOffset(aInput, sourceMap.GetMappedSurface(),
   1293                     destRect.TopLeft() - srcRect.TopLeft());
   1294    uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
   1295                                    destRect.TopLeft() - tmpRect.TopLeft());
   1296 
   1297    FilterProcessing::ApplyMorphologyHorizontal(
   1298        sourceData, sourceMap.GetStride(), tmpData, tmpMap.GetStride(), tmpRect,
   1299        rx, aOperator);
   1300  }
   1301 
   1302  RefPtr<DataSourceSurface> dest;
   1303  if (ry == 0) {
   1304    dest = tmp;
   1305  } else {
   1306    dest = Factory::CreateDataSourceSurface(destRect.Size(),
   1307                                            SurfaceFormat::B8G8R8A8);
   1308    if (MOZ2D_WARN_IF(!dest)) {
   1309      return nullptr;
   1310    }
   1311 
   1312    DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::READ);
   1313    DataSourceSurface::ScopedMap destMap(dest, DataSourceSurface::WRITE);
   1314    if (MOZ2D_WARN_IF(!tmpMap.IsMapped() || !destMap.IsMapped())) {
   1315      return nullptr;
   1316    }
   1317    int32_t tmpStride = tmpMap.GetStride();
   1318    const uint8_t* tmpData = DataAtOffset(
   1319        tmp, tmpMap.GetMappedSurface(), destRect.TopLeft() - tmpRect.TopLeft());
   1320 
   1321    int32_t destStride = destMap.GetStride();
   1322    uint8_t* destData = destMap.GetData();
   1323 
   1324    FilterProcessing::ApplyMorphologyVertical(
   1325        tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
   1326  }
   1327 
   1328  return dest.forget();
   1329 }
   1330 
   1331 already_AddRefed<DataSourceSurface> FilterNodeMorphologySoftware::Render(
   1332    const IntRect& aRect) {
   1333  IntRect srcRect = aRect;
   1334  srcRect.Inflate(mRadii);
   1335 
   1336  RefPtr<DataSourceSurface> input =
   1337      GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
   1338  if (!input) {
   1339    return nullptr;
   1340  }
   1341 
   1342  int32_t rx = mRadii.width;
   1343  int32_t ry = mRadii.height;
   1344 
   1345  if (rx == 0 && ry == 0) {
   1346    return input.forget();
   1347  }
   1348 
   1349  return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
   1350 }
   1351 
   1352 void FilterNodeMorphologySoftware::RequestFromInputsForRect(
   1353    const IntRect& aRect) {
   1354  IntRect srcRect = aRect;
   1355  srcRect.Inflate(mRadii);
   1356  RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
   1357 }
   1358 
   1359 IntRect FilterNodeMorphologySoftware::GetOutputRectInRect(
   1360    const IntRect& aRect) {
   1361  IntRect inflatedSourceRect = aRect;
   1362  inflatedSourceRect.Inflate(mRadii);
   1363  IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
   1364  if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
   1365    inputRect.Deflate(mRadii);
   1366  } else {
   1367    inputRect.Inflate(mRadii);
   1368  }
   1369  return inputRect.Intersect(aRect);
   1370 }
   1371 
   1372 int32_t FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
   1373  switch (aInputEnumIndex) {
   1374    case IN_COLOR_MATRIX_IN:
   1375      return 0;
   1376    default:
   1377      return -1;
   1378  }
   1379 }
   1380 
   1381 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
   1382                                                 const Matrix5x4& aMatrix) {
   1383  MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
   1384  mMatrix = aMatrix;
   1385  Invalidate();
   1386 }
   1387 
   1388 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
   1389                                                 uint32_t aAlphaMode) {
   1390  MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
   1391  mAlphaMode = (AlphaMode)aAlphaMode;
   1392  Invalidate();
   1393 }
   1394 
   1395 static already_AddRefed<DataSourceSurface> Premultiply(
   1396    DataSourceSurface* aSurface) {
   1397  if (aSurface->GetFormat() == SurfaceFormat::A8) {
   1398    RefPtr<DataSourceSurface> surface(aSurface);
   1399    return surface.forget();
   1400  }
   1401 
   1402  IntSize size = aSurface->GetSize();
   1403  RefPtr<DataSourceSurface> target =
   1404      Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
   1405  if (MOZ2D_WARN_IF(!target)) {
   1406    return nullptr;
   1407  }
   1408 
   1409  DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
   1410  DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   1411  if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
   1412    return nullptr;
   1413  }
   1414 
   1415  const uint8_t* inputData = inputMap.GetData();
   1416  int32_t inputStride = inputMap.GetStride();
   1417  uint8_t* targetData = targetMap.GetData();
   1418  int32_t targetStride = targetMap.GetStride();
   1419 
   1420  FilterProcessing::DoPremultiplicationCalculation(
   1421      size, targetData, targetStride, inputData, inputStride);
   1422 
   1423  return target.forget();
   1424 }
   1425 
   1426 static already_AddRefed<DataSourceSurface> Unpremultiply(
   1427    DataSourceSurface* aSurface) {
   1428  if (aSurface->GetFormat() == SurfaceFormat::A8) {
   1429    RefPtr<DataSourceSurface> surface(aSurface);
   1430    return surface.forget();
   1431  }
   1432 
   1433  IntSize size = aSurface->GetSize();
   1434  RefPtr<DataSourceSurface> target =
   1435      Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
   1436  if (MOZ2D_WARN_IF(!target)) {
   1437    return nullptr;
   1438  }
   1439 
   1440  DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
   1441  DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   1442  if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
   1443    return nullptr;
   1444  }
   1445 
   1446  uint8_t* inputData = inputMap.GetData();
   1447  int32_t inputStride = inputMap.GetStride();
   1448  uint8_t* targetData = targetMap.GetData();
   1449  int32_t targetStride = targetMap.GetStride();
   1450 
   1451  FilterProcessing::DoUnpremultiplicationCalculation(
   1452      size, targetData, targetStride, inputData, inputStride);
   1453 
   1454  return target.forget();
   1455 }
   1456 
   1457 static already_AddRefed<DataSourceSurface> Opacity(DataSourceSurface* aSurface,
   1458                                                   Float aValue) {
   1459  if (aValue == 1.0f) {
   1460    RefPtr<DataSourceSurface> surface(aSurface);
   1461    return surface.forget();
   1462  }
   1463 
   1464  IntSize size = aSurface->GetSize();
   1465  RefPtr<DataSourceSurface> target =
   1466      Factory::CreateDataSourceSurface(size, aSurface->GetFormat());
   1467  if (MOZ2D_WARN_IF(!target)) {
   1468    return nullptr;
   1469  }
   1470 
   1471  DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
   1472  DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   1473  if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
   1474    return nullptr;
   1475  }
   1476 
   1477  uint8_t* inputData = inputMap.GetData();
   1478  int32_t inputStride = inputMap.GetStride();
   1479  uint8_t* targetData = targetMap.GetData();
   1480  int32_t targetStride = targetMap.GetStride();
   1481 
   1482  if (aSurface->GetFormat() == SurfaceFormat::A8) {
   1483    FilterProcessing::DoOpacityCalculationA8(size, targetData, targetStride,
   1484                                             inputData, inputStride, aValue);
   1485  } else {
   1486    MOZ_ASSERT(aSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
   1487    FilterProcessing::DoOpacityCalculation(size, targetData, targetStride,
   1488                                           inputData, inputStride, aValue);
   1489  }
   1490 
   1491  return target.forget();
   1492 }
   1493 
   1494 already_AddRefed<DataSourceSurface> FilterNodeColorMatrixSoftware::Render(
   1495    const IntRect& aRect) {
   1496  RefPtr<DataSourceSurface> input =
   1497      GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
   1498  if (!input) {
   1499    return nullptr;
   1500  }
   1501 
   1502  if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
   1503    input = Unpremultiply(input);
   1504  }
   1505 
   1506  RefPtr<DataSourceSurface> result =
   1507      FilterProcessing::ApplyColorMatrix(input, mMatrix);
   1508 
   1509  if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
   1510    result = Premultiply(result);
   1511  }
   1512 
   1513  return result.forget();
   1514 }
   1515 
   1516 void FilterNodeColorMatrixSoftware::RequestFromInputsForRect(
   1517    const IntRect& aRect) {
   1518  RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
   1519 }
   1520 
   1521 IntRect FilterNodeColorMatrixSoftware::MapRectToSource(
   1522    const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
   1523  return MapInputRectToSource(IN_COLOR_MATRIX_IN, aRect, aMax, aSourceNode);
   1524 }
   1525 
   1526 IntRect FilterNodeColorMatrixSoftware::GetOutputRectInRect(
   1527    const IntRect& aRect) {
   1528  if (mMatrix._54 > 0.0f) {
   1529    return aRect;
   1530  }
   1531  return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
   1532 }
   1533 
   1534 void FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex,
   1535                                           const DeviceColor& aColor) {
   1536  MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
   1537  mColor = aColor;
   1538  Invalidate();
   1539 }
   1540 
   1541 static uint32_t ColorToBGRA(const DeviceColor& aColor) {
   1542  union {
   1543    uint32_t color;
   1544    uint8_t components[4];
   1545  };
   1546  components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
   1547      NS_lround(aColor.r * aColor.a * 255.0f);
   1548  components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
   1549      NS_lround(aColor.g * aColor.a * 255.0f);
   1550  components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
   1551      NS_lround(aColor.b * aColor.a * 255.0f);
   1552  components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
   1553  return color;
   1554 }
   1555 
   1556 static SurfaceFormat FormatForColor(DeviceColor aColor) {
   1557  if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
   1558    return SurfaceFormat::A8;
   1559  }
   1560  return SurfaceFormat::B8G8R8A8;
   1561 }
   1562 
   1563 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::Render(
   1564    const IntRect& aRect) {
   1565  SurfaceFormat format = FormatForColor(mColor);
   1566  RefPtr<DataSourceSurface> target =
   1567      Factory::CreateDataSourceSurface(aRect.Size(), format);
   1568  if (MOZ2D_WARN_IF(!target)) {
   1569    return nullptr;
   1570  }
   1571 
   1572  DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   1573  if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
   1574    return nullptr;
   1575  }
   1576 
   1577  uint8_t* targetData = targetMap.GetData();
   1578  int32_t stride = targetMap.GetStride();
   1579 
   1580  if (format == SurfaceFormat::B8G8R8A8) {
   1581    uint32_t color = ColorToBGRA(mColor);
   1582    for (int32_t y = 0; y < aRect.Height(); y++) {
   1583      for (int32_t x = 0; x < aRect.Width(); x++) {
   1584        *((uint32_t*)targetData + x) = color;
   1585      }
   1586      PodZero(&targetData[aRect.Width() * 4], stride - aRect.Width() * 4);
   1587      targetData += stride;
   1588    }
   1589  } else if (format == SurfaceFormat::A8) {
   1590    uint8_t alpha = NS_lround(mColor.a * 255.0f);
   1591    for (int32_t y = 0; y < aRect.Height(); y++) {
   1592      for (int32_t x = 0; x < aRect.Width(); x++) {
   1593        targetData[x] = alpha;
   1594      }
   1595      PodZero(&targetData[aRect.Width()], stride - aRect.Width());
   1596      targetData += stride;
   1597    }
   1598  } else {
   1599    gfxDevCrash(LogReason::FilterInputFormat)
   1600        << "Bad format in flood render " << (int)format;
   1601    return nullptr;
   1602  }
   1603 
   1604  return target.forget();
   1605 }
   1606 
   1607 // Override GetOutput to get around caching. Rendering simple floods is
   1608 // comparatively fast.
   1609 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::GetOutput(
   1610    const IntRect& aRect) {
   1611  return Render(aRect);
   1612 }
   1613 
   1614 IntRect FilterNodeFloodSoftware::MapRectToSource(const IntRect& aRect,
   1615                                                 const IntRect& aMax,
   1616                                                 FilterNode* aSourceNode) {
   1617  return IntRect();
   1618 }
   1619 
   1620 IntRect FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect) {
   1621  if (mColor.a == 0.0f) {
   1622    return IntRect();
   1623  }
   1624  return aRect;
   1625 }
   1626 
   1627 int32_t FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex) {
   1628  switch (aInputEnumIndex) {
   1629    case IN_TILE_IN:
   1630      return 0;
   1631    default:
   1632      return -1;
   1633  }
   1634 }
   1635 
   1636 void FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
   1637                                          const IntRect& aSourceRect) {
   1638  MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
   1639  mSourceRect.SetRect(int32_t(aSourceRect.X()), int32_t(aSourceRect.Y()),
   1640                      int32_t(aSourceRect.Width()),
   1641                      int32_t(aSourceRect.Height()));
   1642  Invalidate();
   1643 }
   1644 
   1645 namespace {
   1646 struct CompareIntRects {
   1647  bool operator()(const IntRect& a, const IntRect& b) const {
   1648    if (a.X() != b.X()) {
   1649      return a.X() < b.X();
   1650    }
   1651    if (a.Y() != b.Y()) {
   1652      return a.Y() < b.Y();
   1653    }
   1654    if (a.Width() != b.Width()) {
   1655      return a.Width() < b.Width();
   1656    }
   1657    return a.Height() < b.Height();
   1658  }
   1659 };
   1660 
   1661 }  // namespace
   1662 
   1663 already_AddRefed<DataSourceSurface> FilterNodeTileSoftware::Render(
   1664    const IntRect& aRect) {
   1665  if (mSourceRect.IsEmpty()) {
   1666    return nullptr;
   1667  }
   1668 
   1669  if (mSourceRect.Contains(aRect)) {
   1670    return GetInputDataSourceSurface(IN_TILE_IN, aRect);
   1671  }
   1672 
   1673  RefPtr<DataSourceSurface> target;
   1674 
   1675  typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects>
   1676      InputMap;
   1677  InputMap inputs;
   1678 
   1679  IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
   1680  IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
   1681  for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
   1682    for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
   1683      IntPoint sourceToDestOffset(ix * mSourceRect.Width(),
   1684                                  iy * mSourceRect.Height());
   1685      IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
   1686      IntRect srcRect = destRect - sourceToDestOffset;
   1687      if (srcRect.IsEmpty()) {
   1688        continue;
   1689      }
   1690 
   1691      RefPtr<DataSourceSurface> input;
   1692      InputMap::iterator it = inputs.find(srcRect);
   1693      if (it == inputs.end()) {
   1694        input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
   1695        inputs[srcRect] = input;
   1696      } else {
   1697        input = it->second;
   1698      }
   1699      if (!input) {
   1700        return nullptr;
   1701      }
   1702      if (!target) {
   1703        // We delay creating the target until now because we want to use the
   1704        // same format as our input filter, and we do not actually know the
   1705        // input format before we call GetInputDataSourceSurface.
   1706        target =
   1707            Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
   1708        if (MOZ2D_WARN_IF(!target)) {
   1709          return nullptr;
   1710        }
   1711      }
   1712 
   1713      if (input->GetFormat() != target->GetFormat()) {
   1714        // Different rectangles of the input can have different formats. If
   1715        // that happens, just convert everything to B8G8R8A8.
   1716        target = FilterProcessing::ConvertToB8G8R8A8(target);
   1717        input = FilterProcessing::ConvertToB8G8R8A8(input);
   1718        if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) {
   1719          return nullptr;
   1720        }
   1721      }
   1722 
   1723      CopyRect(input, target, srcRect - srcRect.TopLeft(),
   1724               destRect.TopLeft() - aRect.TopLeft());
   1725    }
   1726  }
   1727 
   1728  return target.forget();
   1729 }
   1730 
   1731 void FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect& aRect) {
   1732  // Do not request anything.
   1733  // Source rects for the tile filter can be discontinuous with large gaps
   1734  // between them. Requesting those from our input filter might cause it to
   1735  // render the whole bounding box of all of them, which would be wasteful.
   1736 }
   1737 
   1738 IntRect FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect) {
   1739  return aRect;
   1740 }
   1741 
   1742 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
   1743    : mDisableR(true), mDisableG(true), mDisableB(true), mDisableA(true) {}
   1744 
   1745 void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
   1746                                                       bool aDisable) {
   1747  switch (aIndex) {
   1748    case ATT_TRANSFER_DISABLE_R:
   1749      mDisableR = aDisable;
   1750      break;
   1751    case ATT_TRANSFER_DISABLE_G:
   1752      mDisableG = aDisable;
   1753      break;
   1754    case ATT_TRANSFER_DISABLE_B:
   1755      mDisableB = aDisable;
   1756      break;
   1757    case ATT_TRANSFER_DISABLE_A:
   1758      mDisableA = aDisable;
   1759      break;
   1760    default:
   1761      MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
   1762  }
   1763  Invalidate();
   1764 }
   1765 
   1766 void FilterNodeComponentTransferSoftware::GenerateLookupTable(
   1767    ptrdiff_t aComponent, uint8_t aTables[4][256], bool aDisabled) {
   1768  if (aDisabled) {
   1769    for (int32_t i = 0; i < 256; ++i) {
   1770      aTables[aComponent][i] = i;
   1771    }
   1772  } else {
   1773    FillLookupTable(aComponent, aTables[aComponent]);
   1774  }
   1775 }
   1776 
   1777 template <uint32_t BytesPerPixel>
   1778 static void TransferComponents(
   1779    DataSourceSurface* aInput, DataSourceSurface* aTarget,
   1780    const uint8_t aLookupTables[BytesPerPixel][256]) {
   1781  MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
   1782  IntSize size = aInput->GetSize();
   1783 
   1784  DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
   1785  DataSourceSurface::ScopedMap targetMap(aTarget, DataSourceSurface::WRITE);
   1786  if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
   1787    return;
   1788  }
   1789 
   1790  uint8_t* sourceData = sourceMap.GetData();
   1791  int32_t sourceStride = sourceMap.GetStride();
   1792  uint8_t* targetData = targetMap.GetData();
   1793  int32_t targetStride = targetMap.GetStride();
   1794 
   1795  MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source");
   1796 
   1797  for (int32_t y = 0; y < size.height; y++) {
   1798    for (int32_t x = 0; x < size.width; x++) {
   1799      uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
   1800      uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
   1801      for (uint32_t i = 0; i < BytesPerPixel; i++) {
   1802        targetData[targetIndex + i] =
   1803            aLookupTables[i][sourceData[sourceIndex + i]];
   1804      }
   1805    }
   1806 
   1807    // Zero padding to keep valgrind happy.
   1808    PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
   1809            targetStride - size.width * BytesPerPixel);
   1810  }
   1811 }
   1812 
   1813 static bool IsAllZero(const uint8_t aLookupTable[256]) {
   1814  for (int32_t i = 0; i < 256; i++) {
   1815    if (aLookupTable[i] != 0) {
   1816      return false;
   1817    }
   1818  }
   1819  return true;
   1820 }
   1821 
   1822 already_AddRefed<DataSourceSurface> FilterNodeComponentTransferSoftware::Render(
   1823    const IntRect& aRect) {
   1824  if (mDisableR && mDisableG && mDisableB && mDisableA) {
   1825    return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
   1826  }
   1827 
   1828  uint8_t lookupTables[4][256];
   1829  GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
   1830  GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
   1831  GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
   1832  GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
   1833 
   1834  bool needColorChannels =
   1835      lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
   1836      lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
   1837      lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
   1838 
   1839  FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
   1840 
   1841  RefPtr<DataSourceSurface> input =
   1842      GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
   1843  if (!input) {
   1844    return nullptr;
   1845  }
   1846 
   1847  if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
   1848    bool colorChannelsBecomeBlack =
   1849        IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
   1850        IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
   1851        IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
   1852 
   1853    if (colorChannelsBecomeBlack) {
   1854      input = FilterProcessing::ExtractAlpha(input);
   1855    }
   1856  }
   1857 
   1858  SurfaceFormat format = input->GetFormat();
   1859  if (format == SurfaceFormat::A8 && mDisableA) {
   1860    return input.forget();
   1861  }
   1862 
   1863  RefPtr<DataSourceSurface> target =
   1864      Factory::CreateDataSourceSurface(aRect.Size(), format);
   1865  if (MOZ2D_WARN_IF(!target)) {
   1866    return nullptr;
   1867  }
   1868 
   1869  if (format == SurfaceFormat::A8) {
   1870    TransferComponents<1>(input, target,
   1871                          &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
   1872  } else {
   1873    TransferComponents<4>(input, target, lookupTables);
   1874  }
   1875 
   1876  return target.forget();
   1877 }
   1878 
   1879 void FilterNodeComponentTransferSoftware::RequestFromInputsForRect(
   1880    const IntRect& aRect) {
   1881  RequestInputRect(IN_TRANSFER_IN, aRect);
   1882 }
   1883 
   1884 IntRect FilterNodeComponentTransferSoftware::MapRectToSource(
   1885    const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
   1886  return MapInputRectToSource(IN_TRANSFER_IN, aRect, aMax, aSourceNode);
   1887 }
   1888 
   1889 IntRect FilterNodeComponentTransferSoftware::GetOutputRectInRect(
   1890    const IntRect& aRect) {
   1891  if (mDisableA) {
   1892    return GetInputRectInRect(IN_TRANSFER_IN, aRect);
   1893  }
   1894  return aRect;
   1895 }
   1896 
   1897 int32_t FilterNodeComponentTransferSoftware::InputIndex(
   1898    uint32_t aInputEnumIndex) {
   1899  switch (aInputEnumIndex) {
   1900    case IN_TRANSFER_IN:
   1901      return 0;
   1902    default:
   1903      return -1;
   1904  }
   1905 }
   1906 
   1907 void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
   1908                                                   const Float* aFloat,
   1909                                                   uint32_t aSize) {
   1910  std::vector<Float> table(aFloat, aFloat + aSize);
   1911  switch (aIndex) {
   1912    case ATT_TABLE_TRANSFER_TABLE_R:
   1913      mTableR = table;
   1914      break;
   1915    case ATT_TABLE_TRANSFER_TABLE_G:
   1916      mTableG = table;
   1917      break;
   1918    case ATT_TABLE_TRANSFER_TABLE_B:
   1919      mTableB = table;
   1920      break;
   1921    case ATT_TABLE_TRANSFER_TABLE_A:
   1922      mTableA = table;
   1923      break;
   1924    default:
   1925      MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
   1926  }
   1927  Invalidate();
   1928 }
   1929 
   1930 void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
   1931                                                      uint8_t aTable[256]) {
   1932  switch (aComponent) {
   1933    case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
   1934      FillLookupTableImpl(mTableR, aTable);
   1935      break;
   1936    case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
   1937      FillLookupTableImpl(mTableG, aTable);
   1938      break;
   1939    case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
   1940      FillLookupTableImpl(mTableB, aTable);
   1941      break;
   1942    case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
   1943      FillLookupTableImpl(mTableA, aTable);
   1944      break;
   1945    default:
   1946      MOZ_ASSERT(false, "unknown component");
   1947      break;
   1948  }
   1949 }
   1950 
   1951 void FilterNodeTableTransferSoftware::FillLookupTableImpl(
   1952    const std::vector<Float>& aTableValues, uint8_t aTable[256]) {
   1953  uint32_t tvLength = aTableValues.size();
   1954  if (tvLength < 2) {
   1955    return;
   1956  }
   1957 
   1958  for (size_t i = 0; i < 256; i++) {
   1959    uint32_t k = (i * (tvLength - 1)) / 255;
   1960    Float v1 = aTableValues[k];
   1961    Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
   1962    int32_t val = int32_t(255 * (v1 + (i / 255.0f - k / float(tvLength - 1)) *
   1963                                          (tvLength - 1) * (v2 - v1)));
   1964    aTable[i] = std::clamp(val, 0, 255);
   1965  }
   1966 }
   1967 
   1968 void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
   1969                                                      const Float* aFloat,
   1970                                                      uint32_t aSize) {
   1971  std::vector<Float> discrete(aFloat, aFloat + aSize);
   1972  switch (aIndex) {
   1973    case ATT_DISCRETE_TRANSFER_TABLE_R:
   1974      mTableR = discrete;
   1975      break;
   1976    case ATT_DISCRETE_TRANSFER_TABLE_G:
   1977      mTableG = discrete;
   1978      break;
   1979    case ATT_DISCRETE_TRANSFER_TABLE_B:
   1980      mTableB = discrete;
   1981      break;
   1982    case ATT_DISCRETE_TRANSFER_TABLE_A:
   1983      mTableA = discrete;
   1984      break;
   1985    default:
   1986      MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
   1987  }
   1988  Invalidate();
   1989 }
   1990 
   1991 void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
   1992                                                         uint8_t aTable[256]) {
   1993  switch (aComponent) {
   1994    case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
   1995      FillLookupTableImpl(mTableR, aTable);
   1996      break;
   1997    case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
   1998      FillLookupTableImpl(mTableG, aTable);
   1999      break;
   2000    case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
   2001      FillLookupTableImpl(mTableB, aTable);
   2002      break;
   2003    case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
   2004      FillLookupTableImpl(mTableA, aTable);
   2005      break;
   2006    default:
   2007      MOZ_ASSERT(false, "unknown component");
   2008      break;
   2009  }
   2010 }
   2011 
   2012 void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
   2013    const std::vector<Float>& aTableValues, uint8_t aTable[256]) {
   2014  uint32_t tvLength = aTableValues.size();
   2015  if (tvLength < 1) {
   2016    return;
   2017  }
   2018 
   2019  for (size_t i = 0; i < 256; i++) {
   2020    uint32_t k = (i * tvLength) / 255;
   2021    k = std::min(k, tvLength - 1);
   2022    Float v = aTableValues[k];
   2023    int32_t val = NS_lround(255 * v);
   2024    aTable[i] = std::clamp(val, 0, 255);
   2025  }
   2026 }
   2027 
   2028 FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
   2029    : mSlopeR(0),
   2030      mSlopeG(0),
   2031      mSlopeB(0),
   2032      mSlopeA(0),
   2033      mInterceptR(0),
   2034      mInterceptG(0),
   2035      mInterceptB(0),
   2036      mInterceptA(0) {}
   2037 
   2038 void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
   2039                                                    Float aValue) {
   2040  switch (aIndex) {
   2041    case ATT_LINEAR_TRANSFER_SLOPE_R:
   2042      mSlopeR = aValue;
   2043      break;
   2044    case ATT_LINEAR_TRANSFER_INTERCEPT_R:
   2045      mInterceptR = aValue;
   2046      break;
   2047    case ATT_LINEAR_TRANSFER_SLOPE_G:
   2048      mSlopeG = aValue;
   2049      break;
   2050    case ATT_LINEAR_TRANSFER_INTERCEPT_G:
   2051      mInterceptG = aValue;
   2052      break;
   2053    case ATT_LINEAR_TRANSFER_SLOPE_B:
   2054      mSlopeB = aValue;
   2055      break;
   2056    case ATT_LINEAR_TRANSFER_INTERCEPT_B:
   2057      mInterceptB = aValue;
   2058      break;
   2059    case ATT_LINEAR_TRANSFER_SLOPE_A:
   2060      mSlopeA = aValue;
   2061      break;
   2062    case ATT_LINEAR_TRANSFER_INTERCEPT_A:
   2063      mInterceptA = aValue;
   2064      break;
   2065    default:
   2066      MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
   2067  }
   2068  Invalidate();
   2069 }
   2070 
   2071 void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
   2072                                                       uint8_t aTable[256]) {
   2073  switch (aComponent) {
   2074    case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
   2075      FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
   2076      break;
   2077    case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
   2078      FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
   2079      break;
   2080    case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
   2081      FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
   2082      break;
   2083    case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
   2084      FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
   2085      break;
   2086    default:
   2087      MOZ_ASSERT(false, "unknown component");
   2088      break;
   2089  }
   2090 }
   2091 
   2092 void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
   2093    Float aSlope, Float aIntercept, uint8_t aTable[256]) {
   2094  for (size_t i = 0; i < 256; i++) {
   2095    int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
   2096    aTable[i] = std::clamp(val, 0, 255);
   2097  }
   2098 }
   2099 
   2100 FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
   2101    : mAmplitudeR(0),
   2102      mAmplitudeG(0),
   2103      mAmplitudeB(0),
   2104      mAmplitudeA(0),
   2105      mExponentR(0),
   2106      mExponentG(0),
   2107      mExponentB(0),
   2108      mExponentA(0),
   2109      mOffsetR(0.0),
   2110      mOffsetG(0.0),
   2111      mOffsetB(0.0),
   2112      mOffsetA(0.0) {}
   2113 
   2114 void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
   2115                                                   Float aValue) {
   2116  switch (aIndex) {
   2117    case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
   2118      mAmplitudeR = aValue;
   2119      break;
   2120    case ATT_GAMMA_TRANSFER_EXPONENT_R:
   2121      mExponentR = aValue;
   2122      break;
   2123    case ATT_GAMMA_TRANSFER_OFFSET_R:
   2124      mOffsetR = aValue;
   2125      break;
   2126    case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
   2127      mAmplitudeG = aValue;
   2128      break;
   2129    case ATT_GAMMA_TRANSFER_EXPONENT_G:
   2130      mExponentG = aValue;
   2131      break;
   2132    case ATT_GAMMA_TRANSFER_OFFSET_G:
   2133      mOffsetG = aValue;
   2134      break;
   2135    case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
   2136      mAmplitudeB = aValue;
   2137      break;
   2138    case ATT_GAMMA_TRANSFER_EXPONENT_B:
   2139      mExponentB = aValue;
   2140      break;
   2141    case ATT_GAMMA_TRANSFER_OFFSET_B:
   2142      mOffsetB = aValue;
   2143      break;
   2144    case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
   2145      mAmplitudeA = aValue;
   2146      break;
   2147    case ATT_GAMMA_TRANSFER_EXPONENT_A:
   2148      mExponentA = aValue;
   2149      break;
   2150    case ATT_GAMMA_TRANSFER_OFFSET_A:
   2151      mOffsetA = aValue;
   2152      break;
   2153    default:
   2154      MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
   2155  }
   2156  Invalidate();
   2157 }
   2158 
   2159 void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
   2160                                                      uint8_t aTable[256]) {
   2161  switch (aComponent) {
   2162    case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
   2163      FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
   2164      break;
   2165    case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
   2166      FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
   2167      break;
   2168    case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
   2169      FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
   2170      break;
   2171    case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
   2172      FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
   2173      break;
   2174    default:
   2175      MOZ_ASSERT(false, "unknown component");
   2176      break;
   2177  }
   2178 }
   2179 
   2180 void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
   2181                                                          Float aExponent,
   2182                                                          Float aOffset,
   2183                                                          uint8_t aTable[256]) {
   2184  for (size_t i = 0; i < 256; i++) {
   2185    int32_t val =
   2186        NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
   2187    aTable[i] = std::clamp(val, 0, 255);
   2188  }
   2189 }
   2190 
   2191 FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
   2192    : mDivisor(0),
   2193      mBias(0),
   2194      mEdgeMode(EDGE_MODE_DUPLICATE),
   2195      mPreserveAlpha(false) {}
   2196 
   2197 int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
   2198  switch (aInputEnumIndex) {
   2199    case IN_CONVOLVE_MATRIX_IN:
   2200      return 0;
   2201    default:
   2202      return -1;
   2203  }
   2204 }
   2205 
   2206 void FilterNodeConvolveMatrixSoftware::SetAttribute(
   2207    uint32_t aIndex, const IntSize& aKernelSize) {
   2208  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
   2209  mKernelSize = aKernelSize;
   2210  Invalidate();
   2211 }
   2212 
   2213 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
   2214                                                    const Float* aMatrix,
   2215                                                    uint32_t aSize) {
   2216  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
   2217  mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
   2218  Invalidate();
   2219 }
   2220 
   2221 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
   2222                                                    Float aValue) {
   2223  switch (aIndex) {
   2224    case ATT_CONVOLVE_MATRIX_DIVISOR:
   2225      mDivisor = aValue;
   2226      break;
   2227    case ATT_CONVOLVE_MATRIX_BIAS:
   2228      mBias = aValue;
   2229      break;
   2230    default:
   2231      MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
   2232  }
   2233  Invalidate();
   2234 }
   2235 
   2236 void FilterNodeConvolveMatrixSoftware::SetAttribute(
   2237    uint32_t aIndex, const Size& aKernelUnitLength) {
   2238  switch (aIndex) {
   2239    case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
   2240      mKernelUnitLength = aKernelUnitLength;
   2241      break;
   2242    default:
   2243      MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
   2244  }
   2245  Invalidate();
   2246 }
   2247 
   2248 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
   2249                                                    const IntPoint& aTarget) {
   2250  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
   2251  mTarget = aTarget;
   2252  Invalidate();
   2253 }
   2254 
   2255 void FilterNodeConvolveMatrixSoftware::SetAttribute(
   2256    uint32_t aIndex, const IntRect& aRenderRect) {
   2257  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_RENDER_RECT);
   2258  mRenderRect = aRenderRect;
   2259  Invalidate();
   2260 }
   2261 
   2262 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
   2263                                                    uint32_t aEdgeMode) {
   2264  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
   2265  mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
   2266  Invalidate();
   2267 }
   2268 
   2269 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
   2270                                                    bool aPreserveAlpha) {
   2271  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
   2272  mPreserveAlpha = aPreserveAlpha;
   2273  Invalidate();
   2274 }
   2275 
   2276 #ifdef DEBUG
   2277 static inline void DebugOnlyCheckColorSamplingAccess(
   2278    const uint8_t* aSampleAddress, const uint8_t* aBoundsBegin,
   2279    const uint8_t* aBoundsEnd) {
   2280  MOZ_ASSERT(aSampleAddress >= aBoundsBegin, "accessing before start");
   2281  MOZ_ASSERT(aSampleAddress < aBoundsEnd, "accessing after end");
   2282 }
   2283 #else
   2284 #  define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
   2285 #endif
   2286 
   2287 static inline uint8_t ColorComponentAtPoint(const uint8_t* aData,
   2288                                            ptrdiff_t aStride,
   2289                                            const uint8_t* aBoundsBegin,
   2290                                            const uint8_t* aBoundsEnd,
   2291                                            int32_t x, int32_t y, ptrdiff_t bpp,
   2292                                            ptrdiff_t c) {
   2293  DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c],
   2294                                    aBoundsBegin, aBoundsEnd);
   2295  return aData[y * aStride + bpp * x + c];
   2296 }
   2297 
   2298 static inline int32_t ColorAtPoint(const uint8_t* aData, ptrdiff_t aStride,
   2299                                   const uint8_t* aBoundsBegin,
   2300                                   const uint8_t* aBoundsEnd, int32_t x,
   2301                                   int32_t y) {
   2302  DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x, aBoundsBegin,
   2303                                    aBoundsEnd);
   2304  return *(uint32_t*)(aData + y * aStride + 4 * x);
   2305 }
   2306 
   2307 // Accepts fractional x & y and does bilinear interpolation.
   2308 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
   2309 static inline uint8_t ColorComponentAtPoint(
   2310    const uint8_t* aData, ptrdiff_t aStride, const uint8_t* aBoundsBegin,
   2311    const uint8_t* aBoundsEnd, Float x, Float y, ptrdiff_t bpp, ptrdiff_t c) {
   2312  const uint32_t f = 256;
   2313  const int32_t lx = floor(x);
   2314  const int32_t ly = floor(y);
   2315  const int32_t tux = uint32_t((x - lx) * f);
   2316  const int32_t tlx = f - tux;
   2317  const int32_t tuy = uint32_t((y - ly) * f);
   2318  const int32_t tly = f - tuy;
   2319  const uint8_t& cll = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
   2320                                             aBoundsEnd, lx, ly, bpp, c);
   2321  const uint8_t& cul = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
   2322                                             aBoundsEnd, lx + 1, ly, bpp, c);
   2323  const uint8_t& clu = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
   2324                                             aBoundsEnd, lx, ly + 1, bpp, c);
   2325  const uint8_t& cuu = ColorComponentAtPoint(
   2326      aData, aStride, aBoundsBegin, aBoundsEnd, lx + 1, ly + 1, bpp, c);
   2327  return ((cll * tlx + cul * tux) * tly + (clu * tlx + cuu * tux) * tuy +
   2328          f * f / 2) /
   2329         (f * f);
   2330 }
   2331 
   2332 static int32_t ClampToNonZero(int32_t a) { return a * (a >= 0); }
   2333 
   2334 template <typename CoordType>
   2335 static void ConvolvePixel(const uint8_t* aSourceData, uint8_t* aTargetData,
   2336                          int32_t aWidth, int32_t aHeight,
   2337                          int32_t aSourceStride, int32_t aTargetStride,
   2338                          const uint8_t* aSourceBegin,
   2339                          const uint8_t* aSourceEnd, int32_t aX, int32_t aY,
   2340                          const int32_t* aKernel, int32_t aBias, int32_t shiftL,
   2341                          int32_t shiftR, bool aPreserveAlpha, int32_t aOrderX,
   2342                          int32_t aOrderY, int32_t aTargetX, int32_t aTargetY,
   2343                          CoordType aKernelUnitLengthX,
   2344                          CoordType aKernelUnitLengthY) {
   2345  int32_t sum[4] = {0, 0, 0, 0};
   2346  int32_t offsets[4] = {
   2347      B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G,
   2348      B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
   2349  int32_t channels = aPreserveAlpha ? 3 : 4;
   2350  int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
   2351 
   2352  for (int32_t y = 0; y < aOrderY; y++) {
   2353    CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
   2354    for (int32_t x = 0; x < aOrderX; x++) {
   2355      CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
   2356      for (int32_t i = 0; i < channels; i++) {
   2357        sum[i] +=
   2358            aKernel[aOrderX * y + x] *
   2359            ColorComponentAtPoint(aSourceData, aSourceStride, aSourceBegin,
   2360                                  aSourceEnd, sampleX, sampleY, 4, offsets[i]);
   2361      }
   2362    }
   2363  }
   2364  for (int32_t i = 0; i < channels; i++) {
   2365    int32_t clamped =
   2366        umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
   2367    aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
   2368        (clamped + roundingAddition) << shiftR >> shiftL;
   2369  }
   2370  if (aPreserveAlpha) {
   2371    aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
   2372        aSourceData[aY * aSourceStride + 4 * aX +
   2373                    B8G8R8A8_COMPONENT_BYTEOFFSET_A];
   2374  }
   2375 }
   2376 
   2377 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::Render(
   2378    const IntRect& aRect) {
   2379  if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
   2380      mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
   2381    return DoRender(aRect, (int32_t)mKernelUnitLength.width,
   2382                    (int32_t)mKernelUnitLength.height);
   2383  }
   2384  return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
   2385 }
   2386 
   2387 static std::vector<Float> ReversedVector(const std::vector<Float>& aVector) {
   2388  size_t length = aVector.size();
   2389  std::vector<Float> result(length, 0);
   2390  for (size_t i = 0; i < length; i++) {
   2391    result[length - 1 - i] = aVector[i];
   2392  }
   2393  return result;
   2394 }
   2395 
   2396 static std::vector<Float> ScaledVector(const std::vector<Float>& aVector,
   2397                                       Float aDivisor) {
   2398  size_t length = aVector.size();
   2399  std::vector<Float> result(length, 0);
   2400  for (size_t i = 0; i < length; i++) {
   2401    result[i] = aVector[i] / aDivisor;
   2402  }
   2403  return result;
   2404 }
   2405 
   2406 static Float MaxVectorSum(const std::vector<Float>& aVector) {
   2407  Float sum = 0;
   2408  size_t length = aVector.size();
   2409  for (size_t i = 0; i < length; i++) {
   2410    if (aVector[i] > 0) {
   2411      sum += aVector[i];
   2412    }
   2413  }
   2414  return sum;
   2415 }
   2416 
   2417 // Returns shiftL and shiftR in such a way that
   2418 // a << shiftL >> shiftR is roughly a * aFloat.
   2419 static void TranslateDoubleToShifts(double aDouble, int32_t& aShiftL,
   2420                                    int32_t& aShiftR) {
   2421  aShiftL = 0;
   2422  aShiftR = 0;
   2423  if (aDouble <= 0) {
   2424    MOZ_CRASH("GFX: TranslateDoubleToShifts");
   2425  }
   2426  if (aDouble < 1) {
   2427    while (1 << (aShiftR + 1) < 1 / aDouble) {
   2428      aShiftR++;
   2429    }
   2430  } else {
   2431    while (1 << (aShiftL + 1) < aDouble) {
   2432      aShiftL++;
   2433    }
   2434  }
   2435 }
   2436 
   2437 template <typename CoordType>
   2438 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::DoRender(
   2439    const IntRect& aRect, CoordType aKernelUnitLengthX,
   2440    CoordType aKernelUnitLengthY) {
   2441  if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
   2442      mKernelMatrix.size() !=
   2443          uint32_t(mKernelSize.width * mKernelSize.height) ||
   2444      !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
   2445      mDivisor == 0) {
   2446    return Factory::CreateDataSourceSurface(aRect.Size(),
   2447                                            SurfaceFormat::B8G8R8A8, true);
   2448  }
   2449 
   2450  IntRect srcRect = InflatedSourceRect(aRect);
   2451 
   2452  // Inflate the source rect by another pixel because the bilinear filtering in
   2453  // ColorComponentAtPoint may want to access the margins.
   2454  srcRect.Inflate(1);
   2455 
   2456  RefPtr<DataSourceSurface> input =
   2457      GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect,
   2458                                NEED_COLOR_CHANNELS, mEdgeMode, &mRenderRect);
   2459 
   2460  if (!input) {
   2461    return nullptr;
   2462  }
   2463 
   2464  RefPtr<DataSourceSurface> target = Factory::CreateDataSourceSurface(
   2465      aRect.Size(), SurfaceFormat::B8G8R8A8, true);
   2466  if (MOZ2D_WARN_IF(!target)) {
   2467    return nullptr;
   2468  }
   2469 
   2470  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
   2471 
   2472  DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
   2473  DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   2474  if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
   2475    return nullptr;
   2476  }
   2477 
   2478  uint8_t* sourceData =
   2479      DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
   2480  int32_t sourceStride = sourceMap.GetStride();
   2481  uint8_t* sourceBegin = sourceMap.GetData();
   2482  uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
   2483  uint8_t* targetData = targetMap.GetData();
   2484  int32_t targetStride = targetMap.GetStride();
   2485 
   2486  // Why exactly are we reversing the kernel?
   2487  std::vector<Float> kernel = ReversedVector(mKernelMatrix);
   2488  kernel = ScaledVector(kernel, mDivisor);
   2489  Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
   2490                                MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
   2491  maxResultAbs = std::max(maxResultAbs, 1.0f);
   2492 
   2493  double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
   2494  MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0,
   2495             "badly chosen float-to-int scale");
   2496  int32_t shiftL, shiftR;
   2497  TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
   2498  double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
   2499  MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0,
   2500             "badly chosen float-to-int scale");
   2501 
   2502  int32_t* intKernel = new int32_t[kernel.size()];
   2503  for (size_t i = 0; i < kernel.size(); i++) {
   2504    intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
   2505  }
   2506  int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
   2507 
   2508  for (int32_t y = 0; y < aRect.Height(); y++) {
   2509    for (int32_t x = 0; x < aRect.Width(); x++) {
   2510      ConvolvePixel(sourceData, targetData, aRect.Width(), aRect.Height(),
   2511                    sourceStride, targetStride, sourceBegin, sourceEnd, x, y,
   2512                    intKernel, bias, shiftL, shiftR, mPreserveAlpha,
   2513                    mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
   2514                    aKernelUnitLengthX, aKernelUnitLengthY);
   2515    }
   2516  }
   2517  delete[] intKernel;
   2518 
   2519  return target.forget();
   2520 }
   2521 
   2522 void FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(
   2523    const IntRect& aRect) {
   2524  RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
   2525 }
   2526 
   2527 IntRect FilterNodeConvolveMatrixSoftware::MapRectToSource(
   2528    const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
   2529  return MapInputRectToSource(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect),
   2530                              aMax, aSourceNode);
   2531 }
   2532 
   2533 IntRect FilterNodeConvolveMatrixSoftware::InflatedSourceRect(
   2534    const IntRect& aDestRect) {
   2535  if (aDestRect.IsEmpty()) {
   2536    return IntRect();
   2537  }
   2538 
   2539  IntMargin margin;
   2540  margin.left = static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width));
   2541  margin.top = static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height));
   2542  margin.right = static_cast<int32_t>(
   2543      ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width));
   2544  margin.bottom = static_cast<int32_t>(
   2545      ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height));
   2546 
   2547  IntRect srcRect = aDestRect;
   2548  srcRect.Inflate(margin);
   2549  return srcRect;
   2550 }
   2551 
   2552 IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect(
   2553    const IntRect& aSourceRect) {
   2554  if (aSourceRect.IsEmpty()) {
   2555    return IntRect();
   2556  }
   2557 
   2558  IntMargin margin;
   2559  margin.left = static_cast<int32_t>(
   2560      ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width));
   2561  margin.top = static_cast<int32_t>(
   2562      ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height));
   2563  margin.right =
   2564      static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width));
   2565  margin.bottom =
   2566      static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height));
   2567 
   2568  IntRect destRect = aSourceRect;
   2569  destRect.Inflate(margin);
   2570  return destRect;
   2571 }
   2572 
   2573 IntRect FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(
   2574    const IntRect& aRect) {
   2575  if (!mPreserveAlpha && mBias > 0) {
   2576    // we transform transparent colors into non-transparent colors in this case
   2577    return aRect;
   2578  }
   2579  IntRect srcRequest = InflatedSourceRect(aRect);
   2580  IntRect srcOutput = GetInputRectInRect(IN_CONVOLVE_MATRIX_IN, srcRequest);
   2581  return InflatedDestRect(srcOutput).Intersect(aRect);
   2582 }
   2583 
   2584 FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
   2585    : mScale(0.0f), mChannelX(COLOR_CHANNEL_R), mChannelY(COLOR_CHANNEL_G) {}
   2586 
   2587 int32_t FilterNodeDisplacementMapSoftware::InputIndex(
   2588    uint32_t aInputEnumIndex) {
   2589  switch (aInputEnumIndex) {
   2590    case IN_DISPLACEMENT_MAP_IN:
   2591      return 0;
   2592    case IN_DISPLACEMENT_MAP_IN2:
   2593      return 1;
   2594    default:
   2595      return -1;
   2596  }
   2597 }
   2598 
   2599 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
   2600                                                     Float aScale) {
   2601  MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
   2602  mScale = aScale;
   2603  Invalidate();
   2604 }
   2605 
   2606 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
   2607                                                     uint32_t aValue) {
   2608  switch (aIndex) {
   2609    case ATT_DISPLACEMENT_MAP_X_CHANNEL:
   2610      mChannelX = static_cast<ColorChannel>(aValue);
   2611      break;
   2612    case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
   2613      mChannelY = static_cast<ColorChannel>(aValue);
   2614      break;
   2615    default:
   2616      MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
   2617  }
   2618  Invalidate();
   2619 }
   2620 
   2621 already_AddRefed<DataSourceSurface> FilterNodeDisplacementMapSoftware::Render(
   2622    const IntRect& aRect) {
   2623  IntRect srcRect = InflatedSourceOrDestRect(aRect);
   2624  RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
   2625      IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
   2626  RefPtr<DataSourceSurface> map = GetInputDataSourceSurface(
   2627      IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
   2628  RefPtr<DataSourceSurface> target =
   2629      Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
   2630  if (MOZ2D_WARN_IF(!(input && map && target))) {
   2631    return nullptr;
   2632  }
   2633 
   2634  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
   2635 
   2636  DataSourceSurface::ScopedMap inputMap(input, DataSourceSurface::READ);
   2637  DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ);
   2638  DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   2639  if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() &&
   2640                      targetMap.IsMapped()))) {
   2641    return nullptr;
   2642  }
   2643 
   2644  uint8_t* sourceData =
   2645      DataAtOffset(input, inputMap.GetMappedSurface(), offset);
   2646  int32_t sourceStride = inputMap.GetStride();
   2647  uint8_t* sourceBegin = inputMap.GetData();
   2648  uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
   2649  uint8_t* mapData = mapMap.GetData();
   2650  int32_t mapStride = mapMap.GetStride();
   2651  uint8_t* targetData = targetMap.GetData();
   2652  int32_t targetStride = targetMap.GetStride();
   2653 
   2654  static const ptrdiff_t channelMap[4] = {
   2655      B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G,
   2656      B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
   2657  uint16_t xChannel = channelMap[mChannelX];
   2658  uint16_t yChannel = channelMap[mChannelY];
   2659 
   2660  float scaleOver255 = mScale / 255.0f;
   2661  float scaleAdjustment = -0.5f * mScale;
   2662 
   2663  for (int32_t y = 0; y < aRect.Height(); y++) {
   2664    for (int32_t x = 0; x < aRect.Width(); x++) {
   2665      uint32_t mapIndex = y * mapStride + 4 * x;
   2666      uint32_t targIndex = y * targetStride + 4 * x;
   2667      int32_t sourceX =
   2668          x + scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
   2669      int32_t sourceY =
   2670          y + scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
   2671      *(uint32_t*)(targetData + targIndex) = ColorAtPoint(
   2672          sourceData, sourceStride, sourceBegin, sourceEnd, sourceX, sourceY);
   2673    }
   2674 
   2675    // Keep valgrind happy.
   2676    PodZero(&targetData[y * targetStride + 4 * aRect.Width()],
   2677            targetStride - 4 * aRect.Width());
   2678  }
   2679 
   2680  return target.forget();
   2681 }
   2682 
   2683 void FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(
   2684    const IntRect& aRect) {
   2685  RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
   2686  RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
   2687 }
   2688 
   2689 IntRect FilterNodeDisplacementMapSoftware::MapRectToSource(
   2690    const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
   2691  IntRect result =
   2692      MapInputRectToSource(IN_DISPLACEMENT_MAP_IN,
   2693                           InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
   2694  result.OrWith(
   2695      MapInputRectToSource(IN_DISPLACEMENT_MAP_IN2, aRect, aMax, aSourceNode));
   2696  return result;
   2697 }
   2698 
   2699 IntRect FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(
   2700    const IntRect& aDestOrSourceRect) {
   2701  IntRect sourceOrDestRect = aDestOrSourceRect;
   2702  sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
   2703  return sourceOrDestRect;
   2704 }
   2705 
   2706 IntRect FilterNodeDisplacementMapSoftware::GetOutputRectInRect(
   2707    const IntRect& aRect) {
   2708  IntRect srcRequest = InflatedSourceOrDestRect(aRect);
   2709  IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
   2710  return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
   2711 }
   2712 
   2713 FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
   2714    : mNumOctaves(0),
   2715      mSeed(0),
   2716      mStitchable(false),
   2717      mType(TURBULENCE_TYPE_TURBULENCE) {}
   2718 
   2719 int32_t FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex) {
   2720  return -1;
   2721 }
   2722 
   2723 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
   2724                                                const Size& aBaseFrequency) {
   2725  switch (aIndex) {
   2726    case ATT_TURBULENCE_BASE_FREQUENCY:
   2727      mBaseFrequency = aBaseFrequency;
   2728      break;
   2729    default:
   2730      MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
   2731      break;
   2732  }
   2733  Invalidate();
   2734 }
   2735 
   2736 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
   2737                                                const IntRect& aRect) {
   2738  switch (aIndex) {
   2739    case ATT_TURBULENCE_RECT:
   2740      mRenderRect = aRect;
   2741      break;
   2742    default:
   2743      MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
   2744      break;
   2745  }
   2746  Invalidate();
   2747 }
   2748 
   2749 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
   2750                                                bool aStitchable) {
   2751  MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
   2752  mStitchable = aStitchable;
   2753  Invalidate();
   2754 }
   2755 
   2756 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
   2757                                                uint32_t aValue) {
   2758  switch (aIndex) {
   2759    case ATT_TURBULENCE_NUM_OCTAVES:
   2760      mNumOctaves = aValue;
   2761      break;
   2762    case ATT_TURBULENCE_SEED:
   2763      mSeed = aValue;
   2764      break;
   2765    case ATT_TURBULENCE_TYPE:
   2766      mType = static_cast<TurbulenceType>(aValue);
   2767      break;
   2768    default:
   2769      MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
   2770      break;
   2771  }
   2772  Invalidate();
   2773 }
   2774 
   2775 already_AddRefed<DataSourceSurface> FilterNodeTurbulenceSoftware::Render(
   2776    const IntRect& aRect) {
   2777  return FilterProcessing::RenderTurbulence(
   2778      aRect.Size(), aRect.TopLeft(), mBaseFrequency, mSeed, mNumOctaves, mType,
   2779      mStitchable, Rect(mRenderRect));
   2780 }
   2781 
   2782 IntRect FilterNodeTurbulenceSoftware::GetOutputRectInRect(
   2783    const IntRect& aRect) {
   2784  return aRect.Intersect(mRenderRect);
   2785 }
   2786 
   2787 IntRect FilterNodeTurbulenceSoftware::MapRectToSource(const IntRect& aRect,
   2788                                                      const IntRect& aMax,
   2789                                                      FilterNode* aSourceNode) {
   2790  return IntRect();
   2791 }
   2792 
   2793 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
   2794    : mK1(0), mK2(0), mK3(0), mK4(0) {}
   2795 
   2796 int32_t FilterNodeArithmeticCombineSoftware::InputIndex(
   2797    uint32_t aInputEnumIndex) {
   2798  switch (aInputEnumIndex) {
   2799    case IN_ARITHMETIC_COMBINE_IN:
   2800      return 0;
   2801    case IN_ARITHMETIC_COMBINE_IN2:
   2802      return 1;
   2803    default:
   2804      return -1;
   2805  }
   2806 }
   2807 
   2808 void FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
   2809                                                       const Float* aFloat,
   2810                                                       uint32_t aSize) {
   2811  MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
   2812  MOZ_RELEASE_ASSERT(aSize == 4);
   2813 
   2814  mK1 = aFloat[0];
   2815  mK2 = aFloat[1];
   2816  mK3 = aFloat[2];
   2817  mK4 = aFloat[3];
   2818 
   2819  Invalidate();
   2820 }
   2821 
   2822 already_AddRefed<DataSourceSurface> FilterNodeArithmeticCombineSoftware::Render(
   2823    const IntRect& aRect) {
   2824  RefPtr<DataSourceSurface> input1 = GetInputDataSourceSurface(
   2825      IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
   2826  RefPtr<DataSourceSurface> input2 = GetInputDataSourceSurface(
   2827      IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
   2828  if (!input1 && !input2) {
   2829    return nullptr;
   2830  }
   2831 
   2832  // If one input is null, treat it as transparent by adjusting the factors.
   2833  Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
   2834  if (!input1) {
   2835    k1 = 0.0f;
   2836    k2 = 0.0f;
   2837    input1 = input2;
   2838  }
   2839 
   2840  if (!input2) {
   2841    k1 = 0.0f;
   2842    k3 = 0.0f;
   2843    input2 = input1;
   2844  }
   2845 
   2846  return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3,
   2847                                                  k4);
   2848 }
   2849 
   2850 void FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(
   2851    const IntRect& aRect) {
   2852  RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
   2853  RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
   2854 }
   2855 
   2856 IntRect FilterNodeArithmeticCombineSoftware::MapRectToSource(
   2857    const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
   2858  IntRect result =
   2859      MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN, aRect, aMax, aSourceNode);
   2860  result.OrWith(MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN2, aRect, aMax,
   2861                                     aSourceNode));
   2862  return result;
   2863 }
   2864 
   2865 IntRect FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(
   2866    const IntRect& aRect) {
   2867  if (mK4 > 0.0f) {
   2868    return aRect;
   2869  }
   2870  IntRect rectFrom1 =
   2871      GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
   2872  IntRect rectFrom2 =
   2873      GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
   2874  IntRect result;
   2875  if (mK1 > 0.0f) {
   2876    result = rectFrom1.Intersect(rectFrom2);
   2877  }
   2878  if (mK2 > 0.0f) {
   2879    result = result.Union(rectFrom1);
   2880  }
   2881  if (mK3 > 0.0f) {
   2882    result = result.Union(rectFrom2);
   2883  }
   2884  return result;
   2885 }
   2886 
   2887 FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
   2888    : mOperator(COMPOSITE_OPERATOR_OVER) {}
   2889 
   2890 int32_t FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex) {
   2891  return aInputEnumIndex - IN_COMPOSITE_IN_START;
   2892 }
   2893 
   2894 void FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex,
   2895                                               uint32_t aCompositeOperator) {
   2896  MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
   2897  mOperator = static_cast<CompositeOperator>(aCompositeOperator);
   2898  Invalidate();
   2899 }
   2900 
   2901 already_AddRefed<DataSourceSurface> FilterNodeCompositeSoftware::Render(
   2902    const IntRect& aRect) {
   2903  RefPtr<DataSourceSurface> start = GetInputDataSourceSurface(
   2904      IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
   2905  RefPtr<DataSourceSurface> dest = Factory::CreateDataSourceSurface(
   2906      aRect.Size(), SurfaceFormat::B8G8R8A8, true);
   2907  if (MOZ2D_WARN_IF(!dest)) {
   2908    return nullptr;
   2909  }
   2910 
   2911  if (start) {
   2912    CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
   2913  }
   2914 
   2915  for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
   2916    RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
   2917        IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
   2918    if (input) {
   2919      FilterProcessing::ApplyComposition(input, dest, mOperator);
   2920    } else {
   2921      // We need to treat input as transparent. Depending on the composite
   2922      // operator, different things happen to dest.
   2923      switch (mOperator) {
   2924        case COMPOSITE_OPERATOR_OVER:
   2925        case COMPOSITE_OPERATOR_ATOP:
   2926        case COMPOSITE_OPERATOR_XOR:
   2927        case COMPOSITE_OPERATOR_LIGHTER:
   2928          // dest is unchanged.
   2929          break;
   2930        case COMPOSITE_OPERATOR_OUT:
   2931          // dest is now transparent, but it can become non-transparent again
   2932          // when compositing additional inputs.
   2933          ClearDataSourceSurface(dest);
   2934          break;
   2935        case COMPOSITE_OPERATOR_IN:
   2936          // Transparency always wins. We're completely transparent now and
   2937          // no additional input can get rid of that transparency.
   2938          return nullptr;
   2939      }
   2940    }
   2941  }
   2942  return dest.forget();
   2943 }
   2944 
   2945 void FilterNodeCompositeSoftware::RequestFromInputsForRect(
   2946    const IntRect& aRect) {
   2947  for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
   2948    RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
   2949  }
   2950 }
   2951 
   2952 IntRect FilterNodeCompositeSoftware::MapRectToSource(const IntRect& aRect,
   2953                                                     const IntRect& aMax,
   2954                                                     FilterNode* aSourceNode) {
   2955  IntRect result;
   2956  for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
   2957    result.OrWith(MapInputRectToSource(IN_COMPOSITE_IN_START + inputIndex,
   2958                                       aRect, aMax, aSourceNode));
   2959  }
   2960  return result;
   2961 }
   2962 
   2963 IntRect FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect) {
   2964  IntRect rect;
   2965  for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
   2966    IntRect inputRect =
   2967        GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
   2968    if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
   2969      rect = rect.Intersect(inputRect);
   2970    } else {
   2971      rect = rect.Union(inputRect);
   2972    }
   2973  }
   2974  return rect;
   2975 }
   2976 
   2977 int32_t FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex) {
   2978  switch (aInputEnumIndex) {
   2979    case IN_GAUSSIAN_BLUR_IN:
   2980      return 0;
   2981    default:
   2982      return -1;
   2983  }
   2984 }
   2985 
   2986 already_AddRefed<DataSourceSurface> FilterNodeBlurXYSoftware::Render(
   2987    const IntRect& aRect) {
   2988  Size sigmaXY = StdDeviationXY();
   2989  IntSize d =
   2990      GaussianBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
   2991 
   2992  if (d.width == 0 && d.height == 0) {
   2993    return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
   2994  }
   2995 
   2996  IntRect srcRect = InflatedSourceOrDestRect(aRect);
   2997  RefPtr<DataSourceSurface> input =
   2998      GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
   2999  if (!input) {
   3000    return nullptr;
   3001  }
   3002 
   3003  RefPtr<DataSourceSurface> target =
   3004      Factory::CreateDataSourceSurface(srcRect.Size(), input->GetFormat());
   3005  if (MOZ2D_WARN_IF(!target)) {
   3006    return nullptr;
   3007  }
   3008  CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
   3009 
   3010  DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
   3011  if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
   3012    return nullptr;
   3013  }
   3014  GaussianBlur blur(Point(sigmaXY.width, sigmaXY.height));
   3015  blur.Blur(targetMap.GetData(), targetMap.GetStride(), target->GetSize(),
   3016            target->GetFormat());
   3017 
   3018  return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
   3019 }
   3020 
   3021 void FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect& aRect) {
   3022  RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
   3023 }
   3024 
   3025 IntRect FilterNodeBlurXYSoftware::MapRectToSource(const IntRect& aRect,
   3026                                                  const IntRect& aMax,
   3027                                                  FilterNode* aSourceNode) {
   3028  return MapInputRectToSource(
   3029      IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
   3030 }
   3031 
   3032 IntRect FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(
   3033    const IntRect& aDestRect) {
   3034  Size sigmaXY = StdDeviationXY();
   3035  IntSize d =
   3036      GaussianBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
   3037  IntRect srcRect = aDestRect;
   3038  srcRect.Inflate(d);
   3039  return srcRect;
   3040 }
   3041 
   3042 IntRect FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect) {
   3043  IntRect srcRequest = InflatedSourceOrDestRect(aRect);
   3044  IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
   3045  return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
   3046 }
   3047 
   3048 FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
   3049    : mStdDeviation(0) {}
   3050 
   3051 static float ClampStdDeviation(float aStdDeviation) {
   3052  // Cap software blur radius for performance reasons.
   3053  return std::clamp(aStdDeviation, 0.f, 100.f);
   3054 }
   3055 
   3056 void FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
   3057                                                  float aStdDeviation) {
   3058  switch (aIndex) {
   3059    case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
   3060      mStdDeviation = ClampStdDeviation(aStdDeviation);
   3061      break;
   3062    default:
   3063      MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
   3064  }
   3065  Invalidate();
   3066 }
   3067 
   3068 Size FilterNodeGaussianBlurSoftware::StdDeviationXY() {
   3069  return Size(mStdDeviation, mStdDeviation);
   3070 }
   3071 
   3072 FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
   3073    : mStdDeviation(0.0), mBlurDirection(BLUR_DIRECTION_X) {}
   3074 
   3075 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
   3076                                                     Float aStdDeviation) {
   3077  switch (aIndex) {
   3078    case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
   3079      mStdDeviation = ClampStdDeviation(aStdDeviation);
   3080      break;
   3081    default:
   3082      MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
   3083  }
   3084  Invalidate();
   3085 }
   3086 
   3087 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
   3088                                                     uint32_t aBlurDirection) {
   3089  switch (aIndex) {
   3090    case ATT_DIRECTIONAL_BLUR_DIRECTION:
   3091      mBlurDirection = (BlurDirection)aBlurDirection;
   3092      break;
   3093    default:
   3094      MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
   3095  }
   3096  Invalidate();
   3097 }
   3098 
   3099 Size FilterNodeDirectionalBlurSoftware::StdDeviationXY() {
   3100  float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
   3101  float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
   3102  return Size(sigmaX, sigmaY);
   3103 }
   3104 
   3105 int32_t FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex) {
   3106  switch (aInputEnumIndex) {
   3107    case IN_CROP_IN:
   3108      return 0;
   3109    default:
   3110      return -1;
   3111  }
   3112 }
   3113 
   3114 void FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
   3115                                          const Rect& aSourceRect) {
   3116  MOZ_ASSERT(aIndex == ATT_CROP_RECT);
   3117  Rect srcRect = aSourceRect;
   3118  srcRect.Round();
   3119  if (!srcRect.ToIntRect(&mCropRect)) {
   3120    mCropRect = IntRect();
   3121  }
   3122  Invalidate();
   3123 }
   3124 
   3125 already_AddRefed<DataSourceSurface> FilterNodeCropSoftware::Render(
   3126    const IntRect& aRect) {
   3127  return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
   3128 }
   3129 
   3130 void FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect& aRect) {
   3131  RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
   3132 }
   3133 
   3134 IntRect FilterNodeCropSoftware::MapRectToSource(const IntRect& aRect,
   3135                                                const IntRect& aMax,
   3136                                                FilterNode* aSourceNode) {
   3137  return MapInputRectToSource(IN_CROP_IN, aRect.Intersect(mCropRect), aMax,
   3138                              aSourceNode);
   3139 }
   3140 
   3141 IntRect FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect) {
   3142  return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
   3143 }
   3144 
   3145 int32_t FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
   3146  switch (aInputEnumIndex) {
   3147    case IN_PREMULTIPLY_IN:
   3148      return 0;
   3149    default:
   3150      return -1;
   3151  }
   3152 }
   3153 
   3154 already_AddRefed<DataSourceSurface> FilterNodePremultiplySoftware::Render(
   3155    const IntRect& aRect) {
   3156  RefPtr<DataSourceSurface> input =
   3157      GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
   3158  return input ? Premultiply(input) : nullptr;
   3159 }
   3160 
   3161 void FilterNodePremultiplySoftware::RequestFromInputsForRect(
   3162    const IntRect& aRect) {
   3163  RequestInputRect(IN_PREMULTIPLY_IN, aRect);
   3164 }
   3165 
   3166 IntRect FilterNodePremultiplySoftware::MapRectToSource(
   3167    const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
   3168  return MapInputRectToSource(IN_PREMULTIPLY_IN, aRect, aMax, aSourceNode);
   3169 }
   3170 
   3171 IntRect FilterNodePremultiplySoftware::GetOutputRectInRect(
   3172    const IntRect& aRect) {
   3173  return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
   3174 }
   3175 
   3176 int32_t FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
   3177  switch (aInputEnumIndex) {
   3178    case IN_UNPREMULTIPLY_IN:
   3179      return 0;
   3180    default:
   3181      return -1;
   3182  }
   3183 }
   3184 
   3185 already_AddRefed<DataSourceSurface> FilterNodeUnpremultiplySoftware::Render(
   3186    const IntRect& aRect) {
   3187  RefPtr<DataSourceSurface> input =
   3188      GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
   3189  return input ? Unpremultiply(input) : nullptr;
   3190 }
   3191 
   3192 void FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(
   3193    const IntRect& aRect) {
   3194  RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
   3195 }
   3196 
   3197 IntRect FilterNodeUnpremultiplySoftware::MapRectToSource(
   3198    const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
   3199  return MapInputRectToSource(IN_UNPREMULTIPLY_IN, aRect, aMax, aSourceNode);
   3200 }
   3201 
   3202 IntRect FilterNodeUnpremultiplySoftware::GetOutputRectInRect(
   3203    const IntRect& aRect) {
   3204  return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
   3205 }
   3206 
   3207 void FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex, Float aValue) {
   3208  MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE);
   3209  mValue = aValue;
   3210  Invalidate();
   3211 }
   3212 
   3213 int32_t FilterNodeOpacitySoftware::InputIndex(uint32_t aInputEnumIndex) {
   3214  switch (aInputEnumIndex) {
   3215    case IN_OPACITY_IN:
   3216      return 0;
   3217    default:
   3218      return -1;
   3219  }
   3220 }
   3221 
   3222 already_AddRefed<DataSourceSurface> FilterNodeOpacitySoftware::Render(
   3223    const IntRect& aRect) {
   3224  RefPtr<DataSourceSurface> input =
   3225      GetInputDataSourceSurface(IN_OPACITY_IN, aRect);
   3226  return input ? Opacity(input, mValue) : nullptr;
   3227 }
   3228 
   3229 void FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect& aRect) {
   3230  RequestInputRect(IN_OPACITY_IN, aRect);
   3231 }
   3232 
   3233 IntRect FilterNodeOpacitySoftware::MapRectToSource(const IntRect& aRect,
   3234                                                   const IntRect& aMax,
   3235                                                   FilterNode* aSourceNode) {
   3236  return MapInputRectToSource(IN_OPACITY_IN, aRect, aMax, aSourceNode);
   3237 }
   3238 
   3239 IntRect FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect& aRect) {
   3240  return GetInputRectInRect(IN_OPACITY_IN, aRect);
   3241 }
   3242 
   3243 bool PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) {
   3244  switch (aIndex) {
   3245    case ATT_POINT_LIGHT_POSITION:
   3246      mPosition = aPoint;
   3247      break;
   3248    default:
   3249      return false;
   3250  }
   3251  return true;
   3252 }
   3253 
   3254 SpotLightSoftware::SpotLightSoftware()
   3255    : mSpecularFocus(0), mLimitingConeAngle(0), mLimitingConeCos(1) {}
   3256 
   3257 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) {
   3258  switch (aIndex) {
   3259    case ATT_SPOT_LIGHT_POSITION:
   3260      mPosition = aPoint;
   3261      break;
   3262    case ATT_SPOT_LIGHT_POINTS_AT:
   3263      mPointsAt = aPoint;
   3264      break;
   3265    default:
   3266      return false;
   3267  }
   3268  return true;
   3269 }
   3270 
   3271 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
   3272  switch (aIndex) {
   3273    case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
   3274      mLimitingConeAngle = aValue;
   3275      break;
   3276    case ATT_SPOT_LIGHT_FOCUS:
   3277      mSpecularFocus = aValue;
   3278      break;
   3279    default:
   3280      return false;
   3281  }
   3282  return true;
   3283 }
   3284 
   3285 DistantLightSoftware::DistantLightSoftware() : mAzimuth(0), mElevation(0) {}
   3286 
   3287 bool DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
   3288  switch (aIndex) {
   3289    case ATT_DISTANT_LIGHT_AZIMUTH:
   3290      mAzimuth = aValue;
   3291      break;
   3292    case ATT_DISTANT_LIGHT_ELEVATION:
   3293      mElevation = aValue;
   3294      break;
   3295    default:
   3296      return false;
   3297  }
   3298  return true;
   3299 }
   3300 
   3301 static inline Point3D Normalized(const Point3D& vec) {
   3302  Point3D copy(vec);
   3303  copy.Normalize();
   3304  return copy;
   3305 }
   3306 
   3307 template <typename LightType, typename LightingType>
   3308 FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(
   3309    const char* aTypeName)
   3310    : mSurfaceScale(0)
   3311 #if defined(MOZILLA_INTERNAL_API) && defined(NS_BUILD_REFCNT_LOGGING)
   3312      ,
   3313      mTypeName(aTypeName)
   3314 #endif
   3315 {
   3316 }
   3317 
   3318 template <typename LightType, typename LightingType>
   3319 int32_t FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(
   3320    uint32_t aInputEnumIndex) {
   3321  switch (aInputEnumIndex) {
   3322    case IN_LIGHTING_IN:
   3323      return 0;
   3324    default:
   3325      return -1;
   3326  }
   3327 }
   3328 
   3329 template <typename LightType, typename LightingType>
   3330 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
   3331    uint32_t aIndex, const Point3D& aPoint) {
   3332  if (mLight.SetAttribute(aIndex, aPoint)) {
   3333    Invalidate();
   3334    return;
   3335  }
   3336  MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
   3337 }
   3338 
   3339 template <typename LightType, typename LightingType>
   3340 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
   3341    uint32_t aIndex, Float aValue) {
   3342  if (mLight.SetAttribute(aIndex, aValue) ||
   3343      mLighting.SetAttribute(aIndex, aValue)) {
   3344    Invalidate();
   3345    return;
   3346  }
   3347  switch (aIndex) {
   3348    case ATT_LIGHTING_SURFACE_SCALE:
   3349      mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
   3350      break;
   3351    default:
   3352      MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
   3353  }
   3354  Invalidate();
   3355 }
   3356 
   3357 template <typename LightType, typename LightingType>
   3358 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
   3359    uint32_t aIndex, const Size& aKernelUnitLength) {
   3360  switch (aIndex) {
   3361    case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
   3362      mKernelUnitLength = aKernelUnitLength;
   3363      break;
   3364    default:
   3365      MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
   3366  }
   3367  Invalidate();
   3368 }
   3369 
   3370 template <typename LightType, typename LightingType>
   3371 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
   3372    uint32_t aIndex, const DeviceColor& aColor) {
   3373  MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
   3374  mColor = aColor;
   3375  Invalidate();
   3376 }
   3377 
   3378 template <typename LightType, typename LightingType>
   3379 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
   3380    uint32_t aIndex, const IntRect& aRenderRect) {
   3381  MOZ_ASSERT(aIndex == ATT_LIGHTING_RENDER_RECT);
   3382  mRenderRect = aRenderRect;
   3383  Invalidate();
   3384 }
   3385 
   3386 template <typename LightType, typename LightingType>
   3387 IntRect
   3388 FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(
   3389    const IntRect& aRect) {
   3390  return aRect;
   3391 }
   3392 
   3393 Point3D PointLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
   3394  return Normalized(mPosition - aTargetPoint);
   3395 }
   3396 
   3397 uint32_t PointLightSoftware::GetColor(uint32_t aLightColor,
   3398                                      const Point3D& aVectorToLight) {
   3399  return aLightColor;
   3400 }
   3401 
   3402 void SpotLightSoftware::Prepare() {
   3403  mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
   3404  mLimitingConeCos =
   3405      std::max<double>(cos(mLimitingConeAngle * M_PI / 180.0), 0.0);
   3406  mPowCache.CacheForExponent(mSpecularFocus);
   3407 }
   3408 
   3409 Point3D SpotLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
   3410  return Normalized(mPosition - aTargetPoint);
   3411 }
   3412 
   3413 uint32_t SpotLightSoftware::GetColor(uint32_t aLightColor,
   3414                                     const Point3D& aVectorToLight) {
   3415  union {
   3416    uint32_t color;
   3417    uint8_t colorC[4];
   3418  };
   3419 
   3420  Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
   3421  if (!mPowCache.HasPowerTable()) {
   3422    dot *= (dot >= mLimitingConeCos);
   3423    color = aLightColor;
   3424    colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] *= dot;
   3425    colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] *= dot;
   3426    colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] *= dot;
   3427  } else {
   3428    color = aLightColor;
   3429    uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
   3430    uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
   3431    MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits),
   3432               "pow() result must not exceed 1.0");
   3433    colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
   3434        uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >>
   3435                PowCache::sOutputIntPrecisionBits);
   3436    colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
   3437        uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >>
   3438                PowCache::sOutputIntPrecisionBits);
   3439    colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
   3440        uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >>
   3441                PowCache::sOutputIntPrecisionBits);
   3442  }
   3443  colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
   3444  return color;
   3445 }
   3446 
   3447 void DistantLightSoftware::Prepare() {
   3448  const double radPerDeg = M_PI / 180.0;
   3449  mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
   3450  mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
   3451  mVectorToLight.z = sin(mElevation * radPerDeg);
   3452 }
   3453 
   3454 Point3D DistantLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
   3455  return mVectorToLight;
   3456 }
   3457 
   3458 uint32_t DistantLightSoftware::GetColor(uint32_t aLightColor,
   3459                                        const Point3D& aVectorToLight) {
   3460  return aLightColor;
   3461 }
   3462 
   3463 template <typename CoordType>
   3464 static Point3D GenerateNormal(const uint8_t* data, int32_t stride,
   3465                              uint8_t* boundsBegin, uint8_t* boundsEnd,
   3466                              int32_t x, int32_t y, float surfaceScale,
   3467                              CoordType dx, CoordType dy) {
   3468  const uint8_t* index = data + y * stride + x;
   3469 
   3470  CoordType zero = 0;
   3471 
   3472  // See this for source of constants:
   3473  //   http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
   3474  int16_t normalX = -1 * ColorComponentAtPoint(index, stride, boundsBegin,
   3475                                               boundsEnd, -dx, -dy, 1, 0) +
   3476                    1 * ColorComponentAtPoint(index, stride, boundsBegin,
   3477                                              boundsEnd, dx, -dy, 1, 0) +
   3478                    -2 * ColorComponentAtPoint(index, stride, boundsBegin,
   3479                                               boundsEnd, -dx, zero, 1, 0) +
   3480                    2 * ColorComponentAtPoint(index, stride, boundsBegin,
   3481                                              boundsEnd, dx, zero, 1, 0) +
   3482                    -1 * ColorComponentAtPoint(index, stride, boundsBegin,
   3483                                               boundsEnd, -dx, dy, 1, 0) +
   3484                    1 * ColorComponentAtPoint(index, stride, boundsBegin,
   3485                                              boundsEnd, dx, dy, 1, 0);
   3486 
   3487  int16_t normalY = -1 * ColorComponentAtPoint(index, stride, boundsBegin,
   3488                                               boundsEnd, -dx, -dy, 1, 0) +
   3489                    -2 * ColorComponentAtPoint(index, stride, boundsBegin,
   3490                                               boundsEnd, zero, -dy, 1, 0) +
   3491                    -1 * ColorComponentAtPoint(index, stride, boundsBegin,
   3492                                               boundsEnd, dx, -dy, 1, 0) +
   3493                    1 * ColorComponentAtPoint(index, stride, boundsBegin,
   3494                                              boundsEnd, -dx, dy, 1, 0) +
   3495                    2 * ColorComponentAtPoint(index, stride, boundsBegin,
   3496                                              boundsEnd, zero, dy, 1, 0) +
   3497                    1 * ColorComponentAtPoint(index, stride, boundsBegin,
   3498                                              boundsEnd, dx, dy, 1, 0);
   3499 
   3500  Point3D normal;
   3501  normal.x = -surfaceScale * normalX / 4.0f;
   3502  normal.y = -surfaceScale * normalY / 4.0f;
   3503  normal.z = 255;
   3504  return Normalized(normal);
   3505 }
   3506 
   3507 template <typename LightType, typename LightingType>
   3508 already_AddRefed<DataSourceSurface>
   3509 FilterNodeLightingSoftware<LightType, LightingType>::Render(
   3510    const IntRect& aRect) {
   3511  if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
   3512      mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
   3513    return DoRender(aRect, (int32_t)mKernelUnitLength.width,
   3514                    (int32_t)mKernelUnitLength.height);
   3515  }
   3516  return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
   3517 }
   3518 
   3519 template <typename LightType, typename LightingType>
   3520 void FilterNodeLightingSoftware<
   3521    LightType, LightingType>::RequestFromInputsForRect(const IntRect& aRect) {
   3522  IntRect srcRect = aRect;
   3523  srcRect.Inflate(ceil(mKernelUnitLength.width),
   3524                  ceil(mKernelUnitLength.height));
   3525  RequestInputRect(IN_LIGHTING_IN, srcRect);
   3526 }
   3527 
   3528 template <typename LightType, typename LightingType>
   3529 IntRect FilterNodeLightingSoftware<LightType, LightingType>::MapRectToSource(
   3530    const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
   3531  IntRect srcRect = aRect;
   3532  srcRect.Inflate(ceil(mKernelUnitLength.width),
   3533                  ceil(mKernelUnitLength.height));
   3534  return MapInputRectToSource(IN_LIGHTING_IN, srcRect, aMax, aSourceNode);
   3535 }
   3536 
   3537 template <typename LightType, typename LightingType>
   3538 template <typename CoordType>
   3539 already_AddRefed<DataSourceSurface>
   3540 FilterNodeLightingSoftware<LightType, LightingType>::DoRender(
   3541    const IntRect& aRect, CoordType aKernelUnitLengthX,
   3542    CoordType aKernelUnitLengthY) {
   3543  MOZ_ASSERT(aKernelUnitLengthX > 0,
   3544             "aKernelUnitLengthX can be a negative or zero value");
   3545  MOZ_ASSERT(aKernelUnitLengthY > 0,
   3546             "aKernelUnitLengthY can be a negative or zero value");
   3547 
   3548  IntRect srcRect = aRect;
   3549  IntSize size = aRect.Size();
   3550  srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
   3551                  ceil(float(aKernelUnitLengthY)));
   3552 
   3553  // Inflate the source rect by another pixel because the bilinear filtering in
   3554  // ColorComponentAtPoint may want to access the margins.
   3555  srcRect.Inflate(1);
   3556 
   3557  IntRect srcRectInRenderRect = srcRect.Intersect(mRenderRect);
   3558 
   3559  RefPtr<DataSourceSurface> input =
   3560      GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8,
   3561                                EDGE_MODE_DUPLICATE, &srcRectInRenderRect);
   3562 
   3563  if (!input) {
   3564    return nullptr;
   3565  }
   3566 
   3567  if (input->GetFormat() != SurfaceFormat::A8) {
   3568    input = FilterProcessing::ExtractAlpha(input);
   3569  }
   3570 
   3571  RefPtr<DataSourceSurface> target =
   3572      Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
   3573  if (MOZ2D_WARN_IF(!target)) {
   3574    return nullptr;
   3575  }
   3576 
   3577  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
   3578 
   3579  DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
   3580  DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   3581  if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) {
   3582    return nullptr;
   3583  }
   3584 
   3585  uint8_t* sourceData =
   3586      DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
   3587  int32_t sourceStride = sourceMap.GetStride();
   3588  uint8_t* sourceBegin = sourceMap.GetData();
   3589  uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
   3590  uint8_t* targetData = targetMap.GetData();
   3591  int32_t targetStride = targetMap.GetStride();
   3592 
   3593  uint32_t lightColor = ColorToBGRA(mColor);
   3594  mLight.Prepare();
   3595  mLighting.Prepare();
   3596 
   3597  for (int32_t y = 0; y < size.height; y++) {
   3598    for (int32_t x = 0; x < size.width; x++) {
   3599      int32_t sourceIndex = y * sourceStride + x;
   3600      int32_t targetIndex = y * targetStride + 4 * x;
   3601 
   3602      Point3D normal =
   3603          GenerateNormal(sourceData, sourceStride, sourceBegin, sourceEnd, x, y,
   3604                         mSurfaceScale, aKernelUnitLengthX, aKernelUnitLengthY);
   3605 
   3606      IntPoint pointInFilterSpace(aRect.X() + x, aRect.Y() + y);
   3607      Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
   3608      Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
   3609      Point3D rayDir = mLight.GetVectorToLight(pt);
   3610      uint32_t color = mLight.GetColor(lightColor, rayDir);
   3611 
   3612      *(uint32_t*)(targetData + targetIndex) =
   3613          mLighting.LightPixel(normal, rayDir, color);
   3614    }
   3615 
   3616    // Zero padding to keep valgrind happy.
   3617    PodZero(&targetData[y * targetStride + 4 * size.width],
   3618            targetStride - 4 * size.width);
   3619  }
   3620 
   3621  return target.forget();
   3622 }
   3623 
   3624 DiffuseLightingSoftware::DiffuseLightingSoftware() : mDiffuseConstant(0) {}
   3625 
   3626 bool DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
   3627  switch (aIndex) {
   3628    case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
   3629      mDiffuseConstant = aValue;
   3630      break;
   3631    default:
   3632      return false;
   3633  }
   3634  return true;
   3635 }
   3636 
   3637 uint32_t DiffuseLightingSoftware::LightPixel(const Point3D& aNormal,
   3638                                             const Point3D& aVectorToLight,
   3639                                             uint32_t aColor) {
   3640  Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
   3641  Float diffuseNL = mDiffuseConstant * dotNL;
   3642 
   3643  union {
   3644    uint32_t bgra;
   3645    uint8_t components[4];
   3646  } color = {aColor};
   3647  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = umin(
   3648      uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]),
   3649      255U);
   3650  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = umin(
   3651      uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]),
   3652      255U);
   3653  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = umin(
   3654      uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]),
   3655      255U);
   3656  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
   3657  return color.bgra;
   3658 }
   3659 
   3660 SpecularLightingSoftware::SpecularLightingSoftware()
   3661    : mSpecularConstant(0), mSpecularExponent(0), mSpecularConstantInt(0) {}
   3662 
   3663 bool SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
   3664  switch (aIndex) {
   3665    case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
   3666      mSpecularConstant = std::clamp(aValue, 0.0f, 255.0f);
   3667      break;
   3668    case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
   3669      mSpecularExponent = std::clamp(aValue, 1.0f, 128.0f);
   3670      break;
   3671    default:
   3672      return false;
   3673  }
   3674  return true;
   3675 }
   3676 
   3677 void SpecularLightingSoftware::Prepare() {
   3678  mPowCache.CacheForExponent(mSpecularExponent);
   3679  mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
   3680 }
   3681 
   3682 uint32_t SpecularLightingSoftware::LightPixel(const Point3D& aNormal,
   3683                                              const Point3D& aVectorToLight,
   3684                                              uint32_t aColor) {
   3685  Point3D vectorToEye(0, 0, 1);
   3686  Point3D halfwayVector = aVectorToLight + vectorToEye;
   3687  Float halfwayLength = halfwayVector.Length();
   3688  if (halfwayLength > 0) {
   3689    halfwayVector /= halfwayLength;
   3690  }
   3691  Float dotNH = aNormal.DotProduct(halfwayVector);
   3692  uint16_t dotNHi =
   3693      uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
   3694  // The exponent for specular is in [1,128] range, so we don't need to check
   3695  // and optimize for the "default power table" scenario here.
   3696  MOZ_ASSERT(mPowCache.HasPowerTable());
   3697  uint32_t specularNHi =
   3698      uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
   3699 
   3700  union {
   3701    uint32_t bgra;
   3702    uint8_t components[4];
   3703  } color = {aColor};
   3704  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
   3705      umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >>
   3706               PowCache::sOutputIntPrecisionBits,
   3707           255U);
   3708  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
   3709      umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >>
   3710               PowCache::sOutputIntPrecisionBits,
   3711           255U);
   3712  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
   3713      umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >>
   3714               PowCache::sOutputIntPrecisionBits,
   3715           255U);
   3716 
   3717  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
   3718      umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
   3719           umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
   3720                color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
   3721  return color.bgra;
   3722 }
   3723 
   3724 }  // namespace gfx
   3725 }  // namespace mozilla