tor-browser

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

SurfaceFilters.h (53696B)


      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 /**
      8 * This header contains various SurfaceFilter implementations that apply
      9 * transformations to image data, for usage with SurfacePipe.
     10 */
     11 
     12 #ifndef mozilla_image_SurfaceFilters_h
     13 #define mozilla_image_SurfaceFilters_h
     14 
     15 #include <algorithm>
     16 #include <stdint.h>
     17 #include <string.h>
     18 
     19 #include "mozilla/Likely.h"
     20 #include "mozilla/Maybe.h"
     21 #include "mozilla/UniquePtr.h"
     22 #include "mozilla/gfx/2D.h"
     23 #include "mozilla/gfx/Swizzle.h"
     24 #include "skia/src/core/SkBlitRow.h"
     25 
     26 #include "DownscalingFilter.h"
     27 #include "SurfaceCache.h"
     28 #include "SurfacePipe.h"
     29 
     30 namespace mozilla {
     31 namespace image {
     32 
     33 //////////////////////////////////////////////////////////////////////////////
     34 // SwizzleFilter
     35 //////////////////////////////////////////////////////////////////////////////
     36 
     37 template <typename Next>
     38 class SwizzleFilter;
     39 
     40 /**
     41 * A configuration struct for SwizzleFilter.
     42 */
     43 struct SwizzleConfig {
     44  template <typename Next>
     45  using Filter = SwizzleFilter<Next>;
     46  gfx::SurfaceFormat mInFormat;
     47  gfx::SurfaceFormat mOutFormat;
     48  bool mPremultiplyAlpha;
     49 };
     50 
     51 /**
     52 * SwizzleFilter performs premultiplication, swizzling and unpacking on
     53 * rows written to it. It can use accelerated methods to perform these
     54 * operations if supported on the platform.
     55 *
     56 * The 'Next' template parameter specifies the next filter in the chain.
     57 */
     58 template <typename Next>
     59 class SwizzleFilter final : public SurfaceFilter {
     60 public:
     61  SwizzleFilter() : mSwizzleFn(nullptr) {}
     62 
     63  template <typename... Rest>
     64  nsresult Configure(const SwizzleConfig& aConfig, const Rest&... aRest) {
     65    nsresult rv = mNext.Configure(aRest...);
     66    if (NS_FAILED(rv)) {
     67      return rv;
     68    }
     69 
     70    if (aConfig.mPremultiplyAlpha) {
     71      mSwizzleFn = gfx::PremultiplyRow(aConfig.mInFormat, aConfig.mOutFormat);
     72    } else {
     73      mSwizzleFn = gfx::SwizzleRow(aConfig.mInFormat, aConfig.mOutFormat);
     74    }
     75 
     76    if (!mSwizzleFn) {
     77      return NS_ERROR_INVALID_ARG;
     78    }
     79 
     80    ConfigureFilter(mNext.InputSize(), sizeof(uint32_t));
     81    return NS_OK;
     82  }
     83 
     84  Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
     85    return mNext.TakeInvalidRect();
     86  }
     87 
     88 protected:
     89  uint8_t* DoResetToFirstRow() override { return mNext.ResetToFirstRow(); }
     90 
     91  uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
     92    uint8_t* rowPtr = mNext.CurrentRowPointer();
     93    if (!rowPtr) {
     94      return nullptr;  // We already got all the input rows we expect.
     95    }
     96 
     97    mSwizzleFn(aInputRow, rowPtr, mNext.InputSize().width);
     98    return mNext.AdvanceRow();
     99  }
    100 
    101  uint8_t* DoAdvanceRow() override {
    102    return DoAdvanceRowFromBuffer(mNext.CurrentRowPointer());
    103  }
    104 
    105  Next mNext;  /// The next SurfaceFilter in the chain.
    106 
    107  gfx::SwizzleRowFn mSwizzleFn;
    108 };
    109 
    110 //////////////////////////////////////////////////////////////////////////////
    111 // ColorManagementFilter
    112 //////////////////////////////////////////////////////////////////////////////
    113 
    114 template <typename Next>
    115 class ColorManagementFilter;
    116 
    117 /**
    118 * A configuration struct for ColorManagementFilter.
    119 */
    120 struct ColorManagementConfig {
    121  template <typename Next>
    122  using Filter = ColorManagementFilter<Next>;
    123  qcms_transform* mTransform;
    124 };
    125 
    126 /**
    127 * ColorManagementFilter performs color transforms with qcms on rows written
    128 * to it.
    129 *
    130 * The 'Next' template parameter specifies the next filter in the chain.
    131 */
    132 template <typename Next>
    133 class ColorManagementFilter final : public SurfaceFilter {
    134 public:
    135  ColorManagementFilter() : mTransform(nullptr) {}
    136 
    137  template <typename... Rest>
    138  nsresult Configure(const ColorManagementConfig& aConfig,
    139                     const Rest&... aRest) {
    140    nsresult rv = mNext.Configure(aRest...);
    141    if (NS_FAILED(rv)) {
    142      return rv;
    143    }
    144 
    145    if (!aConfig.mTransform) {
    146      return NS_ERROR_INVALID_ARG;
    147    }
    148 
    149    mTransform = aConfig.mTransform;
    150    ConfigureFilter(mNext.InputSize(), sizeof(uint32_t));
    151    return NS_OK;
    152  }
    153 
    154  Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
    155    return mNext.TakeInvalidRect();
    156  }
    157 
    158 protected:
    159  uint8_t* DoResetToFirstRow() override { return mNext.ResetToFirstRow(); }
    160 
    161  uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
    162    qcms_transform_data(mTransform, aInputRow, mNext.CurrentRowPointer(),
    163                        mNext.InputSize().width);
    164    return mNext.AdvanceRow();
    165  }
    166 
    167  uint8_t* DoAdvanceRow() override {
    168    return DoAdvanceRowFromBuffer(mNext.CurrentRowPointer());
    169  }
    170 
    171  Next mNext;  /// The next SurfaceFilter in the chain.
    172 
    173  qcms_transform* mTransform;
    174 };
    175 
    176 //////////////////////////////////////////////////////////////////////////////
    177 // DeinterlacingFilter
    178 //////////////////////////////////////////////////////////////////////////////
    179 
    180 template <typename PixelType, typename Next>
    181 class DeinterlacingFilter;
    182 
    183 /**
    184 * A configuration struct for DeinterlacingFilter.
    185 *
    186 * The 'PixelType' template parameter should be either uint32_t (for output to a
    187 * SurfaceSink) or uint8_t (for output to a PalettedSurfaceSink).
    188 */
    189 template <typename PixelType>
    190 struct DeinterlacingConfig {
    191  template <typename Next>
    192  using Filter = DeinterlacingFilter<PixelType, Next>;
    193  bool mProgressiveDisplay;  /// If true, duplicate rows during deinterlacing
    194                             /// to make progressive display look better, at
    195                             /// the cost of some performance.
    196 };
    197 
    198 /**
    199 * DeinterlacingFilter performs deinterlacing by reordering the rows that are
    200 * written to it.
    201 *
    202 * The 'PixelType' template parameter should be either uint32_t (for output to a
    203 * SurfaceSink) or uint8_t (for output to a PalettedSurfaceSink).
    204 *
    205 * The 'Next' template parameter specifies the next filter in the chain.
    206 */
    207 template <typename PixelType, typename Next>
    208 class DeinterlacingFilter final : public SurfaceFilter {
    209 public:
    210  DeinterlacingFilter()
    211      : mInputRow(0), mOutputRow(0), mPass(0), mProgressiveDisplay(true) {}
    212 
    213  template <typename... Rest>
    214  nsresult Configure(const DeinterlacingConfig<PixelType>& aConfig,
    215                     const Rest&... aRest) {
    216    nsresult rv = mNext.Configure(aRest...);
    217    if (NS_FAILED(rv)) {
    218      return rv;
    219    }
    220 
    221    gfx::IntSize outputSize = mNext.InputSize();
    222    mProgressiveDisplay = aConfig.mProgressiveDisplay;
    223 
    224    const CheckedUint32 bufferSize = CheckedUint32(outputSize.width) *
    225                                     CheckedUint32(outputSize.height) *
    226                                     CheckedUint32(sizeof(PixelType));
    227 
    228    // Use the size of the SurfaceCache as a heuristic to avoid gigantic
    229    // allocations. Even if DownscalingFilter allowed us to allocate space for
    230    // the output image, the deinterlacing buffer may still be too big, and
    231    // fallible allocation won't always save us in the presence of overcommit.
    232    if (!bufferSize.isValid() || !SurfaceCache::CanHold(bufferSize.value())) {
    233      return NS_ERROR_OUT_OF_MEMORY;
    234    }
    235 
    236    // Allocate the buffer, which contains deinterlaced scanlines of the image.
    237    // The buffer is necessary so that we can output rows which have already
    238    // been deinterlaced again on subsequent passes. Since a later stage in the
    239    // pipeline may be transforming the rows it receives (for example, by
    240    // downscaling them), the rows may no longer exist in their original form on
    241    // the surface itself.
    242    mBuffer.reset(new (fallible) uint8_t[bufferSize.value()]);
    243    if (MOZ_UNLIKELY(!mBuffer)) {
    244      return NS_ERROR_OUT_OF_MEMORY;
    245    }
    246 
    247    // Clear the buffer to avoid writing uninitialized memory to the output.
    248    memset(mBuffer.get(), 0, bufferSize.value());
    249 
    250    ConfigureFilter(outputSize, sizeof(PixelType));
    251    return NS_OK;
    252  }
    253 
    254  Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
    255    return mNext.TakeInvalidRect();
    256  }
    257 
    258 protected:
    259  uint8_t* DoResetToFirstRow() override {
    260    mNext.ResetToFirstRow();
    261    mPass = 0;
    262    mInputRow = 0;
    263    mOutputRow = InterlaceOffset(mPass);
    264    return GetRowPointer(mOutputRow);
    265  }
    266 
    267  uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
    268    CopyInputRow(aInputRow);
    269    return DoAdvanceRow();
    270  }
    271 
    272  uint8_t* DoAdvanceRow() override {
    273    if (mPass >= 4) {
    274      return nullptr;  // We already finished all passes.
    275    }
    276    if (mInputRow >= InputSize().height) {
    277      return nullptr;  // We already got all the input rows we expect.
    278    }
    279 
    280    // Duplicate from the first Haeberli row to the remaining Haeberli rows
    281    // within the buffer.
    282    DuplicateRows(
    283        HaeberliOutputStartRow(mPass, mProgressiveDisplay, mOutputRow),
    284        HaeberliOutputUntilRow(mPass, mProgressiveDisplay, InputSize(),
    285                               mOutputRow));
    286 
    287    // Write the current set of Haeberli rows (which contains the current row)
    288    // to the next stage in the pipeline.
    289    OutputRows(HaeberliOutputStartRow(mPass, mProgressiveDisplay, mOutputRow),
    290               HaeberliOutputUntilRow(mPass, mProgressiveDisplay, InputSize(),
    291                                      mOutputRow));
    292 
    293    // Determine which output row the next input row corresponds to.
    294    bool advancedPass = false;
    295    uint32_t stride = InterlaceStride(mPass);
    296    int32_t nextOutputRow = mOutputRow + stride;
    297    while (nextOutputRow >= InputSize().height) {
    298      // Copy any remaining rows from the buffer.
    299      if (!advancedPass) {
    300        OutputRows(HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
    301                                          InputSize(), mOutputRow),
    302                   InputSize().height);
    303      }
    304 
    305      // We finished the current pass; advance to the next one.
    306      mPass++;
    307      if (mPass >= 4) {
    308        return nullptr;  // Finished all passes.
    309      }
    310 
    311      // Tell the next pipeline stage that we're starting the next pass.
    312      mNext.ResetToFirstRow();
    313 
    314      // Update our state to reflect the pass change.
    315      advancedPass = true;
    316      stride = InterlaceStride(mPass);
    317      nextOutputRow = InterlaceOffset(mPass);
    318    }
    319 
    320    MOZ_ASSERT(nextOutputRow >= 0);
    321    MOZ_ASSERT(nextOutputRow < InputSize().height);
    322 
    323    MOZ_ASSERT(
    324        HaeberliOutputStartRow(mPass, mProgressiveDisplay, nextOutputRow) >= 0);
    325    MOZ_ASSERT(HaeberliOutputStartRow(mPass, mProgressiveDisplay,
    326                                      nextOutputRow) < InputSize().height);
    327    MOZ_ASSERT(HaeberliOutputStartRow(mPass, mProgressiveDisplay,
    328                                      nextOutputRow) <= nextOutputRow);
    329 
    330    MOZ_ASSERT(HaeberliOutputUntilRow(mPass, mProgressiveDisplay, InputSize(),
    331                                      nextOutputRow) >= 0);
    332    MOZ_ASSERT(HaeberliOutputUntilRow(mPass, mProgressiveDisplay, InputSize(),
    333                                      nextOutputRow) <= InputSize().height);
    334    MOZ_ASSERT(HaeberliOutputUntilRow(mPass, mProgressiveDisplay, InputSize(),
    335                                      nextOutputRow) > nextOutputRow);
    336 
    337    int32_t nextHaeberliOutputRow =
    338        HaeberliOutputStartRow(mPass, mProgressiveDisplay, nextOutputRow);
    339 
    340    // Copy rows from the buffer until we reach the desired output row.
    341    if (advancedPass) {
    342      OutputRows(0, nextHaeberliOutputRow);
    343    } else {
    344      OutputRows(HaeberliOutputUntilRow(mPass, mProgressiveDisplay, InputSize(),
    345                                        mOutputRow),
    346                 nextHaeberliOutputRow);
    347    }
    348 
    349    // Update our position within the buffer.
    350    mInputRow++;
    351    mOutputRow = nextOutputRow;
    352 
    353    // We'll actually write to the first Haeberli output row, then copy it until
    354    // we reach the last Haeberli output row. The assertions above make sure
    355    // this always includes mOutputRow.
    356    return GetRowPointer(nextHaeberliOutputRow);
    357  }
    358 
    359 private:
    360  static uint32_t InterlaceOffset(uint32_t aPass) {
    361    MOZ_ASSERT(aPass < 4, "Invalid pass");
    362    static const uint8_t offset[] = {0, 4, 2, 1};
    363    return offset[aPass];
    364  }
    365 
    366  static uint32_t InterlaceStride(uint32_t aPass) {
    367    MOZ_ASSERT(aPass < 4, "Invalid pass");
    368    static const uint8_t stride[] = {8, 8, 4, 2};
    369    return stride[aPass];
    370  }
    371 
    372  static int32_t HaeberliOutputStartRow(uint32_t aPass,
    373                                        bool aProgressiveDisplay,
    374                                        int32_t aOutputRow) {
    375    MOZ_ASSERT(aPass < 4, "Invalid pass");
    376    static const uint8_t firstRowOffset[] = {3, 1, 0, 0};
    377 
    378    if (aProgressiveDisplay) {
    379      return std::max(aOutputRow - firstRowOffset[aPass], 0);
    380    } else {
    381      return aOutputRow;
    382    }
    383  }
    384 
    385  static int32_t HaeberliOutputUntilRow(uint32_t aPass,
    386                                        bool aProgressiveDisplay,
    387                                        const gfx::IntSize& aInputSize,
    388                                        int32_t aOutputRow) {
    389    MOZ_ASSERT(aPass < 4, "Invalid pass");
    390    static const uint8_t lastRowOffset[] = {4, 2, 1, 0};
    391 
    392    if (aProgressiveDisplay) {
    393      return std::min(aOutputRow + lastRowOffset[aPass],
    394                      aInputSize.height - 1) +
    395             1;  // Add one because this is an open interval on the right.
    396    } else {
    397      return aOutputRow + 1;
    398    }
    399  }
    400 
    401  void DuplicateRows(int32_t aStart, int32_t aUntil) {
    402    MOZ_ASSERT(aStart >= 0);
    403    MOZ_ASSERT(aUntil >= 0);
    404 
    405    if (aUntil <= aStart || aStart >= InputSize().height) {
    406      return;
    407    }
    408 
    409    // The source row is the first row in the range.
    410    const uint8_t* sourceRowPointer = GetRowPointer(aStart);
    411 
    412    // We duplicate the source row into each subsequent row in the range.
    413    for (int32_t destRow = aStart + 1; destRow < aUntil; ++destRow) {
    414      uint8_t* destRowPointer = GetRowPointer(destRow);
    415      memcpy(destRowPointer, sourceRowPointer,
    416             InputSize().width * sizeof(PixelType));
    417    }
    418  }
    419 
    420  void OutputRows(int32_t aStart, int32_t aUntil) {
    421    MOZ_ASSERT(aStart >= 0);
    422    MOZ_ASSERT(aUntil >= 0);
    423 
    424    if (aUntil <= aStart || aStart >= InputSize().height) {
    425      return;
    426    }
    427 
    428    for (int32_t rowToOutput = aStart; rowToOutput < aUntil; ++rowToOutput) {
    429      mNext.WriteBuffer(
    430          reinterpret_cast<PixelType*>(GetRowPointer(rowToOutput)));
    431    }
    432  }
    433 
    434  uint8_t* GetRowPointer(uint32_t aRow) const {
    435 #ifdef DEBUG
    436    uint64_t offset64 = uint64_t(aRow) * uint64_t(InputSize().width) *
    437                        uint64_t(sizeof(PixelType));
    438    uint64_t bufferLength = uint64_t(InputSize().width) *
    439                            uint64_t(InputSize().height) *
    440                            uint64_t(sizeof(PixelType));
    441    MOZ_ASSERT(offset64 < bufferLength, "Start of row is outside of image");
    442    MOZ_ASSERT(
    443        offset64 + uint64_t(InputSize().width) * uint64_t(sizeof(PixelType)) <=
    444            bufferLength,
    445        "End of row is outside of image");
    446 #endif
    447    uint32_t offset = aRow * InputSize().width * sizeof(PixelType);
    448    return mBuffer.get() + offset;
    449  }
    450 
    451  Next mNext;  /// The next SurfaceFilter in the chain.
    452 
    453  UniquePtr<uint8_t[]> mBuffer;  /// The buffer used to store reordered rows.
    454  int32_t mInputRow;             /// The current row we're reading. (0-indexed)
    455  int32_t mOutputRow;            /// The current row we're writing. (0-indexed)
    456  uint8_t mPass;                 /// Which pass we're on. (0-indexed)
    457  bool mProgressiveDisplay;      /// If true, duplicate rows to optimize for
    458                                 /// progressive display.
    459 };
    460 
    461 //////////////////////////////////////////////////////////////////////////////
    462 // BlendAnimationFilter
    463 //////////////////////////////////////////////////////////////////////////////
    464 
    465 template <typename Next>
    466 class BlendAnimationFilter;
    467 
    468 /**
    469 * A configuration struct for BlendAnimationFilter.
    470 */
    471 struct BlendAnimationConfig {
    472  template <typename Next>
    473  using Filter = BlendAnimationFilter<Next>;
    474  Decoder* mDecoder;  /// The decoder producing the animation.
    475 };
    476 
    477 /**
    478 * BlendAnimationFilter turns a partial image as part of an animation into a
    479 * complete frame given its frame rect, blend method, and the base frame's
    480 * data buffer, frame rect and disposal method. Any excess data caused by a
    481 * frame rect not being contained by the output size will be discarded.
    482 *
    483 * The base frame is an already produced complete frame from the animation.
    484 * It may be any previous frame depending on the disposal method, although
    485 * most often it will be the immediate previous frame to the current we are
    486 * generating.
    487 *
    488 * The 'Next' template parameter specifies the next filter in the chain.
    489 */
    490 template <typename Next>
    491 class BlendAnimationFilter final : public SurfaceFilter {
    492 public:
    493  BlendAnimationFilter()
    494      : mRow(0),
    495        mRowLength(0),
    496        mRecycleRow(0),
    497        mRecycleRowMost(0),
    498        mRecycleRowOffset(0),
    499        mRecycleRowLength(0),
    500        mClearRow(0),
    501        mClearRowMost(0),
    502        mClearPrefixLength(0),
    503        mClearInfixOffset(0),
    504        mClearInfixLength(0),
    505        mClearPostfixOffset(0),
    506        mClearPostfixLength(0),
    507        mOverProc(nullptr),
    508        mBaseFrameStartPtr(nullptr),
    509        mBaseFrameRowPtr(nullptr) {}
    510 
    511  template <typename... Rest>
    512  nsresult Configure(const BlendAnimationConfig& aConfig,
    513                     const Rest&... aRest) {
    514    nsresult rv = mNext.Configure(aRest...);
    515    if (NS_FAILED(rv)) {
    516      return rv;
    517    }
    518 
    519    imgFrame* currentFrame = aConfig.mDecoder->GetCurrentFrame();
    520    if (!currentFrame) {
    521      MOZ_ASSERT_UNREACHABLE("Decoder must have current frame!");
    522      return NS_ERROR_FAILURE;
    523    }
    524 
    525    mFrameRect = mUnclampedFrameRect = currentFrame->GetBlendRect();
    526    gfx::IntSize outputSize = mNext.InputSize();
    527    mRowLength = outputSize.width * sizeof(uint32_t);
    528 
    529    // Forbid frame rects with negative size.
    530    if (mUnclampedFrameRect.width < 0 || mUnclampedFrameRect.height < 0) {
    531      return NS_ERROR_FAILURE;
    532    }
    533 
    534    // Clamp mFrameRect to the output size.
    535    gfx::IntRect outputRect(0, 0, outputSize.width, outputSize.height);
    536    mFrameRect = mFrameRect.Intersect(outputRect);
    537    bool fullFrame = outputRect.IsEqualEdges(mFrameRect);
    538 
    539    // If there's no intersection, |mFrameRect| will be an empty rect positioned
    540    // at the maximum of |inputRect|'s and |aFrameRect|'s coordinates, which is
    541    // not what we want. Force it to (0, 0) sized 0 x 0 in that case.
    542    if (mFrameRect.IsEmpty()) {
    543      mFrameRect.SetRect(0, 0, 0, 0);
    544    }
    545 
    546    BlendMethod blendMethod = currentFrame->GetBlendMethod();
    547    switch (blendMethod) {
    548      default:
    549        blendMethod = BlendMethod::SOURCE;
    550        MOZ_FALLTHROUGH_ASSERT("Unexpected blend method!");
    551      case BlendMethod::SOURCE:
    552        // Default, overwrites base frame data (if any) with new.
    553        break;
    554      case BlendMethod::OVER:
    555        // OVER only has an impact on the output if we have new data to blend
    556        // with.
    557        if (mFrameRect.IsEmpty()) {
    558          blendMethod = BlendMethod::SOURCE;
    559        }
    560        break;
    561    }
    562 
    563    // Determine what we need to clear and what we need to copy. If this frame
    564    // is a full frame and uses source blending, there is no need to consider
    565    // the disposal method of the previous frame.
    566    gfx::IntRect dirtyRect(outputRect);
    567    gfx::IntRect clearRect;
    568    if (!fullFrame || blendMethod != BlendMethod::SOURCE) {
    569      const RawAccessFrameRef& restoreFrame =
    570          aConfig.mDecoder->GetRestoreFrameRef();
    571      if (restoreFrame) {
    572        MOZ_ASSERT(restoreFrame->GetSize() == outputSize);
    573        MOZ_ASSERT(restoreFrame->IsFinished());
    574 
    575        // We can safely use this pointer without holding a RawAccessFrameRef
    576        // because the decoder will keep it alive for us.
    577        mBaseFrameStartPtr = restoreFrame.Data();
    578        MOZ_ASSERT(mBaseFrameStartPtr);
    579 
    580        gfx::IntRect restoreBlendRect = restoreFrame->GetBoundedBlendRect();
    581        gfx::IntRect restoreDirtyRect = aConfig.mDecoder->GetRestoreDirtyRect();
    582        switch (restoreFrame->GetDisposalMethod()) {
    583          default:
    584          case DisposalMethod::RESTORE_PREVIOUS:
    585            MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
    586          case DisposalMethod::NOT_SPECIFIED:
    587          case DisposalMethod::KEEP:
    588            dirtyRect = mFrameRect.Union(restoreDirtyRect);
    589            break;
    590          case DisposalMethod::CLEAR:
    591            // We only need to clear if the rect is outside the frame rect (i.e.
    592            // overwrites a non-overlapping area) or the blend method may cause
    593            // us to combine old data and new.
    594            if (!mFrameRect.Contains(restoreBlendRect) ||
    595                blendMethod == BlendMethod::OVER) {
    596              clearRect = restoreBlendRect;
    597            }
    598 
    599            // If we are clearing the whole frame, we do not need to retain a
    600            // reference to the base frame buffer.
    601            if (outputRect.IsEqualEdges(clearRect)) {
    602              mBaseFrameStartPtr = nullptr;
    603            } else {
    604              dirtyRect = mFrameRect.Union(restoreDirtyRect).Union(clearRect);
    605            }
    606            break;
    607        }
    608      } else if (!fullFrame) {
    609        // This must be the first frame, clear everything.
    610        clearRect = outputRect;
    611      }
    612    }
    613 
    614    // We may be able to reuse parts of our underlying buffer that we are
    615    // writing the new frame to. The recycle rect gives us the invalidation
    616    // region which needs to be copied from the restore frame.
    617    const gfx::IntRect& recycleRect = aConfig.mDecoder->GetRecycleRect();
    618    mRecycleRow = recycleRect.y;
    619    mRecycleRowMost = recycleRect.YMost();
    620    mRecycleRowOffset = recycleRect.x * sizeof(uint32_t);
    621    mRecycleRowLength = recycleRect.width * sizeof(uint32_t);
    622 
    623    if (!clearRect.IsEmpty()) {
    624      // The clear rect interacts with the recycle rect because we need to copy
    625      // the prefix and postfix data from the base frame. The one thing we do
    626      // know is that the infix area is always cleared explicitly.
    627      mClearRow = clearRect.y;
    628      mClearRowMost = clearRect.YMost();
    629      mClearInfixOffset = clearRect.x * sizeof(uint32_t);
    630      mClearInfixLength = clearRect.width * sizeof(uint32_t);
    631 
    632      // The recycle row offset is where we need to begin copying base frame
    633      // data for a row. If this offset begins after or at the clear infix
    634      // offset, then there is no prefix data at all.
    635      if (mClearInfixOffset > mRecycleRowOffset) {
    636        mClearPrefixLength = mClearInfixOffset - mRecycleRowOffset;
    637      }
    638 
    639      // Similar to the prefix, if the postfix offset begins outside the recycle
    640      // rect, then we know we already have all the data we need.
    641      mClearPostfixOffset = mClearInfixOffset + mClearInfixLength;
    642      size_t recycleRowEndOffset = mRecycleRowOffset + mRecycleRowLength;
    643      if (mClearPostfixOffset < recycleRowEndOffset) {
    644        mClearPostfixLength = recycleRowEndOffset - mClearPostfixOffset;
    645      }
    646    }
    647 
    648    // The dirty rect, or delta between the current frame and the previous frame
    649    // (chronologically, not necessarily the restore frame) is the last
    650    // animation parameter we need to initialize the new frame with.
    651    currentFrame->SetDirtyRect(dirtyRect);
    652 
    653    if (!mBaseFrameStartPtr) {
    654      // Switch to SOURCE if no base frame to ensure we don't allocate an
    655      // intermediate buffer below. OVER does nothing without the base frame
    656      // data.
    657      blendMethod = BlendMethod::SOURCE;
    658    }
    659 
    660    // Skia provides arch-specific accelerated methods to perform blending.
    661    // Note that this is an internal Skia API and may be prone to change,
    662    // but we avoid the overhead of setting up Skia objects.
    663    if (blendMethod == BlendMethod::OVER) {
    664      mOverProc = SkBlitRow::Factory32(SkBlitRow::kSrcPixelAlpha_Flag32);
    665      MOZ_ASSERT(mOverProc);
    666    }
    667 
    668    // We don't need an intermediate buffer unless the unclamped frame rect
    669    // width is larger than the clamped frame rect width. In that case, the
    670    // caller will end up writing data that won't end up in the final image at
    671    // all, and we'll need a buffer to give that data a place to go.
    672    if (mFrameRect.width < mUnclampedFrameRect.width || mOverProc) {
    673      mBuffer.reset(new (fallible)
    674                        uint8_t[mUnclampedFrameRect.width * sizeof(uint32_t)]);
    675      if (MOZ_UNLIKELY(!mBuffer)) {
    676        return NS_ERROR_OUT_OF_MEMORY;
    677      }
    678 
    679      memset(mBuffer.get(), 0, mUnclampedFrameRect.width * sizeof(uint32_t));
    680    }
    681 
    682    ConfigureFilter(mUnclampedFrameRect.Size(), sizeof(uint32_t));
    683    return NS_OK;
    684  }
    685 
    686  Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
    687    return mNext.TakeInvalidRect();
    688  }
    689 
    690 protected:
    691  uint8_t* DoResetToFirstRow() override {
    692    uint8_t* rowPtr = mNext.ResetToFirstRow();
    693    if (rowPtr == nullptr) {
    694      mRow = mFrameRect.YMost();
    695      return nullptr;
    696    }
    697 
    698    mRow = 0;
    699    mBaseFrameRowPtr = mBaseFrameStartPtr;
    700 
    701    while (mRow < mFrameRect.y) {
    702      WriteBaseFrameRow();
    703      AdvanceRowOutsideFrameRect();
    704    }
    705 
    706    // We're at the beginning of the frame rect now, so return if we're either
    707    // ready for input or we're already done.
    708    rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
    709    if (!mFrameRect.IsEmpty() || rowPtr == nullptr) {
    710      // Note that the pointer we're returning is for the next row we're
    711      // actually going to write to, but we may discard writes before that point
    712      // if mRow < mFrameRect.y.
    713      mRow = mUnclampedFrameRect.y;
    714      WriteBaseFrameRow();
    715      return AdjustRowPointer(rowPtr);
    716    }
    717 
    718    // We've finished the region specified by the frame rect, but the frame rect
    719    // is empty, so we need to output the rest of the image immediately. Advance
    720    // to the end of the next pipeline stage's buffer, outputting rows that are
    721    // copied from the base frame and/or cleared.
    722    WriteBaseFrameRowsUntilComplete();
    723 
    724    mRow = mFrameRect.YMost();
    725    return nullptr;  // We're done.
    726  }
    727 
    728  uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
    729    CopyInputRow(aInputRow);
    730    return DoAdvanceRow();
    731  }
    732 
    733  uint8_t* DoAdvanceRow() override {
    734    uint8_t* rowPtr = nullptr;
    735 
    736    const int32_t currentRow = mRow;
    737    mRow++;
    738 
    739    // The unclamped frame rect has a negative offset which means -y rows from
    740    // the decoder need to be discarded before we advance properly.
    741    if (currentRow >= 0 && mBaseFrameRowPtr) {
    742      mBaseFrameRowPtr += mRowLength;
    743    }
    744 
    745    if (currentRow < mFrameRect.y) {
    746      // This row is outside of the frame rect, so just drop it on the floor.
    747      rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
    748      return AdjustRowPointer(rowPtr);
    749    } else if (NS_WARN_IF(currentRow >= mFrameRect.YMost())) {
    750      return nullptr;
    751    }
    752 
    753    // If we had to buffer, merge the data into the row. Otherwise we had the
    754    // decoder write directly to the next stage's buffer.
    755    if (mBuffer) {
    756      int32_t width = mFrameRect.width;
    757      uint32_t* dst = reinterpret_cast<uint32_t*>(mNext.CurrentRowPointer());
    758      uint32_t* src = reinterpret_cast<uint32_t*>(mBuffer.get()) -
    759                      std::min(mUnclampedFrameRect.x, 0);
    760      dst += mFrameRect.x;
    761      if (mOverProc) {
    762        mOverProc(dst, src, width, 0xFF);
    763      } else {
    764        memcpy(dst, src, width * sizeof(uint32_t));
    765      }
    766      rowPtr = mNext.AdvanceRow() ? mBuffer.get() : nullptr;
    767    } else {
    768      MOZ_ASSERT(!mOverProc);
    769      rowPtr = mNext.AdvanceRow();
    770    }
    771 
    772    // If there's still more data coming or we're already done, just adjust the
    773    // pointer and return.
    774    if (mRow < mFrameRect.YMost() || rowPtr == nullptr) {
    775      WriteBaseFrameRow();
    776      return AdjustRowPointer(rowPtr);
    777    }
    778 
    779    // We've finished the region specified by the frame rect. Advance to the end
    780    // of the next pipeline stage's buffer, outputting rows that are copied from
    781    // the base frame and/or cleared.
    782    WriteBaseFrameRowsUntilComplete();
    783 
    784    return nullptr;  // We're done.
    785  }
    786 
    787 private:
    788  void WriteBaseFrameRowsUntilComplete() {
    789    do {
    790      WriteBaseFrameRow();
    791    } while (AdvanceRowOutsideFrameRect());
    792  }
    793 
    794  void WriteBaseFrameRow() {
    795    uint8_t* dest = mNext.CurrentRowPointer();
    796    if (!dest) {
    797      return;
    798    }
    799 
    800    // No need to copy pixels from the base frame for rows that will not change
    801    // between the recycled frame and the new frame.
    802    bool needBaseFrame = mRow >= mRecycleRow && mRow < mRecycleRowMost;
    803 
    804    if (!mBaseFrameRowPtr) {
    805      // No base frame, so we are clearing everything.
    806      if (needBaseFrame) {
    807        memset(dest + mRecycleRowOffset, 0, mRecycleRowLength);
    808      }
    809    } else if (mClearRow <= mRow && mClearRowMost > mRow) {
    810      // We have a base frame, but we are inside the area to be cleared.
    811      // Only copy the data we need from the source.
    812      if (needBaseFrame) {
    813        memcpy(dest + mRecycleRowOffset, mBaseFrameRowPtr + mRecycleRowOffset,
    814               mClearPrefixLength);
    815        memcpy(dest + mClearPostfixOffset,
    816               mBaseFrameRowPtr + mClearPostfixOffset, mClearPostfixLength);
    817      }
    818      memset(dest + mClearInfixOffset, 0, mClearInfixLength);
    819    } else if (needBaseFrame) {
    820      memcpy(dest + mRecycleRowOffset, mBaseFrameRowPtr + mRecycleRowOffset,
    821             mRecycleRowLength);
    822    }
    823  }
    824 
    825  bool AdvanceRowOutsideFrameRect() {
    826    // The unclamped frame rect may have a negative offset however we should
    827    // never be advancing the row via this path (otherwise mBaseFrameRowPtr
    828    // will be wrong.
    829    MOZ_ASSERT(mRow >= 0);
    830    MOZ_ASSERT(mRow < mFrameRect.y || mRow >= mFrameRect.YMost());
    831 
    832    mRow++;
    833    if (mBaseFrameRowPtr) {
    834      mBaseFrameRowPtr += mRowLength;
    835    }
    836 
    837    return mNext.AdvanceRow() != nullptr;
    838  }
    839 
    840  uint8_t* AdjustRowPointer(uint8_t* aNextRowPointer) const {
    841    if (mBuffer) {
    842      MOZ_ASSERT(aNextRowPointer == mBuffer.get() ||
    843                 aNextRowPointer == nullptr);
    844      return aNextRowPointer;  // No adjustment needed for an intermediate
    845                               // buffer.
    846    }
    847 
    848    if (mFrameRect.IsEmpty() || mRow >= mFrameRect.YMost() ||
    849        aNextRowPointer == nullptr) {
    850      return nullptr;  // Nothing left to write.
    851    }
    852 
    853    MOZ_ASSERT(!mOverProc);
    854    return aNextRowPointer + mFrameRect.x * sizeof(uint32_t);
    855  }
    856 
    857  Next mNext;  /// The next SurfaceFilter in the chain.
    858 
    859  gfx::IntRect mFrameRect;  /// The surface subrect which contains data,
    860                            /// clamped to the image size.
    861  gfx::IntRect mUnclampedFrameRect;  /// The frame rect before clamping.
    862  UniquePtr<uint8_t[]> mBuffer;      /// The intermediate buffer, if one is
    863                                     /// necessary because the frame rect width
    864  /// is larger than the image's logical width.
    865  int32_t mRow;              /// The row in unclamped frame rect space
    866                             /// that we're currently writing.
    867  size_t mRowLength;         /// Length in bytes of a row that is the input
    868                             /// for the next filter.
    869  int32_t mRecycleRow;       /// The starting row of the recycle rect.
    870  int32_t mRecycleRowMost;   /// The ending row of the recycle rect.
    871  size_t mRecycleRowOffset;  /// Row offset in bytes of the recycle rect.
    872  size_t mRecycleRowLength;  /// Row length in bytes of the recycle rect.
    873 
    874  /// The frame area to clear before blending the current frame.
    875  int32_t mClearRow;           /// The starting row of the clear rect.
    876  int32_t mClearRowMost;       /// The ending row of the clear rect.
    877  size_t mClearPrefixLength;   /// Row length in bytes of clear prefix.
    878  size_t mClearInfixOffset;    /// Row offset in bytes of clear area.
    879  size_t mClearInfixLength;    /// Row length in bytes of clear area.
    880  size_t mClearPostfixOffset;  /// Row offset in bytes of clear postfix.
    881  size_t mClearPostfixLength;  /// Row length in bytes of clear postfix.
    882 
    883  SkBlitRow::Proc32 mOverProc;  /// Function pointer to perform over blending.
    884  const uint8_t*
    885      mBaseFrameStartPtr;           /// Starting row pointer to the base frame
    886                                    /// data from which we copy pixel data from.
    887  const uint8_t* mBaseFrameRowPtr;  /// Current row pointer to the base frame
    888                                    /// data.
    889 };
    890 
    891 //////////////////////////////////////////////////////////////////////////////
    892 // RemoveFrameRectFilter
    893 //////////////////////////////////////////////////////////////////////////////
    894 
    895 template <typename Next>
    896 class RemoveFrameRectFilter;
    897 
    898 /**
    899 * A configuration struct for RemoveFrameRectFilter.
    900 */
    901 struct RemoveFrameRectConfig {
    902  template <typename Next>
    903  using Filter = RemoveFrameRectFilter<Next>;
    904  gfx::IntRect mFrameRect;  /// The surface subrect which contains data.
    905 };
    906 
    907 /**
    908 * RemoveFrameRectFilter turns an image with a frame rect that does not match
    909 * its logical size into an image with no frame rect. It does this by writing
    910 * transparent pixels into any padding regions and throwing away excess data.
    911 *
    912 * The 'Next' template parameter specifies the next filter in the chain.
    913 */
    914 template <typename Next>
    915 class RemoveFrameRectFilter final : public SurfaceFilter {
    916 public:
    917  RemoveFrameRectFilter() : mRow(0) {}
    918 
    919  template <typename... Rest>
    920  nsresult Configure(const RemoveFrameRectConfig& aConfig,
    921                     const Rest&... aRest) {
    922    nsresult rv = mNext.Configure(aRest...);
    923    if (NS_FAILED(rv)) {
    924      return rv;
    925    }
    926 
    927    mFrameRect = mUnclampedFrameRect = aConfig.mFrameRect;
    928    gfx::IntSize outputSize = mNext.InputSize();
    929 
    930    // Forbid frame rects with negative size.
    931    if (aConfig.mFrameRect.Width() < 0 || aConfig.mFrameRect.Height() < 0) {
    932      return NS_ERROR_INVALID_ARG;
    933    }
    934 
    935    // Clamp mFrameRect to the output size.
    936    gfx::IntRect outputRect(0, 0, outputSize.width, outputSize.height);
    937    mFrameRect = mFrameRect.Intersect(outputRect);
    938 
    939    // If there's no intersection, |mFrameRect| will be an empty rect positioned
    940    // at the maximum of |inputRect|'s and |aFrameRect|'s coordinates, which is
    941    // not what we want. Force it to (0, 0) in that case.
    942    if (mFrameRect.IsEmpty()) {
    943      mFrameRect.MoveTo(0, 0);
    944    }
    945 
    946    // We don't need an intermediate buffer unless the unclamped frame rect
    947    // width is larger than the clamped frame rect width. In that case, the
    948    // caller will end up writing data that won't end up in the final image at
    949    // all, and we'll need a buffer to give that data a place to go.
    950    if (mFrameRect.Width() < mUnclampedFrameRect.Width()) {
    951      mBuffer.reset(new (
    952          fallible) uint8_t[mUnclampedFrameRect.Width() * sizeof(uint32_t)]);
    953      if (MOZ_UNLIKELY(!mBuffer)) {
    954        return NS_ERROR_OUT_OF_MEMORY;
    955      }
    956 
    957      memset(mBuffer.get(), 0, mUnclampedFrameRect.Width() * sizeof(uint32_t));
    958    }
    959 
    960    ConfigureFilter(mUnclampedFrameRect.Size(), sizeof(uint32_t));
    961    return NS_OK;
    962  }
    963 
    964  Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
    965    return mNext.TakeInvalidRect();
    966  }
    967 
    968 protected:
    969  uint8_t* DoResetToFirstRow() override {
    970    uint8_t* rowPtr = mNext.ResetToFirstRow();
    971    if (rowPtr == nullptr) {
    972      mRow = mFrameRect.YMost();
    973      return nullptr;
    974    }
    975 
    976    mRow = mUnclampedFrameRect.Y();
    977 
    978    // Advance the next pipeline stage to the beginning of the frame rect,
    979    // outputting blank rows.
    980    if (mFrameRect.Y() > 0) {
    981      for (int32_t rowToOutput = 0; rowToOutput < mFrameRect.Y();
    982           ++rowToOutput) {
    983        mNext.WriteEmptyRow();
    984      }
    985    }
    986 
    987    // We're at the beginning of the frame rect now, so return if we're either
    988    // ready for input or we're already done.
    989    rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
    990    if (!mFrameRect.IsEmpty() || rowPtr == nullptr) {
    991      // Note that the pointer we're returning is for the next row we're
    992      // actually going to write to, but we may discard writes before that point
    993      // if mRow < mFrameRect.y.
    994      return AdjustRowPointer(rowPtr);
    995    }
    996 
    997    // We've finished the region specified by the frame rect, but the frame rect
    998    // is empty, so we need to output the rest of the image immediately. Advance
    999    // to the end of the next pipeline stage's buffer, outputting blank rows.
   1000    while (mNext.WriteEmptyRow() == WriteState::NEED_MORE_DATA) {
   1001    }
   1002 
   1003    mRow = mFrameRect.YMost();
   1004    return nullptr;  // We're done.
   1005  }
   1006 
   1007  uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
   1008    CopyInputRow(aInputRow);
   1009    return DoAdvanceRow();
   1010  }
   1011 
   1012  uint8_t* DoAdvanceRow() override {
   1013    uint8_t* rowPtr = nullptr;
   1014 
   1015    const int32_t currentRow = mRow;
   1016    mRow++;
   1017 
   1018    if (currentRow < mFrameRect.Y()) {
   1019      // This row is outside of the frame rect, so just drop it on the floor.
   1020      rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
   1021      return AdjustRowPointer(rowPtr);
   1022    } else if (currentRow >= mFrameRect.YMost()) {
   1023      NS_WARNING("RemoveFrameRectFilter: Advancing past end of frame rect");
   1024      return nullptr;
   1025    }
   1026 
   1027    // If we had to buffer, copy the data. Otherwise, just advance the row.
   1028    if (mBuffer) {
   1029      // We write from the beginning of the buffer unless
   1030      // |mUnclampedFrameRect.x| is negative; if that's the case, we have to
   1031      // skip the portion of the unclamped frame rect that's outside the row.
   1032      uint32_t* source = reinterpret_cast<uint32_t*>(mBuffer.get()) -
   1033                         std::min(mUnclampedFrameRect.X(), 0);
   1034 
   1035      // We write |mFrameRect.width| columns starting at |mFrameRect.x|; we've
   1036      // already clamped these values to the size of the output, so we don't
   1037      // have to worry about bounds checking here (though WriteBuffer() will do
   1038      // it for us in any case).
   1039      WriteState state =
   1040          mNext.WriteBuffer(source, mFrameRect.X(), mFrameRect.Width());
   1041 
   1042      rowPtr = state == WriteState::NEED_MORE_DATA ? mBuffer.get() : nullptr;
   1043    } else {
   1044      rowPtr = mNext.AdvanceRow();
   1045    }
   1046 
   1047    // If there's still more data coming or we're already done, just adjust the
   1048    // pointer and return.
   1049    if (mRow < mFrameRect.YMost() || rowPtr == nullptr) {
   1050      return AdjustRowPointer(rowPtr);
   1051    }
   1052 
   1053    // We've finished the region specified by the frame rect. Advance to the end
   1054    // of the next pipeline stage's buffer, outputting blank rows.
   1055    while (mNext.WriteEmptyRow() == WriteState::NEED_MORE_DATA) {
   1056    }
   1057 
   1058    mRow = mFrameRect.YMost();
   1059    return nullptr;  // We're done.
   1060  }
   1061 
   1062 private:
   1063  uint8_t* AdjustRowPointer(uint8_t* aNextRowPointer) const {
   1064    if (mBuffer) {
   1065      MOZ_ASSERT(aNextRowPointer == mBuffer.get() ||
   1066                 aNextRowPointer == nullptr);
   1067      return aNextRowPointer;  // No adjustment needed for an intermediate
   1068                               // buffer.
   1069    }
   1070 
   1071    if (mFrameRect.IsEmpty() || mRow >= mFrameRect.YMost() ||
   1072        aNextRowPointer == nullptr) {
   1073      return nullptr;  // Nothing left to write.
   1074    }
   1075 
   1076    return aNextRowPointer + mFrameRect.X() * sizeof(uint32_t);
   1077  }
   1078 
   1079  Next mNext;  /// The next SurfaceFilter in the chain.
   1080 
   1081  gfx::IntRect mFrameRect;  /// The surface subrect which contains data,
   1082                            /// clamped to the image size.
   1083  gfx::IntRect mUnclampedFrameRect;  /// The frame rect before clamping.
   1084  UniquePtr<uint8_t[]> mBuffer;      /// The intermediate buffer, if one is
   1085                                     /// necessary because the frame rect width
   1086  /// is larger than the image's logical width.
   1087  int32_t mRow;  /// The row in unclamped frame rect space
   1088                 /// that we're currently writing.
   1089 };
   1090 
   1091 //////////////////////////////////////////////////////////////////////////////
   1092 // ADAM7InterpolatingFilter
   1093 //////////////////////////////////////////////////////////////////////////////
   1094 
   1095 template <typename Next>
   1096 class ADAM7InterpolatingFilter;
   1097 
   1098 /**
   1099 * A configuration struct for ADAM7InterpolatingFilter.
   1100 */
   1101 struct ADAM7InterpolatingConfig {
   1102  template <typename Next>
   1103  using Filter = ADAM7InterpolatingFilter<Next>;
   1104 };
   1105 
   1106 /**
   1107 * ADAM7InterpolatingFilter performs bilinear interpolation over an ADAM7
   1108 * interlaced image.
   1109 *
   1110 * ADAM7 breaks up the image into 8x8 blocks. On each of the 7 passes, a new set
   1111 * of pixels in each block receives their final values, according to the
   1112 * following pattern:
   1113 *
   1114 *    1 6 4 6 2 6 4 6
   1115 *    7 7 7 7 7 7 7 7
   1116 *    5 6 5 6 5 6 5 6
   1117 *    7 7 7 7 7 7 7 7
   1118 *    3 6 4 6 3 6 4 6
   1119 *    7 7 7 7 7 7 7 7
   1120 *    5 6 5 6 5 6 5 6
   1121 *    7 7 7 7 7 7 7 7
   1122 *
   1123 * When rendering the pixels that have not yet received their final values, we
   1124 * can get much better intermediate results if we interpolate between
   1125 * the pixels we *have* gotten so far. This filter performs bilinear
   1126 * interpolation by first performing linear interpolation horizontally for each
   1127 * "important" row (which we'll define as a row that has received any pixels
   1128 * with final values at all) and then performing linear interpolation vertically
   1129 * to produce pixel values for rows which aren't important on the current pass.
   1130 *
   1131 * Note that this filter totally ignores the data which is written to rows which
   1132 * aren't important on the current pass! It's fine to write nothing at all for
   1133 * these rows, although doing so won't cause any harm.
   1134 *
   1135 * XXX(seth): In bug 1280552 we'll add a SIMD implementation for this filter.
   1136 *
   1137 * The 'Next' template parameter specifies the next filter in the chain.
   1138 */
   1139 template <typename Next>
   1140 class ADAM7InterpolatingFilter final : public SurfaceFilter {
   1141 public:
   1142  ADAM7InterpolatingFilter()
   1143      : mPass(0)  // The current pass, in the range 1..7. Starts at 0 so that
   1144                  // DoResetToFirstRow() doesn't have to special case the first
   1145                  // pass.
   1146        ,
   1147        mRow(0) {}
   1148 
   1149  template <typename... Rest>
   1150  nsresult Configure(const ADAM7InterpolatingConfig& aConfig,
   1151                     const Rest&... aRest) {
   1152    nsresult rv = mNext.Configure(aRest...);
   1153    if (NS_FAILED(rv)) {
   1154      return rv;
   1155    }
   1156 
   1157    // We have two intermediate buffers, one for the previous row with final
   1158    // pixel values and one for the row that the previous filter in the chain is
   1159    // currently writing to.
   1160    size_t inputWidthInBytes = mNext.InputSize().width * sizeof(uint32_t);
   1161    mPreviousRow.reset(new (fallible) uint8_t[inputWidthInBytes]);
   1162    if (MOZ_UNLIKELY(!mPreviousRow)) {
   1163      return NS_ERROR_OUT_OF_MEMORY;
   1164    }
   1165 
   1166    mCurrentRow.reset(new (fallible) uint8_t[inputWidthInBytes]);
   1167    if (MOZ_UNLIKELY(!mCurrentRow)) {
   1168      return NS_ERROR_OUT_OF_MEMORY;
   1169    }
   1170 
   1171    memset(mPreviousRow.get(), 0, inputWidthInBytes);
   1172    memset(mCurrentRow.get(), 0, inputWidthInBytes);
   1173 
   1174    ConfigureFilter(mNext.InputSize(), sizeof(uint32_t));
   1175    return NS_OK;
   1176  }
   1177 
   1178  Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
   1179    return mNext.TakeInvalidRect();
   1180  }
   1181 
   1182 protected:
   1183  uint8_t* DoResetToFirstRow() override {
   1184    mRow = 0;
   1185    mPass = std::min(mPass + 1, 7);
   1186 
   1187    uint8_t* rowPtr = mNext.ResetToFirstRow();
   1188    if (mPass == 7) {
   1189      // Short circuit this filter on the final pass, since all pixels have
   1190      // their final values at that point.
   1191      return rowPtr;
   1192    }
   1193 
   1194    return mCurrentRow.get();
   1195  }
   1196 
   1197  uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
   1198    CopyInputRow(aInputRow);
   1199    return DoAdvanceRow();
   1200  }
   1201 
   1202  uint8_t* DoAdvanceRow() override {
   1203    MOZ_ASSERT(0 < mPass && mPass <= 7, "Invalid pass");
   1204 
   1205    int32_t currentRow = mRow;
   1206    ++mRow;
   1207 
   1208    if (mPass == 7) {
   1209      // On the final pass we short circuit this filter totally.
   1210      return mNext.AdvanceRow();
   1211    }
   1212 
   1213    const int32_t lastImportantRow =
   1214        LastImportantRow(InputSize().height, mPass);
   1215    if (currentRow > lastImportantRow) {
   1216      return nullptr;  // This pass is already complete.
   1217    }
   1218 
   1219    if (!IsImportantRow(currentRow, mPass)) {
   1220      // We just ignore whatever the caller gives us for these rows. We'll
   1221      // interpolate them in later.
   1222      return mCurrentRow.get();
   1223    }
   1224 
   1225    // This is an important row. We need to perform horizontal interpolation for
   1226    // these rows.
   1227    InterpolateHorizontally(mCurrentRow.get(), InputSize().width, mPass);
   1228 
   1229    // Interpolate vertically between the previous important row and the current
   1230    // important row. We skip this if the current row is 0 (which is always an
   1231    // important row), because in that case there is no previous important row
   1232    // to interpolate with.
   1233    if (currentRow != 0) {
   1234      InterpolateVertically(mPreviousRow.get(), mCurrentRow.get(), mPass,
   1235                            mNext);
   1236    }
   1237 
   1238    // Write out the current row itself, which, being an important row, does not
   1239    // need vertical interpolation.
   1240    uint32_t* currentRowAsPixels =
   1241        reinterpret_cast<uint32_t*>(mCurrentRow.get());
   1242    mNext.WriteBuffer(currentRowAsPixels);
   1243 
   1244    if (currentRow == lastImportantRow) {
   1245      // This is the last important row, which completes this pass. Note that
   1246      // for very small images, this may be the first row! Since there won't be
   1247      // another important row, there's nothing to interpolate with vertically,
   1248      // so we just duplicate this row until the end of the image.
   1249      while (mNext.WriteBuffer(currentRowAsPixels) ==
   1250             WriteState::NEED_MORE_DATA) {
   1251      }
   1252 
   1253      // All of the remaining rows in the image were determined above, so we're
   1254      // done.
   1255      return nullptr;
   1256    }
   1257 
   1258    // The current row is now the previous important row; save it.
   1259    std::swap(mPreviousRow, mCurrentRow);
   1260 
   1261    MOZ_ASSERT(mRow < InputSize().height,
   1262               "Reached the end of the surface without "
   1263               "hitting the last important row?");
   1264 
   1265    return mCurrentRow.get();
   1266  }
   1267 
   1268 private:
   1269  static void InterpolateVertically(uint8_t* aPreviousRow, uint8_t* aCurrentRow,
   1270                                    uint8_t aPass, SurfaceFilter& aNext) {
   1271    const float* weights = InterpolationWeights(ImportantRowStride(aPass));
   1272 
   1273    // We need to interpolate vertically to generate the rows between the
   1274    // previous important row and the next one. Recall that important rows are
   1275    // rows which contain at least some final pixels; see
   1276    // InterpolateHorizontally() for some additional explanation as to what that
   1277    // means. Note that we've already written out the previous important row, so
   1278    // we start the iteration at 1.
   1279    for (int32_t outRow = 1; outRow < ImportantRowStride(aPass); ++outRow) {
   1280      const float weight = weights[outRow];
   1281 
   1282      // We iterate through the previous and current important row every time we
   1283      // write out an interpolated row, so we need to copy the pointers.
   1284      uint8_t* prevRowBytes = aPreviousRow;
   1285      uint8_t* currRowBytes = aCurrentRow;
   1286 
   1287      // Write out the interpolated pixels. Interpolation is componentwise.
   1288      aNext.template WritePixelsToRow<uint32_t>([&] {
   1289        uint32_t pixel = 0;
   1290        auto* component = reinterpret_cast<uint8_t*>(&pixel);
   1291        *component++ =
   1292            InterpolateByte(*prevRowBytes++, *currRowBytes++, weight);
   1293        *component++ =
   1294            InterpolateByte(*prevRowBytes++, *currRowBytes++, weight);
   1295        *component++ =
   1296            InterpolateByte(*prevRowBytes++, *currRowBytes++, weight);
   1297        *component++ =
   1298            InterpolateByte(*prevRowBytes++, *currRowBytes++, weight);
   1299        return AsVariant(pixel);
   1300      });
   1301    }
   1302  }
   1303 
   1304  static void InterpolateHorizontally(uint8_t* aRow, int32_t aWidth,
   1305                                      uint8_t aPass) {
   1306    // Collect the data we'll need to perform horizontal interpolation. The
   1307    // terminology here bears some explanation: a "final pixel" is a pixel which
   1308    // has received its final value. On each pass, a new set of pixels receives
   1309    // their final value; see the diagram above of the 8x8 pattern that ADAM7
   1310    // uses. Any pixel which hasn't received its final value on this pass
   1311    // derives its value from either horizontal or vertical interpolation
   1312    // instead.
   1313    const size_t finalPixelStride = FinalPixelStride(aPass);
   1314    const size_t finalPixelStrideBytes = finalPixelStride * sizeof(uint32_t);
   1315    const size_t lastFinalPixel = LastFinalPixel(aWidth, aPass);
   1316    const size_t lastFinalPixelBytes = lastFinalPixel * sizeof(uint32_t);
   1317    const float* weights = InterpolationWeights(finalPixelStride);
   1318 
   1319    // Interpolate blocks of pixels which lie between two final pixels.
   1320    // Horizontal interpolation is done in place, as we'll need the results
   1321    // later when we vertically interpolate.
   1322    for (size_t blockBytes = 0; blockBytes < lastFinalPixelBytes;
   1323         blockBytes += finalPixelStrideBytes) {
   1324      uint8_t* finalPixelA = aRow + blockBytes;
   1325      uint8_t* finalPixelB = aRow + blockBytes + finalPixelStrideBytes;
   1326 
   1327      MOZ_ASSERT(finalPixelA < aRow + aWidth * sizeof(uint32_t),
   1328                 "Running off end of buffer");
   1329      MOZ_ASSERT(finalPixelB < aRow + aWidth * sizeof(uint32_t),
   1330                 "Running off end of buffer");
   1331 
   1332      // Interpolate the individual pixels componentwise. Note that we start
   1333      // iteration at 1 since we don't need to apply any interpolation to the
   1334      // first pixel in the block, which has its final value.
   1335      for (size_t pixelIndex = 1; pixelIndex < finalPixelStride; ++pixelIndex) {
   1336        const float weight = weights[pixelIndex];
   1337        uint8_t* pixel = aRow + blockBytes + pixelIndex * sizeof(uint32_t);
   1338 
   1339        MOZ_ASSERT(pixel < aRow + aWidth * sizeof(uint32_t),
   1340                   "Running off end of buffer");
   1341 
   1342        for (size_t component = 0; component < sizeof(uint32_t); ++component) {
   1343          pixel[component] = InterpolateByte(finalPixelA[component],
   1344                                             finalPixelB[component], weight);
   1345        }
   1346      }
   1347    }
   1348 
   1349    // For the pixels after the last final pixel in the row, there isn't a
   1350    // second final pixel to interpolate with, so just duplicate.
   1351    uint32_t* rowPixels = reinterpret_cast<uint32_t*>(aRow);
   1352    uint32_t pixelToDuplicate = rowPixels[lastFinalPixel];
   1353    for (int32_t pixelIndex = lastFinalPixel + 1; pixelIndex < aWidth;
   1354         ++pixelIndex) {
   1355      MOZ_ASSERT(pixelIndex < aWidth, "Running off end of buffer");
   1356      rowPixels[pixelIndex] = pixelToDuplicate;
   1357    }
   1358  }
   1359 
   1360  static uint8_t InterpolateByte(uint8_t aByteA, uint8_t aByteB,
   1361                                 float aWeight) {
   1362    return uint8_t(aByteA * aWeight + aByteB * (1.0f - aWeight));
   1363  }
   1364 
   1365  static int32_t ImportantRowStride(uint8_t aPass) {
   1366    MOZ_ASSERT(0 < aPass && aPass <= 7, "Invalid pass");
   1367 
   1368    // The stride between important rows for each pass, with a dummy value for
   1369    // the nonexistent pass 0.
   1370    static int32_t strides[] = {1, 8, 8, 4, 4, 2, 2, 1};
   1371 
   1372    return strides[aPass];
   1373  }
   1374 
   1375  static bool IsImportantRow(int32_t aRow, uint8_t aPass) {
   1376    MOZ_ASSERT(aRow >= 0);
   1377 
   1378    // Whether the row is important comes down to divisibility by the stride for
   1379    // this pass, which is always a power of 2, so we can check using a mask.
   1380    int32_t mask = ImportantRowStride(aPass) - 1;
   1381    return (aRow & mask) == 0;
   1382  }
   1383 
   1384  static int32_t LastImportantRow(int32_t aHeight, uint8_t aPass) {
   1385    MOZ_ASSERT(aHeight > 0);
   1386 
   1387    // We can find the last important row using the same mask trick as above.
   1388    int32_t lastRow = aHeight - 1;
   1389    int32_t mask = ImportantRowStride(aPass) - 1;
   1390    return lastRow - (lastRow & mask);
   1391  }
   1392 
   1393  static size_t FinalPixelStride(uint8_t aPass) {
   1394    MOZ_ASSERT(0 < aPass && aPass <= 7, "Invalid pass");
   1395 
   1396    // The stride between the final pixels in important rows for each pass, with
   1397    // a dummy value for the nonexistent pass 0.
   1398    static size_t strides[] = {1, 8, 4, 4, 2, 2, 1, 1};
   1399 
   1400    return strides[aPass];
   1401  }
   1402 
   1403  static size_t LastFinalPixel(int32_t aWidth, uint8_t aPass) {
   1404    MOZ_ASSERT(aWidth >= 0);
   1405 
   1406    // Again, we can use the mask trick above to find the last important pixel.
   1407    int32_t lastColumn = aWidth - 1;
   1408    size_t mask = FinalPixelStride(aPass) - 1;
   1409    return lastColumn - (lastColumn & mask);
   1410  }
   1411 
   1412  static const float* InterpolationWeights(int32_t aStride) {
   1413    // Precalculated interpolation weights. These are used to interpolate
   1414    // between final pixels or between important rows. Although no interpolation
   1415    // is actually applied to the previous final pixel or important row value,
   1416    // the arrays still start with 1.0f, which is always skipped, primarily
   1417    // because otherwise |stride1Weights| would have zero elements.
   1418    static float stride8Weights[] = {1.0f,     7 / 8.0f, 6 / 8.0f, 5 / 8.0f,
   1419                                     4 / 8.0f, 3 / 8.0f, 2 / 8.0f, 1 / 8.0f};
   1420    static float stride4Weights[] = {1.0f, 3 / 4.0f, 2 / 4.0f, 1 / 4.0f};
   1421    static float stride2Weights[] = {1.0f, 1 / 2.0f};
   1422    static float stride1Weights[] = {1.0f};
   1423 
   1424    switch (aStride) {
   1425      case 8:
   1426        return stride8Weights;
   1427      case 4:
   1428        return stride4Weights;
   1429      case 2:
   1430        return stride2Weights;
   1431      case 1:
   1432        return stride1Weights;
   1433      default:
   1434        MOZ_CRASH();
   1435    }
   1436  }
   1437 
   1438  Next mNext;  /// The next SurfaceFilter in the chain.
   1439 
   1440  UniquePtr<uint8_t[]>
   1441      mPreviousRow;  /// The last important row (i.e., row with
   1442                     /// final pixel values) that got written to.
   1443  UniquePtr<uint8_t[]> mCurrentRow;  /// The row that's being written to right
   1444                                     /// now.
   1445  uint8_t mPass;                     /// Which ADAM7 pass we're on. Valid passes
   1446                                     /// are 1..7 during processing and 0 prior
   1447                                     /// to configuration.
   1448  int32_t mRow;                      /// The row we're currently reading.
   1449 };
   1450 
   1451 }  // namespace image
   1452 }  // namespace mozilla
   1453 
   1454 #endif  // mozilla_image_SurfaceFilters_h