LCOV - code coverage report
Current view: top level - image - SurfaceFilters.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 309 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 159 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      24             : #include "DownscalingFilter.h"
      25             : #include "SurfaceCache.h"
      26             : #include "SurfacePipe.h"
      27             : 
      28             : namespace mozilla {
      29             : namespace image {
      30             : 
      31             : //////////////////////////////////////////////////////////////////////////////
      32             : // DeinterlacingFilter
      33             : //////////////////////////////////////////////////////////////////////////////
      34             : 
      35             : template <typename PixelType, typename Next> class DeinterlacingFilter;
      36             : 
      37             : /**
      38             :  * A configuration struct for DeinterlacingFilter.
      39             :  *
      40             :  * The 'PixelType' template parameter should be either uint32_t (for output to a
      41             :  * SurfaceSink) or uint8_t (for output to a PalettedSurfaceSink).
      42             :  */
      43             : template <typename PixelType>
      44             : struct DeinterlacingConfig
      45             : {
      46             :   template <typename Next> using Filter = DeinterlacingFilter<PixelType, Next>;
      47             :   bool mProgressiveDisplay; /// If true, duplicate rows during deinterlacing
      48             :                             /// to make progressive display look better, at
      49             :                             /// the cost of some performance.
      50             : };
      51             : 
      52             : /**
      53             :  * DeinterlacingFilter performs deinterlacing by reordering the rows that are
      54             :  * written to it.
      55             :  *
      56             :  * The 'PixelType' template parameter should be either uint32_t (for output to a
      57             :  * SurfaceSink) or uint8_t (for output to a PalettedSurfaceSink).
      58             :  *
      59             :  * The 'Next' template parameter specifies the next filter in the chain.
      60             :  */
      61             : template <typename PixelType, typename Next>
      62           0 : class DeinterlacingFilter final : public SurfaceFilter
      63             : {
      64             : public:
      65           0 :   DeinterlacingFilter()
      66             :     : mInputRow(0)
      67             :     , mOutputRow(0)
      68             :     , mPass(0)
      69           0 :     , mProgressiveDisplay(true)
      70           0 :   { }
      71             : 
      72             :   template <typename... Rest>
      73           0 :   nsresult Configure(const DeinterlacingConfig<PixelType>& aConfig, Rest... aRest)
      74             :   {
      75           0 :     nsresult rv = mNext.Configure(aRest...);
      76           0 :     if (NS_FAILED(rv)) {
      77           0 :       return rv;
      78             :     }
      79             : 
      80           0 :     if (sizeof(PixelType) == 1 && !mNext.IsValidPalettedPipe()) {
      81           0 :       NS_WARNING("Paletted DeinterlacingFilter used with non-paletted pipe?");
      82           0 :       return NS_ERROR_INVALID_ARG;
      83             :     }
      84           0 :     if (sizeof(PixelType) == 4 && mNext.IsValidPalettedPipe()) {
      85           0 :       NS_WARNING("Non-paletted DeinterlacingFilter used with paletted pipe?");
      86           0 :       return NS_ERROR_INVALID_ARG;
      87             :     }
      88             : 
      89           0 :     gfx::IntSize outputSize = mNext.InputSize();
      90           0 :     mProgressiveDisplay = aConfig.mProgressiveDisplay;
      91             : 
      92           0 :     const uint32_t bufferSize = outputSize.width *
      93           0 :                                 outputSize.height *
      94           0 :                                 sizeof(PixelType);
      95             : 
      96             :     // Use the size of the SurfaceCache as a heuristic to avoid gigantic
      97             :     // allocations. Even if DownscalingFilter allowed us to allocate space for
      98             :     // the output image, the deinterlacing buffer may still be too big, and
      99             :     // fallible allocation won't always save us in the presence of overcommit.
     100           0 :     if (!SurfaceCache::CanHold(bufferSize)) {
     101           0 :       return NS_ERROR_OUT_OF_MEMORY;
     102             :     }
     103             : 
     104             :     // Allocate the buffer, which contains deinterlaced scanlines of the image.
     105             :     // The buffer is necessary so that we can output rows which have already
     106             :     // been deinterlaced again on subsequent passes. Since a later stage in the
     107             :     // pipeline may be transforming the rows it receives (for example, by
     108             :     // downscaling them), the rows may no longer exist in their original form on
     109             :     // the surface itself.
     110           0 :     mBuffer.reset(new (fallible) uint8_t[bufferSize]);
     111           0 :     if (MOZ_UNLIKELY(!mBuffer)) {
     112           0 :       return NS_ERROR_OUT_OF_MEMORY;
     113             :     }
     114             : 
     115             :     // Clear the buffer to avoid writing uninitialized memory to the output.
     116           0 :     memset(mBuffer.get(), 0, bufferSize);
     117             : 
     118           0 :     ConfigureFilter(outputSize, sizeof(PixelType));
     119           0 :     return NS_OK;
     120             :   }
     121             : 
     122           0 :   bool IsValidPalettedPipe() const override
     123             :   {
     124           0 :     return sizeof(PixelType) == 1 && mNext.IsValidPalettedPipe();
     125             :   }
     126             : 
     127           0 :   Maybe<SurfaceInvalidRect> TakeInvalidRect() override
     128             :   {
     129           0 :     return mNext.TakeInvalidRect();
     130             :   }
     131             : 
     132             : protected:
     133           0 :   uint8_t* DoResetToFirstRow() override
     134             :   {
     135           0 :     mNext.ResetToFirstRow();
     136           0 :     mPass = 0;
     137           0 :     mInputRow = 0;
     138           0 :     mOutputRow = InterlaceOffset(mPass);
     139           0 :     return GetRowPointer(mOutputRow);
     140             :   }
     141             : 
     142           0 :   uint8_t* DoAdvanceRow() override
     143             :   {
     144           0 :     if (mPass >= 4) {
     145           0 :       return nullptr;  // We already finished all passes.
     146             :     }
     147           0 :     if (mInputRow >= InputSize().height) {
     148           0 :       return nullptr;  // We already got all the input rows we expect.
     149             :     }
     150             : 
     151             :     // Duplicate from the first Haeberli row to the remaining Haeberli rows
     152             :     // within the buffer.
     153           0 :     DuplicateRows(HaeberliOutputStartRow(mPass, mProgressiveDisplay, mOutputRow),
     154           0 :                   HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
     155             :                                          InputSize(), mOutputRow));
     156             : 
     157             :     // Write the current set of Haeberli rows (which contains the current row)
     158             :     // to the next stage in the pipeline.
     159           0 :     OutputRows(HaeberliOutputStartRow(mPass, mProgressiveDisplay, mOutputRow),
     160           0 :                HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
     161             :                                       InputSize(), mOutputRow));
     162             : 
     163             :     // Determine which output row the next input row corresponds to.
     164           0 :     bool advancedPass = false;
     165           0 :     uint32_t stride = InterlaceStride(mPass);
     166           0 :     int32_t nextOutputRow = mOutputRow + stride;
     167           0 :     while (nextOutputRow >= InputSize().height) {
     168             :       // Copy any remaining rows from the buffer.
     169           0 :       if (!advancedPass) {
     170           0 :         OutputRows(HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
     171             :                                           InputSize(), mOutputRow),
     172           0 :                    InputSize().height);
     173             :       }
     174             : 
     175             :       // We finished the current pass; advance to the next one.
     176           0 :       mPass++;
     177           0 :       if (mPass >= 4) {
     178           0 :         return nullptr;  // Finished all passes.
     179             :       }
     180             : 
     181             :       // Tell the next pipeline stage that we're starting the next pass.
     182           0 :       mNext.ResetToFirstRow();
     183             : 
     184             :       // Update our state to reflect the pass change.
     185           0 :       advancedPass = true;
     186           0 :       stride = InterlaceStride(mPass);
     187           0 :       nextOutputRow = InterlaceOffset(mPass);
     188             :     }
     189             : 
     190           0 :     MOZ_ASSERT(nextOutputRow >= 0);
     191           0 :     MOZ_ASSERT(nextOutputRow < InputSize().height);
     192             : 
     193           0 :     MOZ_ASSERT(HaeberliOutputStartRow(mPass, mProgressiveDisplay,
     194             :                                       nextOutputRow) >= 0);
     195           0 :     MOZ_ASSERT(HaeberliOutputStartRow(mPass, mProgressiveDisplay,
     196             :                                       nextOutputRow) < InputSize().height);
     197           0 :     MOZ_ASSERT(HaeberliOutputStartRow(mPass, mProgressiveDisplay,
     198             :                                       nextOutputRow) <= nextOutputRow);
     199             : 
     200           0 :     MOZ_ASSERT(HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
     201             :                                       InputSize(), nextOutputRow) >= 0);
     202           0 :     MOZ_ASSERT(HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
     203             :                                       InputSize(), nextOutputRow)
     204             :                  <= InputSize().height);
     205           0 :     MOZ_ASSERT(HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
     206             :                                       InputSize(), nextOutputRow)
     207             :                  > nextOutputRow);
     208             : 
     209             :     int32_t nextHaeberliOutputRow =
     210           0 :       HaeberliOutputStartRow(mPass, mProgressiveDisplay, nextOutputRow);
     211             : 
     212             :     // Copy rows from the buffer until we reach the desired output row.
     213           0 :     if (advancedPass) {
     214           0 :       OutputRows(0, nextHaeberliOutputRow);
     215             :     } else {
     216           0 :       OutputRows(HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
     217             :                                         InputSize(), mOutputRow),
     218             :                  nextHaeberliOutputRow);
     219             :     }
     220             : 
     221             :     // Update our position within the buffer.
     222           0 :     mInputRow++;
     223           0 :     mOutputRow = nextOutputRow;
     224             : 
     225             :     // We'll actually write to the first Haeberli output row, then copy it until
     226             :     // we reach the last Haeberli output row. The assertions above make sure
     227             :     // this always includes mOutputRow.
     228           0 :     return GetRowPointer(nextHaeberliOutputRow);
     229             :   }
     230             : 
     231             : private:
     232           0 :   static uint32_t InterlaceOffset(uint32_t aPass)
     233             :   {
     234           0 :     MOZ_ASSERT(aPass < 4, "Invalid pass");
     235             :     static const uint8_t offset[] = { 0, 4, 2, 1 };
     236           0 :     return offset[aPass];
     237             :   }
     238             : 
     239           0 :   static uint32_t InterlaceStride(uint32_t aPass)
     240             :   {
     241           0 :     MOZ_ASSERT(aPass < 4, "Invalid pass");
     242             :     static const uint8_t stride[] = { 8, 8, 4, 2 };
     243           0 :     return stride[aPass];
     244             :   }
     245             : 
     246           0 :   static int32_t HaeberliOutputStartRow(uint32_t aPass,
     247             :                                         bool aProgressiveDisplay,
     248             :                                         int32_t aOutputRow)
     249             :   {
     250           0 :     MOZ_ASSERT(aPass < 4, "Invalid pass");
     251             :     static const uint8_t firstRowOffset[] = { 3, 1, 0, 0 };
     252             : 
     253           0 :     if (aProgressiveDisplay) {
     254           0 :       return std::max(aOutputRow - firstRowOffset[aPass], 0);
     255             :     } else {
     256           0 :       return aOutputRow;
     257             :     }
     258             :   }
     259             : 
     260           0 :   static int32_t HaeberliOutputUntilRow(uint32_t aPass,
     261             :                                         bool aProgressiveDisplay,
     262             :                                         const gfx::IntSize& aInputSize,
     263             :                                         int32_t aOutputRow)
     264             :   {
     265           0 :     MOZ_ASSERT(aPass < 4, "Invalid pass");
     266             :     static const uint8_t lastRowOffset[] = { 4, 2, 1, 0 };
     267             : 
     268           0 :     if (aProgressiveDisplay) {
     269           0 :       return std::min(aOutputRow + lastRowOffset[aPass],
     270           0 :                       aInputSize.height - 1)
     271           0 :              + 1;  // Add one because this is an open interval on the right.
     272             :     } else {
     273           0 :       return aOutputRow + 1;
     274             :     }
     275             :   }
     276             : 
     277           0 :   void DuplicateRows(int32_t aStart, int32_t aUntil)
     278             :   {
     279           0 :     MOZ_ASSERT(aStart >= 0);
     280           0 :     MOZ_ASSERT(aUntil >= 0);
     281             : 
     282           0 :     if (aUntil <= aStart || aStart >= InputSize().height) {
     283           0 :       return;
     284             :     }
     285             : 
     286             :     // The source row is the first row in the range.
     287           0 :     const uint8_t* sourceRowPointer = GetRowPointer(aStart);
     288             : 
     289             :     // We duplicate the source row into each subsequent row in the range.
     290           0 :     for (int32_t destRow = aStart + 1 ; destRow < aUntil ; ++destRow) {
     291           0 :       uint8_t* destRowPointer = GetRowPointer(destRow);
     292           0 :       memcpy(destRowPointer, sourceRowPointer, InputSize().width * sizeof(PixelType));
     293             :     }
     294             :   }
     295             : 
     296           0 :   void OutputRows(int32_t aStart, int32_t aUntil)
     297             :   {
     298           0 :     MOZ_ASSERT(aStart >= 0);
     299           0 :     MOZ_ASSERT(aUntil >= 0);
     300             : 
     301           0 :     if (aUntil <= aStart || aStart >= InputSize().height) {
     302           0 :       return;
     303             :     }
     304             : 
     305           0 :     for (int32_t rowToOutput = aStart; rowToOutput < aUntil; ++rowToOutput) {
     306           0 :       mNext.WriteBuffer(reinterpret_cast<PixelType*>(GetRowPointer(rowToOutput)));
     307             :     }
     308             :   }
     309             : 
     310           0 :   uint8_t* GetRowPointer(uint32_t aRow) const
     311             :   {
     312           0 :     uint32_t offset = aRow * InputSize().width * sizeof(PixelType);
     313           0 :     MOZ_ASSERT(offset < InputSize().width * InputSize().height * sizeof(PixelType),
     314             :                "Start of row is outside of image");
     315           0 :     MOZ_ASSERT(offset + InputSize().width * sizeof(PixelType)
     316             :                  <= InputSize().width * InputSize().height * sizeof(PixelType),
     317             :                "End of row is outside of image");
     318           0 :     return mBuffer.get() + offset;
     319             :   }
     320             : 
     321             :   Next mNext;                    /// The next SurfaceFilter in the chain.
     322             : 
     323             :   UniquePtr<uint8_t[]> mBuffer;  /// The buffer used to store reordered rows.
     324             :   int32_t mInputRow;             /// The current row we're reading. (0-indexed)
     325             :   int32_t mOutputRow;            /// The current row we're writing. (0-indexed)
     326             :   uint8_t mPass;                 /// Which pass we're on. (0-indexed)
     327             :   bool mProgressiveDisplay;      /// If true, duplicate rows to optimize for
     328             :                                  /// progressive display.
     329             : };
     330             : 
     331             : 
     332             : //////////////////////////////////////////////////////////////////////////////
     333             : // RemoveFrameRectFilter
     334             : //////////////////////////////////////////////////////////////////////////////
     335             : 
     336             : template <typename Next> class RemoveFrameRectFilter;
     337             : 
     338             : /**
     339             :  * A configuration struct for RemoveFrameRectFilter.
     340             :  */
     341             : struct RemoveFrameRectConfig
     342             : {
     343             :   template <typename Next> using Filter = RemoveFrameRectFilter<Next>;
     344             :   gfx::IntRect mFrameRect;  /// The surface subrect which contains data.
     345             : };
     346             : 
     347             : /**
     348             :  * RemoveFrameRectFilter turns an image with a frame rect that does not match
     349             :  * its logical size into an image with no frame rect. It does this by writing
     350             :  * transparent pixels into any padding regions and throwing away excess data.
     351             :  *
     352             :  * The 'Next' template parameter specifies the next filter in the chain.
     353             :  */
     354             : template <typename Next>
     355           0 : class RemoveFrameRectFilter final : public SurfaceFilter
     356             : {
     357             : public:
     358           0 :   RemoveFrameRectFilter()
     359           0 :     : mRow(0)
     360           0 :   { }
     361             : 
     362             :   template <typename... Rest>
     363           0 :   nsresult Configure(const RemoveFrameRectConfig& aConfig, Rest... aRest)
     364             :   {
     365           0 :     nsresult rv = mNext.Configure(aRest...);
     366           0 :     if (NS_FAILED(rv)) {
     367           0 :       return rv;
     368             :     }
     369             : 
     370           0 :     if (mNext.IsValidPalettedPipe()) {
     371           0 :       NS_WARNING("RemoveFrameRectFilter used with paletted pipe?");
     372           0 :       return NS_ERROR_INVALID_ARG;
     373             :     }
     374             : 
     375           0 :     mFrameRect = mUnclampedFrameRect = aConfig.mFrameRect;
     376           0 :     gfx::IntSize outputSize = mNext.InputSize();
     377             : 
     378             :     // Forbid frame rects with negative size.
     379           0 :     if (aConfig.mFrameRect.width < 0 || aConfig.mFrameRect.height < 0) {
     380           0 :       return NS_ERROR_INVALID_ARG;
     381             :     }
     382             : 
     383             :     // Clamp mFrameRect to the output size.
     384           0 :     gfx::IntRect outputRect(0, 0, outputSize.width, outputSize.height);
     385           0 :     mFrameRect = mFrameRect.Intersect(outputRect);
     386             : 
     387             :     // If there's no intersection, |mFrameRect| will be an empty rect positioned
     388             :     // at the maximum of |inputRect|'s and |aFrameRect|'s coordinates, which is
     389             :     // not what we want. Force it to (0, 0) in that case.
     390           0 :     if (mFrameRect.IsEmpty()) {
     391           0 :       mFrameRect.MoveTo(0, 0);
     392             :     }
     393             : 
     394             :     // We don't need an intermediate buffer unless the unclamped frame rect
     395             :     // width is larger than the clamped frame rect width. In that case, the
     396             :     // caller will end up writing data that won't end up in the final image at
     397             :     // all, and we'll need a buffer to give that data a place to go.
     398           0 :     if (mFrameRect.width < mUnclampedFrameRect.width) {
     399           0 :       mBuffer.reset(new (fallible) uint8_t[mUnclampedFrameRect.width *
     400             :                                            sizeof(uint32_t)]);
     401           0 :       if (MOZ_UNLIKELY(!mBuffer)) {
     402           0 :         return NS_ERROR_OUT_OF_MEMORY;
     403             :       }
     404             : 
     405           0 :       memset(mBuffer.get(), 0, mUnclampedFrameRect.width * sizeof(uint32_t));
     406             :     }
     407             : 
     408           0 :     ConfigureFilter(mUnclampedFrameRect.Size(), sizeof(uint32_t));
     409           0 :     return NS_OK;
     410             :   }
     411             : 
     412           0 :   Maybe<SurfaceInvalidRect> TakeInvalidRect() override
     413             :   {
     414           0 :     return mNext.TakeInvalidRect();
     415             :   }
     416             : 
     417             : protected:
     418           0 :   uint8_t* DoResetToFirstRow() override
     419             :   {
     420           0 :     uint8_t* rowPtr = mNext.ResetToFirstRow();
     421           0 :     if (rowPtr == nullptr) {
     422           0 :       mRow = mFrameRect.YMost();
     423           0 :       return nullptr;
     424             :     }
     425             : 
     426           0 :     mRow = mUnclampedFrameRect.y;
     427             : 
     428             :     // Advance the next pipeline stage to the beginning of the frame rect,
     429             :     // outputting blank rows.
     430           0 :     if (mFrameRect.y > 0) {
     431           0 :       for (int32_t rowToOutput = 0; rowToOutput < mFrameRect.y ; ++rowToOutput) {
     432           0 :         mNext.WriteEmptyRow();
     433             :       }
     434             :     }
     435             : 
     436             :     // We're at the beginning of the frame rect now, so return if we're either
     437             :     // ready for input or we're already done.
     438           0 :     rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
     439           0 :     if (!mFrameRect.IsEmpty() || rowPtr == nullptr) {
     440             :       // Note that the pointer we're returning is for the next row we're
     441             :       // actually going to write to, but we may discard writes before that point
     442             :       // if mRow < mFrameRect.y.
     443           0 :       return AdjustRowPointer(rowPtr);
     444             :     }
     445             : 
     446             :     // We've finished the region specified by the frame rect, but the frame rect
     447             :     // is empty, so we need to output the rest of the image immediately. Advance
     448             :     // to the end of the next pipeline stage's buffer, outputting blank rows.
     449           0 :     while (mNext.WriteEmptyRow() == WriteState::NEED_MORE_DATA) { }
     450             : 
     451           0 :     mRow = mFrameRect.YMost();
     452           0 :     return nullptr;  // We're done.
     453             :   }
     454             : 
     455           0 :   uint8_t* DoAdvanceRow() override
     456             :   {
     457           0 :     uint8_t* rowPtr = nullptr;
     458             : 
     459           0 :     const int32_t currentRow = mRow;
     460           0 :     mRow++;
     461             : 
     462           0 :     if (currentRow < mFrameRect.y) {
     463             :       // This row is outside of the frame rect, so just drop it on the floor.
     464           0 :       rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
     465           0 :       return AdjustRowPointer(rowPtr);
     466           0 :     } else if (currentRow >= mFrameRect.YMost()) {
     467           0 :       NS_WARNING("RemoveFrameRectFilter: Advancing past end of frame rect");
     468           0 :       return nullptr;
     469             :     }
     470             : 
     471             :     // If we had to buffer, copy the data. Otherwise, just advance the row.
     472           0 :     if (mBuffer) {
     473             :       // We write from the beginning of the buffer unless |mUnclampedFrameRect.x|
     474             :       // is negative; if that's the case, we have to skip the portion of the
     475             :       // unclamped frame rect that's outside the row.
     476           0 :       uint32_t* source = reinterpret_cast<uint32_t*>(mBuffer.get()) -
     477           0 :                          std::min(mUnclampedFrameRect.x, 0);
     478             : 
     479             :       // We write |mFrameRect.width| columns starting at |mFrameRect.x|; we've
     480             :       // already clamped these values to the size of the output, so we don't
     481             :       // have to worry about bounds checking here (though WriteBuffer() will do
     482             :       // it for us in any case).
     483           0 :       WriteState state = mNext.WriteBuffer(source, mFrameRect.x, mFrameRect.width);
     484             : 
     485           0 :       rowPtr = state == WriteState::NEED_MORE_DATA ? mBuffer.get()
     486             :                                                    : nullptr;
     487             :     } else {
     488           0 :       rowPtr = mNext.AdvanceRow();
     489             :     }
     490             : 
     491             :     // If there's still more data coming or we're already done, just adjust the
     492             :     // pointer and return.
     493           0 :     if (mRow < mFrameRect.YMost() || rowPtr == nullptr) {
     494           0 :       return AdjustRowPointer(rowPtr);
     495             :     }
     496             : 
     497             :     // We've finished the region specified by the frame rect. Advance to the end
     498             :     // of the next pipeline stage's buffer, outputting blank rows.
     499           0 :     while (mNext.WriteEmptyRow() == WriteState::NEED_MORE_DATA) { }
     500             : 
     501           0 :     mRow = mFrameRect.YMost();
     502           0 :     return nullptr;  // We're done.
     503             :   }
     504             : 
     505             : private:
     506           0 :   uint8_t* AdjustRowPointer(uint8_t* aNextRowPointer) const
     507             :   {
     508           0 :     if (mBuffer) {
     509           0 :       MOZ_ASSERT(aNextRowPointer == mBuffer.get() || aNextRowPointer == nullptr);
     510           0 :       return aNextRowPointer;  // No adjustment needed for an intermediate buffer.
     511             :     }
     512             : 
     513           0 :     if (mFrameRect.IsEmpty() ||
     514           0 :         mRow >= mFrameRect.YMost() ||
     515             :         aNextRowPointer == nullptr) {
     516           0 :       return nullptr;  // Nothing left to write.
     517             :     }
     518             : 
     519           0 :     return aNextRowPointer + mFrameRect.x * sizeof(uint32_t);
     520             :   }
     521             : 
     522             :   Next mNext;                        /// The next SurfaceFilter in the chain.
     523             : 
     524             :   gfx::IntRect mFrameRect;           /// The surface subrect which contains data,
     525             :                                      /// clamped to the image size.
     526             :   gfx::IntRect mUnclampedFrameRect;  /// The frame rect before clamping.
     527             :   UniquePtr<uint8_t[]> mBuffer;      /// The intermediate buffer, if one is
     528             :                                      /// necessary because the frame rect width
     529             :                                      /// is larger than the image's logical width.
     530             :   int32_t  mRow;                     /// The row in unclamped frame rect space
     531             :                                      /// that we're currently writing.
     532             : };
     533             : 
     534             : 
     535             : //////////////////////////////////////////////////////////////////////////////
     536             : // ADAM7InterpolatingFilter
     537             : //////////////////////////////////////////////////////////////////////////////
     538             : 
     539             : template <typename Next> class ADAM7InterpolatingFilter;
     540             : 
     541             : /**
     542             :  * A configuration struct for ADAM7InterpolatingFilter.
     543             :  */
     544             : struct ADAM7InterpolatingConfig
     545             : {
     546             :   template <typename Next> using Filter = ADAM7InterpolatingFilter<Next>;
     547             : };
     548             : 
     549             : /**
     550             :  * ADAM7InterpolatingFilter performs bilinear interpolation over an ADAM7
     551             :  * interlaced image.
     552             :  *
     553             :  * ADAM7 breaks up the image into 8x8 blocks. On each of the 7 passes, a new set
     554             :  * of pixels in each block receives their final values, according to the
     555             :  * following pattern:
     556             :  *
     557             :  *    1 6 4 6 2 6 4 6
     558             :  *    7 7 7 7 7 7 7 7
     559             :  *    5 6 5 6 5 6 5 6
     560             :  *    7 7 7 7 7 7 7 7
     561             :  *    3 6 4 6 3 6 4 6
     562             :  *    7 7 7 7 7 7 7 7
     563             :  *    5 6 5 6 5 6 5 6
     564             :  *    7 7 7 7 7 7 7 7
     565             :  *
     566             :  * When rendering the pixels that have not yet received their final values, we
     567             :  * can get much better intermediate results if we interpolate between
     568             :  * the pixels we *have* gotten so far. This filter performs bilinear
     569             :  * interpolation by first performing linear interpolation horizontally for each
     570             :  * "important" row (which we'll define as a row that has received any pixels
     571             :  * with final values at all) and then performing linear interpolation vertically
     572             :  * to produce pixel values for rows which aren't important on the current pass.
     573             :  *
     574             :  * Note that this filter totally ignores the data which is written to rows which
     575             :  * aren't important on the current pass! It's fine to write nothing at all for
     576             :  * these rows, although doing so won't cause any harm.
     577             :  *
     578             :  * XXX(seth): In bug 1280552 we'll add a SIMD implementation for this filter.
     579             :  *
     580             :  * The 'Next' template parameter specifies the next filter in the chain.
     581             :  */
     582             : template <typename Next>
     583           0 : class ADAM7InterpolatingFilter final : public SurfaceFilter
     584             : {
     585             : public:
     586           0 :   ADAM7InterpolatingFilter()
     587             :     : mPass(0)  // The current pass, in the range 1..7. Starts at 0 so that
     588             :                 // DoResetToFirstRow() doesn't have to special case the first pass.
     589           0 :     , mRow(0)
     590           0 :   { }
     591             : 
     592             :   template <typename... Rest>
     593           0 :   nsresult Configure(const ADAM7InterpolatingConfig& aConfig, Rest... aRest)
     594             :   {
     595           0 :     nsresult rv = mNext.Configure(aRest...);
     596           0 :     if (NS_FAILED(rv)) {
     597           0 :       return rv;
     598             :     }
     599             : 
     600           0 :     if (mNext.IsValidPalettedPipe()) {
     601           0 :       NS_WARNING("ADAM7InterpolatingFilter used with paletted pipe?");
     602           0 :       return NS_ERROR_INVALID_ARG;
     603             :     }
     604             : 
     605             :     // We have two intermediate buffers, one for the previous row with final
     606             :     // pixel values and one for the row that the previous filter in the chain is
     607             :     // currently writing to.
     608           0 :     size_t inputWidthInBytes = mNext.InputSize().width * sizeof(uint32_t);
     609           0 :     mPreviousRow.reset(new (fallible) uint8_t[inputWidthInBytes]);
     610           0 :     if (MOZ_UNLIKELY(!mPreviousRow)) {
     611           0 :       return NS_ERROR_OUT_OF_MEMORY;
     612             :     }
     613             : 
     614           0 :     mCurrentRow.reset(new (fallible) uint8_t[inputWidthInBytes]);
     615           0 :     if (MOZ_UNLIKELY(!mCurrentRow)) {
     616           0 :       return NS_ERROR_OUT_OF_MEMORY;
     617             :     }
     618             : 
     619           0 :     memset(mPreviousRow.get(), 0, inputWidthInBytes);
     620           0 :     memset(mCurrentRow.get(), 0, inputWidthInBytes);
     621             : 
     622           0 :     ConfigureFilter(mNext.InputSize(), sizeof(uint32_t));
     623           0 :     return NS_OK;
     624             :   }
     625             : 
     626           0 :   Maybe<SurfaceInvalidRect> TakeInvalidRect() override
     627             :   {
     628           0 :     return mNext.TakeInvalidRect();
     629             :   }
     630             : 
     631             : protected:
     632           0 :   uint8_t* DoResetToFirstRow() override
     633             :   {
     634           0 :     mRow = 0;
     635           0 :     mPass = std::min(mPass + 1, 7);
     636             : 
     637           0 :     uint8_t* rowPtr = mNext.ResetToFirstRow();
     638           0 :     if (mPass == 7) {
     639             :       // Short circuit this filter on the final pass, since all pixels have
     640             :       // their final values at that point.
     641           0 :       return rowPtr;
     642             :     }
     643             : 
     644           0 :     return mCurrentRow.get();
     645             :   }
     646             : 
     647           0 :   uint8_t* DoAdvanceRow() override
     648             :   {
     649           0 :     MOZ_ASSERT(0 < mPass && mPass <= 7, "Invalid pass");
     650             : 
     651           0 :     int32_t currentRow = mRow;
     652           0 :     ++mRow;
     653             : 
     654           0 :     if (mPass == 7) {
     655             :       // On the final pass we short circuit this filter totally.
     656           0 :       return mNext.AdvanceRow();
     657             :     }
     658             : 
     659           0 :     const int32_t lastImportantRow = LastImportantRow(InputSize().height, mPass);
     660           0 :     if (currentRow > lastImportantRow) {
     661           0 :       return nullptr;  // This pass is already complete.
     662             :     }
     663             : 
     664           0 :     if (!IsImportantRow(currentRow, mPass)) {
     665             :       // We just ignore whatever the caller gives us for these rows. We'll
     666             :       // interpolate them in later.
     667           0 :       return mCurrentRow.get();
     668             :     }
     669             : 
     670             :     // This is an important row. We need to perform horizontal interpolation for
     671             :     // these rows.
     672           0 :     InterpolateHorizontally(mCurrentRow.get(), InputSize().width, mPass);
     673             : 
     674             :     // Interpolate vertically between the previous important row and the current
     675             :     // important row. We skip this if the current row is 0 (which is always an
     676             :     // important row), because in that case there is no previous important row
     677             :     // to interpolate with.
     678           0 :     if (currentRow != 0) {
     679           0 :       InterpolateVertically(mPreviousRow.get(), mCurrentRow.get(), mPass, mNext);
     680             :     }
     681             : 
     682             :     // Write out the current row itself, which, being an important row, does not
     683             :     // need vertical interpolation.
     684           0 :     uint32_t* currentRowAsPixels = reinterpret_cast<uint32_t*>(mCurrentRow.get());
     685           0 :     mNext.WriteBuffer(currentRowAsPixels);
     686             : 
     687           0 :     if (currentRow == lastImportantRow) {
     688             :       // This is the last important row, which completes this pass. Note that
     689             :       // for very small images, this may be the first row! Since there won't be
     690             :       // another important row, there's nothing to interpolate with vertically,
     691             :       // so we just duplicate this row until the end of the image.
     692           0 :       while (mNext.WriteBuffer(currentRowAsPixels) == WriteState::NEED_MORE_DATA) { }
     693             : 
     694             :       // All of the remaining rows in the image were determined above, so we're done.
     695           0 :       return nullptr;
     696             :     }
     697             : 
     698             :     // The current row is now the previous important row; save it.
     699           0 :     Swap(mPreviousRow, mCurrentRow);
     700             : 
     701           0 :     MOZ_ASSERT(mRow < InputSize().height, "Reached the end of the surface without "
     702             :                                           "hitting the last important row?");
     703             : 
     704           0 :     return mCurrentRow.get();
     705             :   }
     706             : 
     707             : private:
     708           0 :   static void InterpolateVertically(uint8_t* aPreviousRow,
     709             :                                     uint8_t* aCurrentRow,
     710             :                                     uint8_t aPass,
     711             :                                     SurfaceFilter& aNext)
     712             :   {
     713           0 :     const float* weights = InterpolationWeights(ImportantRowStride(aPass));
     714             : 
     715             :     // We need to interpolate vertically to generate the rows between the
     716             :     // previous important row and the next one. Recall that important rows are
     717             :     // rows which contain at least some final pixels; see
     718             :     // InterpolateHorizontally() for some additional explanation as to what that
     719             :     // means. Note that we've already written out the previous important row, so
     720             :     // we start the iteration at 1.
     721           0 :     for (int32_t outRow = 1; outRow < ImportantRowStride(aPass); ++outRow) {
     722           0 :       const float weight = weights[outRow];
     723             : 
     724             :       // We iterate through the previous and current important row every time we
     725             :       // write out an interpolated row, so we need to copy the pointers.
     726           0 :       uint8_t* prevRowBytes = aPreviousRow;
     727           0 :       uint8_t* currRowBytes = aCurrentRow;
     728             : 
     729             :       // Write out the interpolated pixels. Interpolation is componentwise.
     730           0 :       aNext.template WritePixelsToRow<uint32_t>([&]{
     731           0 :         uint32_t pixel = 0;
     732           0 :         auto* component = reinterpret_cast<uint8_t*>(&pixel);
     733           0 :         *component++ = InterpolateByte(*prevRowBytes++, *currRowBytes++, weight);
     734           0 :         *component++ = InterpolateByte(*prevRowBytes++, *currRowBytes++, weight);
     735           0 :         *component++ = InterpolateByte(*prevRowBytes++, *currRowBytes++, weight);
     736           0 :         *component++ = InterpolateByte(*prevRowBytes++, *currRowBytes++, weight);
     737           0 :         return AsVariant(pixel);
     738             :       });
     739             :     }
     740           0 :   }
     741             : 
     742           0 :   static void InterpolateHorizontally(uint8_t* aRow, int32_t aWidth, uint8_t aPass)
     743             :   {
     744             :     // Collect the data we'll need to perform horizontal interpolation. The
     745             :     // terminology here bears some explanation: a "final pixel" is a pixel which
     746             :     // has received its final value. On each pass, a new set of pixels receives
     747             :     // their final value; see the diagram above of the 8x8 pattern that ADAM7
     748             :     // uses. Any pixel which hasn't received its final value on this pass
     749             :     // derives its value from either horizontal or vertical interpolation
     750             :     // instead.
     751           0 :     const size_t finalPixelStride = FinalPixelStride(aPass);
     752           0 :     const size_t finalPixelStrideBytes = finalPixelStride * sizeof(uint32_t);
     753           0 :     const size_t lastFinalPixel = LastFinalPixel(aWidth, aPass);
     754           0 :     const size_t lastFinalPixelBytes = lastFinalPixel * sizeof(uint32_t);
     755           0 :     const float* weights = InterpolationWeights(finalPixelStride);
     756             : 
     757             :     // Interpolate blocks of pixels which lie between two final pixels.
     758             :     // Horizontal interpolation is done in place, as we'll need the results
     759             :     // later when we vertically interpolate.
     760           0 :     for (size_t blockBytes = 0;
     761           0 :          blockBytes < lastFinalPixelBytes;
     762             :          blockBytes += finalPixelStrideBytes) {
     763           0 :       uint8_t* finalPixelA = aRow + blockBytes;
     764           0 :       uint8_t* finalPixelB = aRow + blockBytes + finalPixelStrideBytes;
     765             : 
     766           0 :       MOZ_ASSERT(finalPixelA < aRow + aWidth * sizeof(uint32_t),
     767             :                  "Running off end of buffer");
     768           0 :       MOZ_ASSERT(finalPixelB < aRow + aWidth * sizeof(uint32_t),
     769             :                  "Running off end of buffer");
     770             : 
     771             :       // Interpolate the individual pixels componentwise. Note that we start
     772             :       // iteration at 1 since we don't need to apply any interpolation to the
     773             :       // first pixel in the block, which has its final value.
     774           0 :       for (size_t pixelIndex = 1; pixelIndex < finalPixelStride; ++pixelIndex) {
     775           0 :         const float weight = weights[pixelIndex];
     776           0 :         uint8_t* pixel = aRow + blockBytes + pixelIndex * sizeof(uint32_t);
     777             : 
     778           0 :         MOZ_ASSERT(pixel < aRow + aWidth * sizeof(uint32_t), "Running off end of buffer");
     779             : 
     780           0 :         for (size_t component = 0; component < sizeof(uint32_t); ++component) {
     781           0 :           pixel[component] =
     782           0 :             InterpolateByte(finalPixelA[component], finalPixelB[component], weight);
     783             :         }
     784             :       }
     785             :     }
     786             : 
     787             :     // For the pixels after the last final pixel in the row, there isn't a
     788             :     // second final pixel to interpolate with, so just duplicate.
     789           0 :     uint32_t* rowPixels = reinterpret_cast<uint32_t*>(aRow);
     790           0 :     uint32_t pixelToDuplicate = rowPixels[lastFinalPixel];
     791           0 :     for (int32_t pixelIndex = lastFinalPixel + 1;
     792           0 :          pixelIndex < aWidth;
     793             :          ++pixelIndex) {
     794           0 :       MOZ_ASSERT(pixelIndex < aWidth, "Running off end of buffer");
     795           0 :       rowPixels[pixelIndex] = pixelToDuplicate;
     796             :     }
     797           0 :   }
     798             : 
     799           0 :   static uint8_t InterpolateByte(uint8_t aByteA, uint8_t aByteB, float aWeight)
     800             :   {
     801           0 :     return uint8_t(aByteA * aWeight + aByteB * (1.0f - aWeight));
     802             :   }
     803             : 
     804           0 :   static int32_t ImportantRowStride(uint8_t aPass)
     805             :   {
     806           0 :     MOZ_ASSERT(0 < aPass && aPass <= 7, "Invalid pass");
     807             : 
     808             :     // The stride between important rows for each pass, with a dummy value for
     809             :     // the nonexistent pass 0.
     810             :     static int32_t strides[] = { 1, 8, 8, 4, 4, 2, 2, 1 };
     811             : 
     812           0 :     return strides[aPass];
     813             :   }
     814             : 
     815           0 :   static bool IsImportantRow(int32_t aRow, uint8_t aPass)
     816             :   {
     817           0 :     MOZ_ASSERT(aRow >= 0);
     818             : 
     819             :     // Whether the row is important comes down to divisibility by the stride for
     820             :     // this pass, which is always a power of 2, so we can check using a mask.
     821           0 :     int32_t mask = ImportantRowStride(aPass) - 1;
     822           0 :     return (aRow & mask) == 0;
     823             :   }
     824             : 
     825           0 :   static int32_t LastImportantRow(int32_t aHeight, uint8_t aPass)
     826             :   {
     827           0 :     MOZ_ASSERT(aHeight > 0);
     828             : 
     829             :     // We can find the last important row using the same mask trick as above.
     830           0 :     int32_t lastRow = aHeight - 1;
     831           0 :     int32_t mask = ImportantRowStride(aPass) - 1;
     832           0 :     return lastRow - (lastRow & mask);
     833             :   }
     834             : 
     835           0 :   static size_t FinalPixelStride(uint8_t aPass)
     836             :   {
     837           0 :     MOZ_ASSERT(0 < aPass && aPass <= 7, "Invalid pass");
     838             : 
     839             :     // The stride between the final pixels in important rows for each pass, with
     840             :     // a dummy value for the nonexistent pass 0.
     841             :     static size_t strides[] = { 1, 8, 4, 4, 2, 2, 1, 1 };
     842             : 
     843           0 :     return strides[aPass];
     844             :   }
     845             : 
     846           0 :   static size_t LastFinalPixel(int32_t aWidth, uint8_t aPass)
     847             :   {
     848           0 :     MOZ_ASSERT(aWidth >= 0);
     849             : 
     850             :     // Again, we can use the mask trick above to find the last important pixel.
     851           0 :     int32_t lastColumn = aWidth - 1;
     852           0 :     size_t mask = FinalPixelStride(aPass) - 1;
     853           0 :     return lastColumn - (lastColumn & mask);
     854             :   }
     855             : 
     856           0 :   static const float* InterpolationWeights(int32_t aStride)
     857             :   {
     858             :     // Precalculated interpolation weights. These are used to interpolate
     859             :     // between final pixels or between important rows. Although no interpolation
     860             :     // is actually applied to the previous final pixel or important row value,
     861             :     // the arrays still start with 1.0f, which is always skipped, primarily
     862             :     // because otherwise |stride1Weights| would have zero elements.
     863             :     static float stride8Weights[] =
     864             :       { 1.0f, 7 / 8.0f, 6 / 8.0f, 5 / 8.0f, 4 / 8.0f, 3 / 8.0f, 2 / 8.0f, 1 / 8.0f };
     865             :     static float stride4Weights[] = { 1.0f, 3 / 4.0f, 2 / 4.0f, 1 / 4.0f };
     866             :     static float stride2Weights[] = { 1.0f, 1 / 2.0f };
     867             :     static float stride1Weights[] = { 1.0f };
     868             : 
     869           0 :     switch (aStride) {
     870           0 :       case 8:  return stride8Weights;
     871           0 :       case 4:  return stride4Weights;
     872           0 :       case 2:  return stride2Weights;
     873           0 :       case 1:  return stride1Weights;
     874           0 :       default: MOZ_CRASH();
     875             :     }
     876             :   }
     877             : 
     878             :   Next mNext;                         /// The next SurfaceFilter in the chain.
     879             : 
     880             :   UniquePtr<uint8_t[]> mPreviousRow;  /// The last important row (i.e., row with
     881             :                                       /// final pixel values) that got written to.
     882             :   UniquePtr<uint8_t[]> mCurrentRow;   /// The row that's being written to right
     883             :                                       /// now.
     884             :   uint8_t mPass;                      /// Which ADAM7 pass we're on. Valid passes
     885             :                                       /// are 1..7 during processing and 0 prior
     886             :                                       /// to configuraiton.
     887             :   int32_t mRow;                       /// The row we're currently reading.
     888             : };
     889             : 
     890             : } // namespace image
     891             : } // namespace mozilla
     892             : 
     893             : #endif // mozilla_image_SurfaceFilters_h

Generated by: LCOV version 1.13