2010-08-23 06:30:45 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* 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/. */
|
2010-08-23 06:30:45 +04:00
|
|
|
|
|
|
|
#include "Decoder.h"
|
2015-01-08 11:01:25 +03:00
|
|
|
|
|
|
|
#include "mozilla/gfx/2D.h"
|
2015-01-16 02:11:36 +03:00
|
|
|
#include "DecodePool.h"
|
|
|
|
#include "GeckoProfiler.h"
|
2016-06-29 23:43:19 +03:00
|
|
|
#include "IDecodingTask.h"
|
2016-07-02 08:20:32 +03:00
|
|
|
#include "ISurfaceProvider.h"
|
2015-01-16 02:11:36 +03:00
|
|
|
#include "nsProxyRelease.h"
|
2013-09-07 17:01:08 +04:00
|
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
#include "nsComponentManagerUtils.h"
|
2015-02-03 18:05:49 +03:00
|
|
|
#include "mozilla/Telemetry.h"
|
2010-08-23 06:30:45 +04:00
|
|
|
|
2015-01-08 11:01:25 +03:00
|
|
|
using mozilla::gfx::IntSize;
|
|
|
|
using mozilla::gfx::SurfaceFormat;
|
|
|
|
|
2010-08-23 06:30:45 +04:00
|
|
|
namespace mozilla {
|
2012-01-06 20:02:27 +04:00
|
|
|
namespace image {
|
2010-08-23 06:30:45 +04:00
|
|
|
|
2016-07-11 10:03:42 +03:00
|
|
|
class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final
|
|
|
|
{
|
|
|
|
public:
|
2016-07-11 10:44:39 +03:00
|
|
|
explicit AutoRecordDecoderTelemetry(Decoder* aDecoder)
|
2016-07-11 10:03:42 +03:00
|
|
|
: mDecoder(aDecoder)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mDecoder);
|
|
|
|
|
|
|
|
// Begin recording telemetry data.
|
|
|
|
mStartTime = TimeStamp::Now();
|
|
|
|
}
|
|
|
|
|
|
|
|
~AutoRecordDecoderTelemetry()
|
|
|
|
{
|
|
|
|
// Finish telemetry.
|
|
|
|
mDecoder->mDecodeTime += (TimeStamp::Now() - mStartTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Decoder* mDecoder;
|
|
|
|
TimeStamp mStartTime;
|
|
|
|
};
|
|
|
|
|
2015-01-16 02:11:35 +03:00
|
|
|
Decoder::Decoder(RasterImage* aImage)
|
2015-07-31 17:29:00 +03:00
|
|
|
: mImageData(nullptr)
|
|
|
|
, mImageDataLength(0)
|
2013-01-28 21:26:36 +04:00
|
|
|
, mColormap(nullptr)
|
2015-07-31 17:29:00 +03:00
|
|
|
, mColormapSize(0)
|
|
|
|
, mImage(aImage)
|
|
|
|
, mProgress(NoProgress)
|
|
|
|
, mFrameCount(0)
|
2016-07-20 03:14:30 +03:00
|
|
|
, mLoopLength(FrameTimeout::Zero())
|
2015-08-15 03:56:44 +03:00
|
|
|
, mDecoderFlags(DefaultDecoderFlags())
|
|
|
|
, mSurfaceFlags(DefaultSurfaceFlags())
|
2015-07-31 17:29:00 +03:00
|
|
|
, mInitialized(false)
|
|
|
|
, mMetadataDecode(false)
|
|
|
|
, mInFrame(false)
|
2016-07-28 03:12:25 +03:00
|
|
|
, mFinishedNewFrame(false)
|
2016-07-11 10:42:56 +03:00
|
|
|
, mReachedTerminalState(false)
|
2012-01-03 00:23:41 +04:00
|
|
|
, mDecodeDone(false)
|
2016-07-11 11:09:10 +03:00
|
|
|
, mError(false)
|
2015-01-16 02:11:36 +03:00
|
|
|
, mDecodeAborted(false)
|
2015-01-27 09:53:20 +03:00
|
|
|
, mShouldReportError(false)
|
2014-11-15 07:06:19 +03:00
|
|
|
{ }
|
2010-08-23 06:30:45 +04:00
|
|
|
|
|
|
|
Decoder::~Decoder()
|
|
|
|
{
|
2015-07-31 17:29:18 +03:00
|
|
|
MOZ_ASSERT(mProgress == NoProgress || !mImage,
|
2014-11-18 12:48:49 +03:00
|
|
|
"Destroying Decoder without taking all its progress changes");
|
2015-07-31 17:29:18 +03:00
|
|
|
MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage,
|
2014-11-18 12:48:49 +03:00
|
|
|
"Destroying Decoder without taking all its invalidations");
|
2010-08-23 06:30:45 +04:00
|
|
|
mInitialized = false;
|
2015-01-16 02:11:35 +03:00
|
|
|
|
2015-07-31 17:29:07 +03:00
|
|
|
if (mImage && !NS_IsMainThread()) {
|
2015-01-16 02:11:35 +03:00
|
|
|
// Dispatch mImage to main thread to prevent it from being destructed by the
|
|
|
|
// decode thread.
|
2016-02-10 10:23:00 +03:00
|
|
|
NS_ReleaseOnMainThread(mImage.forget());
|
2015-01-16 02:11:35 +03:00
|
|
|
}
|
2010-08-23 06:30:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Common implementation of the decoder interface.
|
|
|
|
*/
|
|
|
|
|
2016-07-11 10:34:50 +03:00
|
|
|
nsresult
|
2011-09-27 20:24:03 +04:00
|
|
|
Decoder::Init()
|
2010-08-23 06:30:45 +04:00
|
|
|
{
|
2010-08-23 06:30:46 +04:00
|
|
|
// No re-initializing
|
2015-01-16 02:11:36 +03:00
|
|
|
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
|
2010-08-23 06:30:45 +04:00
|
|
|
|
2016-07-03 08:21:00 +03:00
|
|
|
// All decoders must have a SourceBufferIterator.
|
|
|
|
MOZ_ASSERT(mIterator);
|
|
|
|
|
2015-08-01 04:10:31 +03:00
|
|
|
// It doesn't make sense to decode anything but the first frame if we can't
|
|
|
|
// store anything in the SurfaceCache, since only the last frame we decode
|
|
|
|
// will be retrievable.
|
|
|
|
MOZ_ASSERT(ShouldUseSurfaceCache() || IsFirstFrameDecode());
|
|
|
|
|
2016-07-11 10:34:50 +03:00
|
|
|
// Implementation-specific initialization.
|
|
|
|
nsresult rv = InitInternal();
|
2013-02-02 05:06:30 +04:00
|
|
|
|
2010-08-23 06:30:45 +04:00
|
|
|
mInitialized = true;
|
2016-07-11 10:34:50 +03:00
|
|
|
|
|
|
|
return rv;
|
2010-08-23 06:30:45 +04:00
|
|
|
}
|
|
|
|
|
2016-07-19 09:46:35 +03:00
|
|
|
LexerResult
|
2016-07-15 08:39:39 +03:00
|
|
|
Decoder::Decode(IResumable* aOnResume /* = nullptr */)
|
2015-01-16 02:11:36 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(mInitialized, "Should be initialized here");
|
|
|
|
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
|
|
|
|
|
2016-07-12 09:02:20 +03:00
|
|
|
// If we're already done, don't attempt to keep decoding.
|
|
|
|
if (GetDecodeDone()) {
|
2016-07-19 09:46:35 +03:00
|
|
|
return LexerResult(HasError() ? TerminalState::FAILURE
|
|
|
|
: TerminalState::SUCCESS);
|
2016-07-12 09:02:20 +03:00
|
|
|
}
|
|
|
|
|
2016-07-16 08:27:12 +03:00
|
|
|
LexerResult lexerResult(TerminalState::FAILURE);
|
2016-07-11 10:44:39 +03:00
|
|
|
{
|
2016-07-15 08:39:39 +03:00
|
|
|
PROFILER_LABEL("ImageDecoder", "Decode", js::ProfileEntry::Category::GRAPHICS);
|
2016-07-11 10:44:39 +03:00
|
|
|
AutoRecordDecoderTelemetry telemetry(this);
|
|
|
|
|
2016-07-16 08:27:12 +03:00
|
|
|
lexerResult = DoDecode(*mIterator, aOnResume);
|
|
|
|
};
|
2016-07-15 08:39:39 +03:00
|
|
|
|
2016-07-16 08:27:12 +03:00
|
|
|
if (lexerResult.is<Yield>()) {
|
2016-07-19 09:46:35 +03:00
|
|
|
// We either need more data to continue (in which case either @aOnResume or
|
|
|
|
// the caller will reschedule us to run again later), or the decoder is
|
|
|
|
// yielding to allow the caller access to some intermediate output.
|
|
|
|
return lexerResult;
|
2016-07-11 10:44:39 +03:00
|
|
|
}
|
2016-07-14 22:30:58 +03:00
|
|
|
|
2016-07-15 08:39:39 +03:00
|
|
|
// We reached a terminal state; we're now done decoding.
|
2016-07-16 08:27:12 +03:00
|
|
|
MOZ_ASSERT(lexerResult.is<TerminalState>());
|
2016-07-11 10:42:56 +03:00
|
|
|
mReachedTerminalState = true;
|
2016-07-12 09:19:58 +03:00
|
|
|
|
|
|
|
// If decoding failed, record that fact.
|
2016-07-16 08:27:12 +03:00
|
|
|
if (lexerResult.as<TerminalState>() == TerminalState::FAILURE) {
|
2016-07-11 11:09:10 +03:00
|
|
|
PostError();
|
2016-07-12 09:19:58 +03:00
|
|
|
}
|
2015-01-16 02:11:36 +03:00
|
|
|
|
2016-07-15 08:39:39 +03:00
|
|
|
// Perform final cleanup.
|
2016-07-14 22:30:58 +03:00
|
|
|
CompleteDecode();
|
2016-07-12 09:23:09 +03:00
|
|
|
|
2016-07-19 09:46:35 +03:00
|
|
|
return LexerResult(HasError() ? TerminalState::FAILURE
|
|
|
|
: TerminalState::SUCCESS);
|
2015-01-16 02:11:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
Decoder::ShouldSyncDecode(size_t aByteLimit)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aByteLimit > 0);
|
|
|
|
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
|
|
|
|
|
|
|
|
return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
|
|
|
|
}
|
|
|
|
|
2010-09-12 19:22:31 +04:00
|
|
|
void
|
2015-01-27 09:53:20 +03:00
|
|
|
Decoder::CompleteDecode()
|
2010-08-23 06:30:45 +04:00
|
|
|
{
|
2016-07-11 10:38:54 +03:00
|
|
|
// Implementation-specific finalization.
|
|
|
|
nsresult rv = BeforeFinishInternal();
|
|
|
|
if (NS_FAILED(rv)) {
|
2016-07-11 11:09:10 +03:00
|
|
|
PostError();
|
2016-07-11 10:38:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
rv = HasError() ? FinishWithErrorInternal()
|
|
|
|
: FinishInternal();
|
|
|
|
if (NS_FAILED(rv)) {
|
2016-07-11 11:09:10 +03:00
|
|
|
PostError();
|
2016-07-11 10:38:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If this was a metadata decode and we never got a size, the decode failed.
|
|
|
|
if (IsMetadataDecode() && !HasSize()) {
|
2016-07-11 11:09:10 +03:00
|
|
|
PostError();
|
2015-02-11 03:47:00 +03:00
|
|
|
}
|
2010-09-12 19:22:30 +04:00
|
|
|
|
|
|
|
// If the implementation left us mid-frame, finish that up.
|
2015-02-11 03:47:00 +03:00
|
|
|
if (mInFrame && !HasError()) {
|
2010-09-12 19:22:30 +04:00
|
|
|
PostFrameStop();
|
2015-02-11 03:47:00 +03:00
|
|
|
}
|
2010-09-12 19:22:30 +04:00
|
|
|
|
2015-01-16 02:11:36 +03:00
|
|
|
// If PostDecodeDone() has not been called, and this decoder wasn't aborted
|
|
|
|
// early because of low-memory conditions or losing a race with another
|
2015-01-27 09:53:20 +03:00
|
|
|
// decoder, we need to send teardown notifications (and report an error to the
|
|
|
|
// console later).
|
2015-07-23 08:39:54 +03:00
|
|
|
if (!IsMetadataDecode() && !mDecodeDone && !WasAborted()) {
|
2015-01-27 09:53:20 +03:00
|
|
|
mShouldReportError = true;
|
|
|
|
|
2016-07-11 10:34:50 +03:00
|
|
|
// Even if we encountered an error, we're still usable if we have at least
|
|
|
|
// one complete frame.
|
|
|
|
if (GetCompleteFrameCount() > 0) {
|
2015-01-27 09:53:20 +03:00
|
|
|
// We're usable, so do exactly what we should have when the decoder
|
|
|
|
// completed.
|
2015-02-06 01:42:38 +03:00
|
|
|
|
|
|
|
// Not writing to the entire frame may have left us transparent.
|
|
|
|
PostHasTransparency();
|
|
|
|
|
2015-01-27 09:53:20 +03:00
|
|
|
if (mInFrame) {
|
|
|
|
PostFrameStop();
|
|
|
|
}
|
|
|
|
PostDecodeDone();
|
|
|
|
} else {
|
|
|
|
// We're not usable. Record some final progress indicating the error.
|
2015-07-23 08:39:54 +03:00
|
|
|
if (!IsMetadataDecode()) {
|
2015-06-20 01:55:12 +03:00
|
|
|
mProgress |= FLAG_DECODE_COMPLETE;
|
2015-01-27 09:53:20 +03:00
|
|
|
}
|
|
|
|
mProgress |= FLAG_HAS_ERROR;
|
|
|
|
}
|
|
|
|
}
|
2015-07-31 17:29:10 +03:00
|
|
|
|
|
|
|
if (mDecodeDone && !IsMetadataDecode()) {
|
|
|
|
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.
|
2015-08-15 03:56:44 +03:00
|
|
|
if (!HasAnimation() &&
|
|
|
|
!(mDecoderFlags & DecoderFlags::IMAGE_IS_TRANSIENT) &&
|
|
|
|
mCurrentFrame) {
|
2015-07-31 17:29:10 +03:00
|
|
|
mCurrentFrame->SetOptimizable();
|
|
|
|
}
|
|
|
|
}
|
2015-01-27 09:53:20 +03:00
|
|
|
}
|
2010-09-12 19:22:30 +04:00
|
|
|
|
2015-09-20 02:21:08 +03:00
|
|
|
nsresult
|
|
|
|
Decoder::SetTargetSize(const nsIntSize& aSize)
|
|
|
|
{
|
|
|
|
// Make sure the size is reasonable.
|
|
|
|
if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a downscaler that we'll filter our output through.
|
|
|
|
mDownscaler.emplace(aSize);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-07-03 06:20:55 +03:00
|
|
|
Maybe<IntSize>
|
|
|
|
Decoder::GetTargetSize()
|
|
|
|
{
|
|
|
|
return mDownscaler ? Some(mDownscaler->TargetSize()) : Nothing();
|
|
|
|
}
|
|
|
|
|
2016-07-28 03:12:25 +03:00
|
|
|
Maybe<uint32_t>
|
|
|
|
Decoder::TakeCompleteFrameCount()
|
|
|
|
{
|
|
|
|
const bool finishedNewFrame = mFinishedNewFrame;
|
|
|
|
mFinishedNewFrame = false;
|
|
|
|
return finishedNewFrame ? Some(GetCompleteFrameCount()) : Nothing();
|
|
|
|
}
|
|
|
|
|
2013-02-02 05:06:30 +04:00
|
|
|
nsresult
|
2015-07-11 05:26:15 +03:00
|
|
|
Decoder::AllocateFrame(uint32_t aFrameNum,
|
|
|
|
const nsIntSize& aTargetSize,
|
|
|
|
const nsIntRect& aFrameRect,
|
|
|
|
gfx::SurfaceFormat aFormat,
|
|
|
|
uint8_t aPaletteDepth)
|
2013-02-02 05:06:30 +04:00
|
|
|
{
|
2015-07-11 05:26:15 +03:00
|
|
|
mCurrentFrame = AllocateFrameInternal(aFrameNum, aTargetSize, aFrameRect,
|
2015-08-15 03:56:44 +03:00
|
|
|
aFormat, aPaletteDepth,
|
|
|
|
mCurrentFrame.get());
|
2013-06-08 00:42:57 +04:00
|
|
|
|
2014-11-27 00:22:10 +03:00
|
|
|
if (mCurrentFrame) {
|
|
|
|
// Gather the raw pointers the decoders will use.
|
|
|
|
mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
|
|
|
|
mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
|
2014-11-26 13:57:09 +03:00
|
|
|
|
2016-08-01 00:41:10 +03:00
|
|
|
// We should now be on |aFrameNum|. (Note that we're comparing the frame
|
|
|
|
// number, which is zero-based, with the frame count, which is one-based.)
|
|
|
|
MOZ_ASSERT(aFrameNum + 1 == mFrameCount);
|
2015-08-14 10:37:13 +03:00
|
|
|
|
2016-08-01 00:41:10 +03:00
|
|
|
// If we're past the first frame, PostIsAnimated() should've been called.
|
|
|
|
MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
|
|
|
|
|
|
|
|
// Update our state to reflect the new frame.
|
|
|
|
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
|
|
|
|
mInFrame = true;
|
2013-02-02 05:06:30 +04:00
|
|
|
}
|
|
|
|
|
2014-11-27 00:22:10 +03:00
|
|
|
return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
|
2013-02-02 05:06:30 +04:00
|
|
|
}
|
|
|
|
|
2015-01-08 11:01:25 +03:00
|
|
|
RawAccessFrameRef
|
2015-07-11 05:26:15 +03:00
|
|
|
Decoder::AllocateFrameInternal(uint32_t aFrameNum,
|
|
|
|
const nsIntSize& aTargetSize,
|
|
|
|
const nsIntRect& aFrameRect,
|
|
|
|
SurfaceFormat aFormat,
|
|
|
|
uint8_t aPaletteDepth,
|
|
|
|
imgFrame* aPreviousFrame)
|
2015-01-08 11:01:25 +03:00
|
|
|
{
|
2016-07-11 10:34:50 +03:00
|
|
|
if (HasError()) {
|
2015-01-08 11:01:25 +03:00
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
2015-07-11 05:26:15 +03:00
|
|
|
if (aFrameNum != mFrameCount) {
|
|
|
|
MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
|
2015-01-08 11:01:25 +03:00
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
2015-01-19 01:02:14 +03:00
|
|
|
if (aTargetSize.width <= 0 || aTargetSize.height <= 0 ||
|
2015-01-08 11:01:25 +03:00
|
|
|
aFrameRect.width <= 0 || aFrameRect.height <= 0) {
|
|
|
|
NS_WARNING("Trying to add frame with zero or negative size");
|
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
2015-05-06 08:19:30 +03:00
|
|
|
const uint32_t bytesPerPixel = aPaletteDepth == 0 ? 4 : 1;
|
2015-07-31 17:29:18 +03:00
|
|
|
if (ShouldUseSurfaceCache() &&
|
|
|
|
!SurfaceCache::CanHold(aFrameRect.Size(), bytesPerPixel)) {
|
2015-01-08 11:01:25 +03:00
|
|
|
NS_WARNING("Trying to add frame that's too large for the SurfaceCache");
|
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
2016-07-02 08:20:32 +03:00
|
|
|
NotNull<RefPtr<imgFrame>> frame = WrapNotNull(new imgFrame());
|
2015-08-15 03:56:44 +03:00
|
|
|
bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
|
2015-01-19 01:02:14 +03:00
|
|
|
if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat,
|
2015-01-08 11:04:31 +03:00
|
|
|
aPaletteDepth, nonPremult))) {
|
2015-01-08 11:01:25 +03:00
|
|
|
NS_WARNING("imgFrame::Init should succeed");
|
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
RawAccessFrameRef ref = frame->RawAccessRef();
|
|
|
|
if (!ref) {
|
2015-01-12 06:28:02 +03:00
|
|
|
frame->Abort();
|
2015-01-08 11:01:25 +03:00
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
2015-07-31 17:29:18 +03:00
|
|
|
if (ShouldUseSurfaceCache()) {
|
2016-07-02 08:20:32 +03:00
|
|
|
NotNull<RefPtr<ISurfaceProvider>> provider =
|
|
|
|
WrapNotNull(new SimpleSurfaceProvider(frame));
|
2015-07-31 17:29:18 +03:00
|
|
|
InsertOutcome outcome =
|
2016-07-02 08:20:32 +03:00
|
|
|
SurfaceCache::Insert(provider, ImageKey(mImage.get()),
|
2015-07-31 17:29:18 +03:00
|
|
|
RasterSurfaceKey(aTargetSize,
|
2015-08-15 03:56:44 +03:00
|
|
|
mSurfaceFlags,
|
2015-09-20 02:20:59 +03:00
|
|
|
aFrameNum));
|
2015-07-31 17:29:18 +03:00
|
|
|
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();
|
|
|
|
}
|
2015-01-08 11:01:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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).
|
2015-01-08 11:04:31 +03:00
|
|
|
AnimationData previousFrameData = aPreviousFrame->GetAnimationData();
|
|
|
|
if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR ||
|
|
|
|
previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL ||
|
|
|
|
previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
|
2016-07-20 09:35:41 +03:00
|
|
|
mFirstFrameRefreshArea = previousFrameData.mRect;
|
2015-01-08 11:01:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2016-07-20 09:35:41 +03:00
|
|
|
mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, frame->GetRect());
|
2015-01-08 11:01:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
mFrameCount++;
|
2015-07-31 17:29:18 +03:00
|
|
|
|
2015-01-08 11:01:25 +03:00
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
2010-08-23 06:30:45 +04:00
|
|
|
/*
|
|
|
|
* Hook stubs. Override these as necessary in decoder implementations.
|
|
|
|
*/
|
|
|
|
|
2016-07-11 10:34:50 +03:00
|
|
|
nsresult Decoder::InitInternal() { return NS_OK; }
|
2016-07-11 10:38:54 +03:00
|
|
|
nsresult Decoder::BeforeFinishInternal() { return NS_OK; }
|
|
|
|
nsresult Decoder::FinishInternal() { return NS_OK; }
|
|
|
|
nsresult Decoder::FinishWithErrorInternal() { return NS_OK; }
|
2010-08-23 06:30:45 +04:00
|
|
|
|
2010-08-23 06:30:46 +04:00
|
|
|
/*
|
|
|
|
* Progress Notifications
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2013-08-25 11:19:42 +04:00
|
|
|
Decoder::PostSize(int32_t aWidth,
|
|
|
|
int32_t aHeight,
|
|
|
|
Orientation aOrientation /* = Orientation()*/)
|
2010-08-23 06:30:46 +04:00
|
|
|
{
|
|
|
|
// Validate
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
|
|
|
|
MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
|
2010-08-23 06:30:46 +04:00
|
|
|
|
|
|
|
// Tell the image
|
2013-08-25 11:19:42 +04:00
|
|
|
mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
|
2010-08-23 06:30:46 +04:00
|
|
|
|
2014-11-15 07:06:19 +03:00
|
|
|
// Record this notification.
|
2014-11-18 01:29:56 +03:00
|
|
|
mProgress |= FLAG_SIZE_AVAILABLE;
|
2010-08-23 06:30:46 +04:00
|
|
|
}
|
|
|
|
|
2014-11-17 22:16:45 +03:00
|
|
|
void
|
|
|
|
Decoder::PostHasTransparency()
|
|
|
|
{
|
|
|
|
mProgress |= FLAG_HAS_TRANSPARENCY;
|
|
|
|
}
|
|
|
|
|
2010-08-23 06:30:46 +04:00
|
|
|
void
|
2016-07-20 02:22:34 +03:00
|
|
|
Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout)
|
2010-08-23 06:30:46 +04:00
|
|
|
{
|
2015-08-14 10:37:13 +03:00
|
|
|
mProgress |= FLAG_IS_ANIMATED;
|
|
|
|
mImageMetadata.SetHasAnimation();
|
|
|
|
mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
|
2010-08-23 06:30:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-09-09 08:07:15 +03:00
|
|
|
Decoder::PostFrameStop(Opacity aFrameOpacity
|
|
|
|
/* = Opacity::SOME_TRANSPARENCY */,
|
2015-02-11 03:47:00 +03:00
|
|
|
DisposalMethod aDisposalMethod
|
2015-09-09 08:07:15 +03:00
|
|
|
/* = DisposalMethod::KEEP */,
|
2016-07-20 03:14:30 +03:00
|
|
|
FrameTimeout aTimeout /* = FrameTimeout::Forever() */,
|
2016-06-25 01:20:32 +03:00
|
|
|
BlendMethod aBlendMethod /* = BlendMethod::OVER */,
|
|
|
|
const Maybe<nsIntRect>& aBlendRect /* = Nothing() */)
|
2010-08-23 06:30:46 +04:00
|
|
|
{
|
|
|
|
// We should be mid-frame
|
2015-07-23 08:39:54 +03:00
|
|
|
MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
|
2014-11-15 07:10:48 +03:00
|
|
|
MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
|
|
|
|
MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
|
2010-08-23 06:30:46 +04:00
|
|
|
|
2016-07-28 03:12:25 +03:00
|
|
|
// Update our state.
|
2010-08-23 06:30:46 +04:00
|
|
|
mInFrame = false;
|
2016-07-28 03:12:25 +03:00
|
|
|
mFinishedNewFrame = true;
|
2010-08-23 06:30:46 +04:00
|
|
|
|
2016-06-25 01:20:32 +03:00
|
|
|
mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout,
|
|
|
|
aBlendMethod, aBlendRect);
|
2012-12-20 00:11:42 +04:00
|
|
|
|
2015-06-20 01:55:12 +03:00
|
|
|
mProgress |= FLAG_FRAME_COMPLETE;
|
2015-01-12 09:29:32 +03:00
|
|
|
|
2016-07-20 03:14:30 +03:00
|
|
|
mLoopLength += aTimeout;
|
|
|
|
|
2015-01-12 09:29:32 +03:00
|
|
|
// If we're not sending partial invalidations, then we send an invalidation
|
|
|
|
// here when the first frame is complete.
|
2016-05-07 23:54:39 +03:00
|
|
|
if (!ShouldSendPartialInvalidations() && mFrameCount == 1) {
|
2015-01-19 01:02:13 +03:00
|
|
|
mInvalidRect.UnionRect(mInvalidRect,
|
2016-07-30 00:11:19 +03:00
|
|
|
IntRect(IntPoint(), Size()));
|
2015-01-12 09:29:32 +03:00
|
|
|
}
|
2010-08-23 06:30:46 +04:00
|
|
|
}
|
|
|
|
|
2010-08-25 00:40:45 +04:00
|
|
|
void
|
2015-01-19 01:02:13 +03:00
|
|
|
Decoder::PostInvalidation(const nsIntRect& aRect,
|
|
|
|
const Maybe<nsIntRect>& aRectAtTargetSize
|
|
|
|
/* = Nothing() */)
|
2010-08-25 00:40:45 +04:00
|
|
|
{
|
|
|
|
// We should be mid-frame
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!");
|
|
|
|
MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!");
|
2010-08-25 00:40:45 +04:00
|
|
|
|
2015-01-12 09:29:32 +03:00
|
|
|
// Record this invalidation, unless we're not sending partial invalidations
|
|
|
|
// or we're past the first frame.
|
2016-05-07 23:54:39 +03:00
|
|
|
if (ShouldSendPartialInvalidations() && mFrameCount == 1) {
|
2015-01-12 09:29:32 +03:00
|
|
|
mInvalidRect.UnionRect(mInvalidRect, aRect);
|
2015-01-19 01:02:13 +03:00
|
|
|
mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
|
2015-01-12 09:29:32 +03:00
|
|
|
}
|
2010-08-25 00:40:45 +04:00
|
|
|
}
|
|
|
|
|
2010-09-12 19:22:30 +04:00
|
|
|
void
|
2012-12-20 00:11:42 +04:00
|
|
|
Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
|
2010-09-12 19:22:30 +04:00
|
|
|
{
|
2015-07-23 08:39:54 +03:00
|
|
|
MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
|
|
|
|
MOZ_ASSERT(!mDecodeDone, "Decode already done!");
|
2010-09-12 19:22:30 +04:00
|
|
|
mDecodeDone = true;
|
|
|
|
|
2012-12-20 20:49:25 +04:00
|
|
|
mImageMetadata.SetLoopCount(aLoopCount);
|
2012-03-24 02:10:50 +04:00
|
|
|
|
2016-07-20 09:35:41 +03:00
|
|
|
// Some metadata that we track should take into account every frame in the
|
|
|
|
// image. If this is a first-frame-only decode, our accumulated loop length
|
|
|
|
// and first frame refresh area only includes the first frame, so it's not
|
|
|
|
// correct and we don't record it.
|
2016-07-20 03:14:30 +03:00
|
|
|
if (!IsFirstFrameDecode()) {
|
|
|
|
mImageMetadata.SetLoopLength(mLoopLength);
|
2016-07-20 09:35:41 +03:00
|
|
|
mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea);
|
2016-07-20 03:14:30 +03:00
|
|
|
}
|
|
|
|
|
2014-11-18 01:29:56 +03:00
|
|
|
mProgress |= FLAG_DECODE_COMPLETE;
|
2010-09-12 19:22:30 +04:00
|
|
|
}
|
|
|
|
|
2010-09-12 19:22:27 +04:00
|
|
|
void
|
2016-07-11 11:09:10 +03:00
|
|
|
Decoder::PostError()
|
2010-09-12 19:22:27 +04:00
|
|
|
{
|
2016-07-11 11:09:10 +03:00
|
|
|
mError = true;
|
2015-01-12 06:28:02 +03:00
|
|
|
|
|
|
|
if (mInFrame && mCurrentFrame) {
|
|
|
|
mCurrentFrame->Abort();
|
|
|
|
}
|
2010-09-12 19:22:27 +04:00
|
|
|
}
|
|
|
|
|
2015-02-03 18:05:49 +03:00
|
|
|
Telemetry::ID
|
|
|
|
Decoder::SpeedHistogram()
|
|
|
|
{
|
|
|
|
// Use HistogramCount as an invalid Histogram ID.
|
|
|
|
return Telemetry::HistogramCount;
|
|
|
|
}
|
|
|
|
|
2012-01-06 20:02:27 +04:00
|
|
|
} // namespace image
|
2010-08-23 06:30:45 +04:00
|
|
|
} // namespace mozilla
|