1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Stuart Parmenter <pavlov@netscape.com>
24 : * Chris Saari <saari@netscape.com>
25 : * Asko Tontti <atontti@cc.hut.fi>
26 : * Arron Mogge <paper@animecity.nu>
27 : * Andrew Smith
28 : * Federico Mena-Quintero <federico@novell.com>
29 : * Bobby Holley <bobbyholley@gmail.com>
30 : *
31 : * Alternatively, the contents of this file may be used under the terms of
32 : * either the GNU General Public License Version 2 or later (the "GPL"), or
33 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34 : * in which case the provisions of the GPL or the LGPL are applicable instead
35 : * of those above. If you wish to allow use of your version of this file only
36 : * under the terms of either the GPL or the LGPL, and not to allow others to
37 : * use your version of this file under the terms of the MPL, indicate your
38 : * decision by deleting the provisions above and replace them with the notice
39 : * and other provisions required by the GPL or the LGPL. If you do not delete
40 : * the provisions above, a recipient may use your version of this file under
41 : * the terms of any one of the MPL, the GPL or the LGPL.
42 : *
43 : * ***** END LICENSE BLOCK ***** */
44 :
45 : #include "base/histogram.h"
46 : #include "nsComponentManagerUtils.h"
47 : #include "imgIContainerObserver.h"
48 : #include "ImageErrors.h"
49 : #include "Decoder.h"
50 : #include "imgIDecoderObserver.h"
51 : #include "RasterImage.h"
52 : #include "nsIInterfaceRequestor.h"
53 : #include "nsIInterfaceRequestorUtils.h"
54 : #include "nsAutoPtr.h"
55 : #include "nsStringStream.h"
56 : #include "prmem.h"
57 : #include "prenv.h"
58 : #include "ImageLogging.h"
59 : #include "ImageLayers.h"
60 :
61 : #include "nsPNGDecoder.h"
62 : #include "nsGIFDecoder2.h"
63 : #include "nsJPEGDecoder.h"
64 : #include "nsBMPDecoder.h"
65 : #include "nsICODecoder.h"
66 : #include "nsIconDecoder.h"
67 :
68 : #include "gfxContext.h"
69 :
70 : #include "mozilla/Preferences.h"
71 : #include "mozilla/StandardInteger.h"
72 : #include "mozilla/Telemetry.h"
73 : #include "mozilla/TimeStamp.h"
74 : #include "mozilla/ClearOnShutdown.h"
75 :
76 : using namespace mozilla;
77 : using namespace mozilla::image;
78 : using namespace mozilla::layers;
79 :
80 : // a mask for flags that will affect the decoding
81 : #define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
82 : #define DECODE_FLAGS_DEFAULT 0
83 :
84 : /* Accounting for compressed data */
85 : #if defined(PR_LOGGING)
86 1464 : static PRLogModuleInfo *gCompressedImageAccountingLog = PR_NewLogModule ("CompressedImageAccounting");
87 : #else
88 : #define gCompressedImageAccountingLog
89 : #endif
90 :
91 : // Tweakable progressive decoding parameters. These are initialized to 0 here
92 : // because otherwise, we have to initialize them in a static initializer, which
93 : // makes us slower to start up.
94 : static bool gInitializedPrefCaches = false;
95 : static PRUint32 gDecodeBytesAtATime = 0;
96 : static PRUint32 gMaxMSBeforeYield = 0;
97 : static PRUint32 gMaxBytesForSyncDecode = 0;
98 :
99 : static void
100 5 : InitPrefCaches()
101 : {
102 : Preferences::AddUintVarCache(&gDecodeBytesAtATime,
103 5 : "image.mem.decode_bytes_at_a_time", 200000);
104 : Preferences::AddUintVarCache(&gMaxMSBeforeYield,
105 5 : "image.mem.max_ms_before_yield", 400);
106 : Preferences::AddUintVarCache(&gMaxBytesForSyncDecode,
107 5 : "image.mem.max_bytes_for_sync_decode", 150000);
108 5 : gInitializedPrefCaches = true;
109 5 : }
110 :
111 : /* We define our own error checking macros here for 2 reasons:
112 : *
113 : * 1) Most of the failures we encounter here will (hopefully) be
114 : * the result of decoding failures (ie, bad data) and not code
115 : * failures. As such, we don't want to clutter up debug consoles
116 : * with spurious messages about NS_ENSURE_SUCCESS failures.
117 : *
118 : * 2) We want to set the internal error flag, shutdown properly,
119 : * and end up in an error state.
120 : *
121 : * So this macro should be called when the desired failure behavior
122 : * is to put the container into an error state and return failure.
123 : * It goes without saying that macro won't compile outside of a
124 : * non-static RasterImage method.
125 : */
126 : #define LOG_CONTAINER_ERROR \
127 : PR_BEGIN_MACRO \
128 : PR_LOG (gImgLog, PR_LOG_ERROR, \
129 : ("RasterImage: [this=%p] Error " \
130 : "detected at line %u for image of " \
131 : "type %s\n", this, __LINE__, \
132 : mSourceDataMimeType.get())); \
133 : PR_END_MACRO
134 :
135 : #define CONTAINER_ENSURE_SUCCESS(status) \
136 : PR_BEGIN_MACRO \
137 : nsresult _status = status; /* eval once */ \
138 : if (_status) { \
139 : LOG_CONTAINER_ERROR; \
140 : DoError(); \
141 : return _status; \
142 : } \
143 : PR_END_MACRO
144 :
145 : #define CONTAINER_ENSURE_TRUE(arg, rv) \
146 : PR_BEGIN_MACRO \
147 : if (!(arg)) { \
148 : LOG_CONTAINER_ERROR; \
149 : DoError(); \
150 : return rv; \
151 : } \
152 : PR_END_MACRO
153 :
154 :
155 :
156 : static int num_containers;
157 : static int num_discardable_containers;
158 : static PRInt64 total_source_bytes;
159 : static PRInt64 discardable_source_bytes;
160 :
161 : /* Are we globally disabling image discarding? */
162 : static bool
163 67 : DiscardingEnabled()
164 : {
165 : static bool inited;
166 : static bool enabled;
167 :
168 67 : if (!inited) {
169 4 : inited = true;
170 :
171 4 : enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nsnull);
172 : }
173 :
174 67 : return enabled;
175 : }
176 :
177 : namespace mozilla {
178 : namespace image {
179 :
180 1464 : /* static */ nsRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
181 :
182 : #ifndef DEBUG
183 : NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
184 : nsISupportsWeakReference)
185 : #else
186 385 : NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsIProperties,
187 : imgIContainerDebug, nsISupportsWeakReference)
188 : #endif
189 :
190 : //******************************************************************************
191 24 : RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
192 : Image(aStatusTracker), // invoke superclass's constructor
193 : mSize(0,0),
194 : mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
195 : mAnim(nsnull),
196 : mLoopCount(-1),
197 : mObserver(nsnull),
198 : mLockCount(0),
199 : mDecoder(nsnull),
200 : mDecodeRequest(this),
201 : mBytesDecoded(0),
202 : mDecodeCount(0),
203 : #ifdef DEBUG
204 : mFramesNotified(0),
205 : #endif
206 : mHasSize(false),
207 : mDecodeOnDraw(false),
208 : mMultipart(false),
209 : mDiscardable(false),
210 : mHasSourceData(false),
211 : mDecoded(false),
212 : mHasBeenDecoded(false),
213 : mInDecoder(false),
214 24 : mAnimationFinished(false)
215 : {
216 : // Set up the discard tracker node.
217 24 : mDiscardTrackerNode.curr = this;
218 24 : mDiscardTrackerNode.prev = mDiscardTrackerNode.next = nsnull;
219 24 : Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
220 :
221 : // Statistics
222 24 : num_containers++;
223 :
224 : // Register our pref observers if we haven't yet.
225 24 : if (NS_UNLIKELY(!gInitializedPrefCaches)) {
226 5 : InitPrefCaches();
227 : }
228 24 : }
229 :
230 : //******************************************************************************
231 72 : RasterImage::~RasterImage()
232 : {
233 24 : delete mAnim;
234 :
235 43 : for (unsigned int i = 0; i < mFrames.Length(); ++i)
236 19 : delete mFrames[i];
237 :
238 : // Discardable statistics
239 24 : if (mDiscardable) {
240 8 : num_discardable_containers--;
241 8 : discardable_source_bytes -= mSourceData.Length();
242 :
243 8 : PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
244 : ("CompressedImageAccounting: destroying RasterImage %p. "
245 : "Total Containers: %d, Discardable containers: %d, "
246 : "Total source bytes: %lld, Source bytes for discardable containers %lld",
247 : this,
248 : num_containers,
249 : num_discardable_containers,
250 : total_source_bytes,
251 : discardable_source_bytes));
252 : }
253 :
254 24 : DiscardTracker::Remove(&mDiscardTrackerNode);
255 :
256 : // If we have a decoder open, shut it down
257 24 : if (mDecoder) {
258 0 : nsresult rv = ShutdownDecoder(eShutdownIntent_Interrupted);
259 0 : if (NS_FAILED(rv))
260 0 : NS_WARNING("Failed to shut down decoder in destructor!");
261 : }
262 :
263 : // Total statistics
264 24 : num_containers--;
265 24 : total_source_bytes -= mSourceData.Length();
266 96 : }
267 :
268 : nsresult
269 24 : RasterImage::Init(imgIDecoderObserver *aObserver,
270 : const char* aMimeType,
271 : const char* aURIString,
272 : PRUint32 aFlags)
273 : {
274 : // We don't support re-initialization
275 24 : if (mInitialized)
276 0 : return NS_ERROR_ILLEGAL_VALUE;
277 :
278 : // Not sure an error can happen before init, but be safe
279 24 : if (mError)
280 0 : return NS_ERROR_FAILURE;
281 :
282 24 : NS_ENSURE_ARG_POINTER(aMimeType);
283 :
284 : // We must be non-discardable and non-decode-on-draw for
285 : // multipart channels
286 24 : NS_ABORT_IF_FALSE(!(aFlags & INIT_FLAG_MULTIPART) ||
287 : (!(aFlags & INIT_FLAG_DISCARDABLE) &&
288 : !(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
289 : "Can't be discardable or decode-on-draw for multipart");
290 :
291 : // Store initialization data
292 24 : mObserver = do_GetWeakReference(aObserver);
293 24 : mSourceDataMimeType.Assign(aMimeType);
294 24 : mURIString.Assign(aURIString);
295 24 : mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
296 24 : mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
297 24 : mMultipart = !!(aFlags & INIT_FLAG_MULTIPART);
298 :
299 : // Statistics
300 24 : if (mDiscardable) {
301 8 : num_discardable_containers++;
302 8 : discardable_source_bytes += mSourceData.Length();
303 : }
304 :
305 : // If we're being called from ExtractFrame (used by borderimage),
306 : // we don't actually do any decoding. Bail early.
307 : // XXX - This should be removed when we fix borderimage
308 24 : if (mSourceDataMimeType.Length() == 0) {
309 2 : mInitialized = true;
310 2 : return NS_OK;
311 : }
312 :
313 : // Instantiate the decoder
314 : //
315 : // If we're doing decode-on-draw, we want to do a quick first pass to get
316 : // the size but nothing else. We instantiate another decoder later to do
317 : // the full decoding.
318 22 : nsresult rv = InitDecoder(/* aDoSizeDecode = */ mDecodeOnDraw);
319 22 : CONTAINER_ENSURE_SUCCESS(rv);
320 :
321 : // Mark us as initialized
322 18 : mInitialized = true;
323 :
324 18 : return NS_OK;
325 : }
326 :
327 : bool
328 0 : RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
329 : {
330 0 : NS_ASSERTION(aTime <= TimeStamp::Now(),
331 : "Given time appears to be in the future");
332 :
333 0 : imgFrame* nextFrame = nsnull;
334 0 : PRUint32 currentFrameIndex = mAnim->currentAnimationFrameIndex;
335 0 : PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
336 0 : PRUint32 timeout = 0;
337 0 : mImageContainer = nsnull;
338 :
339 : // Figure out if we have the next full frame. This is more complicated than
340 : // just checking for mFrames.Length() because decoders append their frames
341 : // before they're filled in.
342 0 : NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= mFrames.Length(),
343 : "How did we get 2 indices too far by incrementing?");
344 :
345 : // If we don't have a decoder, we know we've got everything we're going to
346 : // get. If we do, we only display fully-downloaded frames; everything else
347 : // gets delayed.
348 0 : bool haveFullNextFrame = !mDecoder ||
349 0 : nextFrameIndex < mDecoder->GetCompleteFrameCount();
350 :
351 : // If we're done decoding the next frame, go ahead and display it now and
352 : // reinit with the next frame's delay time.
353 0 : if (haveFullNextFrame) {
354 0 : if (mFrames.Length() == nextFrameIndex) {
355 : // End of Animation, unless we are looping forever
356 :
357 : // If animation mode is "loop once", it's time to stop animating
358 0 : if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
359 0 : mAnimationFinished = true;
360 0 : EvaluateAnimation();
361 : }
362 :
363 : // We may have used compositingFrame to build a frame, and then copied
364 : // it back into mFrames[..]. If so, delete composite to save memory
365 0 : if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1) {
366 0 : mAnim->compositingFrame = nsnull;
367 : }
368 :
369 0 : nextFrameIndex = 0;
370 :
371 0 : if (mLoopCount > 0) {
372 0 : mLoopCount--;
373 : }
374 :
375 0 : if (!mAnimating) {
376 : // break out early if we are actually done animating
377 0 : return false;
378 : }
379 : }
380 :
381 0 : if (!(nextFrame = mFrames[nextFrameIndex])) {
382 : // something wrong with the next frame, skip it
383 0 : mAnim->currentAnimationFrameIndex = nextFrameIndex;
384 0 : return false;
385 : }
386 :
387 0 : timeout = nextFrame->GetTimeout();
388 :
389 : } else {
390 : // Uh oh, the frame we want to show is currently being decoded (partial)
391 : // Wait until the next refresh driver tick and try again
392 0 : return false;
393 : }
394 :
395 0 : if (!(timeout > 0)) {
396 0 : mAnimationFinished = true;
397 0 : EvaluateAnimation();
398 : }
399 :
400 0 : if (nextFrameIndex == 0) {
401 0 : *aDirtyRect = mAnim->firstFrameRefreshArea;
402 : } else {
403 0 : imgFrame *curFrame = mFrames[currentFrameIndex];
404 :
405 0 : if (!curFrame) {
406 0 : return false;
407 : }
408 :
409 : // Change frame
410 0 : if (NS_FAILED(DoComposite(aDirtyRect, curFrame,
411 : nextFrame, nextFrameIndex))) {
412 : // something went wrong, move on to next
413 0 : NS_WARNING("RasterImage::AdvanceFrame(): Compositing of frame failed");
414 0 : nextFrame->SetCompositingFailed(true);
415 0 : mAnim->currentAnimationFrameIndex = nextFrameIndex;
416 0 : mAnim->currentAnimationFrameTime = aTime;
417 0 : return false;
418 : }
419 :
420 0 : nextFrame->SetCompositingFailed(false);
421 : }
422 :
423 : // Set currentAnimationFrameIndex at the last possible moment
424 0 : mAnim->currentAnimationFrameIndex = nextFrameIndex;
425 0 : mAnim->currentAnimationFrameTime = aTime;
426 :
427 0 : return true;
428 : }
429 :
430 : //******************************************************************************
431 : // [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
432 : NS_IMETHODIMP_(void)
433 0 : RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
434 : {
435 0 : if (!mAnimating || !ShouldAnimate()) {
436 0 : return;
437 : }
438 :
439 0 : EnsureAnimExists();
440 :
441 : // only advance the frame if the current time is greater than or
442 : // equal to the current frame's end time.
443 0 : TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
444 0 : bool frameAdvanced = false;
445 :
446 : // The dirtyRect variable will contain an accumulation of the sub-rectangles
447 : // that are dirty for each frame we advance in AdvanceFrame().
448 0 : nsIntRect dirtyRect;
449 :
450 0 : while (currentFrameEndTime <= aTime) {
451 0 : TimeStamp oldFrameEndTime = currentFrameEndTime;
452 0 : nsIntRect frameDirtyRect;
453 0 : bool didAdvance = AdvanceFrame(aTime, &frameDirtyRect);
454 0 : frameAdvanced = frameAdvanced || didAdvance;
455 0 : currentFrameEndTime = GetCurrentImgFrameEndTime();
456 :
457 : // Accumulate the dirty area.
458 0 : dirtyRect = dirtyRect.Union(frameDirtyRect);
459 :
460 : // if we didn't advance a frame, and our frame end time didn't change,
461 : // then we need to break out of this loop & wait for the frame(s)
462 : // to finish downloading
463 0 : if (!frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
464 0 : break;
465 : }
466 : }
467 :
468 0 : if (frameAdvanced) {
469 0 : nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
470 :
471 0 : if (!observer) {
472 0 : NS_ERROR("Refreshing image after its imgRequest is gone");
473 0 : StopAnimation();
474 : return;
475 : }
476 :
477 : // Notify listeners that our frame has actually changed, but do this only
478 : // once for all frames that we've now passed (if AdvanceFrame() was called
479 : // more than once).
480 : #ifdef DEBUG
481 0 : mFramesNotified++;
482 : #endif
483 :
484 0 : observer->FrameChanged(nsnull, this, &dirtyRect);
485 : }
486 : }
487 :
488 : //******************************************************************************
489 : /* [noscript] imgIContainer extractFrame(PRUint32 aWhichFrame,
490 : * [const] in nsIntRect aRegion,
491 : * in PRUint32 aFlags); */
492 : NS_IMETHODIMP
493 3 : RasterImage::ExtractFrame(PRUint32 aWhichFrame,
494 : const nsIntRect &aRegion,
495 : PRUint32 aFlags,
496 : imgIContainer **_retval)
497 : {
498 3 : NS_ENSURE_ARG_POINTER(_retval);
499 :
500 : nsresult rv;
501 :
502 3 : if (aWhichFrame > FRAME_MAX_VALUE)
503 0 : return NS_ERROR_INVALID_ARG;
504 :
505 3 : if (mError)
506 1 : return NS_ERROR_FAILURE;
507 :
508 : // Disallowed in the API
509 2 : if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
510 0 : return NS_ERROR_FAILURE;
511 :
512 : // Make a new container. This should switch to another class with bug 505959.
513 4 : nsRefPtr<RasterImage> img(new RasterImage());
514 :
515 : // We don't actually have a mimetype in this case. The empty string tells the
516 : // init routine not to try to instantiate a decoder. This should be fixed in
517 : // bug 505959.
518 2 : img->Init(nsnull, "", "", INIT_FLAG_NONE);
519 2 : img->SetSize(aRegion.width, aRegion.height);
520 2 : img->mDecoded = true; // Also, we need to mark the image as decoded
521 2 : img->mHasBeenDecoded = true;
522 2 : img->mFrameDecodeFlags = aFlags & DECODE_FLAGS_MASK;
523 :
524 2 : if (img->mFrameDecodeFlags != mFrameDecodeFlags) {
525 : // if we can't discard, then we're screwed; we have no way
526 : // to re-decode. Similarly if we aren't allowed to do a sync
527 : // decode.
528 0 : if (!(aFlags & FLAG_SYNC_DECODE))
529 0 : return NS_ERROR_NOT_AVAILABLE;
530 0 : if (!CanForciblyDiscard() || mDecoder || mAnim)
531 0 : return NS_ERROR_NOT_AVAILABLE;
532 0 : ForceDiscard();
533 :
534 0 : mFrameDecodeFlags = img->mFrameDecodeFlags;
535 : }
536 :
537 : // If a synchronous decode was requested, do it
538 2 : if (aFlags & FLAG_SYNC_DECODE) {
539 2 : rv = SyncDecode();
540 2 : CONTAINER_ENSURE_SUCCESS(rv);
541 : }
542 :
543 : // Get the frame. If it's not there, it's probably the caller's fault for
544 : // not waiting for the data to be loaded from the network or not passing
545 : // FLAG_SYNC_DECODE
546 : PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
547 2 : 0 : GetCurrentImgFrameIndex();
548 2 : imgFrame *frame = GetDrawableImgFrame(frameIndex);
549 2 : if (!frame) {
550 0 : *_retval = nsnull;
551 0 : return NS_ERROR_FAILURE;
552 : }
553 :
554 : // The frame can be smaller than the image. We want to extract only the part
555 : // of the frame that actually exists.
556 2 : nsIntRect framerect = frame->GetRect();
557 2 : framerect.IntersectRect(framerect, aRegion);
558 :
559 2 : if (framerect.IsEmpty())
560 0 : return NS_ERROR_NOT_AVAILABLE;
561 :
562 4 : nsAutoPtr<imgFrame> subframe;
563 2 : rv = frame->Extract(framerect, getter_Transfers(subframe));
564 2 : if (NS_FAILED(rv))
565 0 : return rv;
566 :
567 2 : img->mFrames.AppendElement(subframe.forget());
568 :
569 2 : img->mStatusTracker->RecordLoaded();
570 2 : img->mStatusTracker->RecordDecoded();
571 :
572 2 : *_retval = img.forget().get();
573 :
574 2 : return NS_OK;
575 : }
576 :
577 : //******************************************************************************
578 : /* readonly attribute PRInt32 width; */
579 : NS_IMETHODIMP
580 7 : RasterImage::GetWidth(PRInt32 *aWidth)
581 : {
582 7 : NS_ENSURE_ARG_POINTER(aWidth);
583 :
584 7 : if (mError) {
585 1 : *aWidth = 0;
586 1 : return NS_ERROR_FAILURE;
587 : }
588 :
589 6 : *aWidth = mSize.width;
590 6 : return NS_OK;
591 : }
592 :
593 : //******************************************************************************
594 : /* readonly attribute PRInt32 height; */
595 : NS_IMETHODIMP
596 7 : RasterImage::GetHeight(PRInt32 *aHeight)
597 : {
598 7 : NS_ENSURE_ARG_POINTER(aHeight);
599 :
600 7 : if (mError) {
601 1 : *aHeight = 0;
602 1 : return NS_ERROR_FAILURE;
603 : }
604 :
605 6 : *aHeight = mSize.height;
606 6 : return NS_OK;
607 : }
608 :
609 : //******************************************************************************
610 : /* unsigned short GetType(); */
611 : NS_IMETHODIMP
612 0 : RasterImage::GetType(PRUint16 *aType)
613 : {
614 0 : NS_ENSURE_ARG_POINTER(aType);
615 :
616 0 : *aType = GetType();
617 0 : return NS_OK;
618 : }
619 :
620 : //******************************************************************************
621 : /* [noscript, notxpcom] PRUint16 GetType(); */
622 : NS_IMETHODIMP_(PRUint16)
623 29 : RasterImage::GetType()
624 : {
625 29 : return imgIContainer::TYPE_RASTER;
626 : }
627 :
628 : imgFrame*
629 64 : RasterImage::GetImgFrameNoDecode(PRUint32 framenum)
630 : {
631 64 : if (!mAnim) {
632 59 : NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
633 59 : return mFrames.SafeElementAt(0, nsnull);
634 : }
635 5 : if (mAnim->lastCompositedFrameIndex == PRInt32(framenum))
636 0 : return mAnim->compositingFrame;
637 5 : return mFrames.SafeElementAt(framenum, nsnull);
638 : }
639 :
640 : imgFrame*
641 32 : RasterImage::GetImgFrame(PRUint32 framenum)
642 : {
643 32 : nsresult rv = WantDecodedFrames();
644 32 : CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nsnull);
645 32 : return GetImgFrameNoDecode(framenum);
646 : }
647 :
648 : imgFrame*
649 21 : RasterImage::GetDrawableImgFrame(PRUint32 framenum)
650 : {
651 21 : imgFrame *frame = GetImgFrame(framenum);
652 :
653 : // We will return a paletted frame if it's not marked as compositing failed
654 : // so we can catch crashes for reasons we haven't investigated.
655 21 : if (frame && frame->GetCompositingFailed())
656 0 : return nsnull;
657 21 : return frame;
658 : }
659 :
660 : PRUint32
661 27 : RasterImage::GetCurrentImgFrameIndex() const
662 : {
663 27 : if (mAnim)
664 1 : return mAnim->currentAnimationFrameIndex;
665 :
666 26 : return 0;
667 : }
668 :
669 : TimeStamp
670 0 : RasterImage::GetCurrentImgFrameEndTime() const
671 : {
672 0 : imgFrame* currentFrame = mFrames[mAnim->currentAnimationFrameIndex];
673 0 : TimeStamp currentFrameTime = mAnim->currentAnimationFrameTime;
674 0 : PRInt64 timeout = currentFrame->GetTimeout();
675 :
676 0 : if (timeout < 0) {
677 : // We need to return a sentinel value in this case, because our logic
678 : // doesn't work correctly if we have a negative timeout value. The reason
679 : // this positive infinity was chosen was because it works with the loop in
680 : // RequestRefresh() above.
681 0 : return TimeStamp() + TimeDuration::FromMilliseconds(UINT64_MAX);
682 : }
683 :
684 0 : TimeDuration durationOfTimeout = TimeDuration::FromMilliseconds(timeout);
685 0 : TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
686 :
687 0 : return currentFrameEndTime;
688 : }
689 :
690 : imgFrame*
691 2 : RasterImage::GetCurrentImgFrame()
692 : {
693 2 : return GetImgFrame(GetCurrentImgFrameIndex());
694 : }
695 :
696 : imgFrame*
697 0 : RasterImage::GetCurrentDrawableImgFrame()
698 : {
699 0 : return GetDrawableImgFrame(GetCurrentImgFrameIndex());
700 : }
701 :
702 : //******************************************************************************
703 : /* readonly attribute boolean currentFrameIsOpaque; */
704 : NS_IMETHODIMP
705 0 : RasterImage::GetCurrentFrameIsOpaque(bool *aIsOpaque)
706 : {
707 0 : NS_ENSURE_ARG_POINTER(aIsOpaque);
708 :
709 0 : if (mError)
710 0 : return NS_ERROR_FAILURE;
711 :
712 : // See if we can get an image frame
713 0 : imgFrame *curframe = GetCurrentImgFrame();
714 :
715 : // If we don't get a frame, the safe answer is "not opaque"
716 0 : if (!curframe)
717 0 : *aIsOpaque = false;
718 :
719 : // Otherwise, we can make a more intelligent decision
720 : else {
721 0 : *aIsOpaque = !curframe->GetNeedsBackground();
722 :
723 : // We are also transparent if the current frame's size doesn't cover our
724 : // entire area.
725 0 : nsIntRect framerect = curframe->GetRect();
726 0 : *aIsOpaque = *aIsOpaque && framerect.IsEqualInterior(nsIntRect(0, 0, mSize.width, mSize.height));
727 : }
728 :
729 0 : return NS_OK;
730 : }
731 :
732 : void
733 2 : RasterImage::GetCurrentFrameRect(nsIntRect& aRect)
734 : {
735 : // Get the current frame
736 2 : imgFrame* curframe = GetCurrentImgFrame();
737 :
738 : // If we have the frame, use that rectangle
739 2 : if (curframe) {
740 2 : aRect = curframe->GetRect();
741 : } else {
742 : // If the frame doesn't exist, we pass the empty rectangle. It's not clear
743 : // whether this is appropriate in general, but at the moment the only
744 : // consumer of this method is imgStatusTracker (when it wants to figure out
745 : // dirty rectangles to send out batched observer updates). This should
746 : // probably be revisited when we fix bug 503973.
747 0 : aRect.MoveTo(0, 0);
748 0 : aRect.SizeTo(0, 0);
749 : }
750 2 : }
751 :
752 : PRUint32
753 4 : RasterImage::GetCurrentFrameIndex()
754 : {
755 4 : return GetCurrentImgFrameIndex();
756 : }
757 :
758 : PRUint32
759 230 : RasterImage::GetNumFrames()
760 : {
761 230 : return mFrames.Length();
762 : }
763 :
764 : //******************************************************************************
765 : /* readonly attribute boolean animated; */
766 : NS_IMETHODIMP
767 16 : RasterImage::GetAnimated(bool *aAnimated)
768 : {
769 16 : if (mError)
770 2 : return NS_ERROR_FAILURE;
771 :
772 14 : NS_ENSURE_ARG_POINTER(aAnimated);
773 :
774 : // If we have mAnim, we can know for sure
775 14 : if (mAnim) {
776 0 : *aAnimated = true;
777 0 : return NS_OK;
778 : }
779 :
780 : // Otherwise, we need to have been decoded to know for sure, since if we were
781 : // decoded at least once mAnim would have been created for animated images
782 14 : if (!mHasBeenDecoded)
783 12 : return NS_ERROR_NOT_AVAILABLE;
784 :
785 : // We know for sure
786 2 : *aAnimated = false;
787 :
788 2 : return NS_OK;
789 : }
790 :
791 :
792 : //******************************************************************************
793 : /* [noscript] gfxImageSurface copyFrame(in PRUint32 aWhichFrame,
794 : * in PRUint32 aFlags); */
795 : NS_IMETHODIMP
796 19 : RasterImage::CopyFrame(PRUint32 aWhichFrame,
797 : PRUint32 aFlags,
798 : gfxImageSurface **_retval)
799 : {
800 19 : if (aWhichFrame > FRAME_MAX_VALUE)
801 0 : return NS_ERROR_INVALID_ARG;
802 :
803 19 : if (mError)
804 0 : return NS_ERROR_FAILURE;
805 :
806 : // Disallowed in the API
807 19 : if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
808 0 : return NS_ERROR_FAILURE;
809 :
810 : nsresult rv;
811 :
812 19 : PRUint32 desiredDecodeFlags = aFlags & DECODE_FLAGS_MASK;
813 19 : if (desiredDecodeFlags != mFrameDecodeFlags) {
814 : // if we can't discard, then we're screwed; we have no way
815 : // to re-decode. Similarly if we aren't allowed to do a sync
816 : // decode.
817 0 : if (!(aFlags & FLAG_SYNC_DECODE))
818 0 : return NS_ERROR_NOT_AVAILABLE;
819 0 : if (!CanForciblyDiscard() || mDecoder || mAnim)
820 0 : return NS_ERROR_NOT_AVAILABLE;
821 0 : ForceDiscard();
822 :
823 0 : mFrameDecodeFlags = desiredDecodeFlags;
824 : }
825 :
826 : // If requested, synchronously flush any data we have lying around to the decoder
827 19 : if (aFlags & FLAG_SYNC_DECODE) {
828 19 : rv = SyncDecode();
829 19 : CONTAINER_ENSURE_SUCCESS(rv);
830 : }
831 :
832 19 : NS_ENSURE_ARG_POINTER(_retval);
833 :
834 : // Get the frame. If it's not there, it's probably the caller's fault for
835 : // not waiting for the data to be loaded from the network or not passing
836 : // FLAG_SYNC_DECODE
837 : PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
838 19 : 0 : GetCurrentImgFrameIndex();
839 19 : imgFrame *frame = GetDrawableImgFrame(frameIndex);
840 19 : if (!frame) {
841 0 : *_retval = nsnull;
842 0 : return NS_ERROR_FAILURE;
843 : }
844 :
845 38 : nsRefPtr<gfxPattern> pattern;
846 19 : frame->GetPattern(getter_AddRefs(pattern));
847 19 : nsIntRect intframerect = frame->GetRect();
848 19 : gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height);
849 :
850 : // Create a 32-bit image surface of our size, but draw using the frame's
851 : // rect, implicitly padding the frame out to the image's size.
852 : nsRefPtr<gfxImageSurface> imgsurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
853 57 : gfxASurface::ImageFormatARGB32);
854 38 : gfxContext ctx(imgsurface);
855 19 : ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
856 19 : ctx.Rectangle(framerect);
857 19 : ctx.Translate(framerect.TopLeft());
858 19 : ctx.SetPattern(pattern);
859 19 : ctx.Fill();
860 :
861 19 : *_retval = imgsurface.forget().get();
862 19 : return NS_OK;
863 : }
864 :
865 : //******************************************************************************
866 : /* [noscript] gfxASurface getFrame(in PRUint32 aWhichFrame,
867 : * in PRUint32 aFlags); */
868 : NS_IMETHODIMP
869 0 : RasterImage::GetFrame(PRUint32 aWhichFrame,
870 : PRUint32 aFlags,
871 : gfxASurface **_retval)
872 : {
873 0 : if (aWhichFrame > FRAME_MAX_VALUE)
874 0 : return NS_ERROR_INVALID_ARG;
875 :
876 0 : if (mError)
877 0 : return NS_ERROR_FAILURE;
878 :
879 : // Disallowed in the API
880 0 : if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
881 0 : return NS_ERROR_FAILURE;
882 :
883 0 : nsresult rv = NS_OK;
884 :
885 0 : if (mDecoded) {
886 : // If we have decoded data, and it is not a perfect match for what we are
887 : // looking for, we must discard to be able to generate the proper data.
888 0 : PRUint32 desiredDecodeFlags = aFlags & DECODE_FLAGS_MASK;
889 0 : if (desiredDecodeFlags != mFrameDecodeFlags) {
890 : // if we can't discard, then we're screwed; we have no way
891 : // to re-decode. Similarly if we aren't allowed to do a sync
892 : // decode.
893 0 : if (!(aFlags & FLAG_SYNC_DECODE))
894 0 : return NS_ERROR_NOT_AVAILABLE;
895 0 : if (!CanForciblyDiscard() || mDecoder || mAnim)
896 0 : return NS_ERROR_NOT_AVAILABLE;
897 :
898 0 : ForceDiscard();
899 :
900 0 : mFrameDecodeFlags = desiredDecodeFlags;
901 : }
902 : }
903 :
904 : // If the caller requested a synchronous decode, do it
905 0 : if (aFlags & FLAG_SYNC_DECODE) {
906 0 : rv = SyncDecode();
907 0 : CONTAINER_ENSURE_SUCCESS(rv);
908 : }
909 :
910 : // Get the frame. If it's not there, it's probably the caller's fault for
911 : // not waiting for the data to be loaded from the network or not passing
912 : // FLAG_SYNC_DECODE
913 : PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
914 0 : 0 : GetCurrentImgFrameIndex();
915 0 : imgFrame *frame = GetDrawableImgFrame(frameIndex);
916 0 : if (!frame) {
917 0 : *_retval = nsnull;
918 0 : return NS_ERROR_FAILURE;
919 : }
920 :
921 0 : nsRefPtr<gfxASurface> framesurf;
922 :
923 : // If this frame covers the entire image, we can just reuse its existing
924 : // surface.
925 0 : nsIntRect framerect = frame->GetRect();
926 0 : if (framerect.x == 0 && framerect.y == 0 &&
927 : framerect.width == mSize.width &&
928 : framerect.height == mSize.height)
929 0 : rv = frame->GetSurface(getter_AddRefs(framesurf));
930 :
931 : // The image doesn't have a surface because it's been optimized away. Create
932 : // one.
933 0 : if (!framesurf) {
934 0 : nsRefPtr<gfxImageSurface> imgsurf;
935 0 : rv = CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
936 0 : framesurf = imgsurf;
937 : }
938 :
939 0 : *_retval = framesurf.forget().get();
940 :
941 0 : return rv;
942 : }
943 :
944 :
945 : NS_IMETHODIMP
946 0 : RasterImage::GetImageContainer(ImageContainer **_retval)
947 : {
948 0 : if (mImageContainer) {
949 0 : *_retval = mImageContainer;
950 0 : NS_ADDREF(*_retval);
951 0 : return NS_OK;
952 : }
953 :
954 0 : CairoImage::Data cairoData;
955 0 : nsRefPtr<gfxASurface> imageSurface;
956 0 : nsresult rv = GetFrame(FRAME_CURRENT, FLAG_SYNC_DECODE, getter_AddRefs(imageSurface));
957 0 : NS_ENSURE_SUCCESS(rv, rv);
958 :
959 0 : cairoData.mSurface = imageSurface;
960 0 : GetWidth(&cairoData.mSize.width);
961 0 : GetHeight(&cairoData.mSize.height);
962 :
963 0 : mImageContainer = LayerManager::CreateImageContainer();
964 :
965 : // Now create a CairoImage to display the surface.
966 0 : layers::Image::Format cairoFormat = layers::Image::CAIRO_SURFACE;
967 0 : nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&cairoFormat, 1);
968 0 : NS_ASSERTION(image, "Failed to create Image");
969 :
970 0 : NS_ASSERTION(image->GetFormat() == cairoFormat, "Wrong format");
971 0 : static_cast<CairoImage*>(image.get())->SetData(cairoData);
972 0 : mImageContainer->SetCurrentImage(image);
973 :
974 0 : *_retval = mImageContainer;
975 0 : NS_ADDREF(*_retval);
976 0 : return NS_OK;
977 : }
978 :
979 : size_t
980 6 : RasterImage::HeapSizeOfSourceWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const
981 : {
982 : // n == 0 is possible for two reasons.
983 : // - This is a zero-length image.
984 : // - We're on a platform where moz_malloc_size_of always returns 0.
985 : // In either case the fallback works appropriately.
986 6 : size_t n = mSourceData.SizeOfExcludingThis(aMallocSizeOf);
987 6 : if (n == 0) {
988 0 : n = mSourceData.Length();
989 0 : NS_ABORT_IF_FALSE(StoringSourceData() || (n == 0),
990 : "Non-zero source data size when we aren't storing it?");
991 : }
992 6 : return n;
993 : }
994 :
995 : static size_t
996 18 : SizeOfDecodedWithComputedFallbackIfHeap(
997 : const nsTArray<imgFrame*>& aFrames, gfxASurface::MemoryLocation aLocation,
998 : nsMallocSizeOfFun aMallocSizeOf)
999 : {
1000 18 : size_t n = 0;
1001 30 : for (PRUint32 i = 0; i < aFrames.Length(); ++i) {
1002 12 : imgFrame* frame = aFrames.SafeElementAt(i, nsnull);
1003 12 : NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
1004 12 : n += frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
1005 : }
1006 :
1007 18 : return n;
1008 : }
1009 :
1010 : size_t
1011 6 : RasterImage::HeapSizeOfDecodedWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const
1012 : {
1013 : return SizeOfDecodedWithComputedFallbackIfHeap(
1014 6 : mFrames, gfxASurface::MEMORY_IN_PROCESS_HEAP, aMallocSizeOf);
1015 : }
1016 :
1017 : size_t
1018 6 : RasterImage::NonHeapSizeOfDecoded() const
1019 : {
1020 6 : return SizeOfDecodedWithComputedFallbackIfHeap(mFrames, gfxASurface::MEMORY_IN_PROCESS_NONHEAP, NULL);
1021 : }
1022 :
1023 : size_t
1024 6 : RasterImage::OutOfProcessSizeOfDecoded() const
1025 : {
1026 6 : return SizeOfDecodedWithComputedFallbackIfHeap(mFrames, gfxASurface::MEMORY_OUT_OF_PROCESS, NULL);
1027 : }
1028 :
1029 : void
1030 0 : RasterImage::DeleteImgFrame(PRUint32 framenum)
1031 : {
1032 0 : NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Deleting invalid frame!");
1033 :
1034 0 : delete mFrames[framenum];
1035 0 : mFrames[framenum] = nsnull;
1036 0 : }
1037 :
1038 : nsresult
1039 17 : RasterImage::InternalAddFrameHelper(PRUint32 framenum, imgFrame *aFrame,
1040 : PRUint8 **imageData, PRUint32 *imageLength,
1041 : PRUint32 **paletteData, PRUint32 *paletteLength)
1042 : {
1043 17 : NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
1044 17 : if (framenum > mFrames.Length())
1045 0 : return NS_ERROR_INVALID_ARG;
1046 :
1047 34 : nsAutoPtr<imgFrame> frame(aFrame);
1048 :
1049 17 : if (paletteData && paletteLength)
1050 2 : frame->GetPaletteData(paletteData, paletteLength);
1051 :
1052 17 : frame->GetImageData(imageData, imageLength);
1053 :
1054 : // We are in the middle of decoding. This will be unlocked when we finish the
1055 : // decoder->Write() call.
1056 17 : frame->LockImageData();
1057 :
1058 17 : mFrames.InsertElementAt(framenum, frame.forget());
1059 :
1060 17 : return NS_OK;
1061 : }
1062 :
1063 : nsresult
1064 17 : RasterImage::InternalAddFrame(PRUint32 framenum,
1065 : PRInt32 aX, PRInt32 aY,
1066 : PRInt32 aWidth, PRInt32 aHeight,
1067 : gfxASurface::gfxImageFormat aFormat,
1068 : PRUint8 aPaletteDepth,
1069 : PRUint8 **imageData,
1070 : PRUint32 *imageLength,
1071 : PRUint32 **paletteData,
1072 : PRUint32 *paletteLength)
1073 : {
1074 : // We assume that we're in the middle of decoding because we unlock the
1075 : // previous frame when we create a new frame, and only when decoding do we
1076 : // lock frames.
1077 17 : NS_ABORT_IF_FALSE(mInDecoder, "Only decoders may add frames!");
1078 :
1079 17 : NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
1080 17 : if (framenum > mFrames.Length())
1081 0 : return NS_ERROR_INVALID_ARG;
1082 :
1083 34 : nsAutoPtr<imgFrame> frame(new imgFrame());
1084 :
1085 17 : nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
1086 17 : NS_ENSURE_SUCCESS(rv, rv);
1087 :
1088 : // We know we are in a decoder. Therefore, we must unlock the previous frame
1089 : // when we move on to decoding into the next frame.
1090 17 : if (mFrames.Length() > 0) {
1091 2 : imgFrame *prevframe = mFrames.ElementAt(mFrames.Length() - 1);
1092 2 : prevframe->UnlockImageData();
1093 : }
1094 :
1095 17 : if (mFrames.Length() == 0) {
1096 : return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
1097 15 : paletteData, paletteLength);
1098 : }
1099 :
1100 2 : if (mFrames.Length() == 1) {
1101 : // Since we're about to add our second frame, initialize animation stuff
1102 1 : EnsureAnimExists();
1103 :
1104 : // If we dispose of the first frame by clearing it, then the
1105 : // First Frame's refresh area is all of itself.
1106 : // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
1107 1 : PRInt32 frameDisposalMethod = mFrames[0]->GetFrameDisposalMethod();
1108 1 : if (frameDisposalMethod == kDisposeClear ||
1109 : frameDisposalMethod == kDisposeRestorePrevious)
1110 1 : mAnim->firstFrameRefreshArea = mFrames[0]->GetRect();
1111 : }
1112 :
1113 : // Calculate firstFrameRefreshArea
1114 : // Some gifs are huge but only have a small area that they animate
1115 : // We only need to refresh that small area when Frame 0 comes around again
1116 2 : nsIntRect frameRect = frame->GetRect();
1117 : mAnim->firstFrameRefreshArea.UnionRect(mAnim->firstFrameRefreshArea,
1118 2 : frameRect);
1119 :
1120 : rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
1121 2 : paletteData, paletteLength);
1122 :
1123 : // We may be able to start animating, if we now have enough frames
1124 2 : EvaluateAnimation();
1125 :
1126 2 : return rv;
1127 : }
1128 :
1129 : nsresult
1130 21 : RasterImage::SetSize(PRInt32 aWidth, PRInt32 aHeight)
1131 : {
1132 21 : if (mError)
1133 0 : return NS_ERROR_FAILURE;
1134 :
1135 : // Ensure that we have positive values
1136 : // XXX - Why isn't the size unsigned? Should this be changed?
1137 21 : if ((aWidth < 0) || (aHeight < 0))
1138 0 : return NS_ERROR_INVALID_ARG;
1139 :
1140 : // if we already have a size, check the new size against the old one
1141 21 : if (mHasSize &&
1142 : ((aWidth != mSize.width) || (aHeight != mSize.height))) {
1143 :
1144 : // Alter the warning depending on whether the channel is multipart
1145 0 : if (!mMultipart)
1146 0 : NS_WARNING("Image changed size on redecode! This should not happen!");
1147 : else
1148 0 : NS_WARNING("Multipart channel sent an image of a different size");
1149 :
1150 : // Make the decoder aware of the error so that it doesn't try to call
1151 : // FinishInternal during ShutdownDecoder.
1152 0 : if (mDecoder)
1153 0 : mDecoder->PostResizeError();
1154 :
1155 0 : DoError();
1156 0 : return NS_ERROR_UNEXPECTED;
1157 : }
1158 :
1159 : // Set the size and flag that we have it
1160 21 : mSize.SizeTo(aWidth, aHeight);
1161 21 : mHasSize = true;
1162 :
1163 21 : return NS_OK;
1164 : }
1165 :
1166 : nsresult
1167 17 : RasterImage::EnsureFrame(PRUint32 aFrameNum, PRInt32 aX, PRInt32 aY,
1168 : PRInt32 aWidth, PRInt32 aHeight,
1169 : gfxASurface::gfxImageFormat aFormat,
1170 : PRUint8 aPaletteDepth,
1171 : PRUint8 **imageData, PRUint32 *imageLength,
1172 : PRUint32 **paletteData, PRUint32 *paletteLength)
1173 : {
1174 17 : if (mError)
1175 0 : return NS_ERROR_FAILURE;
1176 :
1177 17 : NS_ENSURE_ARG_POINTER(imageData);
1178 17 : NS_ENSURE_ARG_POINTER(imageLength);
1179 17 : NS_ABORT_IF_FALSE(aFrameNum <= mFrames.Length(), "Invalid frame index!");
1180 :
1181 17 : if (aPaletteDepth > 0) {
1182 2 : NS_ENSURE_ARG_POINTER(paletteData);
1183 2 : NS_ENSURE_ARG_POINTER(paletteLength);
1184 : }
1185 :
1186 17 : if (aFrameNum > mFrames.Length())
1187 0 : return NS_ERROR_INVALID_ARG;
1188 :
1189 : // Adding a frame that doesn't already exist.
1190 17 : if (aFrameNum == mFrames.Length())
1191 : return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
1192 : aPaletteDepth, imageData, imageLength,
1193 17 : paletteData, paletteLength);
1194 :
1195 0 : imgFrame *frame = GetImgFrame(aFrameNum);
1196 0 : if (!frame)
1197 : return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
1198 : aPaletteDepth, imageData, imageLength,
1199 0 : paletteData, paletteLength);
1200 :
1201 : // See if we can re-use the frame that already exists.
1202 0 : nsIntRect rect = frame->GetRect();
1203 0 : if (rect.x == aX && rect.y == aY && rect.width == aWidth &&
1204 0 : rect.height == aHeight && frame->GetFormat() == aFormat &&
1205 0 : frame->GetPaletteDepth() == aPaletteDepth) {
1206 0 : frame->GetImageData(imageData, imageLength);
1207 0 : if (paletteData) {
1208 0 : frame->GetPaletteData(paletteData, paletteLength);
1209 : }
1210 :
1211 : // We can re-use the frame if it has image data.
1212 0 : if (*imageData && paletteData && *paletteData) {
1213 0 : return NS_OK;
1214 : }
1215 0 : if (*imageData && !paletteData) {
1216 0 : return NS_OK;
1217 : }
1218 : }
1219 :
1220 0 : DeleteImgFrame(aFrameNum);
1221 : return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
1222 : aPaletteDepth, imageData, imageLength,
1223 0 : paletteData, paletteLength);
1224 : }
1225 :
1226 : nsresult
1227 15 : RasterImage::EnsureFrame(PRUint32 aFramenum, PRInt32 aX, PRInt32 aY,
1228 : PRInt32 aWidth, PRInt32 aHeight,
1229 : gfxASurface::gfxImageFormat aFormat,
1230 : PRUint8** imageData, PRUint32* imageLength)
1231 : {
1232 : return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
1233 : /* aPaletteDepth = */ 0, imageData, imageLength,
1234 : /* aPaletteData = */ nsnull,
1235 15 : /* aPaletteLength = */ nsnull);
1236 : }
1237 :
1238 : void
1239 32 : RasterImage::FrameUpdated(PRUint32 aFrameNum, nsIntRect &aUpdatedRect)
1240 : {
1241 32 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1242 :
1243 32 : imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
1244 32 : NS_ABORT_IF_FALSE(frame, "Calling FrameUpdated on frame that doesn't exist!");
1245 :
1246 32 : frame->ImageUpdated(aUpdatedRect);
1247 : // The image has changed, so we need to invalidate our cached ImageContainer.
1248 32 : mImageContainer = NULL;
1249 32 : }
1250 :
1251 : nsresult
1252 4 : RasterImage::SetFrameDisposalMethod(PRUint32 aFrameNum,
1253 : PRInt32 aDisposalMethod)
1254 : {
1255 4 : if (mError)
1256 0 : return NS_ERROR_FAILURE;
1257 :
1258 4 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1259 4 : if (aFrameNum >= mFrames.Length())
1260 0 : return NS_ERROR_INVALID_ARG;
1261 :
1262 4 : imgFrame *frame = GetImgFrame(aFrameNum);
1263 4 : NS_ABORT_IF_FALSE(frame,
1264 : "Calling SetFrameDisposalMethod on frame that doesn't exist!");
1265 4 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1266 :
1267 4 : frame->SetFrameDisposalMethod(aDisposalMethod);
1268 :
1269 4 : return NS_OK;
1270 : }
1271 :
1272 : nsresult
1273 4 : RasterImage::SetFrameTimeout(PRUint32 aFrameNum, PRInt32 aTimeout)
1274 : {
1275 4 : if (mError)
1276 0 : return NS_ERROR_FAILURE;
1277 :
1278 4 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1279 4 : if (aFrameNum >= mFrames.Length())
1280 0 : return NS_ERROR_INVALID_ARG;
1281 :
1282 4 : imgFrame *frame = GetImgFrame(aFrameNum);
1283 4 : NS_ABORT_IF_FALSE(frame, "Calling SetFrameTimeout on frame that doesn't exist!");
1284 4 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1285 :
1286 4 : frame->SetTimeout(aTimeout);
1287 :
1288 4 : return NS_OK;
1289 : }
1290 :
1291 : nsresult
1292 0 : RasterImage::SetFrameBlendMethod(PRUint32 aFrameNum, PRInt32 aBlendMethod)
1293 : {
1294 0 : if (mError)
1295 0 : return NS_ERROR_FAILURE;
1296 :
1297 0 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1298 0 : if (aFrameNum >= mFrames.Length())
1299 0 : return NS_ERROR_INVALID_ARG;
1300 :
1301 0 : imgFrame *frame = GetImgFrame(aFrameNum);
1302 0 : NS_ABORT_IF_FALSE(frame, "Calling SetFrameBlendMethod on frame that doesn't exist!");
1303 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1304 :
1305 0 : frame->SetBlendMethod(aBlendMethod);
1306 :
1307 0 : return NS_OK;
1308 : }
1309 :
1310 : nsresult
1311 1 : RasterImage::SetFrameHasNoAlpha(PRUint32 aFrameNum)
1312 : {
1313 1 : if (mError)
1314 0 : return NS_ERROR_FAILURE;
1315 :
1316 1 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1317 1 : if (aFrameNum >= mFrames.Length())
1318 0 : return NS_ERROR_INVALID_ARG;
1319 :
1320 1 : imgFrame *frame = GetImgFrame(aFrameNum);
1321 1 : NS_ABORT_IF_FALSE(frame, "Calling SetFrameHasNoAlpha on frame that doesn't exist!");
1322 1 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1323 :
1324 1 : frame->SetHasNoAlpha();
1325 :
1326 1 : return NS_OK;
1327 : }
1328 :
1329 : nsresult
1330 15 : RasterImage::DecodingComplete()
1331 : {
1332 15 : if (mError)
1333 0 : return NS_ERROR_FAILURE;
1334 :
1335 : // Flag that we're done decoding.
1336 : // XXX - these should probably be combined when we fix animated image
1337 : // discarding with bug 500402.
1338 15 : mDecoded = true;
1339 15 : mHasBeenDecoded = true;
1340 :
1341 : nsresult rv;
1342 :
1343 : // We now have one of the qualifications for discarding. Re-evaluate.
1344 15 : if (CanDiscard()) {
1345 0 : NS_ABORT_IF_FALSE(!DiscardingActive(),
1346 : "We shouldn't have been discardable before this");
1347 0 : rv = DiscardTracker::Reset(&mDiscardTrackerNode);
1348 0 : CONTAINER_ENSURE_SUCCESS(rv);
1349 : }
1350 :
1351 : // If there's only 1 frame, optimize it. Optimizing animated images
1352 : // is not supported.
1353 : //
1354 : // We don't optimize the frame for multipart images because we reuse
1355 : // the frame.
1356 15 : if ((mFrames.Length() == 1) && !mMultipart) {
1357 14 : rv = mFrames[0]->Optimize();
1358 14 : NS_ENSURE_SUCCESS(rv, rv);
1359 : }
1360 :
1361 15 : return NS_OK;
1362 : }
1363 :
1364 : //******************************************************************************
1365 : /* void StartAnimation () */
1366 : nsresult
1367 0 : RasterImage::StartAnimation()
1368 : {
1369 0 : if (mError)
1370 0 : return NS_ERROR_FAILURE;
1371 :
1372 0 : NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
1373 :
1374 0 : EnsureAnimExists();
1375 :
1376 0 : imgFrame* currentFrame = GetCurrentImgFrame();
1377 0 : if (currentFrame) {
1378 0 : if (currentFrame->GetTimeout() < 0) { // -1 means display this frame forever
1379 0 : mAnimationFinished = true;
1380 0 : return NS_ERROR_ABORT;
1381 : }
1382 :
1383 : // We need to set the time that this initial frame was first displayed, as
1384 : // this is used in AdvanceFrame().
1385 0 : mAnim->currentAnimationFrameTime = TimeStamp::Now();
1386 : }
1387 :
1388 0 : return NS_OK;
1389 : }
1390 :
1391 : //******************************************************************************
1392 : /* void stopAnimation (); */
1393 : nsresult
1394 0 : RasterImage::StopAnimation()
1395 : {
1396 0 : NS_ABORT_IF_FALSE(mAnimating, "Should be animating!");
1397 :
1398 0 : if (mError)
1399 0 : return NS_ERROR_FAILURE;
1400 :
1401 0 : return NS_OK;
1402 : }
1403 :
1404 : //******************************************************************************
1405 : /* void resetAnimation (); */
1406 : NS_IMETHODIMP
1407 0 : RasterImage::ResetAnimation()
1408 : {
1409 0 : if (mError)
1410 0 : return NS_ERROR_FAILURE;
1411 :
1412 0 : if (mAnimationMode == kDontAnimMode ||
1413 0 : !mAnim || mAnim->currentAnimationFrameIndex == 0)
1414 0 : return NS_OK;
1415 :
1416 0 : mAnimationFinished = false;
1417 :
1418 0 : if (mAnimating)
1419 0 : StopAnimation();
1420 :
1421 0 : mAnim->lastCompositedFrameIndex = -1;
1422 0 : mAnim->currentAnimationFrameIndex = 0;
1423 0 : mImageContainer = nsnull;
1424 :
1425 : // Note - We probably want to kick off a redecode somewhere around here when
1426 : // we fix bug 500402.
1427 :
1428 : // Update display if we were animating before
1429 0 : nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
1430 0 : if (mAnimating && observer)
1431 0 : observer->FrameChanged(nsnull, this, &(mAnim->firstFrameRefreshArea));
1432 :
1433 0 : if (ShouldAnimate()) {
1434 0 : StartAnimation();
1435 : // The animation may not have been running before, if mAnimationFinished
1436 : // was false (before we changed it to true in this function). So, mark the
1437 : // animation as running.
1438 0 : mAnimating = true;
1439 : }
1440 :
1441 0 : return NS_OK;
1442 : }
1443 :
1444 : void
1445 4 : RasterImage::SetLoopCount(PRInt32 aLoopCount)
1446 : {
1447 4 : if (mError)
1448 0 : return;
1449 :
1450 : // -1 infinite
1451 : // 0 no looping, one iteration
1452 : // 1 one loop, two iterations
1453 : // ...
1454 4 : mLoopCount = aLoopCount;
1455 : }
1456 :
1457 : nsresult
1458 57 : RasterImage::AddSourceData(const char *aBuffer, PRUint32 aCount)
1459 : {
1460 57 : if (mError)
1461 0 : return NS_ERROR_FAILURE;
1462 :
1463 57 : NS_ENSURE_ARG_POINTER(aBuffer);
1464 57 : nsresult rv = NS_OK;
1465 :
1466 : // We should not call this if we're not initialized
1467 57 : NS_ABORT_IF_FALSE(mInitialized, "Calling AddSourceData() on uninitialized "
1468 : "RasterImage!");
1469 :
1470 : // We should not call this if we're already finished adding source data
1471 57 : NS_ABORT_IF_FALSE(!mHasSourceData, "Calling AddSourceData() after calling "
1472 : "sourceDataComplete()!");
1473 :
1474 : // This call should come straight from necko - no reentrancy allowed
1475 57 : NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
1476 :
1477 : // If we're not storing source data, write it directly to the decoder
1478 57 : if (!StoringSourceData()) {
1479 53 : rv = WriteToDecoder(aBuffer, aCount);
1480 53 : CONTAINER_ENSURE_SUCCESS(rv);
1481 :
1482 : // We're not storing source data, so this data is probably coming straight
1483 : // from the network. In this case, we want to display data as soon as we
1484 : // get it, so we want to flush invalidations after every write.
1485 106 : nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
1486 53 : mInDecoder = true;
1487 53 : mDecoder->FlushInvalidations();
1488 53 : mInDecoder = false;
1489 : }
1490 :
1491 : // Otherwise, we're storing data in the source buffer
1492 : else {
1493 :
1494 : // Store the data
1495 4 : char *newElem = mSourceData.AppendElements(aBuffer, aCount);
1496 4 : if (!newElem)
1497 0 : return NS_ERROR_OUT_OF_MEMORY;
1498 :
1499 : // If there's a decoder open, that means we want to do more decoding.
1500 : // Wake up the worker.
1501 4 : if (mDecoder) {
1502 4 : DecodeWorker::Singleton()->RequestDecode(this);
1503 : }
1504 : }
1505 :
1506 : // Statistics
1507 57 : total_source_bytes += aCount;
1508 57 : if (mDiscardable)
1509 4 : discardable_source_bytes += aCount;
1510 57 : PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
1511 : ("CompressedImageAccounting: Added compressed data to RasterImage %p (%s). "
1512 : "Total Containers: %d, Discardable containers: %d, "
1513 : "Total source bytes: %lld, Source bytes for discardable containers %lld",
1514 : this,
1515 : mSourceDataMimeType.get(),
1516 : num_containers,
1517 : num_discardable_containers,
1518 : total_source_bytes,
1519 : discardable_source_bytes));
1520 :
1521 57 : return NS_OK;
1522 : }
1523 :
1524 : /* Note! buf must be declared as char buf[9]; */
1525 : // just used for logging and hashing the header
1526 : static void
1527 0 : get_header_str (char *buf, char *data, PRSize data_len)
1528 : {
1529 : int i;
1530 : int n;
1531 : static char hex[] = "0123456789abcdef";
1532 :
1533 0 : n = data_len < 4 ? data_len : 4;
1534 :
1535 0 : for (i = 0; i < n; i++) {
1536 0 : buf[i * 2] = hex[(data[i] >> 4) & 0x0f];
1537 0 : buf[i * 2 + 1] = hex[data[i] & 0x0f];
1538 : }
1539 :
1540 0 : buf[i * 2] = 0;
1541 0 : }
1542 :
1543 : nsresult
1544 22 : RasterImage::SourceDataComplete()
1545 : {
1546 22 : if (mError)
1547 4 : return NS_ERROR_FAILURE;
1548 :
1549 : // If we've been called before, ignore. Otherwise, flag that we have everything
1550 18 : if (mHasSourceData)
1551 0 : return NS_OK;
1552 18 : mHasSourceData = true;
1553 :
1554 : // This call should come straight from necko - no reentrancy allowed
1555 18 : NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
1556 :
1557 : // If we're not storing any source data, then all the data was written
1558 : // directly to the decoder in the AddSourceData() calls. This means we're
1559 : // done, so we can shut down the decoder.
1560 18 : if (!StoringSourceData()) {
1561 14 : nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
1562 14 : CONTAINER_ENSURE_SUCCESS(rv);
1563 : }
1564 :
1565 : // If there's a decoder open, synchronously decode the beginning of the image
1566 : // to check for errors and get the image's size. (If we already have the
1567 : // image's size, this does nothing.) Then kick off an async decode of the
1568 : // rest of the image.
1569 17 : if (mDecoder) {
1570 4 : nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
1571 4 : CONTAINER_ENSURE_SUCCESS(rv);
1572 : }
1573 :
1574 : // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
1575 : // finish decoding this image.
1576 17 : if (mDecoder) {
1577 0 : DecodeWorker::Singleton()->RequestDecode(this);
1578 : }
1579 :
1580 : // Free up any extra space in the backing buffer
1581 17 : mSourceData.Compact();
1582 :
1583 : // Log header information
1584 17 : if (PR_LOG_TEST(gCompressedImageAccountingLog, PR_LOG_DEBUG)) {
1585 : char buf[9];
1586 0 : get_header_str(buf, mSourceData.Elements(), mSourceData.Length());
1587 0 : PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
1588 : ("CompressedImageAccounting: RasterImage::SourceDataComplete() - data "
1589 : "is done for container %p (%s) - header %p is 0x%s (length %d)",
1590 : this,
1591 : mSourceDataMimeType.get(),
1592 : mSourceData.Elements(),
1593 : buf,
1594 : mSourceData.Length()));
1595 : }
1596 :
1597 : // We now have one of the qualifications for discarding. Re-evaluate.
1598 17 : if (CanDiscard()) {
1599 0 : nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
1600 0 : CONTAINER_ENSURE_SUCCESS(rv);
1601 : }
1602 17 : return NS_OK;
1603 : }
1604 :
1605 : nsresult
1606 0 : RasterImage::NewSourceData()
1607 : {
1608 : nsresult rv;
1609 :
1610 0 : if (mError)
1611 0 : return NS_ERROR_FAILURE;
1612 :
1613 : // The source data should be complete before calling this
1614 0 : NS_ABORT_IF_FALSE(mHasSourceData,
1615 : "Calling NewSourceData before SourceDataComplete!");
1616 0 : if (!mHasSourceData)
1617 0 : return NS_ERROR_ILLEGAL_VALUE;
1618 :
1619 : // Only supported for multipart channels. It wouldn't be too hard to change this,
1620 : // but it would involve making sure that things worked for decode-on-draw and
1621 : // discarding. Presently there's no need for this, so we don't.
1622 0 : NS_ABORT_IF_FALSE(mMultipart, "NewSourceData not supported for multipart");
1623 0 : if (!mMultipart)
1624 0 : return NS_ERROR_ILLEGAL_VALUE;
1625 :
1626 : // We're multipart, so we shouldn't be storing source data
1627 0 : NS_ABORT_IF_FALSE(!StoringSourceData(),
1628 : "Shouldn't be storing source data for multipart");
1629 :
1630 : // We're not storing the source data and we got SourceDataComplete. We should
1631 : // have shut down the previous decoder
1632 0 : NS_ABORT_IF_FALSE(!mDecoder, "Shouldn't have a decoder in NewSourceData");
1633 :
1634 : // The decoder was shut down and we didn't flag an error, so we should be decoded
1635 0 : NS_ABORT_IF_FALSE(mDecoded, "Should be decoded in NewSourceData");
1636 :
1637 : // Reset some flags
1638 0 : mDecoded = false;
1639 0 : mHasSourceData = false;
1640 :
1641 : // We're decode-on-load here. Open up a new decoder just like what happens when
1642 : // we call Init() for decode-on-load images.
1643 0 : rv = InitDecoder(/* aDoSizeDecode = */ false);
1644 0 : CONTAINER_ENSURE_SUCCESS(rv);
1645 :
1646 0 : return NS_OK;
1647 : }
1648 :
1649 : nsresult
1650 0 : RasterImage::SetSourceSizeHint(PRUint32 sizeHint)
1651 : {
1652 0 : if (sizeHint && StoringSourceData())
1653 0 : return mSourceData.SetCapacity(sizeHint) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1654 0 : return NS_OK;
1655 : }
1656 :
1657 : //******************************************************************************
1658 : // DoComposite gets called when the timer for animation get fired and we have to
1659 : // update the composited frame of the animation.
1660 : nsresult
1661 0 : RasterImage::DoComposite(nsIntRect* aDirtyRect,
1662 : imgFrame* aPrevFrame,
1663 : imgFrame* aNextFrame,
1664 : PRInt32 aNextFrameIndex)
1665 : {
1666 0 : NS_ENSURE_ARG_POINTER(aDirtyRect);
1667 0 : NS_ENSURE_ARG_POINTER(aPrevFrame);
1668 0 : NS_ENSURE_ARG_POINTER(aNextFrame);
1669 :
1670 0 : PRInt32 prevFrameDisposalMethod = aPrevFrame->GetFrameDisposalMethod();
1671 0 : if (prevFrameDisposalMethod == kDisposeRestorePrevious &&
1672 0 : !mAnim->compositingPrevFrame)
1673 0 : prevFrameDisposalMethod = kDisposeClear;
1674 :
1675 0 : nsIntRect prevFrameRect = aPrevFrame->GetRect();
1676 : bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
1677 : prevFrameRect.width == mSize.width &&
1678 0 : prevFrameRect.height == mSize.height);
1679 :
1680 : // Optimization: DisposeClearAll if the previous frame is the same size as
1681 : // container and it's clearing itself
1682 0 : if (isFullPrevFrame &&
1683 : (prevFrameDisposalMethod == kDisposeClear))
1684 0 : prevFrameDisposalMethod = kDisposeClearAll;
1685 :
1686 0 : PRInt32 nextFrameDisposalMethod = aNextFrame->GetFrameDisposalMethod();
1687 0 : nsIntRect nextFrameRect = aNextFrame->GetRect();
1688 : bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
1689 : nextFrameRect.width == mSize.width &&
1690 0 : nextFrameRect.height == mSize.height);
1691 :
1692 0 : if (!aNextFrame->GetIsPaletted()) {
1693 : // Optimization: Skip compositing if the previous frame wants to clear the
1694 : // whole image
1695 0 : if (prevFrameDisposalMethod == kDisposeClearAll) {
1696 0 : aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
1697 0 : return NS_OK;
1698 : }
1699 :
1700 : // Optimization: Skip compositing if this frame is the same size as the
1701 : // container and it's fully drawing over prev frame (no alpha)
1702 0 : if (isFullNextFrame &&
1703 : (nextFrameDisposalMethod != kDisposeRestorePrevious) &&
1704 0 : !aNextFrame->GetHasAlpha()) {
1705 0 : aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
1706 0 : return NS_OK;
1707 : }
1708 : }
1709 :
1710 : // Calculate area that needs updating
1711 0 : switch (prevFrameDisposalMethod) {
1712 : default:
1713 : case kDisposeNotSpecified:
1714 : case kDisposeKeep:
1715 0 : *aDirtyRect = nextFrameRect;
1716 0 : break;
1717 :
1718 : case kDisposeClearAll:
1719 : // Whole image container is cleared
1720 0 : aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
1721 0 : break;
1722 :
1723 : case kDisposeClear:
1724 : // Calc area that needs to be redrawn (the combination of previous and
1725 : // this frame)
1726 : // XXX - This could be done with multiple framechanged calls
1727 : // Having prevFrame way at the top of the image, and nextFrame
1728 : // way at the bottom, and both frames being small, we'd be
1729 : // telling framechanged to refresh the whole image when only two
1730 : // small areas are needed.
1731 0 : aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
1732 0 : break;
1733 :
1734 : case kDisposeRestorePrevious:
1735 0 : aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
1736 0 : break;
1737 : }
1738 :
1739 : // Optimization:
1740 : // Skip compositing if the last composited frame is this frame
1741 : // (Only one composited frame was made for this animation. Example:
1742 : // Only Frame 3 of a 10 frame image required us to build a composite frame
1743 : // On the second loop, we do not need to rebuild the frame
1744 : // since it's still sitting in compositingFrame)
1745 0 : if (mAnim->lastCompositedFrameIndex == aNextFrameIndex) {
1746 0 : return NS_OK;
1747 : }
1748 :
1749 0 : bool needToBlankComposite = false;
1750 :
1751 : // Create the Compositing Frame
1752 0 : if (!mAnim->compositingFrame) {
1753 0 : mAnim->compositingFrame = new imgFrame();
1754 : nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
1755 0 : gfxASurface::ImageFormatARGB32);
1756 0 : if (NS_FAILED(rv)) {
1757 0 : mAnim->compositingFrame = nsnull;
1758 0 : return rv;
1759 : }
1760 0 : needToBlankComposite = true;
1761 0 : } else if (aNextFrameIndex != mAnim->lastCompositedFrameIndex+1) {
1762 :
1763 : // If we are not drawing on top of last composited frame,
1764 : // then we are building a new composite frame, so let's clear it first.
1765 0 : needToBlankComposite = true;
1766 : }
1767 :
1768 : // More optimizations possible when next frame is not transparent
1769 : // But if the next frame has kDisposeRestorePrevious,
1770 : // this "no disposal" optimization is not possible,
1771 : // because the frame in "after disposal operation" state
1772 : // needs to be stored in compositingFrame, so it can be
1773 : // copied into compositingPrevFrame later.
1774 0 : bool doDisposal = true;
1775 0 : if (!aNextFrame->GetHasAlpha() &&
1776 : nextFrameDisposalMethod != kDisposeRestorePrevious) {
1777 0 : if (isFullNextFrame) {
1778 : // Optimization: No need to dispose prev.frame when
1779 : // next frame is full frame and not transparent.
1780 0 : doDisposal = false;
1781 : // No need to blank the composite frame
1782 0 : needToBlankComposite = false;
1783 : } else {
1784 0 : if ((prevFrameRect.x >= nextFrameRect.x) &&
1785 : (prevFrameRect.y >= nextFrameRect.y) &&
1786 : (prevFrameRect.x + prevFrameRect.width <= nextFrameRect.x + nextFrameRect.width) &&
1787 : (prevFrameRect.y + prevFrameRect.height <= nextFrameRect.y + nextFrameRect.height)) {
1788 : // Optimization: No need to dispose prev.frame when
1789 : // next frame fully overlaps previous frame.
1790 0 : doDisposal = false;
1791 : }
1792 : }
1793 : }
1794 :
1795 0 : if (doDisposal) {
1796 : // Dispose of previous: clear, restore, or keep (copy)
1797 0 : switch (prevFrameDisposalMethod) {
1798 : case kDisposeClear:
1799 0 : if (needToBlankComposite) {
1800 : // If we just created the composite, it could have anything in it's
1801 : // buffer. Clear whole frame
1802 0 : ClearFrame(mAnim->compositingFrame);
1803 : } else {
1804 : // Only blank out previous frame area (both color & Mask/Alpha)
1805 0 : ClearFrame(mAnim->compositingFrame, prevFrameRect);
1806 : }
1807 0 : break;
1808 :
1809 : case kDisposeClearAll:
1810 0 : ClearFrame(mAnim->compositingFrame);
1811 0 : break;
1812 :
1813 : case kDisposeRestorePrevious:
1814 : // It would be better to copy only the area changed back to
1815 : // compositingFrame.
1816 0 : if (mAnim->compositingPrevFrame) {
1817 0 : CopyFrameImage(mAnim->compositingPrevFrame, mAnim->compositingFrame);
1818 :
1819 : // destroy only if we don't need it for this frame's disposal
1820 0 : if (nextFrameDisposalMethod != kDisposeRestorePrevious)
1821 0 : mAnim->compositingPrevFrame = nsnull;
1822 : } else {
1823 0 : ClearFrame(mAnim->compositingFrame);
1824 : }
1825 0 : break;
1826 :
1827 : default:
1828 : // Copy previous frame into compositingFrame before we put the new frame on top
1829 : // Assumes that the previous frame represents a full frame (it could be
1830 : // smaller in size than the container, as long as the frame before it erased
1831 : // itself)
1832 : // Note: Frame 1 never gets into DoComposite(), so (aNextFrameIndex - 1) will
1833 : // always be a valid frame number.
1834 0 : if (mAnim->lastCompositedFrameIndex != aNextFrameIndex - 1) {
1835 0 : if (isFullPrevFrame && !aPrevFrame->GetIsPaletted()) {
1836 : // Just copy the bits
1837 0 : CopyFrameImage(aPrevFrame, mAnim->compositingFrame);
1838 : } else {
1839 0 : if (needToBlankComposite) {
1840 : // Only blank composite when prev is transparent or not full.
1841 0 : if (aPrevFrame->GetHasAlpha() || !isFullPrevFrame) {
1842 0 : ClearFrame(mAnim->compositingFrame);
1843 : }
1844 : }
1845 0 : DrawFrameTo(aPrevFrame, mAnim->compositingFrame, prevFrameRect);
1846 : }
1847 : }
1848 : }
1849 0 : } else if (needToBlankComposite) {
1850 : // If we just created the composite, it could have anything in it's
1851 : // buffers. Clear them
1852 0 : ClearFrame(mAnim->compositingFrame);
1853 : }
1854 :
1855 : // Check if the frame we are composing wants the previous image restored afer
1856 : // it is done. Don't store it (again) if last frame wanted its image restored
1857 : // too
1858 0 : if ((nextFrameDisposalMethod == kDisposeRestorePrevious) &&
1859 : (prevFrameDisposalMethod != kDisposeRestorePrevious)) {
1860 : // We are storing the whole image.
1861 : // It would be better if we just stored the area that nextFrame is going to
1862 : // overwrite.
1863 0 : if (!mAnim->compositingPrevFrame) {
1864 0 : mAnim->compositingPrevFrame = new imgFrame();
1865 : nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
1866 0 : gfxASurface::ImageFormatARGB32);
1867 0 : if (NS_FAILED(rv)) {
1868 0 : mAnim->compositingPrevFrame = nsnull;
1869 0 : return rv;
1870 : }
1871 : }
1872 :
1873 0 : CopyFrameImage(mAnim->compositingFrame, mAnim->compositingPrevFrame);
1874 : }
1875 :
1876 : // blit next frame into it's correct spot
1877 0 : DrawFrameTo(aNextFrame, mAnim->compositingFrame, nextFrameRect);
1878 :
1879 : // Set timeout of CompositeFrame to timeout of frame we just composed
1880 : // Bug 177948
1881 0 : PRInt32 timeout = aNextFrame->GetTimeout();
1882 0 : mAnim->compositingFrame->SetTimeout(timeout);
1883 :
1884 : // Tell the image that it is fully 'downloaded'.
1885 0 : nsresult rv = mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect());
1886 0 : if (NS_FAILED(rv)) {
1887 0 : return rv;
1888 : }
1889 :
1890 : // We don't want to keep composite images for 8bit frames.
1891 : // Also this optimization won't work if the next frame has
1892 : // kDisposeRestorePrevious, because it would need to be restored
1893 : // into "after prev disposal but before next blend" state,
1894 : // not into empty frame.
1895 0 : if (isFullNextFrame && mAnimationMode == kNormalAnimMode && mLoopCount != 0 &&
1896 : nextFrameDisposalMethod != kDisposeRestorePrevious &&
1897 0 : !aNextFrame->GetIsPaletted()) {
1898 : // We have a composited full frame
1899 : // Store the composited frame into the mFrames[..] so we don't have to
1900 : // continuously re-build it
1901 : // Then set the previous frame's disposal to CLEAR_ALL so we just draw the
1902 : // frame next time around
1903 0 : if (CopyFrameImage(mAnim->compositingFrame, aNextFrame)) {
1904 0 : aPrevFrame->SetFrameDisposalMethod(kDisposeClearAll);
1905 0 : mAnim->lastCompositedFrameIndex = -1;
1906 0 : return NS_OK;
1907 : }
1908 : }
1909 :
1910 0 : mAnim->lastCompositedFrameIndex = aNextFrameIndex;
1911 :
1912 0 : return NS_OK;
1913 : }
1914 :
1915 : //******************************************************************************
1916 : // Fill aFrame with black. Does also clears the mask.
1917 : void
1918 0 : RasterImage::ClearFrame(imgFrame *aFrame)
1919 : {
1920 0 : if (!aFrame)
1921 0 : return;
1922 :
1923 0 : nsresult rv = aFrame->LockImageData();
1924 0 : if (NS_FAILED(rv))
1925 0 : return;
1926 :
1927 0 : nsRefPtr<gfxASurface> surf;
1928 0 : aFrame->GetSurface(getter_AddRefs(surf));
1929 :
1930 : // Erase the surface to transparent
1931 0 : gfxContext ctx(surf);
1932 0 : ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
1933 0 : ctx.Paint();
1934 :
1935 0 : aFrame->UnlockImageData();
1936 : }
1937 :
1938 : //******************************************************************************
1939 : void
1940 0 : RasterImage::ClearFrame(imgFrame *aFrame, nsIntRect &aRect)
1941 : {
1942 0 : if (!aFrame || aRect.width <= 0 || aRect.height <= 0)
1943 0 : return;
1944 :
1945 0 : nsresult rv = aFrame->LockImageData();
1946 0 : if (NS_FAILED(rv))
1947 0 : return;
1948 :
1949 0 : nsRefPtr<gfxASurface> surf;
1950 0 : aFrame->GetSurface(getter_AddRefs(surf));
1951 :
1952 : // Erase the destination rectangle to transparent
1953 0 : gfxContext ctx(surf);
1954 0 : ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
1955 0 : ctx.Rectangle(gfxRect(aRect.x, aRect.y, aRect.width, aRect.height));
1956 0 : ctx.Fill();
1957 :
1958 0 : aFrame->UnlockImageData();
1959 : }
1960 :
1961 :
1962 : //******************************************************************************
1963 : // Whether we succeed or fail will not cause a crash, and there's not much
1964 : // we can do about a failure, so there we don't return a nsresult
1965 : bool
1966 0 : RasterImage::CopyFrameImage(imgFrame *aSrcFrame,
1967 : imgFrame *aDstFrame)
1968 : {
1969 : PRUint8* aDataSrc;
1970 : PRUint8* aDataDest;
1971 : PRUint32 aDataLengthSrc;
1972 : PRUint32 aDataLengthDest;
1973 :
1974 0 : if (!aSrcFrame || !aDstFrame)
1975 0 : return false;
1976 :
1977 0 : if (NS_FAILED(aDstFrame->LockImageData()))
1978 0 : return false;
1979 :
1980 : // Copy Image Over
1981 0 : aSrcFrame->GetImageData(&aDataSrc, &aDataLengthSrc);
1982 0 : aDstFrame->GetImageData(&aDataDest, &aDataLengthDest);
1983 0 : if (!aDataDest || !aDataSrc || aDataLengthDest != aDataLengthSrc) {
1984 0 : aDstFrame->UnlockImageData();
1985 0 : return false;
1986 : }
1987 0 : memcpy(aDataDest, aDataSrc, aDataLengthSrc);
1988 0 : aDstFrame->UnlockImageData();
1989 :
1990 0 : return true;
1991 : }
1992 :
1993 : //******************************************************************************
1994 : /*
1995 : * aSrc is the current frame being drawn,
1996 : * aDst is the composition frame where the current frame is drawn into.
1997 : * aSrcRect is the size of the current frame, and the position of that frame
1998 : * in the composition frame.
1999 : */
2000 : nsresult
2001 0 : RasterImage::DrawFrameTo(imgFrame *aSrc,
2002 : imgFrame *aDst,
2003 : nsIntRect& aSrcRect)
2004 : {
2005 0 : NS_ENSURE_ARG_POINTER(aSrc);
2006 0 : NS_ENSURE_ARG_POINTER(aDst);
2007 :
2008 0 : nsIntRect dstRect = aDst->GetRect();
2009 :
2010 : // According to both AGIF and APNG specs, offsets are unsigned
2011 0 : if (aSrcRect.x < 0 || aSrcRect.y < 0) {
2012 0 : NS_WARNING("RasterImage::DrawFrameTo: negative offsets not allowed");
2013 0 : return NS_ERROR_FAILURE;
2014 : }
2015 : // Outside the destination frame, skip it
2016 0 : if ((aSrcRect.x > dstRect.width) || (aSrcRect.y > dstRect.height)) {
2017 0 : return NS_OK;
2018 : }
2019 :
2020 0 : if (aSrc->GetIsPaletted()) {
2021 : // Larger than the destination frame, clip it
2022 0 : PRInt32 width = NS_MIN(aSrcRect.width, dstRect.width - aSrcRect.x);
2023 0 : PRInt32 height = NS_MIN(aSrcRect.height, dstRect.height - aSrcRect.y);
2024 :
2025 : // The clipped image must now fully fit within destination image frame
2026 0 : NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
2027 : (aSrcRect.x + width <= dstRect.width) &&
2028 : (aSrcRect.y + height <= dstRect.height),
2029 : "RasterImage::DrawFrameTo: Invalid aSrcRect");
2030 :
2031 : // clipped image size may be smaller than source, but not larger
2032 0 : NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
2033 : "RasterImage::DrawFrameTo: source must be smaller than dest");
2034 :
2035 0 : if (NS_FAILED(aDst->LockImageData()))
2036 0 : return NS_ERROR_FAILURE;
2037 :
2038 : // Get pointers to image data
2039 : PRUint32 size;
2040 : PRUint8 *srcPixels;
2041 : PRUint32 *colormap;
2042 : PRUint32 *dstPixels;
2043 :
2044 0 : aSrc->GetImageData(&srcPixels, &size);
2045 0 : aSrc->GetPaletteData(&colormap, &size);
2046 0 : aDst->GetImageData((PRUint8 **)&dstPixels, &size);
2047 0 : if (!srcPixels || !dstPixels || !colormap) {
2048 0 : aDst->UnlockImageData();
2049 0 : return NS_ERROR_FAILURE;
2050 : }
2051 :
2052 : // Skip to the right offset
2053 0 : dstPixels += aSrcRect.x + (aSrcRect.y * dstRect.width);
2054 0 : if (!aSrc->GetHasAlpha()) {
2055 0 : for (PRInt32 r = height; r > 0; --r) {
2056 0 : for (PRInt32 c = 0; c < width; c++) {
2057 0 : dstPixels[c] = colormap[srcPixels[c]];
2058 : }
2059 : // Go to the next row in the source resp. destination image
2060 0 : srcPixels += aSrcRect.width;
2061 0 : dstPixels += dstRect.width;
2062 : }
2063 : } else {
2064 0 : for (PRInt32 r = height; r > 0; --r) {
2065 0 : for (PRInt32 c = 0; c < width; c++) {
2066 0 : const PRUint32 color = colormap[srcPixels[c]];
2067 0 : if (color)
2068 0 : dstPixels[c] = color;
2069 : }
2070 : // Go to the next row in the source resp. destination image
2071 0 : srcPixels += aSrcRect.width;
2072 0 : dstPixels += dstRect.width;
2073 : }
2074 : }
2075 :
2076 0 : aDst->UnlockImageData();
2077 0 : return NS_OK;
2078 : }
2079 :
2080 0 : nsRefPtr<gfxPattern> srcPatt;
2081 0 : aSrc->GetPattern(getter_AddRefs(srcPatt));
2082 :
2083 0 : aDst->LockImageData();
2084 0 : nsRefPtr<gfxASurface> dstSurf;
2085 0 : aDst->GetSurface(getter_AddRefs(dstSurf));
2086 :
2087 0 : gfxContext dst(dstSurf);
2088 0 : dst.Translate(gfxPoint(aSrcRect.x, aSrcRect.y));
2089 0 : dst.Rectangle(gfxRect(0, 0, aSrcRect.width, aSrcRect.height), true);
2090 :
2091 : // first clear the surface if the blend flag says so
2092 0 : PRInt32 blendMethod = aSrc->GetBlendMethod();
2093 0 : if (blendMethod == kBlendSource) {
2094 0 : gfxContext::GraphicsOperator defaultOperator = dst.CurrentOperator();
2095 0 : dst.SetOperator(gfxContext::OPERATOR_CLEAR);
2096 0 : dst.Fill();
2097 0 : dst.SetOperator(defaultOperator);
2098 : }
2099 0 : dst.SetPattern(srcPatt);
2100 0 : dst.Paint();
2101 :
2102 0 : aDst->UnlockImageData();
2103 :
2104 0 : return NS_OK;
2105 : }
2106 :
2107 :
2108 : /********* Methods to implement lazy allocation of nsIProperties object *************/
2109 : NS_IMETHODIMP
2110 0 : RasterImage::Get(const char *prop, const nsIID & iid, void * *result)
2111 : {
2112 0 : if (!mProperties)
2113 0 : return NS_ERROR_FAILURE;
2114 0 : return mProperties->Get(prop, iid, result);
2115 : }
2116 :
2117 : NS_IMETHODIMP
2118 0 : RasterImage::Set(const char *prop, nsISupports *value)
2119 : {
2120 0 : if (!mProperties)
2121 0 : mProperties = do_CreateInstance("@mozilla.org/properties;1");
2122 0 : if (!mProperties)
2123 0 : return NS_ERROR_OUT_OF_MEMORY;
2124 0 : return mProperties->Set(prop, value);
2125 : }
2126 :
2127 : NS_IMETHODIMP
2128 0 : RasterImage::Has(const char *prop, bool *_retval)
2129 : {
2130 0 : NS_ENSURE_ARG_POINTER(_retval);
2131 0 : if (!mProperties) {
2132 0 : *_retval = false;
2133 0 : return NS_OK;
2134 : }
2135 0 : return mProperties->Has(prop, _retval);
2136 : }
2137 :
2138 : NS_IMETHODIMP
2139 0 : RasterImage::Undefine(const char *prop)
2140 : {
2141 0 : if (!mProperties)
2142 0 : return NS_ERROR_FAILURE;
2143 0 : return mProperties->Undefine(prop);
2144 : }
2145 :
2146 : NS_IMETHODIMP
2147 0 : RasterImage::GetKeys(PRUint32 *count, char ***keys)
2148 : {
2149 0 : if (!mProperties) {
2150 0 : *count = 0;
2151 0 : *keys = nsnull;
2152 0 : return NS_OK;
2153 : }
2154 0 : return mProperties->GetKeys(count, keys);
2155 : }
2156 :
2157 : void
2158 0 : RasterImage::Discard(bool force)
2159 : {
2160 : // We should be ok for discard
2161 0 : NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!");
2162 :
2163 : // We should never discard when we have an active decoder
2164 0 : NS_ABORT_IF_FALSE(!mDecoder, "Asked to discard with open decoder!");
2165 :
2166 : // As soon as an image becomes animated, it becomes non-discardable and any
2167 : // timers are cancelled.
2168 0 : NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!");
2169 :
2170 : // For post-operation logging
2171 0 : int old_frame_count = mFrames.Length();
2172 :
2173 : // Delete all the decoded frames, then clear the array.
2174 0 : for (int i = 0; i < old_frame_count; ++i)
2175 0 : delete mFrames[i];
2176 0 : mFrames.Clear();
2177 :
2178 : // Flag that we no longer have decoded frames for this image
2179 0 : mDecoded = false;
2180 :
2181 : // Notify that we discarded
2182 0 : nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
2183 0 : if (observer)
2184 0 : observer->OnDiscard(nsnull);
2185 :
2186 0 : if (force)
2187 0 : DiscardTracker::Remove(&mDiscardTrackerNode);
2188 :
2189 : // Log
2190 0 : PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG,
2191 : ("CompressedImageAccounting: discarded uncompressed image "
2192 : "data from RasterImage %p (%s) - %d frames (cached count: %d); "
2193 : "Total Containers: %d, Discardable containers: %d, "
2194 : "Total source bytes: %lld, Source bytes for discardable containers %lld",
2195 : this,
2196 : mSourceDataMimeType.get(),
2197 : old_frame_count,
2198 : mFrames.Length(),
2199 : num_containers,
2200 : num_discardable_containers,
2201 : total_source_bytes,
2202 : discardable_source_bytes));
2203 0 : }
2204 :
2205 : // Helper method to determine if we can discard an image
2206 : bool
2207 67 : RasterImage::CanDiscard() {
2208 67 : return (DiscardingEnabled() && // Globally enabled...
2209 : mDiscardable && // ...Enabled at creation time...
2210 : (mLockCount == 0) && // ...not temporarily disabled...
2211 : mHasSourceData && // ...have the source data...
2212 67 : mDecoded); // ...and have something to discard.
2213 : }
2214 :
2215 : bool
2216 0 : RasterImage::CanForciblyDiscard() {
2217 : return mDiscardable && // ...Enabled at creation time...
2218 0 : mHasSourceData; // ...have the source data...
2219 : }
2220 :
2221 : // Helper method to tell us whether the clock is currently running for
2222 : // discarding this image. Mainly for assertions.
2223 : bool
2224 26 : RasterImage::DiscardingActive() {
2225 26 : return !!(mDiscardTrackerNode.prev || mDiscardTrackerNode.next);
2226 : }
2227 :
2228 : // Helper method to determine if we're storing the source data in a buffer
2229 : // or just writing it directly to the decoder
2230 : bool
2231 92 : RasterImage::StoringSourceData() const {
2232 92 : return (mDecodeOnDraw || mDiscardable);
2233 : }
2234 :
2235 :
2236 : // Sets up a decoder for this image. It is an error to call this function
2237 : // when decoding is already in process (ie - when mDecoder is non-null).
2238 : nsresult
2239 24 : RasterImage::InitDecoder(bool aDoSizeDecode)
2240 : {
2241 : // Ensure that the decoder is not already initialized
2242 24 : NS_ABORT_IF_FALSE(!mDecoder, "Calling InitDecoder() while already decoding!");
2243 :
2244 : // We shouldn't be firing up a decoder if we already have the frames decoded
2245 24 : NS_ABORT_IF_FALSE(!mDecoded, "Calling InitDecoder() but already decoded!");
2246 :
2247 : // Since we're not decoded, we should not have a discard timer active
2248 24 : NS_ABORT_IF_FALSE(!DiscardingActive(), "Discard Timer active in InitDecoder()!");
2249 :
2250 : // Figure out which decoder we want
2251 24 : eDecoderType type = GetDecoderType(mSourceDataMimeType.get());
2252 24 : CONTAINER_ENSURE_TRUE(type != eDecoderType_unknown, NS_IMAGELIB_ERROR_NO_DECODER);
2253 :
2254 40 : nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
2255 : // Instantiate the appropriate decoder
2256 20 : switch (type) {
2257 : case eDecoderType_png:
2258 12 : mDecoder = new nsPNGDecoder(*this, observer);
2259 6 : break;
2260 : case eDecoderType_gif:
2261 8 : mDecoder = new nsGIFDecoder2(*this, observer);
2262 4 : break;
2263 : case eDecoderType_jpeg:
2264 10 : mDecoder = new nsJPEGDecoder(*this, observer);
2265 5 : break;
2266 : case eDecoderType_bmp:
2267 0 : mDecoder = new nsBMPDecoder(*this, observer);
2268 0 : break;
2269 : case eDecoderType_ico:
2270 10 : mDecoder = new nsICODecoder(*this, observer);
2271 5 : break;
2272 : case eDecoderType_icon:
2273 0 : mDecoder = new nsIconDecoder(*this, observer);
2274 0 : break;
2275 : default:
2276 0 : NS_ABORT_IF_FALSE(0, "Shouldn't get here!");
2277 : }
2278 :
2279 : // Initialize the decoder
2280 20 : mDecoder->SetSizeDecode(aDoSizeDecode);
2281 20 : mDecoder->SetDecodeFlags(mFrameDecodeFlags);
2282 20 : mDecoder->Init();
2283 20 : CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
2284 :
2285 20 : if (!aDoSizeDecode) {
2286 16 : Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
2287 16 : mDecodeCount++;
2288 16 : Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
2289 : }
2290 :
2291 20 : return NS_OK;
2292 : }
2293 :
2294 : // Flushes, closes, and nulls-out a decoder. Cleans up any related decoding
2295 : // state. It is an error to call this function when there is no initialized
2296 : // decoder.
2297 : //
2298 : // aIntent specifies the intent of the shutdown. If aIntent is
2299 : // eShutdownIntent_Done, an error is flagged if we didn't get what we should
2300 : // have out of the decode. If aIntent is eShutdownIntent_Interrupted, we don't
2301 : // check this. If aIntent is eShutdownIntent_Error, we shut down in error mode.
2302 : nsresult
2303 20 : RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
2304 : {
2305 : // Ensure that our intent is valid
2306 20 : NS_ABORT_IF_FALSE((aIntent >= 0) || (aIntent < eShutdownIntent_AllCount),
2307 : "Invalid shutdown intent");
2308 :
2309 : // Ensure that the decoder is initialized
2310 20 : NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
2311 :
2312 : // Figure out what kind of decode we were doing before we get rid of our decoder
2313 20 : bool wasSizeDecode = mDecoder->IsSizeDecode();
2314 :
2315 : // Finalize the decoder
2316 : // null out mDecoder, _then_ check for errors on the close (otherwise the
2317 : // error routine might re-invoke ShutdownDecoder)
2318 40 : nsRefPtr<Decoder> decoder = mDecoder;
2319 20 : mDecoder = nsnull;
2320 :
2321 20 : mInDecoder = true;
2322 20 : decoder->Finish();
2323 20 : mInDecoder = false;
2324 :
2325 : // Kill off our decode request, if it's pending. (If not, this call is
2326 : // harmless.)
2327 20 : DecodeWorker::Singleton()->StopDecoding(this);
2328 :
2329 20 : nsresult decoderStatus = decoder->GetDecoderError();
2330 20 : if (NS_FAILED(decoderStatus)) {
2331 0 : DoError();
2332 0 : return decoderStatus;
2333 : }
2334 :
2335 : // We just shut down the decoder. If we didn't get what we want, but expected
2336 : // to, flag an error
2337 20 : bool failed = false;
2338 20 : if (wasSizeDecode && !mHasSize)
2339 0 : failed = true;
2340 20 : if (!wasSizeDecode && !mDecoded)
2341 1 : failed = true;
2342 20 : if ((aIntent == eShutdownIntent_Done) && failed) {
2343 1 : DoError();
2344 1 : return NS_ERROR_FAILURE;
2345 : }
2346 :
2347 : // Reset number of decoded bytes
2348 19 : mBytesDecoded = 0;
2349 :
2350 19 : return NS_OK;
2351 : }
2352 :
2353 : // Writes the data to the decoder, updating the total number of bytes written.
2354 : nsresult
2355 59 : RasterImage::WriteToDecoder(const char *aBuffer, PRUint32 aCount)
2356 : {
2357 : // We should have a decoder
2358 59 : NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
2359 :
2360 : // The decoder will start decoding into the current frame (if we have one).
2361 : // When it needs to add another frame, we will unlock this frame and lock the
2362 : // new frame.
2363 : // Our invariant is that, while in the decoder, the last frame is always
2364 : // locked, and all others are unlocked.
2365 59 : if (mFrames.Length() > 0) {
2366 21 : imgFrame *curframe = mFrames.ElementAt(mFrames.Length() - 1);
2367 21 : curframe->LockImageData();
2368 : }
2369 :
2370 : // Write
2371 118 : nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
2372 59 : mInDecoder = true;
2373 59 : mDecoder->Write(aBuffer, aCount);
2374 59 : mInDecoder = false;
2375 :
2376 : // We unlock the current frame, even if that frame is different from the
2377 : // frame we entered the decoder with. (See above.)
2378 59 : if (mFrames.Length() > 0) {
2379 36 : imgFrame *curframe = mFrames.ElementAt(mFrames.Length() - 1);
2380 36 : curframe->UnlockImageData();
2381 : }
2382 :
2383 59 : if (!mDecoder)
2384 0 : return NS_ERROR_FAILURE;
2385 :
2386 59 : CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
2387 :
2388 : // Keep track of the total number of bytes written over the lifetime of the
2389 : // decoder
2390 59 : mBytesDecoded += aCount;
2391 :
2392 59 : return NS_OK;
2393 : }
2394 :
2395 : // This function is called in situations where it's clear that we want the
2396 : // frames in decoded form (Draw, GetFrame, CopyFrame, ExtractFrame, etc).
2397 : // If we're completely decoded, this method resets the discard timer (if
2398 : // we're discardable), since wanting the frames now is a good indicator of
2399 : // wanting them again soon. If we're not decoded, this method kicks off
2400 : // asynchronous decoding to generate the frames.
2401 : nsresult
2402 32 : RasterImage::WantDecodedFrames()
2403 : {
2404 : nsresult rv;
2405 :
2406 : // If we can discard, the clock should be running. Reset it.
2407 32 : if (CanDiscard()) {
2408 0 : NS_ABORT_IF_FALSE(DiscardingActive(),
2409 : "Decoded and discardable but discarding not activated!");
2410 0 : rv = DiscardTracker::Reset(&mDiscardTrackerNode);
2411 0 : CONTAINER_ENSURE_SUCCESS(rv);
2412 : }
2413 :
2414 : // Request a decode (no-op if we're decoded)
2415 32 : return RequestDecode();
2416 : }
2417 :
2418 : //******************************************************************************
2419 : /* void requestDecode() */
2420 : NS_IMETHODIMP
2421 32 : RasterImage::RequestDecode()
2422 : {
2423 : nsresult rv;
2424 :
2425 32 : if (mError)
2426 0 : return NS_ERROR_FAILURE;
2427 :
2428 : // If we're fully decoded, we have nothing to do
2429 32 : if (mDecoded)
2430 23 : return NS_OK;
2431 :
2432 : // If we're not storing source data, we have nothing to do
2433 9 : if (!StoringSourceData())
2434 3 : return NS_OK;
2435 :
2436 : // If we've already got a full decoder running, we have nothing to do
2437 6 : if (mDecoder && !mDecoder->IsSizeDecode())
2438 6 : return NS_OK;
2439 :
2440 : // If our callstack goes through a size decoder, we have a problem.
2441 : // We need to shutdown the size decode and replace it with a full
2442 : // decoder, but can't do that from within the decoder itself. Thus, we post
2443 : // an asynchronous event to the event loop to do it later. Since
2444 : // RequestDecode() is an asynchronous function this works fine (though it's
2445 : // a little slower).
2446 0 : if (mInDecoder) {
2447 0 : nsRefPtr<imgDecodeRequestor> requestor = new imgDecodeRequestor(this);
2448 0 : return NS_DispatchToCurrentThread(requestor);
2449 : }
2450 :
2451 :
2452 : // If we have a size decode open, interrupt it and shut it down; or if
2453 : // the decoder has different flags than what we need
2454 0 : if (mDecoder &&
2455 0 : (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
2456 : {
2457 0 : rv = ShutdownDecoder(eShutdownIntent_Interrupted);
2458 0 : CONTAINER_ENSURE_SUCCESS(rv);
2459 : }
2460 :
2461 : // If we don't have a decoder, create one
2462 0 : if (!mDecoder) {
2463 0 : NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
2464 0 : rv = InitDecoder(/* aDoSizeDecode = */ false);
2465 0 : CONTAINER_ENSURE_SUCCESS(rv);
2466 : }
2467 :
2468 : // If we've read all the data we have, we're done
2469 0 : if (mBytesDecoded == mSourceData.Length())
2470 0 : return NS_OK;
2471 :
2472 : // If it's a smallish image, it's not worth it to do things async
2473 0 : if (!mDecoded && !mInDecoder && mHasSourceData && (mSourceData.Length() < gMaxBytesForSyncDecode))
2474 0 : return SyncDecode();
2475 :
2476 : // If we get this far, dispatch the worker. We do this instead of starting
2477 : // any immediate decoding to guarantee that all our decode notifications are
2478 : // dispatched asynchronously, and to ensure we stay responsive.
2479 0 : DecodeWorker::Singleton()->RequestDecode(this);
2480 :
2481 0 : return NS_OK;
2482 : }
2483 :
2484 : // Synchronously decodes as much data as possible
2485 : nsresult
2486 21 : RasterImage::SyncDecode()
2487 : {
2488 : nsresult rv;
2489 :
2490 : // If we're decoded already, no worries
2491 21 : if (mDecoded)
2492 19 : return NS_OK;
2493 :
2494 : // If we're not storing source data, there isn't much to do here
2495 2 : if (!StoringSourceData())
2496 0 : return NS_OK;
2497 :
2498 : // We really have no good way of forcing a synchronous decode if we're being
2499 : // called in a re-entrant manner (ie, from an event listener fired by a
2500 : // decoder), because the decoding machinery is already tied up. We thus explicitly
2501 : // disallow this type of call in the API, and check for it in API methods.
2502 2 : NS_ABORT_IF_FALSE(!mInDecoder, "Yikes, forcing sync in reentrant call!");
2503 :
2504 : // If we have a size decoder open, or one with different flags than
2505 : // what we need, shut it down
2506 2 : if (mDecoder &&
2507 2 : (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
2508 : {
2509 0 : rv = ShutdownDecoder(eShutdownIntent_Interrupted);
2510 0 : CONTAINER_ENSURE_SUCCESS(rv);
2511 : }
2512 :
2513 : // If we don't have a decoder, create one
2514 2 : if (!mDecoder) {
2515 2 : NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
2516 2 : rv = InitDecoder(/* aDoSizeDecode = */ false);
2517 2 : CONTAINER_ENSURE_SUCCESS(rv);
2518 : }
2519 :
2520 : // Write everything we have
2521 2 : rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
2522 4 : mSourceData.Length() - mBytesDecoded);
2523 2 : CONTAINER_ENSURE_SUCCESS(rv);
2524 :
2525 : // When we're doing a sync decode, we want to get as much information from the
2526 : // image as possible. We've send the decoder all of our data, so now's a good
2527 : // time to flush any invalidations (in case we don't have all the data and what
2528 : // we got left us mid-frame).
2529 4 : nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
2530 2 : mInDecoder = true;
2531 2 : mDecoder->FlushInvalidations();
2532 2 : mInDecoder = false;
2533 :
2534 : // If we finished the decode, shutdown the decoder
2535 2 : if (mDecoder && IsDecodeFinished()) {
2536 2 : rv = ShutdownDecoder(eShutdownIntent_Done);
2537 2 : CONTAINER_ENSURE_SUCCESS(rv);
2538 : }
2539 :
2540 : // All good if no errors!
2541 2 : return mError ? NS_ERROR_FAILURE : NS_OK;
2542 : }
2543 :
2544 : //******************************************************************************
2545 : /* [noscript] void draw(in gfxContext aContext,
2546 : * in gfxGraphicsFilter aFilter,
2547 : * [const] in gfxMatrix aUserSpaceToImageSpace,
2548 : * [const] in gfxRect aFill,
2549 : * [const] in nsIntRect aSubimage,
2550 : * [const] in nsIntSize aViewportSize,
2551 : * in PRUint32 aFlags); */
2552 : NS_IMETHODIMP
2553 0 : RasterImage::Draw(gfxContext *aContext,
2554 : gfxPattern::GraphicsFilter aFilter,
2555 : const gfxMatrix &aUserSpaceToImageSpace,
2556 : const gfxRect &aFill,
2557 : const nsIntRect &aSubimage,
2558 : const nsIntSize& /*aViewportSize - ignored*/,
2559 : PRUint32 aFlags)
2560 : {
2561 0 : if (mError)
2562 0 : return NS_ERROR_FAILURE;
2563 :
2564 : // Disallowed in the API
2565 0 : if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
2566 0 : return NS_ERROR_FAILURE;
2567 :
2568 : // Illegal -- you can't draw with non-default decode flags.
2569 : // (Disabling colorspace conversion might make sense to allow, but
2570 : // we don't currently.)
2571 0 : if ((aFlags & DECODE_FLAGS_MASK) != DECODE_FLAGS_DEFAULT)
2572 0 : return NS_ERROR_FAILURE;
2573 :
2574 0 : NS_ENSURE_ARG_POINTER(aContext);
2575 :
2576 : // We can only draw with the default decode flags
2577 0 : if (mFrameDecodeFlags != DECODE_FLAGS_DEFAULT) {
2578 0 : if (!CanForciblyDiscard())
2579 0 : return NS_ERROR_NOT_AVAILABLE;
2580 0 : ForceDiscard();
2581 :
2582 0 : mFrameDecodeFlags = DECODE_FLAGS_DEFAULT;
2583 : }
2584 :
2585 : // If this image is a candidate for discarding, reset its position in the
2586 : // discard tracker so we're less likely to discard it right away.
2587 : //
2588 : // (We don't normally draw unlocked images, so this conditition will usually
2589 : // be false. But we will draw unlocked images if image locking is globally
2590 : // disabled via the content.image.allow_locking pref.)
2591 0 : if (DiscardingActive()) {
2592 0 : DiscardTracker::Reset(&mDiscardTrackerNode);
2593 : }
2594 :
2595 : // We use !mDecoded && mHasSourceData to mean discarded.
2596 0 : if (!mDecoded && mHasSourceData) {
2597 0 : mDrawStartTime = TimeStamp::Now();
2598 :
2599 : // We're drawing this image, so indicate that we should decode it as soon
2600 : // as possible.
2601 0 : DecodeWorker::Singleton()->MarkAsASAP(this);
2602 : }
2603 :
2604 : // If a synchronous draw is requested, flush anything that might be sitting around
2605 0 : if (aFlags & FLAG_SYNC_DECODE) {
2606 0 : nsresult rv = SyncDecode();
2607 0 : NS_ENSURE_SUCCESS(rv, rv);
2608 : }
2609 :
2610 0 : imgFrame *frame = GetCurrentDrawableImgFrame();
2611 0 : if (!frame) {
2612 0 : return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
2613 : }
2614 :
2615 0 : nsIntRect framerect = frame->GetRect();
2616 : nsIntMargin padding(framerect.x, framerect.y,
2617 0 : mSize.width - framerect.XMost(),
2618 0 : mSize.height - framerect.YMost());
2619 :
2620 0 : frame->Draw(aContext, aFilter, aUserSpaceToImageSpace, aFill, padding, aSubimage);
2621 :
2622 0 : if (mDecoded && !mDrawStartTime.IsNull()) {
2623 0 : TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
2624 0 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, PRInt32(drawLatency.ToMicroseconds()));
2625 : // clear the value of mDrawStartTime
2626 0 : mDrawStartTime = TimeStamp();
2627 : }
2628 0 : return NS_OK;
2629 : }
2630 :
2631 : //******************************************************************************
2632 : /* [notxpcom] nsIFrame GetRootLayoutFrame() */
2633 : nsIFrame*
2634 0 : RasterImage::GetRootLayoutFrame()
2635 : {
2636 0 : return nsnull;
2637 : }
2638 :
2639 : //******************************************************************************
2640 : /* void lockImage() */
2641 : NS_IMETHODIMP
2642 4 : RasterImage::LockImage()
2643 : {
2644 4 : if (mError)
2645 0 : return NS_ERROR_FAILURE;
2646 :
2647 : // Cancel the discard timer if it's there
2648 4 : DiscardTracker::Remove(&mDiscardTrackerNode);
2649 :
2650 : // Increment the lock count
2651 4 : mLockCount++;
2652 :
2653 4 : return NS_OK;
2654 : }
2655 :
2656 : //******************************************************************************
2657 : /* void unlockImage() */
2658 : NS_IMETHODIMP
2659 3 : RasterImage::UnlockImage()
2660 : {
2661 3 : if (mError)
2662 1 : return NS_ERROR_FAILURE;
2663 :
2664 : // It's an error to call this function if the lock count is 0
2665 2 : NS_ABORT_IF_FALSE(mLockCount > 0,
2666 : "Calling UnlockImage with mLockCount == 0!");
2667 2 : if (mLockCount == 0)
2668 0 : return NS_ERROR_ABORT;
2669 :
2670 : // We're locked, so discarding should not be active
2671 2 : NS_ABORT_IF_FALSE(!DiscardingActive(), "Locked, but discarding activated");
2672 :
2673 : // Decrement our lock count
2674 2 : mLockCount--;
2675 :
2676 : // If we've decoded this image once before, we're currently decoding again,
2677 : // and our lock count is now zero (so nothing is forcing us to keep the
2678 : // decoded data around), try to cancel the decode and throw away whatever
2679 : // we've decoded.
2680 2 : if (mHasBeenDecoded && mDecoder &&
2681 0 : mLockCount == 0 && CanForciblyDiscard()) {
2682 0 : PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG,
2683 : ("RasterImage[0x%p] canceling decode because image "
2684 : "is now unlocked.", this));
2685 0 : ShutdownDecoder(eShutdownIntent_Interrupted);
2686 0 : ForceDiscard();
2687 0 : return NS_OK;
2688 : }
2689 :
2690 : // Otherwise, we might still be a candidate for discarding in the future. If
2691 : // we are, add ourselves to the discard tracker.
2692 2 : if (CanDiscard()) {
2693 1 : nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
2694 1 : CONTAINER_ENSURE_SUCCESS(rv);
2695 : }
2696 :
2697 2 : return NS_OK;
2698 : }
2699 :
2700 : //******************************************************************************
2701 : /* void requestDiscard() */
2702 : NS_IMETHODIMP
2703 0 : RasterImage::RequestDiscard()
2704 : {
2705 0 : if (CanDiscard()) {
2706 0 : ForceDiscard();
2707 : }
2708 :
2709 0 : return NS_OK;
2710 : }
2711 :
2712 : // Flushes up to aMaxBytes to the decoder.
2713 : nsresult
2714 4 : RasterImage::DecodeSomeData(PRUint32 aMaxBytes)
2715 : {
2716 : // We should have a decoder if we get here
2717 4 : NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
2718 :
2719 : // If we have nothing to decode, return
2720 4 : if (mBytesDecoded == mSourceData.Length())
2721 0 : return NS_OK;
2722 :
2723 :
2724 : // write the proper amount of data
2725 : PRUint32 bytesToDecode = NS_MIN(aMaxBytes,
2726 4 : mSourceData.Length() - mBytesDecoded);
2727 4 : nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
2728 4 : bytesToDecode);
2729 :
2730 4 : return rv;
2731 : }
2732 :
2733 : // There are various indicators that tell us we're finished with the decode
2734 : // task at hand and can shut down the decoder.
2735 : //
2736 : // This method may not be called if there is no decoder.
2737 : bool
2738 6 : RasterImage::IsDecodeFinished()
2739 : {
2740 : // Precondition
2741 6 : NS_ABORT_IF_FALSE(mDecoder, "Can't call IsDecodeFinished() without decoder!");
2742 :
2743 : // Assume it's not finished
2744 6 : bool decodeFinished = false;
2745 :
2746 : // There shouldn't be any reason to call this if we're not storing
2747 : // source data
2748 6 : NS_ABORT_IF_FALSE(StoringSourceData(),
2749 : "just shut down on SourceDataComplete!");
2750 :
2751 : // The decode is complete if we got what we wanted...
2752 6 : if (mDecoder->IsSizeDecode()) {
2753 4 : if (mHasSize)
2754 4 : decodeFinished = true;
2755 : }
2756 : else {
2757 2 : if (mDecoded)
2758 1 : decodeFinished = true;
2759 : }
2760 :
2761 : // ...or if we have all the source data and wrote all the source data.
2762 : //
2763 : // (NB - This can be distinct from the above case even for non-erroneous
2764 : // images because the decoder might not call DecodingComplete() until we
2765 : // call Close() in ShutdownDecoder())
2766 6 : if (mHasSourceData && (mBytesDecoded == mSourceData.Length()))
2767 6 : decodeFinished = true;
2768 :
2769 6 : return decodeFinished;
2770 : }
2771 :
2772 : // Indempotent error flagging routine. If a decoder is open, shuts it down.
2773 : void
2774 10 : RasterImage::DoError()
2775 : {
2776 : // If we've flagged an error before, we have nothing to do
2777 10 : if (mError)
2778 5 : return;
2779 :
2780 : // If we're mid-decode, shut down the decoder.
2781 5 : if (mDecoder)
2782 0 : ShutdownDecoder(eShutdownIntent_Error);
2783 :
2784 : // Put the container in an error state
2785 5 : mError = true;
2786 :
2787 : // Log our error
2788 5 : LOG_CONTAINER_ERROR;
2789 : }
2790 :
2791 : // nsIInputStream callback to copy the incoming image data directly to the
2792 : // RasterImage without processing. The RasterImage is passed as the closure.
2793 : // Always reads everything it gets, even if the data is erroneous.
2794 : NS_METHOD
2795 57 : RasterImage::WriteToRasterImage(nsIInputStream* /* unused */,
2796 : void* aClosure,
2797 : const char* aFromRawSegment,
2798 : PRUint32 /* unused */,
2799 : PRUint32 aCount,
2800 : PRUint32* aWriteCount)
2801 : {
2802 : // Retrieve the RasterImage
2803 57 : RasterImage* image = static_cast<RasterImage*>(aClosure);
2804 :
2805 : // Copy the source data. Unless we hit OOM, we squelch the return value
2806 : // here, because returning an error means that ReadSegments stops
2807 : // reading data, violating our invariant that we read everything we get.
2808 : // If we hit OOM then we fail and the load is aborted.
2809 57 : nsresult rv = image->AddSourceData(aFromRawSegment, aCount);
2810 57 : if (rv == NS_ERROR_OUT_OF_MEMORY) {
2811 0 : image->DoError();
2812 0 : return rv;
2813 : }
2814 :
2815 : // We wrote everything we got
2816 57 : *aWriteCount = aCount;
2817 :
2818 57 : return NS_OK;
2819 : }
2820 :
2821 : bool
2822 2 : RasterImage::ShouldAnimate()
2823 : {
2824 2 : return Image::ShouldAnimate() && mFrames.Length() >= 2 &&
2825 2 : !mAnimationFinished;
2826 : }
2827 :
2828 : /* readonly attribute PRUint32 framesNotified; */
2829 : #ifdef DEBUG
2830 : NS_IMETHODIMP
2831 0 : RasterImage::GetFramesNotified(PRUint32 *aFramesNotified)
2832 : {
2833 0 : NS_ENSURE_ARG_POINTER(aFramesNotified);
2834 :
2835 0 : *aFramesNotified = mFramesNotified;
2836 :
2837 0 : return NS_OK;
2838 : }
2839 : #endif
2840 :
2841 : /* static */ RasterImage::DecodeWorker*
2842 28 : RasterImage::DecodeWorker::Singleton()
2843 : {
2844 28 : if (!sSingleton) {
2845 4 : sSingleton = new DecodeWorker();
2846 4 : ClearOnShutdown(&sSingleton);
2847 : }
2848 :
2849 28 : return sSingleton;
2850 : }
2851 :
2852 : void
2853 0 : RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
2854 : {
2855 0 : DecodeRequest* request = &aImg->mDecodeRequest;
2856 :
2857 : // If we're already an ASAP request, there's nothing to do here.
2858 0 : if (request->mIsASAP) {
2859 0 : return;
2860 : }
2861 :
2862 0 : request->mIsASAP = true;
2863 :
2864 0 : if (request->isInList()) {
2865 : // If the decode request is in a list, it must be in the normal decode
2866 : // requests list -- if it had been in the ASAP list, then mIsASAP would
2867 : // have been true above. Move the request to the ASAP list.
2868 0 : request->remove();
2869 0 : mASAPDecodeRequests.insertBack(request);
2870 :
2871 : // Since request is in a list, one of the decode worker's lists is
2872 : // non-empty, so the worker should be pending in the event loop.
2873 : //
2874 : // (Note that this invariant only holds while we are not in Run(), because
2875 : // DecodeSomeOfImage adds requests to the decode worker using
2876 : // AddDecodeRequest, not RequestDecode, and AddDecodeRequest does not call
2877 : // EnsurePendingInEventLoop. Therefore, it is an error to call MarkAsASAP
2878 : // from within DecodeWorker::Run.)
2879 0 : MOZ_ASSERT(mPendingInEventLoop);
2880 : }
2881 : }
2882 :
2883 : void
2884 4 : RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest)
2885 : {
2886 4 : if (aRequest->isInList()) {
2887 : // The image is already in our list of images to decode, so we don't have
2888 : // to do anything here.
2889 0 : return;
2890 : }
2891 :
2892 4 : if (aRequest->mIsASAP) {
2893 0 : mASAPDecodeRequests.insertBack(aRequest);
2894 : } else {
2895 4 : mNormalDecodeRequests.insertBack(aRequest);
2896 : }
2897 : }
2898 :
2899 : void
2900 4 : RasterImage::DecodeWorker::RequestDecode(RasterImage* aImg)
2901 : {
2902 4 : AddDecodeRequest(&aImg->mDecodeRequest);
2903 4 : EnsurePendingInEventLoop();
2904 4 : }
2905 :
2906 : void
2907 4 : RasterImage::DecodeWorker::EnsurePendingInEventLoop()
2908 : {
2909 4 : if (!mPendingInEventLoop) {
2910 4 : mPendingInEventLoop = true;
2911 4 : NS_DispatchToCurrentThread(this);
2912 : }
2913 4 : }
2914 :
2915 : void
2916 20 : RasterImage::DecodeWorker::StopDecoding(RasterImage* aImg)
2917 : {
2918 20 : DecodeRequest* request = &aImg->mDecodeRequest;
2919 20 : if (request->isInList()) {
2920 4 : request->remove();
2921 : }
2922 20 : request->mDecodeTime = TimeDuration(0);
2923 20 : request->mIsASAP = false;
2924 20 : }
2925 :
2926 : NS_IMETHODIMP
2927 4 : RasterImage::DecodeWorker::Run()
2928 : {
2929 : // We just got called back by the event loop; therefore, we're no longer
2930 : // pending.
2931 4 : mPendingInEventLoop = false;
2932 :
2933 4 : TimeStamp eventStart = TimeStamp::Now();
2934 :
2935 : // Now decode until we either run out of time or run out of images.
2936 0 : do {
2937 : // Try to get an ASAP request to handle. If there isn't one, try to get a
2938 : // normal request. If no normal request is pending either, then we're done
2939 : // here.
2940 4 : DecodeRequest* request = mASAPDecodeRequests.popFirst();
2941 4 : if (!request)
2942 4 : request = mNormalDecodeRequests.popFirst();
2943 4 : if (!request)
2944 4 : break;
2945 :
2946 : // This has to be a strong pointer, because DecodeSomeOfImage may destroy
2947 : // image->mDecoder, which may be holding the only other reference to image.
2948 0 : nsRefPtr<RasterImage> image = request->mImage;
2949 0 : DecodeSomeOfImage(image);
2950 :
2951 : // If we aren't yet finished decoding and we have more data in hand, add
2952 : // this request to the back of the list.
2953 0 : if (image->mDecoder &&
2954 0 : !image->mError &&
2955 0 : !image->IsDecodeFinished() &&
2956 0 : image->mSourceData.Length() > image->mBytesDecoded) {
2957 0 : AddDecodeRequest(request);
2958 : }
2959 :
2960 0 : } while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield);
2961 :
2962 : // If decode requests are pending, re-post ourself to the event loop.
2963 4 : if (!mASAPDecodeRequests.isEmpty() || !mNormalDecodeRequests.isEmpty()) {
2964 0 : EnsurePendingInEventLoop();
2965 : }
2966 :
2967 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_LATENCY,
2968 4 : PRUint32((TimeStamp::Now() - eventStart).ToMilliseconds()));
2969 :
2970 4 : return NS_OK;
2971 : }
2972 :
2973 : nsresult
2974 4 : RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
2975 : {
2976 4 : return DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
2977 : }
2978 :
2979 : nsresult
2980 4 : RasterImage::DecodeWorker::DecodeSomeOfImage(
2981 : RasterImage* aImg,
2982 : DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */)
2983 : {
2984 4 : NS_ABORT_IF_FALSE(aImg->mInitialized,
2985 : "Worker active for uninitialized container!");
2986 :
2987 4 : if (aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize)
2988 0 : return NS_OK;
2989 :
2990 : // If an error is flagged, it probably happened while we were waiting
2991 : // in the event queue.
2992 4 : if (aImg->mError)
2993 0 : return NS_OK;
2994 :
2995 : // If mDecoded or we don't have a decoder, we must have finished already (for
2996 : // example, a synchronous decode request came while the worker was pending).
2997 4 : if (!aImg->mDecoder || aImg->mDecoded)
2998 0 : return NS_OK;
2999 :
3000 8 : nsRefPtr<Decoder> decoderKungFuDeathGrip = aImg->mDecoder;
3001 :
3002 : PRUint32 maxBytes;
3003 4 : if (aImg->mDecoder->IsSizeDecode()) {
3004 : // Decode all available data if we're a size decode; they're cheap, and we
3005 : // want them to be more or less synchronous.
3006 4 : maxBytes = aImg->mSourceData.Length();
3007 : } else {
3008 : // We're only guaranteed to decode this many bytes, so in particular,
3009 : // gDecodeBytesAtATime should be set high enough for us to read the size
3010 : // from most images.
3011 0 : maxBytes = gDecodeBytesAtATime;
3012 : }
3013 :
3014 4 : PRInt32 chunkCount = 0;
3015 4 : TimeStamp start = TimeStamp::Now();
3016 4 : TimeStamp deadline = start + TimeDuration::FromMilliseconds(gMaxMSBeforeYield);
3017 :
3018 : // Decode some chunks of data.
3019 0 : do {
3020 4 : chunkCount++;
3021 4 : nsresult rv = aImg->DecodeSomeData(maxBytes);
3022 4 : if (NS_FAILED(rv)) {
3023 0 : aImg->DoError();
3024 0 : return rv;
3025 : }
3026 :
3027 : // We keep decoding chunks until either:
3028 : // * we're an UNTIL_SIZE decode and we get the size,
3029 : // * we don't have any data left to decode,
3030 : // * the decode completes, or
3031 : // * we run out of time.
3032 :
3033 4 : if (aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize)
3034 4 : break;
3035 :
3036 0 : } while (aImg->mSourceData.Length() > aImg->mBytesDecoded &&
3037 0 : !aImg->IsDecodeFinished() &&
3038 0 : TimeStamp::Now() < deadline);
3039 :
3040 4 : aImg->mDecodeRequest.mDecodeTime += (TimeStamp::Now() - start);
3041 :
3042 4 : if (chunkCount && !aImg->mDecoder->IsSizeDecode()) {
3043 0 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, chunkCount);
3044 : }
3045 :
3046 : // Flush invalidations (and therefore paint) now that we've decoded all the
3047 : // chunks we're going to.
3048 : //
3049 : // However, don't paint if:
3050 : //
3051 : // * This was an until-size decode. Until-size decodes are always followed
3052 : // by normal decodes, so don't bother painting.
3053 : //
3054 : // * The decoder flagged an error. The decoder may have written garbage
3055 : // into the output buffer; don't paint it to the screen.
3056 : //
3057 : // * We have all the source data. This disables progressive display of
3058 : // previously-decoded images, thus letting us finish decoding faster,
3059 : // since we don't waste time painting while we decode.
3060 : // Decoder::PostFrameStop() will flush invalidations once the decode is
3061 : // done.
3062 :
3063 4 : if (aDecodeType != DECODE_TYPE_UNTIL_SIZE &&
3064 0 : !aImg->mDecoder->HasError() &&
3065 0 : !aImg->mHasSourceData) {
3066 0 : aImg->mInDecoder = true;
3067 0 : aImg->mDecoder->FlushInvalidations();
3068 0 : aImg->mInDecoder = false;
3069 : }
3070 :
3071 : // If the decode finished, shut down the decoder.
3072 4 : if (aImg->mDecoder && aImg->IsDecodeFinished()) {
3073 :
3074 : // Do some telemetry if this isn't a size decode.
3075 4 : DecodeRequest* request = &aImg->mDecodeRequest;
3076 4 : if (!aImg->mDecoder->IsSizeDecode()) {
3077 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
3078 0 : PRInt32(request->mDecodeTime.ToMicroseconds()));
3079 :
3080 : // We record the speed for only some decoders. The rest have
3081 : // SpeedHistogram return HistogramCount.
3082 0 : Telemetry::ID id = aImg->mDecoder->SpeedHistogram();
3083 0 : if (id < Telemetry::HistogramCount) {
3084 : PRInt32 KBps = PRInt32(request->mImage->mBytesDecoded /
3085 0 : (1024 * request->mDecodeTime.ToSeconds()));
3086 0 : Telemetry::Accumulate(id, KBps);
3087 : }
3088 : }
3089 :
3090 4 : nsresult rv = aImg->ShutdownDecoder(RasterImage::eShutdownIntent_Done);
3091 4 : if (NS_FAILED(rv)) {
3092 0 : aImg->DoError();
3093 0 : return rv;
3094 : }
3095 : }
3096 :
3097 4 : return NS_OK;
3098 : }
3099 :
3100 : } // namespace image
3101 4392 : } // namespace mozilla
|