Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / image / ImageUtils.cpp
blob2602b52ec13902ee7b73d86acee9a4d48625c621
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"
8 #include "Decoder.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 {
27 public:
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) &&
40 !mOwner.IsDead();
43 bool MaybeStart() {
44 if (!IsValid()) {
45 return false;
48 MOZ_LOG(sLog, LogLevel::Debug,
49 ("[%p] AnonymousDecoderTask::Start -- queue", this));
50 DecodePool::Singleton()->AsyncRun(this);
51 return true;
54 void Resume() final {
55 if (!IsValid()) {
56 return;
59 MOZ_LOG(sLog, LogLevel::Debug,
60 ("[%p] AnonymousDecoderTask::Resume -- queue", this));
61 DecodePool::Singleton()->AsyncRun(this);
64 void Run() final {
65 bool resume = true;
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));
72 OnNeedMoreData();
73 return;
76 // Check if we have a new frame to process.
77 RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame();
78 if (frame) {
79 RefPtr<gfx::SourceSurface> surface = frame->GetSourceSurface();
80 if (surface) {
81 MOZ_LOG(sLog, LogLevel::Debug,
82 ("[%p] AnonymousDecoderTask::Run -- new frame %p", this,
83 frame.get()));
84 resume = OnFrameAvailable(std::move(frame), std::move(surface));
85 } else {
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));
94 break;
97 MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE));
101 protected:
102 virtual ~AnonymousDecoderTask() = default;
104 virtual void OnNeedMoreData() {}
106 // Returns true if the caller should continue decoding more frames if
107 // possible.
108 virtual bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
109 RefPtr<gfx::SourceSurface>&& aSurface) {
110 MOZ_ASSERT_UNREACHABLE("Unhandled frame!");
111 return true;
114 virtual void OnComplete(bool aSuccess) = 0;
116 RefPtr<Decoder> mDecoder;
117 ThreadSafeWeakPtr<AnonymousDecoder> mOwner;
120 class AnonymousMetadataDecoderTask final : public AnonymousDecoderTask {
121 public:
122 AnonymousMetadataDecoderTask(RefPtr<Decoder>&& aDecoder,
123 ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
124 : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
126 protected:
127 void OnComplete(bool aSuccess) override {
128 RefPtr<AnonymousDecoder> owner(mOwner);
129 if (!owner) {
130 return;
133 if (!aSuccess) {
134 owner->OnMetadata(nullptr);
135 return;
138 const auto& mdIn = mDecoder->GetImageMetadata();
139 owner->OnMetadata(&mdIn);
143 class AnonymousFrameCountDecoderTask final : public AnonymousDecoderTask {
144 public:
145 AnonymousFrameCountDecoderTask(RefPtr<Decoder>&& aDecoder,
146 ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
147 : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
149 protected:
150 void UpdateFrameCount(bool aComplete) {
151 RefPtr<AnonymousDecoder> owner(mOwner);
152 if (!owner) {
153 return;
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 {
169 public:
170 AnonymousFramesDecoderTask(RefPtr<Decoder>&& aDecoder,
171 ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
172 : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
174 void SetOutputSize(const OrientedIntSize& aSize) {
175 if (mDecoder) {
176 mDecoder->SetOutputSize(aSize);
180 protected:
181 bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
182 RefPtr<gfx::SourceSurface>&& aSurface) override {
183 RefPtr<AnonymousDecoder> owner(mOwner);
184 if (!owner) {
185 return false;
188 return owner->OnFrameAvailable(std::move(aFrame), std::move(aSurface));
191 void OnComplete(bool aSuccess) override {
192 RefPtr<AnonymousDecoder> owner(mOwner);
193 if (!owner) {
194 return;
197 owner->OnFramesComplete();
201 class AnonymousDecoderImpl final : public AnonymousDecoder {
202 public:
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); }
215 #endif
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));
223 return false;
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 "
231 "decoder",
232 this));
233 return false;
236 DecoderFlags flags =
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 "
243 "count decoder",
244 this));
245 return false;
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));
258 return true;
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));
270 mFramesToDecode = 0;
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) {
286 return;
289 if (!aMetadata) {
290 MOZ_LOG(sLog, LogLevel::Error,
291 ("[%p] AnonymousDecoderImpl::OnMetadata -- failed", this));
292 DestroyLocked(NS_ERROR_FAILURE);
293 return;
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, "
304 "animated %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) {
311 MOZ_LOG(
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));
317 } else {
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) {
331 MOZ_LOG(
332 sLog, LogLevel::Debug,
333 ("[%p] AnonymousDecoderImpl::OnMetadata -- start frame count task",
334 this));
335 mFrameCountTaskRunning = mFrameCountTask->MaybeStart();
336 return;
339 mMetadataPromise.Resolve(mMetadataResult, __func__);
341 if (mFramesTask && mFramesToDecode > 0 && !mFramesTaskRunning) {
342 MOZ_LOG(sLog, LogLevel::Debug,
343 ("[%p] AnonymousDecoderImpl::OnMetadata -- start frames task, "
344 "want %zu",
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
354 // finished.
355 if (!mFrameCountTask) {
356 return;
359 MOZ_LOG(sLog, LogLevel::Debug,
360 ("[%p] AnonymousDecoderImpl::OnFrameCount -- frameCount %u, "
361 "complete %d",
362 this, aFrameCount, aComplete));
364 bool resolve = aComplete;
365 if (mFrameCount < aFrameCount) {
366 mFrameCount = aFrameCount;
367 resolve = true;
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__);
375 if (mMetadataTask) {
376 mMetadataTask = nullptr;
377 if (mFramesTask && mFramesToDecode > 0 && !mFramesTaskRunning) {
378 MOZ_LOG(
379 sLog, LogLevel::Debug,
380 ("[%p] AnonymousDecoderImpl::OnFrameCount -- start frames task, "
381 "want %zu",
382 this, mFramesToDecode));
383 mFramesTaskRunning = mFramesTask->MaybeStart();
387 if (resolve) {
388 mFrameCountPromise.ResolveIfExists(
389 DecodeFrameCountResult{aFrameCount, aComplete}, __func__);
392 if (aComplete) {
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.
404 if (!mFramesTask) {
405 mFramesTaskRunning = false;
406 return false;
409 // Filter duplicate frames.
410 if (mLastFrame == aFrame) {
411 return true;
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()) {
426 return true;
429 mFramesToDecode = 0;
430 if (!mFramesPromise.IsEmpty()) {
431 mFramesPromise.Resolve(std::move(mPendingFramesResult), __func__);
433 mFramesTaskRunning = false;
434 return false;
437 void OnFramesComplete() override {
438 MutexAutoLock lock(mMutex);
440 // We must have already gotten destroyed before frame decoding finished.
441 if (!mFramesTask) {
442 return;
445 MOZ_LOG(
446 sLog, LogLevel::Debug,
447 ("[%p] AnonymousDecoderImpl::OnFramesComplete -- wanted %zu, got %zu",
448 this, mFramesToDecode, mPendingFramesResult.mFrames.Length()));
450 mFramesToDecode = 0;
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",
465 this));
466 if (mMetadataResult.mWidth > 0 && mMetadataResult.mHeight > 0) {
467 return DecodeMetadataPromise::CreateAndResolve(mMetadataResult,
468 __func__);
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},
499 __func__);
502 // mFrameCountTask is launching when metadata decoding is finished.
503 MOZ_LOG(sLog, LogLevel::Debug,
504 ("[%p] AnonymousDecoderImpl::DecodeFrameCount -- waiting, known "
505 "%u, detected %u",
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.
514 if (!mFramesTask) {
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));
536 mFramesToDecode = 0;
537 mFramesPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
540 private:
541 Mutex mMutex;
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)) {
565 return nullptr;
568 if (NS_WARN_IF(aType == DecoderType::UNKNOWN)) {
569 return nullptr;
572 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
573 aType, WrapNotNull(aSourceBuffer), Nothing(),
574 DecoderFlags::IMAGE_IS_TRANSIENT, aSurfaceFlags);
575 if (NS_WARN_IF(!decoder)) {
576 return nullptr;
579 auto anonymousDecoder = MakeRefPtr<AnonymousDecoderImpl>(aOutputSize);
580 if (NS_WARN_IF(!anonymousDecoder->Initialize(std::move(decoder)))) {
581 return nullptr;
584 return anonymousDecoder.forget();
587 /* static */ DecoderType ImageUtils::GetDecoderType(
588 const nsACString& aMimeType) {
589 return DecoderFactory::GetDecoderType(aMimeType.Data());
592 } // namespace mozilla::image