image/src/RasterImage.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

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_ */

mercurial