/image/src/RasterImage.cpp
http://github.com/zpao/v8monkey · C++ · 3077 lines · 1933 code · 523 blank · 621 comment · 434 complexity · 9c3241205f36981702bbe7c9fb67afd9 MD5 · raw file
Large files are truncated click here to view the full file
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Stuart Parmenter <pavlov@netscape.com>
- * Chris Saari <saari@netscape.com>
- * Asko Tontti <atontti@cc.hut.fi>
- * Arron Mogge <paper@animecity.nu>
- * Andrew Smith
- * Federico Mena-Quintero <federico@novell.com>
- * Bobby Holley <bobbyholley@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
- #include "base/histogram.h"
- #include "nsComponentManagerUtils.h"
- #include "imgIContainerObserver.h"
- #include "ImageErrors.h"
- #include "Decoder.h"
- #include "imgIDecoderObserver.h"
- #include "RasterImage.h"
- #include "nsIInterfaceRequestor.h"
- #include "nsIInterfaceRequestorUtils.h"
- #include "nsAutoPtr.h"
- #include "nsStringStream.h"
- #include "prmem.h"
- #include "prenv.h"
- #include "ImageLogging.h"
- #include "ImageLayers.h"
- #include "nsPNGDecoder.h"
- #include "nsGIFDecoder2.h"
- #include "nsJPEGDecoder.h"
- #include "nsBMPDecoder.h"
- #include "nsICODecoder.h"
- #include "nsIconDecoder.h"
- #include "gfxContext.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/StdInt.h"
- #include "mozilla/Telemetry.h"
- #include "mozilla/TimeStamp.h"
- #include "mozilla/ClearOnShutdown.h"
- using namespace mozilla;
- using namespace mozilla::image;
- using namespace mozilla::layers;
- // a mask for flags that will affect the decoding
- #define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
- #define DECODE_FLAGS_DEFAULT 0
- /* Accounting for compressed data */
- #if defined(PR_LOGGING)
- static PRLogModuleInfo *gCompressedImageAccountingLog = PR_NewLogModule ("CompressedImageAccounting");
- #else
- #define gCompressedImageAccountingLog
- #endif
- // Tweakable progressive decoding parameters. These are initialized to 0 here
- // because otherwise, we have to initialize them in a static initializer, which
- // makes us slower to start up.
- static bool gInitializedPrefCaches = false;
- static PRUint32 gDecodeBytesAtATime = 0;
- static PRUint32 gMaxMSBeforeYield = 0;
- static PRUint32 gMaxBytesForSyncDecode = 0;
- static void
- InitPrefCaches()
- {
- Preferences::AddUintVarCache(&gDecodeBytesAtATime,
- "image.mem.decode_bytes_at_a_time", 200000);
- Preferences::AddUintVarCache(&gMaxMSBeforeYield,
- "image.mem.max_ms_before_yield", 400);
- Preferences::AddUintVarCache(&gMaxBytesForSyncDecode,
- "image.mem.max_bytes_for_sync_decode", 150000);
- gInitializedPrefCaches = true;
- }
- /* We define our own error checking macros here for 2 reasons:
- *
- * 1) Most of the failures we encounter here will (hopefully) be
- * the result of decoding failures (ie, bad data) and not code
- * failures. As such, we don't want to clutter up debug consoles
- * with spurious messages about NS_ENSURE_SUCCESS failures.
- *
- * 2) We want to set the internal error flag, shutdown properly,
- * and end up in an error state.
- *
- * So this macro should be called when the desired failure behavior
- * is to put the container into an error state and return failure.
- * It goes without saying that macro won't compile outside of a
- * non-static RasterImage method.
- */
- #define LOG_CONTAINER_ERROR \
- PR_BEGIN_MACRO \
- PR_LOG (gImgLog, PR_LOG_ERROR, \
- ("RasterImage: [this=%p] Error " \
- "detected at line %u for image of " \
- "type %s\n", this, __LINE__, \
- mSourceDataMimeType.get())); \
- PR_END_MACRO
- #define CONTAINER_ENSURE_SUCCESS(status) \
- PR_BEGIN_MACRO \
- nsresult _status = status; /* eval once */ \
- if (_status) { \
- LOG_CONTAINER_ERROR; \
- DoError(); \
- return _status; \
- } \
- PR_END_MACRO
- #define CONTAINER_ENSURE_TRUE(arg, rv) \
- PR_BEGIN_MACRO \
- if (!(arg)) { \
- LOG_CONTAINER_ERROR; \
- DoError(); \
- return rv; \
- } \
- PR_END_MACRO
- static int num_containers;
- static int num_discardable_containers;
- static PRInt64 total_source_bytes;
- static PRInt64 discardable_source_bytes;
- /* Are we globally disabling image discarding? */
- static bool
- DiscardingEnabled()
- {
- static bool inited;
- static bool enabled;
- if (!inited) {
- inited = true;
- enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nsnull);
- }
- return enabled;
- }
- namespace mozilla {
- namespace image {
- /* static */ nsRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
- #ifndef DEBUG
- NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
- nsISupportsWeakReference)
- #else
- NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsIProperties,
- imgIContainerDebug, nsISupportsWeakReference)
- #endif
- //******************************************************************************
- RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
- Image(aStatusTracker), // invoke superclass's constructor
- mSize(0,0),
- mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
- mAnim(nsnull),
- mLoopCount(-1),
- mObserver(nsnull),
- mLockCount(0),
- mDecoder(nsnull),
- mDecodeRequest(this),
- mBytesDecoded(0),
- mDecodeCount(0),
- #ifdef DEBUG
- mFramesNotified(0),
- #endif
- mHasSize(false),
- mDecodeOnDraw(false),
- mMultipart(false),
- mDiscardable(false),
- mHasSourceData(false),
- mDecoded(false),
- mHasBeenDecoded(false),
- mInDecoder(false),
- mAnimationFinished(false)
- {
- // Set up the discard tracker node.
- mDiscardTrackerNode.curr = this;
- mDiscardTrackerNode.prev = mDiscardTrackerNode.next = nsnull;
- Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
- // Statistics
- num_containers++;
- // Register our pref observers if we haven't yet.
- if (NS_UNLIKELY(!gInitializedPrefCaches)) {
- InitPrefCaches();
- }
- }
- //******************************************************************************
- RasterImage::~RasterImage()
- {
- delete mAnim;
- for (unsigned int i = 0; i < mFrames.Length(); ++i)
- delete mFrames[i];
- // Discardable statistics
- if (mDiscardable) {
- num_discardable_containers--;
- discardable_source_bytes -= mSourceData.Length();
- PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
- ("CompressedImageAccounting: destroying RasterImage %p. "
- "Total Containers: %d, Discardable containers: %d, "
- "Total source bytes: %lld, Source bytes for discardable containers %lld",
- this,
- num_containers,
- num_discardable_containers,
- total_source_bytes,
- discardable_source_bytes));
- }
- DiscardTracker::Remove(&mDiscardTrackerNode);
- // If we have a decoder open, shut it down
- if (mDecoder) {
- nsresult rv = ShutdownDecoder(eShutdownIntent_Interrupted);
- if (NS_FAILED(rv))
- NS_WARNING("Failed to shut down decoder in destructor!");
- }
- // Total statistics
- num_containers--;
- total_source_bytes -= mSourceData.Length();
- }
- nsresult
- RasterImage::Init(imgIDecoderObserver *aObserver,
- const char* aMimeType,
- const char* aURIString,
- PRUint32 aFlags)
- {
- // We don't support re-initialization
- if (mInitialized)
- return NS_ERROR_ILLEGAL_VALUE;
- // Not sure an error can happen before init, but be safe
- if (mError)
- return NS_ERROR_FAILURE;
- NS_ENSURE_ARG_POINTER(aMimeType);
- // We must be non-discardable and non-decode-on-draw for
- // multipart channels
- NS_ABORT_IF_FALSE(!(aFlags & INIT_FLAG_MULTIPART) ||
- (!(aFlags & INIT_FLAG_DISCARDABLE) &&
- !(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
- "Can't be discardable or decode-on-draw for multipart");
- // Store initialization data
- mObserver = do_GetWeakReference(aObserver);
- mSourceDataMimeType.Assign(aMimeType);
- mURIString.Assign(aURIString);
- mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
- mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
- mMultipart = !!(aFlags & INIT_FLAG_MULTIPART);
- // Statistics
- if (mDiscardable) {
- num_discardable_containers++;
- discardable_source_bytes += mSourceData.Length();
- }
- // If we're being called from ExtractFrame (used by borderimage),
- // we don't actually do any decoding. Bail early.
- // XXX - This should be removed when we fix borderimage
- if (mSourceDataMimeType.Length() == 0) {
- mInitialized = true;
- return NS_OK;
- }
- // Instantiate the decoder
- //
- // If we're doing decode-on-draw, we want to do a quick first pass to get
- // the size but nothing else. We instantiate another decoder later to do
- // the full decoding.
- nsresult rv = InitDecoder(/* aDoSizeDecode = */ mDecodeOnDraw);
- CONTAINER_ENSURE_SUCCESS(rv);
- // Mark us as initialized
- mInitialized = true;
- return NS_OK;
- }
- bool
- RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
- {
- NS_ASSERTION(aTime <= TimeStamp::Now(),
- "Given time appears to be in the future");
- imgFrame* nextFrame = nsnull;
- PRUint32 currentFrameIndex = mAnim->currentAnimationFrameIndex;
- PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
- PRUint32 timeout = 0;
- mImageContainer = nsnull;
- // Figure out if we have the next full frame. This is more complicated than
- // just checking for mFrames.Length() because decoders append their frames
- // before they're filled in.
- NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= mFrames.Length(),
- "How did we get 2 indices too far by incrementing?");
- // If we don't have a decoder, we know we've got everything we're going to
- // get. If we do, we only display fully-downloaded frames; everything else
- // gets delayed.
- bool haveFullNextFrame = !mDecoder ||
- nextFrameIndex < mDecoder->GetCompleteFrameCount();
- // If we're done decoding the next frame, go ahead and display it now and
- // reinit with the next frame's delay time.
- if (haveFullNextFrame) {
- if (mFrames.Length() == nextFrameIndex) {
- // End of Animation, unless we are looping forever
- // If animation mode is "loop once", it's time to stop animating
- if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
- mAnimationFinished = true;
- EvaluateAnimation();
- }
- // We may have used compositingFrame to build a frame, and then copied
- // it back into mFrames[..]. If so, delete composite to save memory
- if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1) {
- mAnim->compositingFrame = nsnull;
- }
- nextFrameIndex = 0;
- if (mLoopCount > 0) {
- mLoopCount--;
- }
- if (!mAnimating) {
- // break out early if we are actually done animating
- return false;
- }
- }
- if (!(nextFrame = mFrames[nextFrameIndex])) {
- // something wrong with the next frame, skip it
- mAnim->currentAnimationFrameIndex = nextFrameIndex;
- return false;
- }
- timeout = nextFrame->GetTimeout();
- } else {
- // Uh oh, the frame we want to show is currently being decoded (partial)
- // Wait until the next refresh driver tick and try again
- return false;
- }
- if (!(timeout > 0)) {
- mAnimationFinished = true;
- EvaluateAnimation();
- }
- imgFrame *frameToUse = nsnull;
- if (nextFrameIndex == 0) {
- frameToUse = nextFrame;
- *aDirtyRect = mAnim->firstFrameRefreshArea;
- } else {
- imgFrame *curFrame = mFrames[currentFrameIndex];
- if (!curFrame) {
- return false;
- }
- // Change frame
- if (NS_FAILED(DoComposite(aDirtyRect, curFrame,
- nextFrame, nextFrameIndex))) {
- // something went wrong, move on to next
- NS_WARNING("RasterImage::AdvanceFrame(): Compositing of frame failed");
- nextFrame->SetCompositingFailed(true);
- mAnim->currentAnimationFrameIndex = nextFrameIndex;
- mAnim->currentAnimationFrameTime = aTime;
- return false;
- }
- nextFrame->SetCompositingFailed(false);
- }
- // Set currentAnimationFrameIndex at the last possible moment
- mAnim->currentAnimationFrameIndex = nextFrameIndex;
- mAnim->currentAnimationFrameTime = aTime;
- return true;
- }
- //******************************************************************************
- // [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
- NS_IMETHODIMP_(void)
- RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
- {
- if (!mAnimating || !ShouldAnimate()) {
- return;
- }
- EnsureAnimExists();
- // only advance the frame if the current time is greater than or
- // equal to the current frame's end time.
- TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
- bool frameAdvanced = false;
- // The dirtyRect variable will contain an accumulation of the sub-rectangles
- // that are dirty for each frame we advance in AdvanceFrame().
- nsIntRect dirtyRect;
- while (currentFrameEndTime <= aTime) {
- TimeStamp oldFrameEndTime = currentFrameEndTime;
- nsIntRect frameDirtyRect;
- bool didAdvance = AdvanceFrame(aTime, &frameDirtyRect);
- frameAdvanced = frameAdvanced || didAdvance;
- currentFrameEndTime = GetCurrentImgFrameEndTime();
- // Accumulate the dirty area.
- dirtyRect = dirtyRect.Union(frameDirtyRect);
- // if we didn't advance a frame, and our frame end time didn't change,
- // then we need to break out of this loop & wait for the frame(s)
- // to finish downloading
- if (!frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
- break;
- }
- }
- if (frameAdvanced) {
- nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
- if (!observer) {
- NS_ERROR("Refreshing image after its imgRequest is gone");
- StopAnimation();
- return;
- }
- // Notify listeners that our frame has actually changed, but do this only
- // once for all frames that we've now passed (if AdvanceFrame() was called
- // more than once).
- #ifdef DEBUG
- mFramesNotified++;
- #endif
- observer->FrameChanged(nsnull, this, &dirtyRect);
- }
- }
- //******************************************************************************
- /* [noscript] imgIContainer extractFrame(PRUint32 aWhichFrame,
- * [const] in nsIntRect aRegion,
- * in PRUint32 aFlags); */
- NS_IMETHODIMP
- RasterImage::ExtractFrame(PRUint32 aWhichFrame,
- const nsIntRect &aRegion,
- PRUint32 aFlags,
- imgIContainer **_retval)
- {
- NS_ENSURE_ARG_POINTER(_retval);
- nsresult rv;
- if (aWhichFrame > FRAME_MAX_VALUE)
- return NS_ERROR_INVALID_ARG;
- if (mError)
- return NS_ERROR_FAILURE;
- // Disallowed in the API
- if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
- return NS_ERROR_FAILURE;
- // Make a new container. This should switch to another class with bug 505959.
- nsRefPtr<RasterImage> img(new RasterImage());
- // We don't actually have a mimetype in this case. The empty string tells the
- // init routine not to try to instantiate a decoder. This should be fixed in
- // bug 505959.
- img->Init(nsnull, "", "", INIT_FLAG_NONE);
- img->SetSize(aRegion.width, aRegion.height);
- img->mDecoded = true; // Also, we need to mark the image as decoded
- img->mHasBeenDecoded = true;
- img->mFrameDecodeFlags = aFlags & DECODE_FLAGS_MASK;
- if (img->mFrameDecodeFlags != mFrameDecodeFlags) {
- // if we can't discard, then we're screwed; we have no way
- // to re-decode. Similarly if we aren't allowed to do a sync
- // decode.
- if (!(aFlags & FLAG_SYNC_DECODE))
- return NS_ERROR_NOT_AVAILABLE;
- if (!CanForciblyDiscard() || mDecoder || mAnim)
- return NS_ERROR_NOT_AVAILABLE;
- ForceDiscard();
- mFrameDecodeFlags = img->mFrameDecodeFlags;
- }
-
- // If a synchronous decode was requested, do it
- if (aFlags & FLAG_SYNC_DECODE) {
- rv = SyncDecode();
- CONTAINER_ENSURE_SUCCESS(rv);
- }
- // Get the frame. If it's not there, it's probably the caller's fault for
- // not waiting for the data to be loaded from the network or not passing
- // FLAG_SYNC_DECODE
- PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
- 0 : GetCurrentImgFrameIndex();
- imgFrame *frame = GetDrawableImgFrame(frameIndex);
- if (!frame) {
- *_retval = nsnull;
- return NS_ERROR_FAILURE;
- }
- // The frame can be smaller than the image. We want to extract only the part
- // of the frame that actually exists.
- nsIntRect framerect = frame->GetRect();
- framerect.IntersectRect(framerect, aRegion);
- if (framerect.IsEmpty())
- return NS_ERROR_NOT_AVAILABLE;
- nsAutoPtr<imgFrame> subframe;
- rv = frame->Extract(framerect, getter_Transfers(subframe));
- if (NS_FAILED(rv))
- return rv;
- img->mFrames.AppendElement(subframe.forget());
- img->mStatusTracker->RecordLoaded();
- img->mStatusTracker->RecordDecoded();
- *_retval = img.forget().get();
- return NS_OK;
- }
- //******************************************************************************
- /* readonly attribute PRInt32 width; */
- NS_IMETHODIMP
- RasterImage::GetWidth(PRInt32 *aWidth)
- {
- NS_ENSURE_ARG_POINTER(aWidth);
- if (mError) {
- *aWidth = 0;
- return NS_ERROR_FAILURE;
- }
- *aWidth = mSize.width;
- return NS_OK;
- }
- //******************************************************************************
- /* readonly attribute PRInt32 height; */
- NS_IMETHODIMP
- RasterImage::GetHeight(PRInt32 *aHeight)
- {
- NS_ENSURE_ARG_POINTER(aHeight);
- if (mError) {
- *aHeight = 0;
- return NS_ERROR_FAILURE;
- }
- *aHeight = mSize.height;
- return NS_OK;
- }
- //******************************************************************************
- /* unsigned short GetType(); */
- NS_IMETHODIMP
- RasterImage::GetType(PRUint16 *aType)
- {
- NS_ENSURE_ARG_POINTER(aType);
- *aType = GetType();
- return NS_OK;
- }
- //******************************************************************************
- /* [noscript, notxpcom] PRUint16 GetType(); */
- NS_IMETHODIMP_(PRUint16)
- RasterImage::GetType()
- {
- return imgIContainer::TYPE_RASTER;
- }
- imgFrame*
- RasterImage::GetImgFrameNoDecode(PRUint32 framenum)
- {
- if (!mAnim) {
- NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
- return mFrames.SafeElementAt(0, nsnull);
- }
- if (mAnim->lastCompositedFrameIndex == PRInt32(framenum))
- return mAnim->compositingFrame;
- return mFrames.SafeElementAt(framenum, nsnull);
- }
- imgFrame*
- RasterImage::GetImgFrame(PRUint32 framenum)
- {
- nsresult rv = WantDecodedFrames();
- CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nsnull);
- return GetImgFrameNoDecode(framenum);
- }
- imgFrame*
- RasterImage::GetDrawableImgFrame(PRUint32 framenum)
- {
- imgFrame *frame = GetImgFrame(framenum);
- // We will return a paletted frame if it's not marked as compositing failed
- // so we can catch crashes for reasons we haven't investigated.
- if (frame && frame->GetCompositingFailed())
- return nsnull;
- return frame;
- }
- PRUint32
- RasterImage::GetCurrentImgFrameIndex() const
- {
- if (mAnim)
- return mAnim->currentAnimationFrameIndex;
- return 0;
- }
- TimeStamp
- RasterImage::GetCurrentImgFrameEndTime() const
- {
- imgFrame* currentFrame = mFrames[mAnim->currentAnimationFrameIndex];
- TimeStamp currentFrameTime = mAnim->currentAnimationFrameTime;
- PRInt64 timeout = currentFrame->GetTimeout();
- if (timeout < 0) {
- // We need to return a sentinel value in this case, because our logic
- // doesn't work correctly if we have a negative timeout value. The reason
- // this positive infinity was chosen was because it works with the loop in
- // RequestRefresh() above.
- return TimeStamp() + TimeDuration::FromMilliseconds(UINT64_MAX);
- }
- TimeDuration durationOfTimeout = TimeDuration::FromMilliseconds(timeout);
- TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
- return currentFrameEndTime;
- }
- imgFrame*
- RasterImage::GetCurrentImgFrame()
- {
- return GetImgFrame(GetCurrentImgFrameIndex());
- }
- imgFrame*
- RasterImage::GetCurrentDrawableImgFrame()
- {
- return GetDrawableImgFrame(GetCurrentImgFrameIndex());
- }
- //******************************************************************************
- /* readonly attribute boolean currentFrameIsOpaque; */
- NS_IMETHODIMP
- RasterImage::GetCurrentFrameIsOpaque(bool *aIsOpaque)
- {
- NS_ENSURE_ARG_POINTER(aIsOpaque);
- if (mError)
- return NS_ERROR_FAILURE;
- // See if we can get an image frame
- imgFrame *curframe = GetCurrentImgFrame();
- // If we don't get a frame, the safe answer is "not opaque"
- if (!curframe)
- *aIsOpaque = false;
- // Otherwise, we can make a more intelligent decision
- else {
- *aIsOpaque = !curframe->GetNeedsBackground();
- // We are also transparent if the current frame's size doesn't cover our
- // entire area.
- nsIntRect framerect = curframe->GetRect();
- *aIsOpaque = *aIsOpaque && framerect.IsEqualInterior(nsIntRect(0, 0, mSize.width, mSize.height));
- }
- return NS_OK;
- }
- void
- RasterImage::GetCurrentFrameRect(nsIntRect& aRect)
- {
- // Get the current frame
- imgFrame* curframe = GetCurrentImgFrame();
- // If we have the frame, use that rectangle
- if (curframe) {
- aRect = curframe->GetRect();
- } else {
- // If the frame doesn't exist, we pass the empty rectangle. It's not clear
- // whether this is appropriate in general, but at the moment the only
- // consumer of this method is imgStatusTracker (when it wants to figure out
- // dirty rectangles to send out batched observer updates). This should
- // probably be revisited when we fix bug 503973.
- aRect.MoveTo(0, 0);
- aRect.SizeTo(0, 0);
- }
- }
- PRUint32
- RasterImage::GetCurrentFrameIndex()
- {
- return GetCurrentImgFrameIndex();
- }
- PRUint32
- RasterImage::GetNumFrames()
- {
- return mFrames.Length();
- }
- //******************************************************************************
- /* readonly attribute boolean animated; */
- NS_IMETHODIMP
- RasterImage::GetAnimated(bool *aAnimated)
- {
- if (mError)
- return NS_ERROR_FAILURE;
- NS_ENSURE_ARG_POINTER(aAnimated);
- // If we have mAnim, we can know for sure
- if (mAnim) {
- *aAnimated = true;
- return NS_OK;
- }
- // Otherwise, we need to have been decoded to know for sure, since if we were
- // decoded at least once mAnim would have been created for animated images
- if (!mHasBeenDecoded)
- return NS_ERROR_NOT_AVAILABLE;
- // We know for sure
- *aAnimated = false;
- return NS_OK;
- }
- //******************************************************************************
- /* [noscript] gfxImageSurface copyFrame(in PRUint32 aWhichFrame,
- * in PRUint32 aFlags); */
- NS_IMETHODIMP
- RasterImage::CopyFrame(PRUint32 aWhichFrame,
- PRUint32 aFlags,
- gfxImageSurface **_retval)
- {
- if (aWhichFrame > FRAME_MAX_VALUE)
- return NS_ERROR_INVALID_ARG;
- if (mError)
- return NS_ERROR_FAILURE;
- // Disallowed in the API
- if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
- return NS_ERROR_FAILURE;
- nsresult rv;
- PRUint32 desiredDecodeFlags = aFlags & DECODE_FLAGS_MASK;
- if (desiredDecodeFlags != mFrameDecodeFlags) {
- // if we can't discard, then we're screwed; we have no way
- // to re-decode. Similarly if we aren't allowed to do a sync
- // decode.
- if (!(aFlags & FLAG_SYNC_DECODE))
- return NS_ERROR_NOT_AVAILABLE;
- if (!CanForciblyDiscard() || mDecoder || mAnim)
- return NS_ERROR_NOT_AVAILABLE;
- ForceDiscard();
- mFrameDecodeFlags = desiredDecodeFlags;
- }
- // If requested, synchronously flush any data we have lying around to the decoder
- if (aFlags & FLAG_SYNC_DECODE) {
- rv = SyncDecode();
- CONTAINER_ENSURE_SUCCESS(rv);
- }
- NS_ENSURE_ARG_POINTER(_retval);
- // Get the frame. If it's not there, it's probably the caller's fault for
- // not waiting for the data to be loaded from the network or not passing
- // FLAG_SYNC_DECODE
- PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
- 0 : GetCurrentImgFrameIndex();
- imgFrame *frame = GetDrawableImgFrame(frameIndex);
- if (!frame) {
- *_retval = nsnull;
- return NS_ERROR_FAILURE;
- }
- nsRefPtr<gfxPattern> pattern;
- frame->GetPattern(getter_AddRefs(pattern));
- nsIntRect intframerect = frame->GetRect();
- gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height);
- // Create a 32-bit image surface of our size, but draw using the frame's
- // rect, implicitly padding the frame out to the image's size.
- nsRefPtr<gfxImageSurface> imgsurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
- gfxASurface::ImageFormatARGB32);
- gfxContext ctx(imgsurface);
- ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
- ctx.SetPattern(pattern);
- ctx.Rectangle(framerect);
- ctx.Fill();
- *_retval = imgsurface.forget().get();
- return NS_OK;
- }
- //******************************************************************************
- /* [noscript] gfxASurface getFrame(in PRUint32 aWhichFrame,
- * in PRUint32 aFlags); */
- NS_IMETHODIMP
- RasterImage::GetFrame(PRUint32 aWhichFrame,
- PRUint32 aFlags,
- gfxASurface **_retval)
- {
- if (aWhichFrame > FRAME_MAX_VALUE)
- return NS_ERROR_INVALID_ARG;
- if (mError)
- return NS_ERROR_FAILURE;
- // Disallowed in the API
- if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
- return NS_ERROR_FAILURE;
- nsresult rv = NS_OK;
- if (mDecoded) {
- // If we have decoded data, and it is not a perfect match for what we are
- // looking for, we must discard to be able to generate the proper data.
- PRUint32 desiredDecodeFlags = aFlags & DECODE_FLAGS_MASK;
- if (desiredDecodeFlags != mFrameDecodeFlags) {
- // if we can't discard, then we're screwed; we have no way
- // to re-decode. Similarly if we aren't allowed to do a sync
- // decode.
- if (!(aFlags & FLAG_SYNC_DECODE))
- return NS_ERROR_NOT_AVAILABLE;
- if (!CanForciblyDiscard() || mDecoder || mAnim)
- return NS_ERROR_NOT_AVAILABLE;
-
- ForceDiscard();
-
- mFrameDecodeFlags = desiredDecodeFlags;
- }
- }
- // If the caller requested a synchronous decode, do it
- if (aFlags & FLAG_SYNC_DECODE) {
- rv = SyncDecode();
- CONTAINER_ENSURE_SUCCESS(rv);
- }
- // Get the frame. If it's not there, it's probably the caller's fault for
- // not waiting for the data to be loaded from the network or not passing
- // FLAG_SYNC_DECODE
- PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
- 0 : GetCurrentImgFrameIndex();
- imgFrame *frame = GetDrawableImgFrame(frameIndex);
- if (!frame) {
- *_retval = nsnull;
- return NS_ERROR_FAILURE;
- }
- nsRefPtr<gfxASurface> framesurf;
- // If this frame covers the entire image, we can just reuse its existing
- // surface.
- nsIntRect framerect = frame->GetRect();
- if (framerect.x == 0 && framerect.y == 0 &&
- framerect.width == mSize.width &&
- framerect.height == mSize.height)
- rv = frame->GetSurface(getter_AddRefs(framesurf));
- // The image doesn't have a surface because it's been optimized away. Create
- // one.
- if (!framesurf) {
- nsRefPtr<gfxImageSurface> imgsurf;
- rv = CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
- framesurf = imgsurf;
- }
- *_retval = framesurf.forget().get();
- return rv;
- }
- NS_IMETHODIMP
- RasterImage::GetImageContainer(ImageContainer **_retval)
- {
- if (mImageContainer) {
- *_retval = mImageContainer;
- NS_ADDREF(*_retval);
- return NS_OK;
- }
-
- CairoImage::Data cairoData;
- nsRefPtr<gfxASurface> imageSurface;
- nsresult rv = GetFrame(FRAME_CURRENT, FLAG_SYNC_DECODE, getter_AddRefs(imageSurface));
- NS_ENSURE_SUCCESS(rv, rv);
- cairoData.mSurface = imageSurface;
- GetWidth(&cairoData.mSize.width);
- GetHeight(&cairoData.mSize.height);
- mImageContainer = LayerManager::CreateImageContainer();
-
- // Now create a CairoImage to display the surface.
- layers::Image::Format cairoFormat = layers::Image::CAIRO_SURFACE;
- nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&cairoFormat, 1);
- NS_ASSERTION(image, "Failed to create Image");
- NS_ASSERTION(image->GetFormat() == cairoFormat, "Wrong format");
- static_cast<CairoImage*>(image.get())->SetData(cairoData);
- mImageContainer->SetCurrentImage(image);
- *_retval = mImageContainer;
- NS_ADDREF(*_retval);
- return NS_OK;
- }
- namespace {
- PRUint32
- GetDecodedSize(const nsTArray<imgFrame *> &aFrames,
- gfxASurface::MemoryLocation aLocation)
- {
- PRUint32 val = 0;
- for (PRUint32 i = 0; i < aFrames.Length(); ++i) {
- imgFrame *frame = aFrames.SafeElementAt(i, nsnull);
- NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
- val += frame->EstimateMemoryUsed(aLocation);
- }
- return val;
- }
- } // anonymous namespace
- PRUint32
- RasterImage::GetDecodedHeapSize()
- {
- return GetDecodedSize(mFrames, gfxASurface::MEMORY_IN_PROCESS_HEAP);
- }
- PRUint32
- RasterImage::GetDecodedNonheapSize()
- {
- return GetDecodedSize(mFrames, gfxASurface::MEMORY_IN_PROCESS_NONHEAP);
- }
- PRUint32
- RasterImage::GetDecodedOutOfProcessSize()
- {
- return GetDecodedSize(mFrames, gfxASurface::MEMORY_OUT_OF_PROCESS);
- }
- PRUint32
- RasterImage::GetSourceHeapSize()
- {
- PRUint32 sourceDataSize = mSourceData.Length();
-
- NS_ABORT_IF_FALSE(StoringSourceData() || (sourceDataSize == 0),
- "Non-zero source data size when we aren't storing it?");
- return sourceDataSize;
- }
- void
- RasterImage::DeleteImgFrame(PRUint32 framenum)
- {
- NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Deleting invalid frame!");
- delete mFrames[framenum];
- mFrames[framenum] = nsnull;
- }
- nsresult
- RasterImage::InternalAddFrameHelper(PRUint32 framenum, imgFrame *aFrame,
- PRUint8 **imageData, PRUint32 *imageLength,
- PRUint32 **paletteData, PRUint32 *paletteLength)
- {
- NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
- if (framenum > mFrames.Length())
- return NS_ERROR_INVALID_ARG;
- nsAutoPtr<imgFrame> frame(aFrame);
- if (paletteData && paletteLength)
- frame->GetPaletteData(paletteData, paletteLength);
- frame->GetImageData(imageData, imageLength);
- // We are in the middle of decoding. This will be unlocked when we finish the
- // decoder->Write() call.
- frame->LockImageData();
- mFrames.InsertElementAt(framenum, frame.forget());
- return NS_OK;
- }
-
- nsresult
- RasterImage::InternalAddFrame(PRUint32 framenum,
- PRInt32 aX, PRInt32 aY,
- PRInt32 aWidth, PRInt32 aHeight,
- gfxASurface::gfxImageFormat aFormat,
- PRUint8 aPaletteDepth,
- PRUint8 **imageData,
- PRUint32 *imageLength,
- PRUint32 **paletteData,
- PRUint32 *paletteLength)
- {
- // We assume that we're in the middle of decoding because we unlock the
- // previous frame when we create a new frame, and only when decoding do we
- // lock frames.
- NS_ABORT_IF_FALSE(mInDecoder, "Only decoders may add frames!");
- NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
- if (framenum > mFrames.Length())
- return NS_ERROR_INVALID_ARG;
- nsAutoPtr<imgFrame> frame(new imgFrame());
- nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
- NS_ENSURE_SUCCESS(rv, rv);
- // We know we are in a decoder. Therefore, we must unlock the previous frame
- // when we move on to decoding into the next frame.
- if (mFrames.Length() > 0) {
- imgFrame *prevframe = mFrames.ElementAt(mFrames.Length() - 1);
- prevframe->UnlockImageData();
- }
- if (mFrames.Length() == 0) {
- return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
- paletteData, paletteLength);
- }
- if (mFrames.Length() == 1) {
- // Since we're about to add our second frame, initialize animation stuff
- EnsureAnimExists();
-
- // If we dispose of the first frame by clearing it, then the
- // First Frame's refresh area is all of itself.
- // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
- PRInt32 frameDisposalMethod = mFrames[0]->GetFrameDisposalMethod();
- if (frameDisposalMethod == kDisposeClear ||
- frameDisposalMethod == kDisposeRestorePrevious)
- mAnim->firstFrameRefreshArea = mFrames[0]->GetRect();
- }
- // Calculate firstFrameRefreshArea
- // Some gifs are huge but only have a small area that they animate
- // We only need to refresh that small area when Frame 0 comes around again
- nsIntRect frameRect = frame->GetRect();
- mAnim->firstFrameRefreshArea.UnionRect(mAnim->firstFrameRefreshArea,
- frameRect);
-
- rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
- paletteData, paletteLength);
-
- // We may be able to start animating, if we now have enough frames
- EvaluateAnimation();
-
- return rv;
- }
- nsresult
- RasterImage::SetSize(PRInt32 aWidth, PRInt32 aHeight)
- {
- if (mError)
- return NS_ERROR_FAILURE;
- // Ensure that we have positive values
- // XXX - Why isn't the size unsigned? Should this be changed?
- if ((aWidth < 0) || (aHeight < 0))
- return NS_ERROR_INVALID_ARG;
- // if we already have a size, check the new size against the old one
- if (mHasSize &&
- ((aWidth != mSize.width) || (aHeight != mSize.height))) {
- // Alter the warning depending on whether the channel is multipart
- if (!mMultipart)
- NS_WARNING("Image changed size on redecode! This should not happen!");
- else
- NS_WARNING("Multipart channel sent an image of a different size");
- // Make the decoder aware of the error so that it doesn't try to call
- // FinishInternal during ShutdownDecoder.
- if (mDecoder)
- mDecoder->PostResizeError();
- DoError();
- return NS_ERROR_UNEXPECTED;
- }
- // Set the size and flag that we have it
- mSize.SizeTo(aWidth, aHeight);
- mHasSize = true;
- return NS_OK;
- }
- nsresult
- RasterImage::EnsureFrame(PRUint32 aFrameNum, PRInt32 aX, PRInt32 aY,
- PRInt32 aWidth, PRInt32 aHeight,
- gfxASurface::gfxImageFormat aFormat,
- PRUint8 aPaletteDepth,
- PRUint8 **imageData, PRUint32 *imageLength,
- PRUint32 **paletteData, PRUint32 *paletteLength)
- {
- if (mError)
- return NS_ERROR_FAILURE;
- NS_ENSURE_ARG_POINTER(imageData);
- NS_ENSURE_ARG_POINTER(imageLength);
- NS_ABORT_IF_FALSE(aFrameNum <= mFrames.Length(), "Invalid frame index!");
- if (aPaletteDepth > 0) {
- NS_ENSURE_ARG_POINTER(paletteData);
- NS_ENSURE_ARG_POINTER(paletteLength);
- }
- if (aFrameNum > mFrames.Length())
- return NS_ERROR_INVALID_ARG;
- // Adding a frame that doesn't already exist.
- if (aFrameNum == mFrames.Length())
- return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
- aPaletteDepth, imageData, imageLength,
- paletteData, paletteLength);
- imgFrame *frame = GetImgFrame(aFrameNum);
- if (!frame)
- return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
- aPaletteDepth, imageData, imageLength,
- paletteData, paletteLength);
- // See if we can re-use the frame that already exists.
- nsIntRect rect = frame->GetRect();
- if (rect.x == aX && rect.y == aY && rect.width == aWidth &&
- rect.height == aHeight && frame->GetFormat() == aFormat &&
- frame->GetPaletteDepth() == aPaletteDepth) {
- frame->GetImageData(imageData, imageLength);
- if (paletteData) {
- frame->GetPaletteData(paletteData, paletteLength);
- }
- // We can re-use the frame if it has image data.
- if (*imageData && paletteData && *paletteData) {
- return NS_OK;
- }
- if (*imageData && !paletteData) {
- return NS_OK;
- }
- }
- DeleteImgFrame(aFrameNum);
- return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
- aPaletteDepth, imageData, imageLength,
- paletteData, paletteLength);
- }
- nsresult
- RasterImage::EnsureFrame(PRUint32 aFramenum, PRInt32 aX, PRInt32 aY,
- PRInt32 aWidth, PRInt32 aHeight,
- gfxASurface::gfxImageFormat aFormat,
- PRUint8** imageData, PRUint32* imageLength)
- {
- return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
- /* aPaletteDepth = */ 0, imageData, imageLength,
- /* aPaletteData = */ nsnull,
- /* aPaletteLength = */ nsnull);
- }
- void
- RasterImage::FrameUpdated(PRUint32 aFrameNum, nsIntRect &aUpdatedRect)
- {
- NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
- imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
- NS_ABORT_IF_FALSE(frame, "Calling FrameUpdated on frame that doesn't exist!");
- frame->ImageUpdated(aUpdatedRect);
- // The image has changed, so we need to invalidate our cached ImageContainer.
- mImageContainer = NULL;
- }
- nsresult
- RasterImage::SetFrameDisposalMethod(PRUint32 aFrameNum,
- PRInt32 aDisposalMethod)
- {
- if (mError)
- return NS_ERROR_FAILURE;
- NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
- if (aFrameNum >= mFrames.Length())
- return NS_ERROR_INVALID_ARG;
- imgFrame *frame = GetImgFrame(aFrameNum);
- NS_ABORT_IF_FALSE(frame,
- "Calling SetFrameDisposalMethod on frame that doesn't exist!");
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
- frame->SetFrameDisposalMethod(aDisposalMethod);
- return NS_OK;
- }
- nsresult
- RasterImage::SetFrameTimeout(PRUint32 aFrameNum, PRInt32 aTimeout)
- {
- if (mError)
- return NS_ERROR_FAILURE;
- NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
- if (aFrameNum >= mFrames.Length())
- return NS_ERROR_INVALID_ARG;
- imgFrame *frame = GetImgFrame(aFrameNum);
- NS_ABORT_IF_FALSE(frame, "Calling SetFrameTimeout on frame that doesn't exist!");
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
- frame->SetTimeout(aTimeout);
- return NS_OK;
- }
- nsresult
- RasterImage::SetFrameBlendMethod(PRUint32 aFrameNum, PRInt32 aBlendMethod)
- {
- if (mError)
- return NS_ERROR_FAILURE;
- NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
- if (aFrameNum >= mFrames.Length())
- return NS_ERROR_INVALID_ARG;
- imgFrame *frame = GetImgFrame(aFrameNum);
- NS_ABORT_IF_FALSE(frame, "Calling SetFrameBlendMethod on frame that doesn't exist!");
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
- frame->SetBlendMethod(aBlendMethod);
- return NS_OK;
- }
- nsresult
- RasterImage::SetFrameHasNoAlpha(PRUint32 aFrameNum)
- {
- if (mError)
- return NS_ERROR_FAILURE;
- NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
- if (aFrameNum >= mFrames.Length())
- return NS_ERROR_INVALID_ARG;
- imgFrame *frame = GetImgFrame(aFrameNum);
- NS_ABORT_IF_FALSE(frame, "Calling SetFrameHasNoAlpha on frame that doesn't exist!");
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
- frame->SetHasNoAlpha();
- return NS_OK;
- }
- nsresult
- RasterImage::DecodingComplete()
- {
- if (mError)
- return NS_ERROR_FAILURE;
- // Flag that we're done decoding.
- // XXX - these should probably be combined when we fix animated image
- // discarding with bug 500402.
- mDecoded = true;
- mHasBeenDecoded = true;
- nsresult rv;
- // We now have one of the qualifications for discarding. Re-evaluate.
- if (CanDiscard()) {
- NS_ABORT_IF_FALSE(!DiscardingActive(),
- "We shouldn't have been discardable before this");
- rv = DiscardTracker::Reset(&mDiscardTrackerNode);
- CONTAINER_ENSURE_SUCCESS(rv);
- }
- // If there's only 1 frame, optimize it. Optimizing animated images
- // is not supported.
- //
- // We don't optimize the frame for multipart images because we reuse
- // the frame.
- if ((mFrames.Length() == 1) && !mMultipart) {
- rv = mFrames[0]->Optimize();
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- //******************************************************************************
- /* void StartAnimation () */
- nsresult
- RasterImage::StartAnimation()
- {
- if (mError)
- return NS_ERROR_FAILURE;
- NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
- EnsureAnimExists();
- imgFrame* currentFrame = GetCurrentImgFrame();
- if (currentFrame) {
- if (currentFrame->GetTimeout() < 0) { // -1 means display this frame forever
- mAnimationFinished = true;
- return NS_ERROR_ABORT;
- }
- // We need to set the time that this initial frame was first displayed, as
- // this is used in AdvanceFrame().
- mAnim->currentAnimationFrameTime = TimeStamp::Now();
- }
-
- return NS_OK;
- }
- //******************************************************************************
- /* void stopAnimation (); */
- nsresult
- RasterImage::StopAnimation()
- {
- NS_ABORT_IF_FALSE(mAnimating, "Should be animating!");
- if (mError)
- return NS_ERROR_FAILURE;
- return NS_OK;
- }
- //******************************************************************************
- /* void resetAnimation (); */
- NS_IMETHODIMP
- RasterImage::ResetAnimation()
- {
- if (mError)
- return NS_ERROR_FAILURE;
- if (mAnimationMode == kDontAnimMode ||
- !mAnim || mAnim->currentAnimationFrameIndex == 0)
- return NS_OK;
- mAnimationFinished = false;
- if (mAnimating)
- StopAnimation();
- mAnim->lastCompositedFrameIndex = -1;
- mAnim->currentAnimationFrameIndex = 0;
- mImageContainer = nsnull;
- // Note - We probably want to kick off a redecode somewhere around here when
- // we fix bug 500402.
- // Update display if we were animating before
- nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
- if (mAnimating && observer)
- observer->FrameChanged(nsnull, this, &(mAnim->firstFrameRefreshArea));
- if (ShouldAnimate()) {
- StartAnimation();
- // The animation may not have been running before, if mAnimationFinished
- // was false (before we changed it to true in this function). So, mark the
- // animation as running.
- mAnimating = true;
- }
- return NS_OK;
- }
- void
- RasterImage::SetLoopCount(PRInt32 aLoopCount)
- {
- if (mError)
- return;
- // -1 infinite
- // 0 no looping, one iteration
- // 1 one loop, two iterations
- // ...
- mLoopCount = aLoopCount;
- }
- nsresult
- RasterImage::AddSourceData(const char *aBuffer, PRUint32 aCount)
- {
- if (mError)
- return NS_ERROR_FAILURE;
- NS_ENSURE_ARG_POINTER(aBuffer);
- nsresult rv = NS_OK;
- // We should not call this if we're not initialized
- NS_ABORT_IF_FALSE(mInitialized, "Calling AddSourceData() on uninitialized "
- "RasterImage!");
- // We should not call this if we're already finished adding source data
- NS_ABORT_IF_FALSE(!mHasSourceData, "Calling AddSourceData() after calling "
- "sourceDataComplete()!");
- // This call should come straight from necko - no reentrancy allowed
- NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
- // If we're not storing source data, write it directly to the decoder
- if (!StoringSourceData()) {
- rv = WriteToDecoder(aBuffer, aCount);
- CONTAINER_ENSURE_SUCCESS(rv);
- // We're not storing source data, so this data is probably coming straight
- // from the network. In this case, we want to display data as soon as we
- // get it, so we want to flush invalidations after every write.
- nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
- mInDecoder = true;
- mDecoder->FlushInvalidations();
- mInDecoder = false;
- }
- // Otherwise, we're storing data in the source buffer
- else {
- // Store the data
- char *newElem = mSourceData.AppendElements(aBuffer, aCount);
- if (!newElem)
- return NS_ERROR_OUT_OF_MEMORY;
- // If there's a decoder open, that means we want to do more decoding.
- // Wake up the worker.
- if (mDecoder) {
- DecodeWorker::Singleton()->RequestDecode(this);
- }
- }
- // Statistics
- total_source_bytes += aCount;
- if (mDiscardable)
- discardable_source_bytes += aCount;
- PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
- ("CompressedImageAccounting: Added compressed data to RasterImage %p (%s). "
- "Total Containers: %d, Discardable containers: %d, "
- "Total source bytes: %lld, Source bytes for discardable containers %lld",
- this,
- mSourceDataMimeType.get(),
- num_containers,
- num_discardable_containers,
- total_source_bytes,
- discardable_source_bytes));
- return NS_OK;
- }
- /* Note! buf must be declared as char buf[9]; */
- // just used for logging and hashing the header
- static void
- get_header_str (char *buf, char *data, PRSize data_len)
- {
- int i;
- int n;
- static char hex[] = "0123456789abcdef";
- n = data_len < 4 ? data_len : 4;
- for (i = 0; i < n; i++) {
- buf[i * 2] = hex[(data[i] >> 4) & 0x0f];
- buf[i * 2 + 1] = hex[data[i] & 0x0f];
- }
- buf[i * 2] = 0;
- }
- nsresult
- RasterImage::SourceDataComplete()
- {
- if (mError)
- return NS_ERROR_FAILURE;
- // If we've been called before, ignore. Otherwise, flag that we have everything
- if (mHasSourceData)
- return NS_OK;
- mHasSourceData = true;
- // This call should come straight from necko - no reentrancy allowed
- NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
- // If we're not storing any source data, then all the data was written
- // directly to the decoder in the AddSourceData() calls. This means we're
- // done, so we can shut down the decoder.
- if (!StoringSourceData()) {
- nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
- CONTAINER_ENSURE_SUCCESS(rv);
- }
- // If there's a decoder open, synchronously decode the beginning of the image
- // to check for errors and get the image's size. (If we already have the
- // image's size, this does nothing.) Then kick off an async decode of the
- // rest of the image.
- if (mDecoder) {
- nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
- CONTAINER_ENSURE_SUCCESS(rv);
- }
- // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
- // finish decoding this image.
- if (mDecoder) {
- DecodeWorker::Singleton()->RequestDecode(this);
- }
- // Free up any extra space in the backing buffer
- mSourceData.Compact();
- // Log header information
- if (PR_LOG_TEST(gCompressedImageAccountingLog, PR_LOG_DEBUG)) {
- char buf[9];
- get_header_str(buf, mSourceData.Elements(), mSourceData.Length());
- PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
- ("CompressedImageAccounting: RasterImage::SourceDataComplete() - data "
- "is done for container %p (%s) - header %p is 0x%s (length %d)",
- this,
- mSourceDataMimeType.get(),
- mSourceData.Elements(),
- buf,
- mSourceData.Length()));
- }
- // We now have one of the qualifications for discarding. Re-evaluate.
- if (CanDiscard()) {
- nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
- CONTAINER_ENSURE_SUCCESS(rv);
- }
- return NS_OK;
- }
- nsresult
- RasterImage::NewSourceData()
- {
- nsresult rv;
- if (mError)
- return NS_ERROR_FAILURE;
- // The source data should be complete before calling this
- NS_ABORT_IF_FALSE(mHasSourceData,
- "Calling NewSourceData before SourceDataComplete!");
- if (!mHasSourceData)
- return NS_ERROR_ILLEGAL_VALUE;
- // Only supported for multipart channels. It wouldn't be too hard to change this,
- // but it would involve making sure that things worked for decode-on-draw and
- // discarding. Presently there's no need for this, so we don't.
- NS_ABORT_IF_FALSE(mMultipart, "NewSourceData not supported for multipart");
- if (!mMultipart)
- return NS_ERROR_ILLEGAL_VALUE;
- // We're multipart, so we shouldn't be storing source data
- NS_ABORT_IF_FALSE(!Storin…