PageRenderTime 120ms CodeModel.GetById 22ms app.highlight 83ms RepoModel.GetById 1ms app.codeStats 1ms

/image/src/RasterImage.cpp

http://github.com/zpao/v8monkey
C++ | 3077 lines | 1933 code | 523 blank | 621 comment | 434 complexity | 9c3241205f36981702bbe7c9fb67afd9 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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/StdInt.h"
  72#include "mozilla/Telemetry.h"
  73#include "mozilla/TimeStamp.h"
  74#include "mozilla/ClearOnShutdown.h"
  75
  76using namespace mozilla;
  77using namespace mozilla::image;
  78using 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)
  86static 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.
  94static bool gInitializedPrefCaches = false;
  95static PRUint32 gDecodeBytesAtATime = 0;
  96static PRUint32 gMaxMSBeforeYield = 0;
  97static PRUint32 gMaxBytesForSyncDecode = 0;
  98
  99static void
 100InitPrefCaches()
 101{
 102  Preferences::AddUintVarCache(&gDecodeBytesAtATime,
 103                               "image.mem.decode_bytes_at_a_time", 200000);
 104  Preferences::AddUintVarCache(&gMaxMSBeforeYield,
 105                               "image.mem.max_ms_before_yield", 400);
 106  Preferences::AddUintVarCache(&gMaxBytesForSyncDecode,
 107                               "image.mem.max_bytes_for_sync_decode", 150000);
 108  gInitializedPrefCaches = true;
 109}
 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
 156static int num_containers;
 157static int num_discardable_containers;
 158static PRInt64 total_source_bytes;
 159static PRInt64 discardable_source_bytes;
 160
 161/* Are we globally disabling image discarding? */
 162static bool
 163DiscardingEnabled()
 164{
 165  static bool inited;
 166  static bool enabled;
 167
 168  if (!inited) {
 169    inited = true;
 170
 171    enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nsnull);
 172  }
 173
 174  return enabled;
 175}
 176
 177namespace mozilla {
 178namespace image {
 179
 180/* static */ nsRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
 181
 182#ifndef DEBUG
 183NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
 184                   nsISupportsWeakReference)
 185#else
 186NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsIProperties,
 187                   imgIContainerDebug, nsISupportsWeakReference)
 188#endif
 189
 190//******************************************************************************
 191RasterImage::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  mAnimationFinished(false)
 215{
 216  // Set up the discard tracker node.
 217  mDiscardTrackerNode.curr = this;
 218  mDiscardTrackerNode.prev = mDiscardTrackerNode.next = nsnull;
 219  Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
 220
 221  // Statistics
 222  num_containers++;
 223
 224  // Register our pref observers if we haven't yet.
 225  if (NS_UNLIKELY(!gInitializedPrefCaches)) {
 226    InitPrefCaches();
 227  }
 228}
 229
 230//******************************************************************************
 231RasterImage::~RasterImage()
 232{
 233  delete mAnim;
 234
 235  for (unsigned int i = 0; i < mFrames.Length(); ++i)
 236    delete mFrames[i];
 237
 238  // Discardable statistics
 239  if (mDiscardable) {
 240    num_discardable_containers--;
 241    discardable_source_bytes -= mSourceData.Length();
 242
 243    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  DiscardTracker::Remove(&mDiscardTrackerNode);
 255
 256  // If we have a decoder open, shut it down
 257  if (mDecoder) {
 258    nsresult rv = ShutdownDecoder(eShutdownIntent_Interrupted);
 259    if (NS_FAILED(rv))
 260      NS_WARNING("Failed to shut down decoder in destructor!");
 261  }
 262
 263  // Total statistics
 264  num_containers--;
 265  total_source_bytes -= mSourceData.Length();
 266}
 267
 268nsresult
 269RasterImage::Init(imgIDecoderObserver *aObserver,
 270                  const char* aMimeType,
 271                  const char* aURIString,
 272                  PRUint32 aFlags)
 273{
 274  // We don't support re-initialization
 275  if (mInitialized)
 276    return NS_ERROR_ILLEGAL_VALUE;
 277
 278  // Not sure an error can happen before init, but be safe
 279  if (mError)
 280    return NS_ERROR_FAILURE;
 281
 282  NS_ENSURE_ARG_POINTER(aMimeType);
 283
 284  // We must be non-discardable and non-decode-on-draw for
 285  // multipart channels
 286  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  mObserver = do_GetWeakReference(aObserver);
 293  mSourceDataMimeType.Assign(aMimeType);
 294  mURIString.Assign(aURIString);
 295  mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
 296  mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
 297  mMultipart = !!(aFlags & INIT_FLAG_MULTIPART);
 298
 299  // Statistics
 300  if (mDiscardable) {
 301    num_discardable_containers++;
 302    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  if (mSourceDataMimeType.Length() == 0) {
 309    mInitialized = true;
 310    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  nsresult rv = InitDecoder(/* aDoSizeDecode = */ mDecodeOnDraw);
 319  CONTAINER_ENSURE_SUCCESS(rv);
 320
 321  // Mark us as initialized
 322  mInitialized = true;
 323
 324  return NS_OK;
 325}
 326
 327bool
 328RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
 329{
 330  NS_ASSERTION(aTime <= TimeStamp::Now(),
 331               "Given time appears to be in the future");
 332
 333  imgFrame* nextFrame = nsnull;
 334  PRUint32 currentFrameIndex = mAnim->currentAnimationFrameIndex;
 335  PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
 336  PRUint32 timeout = 0;
 337  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  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  bool haveFullNextFrame = !mDecoder ||
 349                           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  if (haveFullNextFrame) {
 354    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      if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
 359        mAnimationFinished = true;
 360        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      if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1) {
 366        mAnim->compositingFrame = nsnull;
 367      }
 368
 369      nextFrameIndex = 0;
 370
 371      if (mLoopCount > 0) {
 372        mLoopCount--;
 373      }
 374
 375      if (!mAnimating) {
 376        // break out early if we are actually done animating
 377        return false;
 378      }
 379    }
 380
 381    if (!(nextFrame = mFrames[nextFrameIndex])) {
 382      // something wrong with the next frame, skip it
 383      mAnim->currentAnimationFrameIndex = nextFrameIndex;
 384      return false;
 385    }
 386
 387    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    return false;
 393  }
 394
 395  if (!(timeout > 0)) {
 396    mAnimationFinished = true;
 397    EvaluateAnimation();
 398  }
 399
 400  imgFrame *frameToUse = nsnull;
 401
 402  if (nextFrameIndex == 0) {
 403    frameToUse = nextFrame;
 404    *aDirtyRect = mAnim->firstFrameRefreshArea;
 405  } else {
 406    imgFrame *curFrame = mFrames[currentFrameIndex];
 407
 408    if (!curFrame) {
 409      return false;
 410    }
 411
 412    // Change frame
 413    if (NS_FAILED(DoComposite(aDirtyRect, curFrame,
 414                              nextFrame, nextFrameIndex))) {
 415      // something went wrong, move on to next
 416      NS_WARNING("RasterImage::AdvanceFrame(): Compositing of frame failed");
 417      nextFrame->SetCompositingFailed(true);
 418      mAnim->currentAnimationFrameIndex = nextFrameIndex;
 419      mAnim->currentAnimationFrameTime = aTime;
 420      return false;
 421    }
 422
 423    nextFrame->SetCompositingFailed(false);
 424  }
 425
 426  // Set currentAnimationFrameIndex at the last possible moment
 427  mAnim->currentAnimationFrameIndex = nextFrameIndex;
 428  mAnim->currentAnimationFrameTime = aTime;
 429
 430  return true;
 431}
 432
 433//******************************************************************************
 434// [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
 435NS_IMETHODIMP_(void)
 436RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
 437{
 438  if (!mAnimating || !ShouldAnimate()) {
 439    return;
 440  }
 441
 442  EnsureAnimExists();
 443
 444  // only advance the frame if the current time is greater than or
 445  // equal to the current frame's end time.
 446  TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
 447  bool frameAdvanced = false;
 448
 449  // The dirtyRect variable will contain an accumulation of the sub-rectangles
 450  // that are dirty for each frame we advance in AdvanceFrame().
 451  nsIntRect dirtyRect;
 452
 453  while (currentFrameEndTime <= aTime) {
 454    TimeStamp oldFrameEndTime = currentFrameEndTime;
 455    nsIntRect frameDirtyRect;
 456    bool didAdvance = AdvanceFrame(aTime, &frameDirtyRect);
 457    frameAdvanced = frameAdvanced || didAdvance;
 458    currentFrameEndTime = GetCurrentImgFrameEndTime();
 459
 460    // Accumulate the dirty area.
 461    dirtyRect = dirtyRect.Union(frameDirtyRect);
 462
 463    // if we didn't advance a frame, and our frame end time didn't change,
 464    // then we need to break out of this loop & wait for the frame(s)
 465    // to finish downloading
 466    if (!frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
 467      break;
 468    }
 469  }
 470
 471  if (frameAdvanced) {
 472    nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
 473
 474    if (!observer) {
 475      NS_ERROR("Refreshing image after its imgRequest is gone");
 476      StopAnimation();
 477      return;
 478    }
 479
 480    // Notify listeners that our frame has actually changed, but do this only
 481    // once for all frames that we've now passed (if AdvanceFrame() was called
 482    // more than once).
 483    #ifdef DEBUG
 484      mFramesNotified++;
 485    #endif
 486
 487    observer->FrameChanged(nsnull, this, &dirtyRect);
 488  }
 489}
 490
 491//******************************************************************************
 492/* [noscript] imgIContainer extractFrame(PRUint32 aWhichFrame,
 493 *                                       [const] in nsIntRect aRegion,
 494 *                                       in PRUint32 aFlags); */
 495NS_IMETHODIMP
 496RasterImage::ExtractFrame(PRUint32 aWhichFrame,
 497                          const nsIntRect &aRegion,
 498                          PRUint32 aFlags,
 499                          imgIContainer **_retval)
 500{
 501  NS_ENSURE_ARG_POINTER(_retval);
 502
 503  nsresult rv;
 504
 505  if (aWhichFrame > FRAME_MAX_VALUE)
 506    return NS_ERROR_INVALID_ARG;
 507
 508  if (mError)
 509    return NS_ERROR_FAILURE;
 510
 511  // Disallowed in the API
 512  if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
 513    return NS_ERROR_FAILURE;
 514
 515  // Make a new container. This should switch to another class with bug 505959.
 516  nsRefPtr<RasterImage> img(new RasterImage());
 517
 518  // We don't actually have a mimetype in this case. The empty string tells the
 519  // init routine not to try to instantiate a decoder. This should be fixed in
 520  // bug 505959.
 521  img->Init(nsnull, "", "", INIT_FLAG_NONE);
 522  img->SetSize(aRegion.width, aRegion.height);
 523  img->mDecoded = true; // Also, we need to mark the image as decoded
 524  img->mHasBeenDecoded = true;
 525  img->mFrameDecodeFlags = aFlags & DECODE_FLAGS_MASK;
 526
 527  if (img->mFrameDecodeFlags != mFrameDecodeFlags) {
 528    // if we can't discard, then we're screwed; we have no way
 529    // to re-decode.  Similarly if we aren't allowed to do a sync
 530    // decode.
 531    if (!(aFlags & FLAG_SYNC_DECODE))
 532      return NS_ERROR_NOT_AVAILABLE;
 533    if (!CanForciblyDiscard() || mDecoder || mAnim)
 534      return NS_ERROR_NOT_AVAILABLE;
 535    ForceDiscard();
 536
 537    mFrameDecodeFlags = img->mFrameDecodeFlags;
 538  }
 539  
 540  // If a synchronous decode was requested, do it
 541  if (aFlags & FLAG_SYNC_DECODE) {
 542    rv = SyncDecode();
 543    CONTAINER_ENSURE_SUCCESS(rv);
 544  }
 545
 546  // Get the frame. If it's not there, it's probably the caller's fault for
 547  // not waiting for the data to be loaded from the network or not passing
 548  // FLAG_SYNC_DECODE
 549  PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
 550                        0 : GetCurrentImgFrameIndex();
 551  imgFrame *frame = GetDrawableImgFrame(frameIndex);
 552  if (!frame) {
 553    *_retval = nsnull;
 554    return NS_ERROR_FAILURE;
 555  }
 556
 557  // The frame can be smaller than the image. We want to extract only the part
 558  // of the frame that actually exists.
 559  nsIntRect framerect = frame->GetRect();
 560  framerect.IntersectRect(framerect, aRegion);
 561
 562  if (framerect.IsEmpty())
 563    return NS_ERROR_NOT_AVAILABLE;
 564
 565  nsAutoPtr<imgFrame> subframe;
 566  rv = frame->Extract(framerect, getter_Transfers(subframe));
 567  if (NS_FAILED(rv))
 568    return rv;
 569
 570  img->mFrames.AppendElement(subframe.forget());
 571
 572  img->mStatusTracker->RecordLoaded();
 573  img->mStatusTracker->RecordDecoded();
 574
 575  *_retval = img.forget().get();
 576
 577  return NS_OK;
 578}
 579
 580//******************************************************************************
 581/* readonly attribute PRInt32 width; */
 582NS_IMETHODIMP
 583RasterImage::GetWidth(PRInt32 *aWidth)
 584{
 585  NS_ENSURE_ARG_POINTER(aWidth);
 586
 587  if (mError) {
 588    *aWidth = 0;
 589    return NS_ERROR_FAILURE;
 590  }
 591
 592  *aWidth = mSize.width;
 593  return NS_OK;
 594}
 595
 596//******************************************************************************
 597/* readonly attribute PRInt32 height; */
 598NS_IMETHODIMP
 599RasterImage::GetHeight(PRInt32 *aHeight)
 600{
 601  NS_ENSURE_ARG_POINTER(aHeight);
 602
 603  if (mError) {
 604    *aHeight = 0;
 605    return NS_ERROR_FAILURE;
 606  }
 607
 608  *aHeight = mSize.height;
 609  return NS_OK;
 610}
 611
 612//******************************************************************************
 613/* unsigned short GetType(); */
 614NS_IMETHODIMP
 615RasterImage::GetType(PRUint16 *aType)
 616{
 617  NS_ENSURE_ARG_POINTER(aType);
 618
 619  *aType = GetType();
 620  return NS_OK;
 621}
 622
 623//******************************************************************************
 624/* [noscript, notxpcom] PRUint16 GetType(); */
 625NS_IMETHODIMP_(PRUint16)
 626RasterImage::GetType()
 627{
 628  return imgIContainer::TYPE_RASTER;
 629}
 630
 631imgFrame*
 632RasterImage::GetImgFrameNoDecode(PRUint32 framenum)
 633{
 634  if (!mAnim) {
 635    NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
 636    return mFrames.SafeElementAt(0, nsnull);
 637  }
 638  if (mAnim->lastCompositedFrameIndex == PRInt32(framenum))
 639    return mAnim->compositingFrame;
 640  return mFrames.SafeElementAt(framenum, nsnull);
 641}
 642
 643imgFrame*
 644RasterImage::GetImgFrame(PRUint32 framenum)
 645{
 646  nsresult rv = WantDecodedFrames();
 647  CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nsnull);
 648  return GetImgFrameNoDecode(framenum);
 649}
 650
 651imgFrame*
 652RasterImage::GetDrawableImgFrame(PRUint32 framenum)
 653{
 654  imgFrame *frame = GetImgFrame(framenum);
 655
 656  // We will return a paletted frame if it's not marked as compositing failed
 657  // so we can catch crashes for reasons we haven't investigated.
 658  if (frame && frame->GetCompositingFailed())
 659    return nsnull;
 660  return frame;
 661}
 662
 663PRUint32
 664RasterImage::GetCurrentImgFrameIndex() const
 665{
 666  if (mAnim)
 667    return mAnim->currentAnimationFrameIndex;
 668
 669  return 0;
 670}
 671
 672TimeStamp
 673RasterImage::GetCurrentImgFrameEndTime() const
 674{
 675  imgFrame* currentFrame = mFrames[mAnim->currentAnimationFrameIndex];
 676  TimeStamp currentFrameTime = mAnim->currentAnimationFrameTime;
 677  PRInt64 timeout = currentFrame->GetTimeout();
 678
 679  if (timeout < 0) {
 680    // We need to return a sentinel value in this case, because our logic
 681    // doesn't work correctly if we have a negative timeout value. The reason
 682    // this positive infinity was chosen was because it works with the loop in
 683    // RequestRefresh() above.
 684    return TimeStamp() + TimeDuration::FromMilliseconds(UINT64_MAX);
 685  }
 686
 687  TimeDuration durationOfTimeout = TimeDuration::FromMilliseconds(timeout);
 688  TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
 689
 690  return currentFrameEndTime;
 691}
 692
 693imgFrame*
 694RasterImage::GetCurrentImgFrame()
 695{
 696  return GetImgFrame(GetCurrentImgFrameIndex());
 697}
 698
 699imgFrame*
 700RasterImage::GetCurrentDrawableImgFrame()
 701{
 702  return GetDrawableImgFrame(GetCurrentImgFrameIndex());
 703}
 704
 705//******************************************************************************
 706/* readonly attribute boolean currentFrameIsOpaque; */
 707NS_IMETHODIMP
 708RasterImage::GetCurrentFrameIsOpaque(bool *aIsOpaque)
 709{
 710  NS_ENSURE_ARG_POINTER(aIsOpaque);
 711
 712  if (mError)
 713    return NS_ERROR_FAILURE;
 714
 715  // See if we can get an image frame
 716  imgFrame *curframe = GetCurrentImgFrame();
 717
 718  // If we don't get a frame, the safe answer is "not opaque"
 719  if (!curframe)
 720    *aIsOpaque = false;
 721
 722  // Otherwise, we can make a more intelligent decision
 723  else {
 724    *aIsOpaque = !curframe->GetNeedsBackground();
 725
 726    // We are also transparent if the current frame's size doesn't cover our
 727    // entire area.
 728    nsIntRect framerect = curframe->GetRect();
 729    *aIsOpaque = *aIsOpaque && framerect.IsEqualInterior(nsIntRect(0, 0, mSize.width, mSize.height));
 730  }
 731
 732  return NS_OK;
 733}
 734
 735void
 736RasterImage::GetCurrentFrameRect(nsIntRect& aRect)
 737{
 738  // Get the current frame
 739  imgFrame* curframe = GetCurrentImgFrame();
 740
 741  // If we have the frame, use that rectangle
 742  if (curframe) {
 743    aRect = curframe->GetRect();
 744  } else {
 745    // If the frame doesn't exist, we pass the empty rectangle. It's not clear
 746    // whether this is appropriate in general, but at the moment the only
 747    // consumer of this method is imgStatusTracker (when it wants to figure out
 748    // dirty rectangles to send out batched observer updates). This should
 749    // probably be revisited when we fix bug 503973.
 750    aRect.MoveTo(0, 0);
 751    aRect.SizeTo(0, 0);
 752  }
 753}
 754
 755PRUint32
 756RasterImage::GetCurrentFrameIndex()
 757{
 758  return GetCurrentImgFrameIndex();
 759}
 760
 761PRUint32
 762RasterImage::GetNumFrames()
 763{
 764  return mFrames.Length();
 765}
 766
 767//******************************************************************************
 768/* readonly attribute boolean animated; */
 769NS_IMETHODIMP
 770RasterImage::GetAnimated(bool *aAnimated)
 771{
 772  if (mError)
 773    return NS_ERROR_FAILURE;
 774
 775  NS_ENSURE_ARG_POINTER(aAnimated);
 776
 777  // If we have mAnim, we can know for sure
 778  if (mAnim) {
 779    *aAnimated = true;
 780    return NS_OK;
 781  }
 782
 783  // Otherwise, we need to have been decoded to know for sure, since if we were
 784  // decoded at least once mAnim would have been created for animated images
 785  if (!mHasBeenDecoded)
 786    return NS_ERROR_NOT_AVAILABLE;
 787
 788  // We know for sure
 789  *aAnimated = false;
 790
 791  return NS_OK;
 792}
 793
 794
 795//******************************************************************************
 796/* [noscript] gfxImageSurface copyFrame(in PRUint32 aWhichFrame,
 797 *                                      in PRUint32 aFlags); */
 798NS_IMETHODIMP
 799RasterImage::CopyFrame(PRUint32 aWhichFrame,
 800                       PRUint32 aFlags,
 801                       gfxImageSurface **_retval)
 802{
 803  if (aWhichFrame > FRAME_MAX_VALUE)
 804    return NS_ERROR_INVALID_ARG;
 805
 806  if (mError)
 807    return NS_ERROR_FAILURE;
 808
 809  // Disallowed in the API
 810  if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
 811    return NS_ERROR_FAILURE;
 812
 813  nsresult rv;
 814
 815  PRUint32 desiredDecodeFlags = aFlags & DECODE_FLAGS_MASK;
 816  if (desiredDecodeFlags != mFrameDecodeFlags) {
 817    // if we can't discard, then we're screwed; we have no way
 818    // to re-decode.  Similarly if we aren't allowed to do a sync
 819    // decode.
 820    if (!(aFlags & FLAG_SYNC_DECODE))
 821      return NS_ERROR_NOT_AVAILABLE;
 822    if (!CanForciblyDiscard() || mDecoder || mAnim)
 823      return NS_ERROR_NOT_AVAILABLE;
 824    ForceDiscard();
 825
 826    mFrameDecodeFlags = desiredDecodeFlags;
 827  }
 828
 829  // If requested, synchronously flush any data we have lying around to the decoder
 830  if (aFlags & FLAG_SYNC_DECODE) {
 831    rv = SyncDecode();
 832    CONTAINER_ENSURE_SUCCESS(rv);
 833  }
 834
 835  NS_ENSURE_ARG_POINTER(_retval);
 836
 837  // Get the frame. If it's not there, it's probably the caller's fault for
 838  // not waiting for the data to be loaded from the network or not passing
 839  // FLAG_SYNC_DECODE
 840  PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
 841                        0 : GetCurrentImgFrameIndex();
 842  imgFrame *frame = GetDrawableImgFrame(frameIndex);
 843  if (!frame) {
 844    *_retval = nsnull;
 845    return NS_ERROR_FAILURE;
 846  }
 847
 848  nsRefPtr<gfxPattern> pattern;
 849  frame->GetPattern(getter_AddRefs(pattern));
 850  nsIntRect intframerect = frame->GetRect();
 851  gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height);
 852
 853  // Create a 32-bit image surface of our size, but draw using the frame's
 854  // rect, implicitly padding the frame out to the image's size.
 855  nsRefPtr<gfxImageSurface> imgsurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
 856                                                             gfxASurface::ImageFormatARGB32);
 857  gfxContext ctx(imgsurface);
 858  ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
 859  ctx.SetPattern(pattern);
 860  ctx.Rectangle(framerect);
 861  ctx.Fill();
 862
 863  *_retval = imgsurface.forget().get();
 864  return NS_OK;
 865}
 866
 867//******************************************************************************
 868/* [noscript] gfxASurface getFrame(in PRUint32 aWhichFrame,
 869 *                                 in PRUint32 aFlags); */
 870NS_IMETHODIMP
 871RasterImage::GetFrame(PRUint32 aWhichFrame,
 872                      PRUint32 aFlags,
 873                      gfxASurface **_retval)
 874{
 875  if (aWhichFrame > FRAME_MAX_VALUE)
 876    return NS_ERROR_INVALID_ARG;
 877
 878  if (mError)
 879    return NS_ERROR_FAILURE;
 880
 881  // Disallowed in the API
 882  if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
 883    return NS_ERROR_FAILURE;
 884
 885  nsresult rv = NS_OK;
 886
 887  if (mDecoded) {
 888    // If we have decoded data, and it is not a perfect match for what we are
 889    // looking for, we must discard to be able to generate the proper data.
 890    PRUint32 desiredDecodeFlags = aFlags & DECODE_FLAGS_MASK;
 891    if (desiredDecodeFlags != mFrameDecodeFlags) {
 892      // if we can't discard, then we're screwed; we have no way
 893      // to re-decode.  Similarly if we aren't allowed to do a sync
 894      // decode.
 895      if (!(aFlags & FLAG_SYNC_DECODE))
 896        return NS_ERROR_NOT_AVAILABLE;
 897      if (!CanForciblyDiscard() || mDecoder || mAnim)
 898        return NS_ERROR_NOT_AVAILABLE;
 899  
 900      ForceDiscard();
 901  
 902      mFrameDecodeFlags = desiredDecodeFlags;
 903    }
 904  }
 905
 906  // If the caller requested a synchronous decode, do it
 907  if (aFlags & FLAG_SYNC_DECODE) {
 908    rv = SyncDecode();
 909    CONTAINER_ENSURE_SUCCESS(rv);
 910  }
 911
 912  // Get the frame. If it's not there, it's probably the caller's fault for
 913  // not waiting for the data to be loaded from the network or not passing
 914  // FLAG_SYNC_DECODE
 915  PRUint32 frameIndex = (aWhichFrame == FRAME_FIRST) ?
 916                          0 : GetCurrentImgFrameIndex();
 917  imgFrame *frame = GetDrawableImgFrame(frameIndex);
 918  if (!frame) {
 919    *_retval = nsnull;
 920    return NS_ERROR_FAILURE;
 921  }
 922
 923  nsRefPtr<gfxASurface> framesurf;
 924
 925  // If this frame covers the entire image, we can just reuse its existing
 926  // surface.
 927  nsIntRect framerect = frame->GetRect();
 928  if (framerect.x == 0 && framerect.y == 0 &&
 929      framerect.width == mSize.width &&
 930      framerect.height == mSize.height)
 931    rv = frame->GetSurface(getter_AddRefs(framesurf));
 932
 933  // The image doesn't have a surface because it's been optimized away. Create
 934  // one.
 935  if (!framesurf) {
 936    nsRefPtr<gfxImageSurface> imgsurf;
 937    rv = CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
 938    framesurf = imgsurf;
 939  }
 940
 941  *_retval = framesurf.forget().get();
 942
 943  return rv;
 944}
 945
 946
 947NS_IMETHODIMP
 948RasterImage::GetImageContainer(ImageContainer **_retval)
 949{
 950  if (mImageContainer) {
 951    *_retval = mImageContainer;
 952    NS_ADDREF(*_retval);
 953    return NS_OK;
 954  }
 955  
 956  CairoImage::Data cairoData;
 957  nsRefPtr<gfxASurface> imageSurface;
 958  nsresult rv = GetFrame(FRAME_CURRENT, FLAG_SYNC_DECODE, getter_AddRefs(imageSurface));
 959  NS_ENSURE_SUCCESS(rv, rv);
 960
 961  cairoData.mSurface = imageSurface;
 962  GetWidth(&cairoData.mSize.width);
 963  GetHeight(&cairoData.mSize.height);
 964
 965  mImageContainer = LayerManager::CreateImageContainer();
 966  
 967  // Now create a CairoImage to display the surface.
 968  layers::Image::Format cairoFormat = layers::Image::CAIRO_SURFACE;
 969  nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&cairoFormat, 1);
 970  NS_ASSERTION(image, "Failed to create Image");
 971
 972  NS_ASSERTION(image->GetFormat() == cairoFormat, "Wrong format");
 973  static_cast<CairoImage*>(image.get())->SetData(cairoData);
 974  mImageContainer->SetCurrentImage(image);
 975
 976  *_retval = mImageContainer;
 977  NS_ADDREF(*_retval);
 978  return NS_OK;
 979}
 980
 981namespace {
 982
 983PRUint32
 984GetDecodedSize(const nsTArray<imgFrame *> &aFrames,
 985               gfxASurface::MemoryLocation aLocation)
 986{
 987  PRUint32 val = 0;
 988  for (PRUint32 i = 0; i < aFrames.Length(); ++i) {
 989    imgFrame *frame = aFrames.SafeElementAt(i, nsnull);
 990    NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
 991    val += frame->EstimateMemoryUsed(aLocation);
 992  }
 993
 994  return val;
 995}
 996
 997} // anonymous namespace
 998
 999PRUint32
