Files
palemoon27/image/Decoder.cpp
T
roytam1 6e2582b2b6 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1117087 Don't reset IM context when nsGtkIMContext receives selection change during dispatching compositionstart r=m_kato (0bbca7a4c)
- Bug 1155494 - Add a 'compressall' message flag. f=handyman r=billm (parts of this patch were written by handyman with r=billm) (5bb9ccd32)
- Bug 1167882 - Move a couple of static helpers functions higher up in TabChild.cpp. r=kats (75a880959)
- Bug 1167882 - Add utility functions MaxScaleRatio() and MinScaleRatio() to Units.h. r=kats (3604aa9e2)
- Bug 1167882 - Use the display size (includes scrollbar areas) rather than the root composition bounds (excludes scrollbar areas) to calculate the intrinsic scale. r=kats (8b6f2917c)
- Fix a potential null-deref in TabChild::HandlePossibleViewportChange. (bug 1156156, r=kats) (55e5e79f4)
- Bug 1164406 - Exclude the scrollbars from the composition bounds in TabChild::HandleCalculateViewportChange() when appropriate. r=kats (28a7efaad)
- Bug 1089090, e10s, support custom cursors, r=jmathies (6f005a600)
- Bug 1125325 - Make TabParent/TabChild UpdateDimensions messages aware of the display scale. r=kats (07ac774b2)
- Bug 1157248 - TabChild should initialize ServiceWorkerManager before starting the first loading, r=nsm (0eb50c148)
- Bug 1060643 - Give content the opportunity to block chrome keyhandlers. r=smaug (93cdd9608)
- Bug 1137555 - Add implementation of PuppetWidget::GetMaxTouchPoints(). r=smaug, r=jimm (d859fd19a)
- Bug 1164763 - Avoid one extra IPC round-trip when compositor-lru-size == 0. r=mattwoodrow,billm (19cfdf029)
- bug 1167295 - manage PDocAccessible with PBrowser instead of PContent r=smaug (5787cd60c)
- Bug 1161592 - If we don't get a target frame from the touch point try using the root scrollable frame. r=botond (c0d49d85f)
- Bug 1162648. When updating layout with the latest async values from the APZC do the scrolling last because it can flush layout. r=botond,kats (f8a354aab)
- Bug 1163572 - Modify UpdateRootFrame to automatically compute the nsIContent* and nsIPresShell* from the metrics. r=botond (9d7fda5c8)
- Bug 1163572 - Have ChromeProcessController also handle root-frame repaint requests. r=botond (67786ad9f)
- Bug 1163572 - Modify UpdateSubFrame to automatically figure out the nsIContent from the scrollId. r=botond (136aff8a1)
- Bug 1175383 TabChild should store PuppetWidget as is rather than as nsIWidget r=smaug (3e2bfd3ad)
- Bug 1158424 - Expose IsRootContent() in AsyncPanZoomController. r=kats (632ba6051)
- Bug 1158424 - Clean up uses of HasNoParentWithSameLayersId() related to zoom constraints. r=kats (c94b5f517)
- Bug 1166942 - Add logging code for gfx::Matrix. r=nical (5dc405fa6)
- Bug 1055557 - Move storage of ZoomConstraints from RemoteContentController to APZCTreeManager. r=botond (8e1056b71)
- Bug 1055557 - Add a ZoomConstraintsClient class to manage pushing zoom constraints updates to the APZ code. r=botond (69f1ae857)
- Bug 1055557 - Constify some methods. r=botond (b81ec32f9)
- Bug 1055557 - Ensure the right presShell resolution is used in ApplyCallbackTransform for fennec-apz scenarios. r=botond (6df0c30e7)
- Bug 1175217 - Fix non-unified (and some unified) builds for nsDisplayListInvalidation.cpp. r=mstange (cd0662dfa)
- Bug 1160421 - Replace nsThreadPool with a custom thread pool implementation in DecodePool. r=tn (66ae4fac2)
- Bug 1130935 part.1 Add a method to check if current IME supports vertical writing mode r=emk (056232715)
- Bug 1130935 part.2 Set proper composition font when writing mode is changed r=emk (e5cc07c43)
- Bug 1130935 part.3 Add hack for Japanist because its candidate window is broken with @System font r=emk (4e7587585)
- Bug 1130935 part.4 nsIMM32Handler::GetCharacterRectOfSelectedTextAt() should return wrting mode if it's necessary r=emk+smaug (92e76951a)
- Bug 1130935 part.5 nsIMM32Handler should compute candidate window position with writing mode r=emk (676e1c274)
- Bug 1130935 part.6 Selection change notification should have selection range and writing mode information r=smaug (a0e37af25)
2021-02-06 08:28:03 +08:00

