1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/image/ImageUtils.h"
7 #include "DecodePool.h"
9 #include "DecoderFactory.h"
10 #include "IDecodingTask.h"
11 #include "mozilla/AppShutdown.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/gfx/2D.h"
14 #include "mozilla/Logging.h"
15 #include "nsNetUtil.h"
16 #include "nsStreamUtils.h"
18 namespace mozilla::image
{
20 static LazyLogModule
sLog("ImageUtils");
22 AnonymousDecoder::AnonymousDecoder() = default;
24 AnonymousDecoder::~AnonymousDecoder() = default;
26 class AnonymousDecoderTask
: public IDecodingTask
{
28 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousDecoderTask
, final
)
30 AnonymousDecoderTask(RefPtr
<Decoder
>&& aDecoder
,
31 ThreadSafeWeakPtr
<AnonymousDecoder
>&& aOwner
)
32 : mDecoder(std::move(aDecoder
)), mOwner(std::move(aOwner
)) {}
34 bool ShouldPreferSyncRun() const final
{ return false; }
36 TaskPriority
Priority() const final
{ return TaskPriority::eLow
; }
38 bool IsValid() const {
39 return !AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal
) &&
48 MOZ_LOG(sLog
, LogLevel::Debug
,
49 ("[%p] AnonymousDecoderTask::Start -- queue", this));
50 DecodePool::Singleton()->AsyncRun(this);
59 MOZ_LOG(sLog
, LogLevel::Debug
,
60 ("[%p] AnonymousDecoderTask::Resume -- queue", this));
61 DecodePool::Singleton()->AsyncRun(this);
66 while (!mOwner
.IsDead() && resume
) {
67 LexerResult result
= mDecoder
->Decode(WrapNotNull(this));
68 if (result
== LexerResult(Yield::NEED_MORE_DATA
)) {
69 MOZ_LOG(sLog
, LogLevel::Debug
,
70 ("[%p] AnonymousDecoderTask::Run -- need more data", this));
71 MOZ_ASSERT(result
== LexerResult(Yield::NEED_MORE_DATA
));
76 // Check if we have a new frame to process.
77 RefPtr
<imgFrame
> frame
= mDecoder
->GetCurrentFrame();
79 RefPtr
<gfx::SourceSurface
> surface
= frame
->GetSourceSurface();
81 MOZ_LOG(sLog
, LogLevel::Debug
,
82 ("[%p] AnonymousDecoderTask::Run -- new frame %p", this,
84 resume
= OnFrameAvailable(std::move(frame
), std::move(surface
));
86 MOZ_ASSERT_UNREACHABLE("No surface from frame?");
90 if (result
.is
<TerminalState
>()) {
91 MOZ_LOG(sLog
, LogLevel::Debug
,
92 ("[%p] AnonymousDecoderTask::Run -- complete", this));
93 OnComplete(result
== LexerResult(TerminalState::SUCCESS
));
97 MOZ_ASSERT(result
== LexerResult(Yield::OUTPUT_AVAILABLE
));
102 virtual ~AnonymousDecoderTask() = default;
104 virtual void OnNeedMoreData() {}
106 // Returns true if the caller should continue decoding more frames if
108 virtual bool OnFrameAvailable(RefPtr
<imgFrame
>&& aFrame
,
109 RefPtr
<gfx::SourceSurface
>&& aSurface
) {
110 MOZ_ASSERT_UNREACHABLE("Unhandled frame!");
114 virtual void OnComplete(bool aSuccess
) = 0;
116 RefPtr
<Decoder
> mDecoder
;
117 ThreadSafeWeakPtr
<AnonymousDecoder
> mOwner
;
120 class AnonymousMetadataDecoderTask final
: public AnonymousDecoderTask
{
122 AnonymousMetadataDecoderTask(RefPtr
<Decoder
>&& aDecoder
,
123 ThreadSafeWeakPtr
<AnonymousDecoder
>&& aOwner
)
124 : AnonymousDecoderTask(std::move(aDecoder
), std::move(aOwner
)) {}
127 void OnComplete(bool aSuccess
) override
{
128 RefPtr
<AnonymousDecoder
> owner(mOwner
);
134 owner
->OnMetadata(nullptr);
138 const auto& mdIn
= mDecoder
->GetImageMetadata();
139 owner
->OnMetadata(&mdIn
);
143 class AnonymousFrameCountDecoderTask final
: public AnonymousDecoderTask
{
145 AnonymousFrameCountDecoderTask(RefPtr
<Decoder
>&& aDecoder
,
146 ThreadSafeWeakPtr
<AnonymousDecoder
>&& aOwner
)
147 : AnonymousDecoderTask(std::move(aDecoder
), std::move(aOwner
)) {}
150 void UpdateFrameCount(bool aComplete
) {
151 RefPtr
<AnonymousDecoder
> owner(mOwner
);
156 const auto& mdIn
= mDecoder
->GetImageMetadata();
157 uint32_t frameCount
= mdIn
.HasFrameCount() ? mdIn
.GetFrameCount() : 0;
158 owner
->OnFrameCount(frameCount
, aComplete
);
161 void OnNeedMoreData() override
{ UpdateFrameCount(/* aComplete */ false); }
163 void OnComplete(bool aSuccess
) override
{
164 UpdateFrameCount(/* aComplete */ true);
168 class AnonymousFramesDecoderTask final
: public AnonymousDecoderTask
{
170 AnonymousFramesDecoderTask(RefPtr
<Decoder
>&& aDecoder
,
171 ThreadSafeWeakPtr
<AnonymousDecoder
>&& aOwner
)
172 : AnonymousDecoderTask(std::move(aDecoder
), std::move(aOwner
)) {}
174 void SetOutputSize(const OrientedIntSize
& aSize
) {
176 mDecoder
->SetOutputSize(aSize
);
181 bool OnFrameAvailable(RefPtr
<imgFrame
>&& aFrame
,
182 RefPtr
<gfx::SourceSurface
>&& aSurface
) override
{
183 RefPtr
<AnonymousDecoder
> owner(mOwner
);
188 return owner
->OnFrameAvailable(std::move(aFrame
), std::move(aSurface
));
191 void OnComplete(bool aSuccess
) override
{
192 RefPtr
<AnonymousDecoder
> owner(mOwner
);
197 owner
->OnFramesComplete();
201 class AnonymousDecoderImpl final
: public AnonymousDecoder
{
203 explicit AnonymousDecoderImpl(const Maybe
<gfx::IntSize
>& aOutputSize
)
204 : mMutex("mozilla::image::AnonymousDecoderImpl::mMutex"),
205 mOutputSize(aOutputSize
) {}
207 ~AnonymousDecoderImpl() override
{ Destroy(); }
209 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
210 const char* typeName() const override
{
211 return "mozilla::image::AnonymousDecoderImpl";
214 size_t typeSize() const override
{ return sizeof(*this); }
217 bool Initialize(RefPtr
<Decoder
>&& aDecoder
) override
{
218 MutexAutoLock
lock(mMutex
);
220 if (NS_WARN_IF(!aDecoder
)) {
221 MOZ_LOG(sLog
, LogLevel::Error
,
222 ("[%p] AnonymousDecoderImpl::Initialize -- bad decoder", this));
226 RefPtr
<Decoder
> metadataDecoder
=
227 DecoderFactory::CloneAnonymousMetadataDecoder(aDecoder
);
228 if (NS_WARN_IF(!metadataDecoder
)) {
229 MOZ_LOG(sLog
, LogLevel::Error
,
230 ("[%p] AnonymousDecoderImpl::Initialize -- failed clone metadata "
237 aDecoder
->GetDecoderFlags() | DecoderFlags::COUNT_FRAMES
;
238 RefPtr
<Decoder
> frameCountDecoder
=
239 DecoderFactory::CloneAnonymousMetadataDecoder(aDecoder
, Some(flags
));
240 if (NS_WARN_IF(!frameCountDecoder
)) {
241 MOZ_LOG(sLog
, LogLevel::Error
,
242 ("[%p] AnonymousDecoderImpl::Initialize -- failed clone frame "
248 mMetadataTask
= new AnonymousMetadataDecoderTask(
249 std::move(metadataDecoder
), ThreadSafeWeakPtr
<AnonymousDecoder
>(this));
250 mFrameCountTask
= new AnonymousFrameCountDecoderTask(
251 std::move(frameCountDecoder
),
252 ThreadSafeWeakPtr
<AnonymousDecoder
>(this));
253 mFramesTask
= new AnonymousFramesDecoderTask(
254 std::move(aDecoder
), ThreadSafeWeakPtr
<AnonymousDecoder
>(this));
256 MOZ_LOG(sLog
, LogLevel::Debug
,
257 ("[%p] AnonymousDecoderImpl::Initialize -- success", this));
261 void Destroy() override
{
262 MutexAutoLock
lock(mMutex
);
263 DestroyLocked(NS_ERROR_ABORT
);
266 void DestroyLocked(nsresult aResult
) MOZ_REQUIRES(mMutex
) {
267 MOZ_LOG(sLog
, LogLevel::Debug
,
268 ("[%p] AnonymousDecoderImpl::Destroy", this));
271 mMetadataTask
= nullptr;
272 mFrameCountTask
= nullptr;
273 mFramesTask
= nullptr;
274 mPendingFramesResult
.mFrames
.Clear();
275 mPendingFramesResult
.mFinished
= true;
276 mMetadataPromise
.RejectIfExists(aResult
, __func__
);
277 mFrameCountPromise
.RejectIfExists(aResult
, __func__
);
278 mFramesPromise
.RejectIfExists(aResult
, __func__
);
281 void OnMetadata(const ImageMetadata
* aMetadata
) override
{
282 MutexAutoLock
lock(mMutex
);
284 // We must have already gotten destroyed before metadata decoding finished.
285 if (!mMetadataTask
) {
290 MOZ_LOG(sLog
, LogLevel::Error
,
291 ("[%p] AnonymousDecoderImpl::OnMetadata -- failed", this));
292 DestroyLocked(NS_ERROR_FAILURE
);
296 const auto size
= aMetadata
->GetSize();
297 mMetadataResult
.mWidth
= size
.width
;
298 mMetadataResult
.mHeight
= size
.height
;
299 mMetadataResult
.mRepetitions
= aMetadata
->GetLoopCount();
300 mMetadataResult
.mAnimated
= aMetadata
->HasAnimation();
302 MOZ_LOG(sLog
, LogLevel::Debug
,
303 ("[%p] AnonymousDecoderImpl::OnMetadata -- %dx%d, repetitions %d, "
305 this, size
.width
, size
.height
, mMetadataResult
.mRepetitions
,
306 mMetadataResult
.mAnimated
));
308 if (mOutputSize
&& !mMetadataResult
.mAnimated
&& mFramesTask
) {
309 if (mOutputSize
->width
<= size
.width
&&
310 mOutputSize
->height
<= size
.height
) {
312 sLog
, LogLevel::Debug
,
313 ("[%p] AnonymousDecoderImpl::OnMetadata -- use output size %dx%d",
314 this, mOutputSize
->width
, mOutputSize
->height
));
315 mFramesTask
->SetOutputSize(
316 OrientedIntSize::FromUnknownSize(*mOutputSize
));
318 MOZ_LOG(sLog
, LogLevel::Debug
,
319 ("[%p] AnonymousDecoderImpl::OnMetadata -- cannot use output "
320 "size %dx%d, exceeds metadata size",
321 this, mOutputSize
->width
, mOutputSize
->height
));
325 if (!mMetadataResult
.mAnimated
) {
326 mMetadataResult
.mFrameCount
= 1;
327 mMetadataResult
.mFrameCountComplete
= true;
328 mMetadataTask
= nullptr;
329 mFrameCountTask
= nullptr;
330 } else if (mFrameCountTask
&& !mFrameCountTaskRunning
) {
332 sLog
, LogLevel::Debug
,
333 ("[%p] AnonymousDecoderImpl::OnMetadata -- start frame count task",
335 mFrameCountTaskRunning
= mFrameCountTask
->MaybeStart();
339 mMetadataPromise
.Resolve(mMetadataResult
, __func__
);
341 if (mFramesTask
&& mFramesToDecode
> 0 && !mFramesTaskRunning
) {
342 MOZ_LOG(sLog
, LogLevel::Debug
,
343 ("[%p] AnonymousDecoderImpl::OnMetadata -- start frames task, "
345 this, mFramesToDecode
));
346 mFramesTaskRunning
= mFramesTask
->MaybeStart();
350 void OnFrameCount(uint32_t aFrameCount
, bool aComplete
) override
{
351 MutexAutoLock
lock(mMutex
);
353 // We must have already gotten destroyed before frame count decoding
355 if (!mFrameCountTask
) {
359 MOZ_LOG(sLog
, LogLevel::Debug
,
360 ("[%p] AnonymousDecoderImpl::OnFrameCount -- frameCount %u, "
362 this, aFrameCount
, aComplete
));
364 bool resolve
= aComplete
;
365 if (mFrameCount
< aFrameCount
) {
366 mFrameCount
= aFrameCount
;
370 // If metadata completing is waiting on an updated frame count, resolve it.
371 mMetadataResult
.mFrameCount
= mFrameCount
;
372 mMetadataResult
.mFrameCountComplete
= aComplete
;
373 mMetadataPromise
.ResolveIfExists(mMetadataResult
, __func__
);
376 mMetadataTask
= nullptr;
377 if (mFramesTask
&& mFramesToDecode
> 0 && !mFramesTaskRunning
) {
379 sLog
, LogLevel::Debug
,
380 ("[%p] AnonymousDecoderImpl::OnFrameCount -- start frames task, "
382 this, mFramesToDecode
));
383 mFramesTaskRunning
= mFramesTask
->MaybeStart();
388 mFrameCountPromise
.ResolveIfExists(
389 DecodeFrameCountResult
{aFrameCount
, aComplete
}, __func__
);
393 mFrameCountTask
= nullptr;
397 bool OnFrameAvailable(RefPtr
<imgFrame
>&& aFrame
,
398 RefPtr
<gfx::SourceSurface
>&& aSurface
) override
{
399 MutexAutoLock
lock(mMutex
);
401 MOZ_DIAGNOSTIC_ASSERT(mFramesTaskRunning
);
403 // We must have already gotten destroyed before frame decoding finished.
405 mFramesTaskRunning
= false;
409 // Filter duplicate frames.
410 if (mLastFrame
== aFrame
) {
414 mPendingFramesResult
.mFrames
.AppendElement(
415 DecodedFrame
{std::move(aSurface
), mMetadataResult
.mAnimated
416 ? aFrame
->GetTimeout()
417 : FrameTimeout::Forever()});
418 mLastFrame
= std::move(aFrame
);
420 MOZ_LOG(sLog
, LogLevel::Debug
,
421 ("[%p] AnonymousDecoderImpl::OnFrameAvailable -- want %zu, got %zu",
422 this, mFramesToDecode
, mPendingFramesResult
.mFrames
.Length()));
424 // Check if we have satisfied the number of requested frames.
425 if (mFramesToDecode
> mPendingFramesResult
.mFrames
.Length()) {
430 if (!mFramesPromise
.IsEmpty()) {
431 mFramesPromise
.Resolve(std::move(mPendingFramesResult
), __func__
);
433 mFramesTaskRunning
= false;
437 void OnFramesComplete() override
{
438 MutexAutoLock
lock(mMutex
);
440 // We must have already gotten destroyed before frame decoding finished.
446 sLog
, LogLevel::Debug
,
447 ("[%p] AnonymousDecoderImpl::OnFramesComplete -- wanted %zu, got %zu",
448 this, mFramesToDecode
, mPendingFramesResult
.mFrames
.Length()));
451 mPendingFramesResult
.mFinished
= true;
452 if (!mFramesPromise
.IsEmpty()) {
453 mFramesPromise
.Resolve(std::move(mPendingFramesResult
), __func__
);
455 mLastFrame
= nullptr;
456 mFramesTask
= nullptr;
459 RefPtr
<DecodeMetadataPromise
> DecodeMetadata() override
{
460 MutexAutoLock
lock(mMutex
);
462 if (!mMetadataTask
) {
463 MOZ_LOG(sLog
, LogLevel::Debug
,
464 ("[%p] AnonymousDecoderImpl::DecodeMetadata -- already complete",
466 if (mMetadataResult
.mWidth
> 0 && mMetadataResult
.mHeight
> 0) {
467 return DecodeMetadataPromise::CreateAndResolve(mMetadataResult
,
470 return DecodeMetadataPromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
473 if (!mMetadataTaskRunning
) {
474 MOZ_LOG(sLog
, LogLevel::Debug
,
475 ("[%p] AnonymousDecoderImpl::DecodeMetadata -- queue", this));
476 mMetadataTaskRunning
= mMetadataTask
->MaybeStart();
479 return mMetadataPromise
.Ensure(__func__
);
482 RefPtr
<DecodeFrameCountPromise
> DecodeFrameCount(
483 uint32_t aKnownFrameCount
) override
{
484 MutexAutoLock
lock(mMutex
);
486 MOZ_ASSERT(mFrameCountPromise
.IsEmpty());
488 // If we have finished, or we have an updated frame count, return right
489 // away. This may drive the frame decoder for the application as the data
490 // comes in from the network.
491 if (!mFrameCountTask
|| aKnownFrameCount
< mFrameCount
) {
492 MOZ_LOG(sLog
, LogLevel::Debug
,
493 ("[%p] AnonymousDecoderImpl::DecodeFrameCount -- known %u, "
494 "detected %u, complete %d",
495 this, aKnownFrameCount
, mFrameCount
, !mFrameCountTask
));
496 return DecodeFrameCountPromise::CreateAndResolve(
497 DecodeFrameCountResult
{mFrameCount
,
498 /* mFinished */ !mFrameCountTask
},
502 // mFrameCountTask is launching when metadata decoding is finished.
503 MOZ_LOG(sLog
, LogLevel::Debug
,
504 ("[%p] AnonymousDecoderImpl::DecodeFrameCount -- waiting, known "
506 this, aKnownFrameCount
, mFrameCount
));
507 return mFrameCountPromise
.Ensure(__func__
);
510 RefPtr
<DecodeFramesPromise
> DecodeFrames(size_t aCount
) override
{
511 MutexAutoLock
lock(mMutex
);
513 // If we cleared our task reference, then we know we finished decoding.
515 mPendingFramesResult
.mFinished
= true;
516 return DecodeFramesPromise::CreateAndResolve(
517 std::move(mPendingFramesResult
), __func__
);
520 // If we are not waiting on any frames, then we know we paused decoding.
521 // If we still are metadata decoding, we need to wait.
522 if (mFramesToDecode
== 0 && !mMetadataTask
&& !mFramesTaskRunning
) {
523 MOZ_LOG(sLog
, LogLevel::Debug
,
524 ("[%p] AnonymousDecoderImpl::DecodeFrames -- queue", this));
525 mFramesTaskRunning
= mFramesTask
->MaybeStart();
528 mFramesToDecode
= std::max(mFramesToDecode
, aCount
);
529 return mFramesPromise
.Ensure(__func__
);
532 void CancelDecodeFrames() override
{
533 MutexAutoLock
lock(mMutex
);
534 MOZ_LOG(sLog
, LogLevel::Debug
,
535 ("[%p] AnonymousDecoderImpl::CancelDecodeFrames", this));
537 mFramesPromise
.RejectIfExists(NS_ERROR_ABORT
, __func__
);
542 MozPromiseHolder
<DecodeMetadataPromise
> mMetadataPromise
543 MOZ_GUARDED_BY(mMutex
);
544 MozPromiseHolder
<DecodeFrameCountPromise
> mFrameCountPromise
545 MOZ_GUARDED_BY(mMutex
);
546 MozPromiseHolder
<DecodeFramesPromise
> mFramesPromise
MOZ_GUARDED_BY(mMutex
);
547 RefPtr
<AnonymousFramesDecoderTask
> mFramesTask
MOZ_GUARDED_BY(mMutex
);
548 RefPtr
<AnonymousMetadataDecoderTask
> mMetadataTask
MOZ_GUARDED_BY(mMutex
);
549 RefPtr
<AnonymousFrameCountDecoderTask
> mFrameCountTask
MOZ_GUARDED_BY(mMutex
);
550 RefPtr
<imgFrame
> mLastFrame
MOZ_GUARDED_BY(mMutex
);
551 DecodeMetadataResult mMetadataResult
MOZ_GUARDED_BY(mMutex
);
552 DecodeFramesResult mPendingFramesResult
MOZ_GUARDED_BY(mMutex
);
553 Maybe
<gfx::IntSize
> mOutputSize
MOZ_GUARDED_BY(mMutex
);
554 size_t mFramesToDecode
MOZ_GUARDED_BY(mMutex
) = 1;
555 uint32_t mFrameCount
MOZ_GUARDED_BY(mMutex
) = 0;
556 bool mMetadataTaskRunning
MOZ_GUARDED_BY(mMutex
) = false;
557 bool mFrameCountTaskRunning
MOZ_GUARDED_BY(mMutex
) = false;
558 bool mFramesTaskRunning
MOZ_GUARDED_BY(mMutex
) = false;
561 /* static */ already_AddRefed
<AnonymousDecoder
> ImageUtils::CreateDecoder(
562 SourceBuffer
* aSourceBuffer
, DecoderType aType
,
563 const Maybe
<gfx::IntSize
>& aOutputSize
, SurfaceFlags aSurfaceFlags
) {
564 if (NS_WARN_IF(!aSourceBuffer
)) {
568 if (NS_WARN_IF(aType
== DecoderType::UNKNOWN
)) {
572 RefPtr
<Decoder
> decoder
= DecoderFactory::CreateAnonymousDecoder(
573 aType
, WrapNotNull(aSourceBuffer
), Nothing(),
574 DecoderFlags::IMAGE_IS_TRANSIENT
, aSurfaceFlags
);
575 if (NS_WARN_IF(!decoder
)) {
579 auto anonymousDecoder
= MakeRefPtr
<AnonymousDecoderImpl
>(aOutputSize
);
580 if (NS_WARN_IF(!anonymousDecoder
->Initialize(std::move(decoder
)))) {
584 return anonymousDecoder
.forget();
587 /* static */ DecoderType
ImageUtils::GetDecoderType(
588 const nsACString
& aMimeType
) {
589 return DecoderFactory::GetDecoderType(aMimeType
.Data());
592 } // namespace mozilla::image