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 1396 : 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 1396 : /* 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.SetPattern(pattern);
857 19 : ctx.Rectangle(framerect);
858 19 : ctx.Fill();
859 :
860 19 : *_retval = imgsurface.forget().get();
861 19 : return NS_OK;
862 : }
863 :
864 : //******************************************************************************
865 : /* [noscript] gfxASurface getFrame(in PRUint32 aWhichFrame,
866 : * in PRUint32 aFlags); */
867 : NS_IMETHODIMP
868 0 : RasterImage::GetFrame(PRUint32 aWhichFrame,
869 : PRUint32 aFlags,
870 : gfxASurface **_retval)
871 : {
872 0 : if (aWhichFrame > FRAME_MAX_VALUE)
873 0 : return NS_ERROR_INVALID_ARG;
874 :
875 0 : if (mError)
876 0 : return NS_ERROR_FAILURE;
877 :
878 : // Disallowed in the API
879 0 : if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
880 0 : return NS_ERROR_FAILURE;
881 :
882 0 : nsresult rv = NS_OK;
883 :
884 0 : if (mDecoded) {
885 : // If we have decoded data, and it is not a perfect match for what we are
886 : // looking for, we must discard to be able to generate the proper data.
887 0 : PRUint32 desiredDecodeFlags = aFlags & DECODE_FLAGS_MASK;
888 0 : if (desiredDecodeFlags != mFrameDecodeFlags) {
889 : // if we can't discard, then we're screwed; we have no way
890 : // to re-decode. Similarly if we aren't allowed to do a sync
891 : // decode.
892 0 : if (!(aFlags & FLAG_SYNC_DECODE))
893 0 : return NS_ERROR_NOT_AVAILABLE;
894 0 : if (!CanForciblyDiscard() || mDecoder || mAnim)
895 0 : return NS_ERROR_NOT_AVAILABLE;
896 :
897 0 : ForceDiscard();
898 :
899 0 : mFrameDecodeFlags = desiredDecodeFlags;
900 : }
901 : }
902 :
903 : // If the caller requested a synchronous decode, do it
904 0 : if (aFlags & FLAG_SYNC_DECODE) {
905 0 : rv = SyncDecode();
906 0 : CONTAINER_ENSURE_SUCCESS(rv);
907 : }
908 :
909 : // Get the frame. If it's not there, it's probably the caller's fault for
910 : // not waiting for the data to be loaded from the network or not passing
911 : // FLAG_SYNC_DECODE
912 : PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
913 0 : 0 : GetCurrentImgFrameIndex();
914 0 : imgFrame *frame = GetDrawableImgFrame(frameIndex);
915 0 : if (!frame) {
916 0 : *_retval = nsnull;
917 0 : return NS_ERROR_FAILURE;
918 : }
919 :
920 0 : nsRefPtr<gfxASurface> framesurf;
921 :
922 : // If this frame covers the entire image, we can just reuse its existing
923 : // surface.
924 0 : nsIntRect framerect = frame->GetRect();
925 0 : if (framerect.x == 0 && framerect.y == 0 &&
926 : framerect.width == mSize.width &&
927 : framerect.height == mSize.height)
928 0 : rv = frame->GetSurface(getter_AddRefs(framesurf));
929 :
930 : // The image doesn't have a surface because it's been optimized away. Create
931 : // one.
932 0 : if (!framesurf) {
933 0 : nsRefPtr<gfxImageSurface> imgsurf;
934 0 : rv = CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
935 0 : framesurf = imgsurf;
936 : }
937 :
938 0 : *_retval = framesurf.forget().get();
939 :
940 0 : return rv;
941 : }
942 :
943 :
944 : NS_IMETHODIMP
945 0 : RasterImage::GetImageContainer(ImageContainer **_retval)
946 : {
947 0 : if (mImageContainer) {
948 0 : *_retval = mImageContainer;
949 0 : NS_ADDREF(*_retval);
950 0 : return NS_OK;
951 : }
952 :
953 0 : CairoImage::Data cairoData;
954 0 : nsRefPtr<gfxASurface> imageSurface;
955 0 : nsresult rv = GetFrame(FRAME_CURRENT, FLAG_SYNC_DECODE, getter_AddRefs(imageSurface));
956 0 : NS_ENSURE_SUCCESS(rv, rv);
957 :
958 0 : cairoData.mSurface = imageSurface;
959 0 : GetWidth(&cairoData.mSize.width);
960 0 : GetHeight(&cairoData.mSize.height);
961 :
962 0 : mImageContainer = LayerManager::CreateImageContainer();
963 :
964 : // Now create a CairoImage to display the surface.
965 0 : layers::Image::Format cairoFormat = layers::Image::CAIRO_SURFACE;
966 0 : nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&cairoFormat, 1);
967 0 : NS_ASSERTION(image, "Failed to create Image");
968 :
969 0 : NS_ASSERTION(image->GetFormat() == cairoFormat, "Wrong format");
970 0 : static_cast<CairoImage*>(image.get())->SetData(cairoData);
971 0 : mImageContainer->SetCurrentImage(image);
972 :
973 0 : *_retval = mImageContainer;
974 0 : NS_ADDREF(*_retval);
975 0 : return NS_OK;
976 : }
977 :
978 : size_t
979 6 : RasterImage::HeapSizeOfSourceWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const
980 : {
981 : // n == 0 is possible for two reasons.
982 : // - This is a zero-length image.
983 : // - We're on a platform where moz_malloc_size_of always returns 0.
984 : // In either case the fallback works appropriately.
985 6 : size_t n = mSourceData.SizeOfExcludingThis(aMallocSizeOf);
986 6 : if (n == 0) {
987 0 : n = mSourceData.Length();
988 0 : NS_ABORT_IF_FALSE(StoringSourceData() || (n == 0),
989 : "Non-zero source data size when we aren't storing it?");
990 : }
991 6 : return n;
992 : }
993 :
994 : static size_t
995 18 : SizeOfDecodedWithComputedFallbackIfHeap(
996 : const nsTArray<imgFrame*>& aFrames, gfxASurface::MemoryLocation aLocation,
997 : nsMallocSizeOfFun aMallocSizeOf)
998 : {
999 18 : size_t n = 0;
1000 30 : for (PRUint32 i = 0; i < aFrames.Length(); ++i) {
1001 12 : imgFrame* frame = aFrames.SafeElementAt(i, nsnull);
1002 12 : NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
1003 12 : n += frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
1004 : }
1005 :
1006 18 : return n;
1007 : }
1008 :
1009 : size_t
1010 6 : RasterImage::HeapSizeOfDecodedWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const
1011 : {
1012 : return SizeOfDecodedWithComputedFallbackIfHeap(
1013 6 : mFrames, gfxASurface::MEMORY_IN_PROCESS_HEAP, aMallocSizeOf);
1014 : }
1015 :
1016 : size_t
1017 6 : RasterImage::NonHeapSizeOfDecoded() const
1018 : {
1019 6 : return SizeOfDecodedWithComputedFallbackIfHeap(mFrames, gfxASurface::MEMORY_IN_PROCESS_NONHEAP, NULL);
1020 : }
1021 :
1022 : size_t
1023 6 : RasterImage::OutOfProcessSizeOfDecoded() const
1024 : {
1025 6 : return SizeOfDecodedWithComputedFallbackIfHeap(mFrames, gfxASurface::MEMORY_OUT_OF_PROCESS, NULL);
1026 : }
1027 :
1028 : void
1029 0 : RasterImage::DeleteImgFrame(PRUint32 framenum)
1030 : {
1031 0 : NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Deleting invalid frame!");
1032 :
1033 0 : delete mFrames[framenum];
1034 0 : mFrames[framenum] = nsnull;
1035 0 : }
1036 :
1037 : nsresult
1038 17 : RasterImage::InternalAddFrameHelper(PRUint32 framenum, imgFrame *aFrame,
1039 : PRUint8 **imageData, PRUint32 *imageLength,
1040 : PRUint32 **paletteData, PRUint32 *paletteLength)
1041 : {
1042 17 : NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
1043 17 : if (framenum > mFrames.Length())
1044 0 : return NS_ERROR_INVALID_ARG;
1045 :
1046 34 : nsAutoPtr<imgFrame> frame(aFrame);
1047 :
1048 17 : if (paletteData && paletteLength)
1049 2 : frame->GetPaletteData(paletteData, paletteLength);
1050 :
1051 17 : frame->GetImageData(imageData, imageLength);
1052 :
1053 : // We are in the middle of decoding. This will be unlocked when we finish the
1054 : // decoder->Write() call.
1055 17 : frame->LockImageData();
1056 :
1057 17 : mFrames.InsertElementAt(framenum, frame.forget());
1058 :
1059 17 : return NS_OK;
1060 : }
1061 :
1062 : nsresult
1063 17 : RasterImage::InternalAddFrame(PRUint32 framenum,
1064 : PRInt32 aX, PRInt32 aY,
1065 : PRInt32 aWidth, PRInt32 aHeight,
1066 : gfxASurface::gfxImageFormat aFormat,
1067 : PRUint8 aPaletteDepth,
1068 : PRUint8 **imageData,
1069 : PRUint32 *imageLength,
1070 : PRUint32 **paletteData,
1071 : PRUint32 *paletteLength)
1072 : {
1073 : // We assume that we're in the middle of decoding because we unlock the
1074 : // previous frame when we create a new frame, and only when decoding do we
1075 : // lock frames.
1076 17 : NS_ABORT_IF_FALSE(mInDecoder, "Only decoders may add frames!");
1077 :
1078 17 : NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
1079 17 : if (framenum > mFrames.Length())
1080 0 : return NS_ERROR_INVALID_ARG;
1081 :
1082 34 : nsAutoPtr<imgFrame> frame(new imgFrame());
1083 :
1084 17 : nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
1085 17 : NS_ENSURE_SUCCESS(rv, rv);
1086 :
1087 : // We know we are in a decoder. Therefore, we must unlock the previous frame
1088 : // when we move on to decoding into the next frame.
1089 17 : if (mFrames.Length() > 0) {
1090 2 : imgFrame *prevframe = mFrames.ElementAt(mFrames.Length() - 1);
1091 2 : prevframe->UnlockImageData();
1092 : }
1093 :
1094 17 : if (mFrames.Length() == 0) {
1095 : return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
1096 15 : paletteData, paletteLength);
1097 : }
1098 :
1099 2 : if (mFrames.Length() == 1) {
1100 : // Since we're about to add our second frame, initialize animation stuff
1101 1 : EnsureAnimExists();
1102 :
1103 : // If we dispose of the first frame by clearing it, then the
1104 : // First Frame's refresh area is all of itself.
1105 : // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
1106 1 : PRInt32 frameDisposalMethod = mFrames[0]->GetFrameDisposalMethod();
1107 1 : if (frameDisposalMethod == kDisposeClear ||
1108 : frameDisposalMethod == kDisposeRestorePrevious)
1109 1 : mAnim->firstFrameRefreshArea = mFrames[0]->GetRect();
1110 : }
1111 :
1112 : // Calculate firstFrameRefreshArea
1113 : // Some gifs are huge but only have a small area that they animate
1114 : // We only need to refresh that small area when Frame 0 comes around again
1115 2 : nsIntRect frameRect = frame->GetRect();
1116 : mAnim->firstFrameRefreshArea.UnionRect(mAnim->firstFrameRefreshArea,
1117 2 : frameRect);
1118 :
1119 : rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
1120 2 : paletteData, paletteLength);
1121 :
1122 : // We may be able to start animating, if we now have enough frames
1123 2 : EvaluateAnimation();
1124 :
1125 2 : return rv;
1126 : }
1127 :
1128 : nsresult
1129 21 : RasterImage::SetSize(PRInt32 aWidth, PRInt32 aHeight)
1130 : {
1131 21 : if (mError)
1132 0 : return NS_ERROR_FAILURE;
1133 :
1134 : // Ensure that we have positive values
1135 : // XXX - Why isn't the size unsigned? Should this be changed?
1136 21 : if ((aWidth < 0) || (aHeight < 0))
1137 0 : return NS_ERROR_INVALID_ARG;
1138 :
1139 : // if we already have a size, check the new size against the old one
1140 21 : if (mHasSize &&
1141 : ((aWidth != mSize.width) || (aHeight != mSize.height))) {
1142 :
1143 : // Alter the warning depending on whether the channel is multipart
1144 0 : if (!mMultipart)
1145 0 : NS_WARNING("Image changed size on redecode! This should not happen!");
1146 : else
1147 0 : NS_WARNING("Multipart channel sent an image of a different size");
1148 :
1149 : // Make the decoder aware of the error so that it doesn't try to call
1150 : // FinishInternal during ShutdownDecoder.
1151 0 : if (mDecoder)
1152 0 : mDecoder->PostResizeError();
1153 :
1154 0 : DoError();
1155 0 : return NS_ERROR_UNEXPECTED;
1156 : }
1157 :
1158 : // Set the size and flag that we have it
1159 21 : mSize.SizeTo(aWidth, aHeight);
1160 21 : mHasSize = true;
1161 :
1162 21 : return NS_OK;
1163 : }
1164 :
1165 : nsresult
1166 17 : RasterImage::EnsureFrame(PRUint32 aFrameNum, PRInt32 aX, PRInt32 aY,
1167 : PRInt32 aWidth, PRInt32 aHeight,
1168 : gfxASurface::gfxImageFormat aFormat,
1169 : PRUint8 aPaletteDepth,
1170 : PRUint8 **imageData, PRUint32 *imageLength,
1171 : PRUint32 **paletteData, PRUint32 *paletteLength)
1172 : {
1173 17 : if (mError)
1174 0 : return NS_ERROR_FAILURE;
1175 :
1176 17 : NS_ENSURE_ARG_POINTER(imageData);
1177 17 : NS_ENSURE_ARG_POINTER(imageLength);
1178 17 : NS_ABORT_IF_FALSE(aFrameNum <= mFrames.Length(), "Invalid frame index!");
1179 :
1180 17 : if (aPaletteDepth > 0) {
1181 2 : NS_ENSURE_ARG_POINTER(paletteData);
1182 2 : NS_ENSURE_ARG_POINTER(paletteLength);
1183 : }
1184 :
1185 17 : if (aFrameNum > mFrames.Length())
1186 0 : return NS_ERROR_INVALID_ARG;
1187 :
1188 : // Adding a frame that doesn't already exist.
1189 17 : if (aFrameNum == mFrames.Length())
1190 : return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
1191 : aPaletteDepth, imageData, imageLength,
1192 17 : paletteData, paletteLength);
1193 :
1194 0 : imgFrame *frame = GetImgFrame(aFrameNum);
1195 0 : if (!frame)
1196 : return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
1197 : aPaletteDepth, imageData, imageLength,
1198 0 : paletteData, paletteLength);
1199 :
1200 : // See if we can re-use the frame that already exists.
1201 0 : nsIntRect rect = frame->GetRect();
1202 0 : if (rect.x == aX && rect.y == aY && rect.width == aWidth &&
1203 0 : rect.height == aHeight && frame->GetFormat() == aFormat &&
1204 0 : frame->GetPaletteDepth() == aPaletteDepth) {
1205 0 : frame->GetImageData(imageData, imageLength);
1206 0 : if (paletteData) {
1207 0 : frame->GetPaletteData(paletteData, paletteLength);
1208 : }
1209 :
1210 : // We can re-use the frame if it has image data.
1211 0 : if (*imageData && paletteData && *paletteData) {
1212 0 : return NS_OK;
1213 : }
1214 0 : if (*imageData && !paletteData) {
1215 0 : return NS_OK;
1216 : }
1217 : }
1218 :
1219 0 : DeleteImgFrame(aFrameNum);
1220 : return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
1221 : aPaletteDepth, imageData, imageLength,
1222 0 : paletteData, paletteLength);
1223 : }
1224 :
1225 : nsresult
1226 15 : RasterImage::EnsureFrame(PRUint32 aFramenum, PRInt32 aX, PRInt32 aY,
1227 : PRInt32 aWidth, PRInt32 aHeight,
1228 : gfxASurface::gfxImageFormat aFormat,
1229 : PRUint8** imageData, PRUint32* imageLength)
1230 : {
1231 : return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
1232 : /* aPaletteDepth = */ 0, imageData, imageLength,
1233 : /* aPaletteData = */ nsnull,
1234 15 : /* aPaletteLength = */ nsnull);
1235 : }
1236 :
1237 : void
1238 32 : RasterImage::FrameUpdated(PRUint32 aFrameNum, nsIntRect &aUpdatedRect)
1239 : {
1240 32 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1241 :
1242 32 : imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
1243 32 : NS_ABORT_IF_FALSE(frame, "Calling FrameUpdated on frame that doesn't exist!");
1244 :
1245 32 : frame->ImageUpdated(aUpdatedRect);
1246 : // The image has changed, so we need to invalidate our cached ImageContainer.
1247 32 : mImageContainer = NULL;
1248 32 : }
1249 :
1250 : nsresult
1251 4 : RasterImage::SetFrameDisposalMethod(PRUint32 aFrameNum,
1252 : PRInt32 aDisposalMethod)
1253 : {
1254 4 : if (mError)
1255 0 : return NS_ERROR_FAILURE;
1256 :
1257 4 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1258 4 : if (aFrameNum >= mFrames.Length())
1259 0 : return NS_ERROR_INVALID_ARG;
1260 :
1261 4 : imgFrame *frame = GetImgFrame(aFrameNum);
1262 4 : NS_ABORT_IF_FALSE(frame,
1263 : "Calling SetFrameDisposalMethod on frame that doesn't exist!");
1264 4 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1265 :
1266 4 : frame->SetFrameDisposalMethod(aDisposalMethod);
1267 :
1268 4 : return NS_OK;
1269 : }
1270 :
1271 : nsresult
1272 4 : RasterImage::SetFrameTimeout(PRUint32 aFrameNum, PRInt32 aTimeout)
1273 : {
1274 4 : if (mError)
1275 0 : return NS_ERROR_FAILURE;
1276 :
1277 4 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1278 4 : if (aFrameNum >= mFrames.Length())
1279 0 : return NS_ERROR_INVALID_ARG;
1280 :
1281 4 : imgFrame *frame = GetImgFrame(aFrameNum);
1282 4 : NS_ABORT_IF_FALSE(frame, "Calling SetFrameTimeout on frame that doesn't exist!");
1283 4 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1284 :
1285 4 : frame->SetTimeout(aTimeout);
1286 :
1287 4 : return NS_OK;
1288 : }
1289 :
1290 : nsresult
1291 0 : RasterImage::SetFrameBlendMethod(PRUint32 aFrameNum, PRInt32 aBlendMethod)
1292 : {
1293 0 : if (mError)
1294 0 : return NS_ERROR_FAILURE;
1295 :
1296 0 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1297 0 : if (aFrameNum >= mFrames.Length())
1298 0 : return NS_ERROR_INVALID_ARG;
1299 :
1300 0 : imgFrame *frame = GetImgFrame(aFrameNum);
1301 0 : NS_ABORT_IF_FALSE(frame, "Calling SetFrameBlendMethod on frame that doesn't exist!");
1302 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1303 :
1304 0 : frame->SetBlendMethod(aBlendMethod);
1305 :
1306 0 : return NS_OK;
1307 : }
1308 :
1309 : nsresult
1310 1 : RasterImage::SetFrameHasNoAlpha(PRUint32 aFrameNum)
1311 : {
1312 1 : if (mError)
1313 0 : return NS_ERROR_FAILURE;
1314 :
1315 1 : NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1316 1 : if (aFrameNum >= mFrames.Length())
1317 0 : return NS_ERROR_INVALID_ARG;
1318 :
1319 1 : imgFrame *frame = GetImgFrame(aFrameNum);
1320 1 : NS_ABORT_IF_FALSE(frame, "Calling SetFrameHasNoAlpha on frame that doesn't exist!");
1321 1 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1322 :
1323 1 : frame->SetHasNoAlpha();
1324 :
1325 1 : return NS_OK;
1326 : }
1327 :
1328 : nsresult
1329 15 : RasterImage::DecodingComplete()
1330 : {
1331 15 : if (mError)
1332 0 : return NS_ERROR_FAILURE;
1333 :
1334 : // Flag that we're done decoding.
1335 : // XXX - these should probably be combined when we fix animated image
1336 : // discarding with bug 500402.
1337 15 : mDecoded = true;
1338 15 : mHasBeenDecoded = true;
1339 :
1340 : nsresult rv;
1341 :
1342 : // We now have one of the qualifications for discarding. Re-evaluate.
1343 15 : if (CanDiscard()) {
1344 0 : NS_ABORT_IF_FALSE(!DiscardingActive(),
1345 : "We shouldn't have been discardable before this");
1346 0 : rv = DiscardTracker::Reset(&mDiscardTrackerNode);
1347 0 : CONTAINER_ENSURE_SUCCESS(rv);
1348 : }
1349 :
1350 : // If there's only 1 frame, optimize it. Optimizing animated images
1351 : // is not supported.
1352 : //
1353 : // We don't optimize the frame for multipart images because we reuse
1354 : // the frame.
1355 15 : if ((mFrames.Length() == 1) && !mMultipart) {
1356 14 : rv = mFrames[0]->Optimize();
1357 14 : NS_ENSURE_SUCCESS(rv, rv);
1358 : }
1359 :
1360 15 : return NS_OK;
1361 : }
1362 :
1363 : //******************************************************************************
1364 : /* void StartAnimation () */
1365 : nsresult
1366 0 : RasterImage::StartAnimation()
1367 : {
1368 0 : if (mError)
1369 0 : return NS_ERROR_FAILURE;
1370 :
1371 0 : NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
1372 :
1373 0 : EnsureAnimExists();
1374 :
1375 0 : imgFrame* currentFrame = GetCurrentImgFrame();
1376 0 : if (currentFrame) {
1377 0 : if (currentFrame->GetTimeout() < 0) { // -1 means display this frame forever
1378 0 : mAnimationFinished = true;
1379 0 : return NS_ERROR_ABORT;
1380 : }
1381 :
1382 : // We need to set the time that this initial frame was first displayed, as
1383 : // this is used in AdvanceFrame().
1384 0 : mAnim->currentAnimationFrameTime = TimeStamp::Now();
1385 : }
1386 :
1387 0 : return NS_OK;
1388 : }
1389 :
1390 : //******************************************************************************
1391 : /* void stopAnimation (); */
1392 : nsresult
1393 0 : RasterImage::StopAnimation()
1394 : {
1395 0 : NS_ABORT_IF_FALSE(mAnimating, "Should be animating!");
1396 :
1397 0 : if (mError)
1398 0 : return NS_ERROR_FAILURE;
1399 :
1400 0 : return NS_OK;
1401 : }
1402 :
1403 : //******************************************************************************
1404 : /* void resetAnimation (); */
1405 : NS_IMETHODIMP
1406 0 : RasterImage::ResetAnimation()
1407 : {
1408 0 : if (mError)
1409 0 : return NS_ERROR_FAILURE;
1410 :
1411 0 : if (mAnimationMode == kDontAnimMode ||
1412 0 : !mAnim || mAnim->currentAnimationFrameIndex == 0)
1413 0 : return NS_OK;
1414 :
1415 0 : mAnimationFinished = false;
1416 :
1417 0 : if (mAnimating)
1418 0 : StopAnimation();
1419 :
1420 0 : mAnim->lastCompositedFrameIndex = -1;
1421 0 : mAnim->currentAnimationFrameIndex = 0;
1422 0 : mImageContainer = nsnull;
1423 :
1424 : // Note - We probably want to kick off a redecode somewhere around here when
1425 : // we fix bug 500402.
1426 :
1427 : // Update display if we were animating before
1428 0 : nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
1429 0 : if (mAnimating && observer)
1430 0 : observer->FrameChanged(nsnull, this, &(mAnim->firstFrameRefreshArea));
1431 :
1432 0 : if (ShouldAnimate()) {
1433 0 : StartAnimation();
1434 : // The animation may not have been running before, if mAnimationFinished
1435 : // was false (before we changed it to true in this function). So, mark the
1436 : // animation as running.
1437 0 : mAnimating = true;
1438 : }
1439 :
1440 0 : return NS_OK;
1441 : }
1442 :
1443 : void
1444 4 : RasterImage::SetLoopCount(PRInt32 aLoopCount)
1445 : {
1446 4 : if (mError)
1447 0 : return;
1448 :
1449 : // -1 infinite
1450 : // 0 no looping, one iteration
1451 : // 1 one loop, two iterations
1452 : // ...
1453 4 : mLoopCount = aLoopCount;
1454 : }
1455 :
1456 : nsresult
1457 57 : RasterImage::AddSourceData(const char *aBuffer, PRUint32 aCount)
1458 : {
1459 57 : if (mError)
1460 0 : return NS_ERROR_FAILURE;
1461 :
1462 57 : NS_ENSURE_ARG_POINTER(aBuffer);
1463 57 : nsresult rv = NS_OK;
1464 :
1465 : // We should not call this if we're not initialized
1466 57 : NS_ABORT_IF_FALSE(mInitialized, "Calling AddSourceData() on uninitialized "
1467 : "RasterImage!");
1468 :
1469 : // We should not call this if we're already finished adding source data
1470 57 : NS_ABORT_IF_FALSE(!mHasSourceData, "Calling AddSourceData() after calling "
1471 : "sourceDataComplete()!");
1472 :
1473 : // This call should come straight from necko - no reentrancy allowed
1474 57 : NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
1475 :
1476 : // If we're not storing source data, write it directly to the decoder
1477 57 : if (!StoringSourceData()) {
1478 53 : rv = WriteToDecoder(aBuffer, aCount);
1479 53 : CONTAINER_ENSURE_SUCCESS(rv);
1480 :
1481 : // We're not storing source data, so this data is probably coming straight
1482 : // from the network. In this case, we want to display data as soon as we
1483 : // get it, so we want to flush invalidations after every write.
1484 106 : nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
1485 53 : mInDecoder = true;
1486 53 : mDecoder->FlushInvalidations();
1487 53 : mInDecoder = false;
1488 : }
1489 :
1490 : // Otherwise, we're storing data in the source buffer
1491 : else {
1492 :
1493 : // Store the data
1494 4 : char *newElem = mSourceData.AppendElements(aBuffer, aCount);
1495 4 : if (!newElem)
1496 0 : return NS_ERROR_OUT_OF_MEMORY;
1497 :
1498 : // If there's a decoder open, that means we want to do more decoding.
1499 : // Wake up the worker.
1500 4 : if (mDecoder) {
1501 4 : DecodeWorker::Singleton()->RequestDecode(this);
1502 : }
1503 : }
1504 :
1505 : // Statistics
1506 57 : total_source_bytes += aCount;
1507 57 : if (mDiscardable)
1508 4 : discardable_source_bytes += aCount;
1509 57 : PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
1510 : ("CompressedImageAccounting: Added compressed data to RasterImage %p (%s). "
1511 : "Total Containers: %d, Discardable containers: %d, "
1512 : "Total source bytes: %lld, Source bytes for discardable containers %lld",
1513 : this,
1514 : mSourceDataMimeType.get(),
1515 : num_containers,
1516 : num_discardable_containers,
1517 : total_source_bytes,
1518 : discardable_source_bytes));
1519 :
1520 57 : return NS_OK;
1521 : }
1522 :
1523 : /* Note! buf must be declared as char buf[9]; */
1524 : // just used for logging and hashing the header
1525 : static void
1526 0 : get_header_str (char *buf, char *data, PRSize data_len)
1527 : {
1528 : int i;
1529 : int n;
1530 : static char hex[] = "0123456789abcdef";
1531 :
1532 0 : n = data_len < 4 ? data_len : 4;
1533 :
1534 0 : for (i = 0; i < n; i++) {
1535 0 : buf[i * 2] = hex[(data[i] >> 4) & 0x0f];
1536 0 : buf[i * 2 + 1] = hex[data[i] & 0x0f];
1537 : }
1538 :
1539 0 : buf[i * 2] = 0;
1540 0 : }
1541 :
1542 : nsresult
1543 22 : RasterImage::SourceDataComplete()
1544 : {
1545 22 : if (mError)
1546 4 : return NS_ERROR_FAILURE;
1547 :
1548 : // If we've been called before, ignore. Otherwise, flag that we have everything
1549 18 : if (mHasSourceData)
1550 0 : return NS_OK;
1551 18 : mHasSourceData = true;
1552 :
1553 : // This call should come straight from necko - no reentrancy allowed
1554 18 : NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
1555 :
1556 : // If we're not storing any source data, then all the data was written
1557 : // directly to the decoder in the AddSourceData() calls. This means we're
1558 : // done, so we can shut down the decoder.
1559 18 : if (!StoringSourceData()) {
1560 14 : nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
1561 14 : CONTAINER_ENSURE_SUCCESS(rv);
1562 : }
1563 :
1564 : // If there's a decoder open, synchronously decode the beginning of the image
1565 : // to check for errors and get the image's size. (If we already have the
1566 : // image's size, this does nothing.) Then kick off an async decode of the
1567 : // rest of the image.
1568 17 : if (mDecoder) {
1569 4 : nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
1570 4 : CONTAINER_ENSURE_SUCCESS(rv);
1571 : }
1572 :
1573 : // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
1574 : // finish decoding this image.
1575 17 : if (mDecoder) {
1576 0 : DecodeWorker::Singleton()->RequestDecode(this);
1577 : }
1578 :
1579 : // Free up any extra space in the backing buffer
1580 17 : mSourceData.Compact();
1581 :
1582 : // Log header information
1583 17 : if (PR_LOG_TEST(gCompressedImageAccountingLog, PR_LOG_DEBUG)) {
1584 : char buf[9];
1585 0 : get_header_str(buf, mSourceData.Elements(), mSourceData.Length());
1586 0 : PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
1587 : ("CompressedImageAccounting: RasterImage::SourceDataComplete() - data "
1588 : "is done for container %p (%s) - header %p is 0x%s (length %d)",
1589 : this,
1590 : mSourceDataMimeType.get(),
1591 : mSourceData.Elements(),
1592 : buf,
1593 : mSourceData.Length()));
1594 : }
1595 :
1596 : // We now have one of the qualifications for discarding. Re-evaluate.
1597 17 : if (CanDiscard()) {
1598 0 : nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
1599 0 : CONTAINER_ENSURE_SUCCESS(rv);
1600 : }
1601 17 : return NS_OK;
1602 : }
1603 :
1604 : nsresult
1605 0 : RasterImage::NewSourceData()
1606 : {
1607 : nsresult rv;
1608 :
1609 0 : if (mError)
1610 0 : return NS_ERROR_FAILURE;
1611 :
1612 : // The source data should be complete before calling this
1613 0 : NS_ABORT_IF_FALSE(mHasSourceData,
1614 : "Calling NewSourceData before SourceDataComplete!");
1615 0 : if (!mHasSourceData)
1616 0 : return NS_ERROR_ILLEGAL_VALUE;
1617 :
1618 : // Only supported for multipart channels. It wouldn't be too hard to change this,
1619 : // but it would involve making sure that things worked for decode-on-draw and
1620 : // discarding. Presently there's no need for this, so we don't.
1621 0 : NS_ABORT_IF_FALSE(mMultipart, "NewSourceData not supported for multipart");
1622 0 : if (!mMultipart)
1623 0 : return NS_ERROR_ILLEGAL_VALUE;
1624 :
1625 : // We're multipart, so we shouldn't be storing source data
1626 0 : NS_ABORT_IF_FALSE(!StoringSourceData(),
1627 : "Shouldn't be storing source data for multipart");
1628 :
1629 : // We're not storing the source data and we got SourceDataComplete. We should
1630 : // have shut down the previous decoder
1631 0 : NS_ABORT_IF_FALSE(!mDecoder, "Shouldn't have a decoder in NewSourceData");
1632 :
1633 : // The decoder was shut down and we didn't flag an error, so we should be decoded
1634 0 : NS_ABORT_IF_FALSE(mDecoded, "Should be decoded in NewSourceData");
1635 :
1636 : // Reset some flags
1637 0 : mDecoded = false;
1638 0 : mHasSourceData = false;
1639 :
1640 : // We're decode-on-load here. Open up a new decoder just like what happens when
1641 : // we call Init() for decode-on-load images.
1642 0 : rv = InitDecoder(/* aDoSizeDecode = */ false);
1643 0 : CONTAINER_ENSURE_SUCCESS(rv);
1644 :
1645 0 : return NS_OK;
1646 : }
1647 :
1648 : nsresult
1649 0 : RasterImage::SetSourceSizeHint(PRUint32 sizeHint)
1650 : {
1651 0 : if (sizeHint && StoringSourceData())
1652 0 : return mSourceData.SetCapacity(sizeHint) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1653 0 : return NS_OK;
1654 : }
1655 :
1656 : //******************************************************************************
1657 : // DoComposite gets called when the timer for animation get fired and we have to
1658 : // update the composited frame of the animation.
1659 : nsresult
1660 0 : RasterImage::DoComposite(nsIntRect* aDirtyRect,
1661 : imgFrame* aPrevFrame,
1662 : imgFrame* aNextFrame,
1663 : PRInt32 aNextFrameIndex)
1664 : {
1665 0 : NS_ENSURE_ARG_POINTER(aDirtyRect);
1666 0 : NS_ENSURE_ARG_POINTER(aPrevFrame);
1667 0 : NS_ENSURE_ARG_POINTER(aNextFrame);
1668 :
1669 0 : PRInt32 prevFrameDisposalMethod = aPrevFrame->GetFrameDisposalMethod();
1670 0 : if (prevFrameDisposalMethod == kDisposeRestorePrevious &&
1671 0 : !mAnim->compositingPrevFrame)
1672 0 : prevFrameDisposalMethod = kDisposeClear;
1673 :
1674 0 : nsIntRect prevFrameRect = aPrevFrame->GetRect();
1675 : bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
1676 : prevFrameRect.width == mSize.width &&
1677 0 : prevFrameRect.height == mSize.height);
1678 :
1679 : // Optimization: DisposeClearAll if the previous frame is the same size as
1680 : // container and it's clearing itself
1681 0 : if (isFullPrevFrame &&
1682 : (prevFrameDisposalMethod == kDisposeClear))
1683 0 : prevFrameDisposalMethod = kDisposeClearAll;
1684 :
1685 0 : PRInt32 nextFrameDisposalMethod = aNextFrame->GetFrameDisposalMethod();
1686 0 : nsIntRect nextFrameRect = aNextFrame->GetRect();
1687 : bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
1688 : nextFrameRect.width == mSize.width &&
1689 0 : nextFrameRect.height == mSize.height);
1690 :
1691 0 : if (!aNextFrame->GetIsPaletted()) {
1692 : // Optimization: Skip compositing if the previous frame wants to clear the
1693 : // whole image
1694 0 : if (prevFrameDisposalMethod == kDisposeClearAll) {
1695 0 : aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
1696 0 : return NS_OK;
1697 : }
1698 :
1699 : // Optimization: Skip compositing if this frame is the same size as the
1700 : // container and it's fully drawing over prev frame (no alpha)
1701 0 : if (isFullNextFrame &&
1702 : (nextFrameDisposalMethod != kDisposeRestorePrevious) &&
1703 0 : !aNextFrame->GetHasAlpha()) {
1704 0 : aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
1705 0 : return NS_OK;
1706 : }
1707 : }
1708 :
1709 : // Calculate area that needs updating
1710 0 : switch (prevFrameDisposalMethod) {
1711 : default:
1712 : case kDisposeNotSpecified:
1713 : case kDisposeKeep:
1714 0 : *aDirtyRect = nextFrameRect;
1715 0 : break;
1716 :
1717 : case kDisposeClearAll:
1718 : // Whole image container is cleared
1719 0 : aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
1720 0 : break;
1721 :
1722 : case kDisposeClear:
1723 : // Calc area that needs to be redrawn (the combination of previous and
1724 : // this frame)
1725 : // XXX - This could be done with multiple framechanged calls
1726 : // Having prevFrame way at the top of the image, and nextFrame
1727 : // way at the bottom, and both frames being small, we'd be
1728 : // telling framechanged to refresh the whole image when only two
1729 : // small areas are needed.
1730 0 : aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
1731 0 : break;
1732 :
1733 : case kDisposeRestorePrevious:
1734 0 : aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
1735 0 : break;
1736 : }
1737 :
1738 : // Optimization:
1739 : // Skip compositing if the last composited frame is this frame
1740 : // (Only one composited frame was made for this animation. Example:
1741 : // Only Frame 3 of a 10 frame image required us to build a composite frame
1742 : // On the second loop, we do not need to rebuild the frame
1743 : // since it's still sitting in compositingFrame)
1744 0 : if (mAnim->lastCompositedFrameIndex == aNextFrameIndex) {
1745 0 : return NS_OK;
1746 : }
1747 :
1748 0 : bool needToBlankComposite = false;
1749 :
1750 : // Create the Compositing Frame
1751 0 : if (!mAnim->compositingFrame) {
1752 0 : mAnim->compositingFrame = new imgFrame();
1753 : nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
1754 0 : gfxASurface::ImageFormatARGB32);
1755 0 : if (NS_FAILED(rv)) {
1756 0 : mAnim->compositingFrame = nsnull;
1757 0 : return rv;
1758 : }
1759 0 : needToBlankComposite = true;
1760 0 : } else if (aNextFrameIndex != mAnim->lastCompositedFrameIndex+1) {
1761 :
1762 : // If we are not drawing on top of last composited frame,
1763 : // then we are building a new composite frame, so let's clear it first.
1764 0 : needToBlankComposite = true;
1765 : }
1766 :
1767 : // More optimizations possible when next frame is not transparent
1768 : // But if the next frame has kDisposeRestorePrevious,
1769 : // this "no disposal" optimization is not possible,
1770 : // because the frame in "after disposal operation" state
1771 : // needs to be stored in compositingFrame, so it can be
1772 : // copied into compositingPrevFrame later.
1773 0 : bool doDisposal = true;
1774 0 : if (!aNextFrame->GetHasAlpha() &&
1775 : nextFrameDisposalMethod != kDisposeRestorePrevious) {
1776 0 : if (isFullNextFrame) {
1777 : // Optimization: No need to dispose prev.frame when
1778 : // next frame is full frame and not transparent.
1779 0 : doDisposal = false;
1780 : // No need to blank the composite frame
1781 0 : needToBlankComposite = false;
1782 : } else {
1783 0 : if ((prevFrameRect.x >= nextFrameRect.x) &&
1784 : (prevFrameRect.y >= nextFrameRect.y) &&
1785 : (prevFrameRect.x + prevFrameRect.width <= nextFrameRect.x + nextFrameRect.width) &&
1786 : (prevFrameRect.y + prevFrameRect.height <= nextFrameRect.y + nextFrameRect.height)) {
1787 : // Optimization: No need to dispose prev.frame when
1788 : // next frame fully overlaps previous frame.
1789 0 : doDisposal = false;
1790 : }
1791 : }
1792 : }
1793 :
1794 0 : if (doDisposal) {
1795 : // Dispose of previous: clear, restore, or keep (copy)
1796 0 : switch (prevFrameDisposalMethod) {
1797 : case kDisposeClear:
1798 0 : if (needToBlankComposite) {
1799 : // If we just created the composite, it could have anything in it's
1800 : // buffer. Clear whole frame
1801 0 : ClearFrame(mAnim->compositingFrame);
1802 : } else {
1803 : // Only blank out previous frame area (both color & Mask/Alpha)
1804 0 : ClearFrame(mAnim->compositingFrame, prevFrameRect);
1805 : }
1806 0 : break;
1807 :
1808 : case kDisposeClearAll:
1809 0 : ClearFrame(mAnim->compositingFrame);
1810 0 : break;
1811 :
1812 : case kDisposeRestorePrevious:
1813 : // It would be better to copy only the area changed back to
1814 : // compositingFrame.
1815 0 : if (mAnim->compositingPrevFrame) {
1816 0 : CopyFrameImage(mAnim->compositingPrevFrame, mAnim->compositingFrame);
1817 :
1818 : // destroy only if we don't need it for this frame's disposal
1819 0 : if (nextFrameDisposalMethod != kDisposeRestorePrevious)
1820 0 : mAnim->compositingPrevFrame = nsnull;
1821 : } else {
1822 0 : ClearFrame(mAnim->compositingFrame);
1823 : }
1824 0 : break;
1825 :
1826 : default:
1827 : // Copy previous frame into compositingFrame before we put the new frame on top
1828 : // Assumes that the previous frame represents a full frame (it could be
1829 : // smaller in size than the container, as long as the frame before it erased
1830 : // itself)
1831 : // Note: Frame 1 never gets into DoComposite(), so (aNextFrameIndex - 1) will
1832 : // always be a valid frame number.
1833 0 : if (mAnim->lastCompositedFrameIndex != aNextFrameIndex - 1) {
1834 0 : if (isFullPrevFrame && !aPrevFrame->GetIsPaletted()) {
1835 : // Just copy the bits
1836 0 : CopyFrameImage(aPrevFrame, mAnim->compositingFrame);
1837 : } else {
1838 0 : if (needToBlankComposite) {
1839 : // Only blank composite when prev is transparent or not full.
1840 0 : if (aPrevFrame->GetHasAlpha() || !isFullPrevFrame) {
1841 0 : ClearFrame(mAnim->compositingFrame);
1842 : }
1843 : }
1844 0 : DrawFrameTo(aPrevFrame, mAnim->compositingFrame, prevFrameRect);
1845 : }
1846 : }
1847 : }
1848 0 : } else if (needToBlankComposite) {
1849 : // If we just created the composite, it could have anything in it's
1850 : // buffers. Clear them
1851 0 : ClearFrame(mAnim->compositingFrame);
1852 : }
1853 :
1854 : // Check if the frame we are composing wants the previous image restored afer
1855 : // it is done. Don't store it (again) if last frame wanted its image restored
1856 : // too
1857 0 : if ((nextFrameDisposalMethod == kDisposeRestorePrevious) &&
1858 : (prevFrameDisposalMethod != kDisposeRestorePrevious)) {
1859 : // We are storing the whole image.
1860 : // It would be better if we just stored the area that nextFrame is going to
1861 : // overwrite.
1862 0 : if (!mAnim->compositingPrevFrame) {
1863 0 : mAnim->compositingPrevFrame = new imgFrame();
1864 : nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
1865 0 : gfxASurface::ImageFormatARGB32);
1866 0 : if (NS_FAILED(rv)) {
1867 0 : mAnim->compositingPrevFrame = nsnull;
1868 0 : return rv;
1869 : }
1870 : }
1871 :
1872 0 : CopyFrameImage(mAnim->compositingFrame, mAnim->compositingPrevFrame);
1873 : }
1874 :
1875 : // blit next frame into it's correct spot
1876 0 : DrawFrameTo(aNextFrame, mAnim->compositingFrame, nextFrameRect);
1877 :
1878 : // Set timeout of CompositeFrame to timeout of frame we just composed
1879 : // Bug 177948
1880 0 : PRInt32 timeout = aNextFrame->GetTimeout();
1881 0 : mAnim->compositingFrame->SetTimeout(timeout);
1882 :
1883 : // Tell the image that it is fully 'downloaded'.
1884 0 : nsresult rv = mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect());
1885 0 : if (NS_FAILED(rv)) {
1886 0 : return rv;
1887 : }
1888 :
1889 : // We don't want to keep composite images for 8bit frames.
1890 : // Also this optimization won't work if the next frame has
1891 : // kDisposeRestorePrevious, because it would need to be restored
1892 : // into "after prev disposal but before next blend" state,
1893 : // not into empty frame.
1894 0 : if (isFullNextFrame && mAnimationMode == kNormalAnimMode && mLoopCount != 0 &&
1895 : nextFrameDisposalMethod != kDisposeRestorePrevious &&
1896 0 : !aNextFrame->GetIsPaletted()) {
1897 : // We have a composited full frame
1898 : // Store the composited frame into the mFrames[..] so we don't have to
1899 : // continuously re-build it
1900 : // Then set the previous frame's disposal to CLEAR_ALL so we just draw the
1901 : // frame next time around
1902 0 : if (CopyFrameImage(mAnim->compositingFrame, aNextFrame)) {
1903 0 : aPrevFrame->SetFrameDisposalMethod(kDisposeClearAll);
1904 0 : mAnim->lastCompositedFrameIndex = -1;
1905 0 : return NS_OK;
1906 : }
1907 : }
1908 :
1909 0 : mAnim->lastCompositedFrameIndex = aNextFrameIndex;
1910 :
1911 0 : return NS_OK;
1912 : }
1913 :
1914 : //******************************************************************************
1915 : // Fill aFrame with black. Does also clears the mask.
1916 : void
1917 0 : RasterImage::ClearFrame(imgFrame *aFrame)
1918 : {
1919 0 : if (!aFrame)
1920 0 : return;
1921 :
1922 0 : nsresult rv = aFrame->LockImageData();
1923 0 : if (NS_FAILED(rv))
1924 0 : return;
1925 :
1926 0 : nsRefPtr<gfxASurface> surf;
1927 0 : aFrame->GetSurface(getter_AddRefs(surf));
1928 :
1929 : // Erase the surface to transparent
1930 0 : gfxContext ctx(surf);
1931 0 : ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
1932 0 : ctx.Paint();
1933 :
1934 0 : aFrame->UnlockImageData();
1935 : }
1936 :
1937 : //******************************************************************************
1938 : void
1939 0 : RasterImage::ClearFrame(imgFrame *aFrame, nsIntRect &aRect)
1940 : {
1941 0 : if (!aFrame || aRect.width <= 0 || aRect.height <= 0)
1942 0 : return;
1943 :
1944 0 : nsresult rv = aFrame->LockImageData();
1945 0 : if (NS_FAILED(rv))
1946 0 : return;
1947 :
1948 0 : nsRefPtr<gfxASurface> surf;
1949 0 : aFrame->GetSurface(getter_AddRefs(surf));
1950 :
1951 : // Erase the destination rectangle to transparent
1952 0 : gfxContext ctx(surf);
1953 0 : ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
1954 0 : ctx.Rectangle(gfxRect(aRect.x, aRect.y, aRect.width, aRect.height));
1955 0 : ctx.Fill();
1956 :
1957 0 : aFrame->UnlockImageData();
1958 : }
1959 :
1960 :
1961 : //******************************************************************************
1962 : // Whether we succeed or fail will not cause a crash, and there's not much
1963 : // we can do about a failure, so there we don't return a nsresult
1964 : bool
1965 0 : RasterImage::CopyFrameImage(imgFrame *aSrcFrame,
1966 : imgFrame *aDstFrame)
1967 : {
1968 : PRUint8* aDataSrc;
1969 : PRUint8* aDataDest;
1970 : PRUint32 aDataLengthSrc;
1971 : PRUint32 aDataLengthDest;
1972 :
1973 0 : if (!aSrcFrame || !aDstFrame)
1974 0 : return false;
1975 :
1976 0 : if (NS_FAILED(aDstFrame->LockImageData()))
1977 0 : return false;
1978 :
1979 : // Copy Image Over
1980 0 : aSrcFrame->GetImageData(&aDataSrc, &aDataLengthSrc);
1981 0 : aDstFrame->GetImageData(&aDataDest, &aDataLengthDest);
1982 0 : if (!aDataDest || !aDataSrc || aDataLengthDest != aDataLengthSrc) {
1983 0 : aDstFrame->UnlockImageData();
1984 0 : return false;
1985 : }
1986 0 : memcpy(aDataDest, aDataSrc, aDataLengthSrc);
1987 0 : aDstFrame->UnlockImageData();
1988 :
1989 0 : return true;
1990 : }
1991 :
1992 : //******************************************************************************
1993 : /*
1994 : * aSrc is the current frame being drawn,
1995 : * aDst is the composition frame where the current frame is drawn into.
1996 : * aSrcRect is the size of the current frame, and the position of that frame
1997 : * in the composition frame.
1998 : */
1999 : nsresult
2000 0 : RasterImage::DrawFrameTo(imgFrame *aSrc,
2001 : imgFrame *aDst,
2002 : nsIntRect& aSrcRect)
2003 : {
2004 0 : NS_ENSURE_ARG_POINTER(aSrc);
2005 0 : NS_ENSURE_ARG_POINTER(aDst);
2006 :
2007 0 : nsIntRect dstRect = aDst->GetRect();
2008 :
2009 : // According to both AGIF and APNG specs, offsets are unsigned
2010 0 : if (aSrcRect.x < 0 || aSrcRect.y < 0) {
2011 0 : NS_WARNING("RasterImage::DrawFrameTo: negative offsets not allowed");
2012 0 : return NS_ERROR_FAILURE;
2013 : }
2014 : // Outside the destination frame, skip it
2015 0 : if ((aSrcRect.x > dstRect.width) || (aSrcRect.y > dstRect.height)) {
2016 0 : return NS_OK;
2017 : }
2018 :
2019 0 : if (aSrc->GetIsPaletted()) {
2020 : // Larger than the destination frame, clip it
2021 0 : PRInt32 width = NS_MIN(aSrcRect.width, dstRect.width - aSrcRect.x);
2022 0 : PRInt32 height = NS_MIN(aSrcRect.height, dstRect.height - aSrcRect.y);
2023 :
2024 : // The clipped image must now fully fit within destination image frame
2025 0 : NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
2026 : (aSrcRect.x + width <= dstRect.width) &&
2027 : (aSrcRect.y + height <= dstRect.height),
2028 : "RasterImage::DrawFrameTo: Invalid aSrcRect");
2029 :
2030 : // clipped image size may be smaller than source, but not larger
2031 0 : NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
2032 : "RasterImage::DrawFrameTo: source must be smaller than dest");
2033 :
2034 0 : if (NS_FAILED(aDst->LockImageData()))
2035 0 : return NS_ERROR_FAILURE;
2036 :
2037 : // Get pointers to image data
2038 : PRUint32 size;
2039 : PRUint8 *srcPixels;
2040 : PRUint32 *colormap;
2041 : PRUint32 *dstPixels;
2042 :
2043 0 : aSrc->GetImageData(&srcPixels, &size);
2044 0 : aSrc->GetPaletteData(&colormap, &size);
2045 0 : aDst->GetImageData((PRUint8 **)&dstPixels, &size);
2046 0 : if (!srcPixels || !dstPixels || !colormap) {
2047 0 : aDst->UnlockImageData();
2048 0 : return NS_ERROR_FAILURE;
2049 : }
2050 :
2051 : // Skip to the right offset
2052 0 : dstPixels += aSrcRect.x + (aSrcRect.y * dstRect.width);
2053 0 : if (!aSrc->GetHasAlpha()) {
2054 0 : for (PRInt32 r = height; r > 0; --r) {
2055 0 : for (PRInt32 c = 0; c < width; c++) {
2056 0 : dstPixels[c] = colormap[srcPixels[c]];
2057 : }
2058 : // Go to the next row in the source resp. destination image
2059 0 : srcPixels += aSrcRect.width;
2060 0 : dstPixels += dstRect.width;
2061 : }
2062 : } else {
2063 0 : for (PRInt32 r = height; r > 0; --r) {
2064 0 : for (PRInt32 c = 0; c < width; c++) {
2065 0 : const PRUint32 color = colormap[srcPixels[c]];
2066 0 : if (color)
2067 0 : dstPixels[c] = color;
2068 : }
2069 : // Go to the next row in the source resp. destination image
2070 0 : srcPixels += aSrcRect.width;
2071 0 : dstPixels += dstRect.width;
2072 : }
2073 : }
2074 :
2075 0 : aDst->UnlockImageData();
2076 0 : return NS_OK;
2077 : }
2078 :
2079 0 : nsRefPtr<gfxPattern> srcPatt;
2080 0 : aSrc->GetPattern(getter_AddRefs(srcPatt));
2081 :
2082 0 : aDst->LockImageData();
2083 0 : nsRefPtr<gfxASurface> dstSurf;
2084 0 : aDst->GetSurface(getter_AddRefs(dstSurf));
2085 :
2086 0 : gfxContext dst(dstSurf);
2087 0 : dst.Translate(gfxPoint(aSrcRect.x, aSrcRect.y));
2088 0 : dst.Rectangle(gfxRect(0, 0, aSrcRect.width, aSrcRect.height), true);
2089 :
2090 : // first clear the surface if the blend flag says so
2091 0 : PRInt32 blendMethod = aSrc->GetBlendMethod();
2092 0 : if (blendMethod == kBlendSource) {
2093 0 : gfxContext::GraphicsOperator defaultOperator = dst.CurrentOperator();
2094 0 : dst.SetOperator(gfxContext::OPERATOR_CLEAR);
2095 0 : dst.Fill();
2096 0 : dst.SetOperator(defaultOperator);
2097 : }
2098 0 : dst.SetPattern(srcPatt);
2099 0 : dst.Paint();
2100 :
2101 0 : aDst->UnlockImageData();
2102 :
2103 0 : return NS_OK;
2104 : }
2105 :
2106 :
2107 : /********* Methods to implement lazy allocation of nsIProperties object *************/
2108 : NS_IMETHODIMP
2109 0 : RasterImage::Get(const char *prop, const nsIID & iid, void * *result)
2110 : {
2111 0 : if (!mProperties)
2112 0 : return NS_ERROR_FAILURE;
2113 0 : return mProperties->Get(prop, iid, result);
2114 : }
2115 :
2116 : NS_IMETHODIMP
2117 0 : RasterImage::Set(const char *prop, nsISupports *value)
2118 : {
2119 0 : if (!mProperties)
2120 0 : mProperties = do_CreateInstance("@mozilla.org/properties;1");
2121 0 : if (!mProperties)
2122 0 : return NS_ERROR_OUT_OF_MEMORY;
2123 0 : return mProperties->Set(prop, value);
2124 : }
2125 :
2126 : NS_IMETHODIMP
2127 0 : RasterImage::Has(const char *prop, bool *_retval)
2128 : {
2129 0 : NS_ENSURE_ARG_POINTER(_retval);
2130 0 : if (!mProperties) {
2131 0 : *_retval = false;
2132 0 : return NS_OK;
2133 : }
2134 0 : return mProperties->Has(prop, _retval);
2135 : }
2136 :
2137 : NS_IMETHODIMP
2138 0 : RasterImage::Undefine(const char *prop)
2139 : {
2140 0 : if (!mProperties)
2141 0 : return NS_ERROR_FAILURE;
2142 0 : return mProperties->Undefine(prop);
2143 : }
2144 :
2145 : NS_IMETHODIMP
2146 0 : RasterImage::GetKeys(PRUint32 *count, char ***keys)
2147 : {
2148 0 : if (!mProperties) {
2149 0 : *count = 0;
2150 0 : *keys = nsnull;
2151 0 : return NS_OK;
2152 : }
2153 0 : return mProperties->GetKeys(count, keys);
2154 : }
2155 :
2156 : void
2157 0 : RasterImage::Discard(bool force)
2158 : {
2159 : // We should be ok for discard
2160 0 : NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!");
2161 :
2162 : // We should never discard when we have an active decoder
2163 0 : NS_ABORT_IF_FALSE(!mDecoder, "Asked to discard with open decoder!");
2164 :
2165 : // As soon as an image becomes animated, it becomes non-discardable and any
2166 : // timers are cancelled.
2167 0 : NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!");
2168 :
2169 : // For post-operation logging
2170 0 : int old_frame_count = mFrames.Length();
2171 :
2172 : // Delete all the decoded frames, then clear the array.
2173 0 : for (int i = 0; i < old_frame_count; ++i)
2174 0 : delete mFrames[i];
2175 0 : mFrames.Clear();
2176 :
2177 : // Flag that we no longer have decoded frames for this image
2178 0 : mDecoded = false;
2179 :
2180 : // Notify that we discarded
2181 0 : nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
2182 0 : if (observer)
2183 0 : observer->OnDiscard(nsnull);
2184 :
2185 0 : if (force)
2186 0 : DiscardTracker::Remove(&mDiscardTrackerNode);
2187 :
2188 : // Log
2189 0 : PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG,
2190 : ("CompressedImageAccounting: discarded uncompressed image "
2191 : "data from RasterImage %p (%s) - %d frames (cached count: %d); "
2192 : "Total Containers: %d, Discardable containers: %d, "
2193 : "Total source bytes: %lld, Source bytes for discardable containers %lld",
2194 : this,
2195 : mSourceDataMimeType.get(),
2196 : old_frame_count,
2197 : mFrames.Length(),
2198 : num_containers,
2199 : num_discardable_containers,
2200 : total_source_bytes,
2201 : discardable_source_bytes));
2202 0 : }
2203 :
2204 : // Helper method to determine if we can discard an image
2205 : bool
2206 67 : RasterImage::CanDiscard() {
2207 67 : return (DiscardingEnabled() && // Globally enabled...
2208 : mDiscardable && // ...Enabled at creation time...
2209 : (mLockCount == 0) && // ...not temporarily disabled...
2210 : mHasSourceData && // ...have the source data...
2211 67 : mDecoded); // ...and have something to discard.
2212 : }
2213 :
2214 : bool
2215 0 : RasterImage::CanForciblyDiscard() {
2216 : return mDiscardable && // ...Enabled at creation time...
2217 0 : mHasSourceData; // ...have the source data...
2218 : }
2219 :
2220 : // Helper method to tell us whether the clock is currently running for
2221 : // discarding this image. Mainly for assertions.
2222 : bool
2223 26 : RasterImage::DiscardingActive() {
2224 26 : return !!(mDiscardTrackerNode.prev || mDiscardTrackerNode.next);
2225 : }
2226 :
2227 : // Helper method to determine if we're storing the source data in a buffer
2228 : // or just writing it directly to the decoder
2229 : bool
2230 92 : RasterImage::StoringSourceData() const {
2231 92 : return (mDecodeOnDraw || mDiscardable);
2232 : }
2233 :
2234 :
2235 : // Sets up a decoder for this image. It is an error to call this function
2236 : // when decoding is already in process (ie - when mDecoder is non-null).
2237 : nsresult
2238 24 : RasterImage::InitDecoder(bool aDoSizeDecode)
2239 : {
2240 : // Ensure that the decoder is not already initialized
2241 24 : NS_ABORT_IF_FALSE(!mDecoder, "Calling InitDecoder() while already decoding!");
2242 :
2243 : // We shouldn't be firing up a decoder if we already have the frames decoded
2244 24 : NS_ABORT_IF_FALSE(!mDecoded, "Calling InitDecoder() but already decoded!");
2245 :
2246 : // Since we're not decoded, we should not have a discard timer active
2247 24 : NS_ABORT_IF_FALSE(!DiscardingActive(), "Discard Timer active in InitDecoder()!");
2248 :
2249 : // Figure out which decoder we want
2250 24 : eDecoderType type = GetDecoderType(mSourceDataMimeType.get());
2251 24 : CONTAINER_ENSURE_TRUE(type != eDecoderType_unknown, NS_IMAGELIB_ERROR_NO_DECODER);
2252 :
2253 40 : nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
2254 : // Instantiate the appropriate decoder
2255 20 : switch (type) {
2256 : case eDecoderType_png:
2257 12 : mDecoder = new nsPNGDecoder(*this, observer);
2258 6 : break;
2259 : case eDecoderType_gif:
2260 8 : mDecoder = new nsGIFDecoder2(*this, observer);
2261 4 : break;
2262 : case eDecoderType_jpeg:
2263 10 : mDecoder = new nsJPEGDecoder(*this, observer);
2264 5 : break;
2265 : case eDecoderType_bmp:
2266 0 : mDecoder = new nsBMPDecoder(*this, observer);
2267 0 : break;
2268 : case eDecoderType_ico:
2269 10 : mDecoder = new nsICODecoder(*this, observer);
2270 5 : break;
2271 : case eDecoderType_icon:
2272 0 : mDecoder = new nsIconDecoder(*this, observer);
2273 0 : break;
2274 : default:
2275 0 : NS_ABORT_IF_FALSE(0, "Shouldn't get here!");
2276 : }
2277 :
2278 : // Initialize the decoder
2279 20 : mDecoder->SetSizeDecode(aDoSizeDecode);
2280 20 : mDecoder->SetDecodeFlags(mFrameDecodeFlags);
2281 20 : mDecoder->Init();
2282 20 : CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
2283 :
2284 20 : if (!aDoSizeDecode) {
2285 16 : Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
2286 16 : mDecodeCount++;
2287 16 : Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
2288 : }
2289 :
2290 20 : return NS_OK;
2291 : }
2292 :
2293 : // Flushes, closes, and nulls-out a decoder. Cleans up any related decoding
2294 : // state. It is an error to call this function when there is no initialized
2295 : // decoder.
2296 : //
2297 : // aIntent specifies the intent of the shutdown. If aIntent is
2298 : // eShutdownIntent_Done, an error is flagged if we didn't get what we should
2299 : // have out of the decode. If aIntent is eShutdownIntent_Interrupted, we don't
2300 : // check this. If aIntent is eShutdownIntent_Error, we shut down in error mode.
2301 : nsresult
2302 20 : RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
2303 : {
2304 : // Ensure that our intent is valid
2305 20 : NS_ABORT_IF_FALSE((aIntent >= 0) || (aIntent < eShutdownIntent_AllCount),
2306 : "Invalid shutdown intent");
2307 :
2308 : // Ensure that the decoder is initialized
2309 20 : NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
2310 :
2311 : // Figure out what kind of decode we were doing before we get rid of our decoder
2312 20 : bool wasSizeDecode = mDecoder->IsSizeDecode();
2313 :
2314 : // Finalize the decoder
2315 : // null out mDecoder, _then_ check for errors on the close (otherwise the
2316 : // error routine might re-invoke ShutdownDecoder)
2317 40 : nsRefPtr<Decoder> decoder = mDecoder;
2318 20 : mDecoder = nsnull;
2319 :
2320 20 : mInDecoder = true;
2321 20 : decoder->Finish();
2322 20 : mInDecoder = false;
2323 :
2324 : // Kill off our decode request, if it's pending. (If not, this call is
2325 : // harmless.)
2326 20 : DecodeWorker::Singleton()->StopDecoding(this);
2327 :
2328 20 : nsresult decoderStatus = decoder->GetDecoderError();
2329 20 : if (NS_FAILED(decoderStatus)) {
2330 0 : DoError();
2331 0 : return decoderStatus;
2332 : }
2333 :
2334 : // We just shut down the decoder. If we didn't get what we want, but expected
2335 : // to, flag an error
2336 20 : bool failed = false;
2337 20 : if (wasSizeDecode && !mHasSize)
2338 0 : failed = true;
2339 20 : if (!wasSizeDecode && !mDecoded)
2340 1 : failed = true;
2341 20 : if ((aIntent == eShutdownIntent_Done) && failed) {
2342 1 : DoError();
2343 1 : return NS_ERROR_FAILURE;
2344 : }
2345 :
2346 : // Reset number of decoded bytes
2347 19 : mBytesDecoded = 0;
2348 :
2349 19 : return NS_OK;
2350 : }
2351 :
2352 : // Writes the data to the decoder, updating the total number of bytes written.
2353 : nsresult
2354 59 : RasterImage::WriteToDecoder(const char *aBuffer, PRUint32 aCount)
2355 : {
2356 : // We should have a decoder
2357 59 : NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
2358 :
2359 : // The decoder will start decoding into the current frame (if we have one).
2360 : // When it needs to add another frame, we will unlock this frame and lock the
2361 : // new frame.
2362 : // Our invariant is that, while in the decoder, the last frame is always
2363 : // locked, and all others are unlocked.
2364 59 : if (mFrames.Length() > 0) {
2365 21 : imgFrame *curframe = mFrames.ElementAt(mFrames.Length() - 1);
2366 21 : curframe->LockImageData();
2367 : }
2368 :
2369 : // Write
2370 118 : nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
2371 59 : mInDecoder = true;
2372 59 : mDecoder->Write(aBuffer, aCount);
2373 59 : mInDecoder = false;
2374 :
2375 : // We unlock the current frame, even if that frame is different from the
2376 : // frame we entered the decoder with. (See above.)
2377 59 : if (mFrames.Length() > 0) {
2378 36 : imgFrame *curframe = mFrames.ElementAt(mFrames.Length() - 1);
2379 36 : curframe->UnlockImageData();
2380 : }
2381 :
2382 59 : if (!mDecoder)
2383 0 : return NS_ERROR_FAILURE;
2384 :
2385 59 : CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
2386 :
2387 : // Keep track of the total number of bytes written over the lifetime of the
2388 : // decoder
2389 59 : mBytesDecoded += aCount;
2390 :
2391 59 : return NS_OK;
2392 : }
2393 :
2394 : // This function is called in situations where it's clear that we want the
2395 : // frames in decoded form (Draw, GetFrame, CopyFrame, ExtractFrame, etc).
2396 : // If we're completely decoded, this method resets the discard timer (if
2397 : // we're discardable), since wanting the frames now is a good indicator of
2398 : // wanting them again soon. If we're not decoded, this method kicks off
2399 : // asynchronous decoding to generate the frames.
2400 : nsresult
2401 32 : RasterImage::WantDecodedFrames()
2402 : {
2403 : nsresult rv;
2404 :
2405 : // If we can discard, the clock should be running. Reset it.
2406 32 : if (CanDiscard()) {
2407 0 : NS_ABORT_IF_FALSE(DiscardingActive(),
2408 : "Decoded and discardable but discarding not activated!");
2409 0 : rv = DiscardTracker::Reset(&mDiscardTrackerNode);
2410 0 : CONTAINER_ENSURE_SUCCESS(rv);
2411 : }
2412 :
2413 : // Request a decode (no-op if we're decoded)
2414 32 : return RequestDecode();
2415 : }
2416 :
2417 : //******************************************************************************
2418 : /* void requestDecode() */
2419 : NS_IMETHODIMP
2420 32 : RasterImage::RequestDecode()
2421 : {
2422 : nsresult rv;
2423 :
2424 32 : if (mError)
2425 0 : return NS_ERROR_FAILURE;
2426 :
2427 : // If we're fully decoded, we have nothing to do
2428 32 : if (mDecoded)
2429 23 : return NS_OK;
2430 :
2431 : // If we're not storing source data, we have nothing to do
2432 9 : if (!StoringSourceData())
2433 3 : return NS_OK;
2434 :
2435 : // If we've already got a full decoder running, we have nothing to do
2436 6 : if (mDecoder && !mDecoder->IsSizeDecode())
2437 6 : return NS_OK;
2438 :
2439 : // If our callstack goes through a size decoder, we have a problem.
2440 : // We need to shutdown the size decode and replace it with a full
2441 : // decoder, but can't do that from within the decoder itself. Thus, we post
2442 : // an asynchronous event to the event loop to do it later. Since
2443 : // RequestDecode() is an asynchronous function this works fine (though it's
2444 : // a little slower).
2445 0 : if (mInDecoder) {
2446 0 : nsRefPtr<imgDecodeRequestor> requestor = new imgDecodeRequestor(this);
2447 0 : return NS_DispatchToCurrentThread(requestor);
2448 : }
2449 :
2450 :
2451 : // If we have a size decode open, interrupt it and shut it down; or if
2452 : // the decoder has different flags than what we need
2453 0 : if (mDecoder &&
2454 0 : (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
2455 : {
2456 0 : rv = ShutdownDecoder(eShutdownIntent_Interrupted);
2457 0 : CONTAINER_ENSURE_SUCCESS(rv);
2458 : }
2459 :
2460 : // If we don't have a decoder, create one
2461 0 : if (!mDecoder) {
2462 0 : NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
2463 0 : rv = InitDecoder(/* aDoSizeDecode = */ false);
2464 0 : CONTAINER_ENSURE_SUCCESS(rv);
2465 : }
2466 :
2467 : // If we've read all the data we have, we're done
2468 0 : if (mBytesDecoded == mSourceData.Length())
2469 0 : return NS_OK;
2470 :
2471 : // If it's a smallish image, it's not worth it to do things async
2472 0 : if (!mDecoded && !mInDecoder && mHasSourceData && (mSourceData.Length() < gMaxBytesForSyncDecode))
2473 0 : return SyncDecode();
2474 :
2475 : // If we get this far, dispatch the worker. We do this instead of starting
2476 : // any immediate decoding to guarantee that all our decode notifications are
2477 : // dispatched asynchronously, and to ensure we stay responsive.
2478 0 : DecodeWorker::Singleton()->RequestDecode(this);
2479 :
2480 0 : return NS_OK;
2481 : }
2482 :
2483 : // Synchronously decodes as much data as possible
2484 : nsresult
2485 21 : RasterImage::SyncDecode()
2486 : {
2487 : nsresult rv;
2488 :
2489 : // If we're decoded already, no worries
2490 21 : if (mDecoded)
2491 19 : return NS_OK;
2492 :
2493 : // If we're not storing source data, there isn't much to do here
2494 2 : if (!StoringSourceData())
2495 0 : return NS_OK;
2496 :
2497 : // We really have no good way of forcing a synchronous decode if we're being
2498 : // called in a re-entrant manner (ie, from an event listener fired by a
2499 : // decoder), because the decoding machinery is already tied up. We thus explicitly
2500 : // disallow this type of call in the API, and check for it in API methods.
2501 2 : NS_ABORT_IF_FALSE(!mInDecoder, "Yikes, forcing sync in reentrant call!");
2502 :
2503 : // If we have a size decoder open, or one with different flags than
2504 : // what we need, shut it down
2505 2 : if (mDecoder &&
2506 2 : (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
2507 : {
2508 0 : rv = ShutdownDecoder(eShutdownIntent_Interrupted);
2509 0 : CONTAINER_ENSURE_SUCCESS(rv);
2510 : }
2511 :
2512 : // If we don't have a decoder, create one
2513 2 : if (!mDecoder) {
2514 2 : NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
2515 2 : rv = InitDecoder(/* aDoSizeDecode = */ false);
2516 2 : CONTAINER_ENSURE_SUCCESS(rv);
2517 : }
2518 :
2519 : // Write everything we have
2520 2 : rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
2521 4 : mSourceData.Length() - mBytesDecoded);
2522 2 : CONTAINER_ENSURE_SUCCESS(rv);
2523 :
2524 : // When we're doing a sync decode, we want to get as much information from the
2525 : // image as possible. We've send the decoder all of our data, so now's a good
2526 : // time to flush any invalidations (in case we don't have all the data and what
2527 : // we got left us mid-frame).
2528 4 : nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
2529 2 : mInDecoder = true;
2530 2 : mDecoder->FlushInvalidations();
2531 2 : mInDecoder = false;
2532 :
2533 : // If we finished the decode, shutdown the decoder
2534 2 : if (mDecoder && IsDecodeFinished()) {
2535 2 : rv = ShutdownDecoder(eShutdownIntent_Done);
2536 2 : CONTAINER_ENSURE_SUCCESS(rv);
2537 : }
2538 :
2539 : // All good if no errors!
2540 2 : return mError ? NS_ERROR_FAILURE : NS_OK;
2541 : }
2542 :
2543 : //******************************************************************************
2544 : /* [noscript] void draw(in gfxContext aContext,
2545 : * in gfxGraphicsFilter aFilter,
2546 : * [const] in gfxMatrix aUserSpaceToImageSpace,
2547 : * [const] in gfxRect aFill,
2548 : * [const] in nsIntRect aSubimage,
2549 : * [const] in nsIntSize aViewportSize,
2550 : * in PRUint32 aFlags); */
2551 : NS_IMETHODIMP
2552 0 : RasterImage::Draw(gfxContext *aContext,
2553 : gfxPattern::GraphicsFilter aFilter,
2554 : const gfxMatrix &aUserSpaceToImageSpace,
2555 : const gfxRect &aFill,
2556 : const nsIntRect &aSubimage,
2557 : const nsIntSize& /*aViewportSize - ignored*/,
2558 : PRUint32 aFlags)
2559 : {
2560 0 : if (mError)
2561 0 : return NS_ERROR_FAILURE;
2562 :
2563 : // Disallowed in the API
2564 0 : if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
2565 0 : return NS_ERROR_FAILURE;
2566 :
2567 : // Illegal -- you can't draw with non-default decode flags.
2568 : // (Disabling colorspace conversion might make sense to allow, but
2569 : // we don't currently.)
2570 0 : if ((aFlags & DECODE_FLAGS_MASK) != DECODE_FLAGS_DEFAULT)
2571 0 : return NS_ERROR_FAILURE;
2572 :
2573 0 : NS_ENSURE_ARG_POINTER(aContext);
2574 :
2575 : // We can only draw with the default decode flags
2576 0 : if (mFrameDecodeFlags != DECODE_FLAGS_DEFAULT) {
2577 0 : if (!CanForciblyDiscard())
2578 0 : return NS_ERROR_NOT_AVAILABLE;
2579 0 : ForceDiscard();
2580 :
2581 0 : mFrameDecodeFlags = DECODE_FLAGS_DEFAULT;
2582 : }
2583 :
2584 : // If this image is a candidate for discarding, reset its position in the
2585 : // discard tracker so we're less likely to discard it right away.
2586 : //
2587 : // (We don't normally draw unlocked images, so this conditition will usually
2588 : // be false. But we will draw unlocked images if image locking is globally
2589 : // disabled via the content.image.allow_locking pref.)
2590 0 : if (DiscardingActive()) {
2591 0 : DiscardTracker::Reset(&mDiscardTrackerNode);
2592 : }
2593 :
2594 : // We use !mDecoded && mHasSourceData to mean discarded.
2595 0 : if (!mDecoded && mHasSourceData) {
2596 0 : mDrawStartTime = TimeStamp::Now();
2597 :
2598 : // We're drawing this image, so indicate that we should decode it as soon
2599 : // as possible.
2600 0 : DecodeWorker::Singleton()->MarkAsASAP(this);
2601 : }
2602 :
2603 : // If a synchronous draw is requested, flush anything that might be sitting around
2604 0 : if (aFlags & FLAG_SYNC_DECODE) {
2605 0 : nsresult rv = SyncDecode();
2606 0 : NS_ENSURE_SUCCESS(rv, rv);
2607 : }
2608 :
2609 0 : imgFrame *frame = GetCurrentDrawableImgFrame();
2610 0 : if (!frame) {
2611 0 : return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
2612 : }
2613 :
2614 0 : nsIntRect framerect = frame->GetRect();
2615 : nsIntMargin padding(framerect.x, framerect.y,
2616 0 : mSize.width - framerect.XMost(),
2617 0 : mSize.height - framerect.YMost());
2618 :
2619 0 : frame->Draw(aContext, aFilter, aUserSpaceToImageSpace, aFill, padding, aSubimage);
2620 :
2621 0 : if (mDecoded && !mDrawStartTime.IsNull()) {
2622 0 : TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
2623 0 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY, PRInt32(drawLatency.ToMicroseconds()));
2624 : // clear the value of mDrawStartTime
2625 0 : mDrawStartTime = TimeStamp();
2626 : }
2627 0 : return NS_OK;
2628 : }
2629 :
2630 : //******************************************************************************
2631 : /* [notxpcom] nsIFrame GetRootLayoutFrame() */
2632 : nsIFrame*
2633 0 : RasterImage::GetRootLayoutFrame()
2634 : {
2635 0 : return nsnull;
2636 : }
2637 :
2638 : //******************************************************************************
2639 : /* void lockImage() */
2640 : NS_IMETHODIMP
2641 4 : RasterImage::LockImage()
2642 : {
2643 4 : if (mError)
2644 0 : return NS_ERROR_FAILURE;
2645 :
2646 : // Cancel the discard timer if it's there
2647 4 : DiscardTracker::Remove(&mDiscardTrackerNode);
2648 :
2649 : // Increment the lock count
2650 4 : mLockCount++;
2651 :
2652 4 : return NS_OK;
2653 : }
2654 :
2655 : //******************************************************************************
2656 : /* void unlockImage() */
2657 : NS_IMETHODIMP
2658 3 : RasterImage::UnlockImage()
2659 : {
2660 3 : if (mError)
2661 1 : return NS_ERROR_FAILURE;
2662 :
2663 : // It's an error to call this function if the lock count is 0
2664 2 : NS_ABORT_IF_FALSE(mLockCount > 0,
2665 : "Calling UnlockImage with mLockCount == 0!");
2666 2 : if (mLockCount == 0)
2667 0 : return NS_ERROR_ABORT;
2668 :
2669 : // We're locked, so discarding should not be active
2670 2 : NS_ABORT_IF_FALSE(!DiscardingActive(), "Locked, but discarding activated");
2671 :
2672 : // Decrement our lock count
2673 2 : mLockCount--;
2674 :
2675 : // If we've decoded this image once before, we're currently decoding again,
2676 : // and our lock count is now zero (so nothing is forcing us to keep the
2677 : // decoded data around), try to cancel the decode and throw away whatever
2678 : // we've decoded.
2679 2 : if (mHasBeenDecoded && mDecoder &&
2680 0 : mLockCount == 0 && CanForciblyDiscard()) {
2681 0 : PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG,
2682 : ("RasterImage[0x%p] canceling decode because image "
2683 : "is now unlocked.", this));
2684 0 : ShutdownDecoder(eShutdownIntent_Interrupted);
2685 0 : ForceDiscard();
2686 0 : return NS_OK;
2687 : }
2688 :
2689 : // Otherwise, we might still be a candidate for discarding in the future. If
2690 : // we are, add ourselves to the discard tracker.
2691 2 : if (CanDiscard()) {
2692 1 : nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
2693 1 : CONTAINER_ENSURE_SUCCESS(rv);
2694 : }
2695 :
2696 2 : return NS_OK;
2697 : }
2698 :
2699 : //******************************************************************************
2700 : /* void requestDiscard() */
2701 : NS_IMETHODIMP
2702 0 : RasterImage::RequestDiscard()
2703 : {
2704 0 : if (CanDiscard()) {
2705 0 : ForceDiscard();
2706 : }
2707 :
2708 0 : return NS_OK;
2709 : }
2710 :
2711 : // Flushes up to aMaxBytes to the decoder.
2712 : nsresult
2713 4 : RasterImage::DecodeSomeData(PRUint32 aMaxBytes)
2714 : {
2715 : // We should have a decoder if we get here
2716 4 : NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
2717 :
2718 : // If we have nothing to decode, return
2719 4 : if (mBytesDecoded == mSourceData.Length())
2720 0 : return NS_OK;
2721 :
2722 :
2723 : // write the proper amount of data
2724 : PRUint32 bytesToDecode = NS_MIN(aMaxBytes,
2725 4 : mSourceData.Length() - mBytesDecoded);
2726 4 : nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
2727 4 : bytesToDecode);
2728 :
2729 4 : return rv;
2730 : }
2731 :
2732 : // There are various indicators that tell us we're finished with the decode
2733 : // task at hand and can shut down the decoder.
2734 : //
2735 : // This method may not be called if there is no decoder.
2736 : bool
2737 6 : RasterImage::IsDecodeFinished()
2738 : {
2739 : // Precondition
2740 6 : NS_ABORT_IF_FALSE(mDecoder, "Can't call IsDecodeFinished() without decoder!");
2741 :
2742 : // Assume it's not finished
2743 6 : bool decodeFinished = false;
2744 :
2745 : // There shouldn't be any reason to call this if we're not storing
2746 : // source data
2747 6 : NS_ABORT_IF_FALSE(StoringSourceData(),
2748 : "just shut down on SourceDataComplete!");
2749 :
2750 : // The decode is complete if we got what we wanted...
2751 6 : if (mDecoder->IsSizeDecode()) {
2752 4 : if (mHasSize)
2753 4 : decodeFinished = true;
2754 : }
2755 : else {
2756 2 : if (mDecoded)
2757 1 : decodeFinished = true;
2758 : }
2759 :
2760 : // ...or if we have all the source data and wrote all the source data.
2761 : //
2762 : // (NB - This can be distinct from the above case even for non-erroneous
2763 : // images because the decoder might not call DecodingComplete() until we
2764 : // call Close() in ShutdownDecoder())
2765 6 : if (mHasSourceData && (mBytesDecoded == mSourceData.Length()))
2766 6 : decodeFinished = true;
2767 :
2768 6 : return decodeFinished;
2769 : }
2770 :
2771 : // Indempotent error flagging routine. If a decoder is open, shuts it down.
2772 : void
2773 10 : RasterImage::DoError()
2774 : {
2775 : // If we've flagged an error before, we have nothing to do
2776 10 : if (mError)
2777 5 : return;
2778 :
2779 : // If we're mid-decode, shut down the decoder.
2780 5 : if (mDecoder)
2781 0 : ShutdownDecoder(eShutdownIntent_Error);
2782 :
2783 : // Put the container in an error state
2784 5 : mError = true;
2785 :
2786 : // Log our error
2787 5 : LOG_CONTAINER_ERROR;
2788 : }
2789 :
2790 : // nsIInputStream callback to copy the incoming image data directly to the
2791 : // RasterImage without processing. The RasterImage is passed as the closure.
2792 : // Always reads everything it gets, even if the data is erroneous.
2793 : NS_METHOD
2794 57 : RasterImage::WriteToRasterImage(nsIInputStream* /* unused */,
2795 : void* aClosure,
2796 : const char* aFromRawSegment,
2797 : PRUint32 /* unused */,
2798 : PRUint32 aCount,
2799 : PRUint32* aWriteCount)
2800 : {
2801 : // Retrieve the RasterImage
2802 57 : RasterImage* image = static_cast<RasterImage*>(aClosure);
2803 :
2804 : // Copy the source data. Unless we hit OOM, we squelch the return value
2805 : // here, because returning an error means that ReadSegments stops
2806 : // reading data, violating our invariant that we read everything we get.
2807 : // If we hit OOM then we fail and the load is aborted.
2808 57 : nsresult rv = image->AddSourceData(aFromRawSegment, aCount);
2809 57 : if (rv == NS_ERROR_OUT_OF_MEMORY) {
2810 0 : image->DoError();
2811 0 : return rv;
2812 : }
2813 :
2814 : // We wrote everything we got
2815 57 : *aWriteCount = aCount;
2816 :
2817 57 : return NS_OK;
2818 : }
2819 :
2820 : bool
2821 2 : RasterImage::ShouldAnimate()
2822 : {
2823 2 : return Image::ShouldAnimate() && mFrames.Length() >= 2 &&
2824 2 : !mAnimationFinished;
2825 : }
2826 :
2827 : /* readonly attribute PRUint32 framesNotified; */
2828 : #ifdef DEBUG
2829 : NS_IMETHODIMP
2830 0 : RasterImage::GetFramesNotified(PRUint32 *aFramesNotified)
2831 : {
2832 0 : NS_ENSURE_ARG_POINTER(aFramesNotified);
2833 :
2834 0 : *aFramesNotified = mFramesNotified;
2835 :
2836 0 : return NS_OK;
2837 : }
2838 : #endif
2839 :
2840 : /* static */ RasterImage::DecodeWorker*
2841 28 : RasterImage::DecodeWorker::Singleton()
2842 : {
2843 28 : if (!sSingleton) {
2844 4 : sSingleton = new DecodeWorker();
2845 4 : ClearOnShutdown(&sSingleton);
2846 : }
2847 :
2848 28 : return sSingleton;
2849 : }
2850 :
2851 : void
2852 0 : RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
2853 : {
2854 0 : DecodeRequest* request = &aImg->mDecodeRequest;
2855 :
2856 : // If we're already an ASAP request, there's nothing to do here.
2857 0 : if (request->mIsASAP) {
2858 0 : return;
2859 : }
2860 :
2861 0 : request->mIsASAP = true;
2862 :
2863 0 : if (request->isInList()) {
2864 : // If the decode request is in a list, it must be in the normal decode
2865 : // requests list -- if it had been in the ASAP list, then mIsASAP would
2866 : // have been true above. Move the request to the ASAP list.
2867 0 : request->remove();
2868 0 : mASAPDecodeRequests.insertBack(request);
2869 :
2870 : // Since request is in a list, one of the decode worker's lists is
2871 : // non-empty, so the worker should be pending in the event loop.
2872 : //
2873 : // (Note that this invariant only holds while we are not in Run(), because
2874 : // DecodeSomeOfImage adds requests to the decode worker using
2875 : // AddDecodeRequest, not RequestDecode, and AddDecodeRequest does not call
2876 : // EnsurePendingInEventLoop. Therefore, it is an error to call MarkAsASAP
2877 : // from within DecodeWorker::Run.)
2878 0 : MOZ_ASSERT(mPendingInEventLoop);
2879 : }
2880 : }
2881 :
2882 : void
2883 4 : RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest)
2884 : {
2885 4 : if (aRequest->isInList()) {
2886 : // The image is already in our list of images to decode, so we don't have
2887 : // to do anything here.
2888 0 : return;
2889 : }
2890 :
2891 4 : if (aRequest->mIsASAP) {
2892 0 : mASAPDecodeRequests.insertBack(aRequest);
2893 : } else {
2894 4 : mNormalDecodeRequests.insertBack(aRequest);
2895 : }
2896 : }
2897 :
2898 : void
2899 4 : RasterImage::DecodeWorker::RequestDecode(RasterImage* aImg)
2900 : {
2901 4 : AddDecodeRequest(&aImg->mDecodeRequest);
2902 4 : EnsurePendingInEventLoop();
2903 4 : }
2904 :
2905 : void
2906 4 : RasterImage::DecodeWorker::EnsurePendingInEventLoop()
2907 : {
2908 4 : if (!mPendingInEventLoop) {
2909 4 : mPendingInEventLoop = true;
2910 4 : NS_DispatchToCurrentThread(this);
2911 : }
2912 4 : }
2913 :
2914 : void
2915 20 : RasterImage::DecodeWorker::StopDecoding(RasterImage* aImg)
2916 : {
2917 20 : DecodeRequest* request = &aImg->mDecodeRequest;
2918 20 : if (request->isInList()) {
2919 4 : request->remove();
2920 : }
2921 20 : request->mDecodeTime = TimeDuration(0);
2922 20 : request->mIsASAP = false;
2923 20 : }
2924 :
2925 : NS_IMETHODIMP
2926 4 : RasterImage::DecodeWorker::Run()
2927 : {
2928 : // We just got called back by the event loop; therefore, we're no longer
2929 : // pending.
2930 4 : mPendingInEventLoop = false;
2931 :
2932 4 : TimeStamp eventStart = TimeStamp::Now();
2933 :
2934 : // Now decode until we either run out of time or run out of images.
2935 0 : do {
2936 : // Try to get an ASAP request to handle. If there isn't one, try to get a
2937 : // normal request. If no normal request is pending either, then we're done
2938 : // here.
2939 4 : DecodeRequest* request = mASAPDecodeRequests.popFirst();
2940 4 : if (!request)
2941 4 : request = mNormalDecodeRequests.popFirst();
2942 4 : if (!request)
2943 4 : break;
2944 :
2945 : // This has to be a strong pointer, because DecodeSomeOfImage may destroy
2946 : // image->mDecoder, which may be holding the only other reference to image.
2947 0 : nsRefPtr<RasterImage> image = request->mImage;
2948 0 : DecodeSomeOfImage(image);
2949 :
2950 : // If we aren't yet finished decoding and we have more data in hand, add
2951 : // this request to the back of the list.
2952 0 : if (image->mDecoder &&
2953 0 : !image->mError &&
2954 0 : !image->IsDecodeFinished() &&
2955 0 : image->mSourceData.Length() > image->mBytesDecoded) {
2956 0 : AddDecodeRequest(request);
2957 : }
2958 :
2959 0 : } while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield);
2960 :
2961 : // If decode requests are pending, re-post ourself to the event loop.
2962 4 : if (!mASAPDecodeRequests.isEmpty() || !mNormalDecodeRequests.isEmpty()) {
2963 0 : EnsurePendingInEventLoop();
2964 : }
2965 :
2966 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_LATENCY,
2967 4 : PRUint32((TimeStamp::Now() - eventStart).ToMilliseconds()));
2968 :
2969 4 : return NS_OK;
2970 : }
2971 :
2972 : nsresult
2973 4 : RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
2974 : {
2975 4 : return DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
2976 : }
2977 :
2978 : nsresult
2979 4 : RasterImage::DecodeWorker::DecodeSomeOfImage(
2980 : RasterImage* aImg,
2981 : DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */)
2982 : {
2983 4 : NS_ABORT_IF_FALSE(aImg->mInitialized,
2984 : "Worker active for uninitialized container!");
2985 :
2986 4 : if (aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize)
2987 0 : return NS_OK;
2988 :
2989 : // If an error is flagged, it probably happened while we were waiting
2990 : // in the event queue.
2991 4 : if (aImg->mError)
2992 0 : return NS_OK;
2993 :
2994 : // If mDecoded or we don't have a decoder, we must have finished already (for
2995 : // example, a synchronous decode request came while the worker was pending).
2996 4 : if (!aImg->mDecoder || aImg->mDecoded)
2997 0 : return NS_OK;
2998 :
2999 8 : nsRefPtr<Decoder> decoderKungFuDeathGrip = aImg->mDecoder;
3000 :
3001 : PRUint32 maxBytes;
3002 4 : if (aImg->mDecoder->IsSizeDecode()) {
3003 : // Decode all available data if we're a size decode; they're cheap, and we
3004 : // want them to be more or less synchronous.
3005 4 : maxBytes = aImg->mSourceData.Length();
3006 : } else {
3007 : // We're only guaranteed to decode this many bytes, so in particular,
3008 : // gDecodeBytesAtATime should be set high enough for us to read the size
3009 : // from most images.
3010 0 : maxBytes = gDecodeBytesAtATime;
3011 : }
3012 :
3013 4 : PRInt32 chunkCount = 0;
3014 4 : TimeStamp start = TimeStamp::Now();
3015 4 : TimeStamp deadline = start + TimeDuration::FromMilliseconds(gMaxMSBeforeYield);
3016 :
3017 : // Decode some chunks of data.
3018 0 : do {
3019 4 : chunkCount++;
3020 4 : nsresult rv = aImg->DecodeSomeData(maxBytes);
3021 4 : if (NS_FAILED(rv)) {
3022 0 : aImg->DoError();
3023 0 : return rv;
3024 : }
3025 :
3026 : // We keep decoding chunks until either:
3027 : // * we're an UNTIL_SIZE decode and we get the size,
3028 : // * we don't have any data left to decode,
3029 : // * the decode completes, or
3030 : // * we run out of time.
3031 :
3032 4 : if (aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize)
3033 4 : break;
3034 :
3035 0 : } while (aImg->mSourceData.Length() > aImg->mBytesDecoded &&
3036 0 : !aImg->IsDecodeFinished() &&
3037 0 : TimeStamp::Now() < deadline);
3038 :
3039 4 : aImg->mDecodeRequest.mDecodeTime += (TimeStamp::Now() - start);
3040 :
3041 4 : if (chunkCount && !aImg->mDecoder->IsSizeDecode()) {
3042 0 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, chunkCount);
3043 : }
3044 :
3045 : // Flush invalidations (and therefore paint) now that we've decoded all the
3046 : // chunks we're going to.
3047 : //
3048 : // However, don't paint if:
3049 : //
3050 : // * This was an until-size decode. Until-size decodes are always followed
3051 : // by normal decodes, so don't bother painting.
3052 : //
3053 : // * The decoder flagged an error. The decoder may have written garbage
3054 : // into the output buffer; don't paint it to the screen.
3055 : //
3056 : // * We have all the source data. This disables progressive display of
3057 : // previously-decoded images, thus letting us finish decoding faster,
3058 : // since we don't waste time painting while we decode.
3059 : // Decoder::PostFrameStop() will flush invalidations once the decode is
3060 : // done.
3061 :
3062 4 : if (aDecodeType != DECODE_TYPE_UNTIL_SIZE &&
3063 0 : !aImg->mDecoder->HasError() &&
3064 0 : !aImg->mHasSourceData) {
3065 0 : aImg->mInDecoder = true;
3066 0 : aImg->mDecoder->FlushInvalidations();
3067 0 : aImg->mInDecoder = false;
3068 : }
3069 :
3070 : // If the decode finished, shut down the decoder.
3071 4 : if (aImg->mDecoder && aImg->IsDecodeFinished()) {
3072 :
3073 : // Do some telemetry if this isn't a size decode.
3074 4 : DecodeRequest* request = &aImg->mDecodeRequest;
3075 4 : if (!aImg->mDecoder->IsSizeDecode()) {
3076 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
3077 0 : PRInt32(request->mDecodeTime.ToMicroseconds()));
3078 :
3079 : // We record the speed for only some decoders. The rest have
3080 : // SpeedHistogram return HistogramCount.
3081 0 : Telemetry::ID id = aImg->mDecoder->SpeedHistogram();
3082 0 : if (id < Telemetry::HistogramCount) {
3083 : PRInt32 KBps = PRInt32(request->mImage->mBytesDecoded /
3084 0 : (1024 * request->mDecodeTime.ToSeconds()));
3085 0 : Telemetry::Accumulate(id, KBps);
3086 : }
3087 : }
3088 :
3089 4 : nsresult rv = aImg->ShutdownDecoder(RasterImage::eShutdownIntent_Done);
3090 4 : if (NS_FAILED(rv)) {
3091 0 : aImg->DoError();
3092 0 : return rv;
3093 : }
3094 : }
3095 :
3096 4 : return NS_OK;
3097 : }
3098 :
3099 : } // namespace image
3100 4188 : } // namespace mozilla
|