711 lines
21 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Decoder.h"
#include "mozilla/gfx/2D.h"
#include "DecodePool.h"
#include "GeckoProfiler.h"
#include "imgIContainer.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "nsProxyRelease.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/Telemetry.h"
using mozilla::gfx::IntSize;
using mozilla::gfx::SurfaceFormat;
namespace mozilla {
namespace image {
Decoder::Decoder(RasterImage* aImage)
: mImage(aImage)
, mProgress(NoProgress)
, mImageData(nullptr)
, mColormap(nullptr)
, mChunkCount(0)
, mFlags(0)
, mBytesDecoded(0)
, mSendPartialInvalidations(false)
, mDataDone(false)
, mDecodeDone(false)
, mDataError(false)
, mDecodeAborted(false)
, mShouldReportError(false)
, mImageIsTransient(false)
, mImageIsLocked(false)
, mFrameCount(0)
, mFailCode(NS_OK)
, mNeedsNewFrame(false)
, mNeedsToFlushData(false)
, mInitialized(false)
, mSizeDecode(false)
, mInFrame(false)
, mIsAnimated(false)
{ }
Decoder::~Decoder()
{
MOZ_ASSERT(mProgress == NoProgress,
"Destroying Decoder without taking all its progress changes");
MOZ_ASSERT(mInvalidRect.IsEmpty(),
"Destroying Decoder without taking all its invalidations");
mInitialized = false;
if (!NS_IsMainThread()) {
// Dispatch mImage to main thread to prevent it from being destructed by the
// decode thread.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
if (mainThread) {
// Handle ambiguous nsISupports inheritance.
RasterImage* rawImg = nullptr;
mImage.swap(rawImg);
DebugOnly<nsresult> rv =
NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
}
}
}
/*
* Common implementation of the decoder interface.
*/
void
Decoder::Init()
{
// No re-initializing
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
// Fire OnStartDecode at init time to support bug 512435.
if (!IsSizeDecode()) {
mProgress |= FLAG_DECODE_STARTED | FLAG_ONLOAD_BLOCKED;
}
// Implementation-specific initialization
InitInternal();
mInitialized = true;
}
// Initializes a decoder whose image and observer is already being used by a
// parent decoder
void
Decoder::InitSharedDecoder(uint8_t* aImageData, uint32_t aImageDataLength,
uint32_t* aColormap, uint32_t aColormapSize,
RawAccessFrameRef&& aFrameRef)
{
// No re-initializing
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
mImageData = aImageData;
mImageDataLength = aImageDataLength;
mColormap = aColormap;
mColormapSize = aColormapSize;
mCurrentFrame = Move(aFrameRef);
// We have all the frame data, so we've started the frame.
if (!IsSizeDecode()) {
mFrameCount++;
PostFrameStart();
}
// Implementation-specific initialization
InitInternal();
mInitialized = true;
}
nsresult
Decoder::Decode()
{
MOZ_ASSERT(mInitialized, "Should be initialized here");
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
// We keep decoding chunks until the decode completes or there are no more
// chunks available.
while (!GetDecodeDone() && !HasError()) {
auto newState = mIterator->AdvanceOrScheduleResume(this);
if (newState == SourceBufferIterator::WAITING) {
// We can't continue because the rest of the data hasn't arrived from the
// network yet. We don't have to do anything special; the
// SourceBufferIterator will ensure that Decode() gets called again on a
// DecodePool thread when more data is available.
return NS_OK;
}
if (newState == SourceBufferIterator::COMPLETE) {
mDataDone = true;
nsresult finalStatus = mIterator->CompletionStatus();
if (NS_FAILED(finalStatus)) {
PostDataError();
}
CompleteDecode();
return finalStatus;
}
MOZ_ASSERT(newState == SourceBufferIterator::READY);
Write(mIterator->Data(), mIterator->Length());
}
CompleteDecode();
return HasError() ? NS_ERROR_FAILURE : NS_OK;
}
void
Decoder::Resume()
{
DecodePool* decodePool = DecodePool::Singleton();
MOZ_ASSERT(decodePool);
decodePool->AsyncDecode(this);
}
bool
Decoder::ShouldSyncDecode(size_t aByteLimit)
{
MOZ_ASSERT(aByteLimit > 0);
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
}
void
Decoder::Write(const char* aBuffer, uint32_t aCount)
{
PROFILER_LABEL("ImageDecoder", "Write",
js::ProfileEntry::Category::GRAPHICS);
// We're strict about decoder errors
MOZ_ASSERT(!HasDecoderError(),
"Not allowed to make more decoder calls after error!");
// Begin recording telemetry data.
TimeStamp start = TimeStamp::Now();
mChunkCount++;
// Keep track of the total number of bytes written.
mBytesDecoded += aCount;
// If we're flushing data, clear the flag.
if (aBuffer == nullptr && aCount == 0) {
MOZ_ASSERT(mNeedsToFlushData, "Flushing when we don't need to");
mNeedsToFlushData = false;
}
// If a data error occured, just ignore future data.
if (HasDataError()) {
return;
}
if (IsSizeDecode() && HasSize()) {
// More data came in since we found the size. We have nothing to do here.
return;
}
MOZ_ASSERT(!NeedsNewFrame() || HasDataError(),
"Should not need a new frame before writing anything");
MOZ_ASSERT(!NeedsToFlushData() || HasDataError(),
"Should not need to flush data before writing anything");
// Pass the data along to the implementation.
WriteInternal(aBuffer, aCount);
// If we need a new frame to proceed, let's create one and call it again.
while (NeedsNewFrame() && !HasDataError()) {
MOZ_ASSERT(!IsSizeDecode(), "Shouldn't need new frame for size decode");
nsresult rv = AllocateFrame();
if (NS_SUCCEEDED(rv)) {
// Use the data we saved when we asked for a new frame.
WriteInternal(nullptr, 0);
}
mNeedsToFlushData = false;
}
// Finish telemetry.
mDecodeTime += (TimeStamp::Now() - start);
}
void
Decoder::CompleteDecode()
{
// Implementation-specific finalization
if (!HasError()) {
FinishInternal();
}
// If the implementation left us mid-frame, finish that up.
if (mInFrame && !HasError()) {
PostFrameStop();
}
// If PostDecodeDone() has not been called, and this decoder wasn't aborted
// early because of low-memory conditions or losing a race with another
// decoder, we need to send teardown notifications (and report an error to the
// console later).
if (!IsSizeDecode() && !mDecodeDone && !WasAborted()) {
mShouldReportError = true;
// If we only have a data error, we're usable if we have at least one
// complete frame.
if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
// We're usable, so do exactly what we should have when the decoder
// completed.
// Not writing to the entire frame may have left us transparent.
PostHasTransparency();
if (mInFrame) {
PostFrameStop();
}
PostDecodeDone();
} else {
// We're not usable. Record some final progress indicating the error.
if (!IsSizeDecode()) {
mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
}
mProgress |= FLAG_HAS_ERROR;
}
}
}
void
Decoder::Finish()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(HasError() || !mInFrame, "Finishing while we're still in a frame");
// If we detected an error in CompleteDecode(), log it to the error console.
if (mShouldReportError && !WasAborted()) {
nsCOMPtr<nsIConsoleService> consoleService =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
nsCOMPtr<nsIScriptError> errorObject =
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
if (consoleService && errorObject && !HasDecoderError()) {
nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
NS_ConvertUTF8toUTF16(mImage->GetURIString()));
if (NS_SUCCEEDED(errorObject->InitWithWindowID(
msg,
NS_ConvertUTF8toUTF16(mImage->GetURIString()),
EmptyString(), 0, 0, nsIScriptError::errorFlag,
"Image", mImage->InnerWindowID()
))) {
consoleService->LogMessage(errorObject);
}
}
}
// Set image metadata before calling DecodingComplete, because
// DecodingComplete calls Optimize().
mImageMetadata.SetOnImage(mImage);
if (HasSize()) {
SetSizeOnImage();
}
if (mDecodeDone && !IsSizeDecode()) {
MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
// If this image wasn't animated and isn't a transient image, mark its frame
// as optimizable. We don't support optimizing animated images and
// optimizing transient images isn't worth it.
if (!mIsAnimated && !mImageIsTransient && mCurrentFrame) {
mCurrentFrame->SetOptimizable();
}
mImage->OnDecodingComplete();
}
}
void
Decoder::FinishSharedDecoder()
{
if (!HasError()) {
FinishInternal();
}
}
nsresult
Decoder::AllocateFrame(const nsIntSize& aTargetSize /* = nsIntSize() */)
{
MOZ_ASSERT(mNeedsNewFrame);
nsIntSize targetSize = aTargetSize;
if (targetSize == nsIntSize()) {
MOZ_ASSERT(HasSize());
targetSize = mImageMetadata.GetSize();
}
mCurrentFrame = EnsureFrame(mNewFrameData.mFrameNum,
targetSize,
mNewFrameData.mFrameRect,
GetDecodeFlags(),
mNewFrameData.mFormat,
mNewFrameData.mPaletteDepth,
mCurrentFrame.get());
if (mCurrentFrame) {
// Gather the raw pointers the decoders will use.
mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
if (mNewFrameData.mFrameNum + 1 == mFrameCount) {
PostFrameStart();
}
} else {
PostDataError();
}
// Mark ourselves as not needing another frame before talking to anyone else
// so they can tell us if they need yet another.
mNeedsNewFrame = false;
// If we've received any data at all, we may have pending data that needs to
// be flushed now that we have a frame to decode into.
if (mBytesDecoded > 0) {
mNeedsToFlushData = true;
}
return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
}
RawAccessFrameRef
Decoder::EnsureFrame(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();
}
MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!");
if (aFrameNum > mFrameCount) {
return RawAccessFrameRef();
}
// Adding a frame that doesn't already exist. This is the normal case.
if (aFrameNum == mFrameCount) {
return InternalAddFrame(aFrameNum, aTargetSize, aFrameRect, aDecodeFlags,
aFormat, aPaletteDepth, aPreviousFrame);
}
// We're replacing a frame. It must be the first frame; there's no reason to
// ever replace any other frame, since the first frame is the only one we
// speculatively allocate without knowing what the decoder really needs.
// XXX(seth): I'm not convinced there's any reason to support this at all. We
// should figure out how to avoid triggering this and rip it out.
MOZ_ASSERT(aFrameNum == 0, "Replacing a frame other than the first?");
MOZ_ASSERT(mFrameCount == 1, "Should have only one frame");
MOZ_ASSERT(aPreviousFrame, "Need the previous frame to replace");
if (aFrameNum != 0 || !aPreviousFrame || mFrameCount != 1) {
return RawAccessFrameRef();
}
MOZ_ASSERT(!aPreviousFrame->GetRect().IsEqualEdges(aFrameRect) ||
aPreviousFrame->GetFormat() != aFormat ||
aPreviousFrame->GetPaletteDepth() != aPaletteDepth,
"Replacing first frame with the same kind of frame?");
// Reset our state.
mInFrame = false;
RawAccessFrameRef ref = Move(mCurrentFrame);
MOZ_ASSERT(ref, "No ref to current frame?");
// Reinitialize the old frame.
nsIntSize oldSize = aPreviousFrame->GetImageSize();
bool nonPremult =
aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
if (NS_FAILED(aPreviousFrame->ReinitForDecoder(oldSize, aFrameRect, aFormat,
aPaletteDepth, nonPremult))) {
NS_WARNING("imgFrame::ReinitForDecoder should succeed");
mFrameCount = 0;
aPreviousFrame->Abort();
return RawAccessFrameRef();
}
return ref;
}
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();
}
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::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;
}
void
Decoder::SetSizeOnImage()
{
MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
nsresult rv = mImage->SetSize(mImageMetadata.GetWidth(),
mImageMetadata.GetHeight(),
mImageMetadata.GetOrientation());
if (NS_FAILED(rv)) {
PostResizeError();
}
}
/*
* Hook stubs. Override these as necessary in decoder implementations.
*/
void Decoder::InitInternal() { }
void Decoder::WriteInternal(const char* aBuffer, uint32_t aCount) { }
void Decoder::FinishInternal() { }
/*
* Progress Notifications
*/
void
Decoder::PostSize(int32_t aWidth,
int32_t aHeight,
Orientation aOrientation /* = Orientation()*/)
{
// Validate
MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
// Tell the image
mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
// Record this notification.
mProgress |= FLAG_SIZE_AVAILABLE;
}
void
Decoder::PostHasTransparency()
{
mProgress |= FLAG_HAS_TRANSPARENCY;
}
void
Decoder::PostFrameStart()
{
// We shouldn't already be mid-frame
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
// Update our state to reflect the new frame
mInFrame = true;
// If we just became animated, record that fact.
if (mFrameCount > 1) {
mIsAnimated = true;
mProgress |= FLAG_IS_ANIMATED;
}
}
void
Decoder::PostFrameStop(Opacity aFrameOpacity /* = Opacity::TRANSPARENT */,
DisposalMethod aDisposalMethod
/* = DisposalMethod::KEEP */,
int32_t aTimeout /* = 0 */,
BlendMethod aBlendMethod /* = BlendMethod::OVER */)
{
// We should be mid-frame
MOZ_ASSERT(!IsSizeDecode(), "Stopping frame during a size decode");
MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
// Update our state
mInFrame = false;
mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod);
mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
// If we're not sending partial invalidations, then we send an invalidation
// here when the first frame is complete.
if (!mSendPartialInvalidations && !mIsAnimated) {
mInvalidRect.UnionRect(mInvalidRect,
gfx::IntRect(gfx::IntPoint(0, 0), GetSize()));
}
}
void
Decoder::PostInvalidation(const nsIntRect& aRect,
const Maybe<nsIntRect>& aRectAtTargetSize
/* = Nothing() */)
{
// We should be mid-frame
MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!");
MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!");
// Record this invalidation, unless we're not sending partial invalidations
// or we're past the first frame.
if (mSendPartialInvalidations && !mIsAnimated) {
mInvalidRect.UnionRect(mInvalidRect, aRect);
mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
}
}
void
Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
{
MOZ_ASSERT(!IsSizeDecode(), "Can't be done with decoding with size decode!");
MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
MOZ_ASSERT(!mDecodeDone, "Decode already done!");
mDecodeDone = true;
mImageMetadata.SetLoopCount(aLoopCount);
mProgress |= FLAG_DECODE_COMPLETE;
}
void
Decoder::PostDataError()
{
mDataError = true;
if (mInFrame && mCurrentFrame) {
mCurrentFrame->Abort();
}
}
void
Decoder::PostDecoderError(nsresult aFailureCode)
{
MOZ_ASSERT(NS_FAILED(aFailureCode), "Not a failure code!");
mFailCode = aFailureCode;
// XXXbholley - we should report the image URI here, but imgContainer
// needs to know its URI first
NS_WARNING("Image decoding error - This is probably a bug!");
if (mInFrame && mCurrentFrame) {
mCurrentFrame->Abort();
}
}
void
Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
uint32_t width, uint32_t height,
gfx::SurfaceFormat format,
uint8_t palette_depth /* = 0 */)
{
// Decoders should never call NeedNewFrame without yielding back to Write().
MOZ_ASSERT(!mNeedsNewFrame);
// We don't want images going back in time or skipping frames.
MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount - 1));
mNewFrameData = NewFrameData(framenum,
nsIntRect(x_offset, y_offset, width, height),
format, palette_depth);
mNeedsNewFrame = true;
}
Telemetry::ID
Decoder::SpeedHistogram()
{
// Use HistogramCount as an invalid Histogram ID.
return Telemetry::HistogramCount;
}
} // namespace image
} // namespace mozilla