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:
SwizzleFilter()61   SwizzleFilter() : mSwizzleFn(nullptr) {}
62 
63   template <typename... Rest>
Configure(const SwizzleConfig & aConfig,const Rest &...aRest)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 
TakeInvalidRect()84   Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
85     return mNext.TakeInvalidRect();
86   }
87 
88  protected:
DoResetToFirstRow()89   uint8_t* DoResetToFirstRow() override { return mNext.ResetToFirstRow(); }
90 
DoAdvanceRowFromBuffer(const uint8_t * aInputRow)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 
DoAdvanceRow()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:
ColorManagementFilter()135   ColorManagementFilter() : mTransform(nullptr) {}
136 
137   template <typename... Rest>
Configure(const ColorManagementConfig & aConfig,const Rest &...aRest)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 
TakeInvalidRect()154   Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
155     return mNext.TakeInvalidRect();
156   }
157 
158  protected:
DoResetToFirstRow()159   uint8_t* DoResetToFirstRow() override { return mNext.ResetToFirstRow(); }
160 
DoAdvanceRowFromBuffer(const uint8_t * aInputRow)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 
DoAdvanceRow()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:
DeinterlacingFilter()210   DeinterlacingFilter()
211       : mInputRow(0), mOutputRow(0), mPass(0), mProgressiveDisplay(true) {}
212 
213   template <typename... Rest>
Configure(const DeinterlacingConfig<PixelType> & aConfig,const Rest &...aRest)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 
TakeInvalidRect()254   Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
255     return mNext.TakeInvalidRect();
256   }
257 
258  protected:
DoResetToFirstRow()259   uint8_t* DoResetToFirstRow() override {
260     mNext.ResetToFirstRow();
261     mPass = 0;
262     mInputRow = 0;
263     mOutputRow = InterlaceOffset(mPass);
264     return GetRowPointer(mOutputRow);
265   }
266 
DoAdvanceRowFromBuffer(const uint8_t * aInputRow)267   uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
268     CopyInputRow(aInputRow);
269     return DoAdvanceRow();
270   }
271 
DoAdvanceRow()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:
InterlaceOffset(uint32_t aPass)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 
InterlaceStride(uint32_t aPass)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 
HaeberliOutputStartRow(uint32_t aPass,bool aProgressiveDisplay,int32_t aOutputRow)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 
HaeberliOutputUntilRow(uint32_t aPass,bool aProgressiveDisplay,const gfx::IntSize & aInputSize,int32_t aOutputRow)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 
DuplicateRows(int32_t aStart,int32_t aUntil)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 
OutputRows(int32_t aStart,int32_t aUntil)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 
GetRowPointer(uint32_t aRow)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:
BlendAnimationFilter()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>
Configure(const BlendAnimationConfig & aConfig,const Rest &...aRest)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 
TakeInvalidRect()686   Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
687     return mNext.TakeInvalidRect();
688   }
689 
690  protected:
DoResetToFirstRow()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 
DoAdvanceRowFromBuffer(const uint8_t * aInputRow)728   uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
729     CopyInputRow(aInputRow);
730     return DoAdvanceRow();
731   }
732 
DoAdvanceRow()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:
WriteBaseFrameRowsUntilComplete()788   void WriteBaseFrameRowsUntilComplete() {
789     do {
790       WriteBaseFrameRow();
791     } while (AdvanceRowOutsideFrameRect());
792   }
793 
WriteBaseFrameRow()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 
AdvanceRowOutsideFrameRect()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 
AdjustRowPointer(uint8_t * aNextRowPointer)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:
RemoveFrameRectFilter()917   RemoveFrameRectFilter() : mRow(0) {}
918 
919   template <typename... Rest>
Configure(const RemoveFrameRectConfig & aConfig,const Rest &...aRest)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 
TakeInvalidRect()964   Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
965     return mNext.TakeInvalidRect();
966   }
967 
968  protected:
DoResetToFirstRow()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 
DoAdvanceRowFromBuffer(const uint8_t * aInputRow)1007   uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
1008     CopyInputRow(aInputRow);
1009     return DoAdvanceRow();
1010   }
1011 
DoAdvanceRow()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:
AdjustRowPointer(uint8_t * aNextRowPointer)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:
ADAM7InterpolatingFilter()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>
Configure(const ADAM7InterpolatingConfig & aConfig,const Rest &...aRest)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 
TakeInvalidRect()1178   Maybe<SurfaceInvalidRect> TakeInvalidRect() override {
1179     return mNext.TakeInvalidRect();
1180   }
1181 
1182  protected:
DoResetToFirstRow()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 
DoAdvanceRowFromBuffer(const uint8_t * aInputRow)1197   uint8_t* DoAdvanceRowFromBuffer(const uint8_t* aInputRow) override {
1198     CopyInputRow(aInputRow);
1199     return DoAdvanceRow();
1200   }
1201 
DoAdvanceRow()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:
InterpolateVertically(uint8_t * aPreviousRow,uint8_t * aCurrentRow,uint8_t aPass,SurfaceFilter & aNext)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 
InterpolateHorizontally(uint8_t * aRow,int32_t aWidth,uint8_t aPass)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 
InterpolateByte(uint8_t aByteA,uint8_t aByteB,float aWeight)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 
ImportantRowStride(uint8_t aPass)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 
IsImportantRow(int32_t aRow,uint8_t aPass)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 
LastImportantRow(int32_t aHeight,uint8_t aPass)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 
FinalPixelStride(uint8_t aPass)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 
LastFinalPixel(int32_t aWidth,uint8_t aPass)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 
InterpolationWeights(int32_t aStride)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
1455