Bug 1901078 - Implement promise based anonymous image decoder. r=tnikkel

This bypasses any caching used on the display pipeline and is
intended to be used by layers that want asynchronous decoding
that fits well into the MozPromise style.

This also fixes an issue with the frame count metadata decoder in the
GIF decoder. The code only began being used with this patch, and the WPT
exposed an overflow bug caused by not clearing nsGIFDecoder2::mColormap
and nsGIFDecoder2::mColormapSize.

Differential Revision: https://phabricator.services.mozilla.com/D212833
This commit is contained in:
Andrew Osmond 2024-07-24 03:16:16 +00:00
parent 6055a0bae2
commit 874ce61a83
7 changed files with 752 additions and 19 deletions

View File

@ -4,7 +4,7 @@
* 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 "ImageUtils.h"
#include "mozilla/dom/ImageUtils.h"
#include "ImageContainer.h"
#include "Intervals.h"

View File

@ -319,6 +319,32 @@ already_AddRefed<Decoder> DecoderFactory::CloneAnimationDecoder(
return decoder.forget();
}
/* static */
already_AddRefed<Decoder> DecoderFactory::CloneAnonymousMetadataDecoder(
Decoder* aDecoder, const Maybe<DecoderFlags>& aDecoderFlags) {
MOZ_ASSERT(aDecoder);
DecoderType type = aDecoder->GetType();
RefPtr<Decoder> decoder =
GetDecoder(type, nullptr, /* aIsRedecode = */ false);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(true);
decoder->SetIterator(aDecoder->GetSourceBuffer()->Iterator());
if (aDecoderFlags) {
decoder->SetDecoderFlags(*aDecoderFlags);
} else {
decoder->SetDecoderFlags(aDecoder->GetDecoderFlags());
}
if (NS_FAILED(decoder->Init())) {
return nullptr;
}
return decoder.forget();
}
/* static */
already_AddRefed<IDecodingTask> DecoderFactory::CreateMetadataDecoder(
DecoderType aType, NotNull<RasterImage*> aImage, DecoderFlags aFlags,

View File

@ -12,6 +12,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/NotNull.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/image/ImageUtils.h"
#include "nsCOMPtr.h"
#include "Orientation.h"
#include "SurfaceFlags.h"
@ -25,24 +26,6 @@ class RasterImage;
class SourceBuffer;
class SourceBufferIterator;
/**
* The type of decoder; this is usually determined from a MIME type using
* DecoderFactory::GetDecoderType().
*/
enum class DecoderType {
PNG,
GIF,
JPEG,
BMP,
BMP_CLIPBOARD,
ICO,
ICON,
WEBP,
AVIF,
JXL,
UNKNOWN
};
class DecoderFactory {
public:
/// @return the type of decoder which is appropriate for @aMimeType.
@ -118,6 +101,15 @@ class DecoderFactory {
*/
static already_AddRefed<Decoder> CloneAnimationDecoder(Decoder* aDecoder);
/**
* Creates and initializes a metadata decoder for an anonymous image, cloned
* from the given decoder.
*
* @param aDecoder Decoder to clone.
*/
static already_AddRefed<Decoder> CloneAnonymousMetadataDecoder(
Decoder* aDecoder, const Maybe<DecoderFlags>& aDecoderFlags = Nothing());
/**
* Creates and initializes a metadata decoder of type @aType. This decoder
* will only decode the image's header, extracting metadata like the size of

567
image/ImageUtils.cpp Normal file
View File

@ -0,0 +1,567 @@
/* -*- 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 "mozilla/image/ImageUtils.h"
#include "DecodePool.h"
#include "Decoder.h"
#include "DecoderFactory.h"
#include "IDecodingTask.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/Logging.h"
#include "nsNetUtil.h"
#include "nsStreamUtils.h"
namespace mozilla::image {
static LazyLogModule sLog("ImageUtils");
AnonymousDecoder::AnonymousDecoder() = default;
AnonymousDecoder::~AnonymousDecoder() = default;
class AnonymousDecoderTask : public IDecodingTask {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousDecoderTask, final)
AnonymousDecoderTask(RefPtr<Decoder>&& aDecoder,
ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
: mDecoder(std::move(aDecoder)), mOwner(std::move(aOwner)) {}
bool ShouldPreferSyncRun() const final { return false; }
TaskPriority Priority() const final { return TaskPriority::eLow; }
bool IsValid() const {
return !AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal) &&
!mOwner.IsDead();
}
bool MaybeStart() {
if (!IsValid()) {
return false;
}
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderTask::Start -- queue", this));
DecodePool::Singleton()->AsyncRun(this);
return true;
}
void Resume() final {
if (!IsValid()) {
return;
}
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderTask::Resume -- queue", this));
DecodePool::Singleton()->AsyncRun(this);
}
void Run() final {
bool resume = true;
while (!mOwner.IsDead() && resume) {
LexerResult result = mDecoder->Decode(WrapNotNull(this));
if (result == LexerResult(Yield::NEED_MORE_DATA)) {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderTask::Run -- need more data", this));
MOZ_ASSERT(result == LexerResult(Yield::NEED_MORE_DATA));
OnNeedMoreData();
return;
}
// Check if we have a new frame to process.
RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
if (frame) {
RefPtr<gfx::SourceSurface> surface = frame->GetSourceSurface();
if (surface) {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderTask::Run -- new frame %p", this,
frame.get()));
resume = OnFrameAvailable(std::move(frame), std::move(surface));
} else {
MOZ_ASSERT_UNREACHABLE("No surface from frame?");
}
}
if (result.is<TerminalState>()) {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderTask::Run -- complete", this));
OnComplete(result == LexerResult(TerminalState::SUCCESS));
break;
}
MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
}
}
protected:
virtual ~AnonymousDecoderTask() = default;
virtual void OnNeedMoreData() {}
// Returns true if the caller should continue decoding more frames if
// possible.
virtual bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
RefPtr<gfx::SourceSurface>&& aSurface) {
MOZ_ASSERT_UNREACHABLE("Unhandled frame!");
return true;
}
virtual void OnComplete(bool aSuccess) = 0;
RefPtr<Decoder> mDecoder;
ThreadSafeWeakPtr<AnonymousDecoder> mOwner;
};
class AnonymousMetadataDecoderTask final : public AnonymousDecoderTask {
public:
AnonymousMetadataDecoderTask(RefPtr<Decoder>&& aDecoder,
ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
: AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
protected:
void OnComplete(bool aSuccess) override {
RefPtr<AnonymousDecoder> owner(mOwner);
if (!owner) {
return;
}
if (!aSuccess) {
owner->OnMetadata(nullptr);
return;
}
const auto& mdIn = mDecoder->GetImageMetadata();
owner->OnMetadata(&mdIn);
}
};
class AnonymousFrameCountDecoderTask final : public AnonymousDecoderTask {
public:
AnonymousFrameCountDecoderTask(RefPtr<Decoder>&& aDecoder,
ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
: AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
protected:
void UpdateFrameCount(bool aComplete) {
RefPtr<AnonymousDecoder> owner(mOwner);
if (!owner) {
return;
}
const auto& mdIn = mDecoder->GetImageMetadata();
uint32_t frameCount = mdIn.HasFrameCount() ? mdIn.GetFrameCount() : 0;
owner->OnFrameCount(frameCount, aComplete);
}
void OnNeedMoreData() override { UpdateFrameCount(/* aComplete */ false); }
void OnComplete(bool aSuccess) override {
UpdateFrameCount(/* aComplete */ true);
}
};
class AnonymousFramesDecoderTask final : public AnonymousDecoderTask {
public:
AnonymousFramesDecoderTask(RefPtr<Decoder>&& aDecoder,
ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
: AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
protected:
bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
RefPtr<gfx::SourceSurface>&& aSurface) override {
RefPtr<AnonymousDecoder> owner(mOwner);
if (!owner) {
return false;
}
return owner->OnFrameAvailable(std::move(aFrame), std::move(aSurface));
}
void OnComplete(bool aSuccess) override {
RefPtr<AnonymousDecoder> owner(mOwner);
if (!owner) {
return;
}
owner->OnFramesComplete();
}
};
class AnonymousDecoderImpl final : public AnonymousDecoder {
public:
AnonymousDecoderImpl()
: mMutex("mozilla::image::AnonymousDecoderImpl::mMutex") {}
~AnonymousDecoderImpl() override { Destroy(); }
#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
const char* typeName() const override {
return "mozilla::image::AnonymousDecoderImpl";
}
size_t typeSize() const override { return sizeof(*this); }
#endif
bool Initialize(RefPtr<Decoder>&& aDecoder) override {
MutexAutoLock lock(mMutex);
if (NS_WARN_IF(!aDecoder)) {
MOZ_LOG(sLog, LogLevel::Error,
("[%p] AnonymousDecoderImpl::Initialize -- bad decoder", this));
return false;
}
RefPtr<Decoder> metadataDecoder =
DecoderFactory::CloneAnonymousMetadataDecoder(aDecoder);
if (NS_WARN_IF(!metadataDecoder)) {
MOZ_LOG(sLog, LogLevel::Error,
("[%p] AnonymousDecoderImpl::Initialize -- failed clone metadata "
"decoder",
this));
return false;
}
DecoderFlags flags =
aDecoder->GetDecoderFlags() | DecoderFlags::COUNT_FRAMES;
RefPtr<Decoder> frameCountDecoder =
DecoderFactory::CloneAnonymousMetadataDecoder(aDecoder, Some(flags));
if (NS_WARN_IF(!frameCountDecoder)) {
MOZ_LOG(sLog, LogLevel::Error,
("[%p] AnonymousDecoderImpl::Initialize -- failed clone frame "
"count decoder",
this));
return false;
}
mMetadataTask = new AnonymousMetadataDecoderTask(
std::move(metadataDecoder), ThreadSafeWeakPtr<AnonymousDecoder>(this));
mFrameCountTask = new AnonymousFrameCountDecoderTask(
std::move(frameCountDecoder),
ThreadSafeWeakPtr<AnonymousDecoder>(this));
mFramesTask = new AnonymousFramesDecoderTask(
std::move(aDecoder), ThreadSafeWeakPtr<AnonymousDecoder>(this));
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::Initialize -- success", this));
return true;
}
void Destroy() override {
MutexAutoLock lock(mMutex);
DestroyLocked(NS_ERROR_ABORT);
}
void DestroyLocked(nsresult aResult) MOZ_REQUIRES(mMutex) {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::Destroy", this));
mFramesToDecode = 0;
mMetadataTask = nullptr;
mFrameCountTask = nullptr;
mFramesTask = nullptr;
mPendingFramesResult.mFrames.Clear();
mPendingFramesResult.mFinished = true;
mMetadataPromise.RejectIfExists(aResult, __func__);
mFrameCountPromise.RejectIfExists(aResult, __func__);
mFramesPromise.RejectIfExists(aResult, __func__);
}
void OnMetadata(const ImageMetadata* aMetadata) override {
MutexAutoLock lock(mMutex);
// We must have already gotten destroyed before metadata decoding finished.
if (!mMetadataTask) {
return;
}
if (!aMetadata) {
MOZ_LOG(sLog, LogLevel::Error,
("[%p] AnonymousDecoderImpl::OnMetadata -- failed", this));
DestroyLocked(NS_ERROR_FAILURE);
return;
}
const auto size = aMetadata->GetSize();
mMetadataResult.mWidth = size.width;
mMetadataResult.mHeight = size.height;
mMetadataResult.mRepetitions = aMetadata->GetLoopCount();
mMetadataResult.mAnimated = aMetadata->HasAnimation();
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::OnMetadata -- %dx%d, repetitions %d, "
"animated %d",
this, size.width, size.height, mMetadataResult.mRepetitions,
mMetadataResult.mAnimated));
if (!mMetadataResult.mAnimated) {
mMetadataResult.mFrameCount = 1;
mMetadataResult.mFrameCountComplete = true;
mMetadataTask = nullptr;
mFrameCountTask = nullptr;
} else if (mFrameCountTask && !mFrameCountTaskRunning) {
MOZ_LOG(
sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::OnMetadata -- start frame count task",
this));
mFrameCountTaskRunning = mFrameCountTask->MaybeStart();
return;
}
mMetadataPromise.Resolve(mMetadataResult, __func__);
if (mFramesTask && mFramesToDecode > 0 && !mFramesTaskRunning) {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::OnMetadata -- start frames task, "
"want %zu",
this, mFramesToDecode));
mFramesTaskRunning = mFramesTask->MaybeStart();
}
}
void OnFrameCount(uint32_t aFrameCount, bool aComplete) override {
MutexAutoLock lock(mMutex);
// We must have already gotten destroyed before frame count decoding
// finished.
if (!mFrameCountTask) {
return;
}
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::OnFrameCount -- frameCount %u, "
"complete %d",
this, aFrameCount, aComplete));
bool resolve = aComplete;
if (mFrameCount < aFrameCount) {
mFrameCount = aFrameCount;
resolve = true;
}
// If metadata completing is waiting on an updated frame count, resolve it.
mMetadataResult.mFrameCount = mFrameCount;
mMetadataResult.mFrameCountComplete = aComplete;
mMetadataPromise.ResolveIfExists(mMetadataResult, __func__);
if (mMetadataTask) {
mMetadataTask = nullptr;
if (mFramesTask && mFramesToDecode > 0 && !mFramesTaskRunning) {
MOZ_LOG(
sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::OnFrameCount -- start frames task, "
"want %zu",
this, mFramesToDecode));
mFramesTaskRunning = mFramesTask->MaybeStart();
}
}
if (resolve) {
mFrameCountPromise.ResolveIfExists(
DecodeFrameCountResult{aFrameCount, aComplete}, __func__);
}
if (aComplete) {
mFrameCountTask = nullptr;
}
}
bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
RefPtr<gfx::SourceSurface>&& aSurface) override {
MutexAutoLock lock(mMutex);
MOZ_DIAGNOSTIC_ASSERT(mFramesTaskRunning);
// We must have already gotten destroyed before frame decoding finished.
if (!mFramesTask) {
mFramesTaskRunning = false;
return false;
}
// Filter duplicate frames.
if (mLastFrame == aFrame) {
return true;
}
mPendingFramesResult.mFrames.AppendElement(
DecodedFrame{std::move(aSurface), mMetadataResult.mAnimated
? aFrame->GetTimeout()
: FrameTimeout::Forever()});
mLastFrame = std::move(aFrame);
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::OnFrameAvailable -- want %zu, got %zu",
this, mFramesToDecode, mPendingFramesResult.mFrames.Length()));
// Check if we have satisfied the number of requested frames.
if (mFramesToDecode > mPendingFramesResult.mFrames.Length()) {
return true;
}
mFramesToDecode = 0;
if (!mFramesPromise.IsEmpty()) {
mFramesPromise.Resolve(std::move(mPendingFramesResult), __func__);
}
mFramesTaskRunning = false;
return false;
}
void OnFramesComplete() override {
MutexAutoLock lock(mMutex);
// We must have already gotten destroyed before frame decoding finished.
if (!mFramesTask) {
return;
}
MOZ_LOG(
sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::OnFramesComplete -- wanted %zu, got %zu",
this, mFramesToDecode, mPendingFramesResult.mFrames.Length()));
mFramesToDecode = 0;
mPendingFramesResult.mFinished = true;
if (!mFramesPromise.IsEmpty()) {
mFramesPromise.Resolve(std::move(mPendingFramesResult), __func__);
}
mLastFrame = nullptr;
mFramesTask = nullptr;
}
RefPtr<DecodeMetadataPromise> DecodeMetadata() override {
MutexAutoLock lock(mMutex);
if (!mMetadataTask) {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::DecodeMetadata -- already complete",
this));
if (mMetadataResult.mWidth > 0 && mMetadataResult.mHeight > 0) {
return DecodeMetadataPromise::CreateAndResolve(mMetadataResult,
__func__);
}
return DecodeMetadataPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
if (!mMetadataTaskRunning) {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::DecodeMetadata -- queue", this));
mMetadataTaskRunning = mMetadataTask->MaybeStart();
}
return mMetadataPromise.Ensure(__func__);
}
RefPtr<DecodeFrameCountPromise> DecodeFrameCount(
uint32_t aKnownFrameCount) override {
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mFrameCountPromise.IsEmpty());
// If we have finished, or we have an updated frame count, return right
// away. This may drive the frame decoder for the application as the data
// comes in from the network.
if (!mFrameCountTask || aKnownFrameCount < mFrameCount) {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::DecodeFrameCount -- known %u, "
"detected %u, complete %d",
this, aKnownFrameCount, mFrameCount, !mFrameCountTask));
return DecodeFrameCountPromise::CreateAndResolve(
DecodeFrameCountResult{mFrameCount,
/* mFinished */ !mFrameCountTask},
__func__);
}
// mFrameCountTask is launching when metadata decoding is finished.
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::DecodeFrameCount -- waiting, known "
"%u, detected %u",
this, aKnownFrameCount, mFrameCount));
return mFrameCountPromise.Ensure(__func__);
}
RefPtr<DecodeFramesPromise> DecodeFrames(size_t aCount) override {
MutexAutoLock lock(mMutex);
// If we cleared our task reference, then we know we finished decoding.
if (!mFramesTask) {
mPendingFramesResult.mFinished = true;
return DecodeFramesPromise::CreateAndResolve(
std::move(mPendingFramesResult), __func__);
}
// If we are not waiting on any frames, then we know we paused decoding.
// If we still are metadata decoding, we need to wait.
if (mFramesToDecode == 0 && !mMetadataTask && !mFramesTaskRunning) {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::DecodeFrames -- queue", this));
mFramesTaskRunning = mFramesTask->MaybeStart();
}
mFramesToDecode = std::max(mFramesToDecode, aCount);
return mFramesPromise.Ensure(__func__);
}
void CancelDecodeFrames() override {
MutexAutoLock lock(mMutex);
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::CancelDecodeFrames", this));
mFramesToDecode = 0;
mFramesPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
}
private:
Mutex mMutex;
MozPromiseHolder<DecodeMetadataPromise> mMetadataPromise
MOZ_GUARDED_BY(mMutex);
MozPromiseHolder<DecodeFrameCountPromise> mFrameCountPromise
MOZ_GUARDED_BY(mMutex);
MozPromiseHolder<DecodeFramesPromise> mFramesPromise MOZ_GUARDED_BY(mMutex);
RefPtr<AnonymousFramesDecoderTask> mFramesTask MOZ_GUARDED_BY(mMutex);
RefPtr<AnonymousMetadataDecoderTask> mMetadataTask MOZ_GUARDED_BY(mMutex);
RefPtr<AnonymousFrameCountDecoderTask> mFrameCountTask MOZ_GUARDED_BY(mMutex);
RefPtr<imgFrame> mLastFrame MOZ_GUARDED_BY(mMutex);
DecodeMetadataResult mMetadataResult MOZ_GUARDED_BY(mMutex);
DecodeFramesResult mPendingFramesResult MOZ_GUARDED_BY(mMutex);
size_t mFramesToDecode MOZ_GUARDED_BY(mMutex) = 1;
uint32_t mFrameCount MOZ_GUARDED_BY(mMutex) = 0;
bool mMetadataTaskRunning MOZ_GUARDED_BY(mMutex) = false;
bool mFrameCountTaskRunning MOZ_GUARDED_BY(mMutex) = false;
bool mFramesTaskRunning MOZ_GUARDED_BY(mMutex) = false;
};
/* static */ already_AddRefed<AnonymousDecoder> ImageUtils::CreateDecoder(
SourceBuffer* aSourceBuffer, DecoderType aType,
SurfaceFlags aSurfaceFlags) {
if (NS_WARN_IF(!aSourceBuffer)) {
return nullptr;
}
if (NS_WARN_IF(aType == DecoderType::UNKNOWN)) {
return nullptr;
}
RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
aType, WrapNotNull(aSourceBuffer), Nothing(),
DecoderFlags::IMAGE_IS_TRANSIENT, aSurfaceFlags);
if (NS_WARN_IF(!decoder)) {
return nullptr;
}
auto anonymousDecoder = MakeRefPtr<AnonymousDecoderImpl>();
if (NS_WARN_IF(!anonymousDecoder->Initialize(std::move(decoder)))) {
return nullptr;
}
return anonymousDecoder.forget();
}
/* static */ DecoderType ImageUtils::GetDecoderType(
const nsACString& aMimeType) {
return DecoderFactory::GetDecoderType(aMimeType.Data());
}
} // namespace mozilla::image

