michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** @file michael@0: * This file declares the RasterImage class, which michael@0: * handles static and animated rasterized images. michael@0: * michael@0: * @author Stuart Parmenter michael@0: * @author Chris Saari michael@0: * @author Arron Mogge michael@0: * @author Andrew Smith michael@0: */ michael@0: michael@0: #ifndef mozilla_imagelib_RasterImage_h_ michael@0: #define mozilla_imagelib_RasterImage_h_ michael@0: michael@0: #include "Image.h" michael@0: #include "FrameBlender.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "imgIContainer.h" michael@0: #include "nsIProperties.h" michael@0: #include "nsTArray.h" michael@0: #include "imgFrame.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "DecodeStrategy.h" michael@0: #include "DiscardTracker.h" michael@0: #include "Orientation.h" michael@0: #include "nsIObserver.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "mozilla/ReentrantMonitor.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: #include "mozilla/WeakPtr.h" michael@0: #ifdef DEBUG michael@0: #include "imgIContainerDebug.h" michael@0: #endif michael@0: michael@0: class nsIInputStream; michael@0: class nsIThreadPool; michael@0: class nsIRequest; michael@0: michael@0: #define NS_RASTERIMAGE_CID \ michael@0: { /* 376ff2c1-9bf6-418a-b143-3340c00112f7 */ \ michael@0: 0x376ff2c1, \ michael@0: 0x9bf6, \ michael@0: 0x418a, \ michael@0: {0xb1, 0x43, 0x33, 0x40, 0xc0, 0x01, 0x12, 0xf7} \ michael@0: } michael@0: michael@0: /** michael@0: * Handles static and animated image containers. michael@0: * michael@0: * michael@0: * @par A Quick Walk Through michael@0: * The decoder initializes this class and calls AppendFrame() to add a frame. michael@0: * Once RasterImage detects more than one frame, it starts the animation michael@0: * with StartAnimation(). Note that the invalidation events for RasterImage are michael@0: * generated automatically using nsRefreshDriver. michael@0: * michael@0: * @par michael@0: * StartAnimation() initializes the animation helper object and sets the time michael@0: * the first frame was displayed to the current clock time. michael@0: * michael@0: * @par michael@0: * When the refresh driver corresponding to the imgIContainer that this image is michael@0: * a part of notifies the RasterImage that it's time to invalidate, michael@0: * RequestRefresh() is called with a given TimeStamp to advance to. As long as michael@0: * the timeout of the given frame (the frame's "delay") plus the time that frame michael@0: * was first displayed is less than or equal to the TimeStamp given, michael@0: * RequestRefresh() calls AdvanceFrame(). michael@0: * michael@0: * @par michael@0: * AdvanceFrame() is responsible for advancing a single frame of the animation. michael@0: * It can return true, meaning that the frame advanced, or false, meaning that michael@0: * the frame failed to advance (usually because the next frame hasn't been michael@0: * decoded yet). It is also responsible for performing the final animation stop michael@0: * procedure if the final frame of a non-looping animation is reached. michael@0: * michael@0: * @par michael@0: * Each frame can have a different method of removing itself. These are michael@0: * listed as imgIContainer::cDispose... constants. Notify() calls michael@0: * DoComposite() to handle any special frame destruction. michael@0: * michael@0: * @par michael@0: * The basic path through DoComposite() is: michael@0: * 1) Calculate Area that needs updating, which is at least the area of michael@0: * aNextFrame. michael@0: * 2) Dispose of previous frame. michael@0: * 3) Draw new image onto compositingFrame. michael@0: * See comments in DoComposite() for more information and optimizations. michael@0: * michael@0: * @par michael@0: * The rest of the RasterImage specific functions are used by DoComposite to michael@0: * destroy the old frame and build the new one. michael@0: * michael@0: * @note michael@0: *
  • "Mask", "Alpha", and "Alpha Level" are interchangeable phrases in michael@0: * respects to RasterImage. michael@0: * michael@0: * @par michael@0: *
  • GIFs never have more than a 1 bit alpha. michael@0: *
  • APNGs may have a full alpha channel. michael@0: * michael@0: * @par michael@0: *
  • Background color specified in GIF is ignored by web browsers. michael@0: * michael@0: * @par michael@0: *
  • If Frame 3 wants to dispose by restoring previous, what it wants is to michael@0: * restore the composition up to and including Frame 2, as well as Frame 2s michael@0: * disposal. So, in the middle of DoComposite when composing Frame 3, right michael@0: * after destroying Frame 2's area, we copy compositingFrame to michael@0: * prevCompositingFrame. When DoComposite gets called to do Frame 4, we michael@0: * copy prevCompositingFrame back, and then draw Frame 4 on top. michael@0: * michael@0: * @par michael@0: * The mAnim structure has members only needed for animated images, so michael@0: * it's not allocated until the second frame is added. michael@0: */ michael@0: michael@0: class ScaleRequest; michael@0: michael@0: namespace mozilla { michael@0: michael@0: namespace layers { michael@0: class LayerManager; michael@0: class ImageContainer; michael@0: class Image; michael@0: } michael@0: michael@0: namespace image { michael@0: michael@0: class Decoder; michael@0: class FrameAnimator; michael@0: michael@0: class RasterImage : public ImageResource michael@0: , public nsIProperties michael@0: , public SupportsWeakPtr michael@0: #ifdef DEBUG michael@0: , public imgIContainerDebug michael@0: #endif michael@0: { michael@0: public: michael@0: MOZ_DECLARE_REFCOUNTED_TYPENAME(RasterImage) michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIPROPERTIES michael@0: NS_DECL_IMGICONTAINER michael@0: #ifdef DEBUG michael@0: NS_DECL_IMGICONTAINERDEBUG michael@0: #endif michael@0: michael@0: // (no public constructor - use ImageFactory) michael@0: virtual ~RasterImage(); michael@0: michael@0: virtual nsresult StartAnimation(); michael@0: virtual nsresult StopAnimation(); michael@0: michael@0: // Methods inherited from Image michael@0: nsresult Init(const char* aMimeType, michael@0: uint32_t aFlags); michael@0: virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE; michael@0: michael@0: // Raster-specific methods michael@0: static NS_METHOD WriteToRasterImage(nsIInputStream* aIn, void* aClosure, michael@0: const char* aFromRawSegment, michael@0: uint32_t aToOffset, uint32_t aCount, michael@0: uint32_t* aWriteCount); michael@0: michael@0: /* The index of the current frame that would be drawn if the image was to be michael@0: * drawn now. */ michael@0: uint32_t GetCurrentFrameIndex(); michael@0: michael@0: /* The total number of frames in this image. */ michael@0: uint32_t GetNumFrames() const; michael@0: michael@0: virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: virtual size_t NonHeapSizeOfDecoded() const; michael@0: virtual size_t OutOfProcessSizeOfDecoded() const; michael@0: michael@0: /* Triggers discarding. */ michael@0: void Discard(bool force = false); michael@0: void ForceDiscard() { Discard(/* force = */ true); } michael@0: michael@0: /* Callbacks for decoders */ michael@0: nsresult SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult); michael@0: michael@0: /** Sets the size and inherent orientation of the container. This should only michael@0: * be called by the decoder. This function may be called multiple times, but michael@0: * will throw an error if subsequent calls do not match the first. michael@0: */ michael@0: nsresult SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation); michael@0: michael@0: /** michael@0: * Ensures that a given frame number exists with the given parameters, and michael@0: * returns pointers to the data storage for that frame. michael@0: * It is not possible to create sparse frame arrays; you can only append michael@0: * frames to the current frame array. michael@0: */ michael@0: nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY, michael@0: int32_t aWidth, int32_t aHeight, michael@0: gfxImageFormat aFormat, michael@0: uint8_t aPaletteDepth, michael@0: uint8_t** imageData, michael@0: uint32_t* imageLength, michael@0: uint32_t** paletteData, michael@0: uint32_t* paletteLength, michael@0: imgFrame** aFrame); michael@0: michael@0: /** michael@0: * A shorthand for EnsureFrame, above, with aPaletteDepth = 0 and paletteData michael@0: * and paletteLength set to null. michael@0: */ michael@0: nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY, michael@0: int32_t aWidth, int32_t aHeight, michael@0: gfxImageFormat aFormat, michael@0: uint8_t** imageData, michael@0: uint32_t* imageLength, michael@0: imgFrame** aFrame); michael@0: michael@0: /* notification that the entire image has been decoded */ michael@0: nsresult DecodingComplete(); michael@0: michael@0: /** michael@0: * Number of times to loop the image. michael@0: * @note -1 means forever. michael@0: */ michael@0: void SetLoopCount(int32_t aLoopCount); michael@0: michael@0: /* Add compressed source data to the imgContainer. michael@0: * michael@0: * The decoder will use this data, either immediately or at draw time, to michael@0: * decode the image. michael@0: * michael@0: * XXX This method's only caller (WriteToContainer) ignores the return michael@0: * value. Should this just return void? michael@0: */ michael@0: nsresult AddSourceData(const char *aBuffer, uint32_t aCount); michael@0: michael@0: virtual nsresult OnImageDataAvailable(nsIRequest* aRequest, michael@0: nsISupports* aContext, michael@0: nsIInputStream* aInStr, michael@0: uint64_t aSourceOffset, michael@0: uint32_t aCount) MOZ_OVERRIDE; michael@0: virtual nsresult OnImageDataComplete(nsIRequest* aRequest, michael@0: nsISupports* aContext, michael@0: nsresult aStatus, michael@0: bool aLastPart) MOZ_OVERRIDE; michael@0: virtual nsresult OnNewSourceData() MOZ_OVERRIDE; michael@0: michael@0: static already_AddRefed GetEventTarget() { michael@0: return DecodePool::Singleton()->GetEventTarget(); michael@0: } michael@0: michael@0: /** michael@0: * A hint of the number of bytes of source data that the image contains. If michael@0: * called early on, this can help reduce copying and reallocations by michael@0: * appropriately preallocating the source data buffer. michael@0: * michael@0: * We take this approach rather than having the source data management code do michael@0: * something more complicated (like chunklisting) because HTTP is by far the michael@0: * dominant source of images, and the Content-Length header is quite reliable. michael@0: * Thus, pre-allocation simplifies code and reduces the total number of michael@0: * allocations. michael@0: */ michael@0: nsresult SetSourceSizeHint(uint32_t sizeHint); michael@0: michael@0: /* Provide a hint for the requested resolution of the resulting image. */ michael@0: void SetRequestedResolution(const nsIntSize requestedResolution) { michael@0: mRequestedResolution = requestedResolution; michael@0: } michael@0: michael@0: nsIntSize GetRequestedResolution() { michael@0: return mRequestedResolution; michael@0: } michael@0: /* Provide a hint for the requested dimension of the resulting image. */ michael@0: void SetRequestedSampleSize(int requestedSampleSize) { michael@0: mRequestedSampleSize = requestedSampleSize; michael@0: } michael@0: michael@0: int GetRequestedSampleSize() { michael@0: return mRequestedSampleSize; michael@0: } michael@0: michael@0: michael@0: michael@0: nsCString GetURIString() { michael@0: nsCString spec; michael@0: if (GetURI()) { michael@0: GetURI()->GetSpec(spec); michael@0: } michael@0: return spec; michael@0: } michael@0: michael@0: // Called from module startup. Sets up RasterImage to be used. michael@0: static void Initialize(); michael@0: michael@0: enum ScaleStatus michael@0: { michael@0: SCALE_INVALID, michael@0: SCALE_PENDING, michael@0: SCALE_DONE michael@0: }; michael@0: michael@0: // Call this with a new ScaleRequest to mark this RasterImage's scale result michael@0: // as waiting for the results of this request. You call to ScalingDone before michael@0: // request is destroyed! michael@0: void ScalingStart(ScaleRequest* request); michael@0: michael@0: // Call this with a finished ScaleRequest to set this RasterImage's scale michael@0: // result. Give it a ScaleStatus of SCALE_DONE if everything succeeded, and michael@0: // SCALE_INVALID otherwise. michael@0: void ScalingDone(ScaleRequest* request, ScaleStatus status); michael@0: michael@0: // Decoder shutdown michael@0: enum eShutdownIntent { michael@0: eShutdownIntent_Done = 0, michael@0: eShutdownIntent_NotNeeded = 1, michael@0: eShutdownIntent_Error = 2, michael@0: eShutdownIntent_AllCount = 3 michael@0: }; michael@0: michael@0: // Decode strategy michael@0: michael@0: private: michael@0: already_AddRefed CurrentStatusTracker() michael@0: { michael@0: mDecodingMonitor.AssertCurrentThreadIn(); michael@0: nsRefPtr statusTracker; michael@0: statusTracker = mDecodeRequest ? mDecodeRequest->mStatusTracker michael@0: : mStatusTracker; michael@0: MOZ_ASSERT(statusTracker); michael@0: return statusTracker.forget(); michael@0: } michael@0: michael@0: nsresult OnImageDataCompleteCore(nsIRequest* aRequest, nsISupports*, nsresult aStatus); michael@0: michael@0: /** michael@0: * Each RasterImage has a pointer to one or zero heap-allocated michael@0: * DecodeRequests. michael@0: */ michael@0: struct DecodeRequest michael@0: { michael@0: DecodeRequest(RasterImage* aImage) michael@0: : mImage(aImage) michael@0: , mBytesToDecode(0) michael@0: , mRequestStatus(REQUEST_INACTIVE) michael@0: , mChunkCount(0) michael@0: , mAllocatedNewFrame(false) michael@0: { michael@0: MOZ_ASSERT(aImage, "aImage cannot be null"); michael@0: MOZ_ASSERT(aImage->mStatusTracker, michael@0: "aImage should have an imgStatusTracker"); michael@0: mStatusTracker = aImage->mStatusTracker->CloneForRecording(); michael@0: } michael@0: michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodeRequest) michael@0: michael@0: // The status tracker that is associated with a given decode request, to michael@0: // ensure their lifetimes are linked. michael@0: nsRefPtr mStatusTracker; michael@0: michael@0: RasterImage* mImage; michael@0: michael@0: uint32_t mBytesToDecode; michael@0: michael@0: enum DecodeRequestStatus michael@0: { michael@0: REQUEST_INACTIVE, michael@0: REQUEST_PENDING, michael@0: REQUEST_ACTIVE, michael@0: REQUEST_WORK_DONE, michael@0: REQUEST_STOPPED michael@0: } mRequestStatus; michael@0: michael@0: /* Keeps track of how much time we've burned decoding this particular decode michael@0: * request. */ michael@0: TimeDuration mDecodeTime; michael@0: michael@0: /* The number of chunks it took to decode this image. */ michael@0: int32_t mChunkCount; michael@0: michael@0: /* True if a new frame has been allocated, but DecodeSomeData hasn't yet michael@0: * been called to flush data to it */ michael@0: bool mAllocatedNewFrame; michael@0: }; michael@0: michael@0: /* michael@0: * DecodePool is a singleton class we use when decoding large images. michael@0: * michael@0: * When we wish to decode an image larger than michael@0: * image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode() michael@0: * for the image. This adds the image to a queue of pending requests and posts michael@0: * the DecodePool singleton to the event queue, if it's not already pending michael@0: * there. michael@0: * michael@0: * When the DecodePool is run from the event queue, it decodes the image (and michael@0: * all others it's managing) in chunks, periodically yielding control back to michael@0: * the event loop. michael@0: */ michael@0: class DecodePool : public nsIObserver michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: static DecodePool* Singleton(); michael@0: michael@0: /** michael@0: * Ask the DecodePool to asynchronously decode this image. michael@0: */ michael@0: void RequestDecode(RasterImage* aImg); michael@0: michael@0: /** michael@0: * Decode aImg for a short amount of time, and post the remainder to the michael@0: * queue. michael@0: */ michael@0: void DecodeABitOf(RasterImage* aImg, DecodeStrategy aStrategy); michael@0: michael@0: /** michael@0: * Ask the DecodePool to stop decoding this image. Internally, we also michael@0: * call this function when we finish decoding an image. michael@0: * michael@0: * Since the DecodePool keeps raw pointers to RasterImages, make sure you michael@0: * call this before a RasterImage is destroyed! michael@0: */ michael@0: static void StopDecoding(RasterImage* aImg); michael@0: michael@0: /** michael@0: * Synchronously decode the beginning of the image until we run out of michael@0: * bytes or we get the image's size. Note that this done on a best-effort michael@0: * basis; if the size is burried too deep in the image, we'll give up. michael@0: * michael@0: * @return NS_ERROR if an error is encountered, and NS_OK otherwise. (Note michael@0: * that we return NS_OK even when the size was not found.) michael@0: */ michael@0: nsresult DecodeUntilSizeAvailable(RasterImage* aImg); michael@0: michael@0: /** michael@0: * Returns an event target interface to the thread pool; primarily for michael@0: * OnDataAvailable delivery off main thread. michael@0: * michael@0: * @return An nsIEventTarget interface to mThreadPool. michael@0: */ michael@0: already_AddRefed GetEventTarget(); michael@0: michael@0: virtual ~DecodePool(); michael@0: michael@0: private: /* statics */ michael@0: static StaticRefPtr sSingleton; michael@0: michael@0: private: /* methods */ michael@0: DecodePool(); michael@0: michael@0: enum DecodeType { michael@0: DECODE_TYPE_UNTIL_TIME, michael@0: DECODE_TYPE_UNTIL_SIZE, michael@0: DECODE_TYPE_UNTIL_DONE_BYTES michael@0: }; michael@0: michael@0: /* Decode some chunks of the given image. If aDecodeType is UNTIL_SIZE, michael@0: * decode until we have the image's size, then stop. If bytesToDecode is michael@0: * non-0, at most bytesToDecode bytes will be decoded. if aDecodeType is michael@0: * UNTIL_DONE_BYTES, decode until all bytesToDecode bytes are decoded. michael@0: */ michael@0: nsresult DecodeSomeOfImage(RasterImage* aImg, michael@0: DecodeStrategy aStrategy, michael@0: DecodeType aDecodeType = DECODE_TYPE_UNTIL_TIME, michael@0: uint32_t bytesToDecode = 0); michael@0: michael@0: /* A decode job dispatched to a thread pool by DecodePool. michael@0: */ michael@0: class DecodeJob : public nsRunnable michael@0: { michael@0: public: michael@0: DecodeJob(DecodeRequest* aRequest, RasterImage* aImg) michael@0: : mRequest(aRequest) michael@0: , mImage(aImg) michael@0: {} michael@0: michael@0: NS_IMETHOD Run(); michael@0: michael@0: protected: michael@0: virtual ~DecodeJob(); michael@0: michael@0: private: michael@0: nsRefPtr mRequest; michael@0: nsRefPtr mImage; michael@0: }; michael@0: michael@0: private: /* members */ michael@0: michael@0: // mThreadPoolMutex protects mThreadPool. For all RasterImages R, michael@0: // R::mDecodingMonitor must be acquired before mThreadPoolMutex michael@0: // if both are acquired; the other order may cause deadlock. michael@0: mozilla::Mutex mThreadPoolMutex; michael@0: nsCOMPtr mThreadPool; michael@0: }; michael@0: michael@0: class DecodeDoneWorker : public nsRunnable michael@0: { michael@0: public: michael@0: /** michael@0: * Called by the DecodePool with an image when it's done some significant michael@0: * portion of decoding that needs to be notified about. michael@0: * michael@0: * Ensures the decode state accumulated by the decoding process gets michael@0: * applied to the image. michael@0: */ michael@0: static void NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request); michael@0: michael@0: NS_IMETHOD Run(); michael@0: michael@0: private: /* methods */ michael@0: DecodeDoneWorker(RasterImage* image, DecodeRequest* request); michael@0: michael@0: private: /* members */ michael@0: michael@0: nsRefPtr mImage; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class FrameNeededWorker : public nsRunnable michael@0: { michael@0: public: michael@0: /** michael@0: * Called by the DecodeJob with an image when it's been told by the michael@0: * decoder that it needs a new frame to be allocated on the main thread. michael@0: * michael@0: * Dispatches an event to do so, which will further dispatch a michael@0: * DecodeRequest event to continue decoding. michael@0: */ michael@0: static void GetNewFrame(RasterImage* image); michael@0: michael@0: NS_IMETHOD Run(); michael@0: michael@0: private: /* methods */ michael@0: FrameNeededWorker(RasterImage* image); michael@0: michael@0: private: /* members */ michael@0: michael@0: nsRefPtr mImage; michael@0: }; michael@0: michael@0: nsresult FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done, michael@0: DecodeRequest* request = nullptr); michael@0: michael@0: bool DrawWithPreDownscaleIfNeeded(imgFrame *aFrame, michael@0: gfxContext *aContext, michael@0: GraphicsFilter aFilter, michael@0: const gfxMatrix &aUserSpaceToImageSpace, michael@0: const gfxRect &aFill, michael@0: const nsIntRect &aSubimage, michael@0: uint32_t aFlags); michael@0: michael@0: nsresult CopyFrame(uint32_t aWhichFrame, michael@0: uint32_t aFlags, michael@0: gfxImageSurface **_retval); michael@0: michael@0: /** michael@0: * Deletes and nulls out the frame in mFrames[framenum]. michael@0: * michael@0: * Does not change the size of mFrames. michael@0: * michael@0: * @param framenum The index of the frame to be deleted. michael@0: * Must lie in [0, mFrames.Length() ) michael@0: */ michael@0: void DeleteImgFrame(uint32_t framenum); michael@0: michael@0: imgFrame* GetImgFrameNoDecode(uint32_t framenum); michael@0: imgFrame* GetImgFrame(uint32_t framenum); michael@0: imgFrame* GetDrawableImgFrame(uint32_t framenum); michael@0: imgFrame* GetCurrentImgFrame(); michael@0: uint32_t GetCurrentImgFrameIndex() const; michael@0: michael@0: size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, michael@0: mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: void EnsureAnimExists(); michael@0: michael@0: nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame, michael@0: uint8_t **imageData, uint32_t *imageLength, michael@0: uint32_t **paletteData, uint32_t *paletteLength, michael@0: imgFrame** aRetFrame); michael@0: nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, michael@0: gfxImageFormat aFormat, uint8_t aPaletteDepth, michael@0: uint8_t **imageData, uint32_t *imageLength, michael@0: uint32_t **paletteData, uint32_t *paletteLength, michael@0: imgFrame** aRetFrame); michael@0: michael@0: nsresult DoImageDataComplete(); michael@0: michael@0: bool ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame); michael@0: michael@0: already_AddRefed GetCurrentImage(); michael@0: void UpdateImageContainer(); michael@0: michael@0: void SetInUpdateImageContainer(bool aInUpdate) { mInUpdateImageContainer = aInUpdate; } michael@0: bool IsInUpdateImageContainer() { return mInUpdateImageContainer; } michael@0: enum RequestDecodeType { michael@0: ASYNCHRONOUS, michael@0: SYNCHRONOUS_NOTIFY, michael@0: SYNCHRONOUS_NOTIFY_AND_SOME_DECODE michael@0: }; michael@0: NS_IMETHOD RequestDecodeCore(RequestDecodeType aDecodeType); michael@0: michael@0: // We would like to just check if we have a zero lock count, but we can't do michael@0: // that for animated images because in EnsureAnimExists we lock the image and michael@0: // never unlock so that animated images always have their lock count >= 1. In michael@0: // that case we use our animation consumers count as a proxy for lock count. michael@0: bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); } michael@0: michael@0: private: // data michael@0: nsIntSize mSize; michael@0: Orientation mOrientation; michael@0: michael@0: // Whether our frames were decoded using any special flags. michael@0: // Some flags (e.g. unpremultiplied data) may not be compatible michael@0: // with the browser's needs for displaying the image to the user. michael@0: // As such, we may need to redecode if we're being asked for michael@0: // a frame with different flags. 0 indicates default flags. michael@0: // michael@0: // Valid flag bits are imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA michael@0: // and imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION. michael@0: uint32_t mFrameDecodeFlags; michael@0: michael@0: //! All the frames of the image michael@0: FrameBlender mFrameBlender; michael@0: michael@0: // The last frame we decoded for multipart images. michael@0: imgFrame* mMultipartDecodedFrame; michael@0: michael@0: nsCOMPtr mProperties; michael@0: michael@0: // IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure michael@0: // that the frames actually exist (they may have been discarded to save memory, or michael@0: // we maybe decoding on draw). michael@0: FrameAnimator* mAnim; michael@0: michael@0: // Discard members michael@0: uint32_t mLockCount; michael@0: DiscardTracker::Node mDiscardTrackerNode; michael@0: michael@0: // Source data members michael@0: nsCString mSourceDataMimeType; michael@0: michael@0: friend class DiscardTracker; michael@0: michael@0: // How many times we've decoded this image. michael@0: // This is currently only used for statistics michael@0: int32_t mDecodeCount; michael@0: michael@0: // If the image contains multiple resolutions, a hint as to which one should be used michael@0: nsIntSize mRequestedResolution; michael@0: michael@0: // A hint for image decoder that directly scale the image to smaller buffer michael@0: int mRequestedSampleSize; michael@0: michael@0: // Cached value for GetImageContainer. michael@0: nsRefPtr mImageContainer; michael@0: michael@0: // If not cached in mImageContainer, this might have our image container michael@0: WeakPtr mImageContainerCache; michael@0: michael@0: #ifdef DEBUG michael@0: uint32_t mFramesNotified; michael@0: #endif michael@0: michael@0: // Below are the pieces of data that can be accessed on more than one thread michael@0: // at once, and hence need to be locked by mDecodingMonitor. michael@0: michael@0: // BEGIN LOCKED MEMBER VARIABLES michael@0: mozilla::ReentrantMonitor mDecodingMonitor; michael@0: michael@0: FallibleTArray mSourceData; michael@0: michael@0: // Decoder and friends michael@0: nsRefPtr mDecoder; michael@0: nsRefPtr mDecodeRequest; michael@0: uint32_t mBytesDecoded; michael@0: michael@0: bool mInDecoder; michael@0: // END LOCKED MEMBER VARIABLES michael@0: michael@0: // Notification state. Used to avoid recursive notifications. michael@0: ImageStatusDiff mStatusDiff; michael@0: bool mNotifying:1; michael@0: michael@0: // Boolean flags (clustered together to conserve space): michael@0: bool mHasSize:1; // Has SetSize() been called? michael@0: bool mDecodeOnDraw:1; // Decoding on draw? michael@0: bool mMultipart:1; // Multipart? michael@0: bool mDiscardable:1; // Is container discardable? michael@0: bool mHasSourceData:1; // Do we have source data? michael@0: michael@0: // Do we have the frames in decoded form? michael@0: bool mDecoded:1; michael@0: bool mHasBeenDecoded:1; michael@0: michael@0: michael@0: // Whether the animation can stop, due to running out michael@0: // of frames, or no more owning request michael@0: bool mAnimationFinished:1; michael@0: michael@0: // Whether we're calling Decoder::Finish() from ShutdownDecoder. michael@0: bool mFinishing:1; michael@0: michael@0: bool mInUpdateImageContainer:1; michael@0: michael@0: // Whether, once we are done doing a size decode, we should immediately kick michael@0: // off a full decode. michael@0: bool mWantFullDecode:1; michael@0: michael@0: // Set when a decode worker detects an error off-main-thread. Once the error michael@0: // is handled on the main thread, mError is set, but mPendingError is used to michael@0: // stop decode work immediately. michael@0: bool mPendingError:1; michael@0: michael@0: // Decoding michael@0: nsresult RequestDecodeIfNeeded(nsresult aStatus, michael@0: eShutdownIntent aIntent, michael@0: bool aDone, michael@0: bool aWasSize); michael@0: nsresult WantDecodedFrames(); michael@0: nsresult SyncDecode(); michael@0: nsresult InitDecoder(bool aDoSizeDecode); michael@0: nsresult WriteToDecoder(const char *aBuffer, uint32_t aCount, DecodeStrategy aStrategy); michael@0: nsresult DecodeSomeData(uint32_t aMaxBytes, DecodeStrategy aStrategy); michael@0: bool IsDecodeFinished(); michael@0: TimeStamp mDrawStartTime; michael@0: michael@0: inline bool CanQualityScale(const gfxSize& scale); michael@0: inline bool CanScale(GraphicsFilter aFilter, gfxSize aScale, uint32_t aFlags); michael@0: michael@0: struct ScaleResult michael@0: { michael@0: ScaleResult() michael@0: : status(SCALE_INVALID) michael@0: {} michael@0: michael@0: gfxSize scale; michael@0: nsAutoPtr frame; michael@0: ScaleStatus status; michael@0: }; michael@0: michael@0: ScaleResult mScaleResult; michael@0: michael@0: // We hold on to a bare pointer to a ScaleRequest while it's outstanding so michael@0: // we can mark it as stopped if necessary. The ScaleWorker/DrawWorker duo michael@0: // will inform us when to let go of this pointer. michael@0: ScaleRequest* mScaleRequest; michael@0: michael@0: // Initializes imgStatusTracker and resets it on RasterImage destruction. michael@0: nsAutoPtr mStatusTrackerInit; michael@0: michael@0: nsresult ShutdownDecoder(eShutdownIntent aIntent); michael@0: michael@0: // Error handling. michael@0: void DoError(); michael@0: michael@0: class HandleErrorWorker : public nsRunnable michael@0: { michael@0: public: michael@0: /** michael@0: * Called from decoder threads when DoError() is called, since errors can't michael@0: * be handled safely off-main-thread. Dispatches an event which reinvokes michael@0: * DoError on the main thread if there isn't one already pending. michael@0: */ michael@0: static void DispatchIfNeeded(RasterImage* aImage); michael@0: michael@0: NS_IMETHOD Run(); michael@0: michael@0: private: michael@0: HandleErrorWorker(RasterImage* aImage); michael@0: michael@0: nsRefPtr mImage; michael@0: }; michael@0: michael@0: // Helpers michael@0: bool CanDiscard(); michael@0: bool CanForciblyDiscard(); michael@0: bool CanForciblyDiscardAndRedecode(); michael@0: bool DiscardingActive(); michael@0: bool StoringSourceData() const; michael@0: michael@0: protected: michael@0: RasterImage(imgStatusTracker* aStatusTracker = nullptr, michael@0: ImageURL* aURI = nullptr); michael@0: michael@0: bool ShouldAnimate(); michael@0: michael@0: friend class ImageFactory; michael@0: }; michael@0: michael@0: inline NS_IMETHODIMP RasterImage::GetAnimationMode(uint16_t *aAnimationMode) { michael@0: return GetAnimationModeInternal(aAnimationMode); michael@0: } michael@0: michael@0: // Asynchronous Decode Requestor michael@0: // michael@0: // We use this class when someone calls requestDecode() from within a decode michael@0: // notification. Since requestDecode() involves modifying the decoder's state michael@0: // (for example, possibly shutting down a header-only decode and starting a michael@0: // full decode), we don't want to do this from inside a decoder. michael@0: class imgDecodeRequestor : public nsRunnable michael@0: { michael@0: public: michael@0: imgDecodeRequestor(RasterImage &aContainer) { michael@0: mContainer = aContainer.asWeakPtr(); michael@0: } michael@0: NS_IMETHOD Run() { michael@0: if (mContainer) michael@0: mContainer->StartDecoding(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: WeakPtr mContainer; michael@0: }; michael@0: michael@0: } // namespace image michael@0: } // namespace mozilla michael@0: michael@0: #endif /* mozilla_imagelib_RasterImage_h_ */