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