From 5376541aef0203dba29a8a546f02c060d140e6ed Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Mon, 17 Nov 2014 11:16:45 -0800 Subject: [PATCH] Bug 1089880 (Part 1) - Add a HAS_TRANSPARENCY notification to ImageLib. r=tn --- image/decoders/nsBMPDecoder.cpp | 11 +++++++++-- image/decoders/nsGIFDecoder2.cpp | 10 ++++++++-- image/decoders/nsICODecoder.cpp | 9 +++++++++ image/decoders/nsPNGDecoder.cpp | 7 ++++++- image/public/imgINotificationObserver.idl | 3 ++- image/src/Decoder.cpp | 6 ++++++ image/src/Decoder.h | 3 +++ image/src/ProgressTracker.cpp | 9 ++++++++- image/src/ProgressTracker.h | 7 ++++--- image/src/VectorImage.cpp | 1 + image/src/imgRequestProxy.cpp | 10 ++++++++++ image/src/imgRequestProxy.h | 17 +++++++++-------- 12 files changed, 75 insertions(+), 18 deletions(-) diff --git a/image/decoders/nsBMPDecoder.cpp b/image/decoders/nsBMPDecoder.cpp index 36acd6c7eb71..76feb149a54f 100644 --- a/image/decoders/nsBMPDecoder.cpp +++ b/image/decoders/nsBMPDecoder.cpp @@ -12,6 +12,7 @@ #include "ImageLogging.h" #include "mozilla/Endian.h" +#include "mozilla/Likely.h" #include "nsBMPDecoder.h" #include "nsIInputStream.h" @@ -658,10 +659,10 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount, memset(start, 0, pixelCount * sizeof(uint32_t)); + PostHasTransparency(); mHaveAlphaData = true; } - SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? - p[3] : 0xFF); + SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF); } else { SetPixel(d, p[2], p[1], p[0]); } @@ -789,6 +790,9 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount, mCurPos += byte; // Delta encoding makes it possible to skip pixels // making the image transparent. + if (MOZ_UNLIKELY(!mHaveAlphaData)) { + PostHasTransparency(); + } mUseAlphaData = mHaveAlphaData = true; if (mCurPos > mBIH.width) { mCurPos = mBIH.width; @@ -804,6 +808,9 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount, mState = eRLEStateInitial; // Delta encoding makes it possible to skip pixels // making the image transparent. + if (MOZ_UNLIKELY(!mHaveAlphaData)) { + PostHasTransparency(); + } mUseAlphaData = mHaveAlphaData = true; mCurLine -= std::min(byte, mCurLine); break; diff --git a/image/decoders/nsGIFDecoder2.cpp b/image/decoders/nsGIFDecoder2.cpp index 9a3e915ae9d9..3af9067fc1f5 100644 --- a/image/decoders/nsGIFDecoder2.cpp +++ b/image/decoders/nsGIFDecoder2.cpp @@ -164,15 +164,16 @@ nsGIFDecoder2::BeginGIF() void nsGIFDecoder2::BeginImageFrame(uint16_t aDepth) { + MOZ_ASSERT(HasSize()); + gfx::SurfaceFormat format; if (mGIFStruct.is_transparent) { format = gfx::SurfaceFormat::B8G8R8A8; + PostHasTransparency(); } else { format = gfx::SurfaceFormat::B8G8R8X8; } - MOZ_ASSERT(HasSize()); - // Use correct format, RGB for first frame, PAL for following frames // and include transparency to allow for optimization of opaque images if (mGIFStruct.images_decoded) { @@ -189,6 +190,11 @@ nsGIFDecoder2::BeginImageFrame(uint16_t aDepth) mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height))) { + + // We need padding on the first frame, which means that we don't draw into + // part of the image at all. Report that as transparency. + PostHasTransparency(); + // Regardless of depth of input, image is decoded into 24bit RGB NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset, mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height, diff --git a/image/decoders/nsICODecoder.cpp b/image/decoders/nsICODecoder.cpp index 7ab0dc40b190..64aafb4b585c 100644 --- a/image/decoders/nsICODecoder.cpp +++ b/image/decoders/nsICODecoder.cpp @@ -542,6 +542,8 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, return; } + uint8_t sawTransparency = 0; + while (mCurLine > 0 && aCount > 0) { uint32_t toCopy = std::min(rowSize - mRowBytes, aCount); if (toCopy) { @@ -567,6 +569,7 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, uint8_t* p_end = mRow + rowSize; while (p < p_end) { uint8_t idx = *p++; + sawTransparency |= idx; for (uint8_t bit = 0x80; bit && decoded>= 1) { // Clear pixel completely for transparency. if (idx & bit) { @@ -577,6 +580,12 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount, } } } + + // If any bits are set in sawTransparency, then we know at least one + // pixel was transparent. + if (sawTransparency) { + PostHasTransparency(); + } } } } diff --git a/image/decoders/nsPNGDecoder.cpp b/image/decoders/nsPNGDecoder.cpp index 2e935b1b4b74..c3a9ace54eeb 100644 --- a/image/decoders/nsPNGDecoder.cpp +++ b/image/decoders/nsPNGDecoder.cpp @@ -145,9 +145,14 @@ void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset, int32_t width, int32_t height, gfx::SurfaceFormat format) { + MOZ_ASSERT(HasSize()); + + if (format == gfx::SurfaceFormat::B8G8R8A8) { + PostHasTransparency(); + } + // Our first full frame is automatically created by the image decoding // infrastructure. Just use it as long as it matches up. - MOZ_ASSERT(HasSize()); nsIntRect neededRect(x_offset, y_offset, width, height); nsRefPtr currentFrame = GetCurrentFrame(); if (mNumFrames != 0 || !currentFrame->GetRect().IsEqualEdges(neededRect)) { diff --git a/image/public/imgINotificationObserver.idl b/image/public/imgINotificationObserver.idl index 98a5a3ad3d37..591be6ee7b5f 100644 --- a/image/public/imgINotificationObserver.idl +++ b/image/public/imgINotificationObserver.idl @@ -14,7 +14,7 @@ interface imgIRequest; [ptr] native nsIntRect(nsIntRect); -[scriptable, builtinclass, uuid(ac65c702-7771-4f6d-b18b-1c7d806ce3c1)] +[scriptable, builtinclass, uuid(03da5641-a333-454a-a859-036d0bb683b7)] interface imgINotificationObserver : nsISupports { const long SIZE_AVAILABLE = 1; @@ -25,6 +25,7 @@ interface imgINotificationObserver : nsISupports const long DISCARD = 6; const long UNLOCKED_DRAW = 7; const long IS_ANIMATED = 8; + const long HAS_TRANSPARENCY = 9; [noscript] void notify(in imgIRequest aProxy, in long aType, [const] in nsIntRect aRect); }; diff --git a/image/src/Decoder.cpp b/image/src/Decoder.cpp index 8b69fce86b16..b7f2ba6db335 100644 --- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -284,6 +284,12 @@ Decoder::PostSize(int32_t aWidth, mProgress |= FLAG_HAS_SIZE; } +void +Decoder::PostHasTransparency() +{ + mProgress |= FLAG_HAS_TRANSPARENCY; +} + void Decoder::PostFrameStart() { diff --git a/image/src/Decoder.h b/image/src/Decoder.h index 618b757be5bd..9d8fb87098d0 100644 --- a/image/src/Decoder.h +++ b/image/src/Decoder.h @@ -188,6 +188,9 @@ protected: int32_t aHeight, Orientation aOrientation = Orientation()); + // Called by decoders if they determine that the image has transparency. + void PostHasTransparency(); + // Called by decoders when they begin a frame. Informs the image, sends // notifications, and does internal book-keeping. void PostFrameStart(); diff --git a/image/src/ProgressTracker.cpp b/image/src/ProgressTracker.cpp index 9c75adbcbe9d..804147f5a5e3 100644 --- a/image/src/ProgressTracker.cpp +++ b/image/src/ProgressTracker.cpp @@ -79,6 +79,11 @@ CheckProgressConsistency(Progress aProgress) } if (aProgress & FLAG_IS_ANIMATED) { MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED); + MOZ_ASSERT(aProgress & FLAG_HAS_SIZE); + } + if (aProgress & FLAG_HAS_TRANSPARENCY) { + MOZ_ASSERT(aProgress & FLAG_DECODE_STARTED); + MOZ_ASSERT(aProgress & FLAG_HAS_SIZE); } if (aProgress & FLAG_IS_MULTIPART) { // No preconditions. @@ -330,7 +335,9 @@ ProgressTracker::SyncNotifyInternal(ProxyArray& aProxies, if (aProgress & FLAG_FRAME_STOPPED) NOTIFY_IMAGE_OBSERVERS(aProxies, OnStopFrame()); - // OnImageIsAnimated + if (aProgress & FLAG_HAS_TRANSPARENCY) + NOTIFY_IMAGE_OBSERVERS(aProxies, OnImageHasTransparency()); + if (aProgress & FLAG_IS_ANIMATED) NOTIFY_IMAGE_OBSERVERS(aProxies, OnImageIsAnimated()); } diff --git a/image/src/ProgressTracker.h b/image/src/ProgressTracker.h index 5f497e8da787..105d2c21b9ab 100644 --- a/image/src/ProgressTracker.h +++ b/image/src/ProgressTracker.h @@ -36,9 +36,10 @@ enum { FLAG_ONLOAD_BLOCKED = 1u << 6, FLAG_ONLOAD_UNBLOCKED = 1u << 7, FLAG_IS_ANIMATED = 1u << 8, - FLAG_IS_MULTIPART = 1u << 9, - FLAG_MULTIPART_STOPPED = 1u << 10, - FLAG_HAS_ERROR = 1u << 11 // STATUS_ERROR + FLAG_HAS_TRANSPARENCY = 1u << 9, + FLAG_IS_MULTIPART = 1u << 10, + FLAG_MULTIPART_STOPPED = 1u << 11, + FLAG_HAS_ERROR = 1u << 12 // STATUS_ERROR }; typedef uint32_t Progress; diff --git a/image/src/VectorImage.cpp b/image/src/VectorImage.cpp index 2cb898e9c7cc..e693839aa20c 100644 --- a/image/src/VectorImage.cpp +++ b/image/src/VectorImage.cpp @@ -1111,6 +1111,7 @@ VectorImage::OnSVGDocumentLoaded() // Tell *our* observers that we're done loading. if (mProgressTracker) { mProgressTracker->SyncNotifyProgress(FLAG_HAS_SIZE | + FLAG_HAS_TRANSPARENCY | FLAG_FRAME_STOPPED | FLAG_DECODE_STOPPED | FLAG_ONLOAD_UNBLOCKED, diff --git a/image/src/imgRequestProxy.cpp b/image/src/imgRequestProxy.cpp index 6c18f1b5e0f9..303f698c2b7a 100644 --- a/image/src/imgRequestProxy.cpp +++ b/image/src/imgRequestProxy.cpp @@ -816,6 +816,16 @@ void imgRequestProxy::OnUnlockedDraw() } } +void imgRequestProxy::OnImageHasTransparency() +{ + LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageHasTransparency"); + if (mListener && !mCanceled) { + // Hold a ref to the listener while we call it, just in case. + nsCOMPtr kungFuDeathGrip(mListener); + mListener->Notify(this, imgINotificationObserver::HAS_TRANSPARENCY, nullptr); + } +} + void imgRequestProxy::OnImageIsAnimated() { LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageIsAnimated"); diff --git a/image/src/imgRequestProxy.h b/image/src/imgRequestProxy.h index d28215826327..263d2c148c18 100644 --- a/image/src/imgRequestProxy.h +++ b/image/src/imgRequestProxy.h @@ -149,14 +149,15 @@ protected: // class) ProgressTracker is the only class allowed to send us // notifications. - void OnStartDecode (); - void OnStartContainer (); - void OnFrameUpdate (const nsIntRect * aRect); - void OnStopFrame (); - void OnStopDecode (); - void OnDiscard (); - void OnUnlockedDraw (); - void OnImageIsAnimated (); + void OnStartDecode(); + void OnStartContainer(); + void OnFrameUpdate(const nsIntRect* aRect); + void OnStopFrame(); + void OnStopDecode(); + void OnDiscard(); + void OnUnlockedDraw(); + void OnImageHasTransparency(); + void OnImageIsAnimated(); /* non-virtual sort-of-nsIRequestObserver methods */ void OnStartRequest();