141
image/ImageUtils.h Normal file
View File

@ -0,0 +1,141 @@
/* -*- 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/. */
#ifndef mozilla_image_ImageUtils_h
#define mozilla_image_ImageUtils_h
#include "FrameTimeout.h"
#include "mozilla/image/SurfaceFlags.h"
#include "mozilla/Assertions.h"
#include "mozilla/Maybe.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ThreadSafeWeakPtr.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
class ErrorResult;
namespace gfx {
class SourceSurface;
}
namespace image {
class Decoder;
class imgFrame;
class ImageMetadata;
class SourceBuffer;
/**
* The type of decoder; this is usually determined from a MIME type using
* DecoderFactory::GetDecoderType() or ImageUtils::GetDecoderType().
*/
enum class DecoderType {
PNG,
GIF,
JPEG,
BMP,
BMP_CLIPBOARD,
ICO,
ICON,
WEBP,
AVIF,
JXL,
UNKNOWN
};
struct DecodeMetadataResult {
int32_t mWidth = 0;
int32_t mHeight = 0;
int32_t mRepetitions = -1;
uint32_t mFrameCount = 0;
bool mAnimated = false;
bool mFrameCountComplete = true;
};
struct DecodeFrameCountResult {
uint32_t mFrameCount = 0;
bool mFinished = false;
};
struct DecodedFrame {
RefPtr<gfx::SourceSurface> mSurface;
FrameTimeout mTimeout;
};
struct DecodeFramesResult {
nsTArray<DecodedFrame> mFrames;
bool mFinished = false;
};
using DecodeMetadataPromise = MozPromise<DecodeMetadataResult, nsresult, true>;
using DecodeFrameCountPromise =
MozPromise<DecodeFrameCountResult, nsresult, true>;
using DecodeFramesPromise = MozPromise<DecodeFramesResult, nsresult, true>;
class AnonymousMetadataDecoderTask;
class AnonymousFrameCountDecoderTask;
class AnonymousFramesDecoderTask;
class AnonymousDecoder : public SupportsThreadSafeWeakPtr<AnonymousDecoder> {
public:
virtual RefPtr<DecodeMetadataPromise> DecodeMetadata() = 0;
virtual void Destroy() = 0;
virtual RefPtr<DecodeFrameCountPromise> DecodeFrameCount(
uint32_t aKnownFrameCount) = 0;
virtual RefPtr<DecodeFramesPromise> DecodeFrames(size_t aCount) = 0;
virtual void CancelDecodeFrames() = 0;
#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
virtual const char* typeName() const = 0;
virtual size_t typeSize() const = 0;
#endif
virtual ~AnonymousDecoder();
protected:
AnonymousDecoder();
// Returns true if successfully initialized else false.
virtual bool Initialize(RefPtr<Decoder>&& aDecoder) = 0;
virtual void OnMetadata(const ImageMetadata* aMetadata) = 0;
virtual void OnFrameCount(uint32_t aFrameCount, bool aComplete) = 0;
// Returns true if the caller should continue decoding more frames if
// possible.
virtual bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
RefPtr<gfx::SourceSurface>&& aSurface) = 0;
virtual void OnFramesComplete() = 0;
friend class AnonymousMetadataDecoderTask;
friend class AnonymousFrameCountDecoderTask;
friend class AnonymousFramesDecoderTask;
};
class ImageUtils {
public:
static already_AddRefed<AnonymousDecoder> CreateDecoder(
SourceBuffer* aSourceBuffer, DecoderType aType,
SurfaceFlags aSurfaceFlags);
static DecoderType GetDecoderType(const nsACString& aMimeType);
private:
ImageUtils() = delete;
~ImageUtils() = delete;
};
} // namespace image
} // namespace mozilla
#endif // mozilla_image_ImageUtils_h

View File

@ -269,6 +269,9 @@ void nsGIFDecoder2::EndImageFrame() {
if (WantsFrameCount()) {
mGIFStruct.pixels_remaining = 0;
mGIFStruct.images_decoded++;
mGIFStruct.delay_time = 0;
mColormap = nullptr;
mColormapSize = 0;
mCurrentFrameIndex = -1;
// Keep updating the count every time we find a frame.

View File

@ -64,7 +64,10 @@ EXPORTS.mozilla.image += [
"encoders/png/nsPNGEncoder.h",
"ICOFileHeaders.h",
"ImageMemoryReporter.h",
"ImageUtils.h",
"Resolution.h",
"SourceBuffer.h",
"SurfaceFlags.h",
"WebRenderImageProvider.h",
]
@ -86,6 +89,7 @@ UNIFIED_SOURCES += [
"ImageFactory.cpp",
"ImageMemoryReporter.cpp",
"ImageOps.cpp",
"ImageUtils.cpp",
"ImageWrapper.cpp",
"imgFrame.cpp",
"imgLoader.cpp",