image/src/FrameBlender.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "FrameBlender.h"
     8 #include "mozilla/MemoryReporting.h"
     9 #include "MainThreadUtils.h"
    11 #include "pixman.h"
    13 using namespace mozilla;
    14 using namespace mozilla::image;
    16 namespace mozilla {
    17 namespace image {
    19 FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */)
    20  : mFrames(aSequenceToUse)
    21  , mAnim(nullptr)
    22  , mLoopCount(-1)
    23 {
    24   if (!mFrames) {
    25     mFrames = new FrameSequence();
    26   }
    27 }
    29 FrameBlender::~FrameBlender()
    30 {
    31   delete mAnim;
    32 }
    34 already_AddRefed<FrameSequence>
    35 FrameBlender::GetFrameSequence()
    36 {
    37   nsRefPtr<FrameSequence> seq(mFrames);
    38   return seq.forget();
    39 }
    41 imgFrame*
    42 FrameBlender::GetFrame(uint32_t framenum) const
    43 {
    44   if (!mAnim) {
    45     NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
    46     return mFrames->GetFrame(0);
    47   }
    48   if (mAnim->lastCompositedFrameIndex == int32_t(framenum))
    49     return mAnim->compositingFrame;
    50   return mFrames->GetFrame(framenum);
    51 }
    53 imgFrame*
    54 FrameBlender::RawGetFrame(uint32_t framenum) const
    55 {
    56   if (!mAnim) {
    57     NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
    58     return mFrames->GetFrame(0);
    59   }
    61   return mFrames->GetFrame(framenum);
    62 }
    64 uint32_t
    65 FrameBlender::GetNumFrames() const
    66 {
    67   return mFrames->GetNumFrames();
    68 }
    70 int32_t
    71 FrameBlender::GetTimeoutForFrame(uint32_t framenum) const
    72 {
    73   const int32_t timeout = RawGetFrame(framenum)->GetRawTimeout();
    74   // Ensure a minimal time between updates so we don't throttle the UI thread.
    75   // consider 0 == unspecified and make it fast but not too fast.  Unless we have
    76   // a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug 207059.
    77   // The behavior of recent IE and Opera versions seems to be:
    78   // IE 6/Win:
    79   //   10 - 50ms go 100ms
    80   //   >50ms go correct speed
    81   // Opera 7 final/Win:
    82   //   10ms goes 100ms
    83   //   >10ms go correct speed
    84   // It seems that there are broken tools out there that set a 0ms or 10ms
    85   // timeout when they really want a "default" one.  So munge values in that
    86   // range.
    87   if (timeout >= 0 && timeout <= 10 && mLoopCount != 0)
    88     return 100;
    89   return timeout;
    90 }
    92 void
    93 FrameBlender::SetLoopCount(int32_t aLoopCount)
    94 {
    95   mLoopCount = aLoopCount;
    96 }
    98 int32_t
    99 FrameBlender::GetLoopCount() const
   100 {
   101   return mLoopCount;
   102 }
   104 void
   105 FrameBlender::RemoveFrame(uint32_t framenum)
   106 {
   107   NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Deleting invalid frame!");
   109   mFrames->RemoveFrame(framenum);
   110 }
   112 void
   113 FrameBlender::ClearFrames()
   114 {
   115   // Forget our old frame sequence, letting whoever else has it deal with it.
   116   mFrames = new FrameSequence();
   117 }
   119 void
   120 FrameBlender::InsertFrame(uint32_t framenum, imgFrame* aFrame)
   121 {
   122   NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Inserting invalid frame!");
   123   mFrames->InsertFrame(framenum, aFrame);
   124   if (GetNumFrames() > 1) {
   125     EnsureAnimExists();
   126   }
   127 }
   129 imgFrame*
   130 FrameBlender::SwapFrame(uint32_t framenum, imgFrame* aFrame)
   131 {
   132   NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Swapping invalid frame!");
   134   imgFrame* ret;
   136   // Steal the imgFrame from wherever it's currently stored
   137   if (mAnim && mAnim->lastCompositedFrameIndex == int32_t(framenum)) {
   138     ret = mAnim->compositingFrame.Forget();
   139     mAnim->lastCompositedFrameIndex = -1;
   140     nsAutoPtr<imgFrame> toDelete(mFrames->SwapFrame(framenum, aFrame));
   141   } else {
   142     ret = mFrames->SwapFrame(framenum, aFrame);
   143   }
   145   return ret;
   146 }
   148 void
   149 FrameBlender::EnsureAnimExists()
   150 {
   151   if (!mAnim) {
   152     // Create the animation context
   153     mAnim = new Anim();
   155     // We should only get into this code path directly after we've created our
   156     // second frame (hence we know we're animated).
   157     MOZ_ASSERT(GetNumFrames() == 2);
   158   }
   159 }
   161 //******************************************************************************
   162 // DoBlend gets called when the timer for animation get fired and we have to
   163 // update the composited frame of the animation.
   164 bool
   165 FrameBlender::DoBlend(nsIntRect* aDirtyRect,
   166                       uint32_t aPrevFrameIndex,
   167                       uint32_t aNextFrameIndex)
   168 {
   169   if (!aDirtyRect) {
   170     return false;
   171   }
   173   const FrameDataPair& prevFrame = mFrames->GetFrame(aPrevFrameIndex);
   174   const FrameDataPair& nextFrame = mFrames->GetFrame(aNextFrameIndex);
   175   if (!prevFrame.HasFrameData() || !nextFrame.HasFrameData()) {
   176     return false;
   177   }
   179   int32_t prevFrameDisposalMethod = prevFrame->GetFrameDisposalMethod();
   180   if (prevFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious &&
   181       !mAnim->compositingPrevFrame)
   182     prevFrameDisposalMethod = FrameBlender::kDisposeClear;
   184   nsIntRect prevFrameRect = prevFrame->GetRect();
   185   bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
   186                           prevFrameRect.width == mSize.width &&
   187                           prevFrameRect.height == mSize.height);
   189   // Optimization: DisposeClearAll if the previous frame is the same size as
   190   //               container and it's clearing itself
   191   if (isFullPrevFrame &&
   192       (prevFrameDisposalMethod == FrameBlender::kDisposeClear))
   193     prevFrameDisposalMethod = FrameBlender::kDisposeClearAll;
   195   int32_t nextFrameDisposalMethod = nextFrame->GetFrameDisposalMethod();
   196   nsIntRect nextFrameRect = nextFrame->GetRect();
   197   bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
   198                           nextFrameRect.width == mSize.width &&
   199                           nextFrameRect.height == mSize.height);
   201   if (!nextFrame->GetIsPaletted()) {
   202     // Optimization: Skip compositing if the previous frame wants to clear the
   203     //               whole image
   204     if (prevFrameDisposalMethod == FrameBlender::kDisposeClearAll) {
   205       aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
   206       return true;
   207     }
   209     // Optimization: Skip compositing if this frame is the same size as the
   210     //               container and it's fully drawing over prev frame (no alpha)
   211     if (isFullNextFrame &&
   212         (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) &&
   213         !nextFrame->GetHasAlpha()) {
   214       aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
   215       return true;
   216     }
   217   }
   219   // Calculate area that needs updating
   220   switch (prevFrameDisposalMethod) {
   221     default:
   222     case FrameBlender::kDisposeNotSpecified:
   223     case FrameBlender::kDisposeKeep:
   224       *aDirtyRect = nextFrameRect;
   225       break;
   227     case FrameBlender::kDisposeClearAll:
   228       // Whole image container is cleared
   229       aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
   230       break;
   232     case FrameBlender::kDisposeClear:
   233       // Calc area that needs to be redrawn (the combination of previous and
   234       // this frame)
   235       // XXX - This could be done with multiple framechanged calls
   236       //       Having prevFrame way at the top of the image, and nextFrame
   237       //       way at the bottom, and both frames being small, we'd be
   238       //       telling framechanged to refresh the whole image when only two
   239       //       small areas are needed.
   240       aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
   241       break;
   243     case FrameBlender::kDisposeRestorePrevious:
   244       aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
   245       break;
   246   }
   248   // Optimization:
   249   //   Skip compositing if the last composited frame is this frame
   250   //   (Only one composited frame was made for this animation.  Example:
   251   //    Only Frame 3 of a 10 frame image required us to build a composite frame
   252   //    On the second loop, we do not need to rebuild the frame
   253   //    since it's still sitting in compositingFrame)
   254   if (mAnim->lastCompositedFrameIndex == int32_t(aNextFrameIndex)) {
   255     return true;
   256   }
   258   bool needToBlankComposite = false;
   260   // Create the Compositing Frame
   261   if (!mAnim->compositingFrame) {
   262     mAnim->compositingFrame.SetFrame(new imgFrame());
   263     nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
   264                                                 gfxImageFormat::ARGB32);
   265     if (NS_FAILED(rv)) {
   266       mAnim->compositingFrame.SetFrame(nullptr);
   267       return false;
   268     }
   269     mAnim->compositingFrame.LockAndGetData();
   270     needToBlankComposite = true;
   271   } else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) {
   273     // If we are not drawing on top of last composited frame,
   274     // then we are building a new composite frame, so let's clear it first.
   275     needToBlankComposite = true;
   276   }
   278   // More optimizations possible when next frame is not transparent
   279   // But if the next frame has FrameBlender::kDisposeRestorePrevious,
   280   // this "no disposal" optimization is not possible,
   281   // because the frame in "after disposal operation" state
   282   // needs to be stored in compositingFrame, so it can be
   283   // copied into compositingPrevFrame later.
   284   bool doDisposal = true;
   285   if (!nextFrame->GetHasAlpha() &&
   286       nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) {
   287     if (isFullNextFrame) {
   288       // Optimization: No need to dispose prev.frame when
   289       // next frame is full frame and not transparent.
   290       doDisposal = false;
   291       // No need to blank the composite frame
   292       needToBlankComposite = false;
   293     } else {
   294       if ((prevFrameRect.x >= nextFrameRect.x) &&
   295           (prevFrameRect.y >= nextFrameRect.y) &&
   296           (prevFrameRect.x + prevFrameRect.width <= nextFrameRect.x + nextFrameRect.width) &&
   297           (prevFrameRect.y + prevFrameRect.height <= nextFrameRect.y + nextFrameRect.height)) {
   298         // Optimization: No need to dispose prev.frame when
   299         // next frame fully overlaps previous frame.
   300         doDisposal = false;
   301       }
   302     }
   303   }
   305   if (doDisposal) {
   306     // Dispose of previous: clear, restore, or keep (copy)
   307     switch (prevFrameDisposalMethod) {
   308       case FrameBlender::kDisposeClear:
   309         if (needToBlankComposite) {
   310           // If we just created the composite, it could have anything in its
   311           // buffer. Clear whole frame
   312           ClearFrame(mAnim->compositingFrame.GetFrameData(),
   313                      mAnim->compositingFrame.GetFrame()->GetRect());
   314         } else {
   315           // Only blank out previous frame area (both color & Mask/Alpha)
   316           ClearFrame(mAnim->compositingFrame.GetFrameData(),
   317                      mAnim->compositingFrame.GetFrame()->GetRect(),
   318                      prevFrameRect);
   319         }
   320         break;
   322       case FrameBlender::kDisposeClearAll:
   323         ClearFrame(mAnim->compositingFrame.GetFrameData(),
   324                    mAnim->compositingFrame.GetFrame()->GetRect());
   325         break;
   327       case FrameBlender::kDisposeRestorePrevious:
   328         // It would be better to copy only the area changed back to
   329         // compositingFrame.
   330         if (mAnim->compositingPrevFrame) {
   331           CopyFrameImage(mAnim->compositingPrevFrame.GetFrameData(),
   332                          mAnim->compositingPrevFrame.GetFrame()->GetRect(),
   333                          mAnim->compositingFrame.GetFrameData(),
   334                          mAnim->compositingFrame.GetFrame()->GetRect());
   336           // destroy only if we don't need it for this frame's disposal
   337           if (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)
   338             mAnim->compositingPrevFrame.SetFrame(nullptr);
   339         } else {
   340           ClearFrame(mAnim->compositingFrame.GetFrameData(),
   341                      mAnim->compositingFrame.GetFrame()->GetRect());
   342         }
   343         break;
   345       default:
   346         // Copy previous frame into compositingFrame before we put the new frame on top
   347         // Assumes that the previous frame represents a full frame (it could be
   348         // smaller in size than the container, as long as the frame before it erased
   349         // itself)
   350         // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1) will
   351         // always be a valid frame number.
   352         if (mAnim->lastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
   353           if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
   354             // Just copy the bits
   355             CopyFrameImage(prevFrame.GetFrameData(),
   356                            prevFrame.GetFrame()->GetRect(),
   357                            mAnim->compositingFrame.GetFrameData(),
   358                            mAnim->compositingFrame.GetFrame()->GetRect());
   359           } else {
   360             if (needToBlankComposite) {
   361               // Only blank composite when prev is transparent or not full.
   362               if (prevFrame->GetHasAlpha() || !isFullPrevFrame) {
   363                 ClearFrame(mAnim->compositingFrame.GetFrameData(),
   364                            mAnim->compositingFrame.GetFrame()->GetRect());
   365               }
   366             }
   367             DrawFrameTo(prevFrame.GetFrameData(), prevFrameRect,
   368                         prevFrame.GetFrame()->PaletteDataLength(),
   369                         prevFrame.GetFrame()->GetHasAlpha(),
   370                         mAnim->compositingFrame.GetFrameData(),
   371                         mAnim->compositingFrame.GetFrame()->GetRect(),
   372                         FrameBlendMethod(prevFrame.GetFrame()->GetBlendMethod()));
   373           }
   374         }
   375     }
   376   } else if (needToBlankComposite) {
   377     // If we just created the composite, it could have anything in it's
   378     // buffers. Clear them
   379     ClearFrame(mAnim->compositingFrame.GetFrameData(),
   380                mAnim->compositingFrame.GetFrame()->GetRect());
   381   }
   383   // Check if the frame we are composing wants the previous image restored afer
   384   // it is done. Don't store it (again) if last frame wanted its image restored
   385   // too
   386   if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) &&
   387       (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) {
   388     // We are storing the whole image.
   389     // It would be better if we just stored the area that nextFrame is going to
   390     // overwrite.
   391     if (!mAnim->compositingPrevFrame) {
   392       mAnim->compositingPrevFrame.SetFrame(new imgFrame());
   393       nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
   394                                                       gfxImageFormat::ARGB32);
   395       if (NS_FAILED(rv)) {
   396         mAnim->compositingPrevFrame.SetFrame(nullptr);
   397         return false;
   398       }
   400       mAnim->compositingPrevFrame.LockAndGetData();
   401     }
   403     CopyFrameImage(mAnim->compositingFrame.GetFrameData(),
   404                    mAnim->compositingFrame.GetFrame()->GetRect(),
   405                    mAnim->compositingPrevFrame.GetFrameData(),
   406                    mAnim->compositingPrevFrame.GetFrame()->GetRect());
   407   }
   409   // blit next frame into it's correct spot
   410   DrawFrameTo(nextFrame.GetFrameData(), nextFrameRect,
   411               nextFrame.GetFrame()->PaletteDataLength(),
   412               nextFrame.GetFrame()->GetHasAlpha(),
   413               mAnim->compositingFrame.GetFrameData(),
   414               mAnim->compositingFrame.GetFrame()->GetRect(),
   415               FrameBlendMethod(nextFrame.GetFrame()->GetBlendMethod()));
   417   // Set timeout of CompositeFrame to timeout of frame we just composed
   418   // Bug 177948
   419   int32_t timeout = nextFrame->GetRawTimeout();
   420   mAnim->compositingFrame->SetRawTimeout(timeout);
   422   // Tell the image that it is fully 'downloaded'.
   423   nsresult rv = mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect());
   424   if (NS_FAILED(rv)) {
   425     return false;
   426   }
   428   mAnim->lastCompositedFrameIndex = int32_t(aNextFrameIndex);
   430   return true;
   431 }
   433 //******************************************************************************
   434 // Fill aFrame with black. Does also clears the mask.
   435 void
   436 FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect)
   437 {
   438   if (!aFrameData)
   439     return;
   441   memset(aFrameData, 0, aFrameRect.width * aFrameRect.height * 4);
   442 }
   444 //******************************************************************************
   445 void
   446 FrameBlender::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect, const nsIntRect& aRectToClear)
   447 {
   448   if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 ||
   449       aRectToClear.width <= 0 || aRectToClear.height <= 0) {
   450     return;
   451   }
   453   nsIntRect toClear = aFrameRect.Intersect(aRectToClear);
   454   if (toClear.IsEmpty()) {
   455     return;
   456   }
   458   uint32_t bytesPerRow = aFrameRect.width * 4;
   459   for (int row = toClear.y; row < toClear.y + toClear.height; ++row) {
   460     memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0, toClear.width * 4);
   461   }
   462 }
   464 //******************************************************************************
   465 // Whether we succeed or fail will not cause a crash, and there's not much
   466 // we can do about a failure, so there we don't return a nsresult
   467 bool
   468 FrameBlender::CopyFrameImage(const uint8_t *aDataSrc, const nsIntRect& aRectSrc,
   469                              uint8_t *aDataDest, const nsIntRect& aRectDest)
   470 {
   471   uint32_t dataLengthSrc = aRectSrc.width * aRectSrc.height * 4;
   472   uint32_t dataLengthDest = aRectDest.width * aRectDest.height * 4;
   474   if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) {
   475     return false;
   476   }
   478   memcpy(aDataDest, aDataSrc, dataLengthDest);
   480   return true;
   481 }
   483 nsresult
   484 FrameBlender::DrawFrameTo(const uint8_t *aSrcData, const nsIntRect& aSrcRect,
   485                           uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
   486                           uint8_t *aDstPixels, const nsIntRect& aDstRect,
   487                           FrameBlender::FrameBlendMethod aBlendMethod)
   488 {
   489   NS_ENSURE_ARG_POINTER(aSrcData);
   490   NS_ENSURE_ARG_POINTER(aDstPixels);
   492   // According to both AGIF and APNG specs, offsets are unsigned
   493   if (aSrcRect.x < 0 || aSrcRect.y < 0) {
   494     NS_WARNING("FrameBlender::DrawFrameTo: negative offsets not allowed");
   495     return NS_ERROR_FAILURE;
   496   }
   497   // Outside the destination frame, skip it
   498   if ((aSrcRect.x > aDstRect.width) || (aSrcRect.y > aDstRect.height)) {
   499     return NS_OK;
   500   }
   502   if (aSrcPaletteLength) {
   503     // Larger than the destination frame, clip it
   504     int32_t width = std::min(aSrcRect.width, aDstRect.width - aSrcRect.x);
   505     int32_t height = std::min(aSrcRect.height, aDstRect.height - aSrcRect.y);
   507     // The clipped image must now fully fit within destination image frame
   508     NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
   509                  (aSrcRect.x + width <= aDstRect.width) &&
   510                  (aSrcRect.y + height <= aDstRect.height),
   511                 "FrameBlender::DrawFrameTo: Invalid aSrcRect");
   513     // clipped image size may be smaller than source, but not larger
   514     NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
   515                  "FrameBlender::DrawFrameTo: source must be smaller than dest");
   517     // Get pointers to image data
   518     const uint8_t *srcPixels = aSrcData + aSrcPaletteLength;
   519     uint32_t *dstPixels = reinterpret_cast<uint32_t*>(aDstPixels);
   520     const uint32_t *colormap = reinterpret_cast<const uint32_t*>(aSrcData);
   522     // Skip to the right offset
   523     dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.width);
   524     if (!aSrcHasAlpha) {
   525       for (int32_t r = height; r > 0; --r) {
   526         for (int32_t c = 0; c < width; c++) {
   527           dstPixels[c] = colormap[srcPixels[c]];
   528         }
   529         // Go to the next row in the source resp. destination image
   530         srcPixels += aSrcRect.width;
   531         dstPixels += aDstRect.width;
   532       }
   533     } else {
   534       for (int32_t r = height; r > 0; --r) {
   535         for (int32_t c = 0; c < width; c++) {
   536           const uint32_t color = colormap[srcPixels[c]];
   537           if (color)
   538             dstPixels[c] = color;
   539         }
   540         // Go to the next row in the source resp. destination image
   541         srcPixels += aSrcRect.width;
   542         dstPixels += aDstRect.width;
   543       }
   544     }
   545   } else {
   546     pixman_image_t* src = pixman_image_create_bits(aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
   547                                                    aSrcRect.width,
   548                                                    aSrcRect.height,
   549                                                    reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(aSrcData)),
   550                                                    aSrcRect.width * 4);
   551     pixman_image_t* dst = pixman_image_create_bits(PIXMAN_a8r8g8b8,
   552                                                    aDstRect.width,
   553                                                    aDstRect.height,
   554                                                    reinterpret_cast<uint32_t*>(aDstPixels),
   555                                                    aDstRect.width * 4);
   557     pixman_image_composite32(aBlendMethod == FrameBlender::kBlendSource ? PIXMAN_OP_SRC : PIXMAN_OP_OVER,
   558                              src,
   559                              nullptr,
   560                              dst,
   561                              0, 0,
   562                              0, 0,
   563                              aSrcRect.x, aSrcRect.y,
   564                              aSrcRect.width, aSrcRect.height);
   566     pixman_image_unref(src);
   567     pixman_image_unref(dst);
   568   }
   570   return NS_OK;
   571 }
   573 void
   574 FrameBlender::Discard()
   575 {
   576   MOZ_ASSERT(NS_IsMainThread());
   578   // As soon as an image becomes animated, it becomes non-discardable and any
   579   // timers are cancelled.
   580   NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!");
   582   // Delete all the decoded frames, then clear the array.
   583   ClearFrames();
   584 }
   586 size_t
   587 FrameBlender::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
   588                                                       MallocSizeOf aMallocSizeOf) const
   589 {
   590   size_t n = mFrames->SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
   592   if (mAnim) {
   593     if (mAnim->compositingFrame) {
   594       n += mAnim->compositingFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
   595     }
   596     if (mAnim->compositingPrevFrame) {
   597       n += mAnim->compositingPrevFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
   598     }
   599   }
   601   return n;
   602 }
   604 void
   605 FrameBlender::ResetAnimation()
   606 {
   607   if (mAnim) {
   608     mAnim->lastCompositedFrameIndex = -1;
   609   }
   610 }
   612 } // namespace image
   613 } // namespace mozilla

mercurial