1000RasterImage::GetDecodedHeapSize()
1001{
1002  return GetDecodedSize(mFrames, gfxASurface::MEMORY_IN_PROCESS_HEAP);
1003}
1004
1005PRUint32
1006RasterImage::GetDecodedNonheapSize()
1007{
1008  return GetDecodedSize(mFrames, gfxASurface::MEMORY_IN_PROCESS_NONHEAP);
1009}
1010
1011PRUint32
1012RasterImage::GetDecodedOutOfProcessSize()
1013{
1014  return GetDecodedSize(mFrames, gfxASurface::MEMORY_OUT_OF_PROCESS);
1015}
1016
1017PRUint32
1018RasterImage::GetSourceHeapSize()
1019{
1020  PRUint32 sourceDataSize = mSourceData.Length();
1021  
1022  NS_ABORT_IF_FALSE(StoringSourceData() || (sourceDataSize == 0),
1023                    "Non-zero source data size when we aren't storing it?");
1024  return sourceDataSize;
1025}
1026
1027void
1028RasterImage::DeleteImgFrame(PRUint32 framenum)
1029{
1030  NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Deleting invalid frame!");
1031
1032  delete mFrames[framenum];
1033  mFrames[framenum] = nsnull;
1034}
1035
1036nsresult
1037RasterImage::InternalAddFrameHelper(PRUint32 framenum, imgFrame *aFrame,
1038                                    PRUint8 **imageData, PRUint32 *imageLength,
1039                                    PRUint32 **paletteData, PRUint32 *paletteLength)
1040{
1041  NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
1042  if (framenum > mFrames.Length())
1043    return NS_ERROR_INVALID_ARG;
1044
1045  nsAutoPtr<imgFrame> frame(aFrame);
1046
1047  if (paletteData && paletteLength)
1048    frame->GetPaletteData(paletteData, paletteLength);
1049
1050  frame->GetImageData(imageData, imageLength);
1051
1052  // We are in the middle of decoding. This will be unlocked when we finish the
1053  // decoder->Write() call.
1054  frame->LockImageData();
1055
1056  mFrames.InsertElementAt(framenum, frame.forget());
1057
1058  return NS_OK;
1059}
1060                                  
1061nsresult
1062RasterImage::InternalAddFrame(PRUint32 framenum,
1063                              PRInt32 aX, PRInt32 aY,
1064                              PRInt32 aWidth, PRInt32 aHeight,
1065                              gfxASurface::gfxImageFormat aFormat,
1066                              PRUint8 aPaletteDepth,
1067                              PRUint8 **imageData,
1068                              PRUint32 *imageLength,
1069                              PRUint32 **paletteData,
1070                              PRUint32 *paletteLength)
1071{
1072  // We assume that we're in the middle of decoding because we unlock the
1073  // previous frame when we create a new frame, and only when decoding do we
1074  // lock frames.
1075  NS_ABORT_IF_FALSE(mInDecoder, "Only decoders may add frames!");
1076
1077  NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
1078  if (framenum > mFrames.Length())
1079    return NS_ERROR_INVALID_ARG;
1080
1081  nsAutoPtr<imgFrame> frame(new imgFrame());
1082
1083  nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
1084  NS_ENSURE_SUCCESS(rv, rv);
1085
1086  // We know we are in a decoder. Therefore, we must unlock the previous frame
1087  // when we move on to decoding into the next frame.
1088  if (mFrames.Length() > 0) {
1089    imgFrame *prevframe = mFrames.ElementAt(mFrames.Length() - 1);
1090    prevframe->UnlockImageData();
1091  }
1092
1093  if (mFrames.Length() == 0) {
1094    return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength, 
1095                                  paletteData, paletteLength);
1096  }
1097
1098  if (mFrames.Length() == 1) {
1099    // Since we're about to add our second frame, initialize animation stuff
1100    EnsureAnimExists();
1101    
1102    // If we dispose of the first frame by clearing it, then the
1103    // First Frame's refresh area is all of itself.
1104    // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
1105    PRInt32 frameDisposalMethod = mFrames[0]->GetFrameDisposalMethod();
1106    if (frameDisposalMethod == kDisposeClear ||
1107        frameDisposalMethod == kDisposeRestorePrevious)
1108      mAnim->firstFrameRefreshArea = mFrames[0]->GetRect();
1109  }
1110
1111  // Calculate firstFrameRefreshArea
1112  // Some gifs are huge but only have a small area that they animate
1113  // We only need to refresh that small area when Frame 0 comes around again
1114  nsIntRect frameRect = frame->GetRect();
1115  mAnim->firstFrameRefreshArea.UnionRect(mAnim->firstFrameRefreshArea, 
1116                                         frameRect);
1117  
1118  rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
1119                              paletteData, paletteLength);
1120  
1121  // We may be able to start animating, if we now have enough frames
1122  EvaluateAnimation();
1123  
1124  return rv;
1125}
1126
1127nsresult
1128RasterImage::SetSize(PRInt32 aWidth, PRInt32 aHeight)
1129{
1130  if (mError)
1131    return NS_ERROR_FAILURE;
1132
1133  // Ensure that we have positive values
1134  // XXX - Why isn't the size unsigned? Should this be changed?
1135  if ((aWidth < 0) || (aHeight < 0))
1136    return NS_ERROR_INVALID_ARG;
1137
1138  // if we already have a size, check the new size against the old one
1139  if (mHasSize &&
1140      ((aWidth != mSize.width) || (aHeight != mSize.height))) {
1141
1142    // Alter the warning depending on whether the channel is multipart
1143    if (!mMultipart)
1144      NS_WARNING("Image changed size on redecode! This should not happen!");
1145    else
1146      NS_WARNING("Multipart channel sent an image of a different size");
1147
1148    // Make the decoder aware of the error so that it doesn't try to call
1149    // FinishInternal during ShutdownDecoder.
1150    if (mDecoder)
1151      mDecoder->PostResizeError();
1152
1153    DoError();
1154    return NS_ERROR_UNEXPECTED;
1155  }
1156
1157  // Set the size and flag that we have it
1158  mSize.SizeTo(aWidth, aHeight);
1159  mHasSize = true;
1160
1161  return NS_OK;
1162}
1163
1164nsresult
1165RasterImage::EnsureFrame(PRUint32 aFrameNum, PRInt32 aX, PRInt32 aY,
1166                         PRInt32 aWidth, PRInt32 aHeight,
1167                         gfxASurface::gfxImageFormat aFormat,
1168                         PRUint8 aPaletteDepth,
1169                         PRUint8 **imageData, PRUint32 *imageLength,
1170                         PRUint32 **paletteData, PRUint32 *paletteLength)
1171{
1172  if (mError)
1173    return NS_ERROR_FAILURE;
1174
1175  NS_ENSURE_ARG_POINTER(imageData);
1176  NS_ENSURE_ARG_POINTER(imageLength);
1177  NS_ABORT_IF_FALSE(aFrameNum <= mFrames.Length(), "Invalid frame index!");
1178
1179  if (aPaletteDepth > 0) {
1180    NS_ENSURE_ARG_POINTER(paletteData);
1181    NS_ENSURE_ARG_POINTER(paletteLength);
1182  }
1183
1184  if (aFrameNum > mFrames.Length())
1185    return NS_ERROR_INVALID_ARG;
1186
1187  // Adding a frame that doesn't already exist.
1188  if (aFrameNum == mFrames.Length())
1189    return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat, 
1190                            aPaletteDepth, imageData, imageLength,
1191                            paletteData, paletteLength);
1192
1193  imgFrame *frame = GetImgFrame(aFrameNum);
1194  if (!frame)
1195    return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat, 
1196                            aPaletteDepth, imageData, imageLength,
1197                            paletteData, paletteLength);
1198
1199  // See if we can re-use the frame that already exists.
1200  nsIntRect rect = frame->GetRect();
1201  if (rect.x == aX && rect.y == aY && rect.width == aWidth &&
1202      rect.height == aHeight && frame->GetFormat() == aFormat &&
1203      frame->GetPaletteDepth() == aPaletteDepth) {
1204    frame->GetImageData(imageData, imageLength);
1205    if (paletteData) {
1206      frame->GetPaletteData(paletteData, paletteLength);
1207    }
1208
1209    // We can re-use the frame if it has image data.
1210    if (*imageData && paletteData && *paletteData) {
1211      return NS_OK;
1212    }
1213    if (*imageData && !paletteData) {
1214      return NS_OK;
1215    }
1216  }
1217
1218  DeleteImgFrame(aFrameNum);
1219  return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
1220                          aPaletteDepth, imageData, imageLength,
1221                          paletteData, paletteLength);
1222}
1223
1224nsresult
1225RasterImage::EnsureFrame(PRUint32 aFramenum, PRInt32 aX, PRInt32 aY,
1226                         PRInt32 aWidth, PRInt32 aHeight,
1227                         gfxASurface::gfxImageFormat aFormat,
1228                         PRUint8** imageData, PRUint32* imageLength)
1229{
1230  return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
1231                     /* aPaletteDepth = */ 0, imageData, imageLength,
1232                     /* aPaletteData = */ nsnull,
1233                     /* aPaletteLength = */ nsnull);
1234}
1235
1236void
1237RasterImage::FrameUpdated(PRUint32 aFrameNum, nsIntRect &aUpdatedRect)
1238{
1239  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1240
1241  imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
1242  NS_ABORT_IF_FALSE(frame, "Calling FrameUpdated on frame that doesn't exist!");
1243
1244  frame->ImageUpdated(aUpdatedRect);
1245  // The image has changed, so we need to invalidate our cached ImageContainer.
1246  mImageContainer = NULL;
1247}
1248
1249nsresult
1250RasterImage::SetFrameDisposalMethod(PRUint32 aFrameNum,
1251                                    PRInt32 aDisposalMethod)
1252{
1253  if (mError)
1254    return NS_ERROR_FAILURE;
1255
1256  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1257  if (aFrameNum >= mFrames.Length())
1258    return NS_ERROR_INVALID_ARG;
1259
1260  imgFrame *frame = GetImgFrame(aFrameNum);
1261  NS_ABORT_IF_FALSE(frame,
1262                    "Calling SetFrameDisposalMethod on frame that doesn't exist!");
1263  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1264
1265  frame->SetFrameDisposalMethod(aDisposalMethod);
1266
1267  return NS_OK;
1268}
1269
1270nsresult
1271RasterImage::SetFrameTimeout(PRUint32 aFrameNum, PRInt32 aTimeout)
1272{
1273  if (mError)
1274    return NS_ERROR_FAILURE;
1275
1276  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1277  if (aFrameNum >= mFrames.Length())
1278    return NS_ERROR_INVALID_ARG;
1279
1280  imgFrame *frame = GetImgFrame(aFrameNum);
1281  NS_ABORT_IF_FALSE(frame, "Calling SetFrameTimeout on frame that doesn't exist!");
1282  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1283
1284  frame->SetTimeout(aTimeout);
1285
1286  return NS_OK;
1287}
1288
1289nsresult
1290RasterImage::SetFrameBlendMethod(PRUint32 aFrameNum, PRInt32 aBlendMethod)
1291{
1292  if (mError)
1293    return NS_ERROR_FAILURE;
1294
1295  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1296  if (aFrameNum >= mFrames.Length())
1297    return NS_ERROR_INVALID_ARG;
1298
1299  imgFrame *frame = GetImgFrame(aFrameNum);
1300  NS_ABORT_IF_FALSE(frame, "Calling SetFrameBlendMethod on frame that doesn't exist!");
1301  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1302
1303  frame->SetBlendMethod(aBlendMethod);
1304
1305  return NS_OK;
1306}
1307
1308nsresult
1309RasterImage::SetFrameHasNoAlpha(PRUint32 aFrameNum)
1310{
1311  if (mError)
1312    return NS_ERROR_FAILURE;
1313
1314  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
1315  if (aFrameNum >= mFrames.Length())
1316    return NS_ERROR_INVALID_ARG;
1317
1318  imgFrame *frame = GetImgFrame(aFrameNum);
1319  NS_ABORT_IF_FALSE(frame, "Calling SetFrameHasNoAlpha on frame that doesn't exist!");
1320  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1321
1322  frame->SetHasNoAlpha();
1323
1324  return NS_OK;
1325}
1326
1327nsresult
1328RasterImage::DecodingComplete()
1329{
1330  if (mError)
1331    return NS_ERROR_FAILURE;
1332
1333  // Flag that we're done decoding.
1334  // XXX - these should probably be combined when we fix animated image
1335  // discarding with bug 500402.
1336  mDecoded = true;
1337  mHasBeenDecoded = true;
1338
1339  nsresult rv;
1340
1341  // We now have one of the qualifications for discarding. Re-evaluate.
1342  if (CanDiscard()) {
1343    NS_ABORT_IF_FALSE(!DiscardingActive(),
1344                      "We shouldn't have been discardable before this");
1345    rv = DiscardTracker::Reset(&mDiscardTrackerNode);
1346    CONTAINER_ENSURE_SUCCESS(rv);
1347  }
1348
1349  // If there's only 1 frame, optimize it. Optimizing animated images
1350  // is not supported.
1351  //
1352  // We don't optimize the frame for multipart images because we reuse
1353  // the frame.
1354  if ((mFrames.Length() == 1) && !mMultipart) {
1355    rv = mFrames[0]->Optimize();
1356    NS_ENSURE_SUCCESS(rv, rv);
1357  }
1358
1359  return NS_OK;
1360}
1361
1362//******************************************************************************
1363/* void StartAnimation () */
1364nsresult
1365RasterImage::StartAnimation()
1366{
1367  if (mError)
1368    return NS_ERROR_FAILURE;
1369
1370  NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
1371
1372  EnsureAnimExists();
1373
1374  imgFrame* currentFrame = GetCurrentImgFrame();
1375  if (currentFrame) {
1376    if (currentFrame->GetTimeout() < 0) { // -1 means display this frame forever
1377      mAnimationFinished = true;
1378      return NS_ERROR_ABORT;
1379    }
1380
1381    // We need to set the time that this initial frame was first displayed, as
1382    // this is used in AdvanceFrame().
1383    mAnim->currentAnimationFrameTime = TimeStamp::Now();
1384  }
1385  
1386  return NS_OK;
1387}
1388
1389//******************************************************************************
1390/* void stopAnimation (); */
1391nsresult
1392RasterImage::StopAnimation()
1393{
1394  NS_ABORT_IF_FALSE(mAnimating, "Should be animating!");
1395
1396  if (mError)
1397    return NS_ERROR_FAILURE;
1398
1399  return NS_OK;
1400}
1401
1402//******************************************************************************
1403/* void resetAnimation (); */
1404NS_IMETHODIMP
1405RasterImage::ResetAnimation()
1406{
1407  if (mError)
1408    return NS_ERROR_FAILURE;
1409
1410  if (mAnimationMode == kDontAnimMode || 
1411      !mAnim || mAnim->currentAnimationFrameIndex == 0)
1412    return NS_OK;
1413
1414  mAnimationFinished = false;
1415
1416  if (mAnimating)
1417    StopAnimation();
1418
1419  mAnim->lastCompositedFrameIndex = -1;
1420  mAnim->currentAnimationFrameIndex = 0;
1421  mImageContainer = nsnull;
1422
1423  // Note - We probably want to kick off a redecode somewhere around here when
1424  // we fix bug 500402.
1425
1426  // Update display if we were animating before
1427  nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
1428  if (mAnimating && observer)
1429    observer->FrameChanged(nsnull, this, &(mAnim->firstFrameRefreshArea));
1430
1431  if (ShouldAnimate()) {
1432    StartAnimation();
1433    // The animation may not have been running before, if mAnimationFinished
1434    // was false (before we changed it to true in this function). So, mark the
1435    // animation as running.
1436    mAnimating = true;
1437  }
1438
1439  return NS_OK;
1440}
1441
1442void
1443RasterImage::SetLoopCount(PRInt32 aLoopCount)
1444{
1445  if (mError)
1446    return;
1447
1448  // -1  infinite
1449  //  0  no looping, one iteration
1450  //  1  one loop, two iterations
1451  //  ...
1452  mLoopCount = aLoopCount;
1453}
1454
1455nsresult
1456RasterImage::AddSourceData(const char *aBuffer, PRUint32 aCount)
1457{
1458  if (mError)
1459    return NS_ERROR_FAILURE;
1460
1461  NS_ENSURE_ARG_POINTER(aBuffer);
1462  nsresult rv = NS_OK;
1463
1464  // We should not call this if we're not initialized
1465  NS_ABORT_IF_FALSE(mInitialized, "Calling AddSourceData() on uninitialized "
1466                                  "RasterImage!");
1467
1468  // We should not call this if we're already finished adding source data
1469  NS_ABORT_IF_FALSE(!mHasSourceData, "Calling AddSourceData() after calling "
1470                                     "sourceDataComplete()!");
1471
1472  // This call should come straight from necko - no reentrancy allowed
1473  NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
1474
1475  // If we're not storing source data, write it directly to the decoder
1476  if (!StoringSourceData()) {
1477    rv = WriteToDecoder(aBuffer, aCount);
1478    CONTAINER_ENSURE_SUCCESS(rv);
1479
1480    // We're not storing source data, so this data is probably coming straight
1481    // from the network. In this case, we want to display data as soon as we
1482    // get it, so we want to flush invalidations after every write.
1483    nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
1484    mInDecoder = true;
1485    mDecoder->FlushInvalidations();
1486    mInDecoder = false;
1487  }
1488
1489  // Otherwise, we're storing data in the source buffer
1490  else {
1491
1492    // Store the data
1493    char *newElem = mSourceData.AppendElements(aBuffer, aCount);
1494    if (!newElem)
1495      return NS_ERROR_OUT_OF_MEMORY;
1496
1497    // If there's a decoder open, that means we want to do more decoding.
1498    // Wake up the worker.
1499    if (mDecoder) {
1500      DecodeWorker::Singleton()->RequestDecode(this);
1501    }
1502  }
1503
1504  // Statistics
1505  total_source_bytes += aCount;
1506  if (mDiscardable)
1507    discardable_source_bytes += aCount;
1508  PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
1509          ("CompressedImageAccounting: Added compressed data to RasterImage %p (%s). "
1510           "Total Containers: %d, Discardable containers: %d, "
1511           "Total source bytes: %lld, Source bytes for discardable containers %lld",
1512           this,
1513           mSourceDataMimeType.get(),
1514           num_containers,
1515           num_discardable_containers,
1516           total_source_bytes,
1517           discardable_source_bytes));
1518
1519  return NS_OK;
1520}
1521
1522/* Note!  buf must be declared as char buf[9]; */
1523// just used for logging and hashing the header
1524static void
1525get_header_str (char *buf, char *data, PRSize data_len)
1526{
1527  int i;
1528  int n;
1529  static char hex[] = "0123456789abcdef";
1530
1531  n = data_len < 4 ? data_len : 4;
1532
1533  for (i = 0; i < n; i++) {
1534    buf[i * 2]     = hex[(data[i] >> 4) & 0x0f];
1535    buf[i * 2 + 1] = hex[data[i] & 0x0f];
1536  }
1537
1538  buf[i * 2] = 0;
1539}
1540
1541nsresult
1542RasterImage::SourceDataComplete()
1543{
1544  if (mError)
1545    return NS_ERROR_FAILURE;
1546
1547  // If we've been called before, ignore. Otherwise, flag that we have everything
1548  if (mHasSourceData)
1549    return NS_OK;
1550  mHasSourceData = true;
1551
1552  // This call should come straight from necko - no reentrancy allowed
1553  NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
1554
1555  // If we're not storing any source data, then all the data was written
1556  // directly to the decoder in the AddSourceData() calls. This means we're
1557  // done, so we can shut down the decoder.
1558  if (!StoringSourceData()) {
1559    nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
1560    CONTAINER_ENSURE_SUCCESS(rv);
1561  }
1562
1563  // If there's a decoder open, synchronously decode the beginning of the image
1564  // to check for errors and get the image's size.  (If we already have the
1565  // image's size, this does nothing.)  Then kick off an async decode of the
1566  // rest of the image.
1567  if (mDecoder) {
1568    nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
1569    CONTAINER_ENSURE_SUCCESS(rv);
1570  }
1571
1572  // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
1573  // finish decoding this image.
1574  if (mDecoder) {
1575    DecodeWorker::Singleton()->RequestDecode(this);
1576  }
1577
1578  // Free up any extra space in the backing buffer
1579  mSourceData.Compact();
1580
1581  // Log header information
1582  if (PR_LOG_TEST(gCompressedImageAccountingLog, PR_LOG_DEBUG)) {
1583    char buf[9];
1584    get_header_str(buf, mSourceData.Elements(), mSourceData.Length());
1585    PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
1586            ("CompressedImageAccounting: RasterImage::SourceDataComplete() - data "
1587             "is done for container %p (%s) - header %p is 0x%s (length %d)",
1588             this,
1589             mSourceDataMimeType.get(),
1590             mSourceData.Elements(),
1591             buf,
1592             mSourceData.Length()));
1593  }
1594
1595  // We now have one of the qualifications for discarding. Re-evaluate.
1596  if (CanDiscard()) {
1597    nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
1598    CONTAINER_ENSURE_SUCCESS(rv);
1599  }
1600  return NS_OK;
1601}
1602
1603nsresult
1604RasterImage::NewSourceData()
1605{
1606  nsresult rv;
1607
1608  if (mError)
1609    return NS_ERROR_FAILURE;
1610
1611  // The source data should be complete before calling this
1612  NS_ABORT_IF_FALSE(mHasSourceData,
1613                    "Calling NewSourceData before SourceDataComplete!");
1614  if (!mHasSourceData)
1615    return NS_ERROR_ILLEGAL_VALUE;
1616
1617  // Only supported for multipart channels. It wouldn't be too hard to change this,
1618  // but it would involve making sure that things worked for decode-on-draw and
1619  // discarding. Presently there's no need for this, so we don't.
1620  NS_ABORT_IF_FALSE(mMultipart, "NewSourceData not supported for multipart");
1621  if (!mMultipart)
1622    return NS_ERROR_ILLEGAL_VALUE;
1623
1624  // We're multipart, so we shouldn't be storing source data
1625  NS_ABORT_IF_FALSE(!Storin

Large files files are truncated, but you can click here to view the full file