ImageUtils.cpp (19585B)
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/. */ 5 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/gfx/2D.h" 13 #include "mozilla/Logging.h" 14 #include "nsNetUtil.h" 15 #include "nsStreamUtils.h" 16 17 namespace mozilla::image { 18 19 static LazyLogModule sLog("ImageUtils"); 20 21 AnonymousDecoder::AnonymousDecoder() = default; 22 23 AnonymousDecoder::~AnonymousDecoder() = default; 24 25 class AnonymousDecoderTask : public IDecodingTask { 26 public: 27 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousDecoderTask, final) 28 29 AnonymousDecoderTask(RefPtr<Decoder>&& aDecoder, 30 ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner) 31 : mDecoder(std::move(aDecoder)), mOwner(std::move(aOwner)) {} 32 33 bool ShouldPreferSyncRun() const final { return false; } 34 35 TaskPriority Priority() const final { return TaskPriority::eLow; } 36 37 bool IsValid() const { 38 return !AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal) && 39 !mOwner.IsDead(); 40 } 41 42 bool MaybeStart() { 43 if (!IsValid()) { 44 return false; 45 } 46 47 MOZ_LOG(sLog, LogLevel::Debug, 48 ("[%p] AnonymousDecoderTask::Start -- queue", this)); 49 DecodePool::Singleton()->AsyncRun(this); 50 return true; 51 } 52 53 void Resume() final { 54 if (!IsValid()) { 55 return; 56 } 57 58 MOZ_LOG(sLog, LogLevel::Debug, 59 ("[%p] AnonymousDecoderTask::Resume -- queue", this)); 60 DecodePool::Singleton()->AsyncRun(this); 61 } 62 63 void Run() final { 64 bool resume = true; 65 while (!mOwner.IsDead() && resume) { 66 LexerResult result = mDecoder->Decode(WrapNotNull(this)); 67 if (result == LexerResult(Yield::NEED_MORE_DATA)) { 68 MOZ_LOG(sLog, LogLevel::Debug, 69 ("[%p] AnonymousDecoderTask::Run -- need more data", this)); 70 MOZ_ASSERT(result == LexerResult(Yield::NEED_MORE_DATA)); 71 OnNeedMoreData(); 72 return; 73 } 74 75 // Check if we have a new frame to process. 76 RefPtr<imgFrame> frame = mDecoder->GetCurrentFrame(); 77 if (frame) { 78 RefPtr<gfx::SourceSurface> surface = frame->GetSourceSurface(); 79 if (surface) { 80 MOZ_LOG(sLog, LogLevel::Debug, 81 ("[%p] AnonymousDecoderTask::Run -- new frame %p", this, 82 frame.get())); 83 resume = OnFrameAvailable(std::move(frame), std::move(surface)); 84 } else { 85 MOZ_ASSERT_UNREACHABLE("No surface from frame?"); 86 } 87 } 88 89 if (result.is<TerminalState>()) { 90 MOZ_LOG(sLog, LogLevel::Debug, 91 ("[%p] AnonymousDecoderTask::Run -- complete", this)); 92 OnComplete(result == LexerResult(TerminalState::SUCCESS)); 93 break; 94 } 95 96 MOZ_ASSERT(result == LexerResult(Yield::OUTPUT_AVAILABLE)); 97 } 98 } 99 100 protected: 101 virtual ~AnonymousDecoderTask() = default; 102 103 virtual void OnNeedMoreData() {} 104 105 // Returns true if the caller should continue decoding more frames if 106 // possible. 107 virtual bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame, 108 RefPtr<gfx::SourceSurface>&& aSurface) { 109 MOZ_ASSERT_UNREACHABLE("Unhandled frame!"); 110 return true; 111 } 112 113 virtual void OnComplete(bool aSuccess) = 0; 114 115 RefPtr<Decoder> mDecoder; 116 ThreadSafeWeakPtr<AnonymousDecoder> mOwner; 117 }; 118 119 class AnonymousMetadataDecoderTask final : public AnonymousDecoderTask { 120 public: 121 AnonymousMetadataDecoderTask(RefPtr<Decoder>&& aDecoder, 122 ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner) 123 : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {} 124 125 protected: 126 void OnComplete(bool aSuccess) override { 127 RefPtr<AnonymousDecoder> owner(mOwner); 128 if (!owner) { 129 return; 130 } 131 132 if (!aSuccess) { 133 owner->OnMetadata(nullptr); 134 return; 135 } 136 137 const auto& mdIn = mDecoder->GetImageMetadata(); 138 owner->OnMetadata(&mdIn); 139 } 140 }; 141 142 class AnonymousFrameCountDecoderTask final : public AnonymousDecoderTask { 143 public: 144 AnonymousFrameCountDecoderTask(RefPtr<Decoder>&& aDecoder, 145 ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner) 146 : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {} 147 148 protected: 149 void UpdateFrameCount(bool aComplete) { 150 RefPtr<AnonymousDecoder> owner(mOwner); 151 if (!owner) { 152 return; 153 } 154 155 const auto& mdIn = mDecoder->GetImageMetadata(); 156 uint32_t frameCount = mdIn.HasFrameCount() ? mdIn.GetFrameCount() : 0; 157 owner->OnFrameCount(frameCount, aComplete); 158 } 159 160 void OnNeedMoreData() override { UpdateFrameCount(/* aComplete */ false); } 161 162 void OnComplete(bool aSuccess) override { 163 UpdateFrameCount(/* aComplete */ true); 164 } 165 }; 166 167 class AnonymousFramesDecoderTask final : public AnonymousDecoderTask { 168 public: 169 AnonymousFramesDecoderTask(RefPtr<Decoder>&& aDecoder, 170 ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner) 171 : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {} 172 173 void SetOutputSize(const OrientedIntSize& aSize) { 174 if (mDecoder) { 175 mDecoder->SetOutputSize(aSize); 176 } 177 } 178 179 protected: 180 bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame, 181 RefPtr<gfx::SourceSurface>&& aSurface) override { 182 RefPtr<AnonymousDecoder> owner(mOwner); 183 if (!owner) { 184 return false; 185 } 186 187 return owner->OnFrameAvailable(std::move(aFrame), std::move(aSurface)); 188 } 189 190 void OnComplete(bool aSuccess) override { 191 RefPtr<AnonymousDecoder> owner(mOwner); 192 if (!owner) { 193 return; 194 } 195 196 owner->OnFramesComplete(); 197 } 198 }; 199 200 class AnonymousDecoderImpl final : public AnonymousDecoder { 201 public: 202 explicit AnonymousDecoderImpl(const Maybe<gfx::IntSize>& aOutputSize) 203 : mMutex("mozilla::image::AnonymousDecoderImpl::mMutex"), 204 mOutputSize(aOutputSize) {} 205 206 ~AnonymousDecoderImpl() override { Destroy(); } 207 208 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING 209 const char* typeName() const override { 210 return "mozilla::image::AnonymousDecoderImpl"; 211 } 212 213 size_t typeSize() const override { return sizeof(*this); } 214 #endif 215 216 bool Initialize(RefPtr<Decoder>&& aDecoder) override { 217 MutexAutoLock lock(mMutex); 218 219 if (NS_WARN_IF(!aDecoder)) { 220 MOZ_LOG(sLog, LogLevel::Error, 221 ("[%p] AnonymousDecoderImpl::Initialize -- bad decoder", this)); 222 return false; 223 } 224 225 RefPtr<Decoder> metadataDecoder = 226 DecoderFactory::CloneAnonymousMetadataDecoder(aDecoder); 227 if (NS_WARN_IF(!metadataDecoder)) { 228 MOZ_LOG(sLog, LogLevel::Error, 229 ("[%p] AnonymousDecoderImpl::Initialize -- failed clone metadata " 230 "decoder", 231 this)); 232 return false; 233 } 234 235 DecoderFlags flags = 236 aDecoder->GetDecoderFlags() | DecoderFlags::COUNT_FRAMES; 237 RefPtr<Decoder> frameCountDecoder = 238 DecoderFactory::CloneAnonymousMetadataDecoder(aDecoder, Some(flags)); 239 if (NS_WARN_IF(!frameCountDecoder)) { 240 MOZ_LOG(sLog, LogLevel::Error, 241 ("[%p] AnonymousDecoderImpl::Initialize -- failed clone frame " 242 "count decoder", 243 this)); 244 return false; 245 } 246 247 mMetadataTask = new AnonymousMetadataDecoderTask( 248 std::move(metadataDecoder), ThreadSafeWeakPtr<AnonymousDecoder>(this)); 249 mFrameCountTask = new AnonymousFrameCountDecoderTask( 250 std::move(frameCountDecoder), 251 ThreadSafeWeakPtr<AnonymousDecoder>(this)); 252 mFramesTask = new AnonymousFramesDecoderTask( 253 std::move(aDecoder), ThreadSafeWeakPtr<AnonymousDecoder>(this)); 254 255 MOZ_LOG(sLog, LogLevel::Debug, 256 ("[%p] AnonymousDecoderImpl::Initialize -- success", this)); 257 return true; 258 } 259 260 void Destroy() override { 261 MutexAutoLock lock(mMutex); 262 DestroyLocked(NS_ERROR_ABORT); 263 } 264 265 void DestroyLocked(nsresult aResult) MOZ_REQUIRES(mMutex) { 266 MOZ_LOG(sLog, LogLevel::Debug, 267 ("[%p] AnonymousDecoderImpl::Destroy", this)); 268 269 mFramesToDecode = 0; 270 mMetadataTask = nullptr; 271 mFrameCountTask = nullptr; 272 mFramesTask = nullptr; 273 mPendingFramesResult.mFrames.Clear(); 274 mPendingFramesResult.mFinished = true; 275 mMetadataPromise.RejectIfExists(aResult, __func__); 276 mFrameCountPromise.RejectIfExists(aResult, __func__); 277 mFramesPromise.RejectIfExists(aResult, __func__); 278 } 279 280 void OnMetadata(const ImageMetadata* aMetadata) override { 281 MutexAutoLock lock(mMutex); 282 283 // We must have already gotten destroyed before metadata decoding finished. 284 if (!mMetadataTask) { 285 return; 286 } 287 288 if (!aMetadata) { 289 MOZ_LOG(sLog, LogLevel::Error, 290 ("[%p] AnonymousDecoderImpl::OnMetadata -- failed", this)); 291 DestroyLocked(NS_ERROR_FAILURE); 292 return; 293 } 294 295 mMetadataResult.mNativeSizes = aMetadata->GetNativeSizes().Clone(); 296 297 const auto size = aMetadata->GetSize(); 298 mMetadataResult.mWidth = size.width; 299 mMetadataResult.mHeight = size.height; 300 mMetadataResult.mRepetitions = aMetadata->GetLoopCount(); 301 mMetadataResult.mAnimated = aMetadata->HasAnimation(); 302 303 MOZ_LOG(sLog, LogLevel::Debug, 304 ("[%p] AnonymousDecoderImpl::OnMetadata -- %dx%d, repetitions %d, " 305 "animated %d", 306 this, size.width, size.height, mMetadataResult.mRepetitions, 307 mMetadataResult.mAnimated)); 308 309 if (mOutputSize && !mMetadataResult.mAnimated && mFramesTask) { 310 if (mOutputSize->width <= size.width && 311 mOutputSize->height <= size.height) { 312 MOZ_LOG( 313 sLog, LogLevel::Debug, 314 ("[%p] AnonymousDecoderImpl::OnMetadata -- use output size %dx%d", 315 this, mOutputSize->width, mOutputSize->height)); 316 mFramesTask->SetOutputSize( 317 OrientedIntSize::FromUnknownSize(*mOutputSize)); 318 } else { 319 MOZ_LOG(sLog, LogLevel::Debug, 320 ("[%p] AnonymousDecoderImpl::OnMetadata -- cannot use output " 321 "size %dx%d, exceeds metadata size", 322 this, mOutputSize->width, mOutputSize->height)); 323 } 324 } 325 326 if (!mMetadataResult.mAnimated) { 327 mMetadataResult.mFrameCount = 1; 328 mMetadataResult.mFrameCountComplete = true; 329 mMetadataTask = nullptr; 330 mFrameCountTask = nullptr; 331 } else if (mFrameCountTask && !mFrameCountTaskRunning) { 332 MOZ_LOG( 333 sLog, LogLevel::Debug, 334 ("[%p] AnonymousDecoderImpl::OnMetadata -- start frame count task", 335 this)); 336 mFrameCountTaskRunning = mFrameCountTask->MaybeStart(); 337 return; 338 } 339 340 mMetadataPromise.Resolve(mMetadataResult, __func__); 341 342 if (mFramesTask && mFramesToDecode > 0 && !mFramesTaskRunning) { 343 MOZ_LOG(sLog, LogLevel::Debug, 344 ("[%p] AnonymousDecoderImpl::OnMetadata -- start frames task, " 345 "want %zu", 346 this, mFramesToDecode)); 347 mFramesTaskRunning = mFramesTask->MaybeStart(); 348 } 349 } 350 351 void OnFrameCount(uint32_t aFrameCount, bool aComplete) override { 352 MutexAutoLock lock(mMutex); 353 354 // We must have already gotten destroyed before frame count decoding 355 // finished. 356 if (!mFrameCountTask) { 357 return; 358 } 359 360 MOZ_LOG(sLog, LogLevel::Debug, 361 ("[%p] AnonymousDecoderImpl::OnFrameCount -- frameCount %u, " 362 "complete %d", 363 this, aFrameCount, aComplete)); 364 365 bool resolve = aComplete; 366 if (mFrameCount < aFrameCount) { 367 mFrameCount = aFrameCount; 368 resolve = true; 369 } 370 371 // If metadata completing is waiting on an updated frame count, resolve it. 372 mMetadataResult.mFrameCount = mFrameCount; 373 mMetadataResult.mFrameCountComplete = aComplete; 374 mMetadataPromise.ResolveIfExists(mMetadataResult, __func__); 375 376 if (mMetadataTask) { 377 mMetadataTask = nullptr; 378 if (mFramesTask && mFramesToDecode > 0 && !mFramesTaskRunning) { 379 MOZ_LOG( 380 sLog, LogLevel::Debug, 381 ("[%p] AnonymousDecoderImpl::OnFrameCount -- start frames task, " 382 "want %zu", 383 this, mFramesToDecode)); 384 mFramesTaskRunning = mFramesTask->MaybeStart(); 385 } 386 } 387 388 if (resolve) { 389 mFrameCountPromise.ResolveIfExists( 390 DecodeFrameCountResult{aFrameCount, aComplete}, __func__); 391 } 392 393 if (aComplete) { 394 mFrameCountTask = nullptr; 395 } 396 } 397 398 bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame, 399 RefPtr<gfx::SourceSurface>&& aSurface) override { 400 MutexAutoLock lock(mMutex); 401 402 MOZ_DIAGNOSTIC_ASSERT(mFramesTaskRunning); 403 404 // We must have already gotten destroyed before frame decoding finished. 405 if (!mFramesTask) { 406 mFramesTaskRunning = false; 407 return false; 408 } 409 410 // Filter duplicate frames. 411 if (mLastFrame == aFrame) { 412 return true; 413 } 414 415 mPendingFramesResult.mFrames.AppendElement( 416 DecodedFrame{std::move(aSurface), mMetadataResult.mAnimated 417 ? aFrame->GetTimeout() 418 : FrameTimeout::Forever()}); 419 mLastFrame = std::move(aFrame); 420 421 MOZ_LOG(sLog, LogLevel::Debug, 422 ("[%p] AnonymousDecoderImpl::OnFrameAvailable -- want %zu, got %zu", 423 this, mFramesToDecode, mPendingFramesResult.mFrames.Length())); 424 425 // Check if we have satisfied the number of requested frames. 426 if (mFramesToDecode > mPendingFramesResult.mFrames.Length()) { 427 return true; 428 } 429 430 mFramesToDecode = 0; 431 if (!mFramesPromise.IsEmpty()) { 432 mFramesPromise.Resolve(std::move(mPendingFramesResult), __func__); 433 } 434 mFramesTaskRunning = false; 435 return false; 436 } 437 438 void OnFramesComplete() override { 439 MutexAutoLock lock(mMutex); 440 441 // We must have already gotten destroyed before frame decoding finished. 442 if (!mFramesTask) { 443 return; 444 } 445 446 MOZ_LOG( 447 sLog, LogLevel::Debug, 448 ("[%p] AnonymousDecoderImpl::OnFramesComplete -- wanted %zu, got %zu", 449 this, mFramesToDecode, mPendingFramesResult.mFrames.Length())); 450 451 mFramesToDecode = 0; 452 mPendingFramesResult.mFinished = true; 453 if (!mFramesPromise.IsEmpty()) { 454 mFramesPromise.Resolve(std::move(mPendingFramesResult), __func__); 455 } 456 mLastFrame = nullptr; 457 mFramesTask = nullptr; 458 } 459 460 RefPtr<DecodeMetadataPromise> DecodeMetadata() override { 461 MutexAutoLock lock(mMutex); 462 463 if (!mMetadataTask) { 464 MOZ_LOG(sLog, LogLevel::Debug, 465 ("[%p] AnonymousDecoderImpl::DecodeMetadata -- already complete", 466 this)); 467 if (mMetadataResult.mWidth > 0 && mMetadataResult.mHeight > 0) { 468 return DecodeMetadataPromise::CreateAndResolve(mMetadataResult, 469 __func__); 470 } 471 return DecodeMetadataPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 472 } 473 474 if (!mMetadataTaskRunning) { 475 MOZ_LOG(sLog, LogLevel::Debug, 476 ("[%p] AnonymousDecoderImpl::DecodeMetadata -- queue", this)); 477 mMetadataTaskRunning = mMetadataTask->MaybeStart(); 478 } 479 480 return mMetadataPromise.Ensure(__func__); 481 } 482 483 RefPtr<DecodeFrameCountPromise> DecodeFrameCount( 484 uint32_t aKnownFrameCount) override { 485 MutexAutoLock lock(mMutex); 486 487 MOZ_ASSERT(mFrameCountPromise.IsEmpty()); 488 489 // If we have finished, or we have an updated frame count, return right 490 // away. This may drive the frame decoder for the application as the data 491 // comes in from the network. 492 if (!mFrameCountTask || aKnownFrameCount < mFrameCount) { 493 MOZ_LOG(sLog, LogLevel::Debug, 494 ("[%p] AnonymousDecoderImpl::DecodeFrameCount -- known %u, " 495 "detected %u, complete %d", 496 this, aKnownFrameCount, mFrameCount, !mFrameCountTask)); 497 return DecodeFrameCountPromise::CreateAndResolve( 498 DecodeFrameCountResult{mFrameCount, 499 /* mFinished */ !mFrameCountTask}, 500 __func__); 501 } 502 503 // mFrameCountTask is launching when metadata decoding is finished. 504 MOZ_LOG(sLog, LogLevel::Debug, 505 ("[%p] AnonymousDecoderImpl::DecodeFrameCount -- waiting, known " 506 "%u, detected %u", 507 this, aKnownFrameCount, mFrameCount)); 508 return mFrameCountPromise.Ensure(__func__); 509 } 510 511 RefPtr<DecodeFramesPromise> DecodeFrames(size_t aCount) override { 512 MutexAutoLock lock(mMutex); 513 514 // If we cleared our task reference, then we know we finished decoding. 515 if (!mFramesTask) { 516 mPendingFramesResult.mFinished = true; 517 return DecodeFramesPromise::CreateAndResolve( 518 std::move(mPendingFramesResult), __func__); 519 } 520 521 // If we are not waiting on any frames, then we know we paused decoding. 522 // If we still are metadata decoding, we need to wait. 523 if (mFramesToDecode == 0 && !mMetadataTask && !mFramesTaskRunning) { 524 MOZ_LOG(sLog, LogLevel::Debug, 525 ("[%p] AnonymousDecoderImpl::DecodeFrames -- queue", this)); 526 mFramesTaskRunning = mFramesTask->MaybeStart(); 527 } 528 529 mFramesToDecode = std::max(mFramesToDecode, aCount); 530 return mFramesPromise.Ensure(__func__); 531 } 532 533 void CancelDecodeFrames() override { 534 MutexAutoLock lock(mMutex); 535 MOZ_LOG(sLog, LogLevel::Debug, 536 ("[%p] AnonymousDecoderImpl::CancelDecodeFrames", this)); 537 mFramesToDecode = 0; 538 mFramesPromise.RejectIfExists(NS_ERROR_ABORT, __func__); 539 } 540 541 private: 542 Mutex mMutex; 543 MozPromiseHolder<DecodeMetadataPromise> mMetadataPromise 544 MOZ_GUARDED_BY(mMutex); 545 MozPromiseHolder<DecodeFrameCountPromise> mFrameCountPromise 546 MOZ_GUARDED_BY(mMutex); 547 MozPromiseHolder<DecodeFramesPromise> mFramesPromise MOZ_GUARDED_BY(mMutex); 548 RefPtr<AnonymousFramesDecoderTask> mFramesTask MOZ_GUARDED_BY(mMutex); 549 RefPtr<AnonymousMetadataDecoderTask> mMetadataTask MOZ_GUARDED_BY(mMutex); 550 RefPtr<AnonymousFrameCountDecoderTask> mFrameCountTask MOZ_GUARDED_BY(mMutex); 551 RefPtr<imgFrame> mLastFrame MOZ_GUARDED_BY(mMutex); 552 DecodeMetadataResult mMetadataResult MOZ_GUARDED_BY(mMutex); 553 DecodeFramesResult mPendingFramesResult MOZ_GUARDED_BY(mMutex); 554 Maybe<gfx::IntSize> mOutputSize MOZ_GUARDED_BY(mMutex); 555 size_t mFramesToDecode MOZ_GUARDED_BY(mMutex) = 1; 556 uint32_t mFrameCount MOZ_GUARDED_BY(mMutex) = 0; 557 bool mMetadataTaskRunning MOZ_GUARDED_BY(mMutex) = false; 558 bool mFrameCountTaskRunning MOZ_GUARDED_BY(mMutex) = false; 559 bool mFramesTaskRunning MOZ_GUARDED_BY(mMutex) = false; 560 }; 561 562 /* static */ already_AddRefed<AnonymousDecoder> ImageUtils::CreateDecoder( 563 SourceBuffer* aSourceBuffer, DecoderType aType, 564 const Maybe<gfx::IntSize>& aOutputSize, SurfaceFlags aSurfaceFlags) { 565 if (NS_WARN_IF(!aSourceBuffer)) { 566 return nullptr; 567 } 568 569 if (NS_WARN_IF(aType == DecoderType::UNKNOWN)) { 570 return nullptr; 571 } 572 573 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder( 574 aType, WrapNotNull(aSourceBuffer), Nothing(), 575 DecoderFlags::IMAGE_IS_TRANSIENT, aSurfaceFlags); 576 if (NS_WARN_IF(!decoder)) { 577 return nullptr; 578 } 579 580 auto anonymousDecoder = MakeRefPtr<AnonymousDecoderImpl>(aOutputSize); 581 if (NS_WARN_IF(!anonymousDecoder->Initialize(std::move(decoder)))) { 582 return nullptr; 583 } 584 585 return anonymousDecoder.forget(); 586 } 587 588 /* static */ DecoderType ImageUtils::GetDecoderType( 589 const nsACString& aMimeType) { 590 return DecoderFactory::GetDecoderType(aMimeType.Data()); 591 } 592 593 } // namespace mozilla::image