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