コード例 #1
0
//******************************************************************************
// DoBlend gets called when the timer for animation get fired and we have to
// update the composited frame of the animation.
bool
FrameBlender::DoBlend(nsIntRect* aDirtyRect,
                      uint32_t aPrevFrameIndex,
                      uint32_t aNextFrameIndex)
{
    nsRefPtr<imgFrame> prevFrame = RawGetFrame(aPrevFrameIndex);
    nsRefPtr<imgFrame> nextFrame = RawGetFrame(aNextFrameIndex);

    MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");

    int32_t prevFrameDisposalMethod = prevFrame->GetFrameDisposalMethod();
    if (prevFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious &&
            !mAnim->compositingPrevFrame) {
        prevFrameDisposalMethod = FrameBlender::kDisposeClear;
    }

    nsIntRect prevFrameRect = prevFrame->GetRect();
    bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
                            prevFrameRect.width == mSize.width &&
                            prevFrameRect.height == mSize.height);

    // Optimization: DisposeClearAll if the previous frame is the same size as
    //               container and it's clearing itself
    if (isFullPrevFrame &&
            (prevFrameDisposalMethod == FrameBlender::kDisposeClear)) {
        prevFrameDisposalMethod = FrameBlender::kDisposeClearAll;
    }

    int32_t nextFrameDisposalMethod = nextFrame->GetFrameDisposalMethod();
    nsIntRect nextFrameRect = nextFrame->GetRect();
    bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
                            nextFrameRect.width == mSize.width &&
                            nextFrameRect.height == mSize.height);

    if (!nextFrame->GetIsPaletted()) {
        // Optimization: Skip compositing if the previous frame wants to clear the
        //               whole image
        if (prevFrameDisposalMethod == FrameBlender::kDisposeClearAll) {
            aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
            return true;
        }

        // Optimization: Skip compositing if this frame is the same size as the
        //               container and it's fully drawing over prev frame (no alpha)
        if (isFullNextFrame &&
                (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) &&
                !nextFrame->GetHasAlpha()) {
            aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
            return true;
        }
    }

    // Calculate area that needs updating
    switch (prevFrameDisposalMethod) {
    default:
    case FrameBlender::kDisposeNotSpecified:
    case FrameBlender::kDisposeKeep:
        *aDirtyRect = nextFrameRect;
        break;

    case FrameBlender::kDisposeClearAll:
        // Whole image container is cleared
        aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
        break;

    case FrameBlender::kDisposeClear:
        // Calc area that needs to be redrawn (the combination of previous and
        // this frame)
        // XXX - This could be done with multiple framechanged calls
        //       Having prevFrame way at the top of the image, and nextFrame
        //       way at the bottom, and both frames being small, we'd be
        //       telling framechanged to refresh the whole image when only two
        //       small areas are needed.
        aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
        break;

    case FrameBlender::kDisposeRestorePrevious:
        aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
        break;
    }

    // Optimization:
    //   Skip compositing if the last composited frame is this frame
    //   (Only one composited frame was made for this animation.  Example:
    //    Only Frame 3 of a 10 frame image required us to build a composite frame
    //    On the second loop, we do not need to rebuild the frame
    //    since it's still sitting in compositingFrame)
    if (mAnim->lastCompositedFrameIndex == int32_t(aNextFrameIndex)) {
        return true;
    }

    bool needToBlankComposite = false;

    // Create the Compositing Frame
    if (!mAnim->compositingFrame) {
        nsRefPtr<imgFrame> newFrame = new imgFrame;
        nsresult rv = newFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
        if (NS_FAILED(rv)) {
            mAnim->compositingFrame.reset();
            return false;
        }
        mAnim->compositingFrame = newFrame->RawAccessRef();
        needToBlankComposite = true;
    } else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) {

        // If we are not drawing on top of last composited frame,
        // then we are building a new composite frame, so let's clear it first.
        needToBlankComposite = true;
    }

    // More optimizations possible when next frame is not transparent
    // But if the next frame has FrameBlender::kDisposeRestorePrevious,
    // this "no disposal" optimization is not possible,
    // because the frame in "after disposal operation" state
    // needs to be stored in compositingFrame, so it can be
    // copied into compositingPrevFrame later.
    bool doDisposal = true;
    if (!nextFrame->GetHasAlpha() &&
            nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious) {
        if (isFullNextFrame) {
            // Optimization: No need to dispose prev.frame when
            // next frame is full frame and not transparent.
            doDisposal = false;
            // No need to blank the composite frame
            needToBlankComposite = false;
        } else {
            if ((prevFrameRect.x >= nextFrameRect.x) &&
                    (prevFrameRect.y >= nextFrameRect.y) &&
                    (prevFrameRect.x + prevFrameRect.width <=
                     nextFrameRect.x + nextFrameRect.width) &&
                    (prevFrameRect.y + prevFrameRect.height <=
                     nextFrameRect.y + nextFrameRect.height)) {
                // Optimization: No need to dispose prev.frame when
                // next frame fully overlaps previous frame.
                doDisposal = false;
            }
        }
    }

    if (doDisposal) {
        // Dispose of previous: clear, restore, or keep (copy)
        switch (prevFrameDisposalMethod) {
        case FrameBlender::kDisposeClear:
            if (needToBlankComposite) {
                // If we just created the composite, it could have anything in its
                // buffer. Clear whole frame
                ClearFrame(mAnim->compositingFrame->GetRawData(),
                           mAnim->compositingFrame->GetRect());
            } else {
                // Only blank out previous frame area (both color & Mask/Alpha)
                ClearFrame(mAnim->compositingFrame->GetRawData(),
                           mAnim->compositingFrame->GetRect(),
                           prevFrameRect);
            }
            break;

        case FrameBlender::kDisposeClearAll:
            ClearFrame(mAnim->compositingFrame->GetRawData(),
                       mAnim->compositingFrame->GetRect());
            break;

        case FrameBlender::kDisposeRestorePrevious:
            // It would be better to copy only the area changed back to
            // compositingFrame.
            if (mAnim->compositingPrevFrame) {
                CopyFrameImage(mAnim->compositingPrevFrame->GetRawData(),
                               mAnim->compositingPrevFrame->GetRect(),
                               mAnim->compositingFrame->GetRawData(),
                               mAnim->compositingFrame->GetRect());

                // destroy only if we don't need it for this frame's disposal
                if (nextFrameDisposalMethod !=
                        FrameBlender::kDisposeRestorePrevious) {
                    mAnim->compositingPrevFrame.reset();
                }
            } else {
                ClearFrame(mAnim->compositingFrame->GetRawData(),
                           mAnim->compositingFrame->GetRect());
            }
            break;

        default:
            // Copy previous frame into compositingFrame before we put the new
            // frame on top
            // Assumes that the previous frame represents a full frame (it could be
            // smaller in size than the container, as long as the frame before it
            // erased itself)
            // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1)
            // will always be a valid frame number.
            if (mAnim->lastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
                if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
                    // Just copy the bits
                    CopyFrameImage(prevFrame->GetRawData(),
                                   prevFrame->GetRect(),
                                   mAnim->compositingFrame->GetRawData(),
                                   mAnim->compositingFrame->GetRect());
                } else {
                    if (needToBlankComposite) {
                        // Only blank composite when prev is transparent or not full.
                        if (prevFrame->GetHasAlpha() || !isFullPrevFrame) {
                            ClearFrame(mAnim->compositingFrame->GetRawData(),
                                       mAnim->compositingFrame->GetRect());
                        }
                    }
                    DrawFrameTo(prevFrame->GetRawData(), prevFrameRect,
                                prevFrame->PaletteDataLength(),
                                prevFrame->GetHasAlpha(),
                                mAnim->compositingFrame->GetRawData(),
                                mAnim->compositingFrame->GetRect(),
                                FrameBlendMethod(prevFrame->GetBlendMethod()));
                }
            }
        }
    } else if (needToBlankComposite) {
        // If we just created the composite, it could have anything in its
        // buffers. Clear them
        ClearFrame(mAnim->compositingFrame->GetRawData(),
                   mAnim->compositingFrame->GetRect());
    }

    // Check if the frame we are composing wants the previous image restored after
    // it is done. Don't store it (again) if last frame wanted its image restored
    // too
    if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) &&
            (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) {
        // We are storing the whole image.
        // It would be better if we just stored the area that nextFrame is going to
        // overwrite.
        if (!mAnim->compositingPrevFrame) {
            nsRefPtr<imgFrame> newFrame = new imgFrame;
            nsresult rv = newFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
            if (NS_FAILED(rv)) {
                mAnim->compositingPrevFrame.reset();
                return false;
            }

            mAnim->compositingPrevFrame = newFrame->RawAccessRef();
        }

        CopyFrameImage(mAnim->compositingFrame->GetRawData(),
                       mAnim->compositingFrame->GetRect(),
                       mAnim->compositingPrevFrame->GetRawData(),
                       mAnim->compositingPrevFrame->GetRect());
    }

    // blit next frame into it's correct spot
    DrawFrameTo(nextFrame->GetRawData(), nextFrameRect,
                nextFrame->PaletteDataLength(),
                nextFrame->GetHasAlpha(),
                mAnim->compositingFrame->GetRawData(),
                mAnim->compositingFrame->GetRect(),
                FrameBlendMethod(nextFrame->GetBlendMethod()));

    // Set timeout of CompositeFrame to timeout of frame we just composed
    // Bug 177948
    int32_t timeout = nextFrame->GetRawTimeout();
    mAnim->compositingFrame->SetRawTimeout(timeout);

    // Tell the image that it is fully 'downloaded'.
    nsresult rv =
        mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect());
    if (NS_FAILED(rv)) {
        return false;
    }

    mAnim->lastCompositedFrameIndex = int32_t(aNextFrameIndex);

    return true;
}
コード例 #2
0
ファイル: FrameAnimator.cpp プロジェクト: alphan102/gecko-dev
//******************************************************************************
// DoBlend gets called when the timer for animation get fired and we have to
// update the composited frame of the animation.
bool
FrameAnimator::DoBlend(IntRect* aDirtyRect,
                       uint32_t aPrevFrameIndex,
                       uint32_t aNextFrameIndex)
{
  RawAccessFrameRef prevFrame = GetRawFrame(aPrevFrameIndex);
  RawAccessFrameRef nextFrame = GetRawFrame(aNextFrameIndex);

  MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");

  AnimationData prevFrameData = prevFrame->GetAnimationData();
  if (prevFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS &&
      !mCompositingPrevFrame) {
    prevFrameData.mDisposalMethod = DisposalMethod::CLEAR;
  }

  IntRect prevRect = prevFrameData.mBlendRect
                   ? prevFrameData.mRect.Intersect(*prevFrameData.mBlendRect)
                   : prevFrameData.mRect;

  bool isFullPrevFrame = prevRect.x == 0 && prevRect.y == 0 &&
                         prevRect.width == mSize.width &&
                         prevRect.height == mSize.height;

  // Optimization: DisposeClearAll if the previous frame is the same size as
  //               container and it's clearing itself
  if (isFullPrevFrame &&
      (prevFrameData.mDisposalMethod == DisposalMethod::CLEAR)) {
    prevFrameData.mDisposalMethod = DisposalMethod::CLEAR_ALL;
  }

  AnimationData nextFrameData = nextFrame->GetAnimationData();

  IntRect nextRect = nextFrameData.mBlendRect
                   ? nextFrameData.mRect.Intersect(*nextFrameData.mBlendRect)
                   : nextFrameData.mRect;

  bool isFullNextFrame = nextRect.x == 0 && nextRect.y == 0 &&
                         nextRect.width == mSize.width &&
                         nextRect.height == mSize.height;

  if (!nextFrame->GetIsPaletted()) {
    // Optimization: Skip compositing if the previous frame wants to clear the
    //               whole image
    if (prevFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL) {
      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
      return true;
    }

    // Optimization: Skip compositing if this frame is the same size as the
    //               container and it's fully drawing over prev frame (no alpha)
    if (isFullNextFrame &&
        (nextFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) &&
        !nextFrameData.mHasAlpha) {
      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
      return true;
    }
  }

  // Calculate area that needs updating
  switch (prevFrameData.mDisposalMethod) {
    default:
      MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
    case DisposalMethod::NOT_SPECIFIED:
    case DisposalMethod::KEEP:
      *aDirtyRect = nextRect;
      break;

    case DisposalMethod::CLEAR_ALL:
      // Whole image container is cleared
      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
      break;

    case DisposalMethod::CLEAR:
      // Calc area that needs to be redrawn (the combination of previous and
      // this frame)
      // XXX - This could be done with multiple framechanged calls
      //       Having prevFrame way at the top of the image, and nextFrame
      //       way at the bottom, and both frames being small, we'd be
      //       telling framechanged to refresh the whole image when only two
      //       small areas are needed.
      aDirtyRect->UnionRect(nextRect, prevRect);
      break;

    case DisposalMethod::RESTORE_PREVIOUS:
      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
      break;
  }

  // Optimization:
  //   Skip compositing if the last composited frame is this frame
  //   (Only one composited frame was made for this animation.  Example:
  //    Only Frame 3 of a 10 frame image required us to build a composite frame
  //    On the second loop, we do not need to rebuild the frame
  //    since it's still sitting in compositingFrame)
  if (mLastCompositedFrameIndex == int32_t(aNextFrameIndex)) {
    return true;
  }

  bool needToBlankComposite = false;

  // Create the Compositing Frame
  if (!mCompositingFrame) {
    RefPtr<imgFrame> newFrame = new imgFrame;
    nsresult rv = newFrame->InitForDecoder(mSize,
                                           SurfaceFormat::B8G8R8A8);
    if (NS_FAILED(rv)) {
      mCompositingFrame.reset();
      return false;
    }
    mCompositingFrame = newFrame->RawAccessRef();
    needToBlankComposite = true;
  } else if (int32_t(aNextFrameIndex) != mLastCompositedFrameIndex+1) {

    // If we are not drawing on top of last composited frame,
    // then we are building a new composite frame, so let's clear it first.
    needToBlankComposite = true;
  }

  AnimationData compositingFrameData = mCompositingFrame->GetAnimationData();

  // More optimizations possible when next frame is not transparent
  // But if the next frame has DisposalMethod::RESTORE_PREVIOUS,
  // this "no disposal" optimization is not possible,
  // because the frame in "after disposal operation" state
  // needs to be stored in compositingFrame, so it can be
  // copied into compositingPrevFrame later.
  bool doDisposal = true;
  if (!nextFrameData.mHasAlpha &&
      nextFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) {
    if (isFullNextFrame) {
      // Optimization: No need to dispose prev.frame when
      // next frame is full frame and not transparent.
      doDisposal = false;
      // No need to blank the composite frame
      needToBlankComposite = false;
    } else {
      if ((prevRect.x >= nextRect.x) && (prevRect.y >= nextRect.y) &&
          (prevRect.x + prevRect.width <= nextRect.x + nextRect.width) &&
          (prevRect.y + prevRect.height <= nextRect.y + nextRect.height)) {
        // Optimization: No need to dispose prev.frame when
        // next frame fully overlaps previous frame.
        doDisposal = false;
      }
    }
  }

  if (doDisposal) {
    // Dispose of previous: clear, restore, or keep (copy)
    switch (prevFrameData.mDisposalMethod) {
      case DisposalMethod::CLEAR:
        if (needToBlankComposite) {
          // If we just created the composite, it could have anything in its
          // buffer. Clear whole frame
          ClearFrame(compositingFrameData.mRawData,
                     compositingFrameData.mRect);
        } else {
          // Only blank out previous frame area (both color & Mask/Alpha)
          ClearFrame(compositingFrameData.mRawData,
                     compositingFrameData.mRect,
                     prevRect);
        }
        break;

      case DisposalMethod::CLEAR_ALL:
        ClearFrame(compositingFrameData.mRawData,
                   compositingFrameData.mRect);
        break;

      case DisposalMethod::RESTORE_PREVIOUS:
        // It would be better to copy only the area changed back to
        // compositingFrame.
        if (mCompositingPrevFrame) {
          AnimationData compositingPrevFrameData =
            mCompositingPrevFrame->GetAnimationData();

          CopyFrameImage(compositingPrevFrameData.mRawData,
                         compositingPrevFrameData.mRect,
                         compositingFrameData.mRawData,
                         compositingFrameData.mRect);

          // destroy only if we don't need it for this frame's disposal
          if (nextFrameData.mDisposalMethod !=
              DisposalMethod::RESTORE_PREVIOUS) {
            mCompositingPrevFrame.reset();
          }
        } else {
          ClearFrame(compositingFrameData.mRawData,
                     compositingFrameData.mRect);
        }
        break;

      default:
        MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
      case DisposalMethod::NOT_SPECIFIED:
      case DisposalMethod::KEEP:
        // Copy previous frame into compositingFrame before we put the new
        // frame on top
        // Assumes that the previous frame represents a full frame (it could be
        // smaller in size than the container, as long as the frame before it
        // erased itself)
        // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1)
        // will always be a valid frame number.
        if (mLastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
          if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
            // Just copy the bits
            CopyFrameImage(prevFrameData.mRawData,
                           prevRect,
                           compositingFrameData.mRawData,
                           compositingFrameData.mRect);
          } else {
            if (needToBlankComposite) {
              // Only blank composite when prev is transparent or not full.
              if (prevFrameData.mHasAlpha || !isFullPrevFrame) {
                ClearFrame(compositingFrameData.mRawData,
                           compositingFrameData.mRect);
              }
            }
            DrawFrameTo(prevFrameData.mRawData, prevFrameData.mRect,
                        prevFrameData.mPaletteDataLength,
                        prevFrameData.mHasAlpha,
                        compositingFrameData.mRawData,
                        compositingFrameData.mRect,
                        prevFrameData.mBlendMethod,
                        prevFrameData.mBlendRect);
          }
        }
    }
  } else if (needToBlankComposite) {
    // If we just created the composite, it could have anything in its
    // buffers. Clear them
    ClearFrame(compositingFrameData.mRawData,
               compositingFrameData.mRect);
  }

  // Check if the frame we are composing wants the previous image restored after
  // it is done. Don't store it (again) if last frame wanted its image restored
  // too
  if ((nextFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) &&
      (prevFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS)) {
    // We are storing the whole image.
    // It would be better if we just stored the area that nextFrame is going to
    // overwrite.
    if (!mCompositingPrevFrame) {
      RefPtr<imgFrame> newFrame = new imgFrame;
      nsresult rv = newFrame->InitForDecoder(mSize,
                                             SurfaceFormat::B8G8R8A8);
      if (NS_FAILED(rv)) {
        mCompositingPrevFrame.reset();
        return false;
      }

      mCompositingPrevFrame = newFrame->RawAccessRef();
    }

    AnimationData compositingPrevFrameData =
      mCompositingPrevFrame->GetAnimationData();

    CopyFrameImage(compositingFrameData.mRawData,
                   compositingFrameData.mRect,
                   compositingPrevFrameData.mRawData,
                   compositingPrevFrameData.mRect);

    mCompositingPrevFrame->Finish();
  }

  // blit next frame into it's correct spot
  DrawFrameTo(nextFrameData.mRawData, nextFrameData.mRect,
              nextFrameData.mPaletteDataLength,
              nextFrameData.mHasAlpha,
              compositingFrameData.mRawData,
              compositingFrameData.mRect,
              nextFrameData.mBlendMethod,
              nextFrameData.mBlendRect);

  // Tell the image that it is fully 'downloaded'.
  mCompositingFrame->Finish();

  mLastCompositedFrameIndex = int32_t(aNextFrameIndex);

  return true;
}
コード例 #3
0
ファイル: imgContainer.cpp プロジェクト: ahadzi/celtx
//******************************************************************************
// DoComposite gets called when the timer for animation get fired and we have to
// update the composited frame of the animation.
nsresult imgContainer::DoComposite(gfxIImageFrame** aFrameToUse,
                                   nsIntRect* aDirtyRect,
                                   gfxIImageFrame* aPrevFrame,
                                   gfxIImageFrame* aNextFrame,
                                   PRInt32 aNextFrameIndex)
{
  NS_ENSURE_ARG_POINTER(aDirtyRect);
  NS_ENSURE_ARG_POINTER(aPrevFrame);
  NS_ENSURE_ARG_POINTER(aNextFrame);
  NS_ENSURE_ARG_POINTER(aFrameToUse);

  PRInt32 prevFrameDisposalMethod;
  aPrevFrame->GetFrameDisposalMethod(&prevFrameDisposalMethod);

  if (prevFrameDisposalMethod == imgIContainer::kDisposeRestorePrevious &&
      !mAnim->compositingPrevFrame)
    prevFrameDisposalMethod = imgIContainer::kDisposeClear;
  nsIntRect prevFrameRect;
  aPrevFrame->GetRect(prevFrameRect);
  PRBool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
                            prevFrameRect.width == mSize.width &&
                            prevFrameRect.height == mSize.height);

  // Optimization: DisposeClearAll if the previous frame is the same size as
  //               container and it's clearing itself
  if (isFullPrevFrame && 
      (prevFrameDisposalMethod == imgIContainer::kDisposeClear))
    prevFrameDisposalMethod = imgIContainer::kDisposeClearAll;

  PRInt32 nextFrameDisposalMethod;
  nsIntRect nextFrameRect;
  aNextFrame->GetFrameDisposalMethod(&nextFrameDisposalMethod);
  aNextFrame->GetRect(nextFrameRect);
  PRBool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
                            nextFrameRect.width == mSize.width &&
                            nextFrameRect.height == mSize.height);

  gfx_format nextFormat;
  aNextFrame->GetFormat(&nextFormat);
  if (nextFormat != gfxIFormats::PAL && nextFormat != gfxIFormats::PAL_A1) {
    // Optimization: Skip compositing if the previous frame wants to clear the
    //               whole image
    if (prevFrameDisposalMethod == imgIContainer::kDisposeClearAll) {
      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
      *aFrameToUse = aNextFrame;
      return NS_OK;
    }
  
    // Optimization: Skip compositing if this frame is the same size as the
    //               container and it's fully drawing over prev frame (no alpha)
    if (isFullNextFrame &&
        (nextFrameDisposalMethod != imgIContainer::kDisposeRestorePrevious) &&
        (nextFormat == gfxIFormats::RGB)) {
      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
      *aFrameToUse = aNextFrame;
      return NS_OK;
    }
  }

  // Calculate area that needs updating
  switch (prevFrameDisposalMethod) {
    default:
    case imgIContainer::kDisposeNotSpecified:
    case imgIContainer::kDisposeKeep:
      *aDirtyRect = nextFrameRect;
      break;

    case imgIContainer::kDisposeClearAll:
      // Whole image container is cleared
      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
      break;

    case imgIContainer::kDisposeClear:
      // Calc area that needs to be redrawn (the combination of previous and
      // this frame)
      // XXX - This could be done with multiple framechanged calls
      //       Having prevFrame way at the top of the image, and nextFrame
      //       way at the bottom, and both frames being small, we'd be
      //       telling framechanged to refresh the whole image when only two
      //       small areas are needed.
      aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
      break;

    case imgIContainer::kDisposeRestorePrevious:
      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
      break;
  }

  // Optimization:
  //   Skip compositing if the last composited frame is this frame
  //   (Only one composited frame was made for this animation.  Example:
  //    Only Frame 3 of a 10 frame image required us to build a composite frame
  //    On the second loop, we do not need to rebuild the frame
  //    since it's still sitting in compositingFrame)
  if (mAnim->lastCompositedFrameIndex == aNextFrameIndex) {
    *aFrameToUse = mAnim->compositingFrame;
    return NS_OK;
  }

  PRBool needToBlankComposite = PR_FALSE;

  // Create the Compositing Frame
  if (!mAnim->compositingFrame) {
    nsresult rv;
    mAnim->compositingFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2", &rv);
    if (NS_FAILED(rv))
      return rv;
    rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
                                       gfxIFormats::RGB_A1, 24);
    if (NS_FAILED(rv)) {
      NS_WARNING("Failed to init compositingFrame!\n");
      mAnim->compositingFrame = nsnull;
      return rv;
    }
    needToBlankComposite = PR_TRUE;
  } else if (aNextFrameIndex == 1) {
    // When we are looping the compositing frame needs to be cleared.
    needToBlankComposite = PR_TRUE;
  }

  // More optimizations possible when next frame is not transparent
  PRBool doDisposal = PR_TRUE;
  if ((nextFormat == gfxIFormats::RGB)||(nextFormat == gfxIFormats::PAL)) {
    if (isFullNextFrame) {
      // Optimization: No need to dispose prev.frame when 
      // next frame is full frame and not transparent.
      doDisposal = PR_FALSE;
      // No need to blank the composite frame
      needToBlankComposite = PR_FALSE;
    } else {
      if ((prevFrameRect.x >= nextFrameRect.x) &&
          (prevFrameRect.y >= nextFrameRect.y) &&
          (prevFrameRect.x + prevFrameRect.width <= nextFrameRect.x + nextFrameRect.width) &&
          (prevFrameRect.y + prevFrameRect.height <= nextFrameRect.y + nextFrameRect.height)) {
        // Optimization: No need to dispose prev.frame when 
        // next frame fully overlaps previous frame.
        doDisposal = PR_FALSE;  
      }
    }      
  }

  if (doDisposal) {
    // Dispose of previous: clear, restore, or keep (copy)
    switch (prevFrameDisposalMethod) {
      case imgIContainer::kDisposeClear:
        if (needToBlankComposite) {
          // If we just created the composite, it could have anything in it's
          // buffer. Clear whole frame
          ClearFrame(mAnim->compositingFrame);
        } else {
          // Only blank out previous frame area (both color & Mask/Alpha)
          ClearFrame(mAnim->compositingFrame, prevFrameRect);
        }
        break;
  
      case imgIContainer::kDisposeClearAll:
        ClearFrame(mAnim->compositingFrame);
        break;
  
      case imgIContainer::kDisposeRestorePrevious:
        // It would be better to copy only the area changed back to
        // compositingFrame.
        if (mAnim->compositingPrevFrame) {
          CopyFrameImage(mAnim->compositingPrevFrame, mAnim->compositingFrame);
  
          // destroy only if we don't need it for this frame's disposal
          if (nextFrameDisposalMethod != imgIContainer::kDisposeRestorePrevious)
            mAnim->compositingPrevFrame = nsnull;
        } else {
          ClearFrame(mAnim->compositingFrame);
        }
        break;
      
      default:
        // Copy previous frame into compositingFrame before we put the new frame on top
        // Assumes that the previous frame represents a full frame (it could be
        // smaller in size than the container, as long as the frame before it erased
        // itself)
        // Note: Frame 1 never gets into DoComposite(), so (aNextFrameIndex - 1) will
        // always be a valid frame number.
        if (mAnim->lastCompositedFrameIndex != aNextFrameIndex - 1) {
          gfx_format prevFormat;
          aPrevFrame->GetFormat(&prevFormat);
          if (isFullPrevFrame && 
              prevFormat != gfxIFormats::PAL && prevFormat != gfxIFormats::PAL_A1) {
            // Just copy the bits
            CopyFrameImage(aPrevFrame, mAnim->compositingFrame);
          } else {
            if (needToBlankComposite) {
              // Only blank composite when prev is transparent or not full.
              if (!isFullPrevFrame ||
                  (prevFormat != gfxIFormats::RGB && prevFormat != gfxIFormats::PAL)) {
                ClearFrame(mAnim->compositingFrame);
              }
            }
            DrawFrameTo(aPrevFrame, mAnim->compositingFrame, prevFrameRect);
          }
        }
    }
  } else if (needToBlankComposite) {
    // If we just created the composite, it could have anything in it's
    // buffers. Clear them
    ClearFrame(mAnim->compositingFrame);
  }

  // Check if the frame we are composing wants the previous image restored afer
  // it is done. Don't store it (again) if last frame wanted its image restored
  // too
  if ((nextFrameDisposalMethod == imgIContainer::kDisposeRestorePrevious) &&
      (prevFrameDisposalMethod != imgIContainer::kDisposeRestorePrevious)) {
    // We are storing the whole image.
    // It would be better if we just stored the area that nextFrame is going to
    // overwrite.
    if (!mAnim->compositingPrevFrame) {
      nsresult rv;
      mAnim->compositingPrevFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2",
                                                       &rv);
      if (NS_FAILED(rv))
        return rv;
      rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
                                              gfxIFormats::RGB_A1, 24);
      if (NS_FAILED(rv))
        return rv;
    }
    CopyFrameImage(mAnim->compositingFrame, mAnim->compositingPrevFrame);
  }

  // blit next frame into it's correct spot
  DrawFrameTo(aNextFrame, mAnim->compositingFrame, nextFrameRect);
  // Set timeout of CompositeFrame to timeout of frame we just composed
  // Bug 177948
  PRInt32 timeout;
  aNextFrame->GetTimeout(&timeout);
  mAnim->compositingFrame->SetTimeout(timeout);

  // Tell the image that it is fully 'downloaded'.
  nsIntRect r;
  mAnim->compositingFrame->GetRect(r);
  nsCOMPtr<nsIImage> img = do_GetInterface(mAnim->compositingFrame);
  img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);

  // We don't want to keep composite images for 8bit frames...
  if (isFullNextFrame && mAnimationMode == kNormalAnimMode && mLoopCount != 0 &&
      nextFormat != gfxIFormats::PAL && nextFormat != gfxIFormats::PAL_A1) {
    // We have a composited full frame
    // Store the composited frame into the mFrames[..] so we don't have to
    // continuously re-build it
    // Then set the previous frame's disposal to CLEAR_ALL so we just draw the
    // frame next time around
    if (CopyFrameImage(mAnim->compositingFrame, aNextFrame)) {
      aPrevFrame->SetFrameDisposalMethod(imgIContainer::kDisposeClearAll);
      mAnim->lastCompositedFrameIndex = -1;
      *aFrameToUse = aNextFrame;
      return NS_OK;
    }
  }

  mAnim->lastCompositedFrameIndex = aNextFrameIndex;
  *aFrameToUse = mAnim->compositingFrame;

  return NS_OK;
}