Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 2 | * |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /** @file |
michael@0 | 8 | * This file declares the RasterImage class, which |
michael@0 | 9 | * handles static and animated rasterized images. |
michael@0 | 10 | * |
michael@0 | 11 | * @author Stuart Parmenter <pavlov@netscape.com> |
michael@0 | 12 | * @author Chris Saari <saari@netscape.com> |
michael@0 | 13 | * @author Arron Mogge <paper@animecity.nu> |
michael@0 | 14 | * @author Andrew Smith <asmith15@learn.senecac.on.ca> |
michael@0 | 15 | */ |
michael@0 | 16 | |
michael@0 | 17 | #ifndef mozilla_imagelib_RasterImage_h_ |
michael@0 | 18 | #define mozilla_imagelib_RasterImage_h_ |
michael@0 | 19 | |
michael@0 | 20 | #include "Image.h" |
michael@0 | 21 | #include "FrameBlender.h" |
michael@0 | 22 | #include "nsCOMPtr.h" |
michael@0 | 23 | #include "imgIContainer.h" |
michael@0 | 24 | #include "nsIProperties.h" |
michael@0 | 25 | #include "nsTArray.h" |
michael@0 | 26 | #include "imgFrame.h" |
michael@0 | 27 | #include "nsThreadUtils.h" |
michael@0 | 28 | #include "DecodeStrategy.h" |
michael@0 | 29 | #include "DiscardTracker.h" |
michael@0 | 30 | #include "Orientation.h" |
michael@0 | 31 | #include "nsIObserver.h" |
michael@0 | 32 | #include "mozilla/MemoryReporting.h" |
michael@0 | 33 | #include "mozilla/Mutex.h" |
michael@0 | 34 | #include "mozilla/ReentrantMonitor.h" |
michael@0 | 35 | #include "mozilla/TimeStamp.h" |
michael@0 | 36 | #include "mozilla/StaticPtr.h" |
michael@0 | 37 | #include "mozilla/WeakPtr.h" |
michael@0 | 38 | #ifdef DEBUG |
michael@0 | 39 | #include "imgIContainerDebug.h" |
michael@0 | 40 | #endif |
michael@0 | 41 | |
michael@0 | 42 | class nsIInputStream; |
michael@0 | 43 | class nsIThreadPool; |
michael@0 | 44 | class nsIRequest; |
michael@0 | 45 | |
michael@0 | 46 | #define NS_RASTERIMAGE_CID \ |
michael@0 | 47 | { /* 376ff2c1-9bf6-418a-b143-3340c00112f7 */ \ |
michael@0 | 48 | 0x376ff2c1, \ |
michael@0 | 49 | 0x9bf6, \ |
michael@0 | 50 | 0x418a, \ |
michael@0 | 51 | {0xb1, 0x43, 0x33, 0x40, 0xc0, 0x01, 0x12, 0xf7} \ |
michael@0 | 52 | } |
michael@0 | 53 | |
michael@0 | 54 | /** |
michael@0 | 55 | * Handles static and animated image containers. |
michael@0 | 56 | * |
michael@0 | 57 | * |
michael@0 | 58 | * @par A Quick Walk Through |
michael@0 | 59 | * The decoder initializes this class and calls AppendFrame() to add a frame. |
michael@0 | 60 | * Once RasterImage detects more than one frame, it starts the animation |
michael@0 | 61 | * with StartAnimation(). Note that the invalidation events for RasterImage are |
michael@0 | 62 | * generated automatically using nsRefreshDriver. |
michael@0 | 63 | * |
michael@0 | 64 | * @par |
michael@0 | 65 | * StartAnimation() initializes the animation helper object and sets the time |
michael@0 | 66 | * the first frame was displayed to the current clock time. |
michael@0 | 67 | * |
michael@0 | 68 | * @par |
michael@0 | 69 | * When the refresh driver corresponding to the imgIContainer that this image is |
michael@0 | 70 | * a part of notifies the RasterImage that it's time to invalidate, |
michael@0 | 71 | * RequestRefresh() is called with a given TimeStamp to advance to. As long as |
michael@0 | 72 | * the timeout of the given frame (the frame's "delay") plus the time that frame |
michael@0 | 73 | * was first displayed is less than or equal to the TimeStamp given, |
michael@0 | 74 | * RequestRefresh() calls AdvanceFrame(). |
michael@0 | 75 | * |
michael@0 | 76 | * @par |
michael@0 | 77 | * AdvanceFrame() is responsible for advancing a single frame of the animation. |
michael@0 | 78 | * It can return true, meaning that the frame advanced, or false, meaning that |
michael@0 | 79 | * the frame failed to advance (usually because the next frame hasn't been |
michael@0 | 80 | * decoded yet). It is also responsible for performing the final animation stop |
michael@0 | 81 | * procedure if the final frame of a non-looping animation is reached. |
michael@0 | 82 | * |
michael@0 | 83 | * @par |
michael@0 | 84 | * Each frame can have a different method of removing itself. These are |
michael@0 | 85 | * listed as imgIContainer::cDispose... constants. Notify() calls |
michael@0 | 86 | * DoComposite() to handle any special frame destruction. |
michael@0 | 87 | * |
michael@0 | 88 | * @par |
michael@0 | 89 | * The basic path through DoComposite() is: |
michael@0 | 90 | * 1) Calculate Area that needs updating, which is at least the area of |
michael@0 | 91 | * aNextFrame. |
michael@0 | 92 | * 2) Dispose of previous frame. |
michael@0 | 93 | * 3) Draw new image onto compositingFrame. |
michael@0 | 94 | * See comments in DoComposite() for more information and optimizations. |
michael@0 | 95 | * |
michael@0 | 96 | * @par |
michael@0 | 97 | * The rest of the RasterImage specific functions are used by DoComposite to |
michael@0 | 98 | * destroy the old frame and build the new one. |
michael@0 | 99 | * |
michael@0 | 100 | * @note |
michael@0 | 101 | * <li> "Mask", "Alpha", and "Alpha Level" are interchangeable phrases in |
michael@0 | 102 | * respects to RasterImage. |
michael@0 | 103 | * |
michael@0 | 104 | * @par |
michael@0 | 105 | * <li> GIFs never have more than a 1 bit alpha. |
michael@0 | 106 | * <li> APNGs may have a full alpha channel. |
michael@0 | 107 | * |
michael@0 | 108 | * @par |
michael@0 | 109 | * <li> Background color specified in GIF is ignored by web browsers. |
michael@0 | 110 | * |
michael@0 | 111 | * @par |
michael@0 | 112 | * <li> If Frame 3 wants to dispose by restoring previous, what it wants is to |
michael@0 | 113 | * restore the composition up to and including Frame 2, as well as Frame 2s |
michael@0 | 114 | * disposal. So, in the middle of DoComposite when composing Frame 3, right |
michael@0 | 115 | * after destroying Frame 2's area, we copy compositingFrame to |
michael@0 | 116 | * prevCompositingFrame. When DoComposite gets called to do Frame 4, we |
michael@0 | 117 | * copy prevCompositingFrame back, and then draw Frame 4 on top. |
michael@0 | 118 | * |
michael@0 | 119 | * @par |
michael@0 | 120 | * The mAnim structure has members only needed for animated images, so |
michael@0 | 121 | * it's not allocated until the second frame is added. |
michael@0 | 122 | */ |
michael@0 | 123 | |
michael@0 | 124 | class ScaleRequest; |
michael@0 | 125 | |
michael@0 | 126 | namespace mozilla { |
michael@0 | 127 | |
michael@0 | 128 | namespace layers { |
michael@0 | 129 | class LayerManager; |
michael@0 | 130 | class ImageContainer; |
michael@0 | 131 | class Image; |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | namespace image { |
michael@0 | 135 | |
michael@0 | 136 | class Decoder; |
michael@0 | 137 | class FrameAnimator; |
michael@0 | 138 | |
michael@0 | 139 | class RasterImage : public ImageResource |
michael@0 | 140 | , public nsIProperties |
michael@0 | 141 | , public SupportsWeakPtr<RasterImage> |
michael@0 | 142 | #ifdef DEBUG |
michael@0 | 143 | , public imgIContainerDebug |
michael@0 | 144 | #endif |
michael@0 | 145 | { |
michael@0 | 146 | public: |
michael@0 | 147 | MOZ_DECLARE_REFCOUNTED_TYPENAME(RasterImage) |
michael@0 | 148 | NS_DECL_THREADSAFE_ISUPPORTS |
michael@0 | 149 | NS_DECL_NSIPROPERTIES |
michael@0 | 150 | NS_DECL_IMGICONTAINER |
michael@0 | 151 | #ifdef DEBUG |
michael@0 | 152 | NS_DECL_IMGICONTAINERDEBUG |
michael@0 | 153 | #endif |
michael@0 | 154 | |
michael@0 | 155 | // (no public constructor - use ImageFactory) |
michael@0 | 156 | virtual ~RasterImage(); |
michael@0 | 157 | |
michael@0 | 158 | virtual nsresult StartAnimation(); |
michael@0 | 159 | virtual nsresult StopAnimation(); |
michael@0 | 160 | |
michael@0 | 161 | // Methods inherited from Image |
michael@0 | 162 | nsresult Init(const char* aMimeType, |
michael@0 | 163 | uint32_t aFlags); |
michael@0 | 164 | virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE; |
michael@0 | 165 | |
michael@0 | 166 | // Raster-specific methods |
michael@0 | 167 | static NS_METHOD WriteToRasterImage(nsIInputStream* aIn, void* aClosure, |
michael@0 | 168 | const char* aFromRawSegment, |
michael@0 | 169 | uint32_t aToOffset, uint32_t aCount, |
michael@0 | 170 | uint32_t* aWriteCount); |
michael@0 | 171 | |
michael@0 | 172 | /* The index of the current frame that would be drawn if the image was to be |
michael@0 | 173 | * drawn now. */ |
michael@0 | 174 | uint32_t GetCurrentFrameIndex(); |
michael@0 | 175 | |
michael@0 | 176 | /* The total number of frames in this image. */ |
michael@0 | 177 | uint32_t GetNumFrames() const; |
michael@0 | 178 | |
michael@0 | 179 | virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 180 | virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 181 | virtual size_t NonHeapSizeOfDecoded() const; |
michael@0 | 182 | virtual size_t OutOfProcessSizeOfDecoded() const; |
michael@0 | 183 | |
michael@0 | 184 | /* Triggers discarding. */ |
michael@0 | 185 | void Discard(bool force = false); |
michael@0 | 186 | void ForceDiscard() { Discard(/* force = */ true); } |
michael@0 | 187 | |
michael@0 | 188 | /* Callbacks for decoders */ |
michael@0 | 189 | nsresult SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult); |
michael@0 | 190 | |
michael@0 | 191 | /** Sets the size and inherent orientation of the container. This should only |
michael@0 | 192 | * be called by the decoder. This function may be called multiple times, but |
michael@0 | 193 | * will throw an error if subsequent calls do not match the first. |
michael@0 | 194 | */ |
michael@0 | 195 | nsresult SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation); |
michael@0 | 196 | |
michael@0 | 197 | /** |
michael@0 | 198 | * Ensures that a given frame number exists with the given parameters, and |
michael@0 | 199 | * returns pointers to the data storage for that frame. |
michael@0 | 200 | * It is not possible to create sparse frame arrays; you can only append |
michael@0 | 201 | * frames to the current frame array. |
michael@0 | 202 | */ |
michael@0 | 203 | nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY, |
michael@0 | 204 | int32_t aWidth, int32_t aHeight, |
michael@0 | 205 | gfxImageFormat aFormat, |
michael@0 | 206 | uint8_t aPaletteDepth, |
michael@0 | 207 | uint8_t** imageData, |
michael@0 | 208 | uint32_t* imageLength, |
michael@0 | 209 | uint32_t** paletteData, |
michael@0 | 210 | uint32_t* paletteLength, |
michael@0 | 211 | imgFrame** aFrame); |
michael@0 | 212 | |
michael@0 | 213 | /** |
michael@0 | 214 | * A shorthand for EnsureFrame, above, with aPaletteDepth = 0 and paletteData |
michael@0 | 215 | * and paletteLength set to null. |
michael@0 | 216 | */ |
michael@0 | 217 | nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY, |
michael@0 | 218 | int32_t aWidth, int32_t aHeight, |
michael@0 | 219 | gfxImageFormat aFormat, |
michael@0 | 220 | uint8_t** imageData, |
michael@0 | 221 | uint32_t* imageLength, |
michael@0 | 222 | imgFrame** aFrame); |
michael@0 | 223 | |
michael@0 | 224 | /* notification that the entire image has been decoded */ |
michael@0 | 225 | nsresult DecodingComplete(); |
michael@0 | 226 | |
michael@0 | 227 | /** |
michael@0 | 228 | * Number of times to loop the image. |
michael@0 | 229 | * @note -1 means forever. |
michael@0 | 230 | */ |
michael@0 | 231 | void SetLoopCount(int32_t aLoopCount); |
michael@0 | 232 | |
michael@0 | 233 | /* Add compressed source data to the imgContainer. |
michael@0 | 234 | * |
michael@0 | 235 | * The decoder will use this data, either immediately or at draw time, to |
michael@0 | 236 | * decode the image. |
michael@0 | 237 | * |
michael@0 | 238 | * XXX This method's only caller (WriteToContainer) ignores the return |
michael@0 | 239 | * value. Should this just return void? |
michael@0 | 240 | */ |
michael@0 | 241 | nsresult AddSourceData(const char *aBuffer, uint32_t aCount); |
michael@0 | 242 | |
michael@0 | 243 | virtual nsresult OnImageDataAvailable(nsIRequest* aRequest, |
michael@0 | 244 | nsISupports* aContext, |
michael@0 | 245 | nsIInputStream* aInStr, |
michael@0 | 246 | uint64_t aSourceOffset, |
michael@0 | 247 | uint32_t aCount) MOZ_OVERRIDE; |
michael@0 | 248 | virtual nsresult OnImageDataComplete(nsIRequest* aRequest, |
michael@0 | 249 | nsISupports* aContext, |
michael@0 | 250 | nsresult aStatus, |
michael@0 | 251 | bool aLastPart) MOZ_OVERRIDE; |
michael@0 | 252 | virtual nsresult OnNewSourceData() MOZ_OVERRIDE; |
michael@0 | 253 | |
michael@0 | 254 | static already_AddRefed<nsIEventTarget> GetEventTarget() { |
michael@0 | 255 | return DecodePool::Singleton()->GetEventTarget(); |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | /** |
michael@0 | 259 | * A hint of the number of bytes of source data that the image contains. If |
michael@0 | 260 | * called early on, this can help reduce copying and reallocations by |
michael@0 | 261 | * appropriately preallocating the source data buffer. |
michael@0 | 262 | * |
michael@0 | 263 | * We take this approach rather than having the source data management code do |
michael@0 | 264 | * something more complicated (like chunklisting) because HTTP is by far the |
michael@0 | 265 | * dominant source of images, and the Content-Length header is quite reliable. |
michael@0 | 266 | * Thus, pre-allocation simplifies code and reduces the total number of |
michael@0 | 267 | * allocations. |
michael@0 | 268 | */ |
michael@0 | 269 | nsresult SetSourceSizeHint(uint32_t sizeHint); |
michael@0 | 270 | |
michael@0 | 271 | /* Provide a hint for the requested resolution of the resulting image. */ |
michael@0 | 272 | void SetRequestedResolution(const nsIntSize requestedResolution) { |
michael@0 | 273 | mRequestedResolution = requestedResolution; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | nsIntSize GetRequestedResolution() { |
michael@0 | 277 | return mRequestedResolution; |
michael@0 | 278 | } |
michael@0 | 279 | /* Provide a hint for the requested dimension of the resulting image. */ |
michael@0 | 280 | void SetRequestedSampleSize(int requestedSampleSize) { |
michael@0 | 281 | mRequestedSampleSize = requestedSampleSize; |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | int GetRequestedSampleSize() { |
michael@0 | 285 | return mRequestedSampleSize; |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | |
michael@0 | 289 | |
michael@0 | 290 | nsCString GetURIString() { |
michael@0 | 291 | nsCString spec; |
michael@0 | 292 | if (GetURI()) { |
michael@0 | 293 | GetURI()->GetSpec(spec); |
michael@0 | 294 | } |
michael@0 | 295 | return spec; |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | // Called from module startup. Sets up RasterImage to be used. |
michael@0 | 299 | static void Initialize(); |
michael@0 | 300 | |
michael@0 | 301 | enum ScaleStatus |
michael@0 | 302 | { |
michael@0 | 303 | SCALE_INVALID, |
michael@0 | 304 | SCALE_PENDING, |
michael@0 | 305 | SCALE_DONE |
michael@0 | 306 | }; |
michael@0 | 307 | |
michael@0 | 308 | // Call this with a new ScaleRequest to mark this RasterImage's scale result |
michael@0 | 309 | // as waiting for the results of this request. You call to ScalingDone before |
michael@0 | 310 | // request is destroyed! |
michael@0 | 311 | void ScalingStart(ScaleRequest* request); |
michael@0 | 312 | |
michael@0 | 313 | // Call this with a finished ScaleRequest to set this RasterImage's scale |
michael@0 | 314 | // result. Give it a ScaleStatus of SCALE_DONE if everything succeeded, and |
michael@0 | 315 | // SCALE_INVALID otherwise. |
michael@0 | 316 | void ScalingDone(ScaleRequest* request, ScaleStatus status); |
michael@0 | 317 | |
michael@0 | 318 | // Decoder shutdown |
michael@0 | 319 | enum eShutdownIntent { |
michael@0 | 320 | eShutdownIntent_Done = 0, |
michael@0 | 321 | eShutdownIntent_NotNeeded = 1, |
michael@0 | 322 | eShutdownIntent_Error = 2, |
michael@0 | 323 | eShutdownIntent_AllCount = 3 |
michael@0 | 324 | }; |
michael@0 | 325 | |
michael@0 | 326 | // Decode strategy |
michael@0 | 327 | |
michael@0 | 328 | private: |
michael@0 | 329 | already_AddRefed<imgStatusTracker> CurrentStatusTracker() |
michael@0 | 330 | { |
michael@0 | 331 | mDecodingMonitor.AssertCurrentThreadIn(); |
michael@0 | 332 | nsRefPtr<imgStatusTracker> statusTracker; |
michael@0 | 333 | statusTracker = mDecodeRequest ? mDecodeRequest->mStatusTracker |
michael@0 | 334 | : mStatusTracker; |
michael@0 | 335 | MOZ_ASSERT(statusTracker); |
michael@0 | 336 | return statusTracker.forget(); |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | nsresult OnImageDataCompleteCore(nsIRequest* aRequest, nsISupports*, nsresult aStatus); |
michael@0 | 340 | |
michael@0 | 341 | /** |
michael@0 | 342 | * Each RasterImage has a pointer to one or zero heap-allocated |
michael@0 | 343 | * DecodeRequests. |
michael@0 | 344 | */ |
michael@0 | 345 | struct DecodeRequest |
michael@0 | 346 | { |
michael@0 | 347 | DecodeRequest(RasterImage* aImage) |
michael@0 | 348 | : mImage(aImage) |
michael@0 | 349 | , mBytesToDecode(0) |
michael@0 | 350 | , mRequestStatus(REQUEST_INACTIVE) |
michael@0 | 351 | , mChunkCount(0) |
michael@0 | 352 | , mAllocatedNewFrame(false) |
michael@0 | 353 | { |
michael@0 | 354 | MOZ_ASSERT(aImage, "aImage cannot be null"); |
michael@0 | 355 | MOZ_ASSERT(aImage->mStatusTracker, |
michael@0 | 356 | "aImage should have an imgStatusTracker"); |
michael@0 | 357 | mStatusTracker = aImage->mStatusTracker->CloneForRecording(); |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodeRequest) |
michael@0 | 361 | |
michael@0 | 362 | // The status tracker that is associated with a given decode request, to |
michael@0 | 363 | // ensure their lifetimes are linked. |
michael@0 | 364 | nsRefPtr<imgStatusTracker> mStatusTracker; |
michael@0 | 365 | |
michael@0 | 366 | RasterImage* mImage; |
michael@0 | 367 | |
michael@0 | 368 | uint32_t mBytesToDecode; |
michael@0 | 369 | |
michael@0 | 370 | enum DecodeRequestStatus |
michael@0 | 371 | { |
michael@0 | 372 | REQUEST_INACTIVE, |
michael@0 | 373 | REQUEST_PENDING, |
michael@0 | 374 | REQUEST_ACTIVE, |
michael@0 | 375 | REQUEST_WORK_DONE, |
michael@0 | 376 | REQUEST_STOPPED |
michael@0 | 377 | } mRequestStatus; |
michael@0 | 378 | |
michael@0 | 379 | /* Keeps track of how much time we've burned decoding this particular decode |
michael@0 | 380 | * request. */ |
michael@0 | 381 | TimeDuration mDecodeTime; |
michael@0 | 382 | |
michael@0 | 383 | /* The number of chunks it took to decode this image. */ |
michael@0 | 384 | int32_t mChunkCount; |
michael@0 | 385 | |
michael@0 | 386 | /* True if a new frame has been allocated, but DecodeSomeData hasn't yet |
michael@0 | 387 | * been called to flush data to it */ |
michael@0 | 388 | bool mAllocatedNewFrame; |
michael@0 | 389 | }; |
michael@0 | 390 | |
michael@0 | 391 | /* |
michael@0 | 392 | * DecodePool is a singleton class we use when decoding large images. |
michael@0 | 393 | * |
michael@0 | 394 | * When we wish to decode an image larger than |
michael@0 | 395 | * image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode() |
michael@0 | 396 | * for the image. This adds the image to a queue of pending requests and posts |
michael@0 | 397 | * the DecodePool singleton to the event queue, if it's not already pending |
michael@0 | 398 | * there. |
michael@0 | 399 | * |
michael@0 | 400 | * When the DecodePool is run from the event queue, it decodes the image (and |
michael@0 | 401 | * all others it's managing) in chunks, periodically yielding control back to |
michael@0 | 402 | * the event loop. |
michael@0 | 403 | */ |
michael@0 | 404 | class DecodePool : public nsIObserver |
michael@0 | 405 | { |
michael@0 | 406 | public: |
michael@0 | 407 | NS_DECL_THREADSAFE_ISUPPORTS |
michael@0 | 408 | NS_DECL_NSIOBSERVER |
michael@0 | 409 | |
michael@0 | 410 | static DecodePool* Singleton(); |
michael@0 | 411 | |
michael@0 | 412 | /** |
michael@0 | 413 | * Ask the DecodePool to asynchronously decode this image. |
michael@0 | 414 | */ |
michael@0 | 415 | void RequestDecode(RasterImage* aImg); |
michael@0 | 416 | |
michael@0 | 417 | /** |
michael@0 | 418 | * Decode aImg for a short amount of time, and post the remainder to the |
michael@0 | 419 | * queue. |
michael@0 | 420 | */ |
michael@0 | 421 | void DecodeABitOf(RasterImage* aImg, DecodeStrategy aStrategy); |
michael@0 | 422 | |
michael@0 | 423 | /** |
michael@0 | 424 | * Ask the DecodePool to stop decoding this image. Internally, we also |
michael@0 | 425 | * call this function when we finish decoding an image. |
michael@0 | 426 | * |
michael@0 | 427 | * Since the DecodePool keeps raw pointers to RasterImages, make sure you |
michael@0 | 428 | * call this before a RasterImage is destroyed! |
michael@0 | 429 | */ |
michael@0 | 430 | static void StopDecoding(RasterImage* aImg); |
michael@0 | 431 | |
michael@0 | 432 | /** |
michael@0 | 433 | * Synchronously decode the beginning of the image until we run out of |
michael@0 | 434 | * bytes or we get the image's size. Note that this done on a best-effort |
michael@0 | 435 | * basis; if the size is burried too deep in the image, we'll give up. |
michael@0 | 436 | * |
michael@0 | 437 | * @return NS_ERROR if an error is encountered, and NS_OK otherwise. (Note |
michael@0 | 438 | * that we return NS_OK even when the size was not found.) |
michael@0 | 439 | */ |
michael@0 | 440 | nsresult DecodeUntilSizeAvailable(RasterImage* aImg); |
michael@0 | 441 | |
michael@0 | 442 | /** |
michael@0 | 443 | * Returns an event target interface to the thread pool; primarily for |
michael@0 | 444 | * OnDataAvailable delivery off main thread. |
michael@0 | 445 | * |
michael@0 | 446 | * @return An nsIEventTarget interface to mThreadPool. |
michael@0 | 447 | */ |
michael@0 | 448 | already_AddRefed<nsIEventTarget> GetEventTarget(); |
michael@0 | 449 | |
michael@0 | 450 | virtual ~DecodePool(); |
michael@0 | 451 | |
michael@0 | 452 | private: /* statics */ |
michael@0 | 453 | static StaticRefPtr<DecodePool> sSingleton; |
michael@0 | 454 | |
michael@0 | 455 | private: /* methods */ |
michael@0 | 456 | DecodePool(); |
michael@0 | 457 | |
michael@0 | 458 | enum DecodeType { |
michael@0 | 459 | DECODE_TYPE_UNTIL_TIME, |
michael@0 | 460 | DECODE_TYPE_UNTIL_SIZE, |
michael@0 | 461 | DECODE_TYPE_UNTIL_DONE_BYTES |
michael@0 | 462 | }; |
michael@0 | 463 | |
michael@0 | 464 | /* Decode some chunks of the given image. If aDecodeType is UNTIL_SIZE, |
michael@0 | 465 | * decode until we have the image's size, then stop. If bytesToDecode is |
michael@0 | 466 | * non-0, at most bytesToDecode bytes will be decoded. if aDecodeType is |
michael@0 | 467 | * UNTIL_DONE_BYTES, decode until all bytesToDecode bytes are decoded. |
michael@0 | 468 | */ |
michael@0 | 469 | nsresult DecodeSomeOfImage(RasterImage* aImg, |
michael@0 | 470 | DecodeStrategy aStrategy, |
michael@0 | 471 | DecodeType aDecodeType = DECODE_TYPE_UNTIL_TIME, |
michael@0 | 472 | uint32_t bytesToDecode = 0); |
michael@0 | 473 | |
michael@0 | 474 | /* A decode job dispatched to a thread pool by DecodePool. |
michael@0 | 475 | */ |
michael@0 | 476 | class DecodeJob : public nsRunnable |
michael@0 | 477 | { |
michael@0 | 478 | public: |
michael@0 | 479 | DecodeJob(DecodeRequest* aRequest, RasterImage* aImg) |
michael@0 | 480 | : mRequest(aRequest) |
michael@0 | 481 | , mImage(aImg) |
michael@0 | 482 | {} |
michael@0 | 483 | |
michael@0 | 484 | NS_IMETHOD Run(); |
michael@0 | 485 | |
michael@0 | 486 | protected: |
michael@0 | 487 | virtual ~DecodeJob(); |
michael@0 | 488 | |
michael@0 | 489 | private: |
michael@0 | 490 | nsRefPtr<DecodeRequest> mRequest; |
michael@0 | 491 | nsRefPtr<RasterImage> mImage; |
michael@0 | 492 | }; |
michael@0 | 493 | |
michael@0 | 494 | private: /* members */ |
michael@0 | 495 | |
michael@0 | 496 | // mThreadPoolMutex protects mThreadPool. For all RasterImages R, |
michael@0 | 497 | // R::mDecodingMonitor must be acquired before mThreadPoolMutex |
michael@0 | 498 | // if both are acquired; the other order may cause deadlock. |
michael@0 | 499 | mozilla::Mutex mThreadPoolMutex; |
michael@0 | 500 | nsCOMPtr<nsIThreadPool> mThreadPool; |
michael@0 | 501 | }; |
michael@0 | 502 | |
michael@0 | 503 | class DecodeDoneWorker : public nsRunnable |
michael@0 | 504 | { |
michael@0 | 505 | public: |
michael@0 | 506 | /** |
michael@0 | 507 | * Called by the DecodePool with an image when it's done some significant |
michael@0 | 508 | * portion of decoding that needs to be notified about. |
michael@0 | 509 | * |
michael@0 | 510 | * Ensures the decode state accumulated by the decoding process gets |
michael@0 | 511 | * applied to the image. |
michael@0 | 512 | */ |
michael@0 | 513 | static void NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request); |
michael@0 | 514 | |
michael@0 | 515 | NS_IMETHOD Run(); |
michael@0 | 516 | |
michael@0 | 517 | private: /* methods */ |
michael@0 | 518 | DecodeDoneWorker(RasterImage* image, DecodeRequest* request); |
michael@0 | 519 | |
michael@0 | 520 | private: /* members */ |
michael@0 | 521 | |
michael@0 | 522 | nsRefPtr<RasterImage> mImage; |
michael@0 | 523 | nsRefPtr<DecodeRequest> mRequest; |
michael@0 | 524 | }; |
michael@0 | 525 | |
michael@0 | 526 | class FrameNeededWorker : public nsRunnable |
michael@0 | 527 | { |
michael@0 | 528 | public: |
michael@0 | 529 | /** |
michael@0 | 530 | * Called by the DecodeJob with an image when it's been told by the |
michael@0 | 531 | * decoder that it needs a new frame to be allocated on the main thread. |
michael@0 | 532 | * |
michael@0 | 533 | * Dispatches an event to do so, which will further dispatch a |
michael@0 | 534 | * DecodeRequest event to continue decoding. |
michael@0 | 535 | */ |
michael@0 | 536 | static void GetNewFrame(RasterImage* image); |
michael@0 | 537 | |
michael@0 | 538 | NS_IMETHOD Run(); |
michael@0 | 539 | |
michael@0 | 540 | private: /* methods */ |
michael@0 | 541 | FrameNeededWorker(RasterImage* image); |
michael@0 | 542 | |
michael@0 | 543 | private: /* members */ |
michael@0 | 544 | |
michael@0 | 545 | nsRefPtr<RasterImage> mImage; |
michael@0 | 546 | }; |
michael@0 | 547 | |
michael@0 | 548 | nsresult FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done, |
michael@0 | 549 | DecodeRequest* request = nullptr); |
michael@0 | 550 | |
michael@0 | 551 | bool DrawWithPreDownscaleIfNeeded(imgFrame *aFrame, |
michael@0 | 552 | gfxContext *aContext, |
michael@0 | 553 | GraphicsFilter aFilter, |
michael@0 | 554 | const gfxMatrix &aUserSpaceToImageSpace, |
michael@0 | 555 | const gfxRect &aFill, |
michael@0 | 556 | const nsIntRect &aSubimage, |
michael@0 | 557 | uint32_t aFlags); |
michael@0 | 558 | |
michael@0 | 559 | nsresult CopyFrame(uint32_t aWhichFrame, |
michael@0 | 560 | uint32_t aFlags, |
michael@0 | 561 | gfxImageSurface **_retval); |
michael@0 | 562 | |
michael@0 | 563 | /** |
michael@0 | 564 | * Deletes and nulls out the frame in mFrames[framenum]. |
michael@0 | 565 | * |
michael@0 | 566 | * Does not change the size of mFrames. |
michael@0 | 567 | * |
michael@0 | 568 | * @param framenum The index of the frame to be deleted. |
michael@0 | 569 | * Must lie in [0, mFrames.Length() ) |
michael@0 | 570 | */ |
michael@0 | 571 | void DeleteImgFrame(uint32_t framenum); |
michael@0 | 572 | |
michael@0 | 573 | imgFrame* GetImgFrameNoDecode(uint32_t framenum); |
michael@0 | 574 | imgFrame* GetImgFrame(uint32_t framenum); |
michael@0 | 575 | imgFrame* GetDrawableImgFrame(uint32_t framenum); |
michael@0 | 576 | imgFrame* GetCurrentImgFrame(); |
michael@0 | 577 | uint32_t GetCurrentImgFrameIndex() const; |
michael@0 | 578 | |
michael@0 | 579 | size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, |
michael@0 | 580 | mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 581 | |
michael@0 | 582 | void EnsureAnimExists(); |
michael@0 | 583 | |
michael@0 | 584 | nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame, |
michael@0 | 585 | uint8_t **imageData, uint32_t *imageLength, |
michael@0 | 586 | uint32_t **paletteData, uint32_t *paletteLength, |
michael@0 | 587 | imgFrame** aRetFrame); |
michael@0 | 588 | nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, |
michael@0 | 589 | gfxImageFormat aFormat, uint8_t aPaletteDepth, |
michael@0 | 590 | uint8_t **imageData, uint32_t *imageLength, |
michael@0 | 591 | uint32_t **paletteData, uint32_t *paletteLength, |
michael@0 | 592 | imgFrame** aRetFrame); |
michael@0 | 593 | |
michael@0 | 594 | nsresult DoImageDataComplete(); |
michael@0 | 595 | |
michael@0 | 596 | bool ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame); |
michael@0 | 597 | |
michael@0 | 598 | already_AddRefed<layers::Image> GetCurrentImage(); |
michael@0 | 599 | void UpdateImageContainer(); |
michael@0 | 600 | |
michael@0 | 601 | void SetInUpdateImageContainer(bool aInUpdate) { mInUpdateImageContainer = aInUpdate; } |
michael@0 | 602 | bool IsInUpdateImageContainer() { return mInUpdateImageContainer; } |
michael@0 | 603 | enum RequestDecodeType { |
michael@0 | 604 | ASYNCHRONOUS, |
michael@0 | 605 | SYNCHRONOUS_NOTIFY, |
michael@0 | 606 | SYNCHRONOUS_NOTIFY_AND_SOME_DECODE |
michael@0 | 607 | }; |
michael@0 | 608 | NS_IMETHOD RequestDecodeCore(RequestDecodeType aDecodeType); |
michael@0 | 609 | |
michael@0 | 610 | // We would like to just check if we have a zero lock count, but we can't do |
michael@0 | 611 | // that for animated images because in EnsureAnimExists we lock the image and |
michael@0 | 612 | // never unlock so that animated images always have their lock count >= 1. In |
michael@0 | 613 | // that case we use our animation consumers count as a proxy for lock count. |
michael@0 | 614 | bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); } |
michael@0 | 615 | |
michael@0 | 616 | private: // data |
michael@0 | 617 | nsIntSize mSize; |
michael@0 | 618 | Orientation mOrientation; |
michael@0 | 619 | |
michael@0 | 620 | // Whether our frames were decoded using any special flags. |
michael@0 | 621 | // Some flags (e.g. unpremultiplied data) may not be compatible |
michael@0 | 622 | // with the browser's needs for displaying the image to the user. |
michael@0 | 623 | // As such, we may need to redecode if we're being asked for |
michael@0 | 624 | // a frame with different flags. 0 indicates default flags. |
michael@0 | 625 | // |
michael@0 | 626 | // Valid flag bits are imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA |
michael@0 | 627 | // and imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION. |
michael@0 | 628 | uint32_t mFrameDecodeFlags; |
michael@0 | 629 | |
michael@0 | 630 | //! All the frames of the image |
michael@0 | 631 | FrameBlender mFrameBlender; |
michael@0 | 632 | |
michael@0 | 633 | // The last frame we decoded for multipart images. |
michael@0 | 634 | imgFrame* mMultipartDecodedFrame; |
michael@0 | 635 | |
michael@0 | 636 | nsCOMPtr<nsIProperties> mProperties; |
michael@0 | 637 | |
michael@0 | 638 | // IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure |
michael@0 | 639 | // that the frames actually exist (they may have been discarded to save memory, or |
michael@0 | 640 | // we maybe decoding on draw). |
michael@0 | 641 | FrameAnimator* mAnim; |
michael@0 | 642 | |
michael@0 | 643 | // Discard members |
michael@0 | 644 | uint32_t mLockCount; |
michael@0 | 645 | DiscardTracker::Node mDiscardTrackerNode; |
michael@0 | 646 | |
michael@0 | 647 | // Source data members |
michael@0 | 648 | nsCString mSourceDataMimeType; |
michael@0 | 649 | |
michael@0 | 650 | friend class DiscardTracker; |
michael@0 | 651 | |
michael@0 | 652 | // How many times we've decoded this image. |
michael@0 | 653 | // This is currently only used for statistics |
michael@0 | 654 | int32_t mDecodeCount; |
michael@0 | 655 | |
michael@0 | 656 | // If the image contains multiple resolutions, a hint as to which one should be used |
michael@0 | 657 | nsIntSize mRequestedResolution; |
michael@0 | 658 | |
michael@0 | 659 | // A hint for image decoder that directly scale the image to smaller buffer |
michael@0 | 660 | int mRequestedSampleSize; |
michael@0 | 661 | |
michael@0 | 662 | // Cached value for GetImageContainer. |
michael@0 | 663 | nsRefPtr<mozilla::layers::ImageContainer> mImageContainer; |
michael@0 | 664 | |
michael@0 | 665 | // If not cached in mImageContainer, this might have our image container |
michael@0 | 666 | WeakPtr<mozilla::layers::ImageContainer> mImageContainerCache; |
michael@0 | 667 | |
michael@0 | 668 | #ifdef DEBUG |
michael@0 | 669 | uint32_t mFramesNotified; |
michael@0 | 670 | #endif |
michael@0 | 671 | |
michael@0 | 672 | // Below are the pieces of data that can be accessed on more than one thread |
michael@0 | 673 | // at once, and hence need to be locked by mDecodingMonitor. |
michael@0 | 674 | |
michael@0 | 675 | // BEGIN LOCKED MEMBER VARIABLES |
michael@0 | 676 | mozilla::ReentrantMonitor mDecodingMonitor; |
michael@0 | 677 | |
michael@0 | 678 | FallibleTArray<char> mSourceData; |
michael@0 | 679 | |
michael@0 | 680 | // Decoder and friends |
michael@0 | 681 | nsRefPtr<Decoder> mDecoder; |
michael@0 | 682 | nsRefPtr<DecodeRequest> mDecodeRequest; |
michael@0 | 683 | uint32_t mBytesDecoded; |
michael@0 | 684 | |
michael@0 | 685 | bool mInDecoder; |
michael@0 | 686 | // END LOCKED MEMBER VARIABLES |
michael@0 | 687 | |
michael@0 | 688 | // Notification state. Used to avoid recursive notifications. |
michael@0 | 689 | ImageStatusDiff mStatusDiff; |
michael@0 | 690 | bool mNotifying:1; |
michael@0 | 691 | |
michael@0 | 692 | // Boolean flags (clustered together to conserve space): |
michael@0 | 693 | bool mHasSize:1; // Has SetSize() been called? |
michael@0 | 694 | bool mDecodeOnDraw:1; // Decoding on draw? |
michael@0 | 695 | bool mMultipart:1; // Multipart? |
michael@0 | 696 | bool mDiscardable:1; // Is container discardable? |
michael@0 | 697 | bool mHasSourceData:1; // Do we have source data? |
michael@0 | 698 | |
michael@0 | 699 | // Do we have the frames in decoded form? |
michael@0 | 700 | bool mDecoded:1; |
michael@0 | 701 | bool mHasBeenDecoded:1; |
michael@0 | 702 | |
michael@0 | 703 | |
michael@0 | 704 | // Whether the animation can stop, due to running out |
michael@0 | 705 | // of frames, or no more owning request |
michael@0 | 706 | bool mAnimationFinished:1; |
michael@0 | 707 | |
michael@0 | 708 | // Whether we're calling Decoder::Finish() from ShutdownDecoder. |
michael@0 | 709 | bool mFinishing:1; |
michael@0 | 710 | |
michael@0 | 711 | bool mInUpdateImageContainer:1; |
michael@0 | 712 | |
michael@0 | 713 | // Whether, once we are done doing a size decode, we should immediately kick |
michael@0 | 714 | // off a full decode. |
michael@0 | 715 | bool mWantFullDecode:1; |
michael@0 | 716 | |
michael@0 | 717 | // Set when a decode worker detects an error off-main-thread. Once the error |
michael@0 | 718 | // is handled on the main thread, mError is set, but mPendingError is used to |
michael@0 | 719 | // stop decode work immediately. |
michael@0 | 720 | bool mPendingError:1; |
michael@0 | 721 | |
michael@0 | 722 | // Decoding |
michael@0 | 723 | nsresult RequestDecodeIfNeeded(nsresult aStatus, |
michael@0 | 724 | eShutdownIntent aIntent, |
michael@0 | 725 | bool aDone, |
michael@0 | 726 | bool aWasSize); |
michael@0 | 727 | nsresult WantDecodedFrames(); |
michael@0 | 728 | nsresult SyncDecode(); |
michael@0 | 729 | nsresult InitDecoder(bool aDoSizeDecode); |
michael@0 | 730 | nsresult WriteToDecoder(const char *aBuffer, uint32_t aCount, DecodeStrategy aStrategy); |
michael@0 | 731 | nsresult DecodeSomeData(uint32_t aMaxBytes, DecodeStrategy aStrategy); |
michael@0 | 732 | bool IsDecodeFinished(); |
michael@0 | 733 | TimeStamp mDrawStartTime; |
michael@0 | 734 | |
michael@0 | 735 | inline bool CanQualityScale(const gfxSize& scale); |
michael@0 | 736 | inline bool CanScale(GraphicsFilter aFilter, gfxSize aScale, uint32_t aFlags); |
michael@0 | 737 | |
michael@0 | 738 | struct ScaleResult |
michael@0 | 739 | { |
michael@0 | 740 | ScaleResult() |
michael@0 | 741 | : status(SCALE_INVALID) |
michael@0 | 742 | {} |
michael@0 | 743 | |
michael@0 | 744 | gfxSize scale; |
michael@0 | 745 | nsAutoPtr<imgFrame> frame; |
michael@0 | 746 | ScaleStatus status; |
michael@0 | 747 | }; |
michael@0 | 748 | |
michael@0 | 749 | ScaleResult mScaleResult; |
michael@0 | 750 | |
michael@0 | 751 | // We hold on to a bare pointer to a ScaleRequest while it's outstanding so |
michael@0 | 752 | // we can mark it as stopped if necessary. The ScaleWorker/DrawWorker duo |
michael@0 | 753 | // will inform us when to let go of this pointer. |
michael@0 | 754 | ScaleRequest* mScaleRequest; |
michael@0 | 755 | |
michael@0 | 756 | // Initializes imgStatusTracker and resets it on RasterImage destruction. |
michael@0 | 757 | nsAutoPtr<imgStatusTrackerInit> mStatusTrackerInit; |
michael@0 | 758 | |
michael@0 | 759 | nsresult ShutdownDecoder(eShutdownIntent aIntent); |
michael@0 | 760 | |
michael@0 | 761 | // Error handling. |
michael@0 | 762 | void DoError(); |
michael@0 | 763 | |
michael@0 | 764 | class HandleErrorWorker : public nsRunnable |
michael@0 | 765 | { |
michael@0 | 766 | public: |
michael@0 | 767 | /** |
michael@0 | 768 | * Called from decoder threads when DoError() is called, since errors can't |
michael@0 | 769 | * be handled safely off-main-thread. Dispatches an event which reinvokes |
michael@0 | 770 | * DoError on the main thread if there isn't one already pending. |
michael@0 | 771 | */ |
michael@0 | 772 | static void DispatchIfNeeded(RasterImage* aImage); |
michael@0 | 773 | |
michael@0 | 774 | NS_IMETHOD Run(); |
michael@0 | 775 | |
michael@0 | 776 | private: |
michael@0 | 777 | HandleErrorWorker(RasterImage* aImage); |
michael@0 | 778 | |
michael@0 | 779 | nsRefPtr<RasterImage> mImage; |
michael@0 | 780 | }; |
michael@0 | 781 | |
michael@0 | 782 | // Helpers |
michael@0 | 783 | bool CanDiscard(); |
michael@0 | 784 | bool CanForciblyDiscard(); |
michael@0 | 785 | bool CanForciblyDiscardAndRedecode(); |
michael@0 | 786 | bool DiscardingActive(); |
michael@0 | 787 | bool StoringSourceData() const; |
michael@0 | 788 | |
michael@0 | 789 | protected: |
michael@0 | 790 | RasterImage(imgStatusTracker* aStatusTracker = nullptr, |
michael@0 | 791 | ImageURL* aURI = nullptr); |
michael@0 | 792 | |
michael@0 | 793 | bool ShouldAnimate(); |
michael@0 | 794 | |
michael@0 | 795 | friend class ImageFactory; |
michael@0 | 796 | }; |
michael@0 | 797 | |
michael@0 | 798 | inline NS_IMETHODIMP RasterImage::GetAnimationMode(uint16_t *aAnimationMode) { |
michael@0 | 799 | return GetAnimationModeInternal(aAnimationMode); |
michael@0 | 800 | } |
michael@0 | 801 | |
michael@0 | 802 | // Asynchronous Decode Requestor |
michael@0 | 803 | // |
michael@0 | 804 | // We use this class when someone calls requestDecode() from within a decode |
michael@0 | 805 | // notification. Since requestDecode() involves modifying the decoder's state |
michael@0 | 806 | // (for example, possibly shutting down a header-only decode and starting a |
michael@0 | 807 | // full decode), we don't want to do this from inside a decoder. |
michael@0 | 808 | class imgDecodeRequestor : public nsRunnable |
michael@0 | 809 | { |
michael@0 | 810 | public: |
michael@0 | 811 | imgDecodeRequestor(RasterImage &aContainer) { |
michael@0 | 812 | mContainer = aContainer.asWeakPtr(); |
michael@0 | 813 | } |
michael@0 | 814 | NS_IMETHOD Run() { |
michael@0 | 815 | if (mContainer) |
michael@0 | 816 | mContainer->StartDecoding(); |
michael@0 | 817 | return NS_OK; |
michael@0 | 818 | } |
michael@0 | 819 | |
michael@0 | 820 | private: |
michael@0 | 821 | WeakPtr<RasterImage> mContainer; |
michael@0 | 822 | }; |
michael@0 | 823 | |
michael@0 | 824 | } // namespace image |
michael@0 | 825 | } // namespace mozilla |
michael@0 | 826 | |
michael@0 | 827 | #endif /* mozilla_imagelib_RasterImage_h_ */ |