RawAccessFrameRef Decoder::InternalAddFrame(uint32_t aFrameNum, const nsIntSize& aTargetSize, const nsIntRect& aFrameRect, uint32_t aDecodeFlags, SurfaceFormat aFormat, uint8_t aPaletteDepth, imgFrame* aPreviousFrame) { MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!"); if (aFrameNum > mFrameCount) { return RawAccessFrameRef(); } if (aTargetSize.width <= 0 || aTargetSize.height <= 0 || aFrameRect.width <= 0 || aFrameRect.height <= 0) { NS_WARNING("Trying to add frame with zero or negative size"); return RawAccessFrameRef(); } if (!SurfaceCache::CanHold(aTargetSize.ToIntSize())) { NS_WARNING("Trying to add frame that's too large for the SurfaceCache"); return RawAccessFrameRef(); } nsRefPtr<imgFrame> frame = new imgFrame(); bool nonPremult = aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat, aPaletteDepth, nonPremult))) { NS_WARNING("imgFrame::Init should succeed"); return RawAccessFrameRef(); } RawAccessFrameRef ref = frame->RawAccessRef(); if (!ref) { frame->Abort(); return RawAccessFrameRef(); } InsertOutcome outcome = SurfaceCache::Insert(frame, ImageKey(mImage.get()), RasterSurfaceKey(aTargetSize.ToIntSize(), aDecodeFlags, aFrameNum), Lifetime::Persistent); if (outcome != InsertOutcome::SUCCESS) { // We either hit InsertOutcome::FAILURE, which is a temporary failure due to // low memory (we know it's not permanent because we checked CanHold() // above), or InsertOutcome::FAILURE_ALREADY_PRESENT, which means that // another decoder beat us to decoding this frame. Either way, we should // abort this decoder rather than treat this as a real error. mDecodeAborted = true; ref->Abort(); return RawAccessFrameRef(); } nsIntRect refreshArea; if (aFrameNum == 1) { MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated"); aPreviousFrame->SetRawAccessOnly(); // If we dispose of the first frame by clearing it, then the first frame's // refresh area is all of itself. // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR). AnimationData previousFrameData = aPreviousFrame->GetAnimationData(); if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR || previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL || previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) { refreshArea = previousFrameData.mRect; } } if (aFrameNum > 0) { ref->SetRawAccessOnly(); // Some GIFs are huge but only have a small area that they animate. We only // need to refresh that small area when frame 0 comes around again. refreshArea.UnionRect(refreshArea, frame->GetRect()); } mFrameCount++; mImage->OnAddedFrame(mFrameCount, refreshArea); return ref; }
RawAccessFrameRef Decoder::AllocateFrameInternal(uint32_t aFrameNum, const nsIntSize& aTargetSize, const nsIntRect& aFrameRect, uint32_t aDecodeFlags, SurfaceFormat aFormat, uint8_t aPaletteDepth, imgFrame* aPreviousFrame) { if (mDataError || NS_FAILED(mFailCode)) { return RawAccessFrameRef(); } if (aFrameNum != mFrameCount) { MOZ_ASSERT_UNREACHABLE("Allocating frames out of order"); return RawAccessFrameRef(); } if (aTargetSize.width <= 0 || aTargetSize.height <= 0 || aFrameRect.width <= 0 || aFrameRect.height <= 0) { NS_WARNING("Trying to add frame with zero or negative size"); return RawAccessFrameRef(); } const uint32_t bytesPerPixel = aPaletteDepth == 0 ? 4 : 1; if (!SurfaceCache::CanHold(aFrameRect.Size(), bytesPerPixel)) { NS_WARNING("Trying to add frame that's too large for the SurfaceCache"); return RawAccessFrameRef(); } nsRefPtr<imgFrame> frame = new imgFrame(); bool nonPremult = aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat, aPaletteDepth, nonPremult))) { NS_WARNING("imgFrame::Init should succeed"); return RawAccessFrameRef(); } RawAccessFrameRef ref = frame->RawAccessRef(); if (!ref) { frame->Abort(); return RawAccessFrameRef(); } InsertOutcome outcome = SurfaceCache::Insert(frame, ImageKey(mImage.get()), RasterSurfaceKey(aTargetSize, aDecodeFlags, aFrameNum), Lifetime::Persistent); if (outcome == InsertOutcome::FAILURE) { // We couldn't insert the surface, almost certainly due to low memory. We // treat this as a permanent error to help the system recover; otherwise, we // might just end up attempting to decode this image again immediately. ref->Abort(); return RawAccessFrameRef(); } else if (outcome == InsertOutcome::FAILURE_ALREADY_PRESENT) { // Another decoder beat us to decoding this frame. We abort this decoder // rather than treat this as a real error. mDecodeAborted = true; ref->Abort(); return RawAccessFrameRef(); } nsIntRect refreshArea; if (aFrameNum == 1) { MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated"); aPreviousFrame->SetRawAccessOnly(); // If we dispose of the first frame by clearing it, then the first frame's // refresh area is all of itself. // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR). AnimationData previousFrameData = aPreviousFrame->GetAnimationData(); if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR || previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL || previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) { refreshArea = previousFrameData.mRect; } } if (aFrameNum > 0) { ref->SetRawAccessOnly(); // Some GIFs are huge but only have a small area that they animate. We only // need to refresh that small area when frame 0 comes around again. refreshArea.UnionRect(refreshArea, frame->GetRect()); } mFrameCount++; mImage->OnAddedFrame(mFrameCount, refreshArea); return ref; }