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
|
|
|
|
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"
|
2017-10-25 01:22:55 +03:00
|
|
|
#include "mozilla/gfx/2D.h"
|
|
|
|
#include "mozilla/gfx/Point.h"
|
|
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
#include "nsComponentManagerUtils.h"
|
2015-01-16 02:11:36 +03:00
|
|
|
#include "nsProxyRelease.h"
|
2013-09-07 17:01:08 +04:00
|
|
|
#include "nsServiceManagerUtils.h"
|
2010-08-23 06:30:45 +04:00
|
|
|
|
2017-10-25 01:22:55 +03:00
|
|
|
using mozilla::gfx::IntPoint;
|
|
|
|
using mozilla::gfx::IntRect;
|
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) : mDecoder(aDecoder) {
|
2016-07-11 10:03:42 +03:00
|
|
|
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),
|
2018-06-04 02:42:24 +03:00
|
|
|
mFrameRecycler(nullptr),
|
2015-07-31 17:29:00 +03:00
|
|
|
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),
|
2016-08-05 14:17:58 +03:00
|
|
|
mHaveExplicitOutputSize(false),
|
2015-07-31 17:29:00 +03:00
|
|
|
mInFrame(false),
|
2016-07-28 03:12:25 +03:00
|
|
|
mFinishedNewFrame(false),
|
2018-03-09 02:33:04 +03:00
|
|
|
mHasFrameToTake(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-27 09:53:20 +03:00
|
|
|
mShouldReportError(false),
|
2017-02-08 23:48:59 +03:00
|
|
|
mFinalizeFrames(true) {}
|
2018-11-30 13:46:48 +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.
|
2017-01-31 22:34:01 +03:00
|
|
|
NS_ReleaseOnMainThreadSystemGroup(mImage.forget());
|
2015-01-16 02:11:35 +03:00
|
|
|
}
|
2010-08-23 06:30:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Common implementation of the decoder interface.
|
|
|
|
*/
|
|
|
|
|
2011-09-27 20:24:03 +04:00
|
|
|
nsresult Decoder::Init() {
|
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);
|
|
|
|
|
2016-08-05 14:17:58 +03:00
|
|
|
// Metadata decoders must not set an output size.
|
|
|
|
MOZ_ASSERT_IF(mMetadataDecode, !mHaveExplicitOutputSize);
|
|
|
|
|
2016-08-18 10:06:41 +03:00
|
|
|
// All decoders must be anonymous except for metadata decoders.
|
|
|
|
// XXX(seth): Soon that exception will be removed.
|
|
|
|
MOZ_ASSERT_IF(mImage, IsMetadataDecode());
|
2015-08-01 04:10:31 +03:00
|
|
|
|
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-15 08:39:39 +03:00
|
|
|
LexerResult 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
|
|
|
{
|
Bug 1375392 - Tweak the PROFILER_LABEL* macros. r=mstange.
This patch makes the following changes to the macros.
- Removes PROFILER_LABEL_FUNC. It's only suitable for use in functions outside
classes, due to PROFILER_FUNCTION_NAME not getting class names, and it was
mostly misused.
- Removes PROFILER_FUNCTION_NAME. It's no longer used, and __func__ is
universally available now anyway.
- Combines the first two string literal arguments of PROFILER_LABEL and
PROFILER_LABEL_DYNAMIC into a single argument. There was no good reason for
them to be separate, and it forced a '::' in the label, which isn't always
appropriate. Also, the meaning of the "name_space" argument was interpreted
in an interesting variety of ways.
- Adds an "AUTO_" prefix to PROFILER_LABEL and PROFILER_LABEL_DYNAMIC, to make
it clearer they construct RAII objects rather than just being function calls.
(I myself have screwed up the scoping because of this in the past.)
- Fills in the 'js::ProfileEntry::Category::' qualifier within the macro, so
the caller doesn't need to. This makes a *lot* more of the uses fit onto a
single line.
The patch also makes the following changes to the macro uses (beyond those
required by the changes described above).
- Fixes a bunch of labels that had gotten out of sync with the name of the
class and/or function that encloses them.
- Removes a useless PROFILER_LABEL use within a trivial scope in
EventStateManager::DispatchMouseOrPointerEvent(). It clearly wasn't serving
any useful purpose. It also serves as extra evidence that the AUTO_ prefix is
a good idea.
- Tweaks DecodePool::SyncRunIf{Preferred,Possible} so that the labelling is
done within them, instead of at their callsites, because that's a more
standard way of doing things.
--HG--
extra : rebase_source : 318d1bc6fc1425a94aacbf489dd46e4f83211de4
2017-06-22 10:08:53 +03:00
|
|
|
AUTO_PROFILER_LABEL("Decoder::Decode", 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
|
|
|
}
|
|
|
|
|
2016-08-05 04:59:10 +03:00
|
|
|
LexerResult Decoder::TerminateFailure() {
|
|
|
|
PostError();
|
|
|
|
|
|
|
|
// Perform final cleanup if need be.
|
|
|
|
if (!mReachedTerminalState) {
|
|
|
|
mReachedTerminalState = true;
|
|
|
|
CompleteDecode();
|
|
|
|
}
|
|
|
|
|
|
|
|
return LexerResult(TerminalState::FAILURE);
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-01-27 09:53:20 +03:00
|
|
|
void Decoder::CompleteDecode() {
|
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
|
|
|
}
|
|
|
|
|
2016-09-26 21:18:37 +03:00
|
|
|
if (IsMetadataDecode()) {
|
|
|
|
// If this was a metadata decode and we never got a size, the decode failed.
|
|
|
|
if (!HasSize()) {
|
|
|
|
PostError();
|
|
|
|
}
|
|
|
|
return;
|
2015-02-11 03:47:00 +03:00
|
|
|
}
|
2010-09-12 19:22:30 +04:00
|
|
|
|
2016-09-26 21:18:37 +03:00
|
|
|
// If the implementation left us mid-frame, finish that up. Note that it may
|
|
|
|
// have left us transparent.
|
|
|
|
if (mInFrame) {
|
|
|
|
PostHasTransparency();
|
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
|
|
|
|
2016-09-26 21:18:37 +03:00
|
|
|
// If PostDecodeDone() has not been called, we may need to send teardown
|
|
|
|
// notifications if it is unrecoverable.
|
|
|
|
if (!mDecodeDone) {
|
|
|
|
// We should always report an error to the console in this case.
|
2015-01-27 09:53:20 +03:00
|
|
|
mShouldReportError = true;
|
|
|
|
|
2016-07-11 10:34:50 +03:00
|
|
|
if (GetCompleteFrameCount() > 0) {
|
2016-09-26 21:18:37 +03:00
|
|
|
// We're usable if we have at least one complete frame, so do exactly
|
|
|
|
// what we should have when the decoder completed.
|
2015-02-06 01:42:38 +03:00
|
|
|
PostHasTransparency();
|
2015-01-27 09:53:20 +03:00
|
|
|
PostDecodeDone();
|
|
|
|
} else {
|
|
|
|
// We're not usable. Record some final progress indicating the error.
|
2016-09-21 14:13:08 +03:00
|
|
|
mProgress |= FLAG_DECODE_COMPLETE | FLAG_HAS_ERROR;
|
2015-01-27 09:53:20 +03:00
|
|
|
}
|
|
|
|
}
|
2015-07-31 17:29:10 +03:00
|
|
|
|
2016-09-26 21:18:37 +03:00
|
|
|
if (mDecodeDone) {
|
2015-07-31 17:29:10 +03:00
|
|
|
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
|
|
|
|
2016-08-05 14:17:58 +03:00
|
|
|
void Decoder::SetOutputSize(const gfx::IntSize& aSize) {
|
|
|
|
mOutputSize = Some(aSize);
|
|
|
|
mHaveExplicitOutputSize = true;
|
2015-09-20 02:21:08 +03:00
|
|
|
}
|
|
|
|
|
2016-08-05 14:17:58 +03:00
|
|
|
Maybe<gfx::IntSize> Decoder::ExplicitOutputSize() const {
|
|
|
|
MOZ_ASSERT_IF(mHaveExplicitOutputSize, mOutputSize);
|
|
|
|
return mHaveExplicitOutputSize ? mOutputSize : Nothing();
|
2016-07-03 06:20:55 +03:00
|
|
|
}
|
|
|
|
|
2016-07-28 03:12:25 +03:00
|
|
|
Maybe<uint32_t> Decoder::TakeCompleteFrameCount() {
|
|
|
|
const bool finishedNewFrame = mFinishedNewFrame;
|
|
|
|
mFinishedNewFrame = false;
|
|
|
|
return finishedNewFrame ? Some(GetCompleteFrameCount()) : Nothing();
|
|
|
|
}
|
|
|
|
|
2016-08-03 03:12:36 +03:00
|
|
|
DecoderFinalStatus Decoder::FinalStatus() const {
|
|
|
|
return DecoderFinalStatus(IsMetadataDecode(), GetDecodeDone(), HasError(),
|
|
|
|
ShouldReportError());
|
|
|
|
}
|
|
|
|
|
2016-08-03 02:45:22 +03:00
|
|
|
DecoderTelemetry Decoder::Telemetry() const {
|
2016-08-03 02:34:23 +03:00
|
|
|
MOZ_ASSERT(mIterator);
|
|
|
|
return DecoderTelemetry(SpeedHistogram(),
|
2018-07-04 15:50:02 +03:00
|
|
|
mIterator ? mIterator->ByteCount() : 0,
|
|
|
|
mIterator ? mIterator->ChunkCount() : 0, mDecodeTime);
|
2016-08-03 02:34:23 +03:00
|
|
|
}
|
|
|
|
|
2018-05-29 15:36:12 +03:00
|
|
|
nsresult Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
|
2016-08-05 14:17:58 +03:00
|
|
|
const gfx::IntRect& aFrameRect,
|
2015-07-11 05:26:15 +03:00
|
|
|
gfx::SurfaceFormat aFormat,
|
2018-05-29 15:36:12 +03:00
|
|
|
uint8_t aPaletteDepth,
|
|
|
|
const Maybe<AnimationParams>& aAnimParams) {
|
|
|
|
mCurrentFrame =
|
|
|
|
AllocateFrameInternal(aOutputSize, aFrameRect, aFormat, aPaletteDepth,
|
2018-09-17 22:06:28 +03:00
|
|
|
aAnimParams, std::move(mCurrentFrame));
|
2013-06-08 00:42:57 +04:00
|
|
|
|
2014-11-27 00:22:10 +03:00
|
|
|
if (mCurrentFrame) {
|
2018-03-09 02:33:04 +03:00
|
|
|
mHasFrameToTake = true;
|
|
|
|
|
2014-11-27 00:22:10 +03:00
|
|
|
// 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.)
|
2018-05-29 15:36:12 +03:00
|
|
|
MOZ_ASSERT_IF(aAnimParams, aAnimParams->mFrameNum + 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
|
|
|
}
|
|
|
|
|
2018-05-29 15:36:12 +03:00
|
|
|
RawAccessFrameRef Decoder::AllocateFrameInternal(
|
|
|
|
const gfx::IntSize& aOutputSize, const gfx::IntRect& aFrameRect,
|
2015-07-11 05:26:15 +03:00
|
|
|
SurfaceFormat aFormat, uint8_t aPaletteDepth,
|
2018-05-29 15:36:12 +03:00
|
|
|
const Maybe<AnimationParams>& aAnimParams,
|
2018-09-17 22:06:28 +03:00
|
|
|
RawAccessFrameRef&& aPreviousFrame) {
|
2016-07-11 10:34:50 +03:00
|
|
|
if (HasError()) {
|
2015-01-08 11:01:25 +03:00
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
2018-05-29 15:36:12 +03:00
|
|
|
uint32_t frameNum = aAnimParams ? aAnimParams->mFrameNum : 0;
|
|
|
|
if (frameNum != mFrameCount) {
|
2015-07-11 05:26:15 +03:00
|
|
|
MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
|
2015-01-08 11:01:25 +03:00
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
2016-08-05 14:17:58 +03:00
|
|
|
if (aOutputSize.width <= 0 || aOutputSize.height <= 0 ||
|
2017-08-14 15:29:56 +03:00
|
|
|
aFrameRect.Width() <= 0 || aFrameRect.Height() <= 0) {
|
2015-01-08 11:01:25 +03:00
|
|
|
NS_WARNING("Trying to add frame with zero or negative size");
|
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
2018-05-29 15:36:12 +03:00
|
|
|
if (frameNum == 1) {
|
2015-01-08 11:01:25 +03:00
|
|
|
MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
|
2018-05-31 02:35:40 +03:00
|
|
|
aPreviousFrame->SetRawAccessOnly();
|
2015-01-08 11:01:25 +03:00
|
|
|
}
|
|
|
|
|
2018-05-29 15:36:12 +03:00
|
|
|
if (frameNum > 0) {
|
2018-09-17 22:06:28 +03:00
|
|
|
if (ShouldBlendAnimation()) {
|
|
|
|
if (aPreviousFrame->GetDisposalMethod() !=
|
|
|
|
DisposalMethod::RESTORE_PREVIOUS) {
|
|
|
|
// If the new restore frame is the direct previous frame, then we know
|
|
|
|
// the dirty rect is composed only of the current frame's blend rect and
|
|
|
|
// the restore frame's clear rect (if applicable) which are handled in
|
|
|
|
// filters.
|
|
|
|
mRestoreFrame = std::move(aPreviousFrame);
|
|
|
|
mRestoreDirtyRect.SetBox(0, 0, 0, 0);
|
|
|
|
} else {
|
|
|
|
// We only need the previous frame's dirty rect, because while there may
|
|
|
|
// have been several frames between us and mRestoreFrame, the only areas
|
|
|
|
// that changed are the restore frame's clear rect, the current frame
|
|
|
|
// blending rect, and the previous frame's blending rect. All else is
|
|
|
|
// forgotten due to us restoring the same frame again.
|
|
|
|
mRestoreDirtyRect = aPreviousFrame->GetBoundedBlendRect();
|
|
|
|
}
|
|
|
|
}
|
2015-01-08 11:01:25 +03:00
|
|
|
}
|
|
|
|
|
2018-06-04 02:42:24 +03:00
|
|
|
RawAccessFrameRef ref;
|
|
|
|
|
|
|
|
// If we have a frame recycler, it must be for an animated image producing
|
|
|
|
// full frames. If the higher layers are discarding frames because of the
|
|
|
|
// memory footprint, then the recycler will allow us to reuse the buffers.
|
|
|
|
// Each frame should be the same size and have mostly the same properties.
|
|
|
|
if (mFrameRecycler) {
|
|
|
|
MOZ_ASSERT(ShouldBlendAnimation());
|
|
|
|
MOZ_ASSERT(aPaletteDepth == 0);
|
|
|
|
MOZ_ASSERT(aAnimParams);
|
|
|
|
MOZ_ASSERT(aFrameRect.IsEqualEdges(IntRect(IntPoint(0, 0), aOutputSize)));
|
|
|
|
|
|
|
|
ref = mFrameRecycler->RecycleFrame(mRecycleRect);
|
|
|
|
if (ref) {
|
|
|
|
// If the recycled frame is actually the current restore frame, we cannot
|
|
|
|
// use it. If the next restore frame is the new frame we are creating, in
|
|
|
|
// theory we could reuse it, but we would need to store the restore frame
|
|
|
|
// animation parameters elsewhere. For now we just drop it.
|
|
|
|
bool blocked = ref.get() == mRestoreFrame.get();
|
|
|
|
if (!blocked) {
|
2019-01-10 15:42:12 +03:00
|
|
|
blocked = NS_FAILED(ref->InitForDecoderRecycle(aAnimParams.ref()));
|
2018-06-04 02:42:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (blocked) {
|
|
|
|
ref.reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Either the recycler had nothing to give us, or we don't have a recycler.
|
|
|
|
// Produce a new frame to store the data.
|
|
|
|
if (!ref) {
|
|
|
|
// There is no underlying data to reuse, so reset the recycle rect to be
|
|
|
|
// the full frame, to ensure the restore frame is fully copied.
|
|
|
|
mRecycleRect = IntRect(IntPoint(0, 0), aOutputSize);
|
|
|
|
|
|
|
|
bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
|
|
|
|
auto frame = MakeNotNull<RefPtr<imgFrame>>();
|
|
|
|
if (NS_FAILED(frame->InitForDecoder(
|
|
|
|
aOutputSize, aFrameRect, aFormat, aPaletteDepth, nonPremult,
|
|
|
|
aAnimParams, ShouldBlendAnimation(), bool(mFrameRecycler)))) {
|
|
|
|
NS_WARNING("imgFrame::Init should succeed");
|
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
ref = frame->RawAccessRef();
|
|
|
|
if (!ref) {
|
|
|
|
frame->Abort();
|
|
|
|
return RawAccessFrameRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frameNum > 0) {
|
|
|
|
frame->SetRawAccessOnly();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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; }
|
2017-02-10 16:33:11 +03:00
|
|
|
|
|
|
|
nsresult Decoder::FinishWithErrorInternal() {
|
|
|
|
MOZ_ASSERT(!mInFrame);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2010-08-23 06:30:45 +04:00
|
|
|
|
2010-08-23 06:30:46 +04:00
|
|
|
/*
|
|
|
|
* Progress Notifications
|
|
|
|
*/
|
|
|
|
|
2013-08-25 11:19:42 +04:00
|
|
|
void Decoder::PostSize(int32_t aWidth, int32_t aHeight,
|
|
|
|
Orientation aOrientation /* = Orientation()*/) {
|
2016-08-05 14:17:58 +03: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
|
|
|
|
2016-08-05 14:17:58 +03:00
|
|
|
// Set our intrinsic size.
|
2013-08-25 11:19:42 +04:00
|
|
|
mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
|
2010-08-23 06:30:46 +04:00
|
|
|
|
2017-07-22 14:50:31 +03:00
|
|
|
// Verify it is the expected size, if given. Note that this is only used by
|
|
|
|
// the ICO decoder for embedded image types, so only its subdecoders are
|
|
|
|
// required to handle failures in PostSize.
|
|
|
|
if (!IsExpectedSize()) {
|
|
|
|
PostError();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-05 14:17:58 +03:00
|
|
|
// Set our output size if it's not already set.
|
|
|
|
if (!mOutputSize) {
|
|
|
|
mOutputSize = Some(IntSize(aWidth, aHeight));
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(mOutputSize->width <= aWidth && mOutputSize->height <= aHeight,
|
|
|
|
"Output size will result in upscaling");
|
|
|
|
|
|
|
|
// Create a downscaler if we need to downscale. This is used by legacy
|
|
|
|
// decoders that haven't been converted to use SurfacePipe yet.
|
|
|
|
// XXX(seth): Obviously, we'll remove this once all decoders use SurfacePipe.
|
|
|
|
if (mOutputSize->width < aWidth || mOutputSize->height < aHeight) {
|
|
|
|
mDownscaler.emplace(*mOutputSize);
|
|
|
|
}
|
|
|
|
|
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; }
|
|
|
|
|
2016-07-20 02:22:34 +03:00
|
|
|
void Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout) {
|
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
|
|
|
}
|
|
|
|
|
2018-05-29 15:36:12 +03:00
|
|
|
void Decoder::PostFrameStop(Opacity aFrameOpacity) {
|
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
|
|
|
|
2018-05-29 15:36:12 +03:00
|
|
|
mCurrentFrame->Finish(aFrameOpacity, mFinalizeFrames);
|
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
|
|
|
|
2018-05-29 15:36:12 +03:00
|
|
|
mLoopLength += mCurrentFrame->GetTimeout();
|
2016-07-20 03:14:30 +03:00
|
|
|
|
2018-06-04 02:21:48 +03:00
|
|
|
if (mFrameCount == 1) {
|
|
|
|
// If we're not sending partial invalidations, then we send an invalidation
|
|
|
|
// here when the first frame is complete.
|
|
|
|
if (!ShouldSendPartialInvalidations()) {
|
|
|
|
mInvalidRect.UnionRect(mInvalidRect, IntRect(IntPoint(), Size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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).
|
|
|
|
switch (mCurrentFrame->GetDisposalMethod()) {
|
|
|
|
default:
|
|
|
|
MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
|
|
|
|
case DisposalMethod::CLEAR:
|
|
|
|
case DisposalMethod::CLEAR_ALL:
|
|
|
|
case DisposalMethod::RESTORE_PREVIOUS:
|
|
|
|
mFirstFrameRefreshArea = IntRect(IntPoint(), Size());
|
|
|
|
break;
|
|
|
|
case DisposalMethod::KEEP:
|
|
|
|
case DisposalMethod::NOT_SPECIFIED:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// 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.
|
|
|
|
mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea,
|
|
|
|
mCurrentFrame->GetBoundedBlendRect());
|
2015-01-12 09:29:32 +03:00
|
|
|
}
|
2010-08-23 06:30:46 +04:00
|
|
|
}
|
|
|
|
|
2016-08-05 14:17:58 +03:00
|
|
|
void Decoder::PostInvalidation(const gfx::IntRect& aRect,
|
|
|
|
const Maybe<gfx::IntRect>& aRectAtOutputSize
|
2015-01-19 01:02:13 +03:00
|
|
|
/* = 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);
|
2016-08-05 14:17:58 +03:00
|
|
|
mCurrentFrame->ImageUpdated(aRectAtOutputSize.valueOr(aRect));
|
2015-01-12 09:29:32 +03:00
|
|
|
}
|
2010-08-25 00:40:45 +04:00
|
|
|
}
|
|
|
|
|
2012-12-20 00:11:42 +04:00
|
|
|
void Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */) {
|
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
|
|
|
}
|
|
|
|
|
2016-07-11 11:09:10 +03:00
|
|
|
void Decoder::PostError() {
|
|
|
|
mError = true;
|
2015-01-12 06:28:02 +03:00
|
|
|
|
2016-09-26 21:18:37 +03:00
|
|
|
if (mInFrame) {
|
|
|
|
MOZ_ASSERT(mCurrentFrame);
|
|
|
|
MOZ_ASSERT(mFrameCount > 0);
|
2015-01-12 06:28:02 +03:00
|
|
|
mCurrentFrame->Abort();
|
2016-09-26 21:18:37 +03:00
|
|
|
mInFrame = false;
|
|
|
|
--mFrameCount;
|
2018-03-09 02:33:04 +03:00
|
|
|
mHasFrameToTake = false;
|
2015-01-12 06:28:02 +03:00
|
|
|
}
|
2010-09-12 19:22:27 +04:00
|
|
|
}
|
|
|
|
|
2012-01-06 20:02:27 +04:00
|
|
|
} // namespace image
|
2010-08-23 06:30:45 +04:00
|
|
|
} // namespace mozilla
|