diff --git a/image/Downscaler.cpp b/image/Downscaler.cpp index 0700eeb32295..a01b67c3a391 100644 --- a/image/Downscaler.cpp +++ b/image/Downscaler.cpp @@ -57,6 +57,7 @@ Downscaler::ReleaseWindow() nsresult Downscaler::BeginFrame(const nsIntSize& aOriginalSize, + const Maybe& aFrameRect, uint8_t* aOutputBuffer, bool aHasAlpha, bool aFlipVertically /* = false */) @@ -71,6 +72,17 @@ Downscaler::BeginFrame(const nsIntSize& aOriginalSize, MOZ_ASSERT(aOriginalSize.width > 0 && aOriginalSize.height > 0, "Invalid original size"); + mFrameRect = aFrameRect.valueOr(nsIntRect(nsIntPoint(), aOriginalSize)); + MOZ_ASSERT(mFrameRect.x >= 0 && mFrameRect.y >= 0 && + mFrameRect.width > 0 && mFrameRect.height > 0, + "Frame rect must have positive components"); + MOZ_ASSERT(nsIntRect(0, 0, aOriginalSize.width, aOriginalSize.height) + .Contains(mFrameRect), + "Frame rect must fit inside image"); + MOZ_ASSERT_IF(!nsIntRect(0, 0, aOriginalSize.width, aOriginalSize.height) + .IsEqualEdges(mFrameRect), + aHasAlpha); + mOriginalSize = aOriginalSize; mScale = gfxSize(double(mOriginalSize.width) / mTargetSize.width, double(mOriginalSize.height) / mTargetSize.height); @@ -78,7 +90,6 @@ Downscaler::BeginFrame(const nsIntSize& aOriginalSize, mHasAlpha = aHasAlpha; mFlipVertically = aFlipVertically; - ResetForNextProgressivePass(); ReleaseWindow(); auto resizeMethod = skia::ImageOperations::RESIZE_LANCZOS3; @@ -124,9 +135,22 @@ Downscaler::BeginFrame(const nsIntSize& aOriginalSize, return NS_ERROR_OUT_OF_MEMORY; } + ResetForNextProgressivePass(); + return NS_OK; } +void +Downscaler::SkipToRow(int32_t aRow) +{ + if (mCurrentInLine < aRow) { + ClearRow(); + do { + CommitRow(); + } while (mCurrentInLine < aRow); + } +} + void Downscaler::ResetForNextProgressivePass() { @@ -134,6 +158,9 @@ Downscaler::ResetForNextProgressivePass() mCurrentOutLine = 0; mCurrentInLine = 0; mLinesInBuffer = 0; + + // If we have a vertical offset, commit rows to shift us past it. + SkipToRow(mFrameRect.y); } static void @@ -193,6 +220,12 @@ Downscaler::CommitRow() } mCurrentInLine += 1; + + // If we're at the end of the part of the original image that has data, commit + // rows to shift us to the end. + if (mCurrentInLine == (mFrameRect.y + mFrameRect.height)) { + SkipToRow(mOriginalSize.height - 1); + } } bool diff --git a/image/Downscaler.h b/image/Downscaler.h index c883d952895b..4083e83df96e 100644 --- a/image/Downscaler.h +++ b/image/Downscaler.h @@ -12,10 +12,10 @@ #ifndef mozilla_image_Downscaler_h #define mozilla_image_Downscaler_h +#include "mozilla/Maybe.h" #include "mozilla/UniquePtr.h" #include "nsRect.h" - namespace skia { class ConvolutionFilter1D; } // namespace skia @@ -64,6 +64,9 @@ public: * Begins a new frame and reinitializes the Downscaler. * * @param aOriginalSize The original size of this frame, before scaling. + * @param aFrameRect The region of the original image which has data. + * Every pixel outside @aFrameRect is considered blank and + * has zero alpha. * @param aOutputBuffer The buffer to which the Downscaler should write its * output; this is the same buffer where the Decoder * would write its output when not downscaling during @@ -75,12 +78,16 @@ public: * the way they are stored in some image formats. */ nsresult BeginFrame(const nsIntSize& aOriginalSize, + const Maybe& aFrameRect, uint8_t* aOutputBuffer, bool aHasAlpha, bool aFlipVertically = false); /// Retrieves the buffer into which the Decoder should write each row. - uint8_t* RowBuffer() { return mRowBuffer.get(); } + uint8_t* RowBuffer() + { + return mRowBuffer.get() + mFrameRect.x * sizeof(uint32_t); + } /// Clears the current row buffer (optionally starting at @aStartingAtCol). void ClearRow(uint32_t aStartingAtCol = 0); @@ -104,9 +111,11 @@ public: private: void DownscaleInputLine(); void ReleaseWindow(); + void SkipToRow(int32_t aRow); nsIntSize mOriginalSize; nsIntSize mTargetSize; + nsIntRect mFrameRect; gfxSize mScale; uint8_t* mOutputBuffer; diff --git a/image/decoders/nsBMPDecoder.cpp b/image/decoders/nsBMPDecoder.cpp index c9c23296247c..b973b3d4eefa 100644 --- a/image/decoders/nsBMPDecoder.cpp +++ b/image/decoders/nsBMPDecoder.cpp @@ -461,7 +461,8 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount) if (mDownscaler) { // BMPs store their rows in reverse order, so the downscaler needs to // reverse them again when writing its output. - rv = mDownscaler->BeginFrame(GetSize(), mImageData, hasTransparency, + rv = mDownscaler->BeginFrame(GetSize(), Nothing(), + mImageData, hasTransparency, /* aFlipVertically = */ true); if (NS_FAILED(rv)) { return; diff --git a/image/decoders/nsGIFDecoder2.cpp b/image/decoders/nsGIFDecoder2.cpp index 2a64a34bcf6e..93c92bc5b0cb 100644 --- a/image/decoders/nsGIFDecoder2.cpp +++ b/image/decoders/nsGIFDecoder2.cpp @@ -279,7 +279,7 @@ nsGIFDecoder2::BeginImageFrame(uint16_t aDepth) } if (mDownscaler) { - rv = mDownscaler->BeginFrame(frameRect.Size(), mImageData, + rv = mDownscaler->BeginFrame(frameRect.Size(), Nothing(), mImageData, mGIFStruct.is_transparent); } diff --git a/image/decoders/nsICODecoder.cpp b/image/decoders/nsICODecoder.cpp index 1f8cf9439795..dadb6fa7489f 100644 --- a/image/decoders/nsICODecoder.cpp +++ b/image/decoders/nsICODecoder.cpp @@ -553,7 +553,7 @@ nsICODecoder::PrepareForMask() mDownscaler->TargetSize().height * sizeof(uint32_t)); mMaskBuffer = MakeUnique(bmpDecoder->GetImageDataLength()); - nsresult rv = mDownscaler->BeginFrame(GetRealSize(), + nsresult rv = mDownscaler->BeginFrame(GetRealSize(), Nothing(), mMaskBuffer.get(), /* aHasAlpha = */ true, /* aFlipVertically = */ true); diff --git a/image/decoders/nsIconDecoder.cpp b/image/decoders/nsIconDecoder.cpp index 55666f67dd4c..72df5913d3f7 100644 --- a/image/decoders/nsIconDecoder.cpp +++ b/image/decoders/nsIconDecoder.cpp @@ -93,7 +93,7 @@ nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount) MOZ_ASSERT(mImageData, "Should have a buffer now"); if (mDownscaler) { - nsresult rv = mDownscaler->BeginFrame(GetSize(), + nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(), mImageData, /* aHasAlpha = */ true); if (NS_FAILED(rv)) { diff --git a/image/decoders/nsJPEGDecoder.cpp b/image/decoders/nsJPEGDecoder.cpp index 61d32c0d53fc..b9d8619a742f 100644 --- a/image/decoders/nsJPEGDecoder.cpp +++ b/image/decoders/nsJPEGDecoder.cpp @@ -398,7 +398,7 @@ nsJPEGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount) MOZ_ASSERT(mImageData, "Should have a buffer now"); if (mDownscaler) { - nsresult rv = mDownscaler->BeginFrame(GetSize(), + nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(), mImageData, /* aHasAlpha = */ false); if (NS_FAILED(rv)) { diff --git a/image/decoders/nsPNGDecoder.cpp b/image/decoders/nsPNGDecoder.cpp index 74119178b0b8..6b60a9b914ab 100644 --- a/image/decoders/nsPNGDecoder.cpp +++ b/image/decoders/nsPNGDecoder.cpp @@ -209,7 +209,8 @@ nsPNGDecoder::CreateFrame(png_uint_32 aXOffset, png_uint_32 aYOffset, if (mDownscaler) { bool hasAlpha = aFormat != SurfaceFormat::B8G8R8X8; - rv = mDownscaler->BeginFrame(frameRect.Size(), mImageData, hasAlpha); + rv = mDownscaler->BeginFrame(frameRect.Size(), Nothing(), + mImageData, hasAlpha); if (NS_FAILED(rv)) { return rv; }