Bug 1942006 - Upstream a variety of Servo-specific code from Servo's downstream fork...
[gecko.git] / image / Decoder.cpp
blobabb58eab521e8469099e2a051cb38f49587583a5
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 "Decoder.h"
8 #include "DecodePool.h"
9 #include "IDecodingTask.h"
10 #include "ISurfaceProvider.h"
11 #include "gfxPlatform.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/gfx/Point.h"
14 #include "mozilla/ProfilerLabels.h"
15 #include "mozilla/Telemetry.h"
16 #include "nsComponentManagerUtils.h"
17 #include "nsProxyRelease.h"
18 #include "nsServiceManagerUtils.h"
20 using mozilla::gfx::IntPoint;
21 using mozilla::gfx::IntRect;
22 using mozilla::gfx::IntSize;
23 using mozilla::gfx::SurfaceFormat;
25 namespace mozilla {
26 namespace image {
28 class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final {
29 public:
30 explicit AutoRecordDecoderTelemetry(Decoder* aDecoder) : mDecoder(aDecoder) {
31 MOZ_ASSERT(mDecoder);
33 // Begin recording telemetry data.
34 mStartTime = TimeStamp::Now();
37 ~AutoRecordDecoderTelemetry() {
38 // Finish telemetry.
39 mDecoder->mDecodeTime += (TimeStamp::Now() - mStartTime);
42 private:
43 Decoder* mDecoder;
44 TimeStamp mStartTime;
47 Decoder::Decoder(RasterImage* aImage)
48 : mInProfile(nullptr),
49 mTransform(nullptr),
50 mImageData(nullptr),
51 mImageDataLength(0),
52 mCMSMode(gfxPlatform::GetCMSMode()),
53 mImage(aImage),
54 mFrameRecycler(nullptr),
55 mProgress(NoProgress),
56 mFrameCount(0),
57 mLoopLength(FrameTimeout::Zero()),
58 mDecoderFlags(DefaultDecoderFlags()),
59 mSurfaceFlags(DefaultSurfaceFlags()),
60 mInitialized(false),
61 mMetadataDecode(false),
62 mHaveExplicitOutputSize(false),
63 mInFrame(false),
64 mFinishedNewFrame(false),
65 mHasFrameToTake(false),
66 mReachedTerminalState(false),
67 mDecodeDone(false),
68 mError(false),
69 mShouldReportError(false),
70 mFinalizeFrames(true) {}
72 Decoder::~Decoder() {
73 MOZ_ASSERT(mProgress == NoProgress || !mImage,
74 "Destroying Decoder without taking all its progress changes");
75 MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage,
76 "Destroying Decoder without taking all its invalidations");
77 mInitialized = false;
79 if (mInProfile) {
80 // mTransform belongs to us only if mInProfile is non-null
81 if (mTransform) {
82 qcms_transform_release(mTransform);
84 qcms_profile_release(mInProfile);
87 if (mImage && !NS_IsMainThread()) {
88 // Dispatch mImage to main thread to prevent it from being destructed by the
89 // decode thread.
90 SurfaceCache::ReleaseImageOnMainThread(mImage.forget());
94 void Decoder::SetSurfaceFlags(SurfaceFlags aSurfaceFlags) {
95 MOZ_ASSERT(!mInitialized);
96 MOZ_ASSERT(!(mSurfaceFlags & SurfaceFlags::NO_COLORSPACE_CONVERSION) ||
97 !(mSurfaceFlags & SurfaceFlags::TO_SRGB_COLORSPACE));
98 mSurfaceFlags = aSurfaceFlags;
99 if (mSurfaceFlags & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
100 mCMSMode = CMSMode::Off;
102 if (mSurfaceFlags & SurfaceFlags::TO_SRGB_COLORSPACE) {
103 // CMSMode::TaggedOnly and CMSMode::All are equivalent when the
104 // TO_SRGB_COLORSPACE flag is set (for untagged images CMSMode::All assumes
105 // they are in sRGB space so it does nothing, which is same as what
106 // CMSMode::TaggedOnly does for untagged images). We just want to avoid
107 // CMSMode::Off so that the sRGB conversion actually happens.
108 mCMSMode = CMSMode::All;
112 qcms_profile* Decoder::GetCMSOutputProfile() const {
113 if (mSurfaceFlags & SurfaceFlags::TO_SRGB_COLORSPACE) {
114 return gfxPlatform::GetCMSsRGBProfile();
116 return gfxPlatform::GetCMSOutputProfile();
119 qcms_transform* Decoder::GetCMSsRGBTransform(SurfaceFormat aFormat) const {
120 if (mSurfaceFlags & SurfaceFlags::TO_SRGB_COLORSPACE) {
121 // We want a transform to convert from sRGB to device space, but we are
122 // already using sRGB as our device space. That means we can skip
123 // color management entirely.
124 return nullptr;
126 if (qcms_profile_is_sRGB(gfxPlatform::GetCMSOutputProfile())) {
127 // Device space is sRGB so we can skip color management as well.
128 return nullptr;
131 switch (aFormat) {
132 case SurfaceFormat::B8G8R8A8:
133 case SurfaceFormat::B8G8R8X8:
134 return gfxPlatform::GetCMSBGRATransform();
135 case SurfaceFormat::R8G8B8A8:
136 case SurfaceFormat::R8G8B8X8:
137 return gfxPlatform::GetCMSRGBATransform();
138 case SurfaceFormat::R8G8B8:
139 return gfxPlatform::GetCMSRGBTransform();
140 default:
141 MOZ_ASSERT_UNREACHABLE("Unsupported surface format!");
142 return nullptr;
147 * Common implementation of the decoder interface.
150 nsresult Decoder::Init() {
151 // No re-initializing
152 MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
154 // All decoders must have a SourceBufferIterator.
155 MOZ_ASSERT(mIterator);
157 // Metadata decoders must not set an output size.
158 MOZ_ASSERT_IF(mMetadataDecode, !mHaveExplicitOutputSize);
160 // All decoders must be anonymous except for metadata decoders.
161 // XXX(seth): Soon that exception will be removed.
162 MOZ_ASSERT_IF(mImage, IsMetadataDecode());
164 // We can only request the frame count for metadata decoders.
165 MOZ_ASSERT_IF(WantsFrameCount(), IsMetadataDecode());
167 // Implementation-specific initialization.
168 nsresult rv = InitInternal();
170 mInitialized = true;
172 return rv;
175 LexerResult Decoder::Decode(IResumable* aOnResume /* = nullptr */) {
176 MOZ_ASSERT(mInitialized, "Should be initialized here");
177 MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
179 // If we're already done, don't attempt to keep decoding.
180 if (GetDecodeDone()) {
181 return LexerResult(HasError() ? TerminalState::FAILURE
182 : TerminalState::SUCCESS);
185 LexerResult lexerResult(TerminalState::FAILURE);
187 AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(GRAPHICS_ImageDecoding);
188 AutoRecordDecoderTelemetry telemetry(this);
190 lexerResult = DoDecode(*mIterator, aOnResume);
193 if (lexerResult.is<Yield>()) {
194 // We either need more data to continue (in which case either @aOnResume or
195 // the caller will reschedule us to run again later), or the decoder is
196 // yielding to allow the caller access to some intermediate output.
197 return lexerResult;
200 // We reached a terminal state; we're now done decoding.
201 MOZ_ASSERT(lexerResult.is<TerminalState>());
202 mReachedTerminalState = true;
204 // If decoding failed, record that fact.
205 if (lexerResult.as<TerminalState>() == TerminalState::FAILURE) {
206 PostError();
209 // Perform final cleanup.
210 CompleteDecode();
212 return LexerResult(HasError() ? TerminalState::FAILURE
213 : TerminalState::SUCCESS);
216 LexerResult Decoder::TerminateFailure() {
217 PostError();
219 // Perform final cleanup if need be.
220 if (!mReachedTerminalState) {
221 mReachedTerminalState = true;
222 CompleteDecode();
225 return LexerResult(TerminalState::FAILURE);
228 bool Decoder::ShouldSyncDecode(size_t aByteLimit) {
229 MOZ_ASSERT(aByteLimit > 0);
230 MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
232 return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
235 void Decoder::CompleteDecode() {
236 // Implementation-specific finalization.
237 nsresult rv = BeforeFinishInternal();
238 if (NS_FAILED(rv)) {
239 PostError();
242 rv = HasError() ? FinishWithErrorInternal() : FinishInternal();
243 if (NS_FAILED(rv)) {
244 PostError();
247 if (IsMetadataDecode()) {
248 // If this was a metadata decode and we never got a size, the decode failed.
249 if (!HasSize()) {
250 PostError();
252 return;
255 // If the implementation left us mid-frame, finish that up. Note that it may
256 // have left us transparent.
257 if (mInFrame) {
258 PostHasTransparency();
259 PostFrameStop();
262 // If PostDecodeDone() has not been called, we may need to send teardown
263 // notifications if it is unrecoverable.
264 if (mDecodeDone) {
265 MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
266 } else {
267 // We should always report an error to the console in this case.
268 mShouldReportError = true;
270 if (GetCompleteFrameCount() > 0) {
271 // We're usable if we have at least one complete frame, so do exactly
272 // what we should have when the decoder completed.
273 PostHasTransparency();
274 PostDecodeDone();
275 } else {
276 // We're not usable. Record some final progress indicating the error.
277 mProgress |= FLAG_DECODE_COMPLETE | FLAG_HAS_ERROR;
282 void Decoder::SetOutputSize(const OrientedIntSize& aSize) {
283 mOutputSize = Some(aSize);
284 mHaveExplicitOutputSize = true;
287 Maybe<OrientedIntSize> Decoder::ExplicitOutputSize() const {
288 MOZ_ASSERT_IF(mHaveExplicitOutputSize, mOutputSize);
289 return mHaveExplicitOutputSize ? mOutputSize : Nothing();
292 Maybe<uint32_t> Decoder::TakeCompleteFrameCount() {
293 const bool finishedNewFrame = mFinishedNewFrame;
294 mFinishedNewFrame = false;
295 return finishedNewFrame ? Some(GetCompleteFrameCount()) : Nothing();
298 DecoderFinalStatus Decoder::FinalStatus() const {
299 return DecoderFinalStatus(IsMetadataDecode(), GetDecodeDone(), HasError(),
300 ShouldReportError());
303 DecoderTelemetry Decoder::Telemetry() const {
304 MOZ_ASSERT(mIterator);
305 return DecoderTelemetry(SpeedHistogram(),
306 mIterator ? mIterator->ByteCount() : 0,
307 mIterator ? mIterator->ChunkCount() : 0, mDecodeTime);
310 nsresult Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
311 gfx::SurfaceFormat aFormat,
312 const Maybe<AnimationParams>& aAnimParams) {
313 mCurrentFrame = AllocateFrameInternal(aOutputSize, aFormat, aAnimParams,
314 std::move(mCurrentFrame));
316 if (mCurrentFrame) {
317 mHasFrameToTake = true;
319 mImageData = mCurrentFrame.Data();
321 // We should now be on |aFrameNum|. (Note that we're comparing the frame
322 // number, which is zero-based, with the frame count, which is one-based.)
323 MOZ_ASSERT_IF(aAnimParams, aAnimParams->mFrameNum + 1 == mFrameCount);
325 // If we're past the first frame, PostIsAnimated() should've been called.
326 MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
328 // Update our state to reflect the new frame.
329 MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
330 mInFrame = true;
331 } else {
332 mImageData = nullptr;
333 mImageDataLength = 0;
336 return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
339 RawAccessFrameRef Decoder::AllocateFrameInternal(
340 const gfx::IntSize& aOutputSize, SurfaceFormat aFormat,
341 const Maybe<AnimationParams>& aAnimParams,
342 RawAccessFrameRef&& aPreviousFrame) {
343 if (HasError()) {
344 return RawAccessFrameRef();
347 uint32_t frameNum = aAnimParams ? aAnimParams->mFrameNum : 0;
348 if (frameNum != mFrameCount) {
349 MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
350 return RawAccessFrameRef();
353 if (aOutputSize.width <= 0 || aOutputSize.height <= 0) {
354 NS_WARNING("Trying to add frame with zero or negative size");
355 return RawAccessFrameRef();
358 if (frameNum > 0) {
359 if (aPreviousFrame->GetDisposalMethod() !=
360 DisposalMethod::RESTORE_PREVIOUS) {
361 // If the new restore frame is the direct previous frame, then we know
362 // the dirty rect is composed only of the current frame's blend rect and
363 // the restore frame's clear rect (if applicable) which are handled in
364 // filters.
365 mRestoreFrame = std::move(aPreviousFrame);
366 mRestoreDirtyRect.SetBox(0, 0, 0, 0);
367 } else {
368 // We only need the previous frame's dirty rect, because while there may
369 // have been several frames between us and mRestoreFrame, the only areas
370 // that changed are the restore frame's clear rect, the current frame
371 // blending rect, and the previous frame's blending rect. All else is
372 // forgotten due to us restoring the same frame again.
373 mRestoreDirtyRect = aPreviousFrame->GetBoundedBlendRect();
377 RawAccessFrameRef ref;
379 // If we have a frame recycler, it must be for an animated image producing
380 // full frames. If the higher layers are discarding frames because of the
381 // memory footprint, then the recycler will allow us to reuse the buffers.
382 // Each frame should be the same size and have mostly the same properties.
383 if (mFrameRecycler) {
384 MOZ_ASSERT(aAnimParams);
386 ref = mFrameRecycler->RecycleFrame(mRecycleRect);
387 if (ref) {
388 // If the recycled frame is actually the current restore frame, we cannot
389 // use it. If the next restore frame is the new frame we are creating, in
390 // theory we could reuse it, but we would need to store the restore frame
391 // animation parameters elsewhere. For now we just drop it.
392 bool blocked = ref.get() == mRestoreFrame.get();
393 if (!blocked) {
394 blocked = NS_FAILED(
395 ref->InitForDecoderRecycle(aAnimParams.ref(), &mImageDataLength));
398 if (blocked) {
399 ref.reset();
404 // Either the recycler had nothing to give us, or we don't have a recycler.
405 // Produce a new frame to store the data.
406 if (!ref) {
407 // There is no underlying data to reuse, so reset the recycle rect to be
408 // the full frame, to ensure the restore frame is fully copied.
409 mRecycleRect = IntRect(IntPoint(0, 0), aOutputSize);
411 bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
412 auto frame = MakeNotNull<RefPtr<imgFrame>>();
413 if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFormat, nonPremult,
414 aAnimParams, bool(mFrameRecycler),
415 &mImageDataLength))) {
416 NS_WARNING("imgFrame::Init should succeed");
417 return RawAccessFrameRef();
420 ref = frame->RawAccessRef(gfx::DataSourceSurface::READ_WRITE);
421 if (!ref) {
422 frame->Abort();
423 return RawAccessFrameRef();
427 mFrameCount++;
429 return ref;
433 * Hook stubs. Override these as necessary in decoder implementations.
436 nsresult Decoder::InitInternal() { return NS_OK; }
437 nsresult Decoder::BeforeFinishInternal() { return NS_OK; }
438 nsresult Decoder::FinishInternal() { return NS_OK; }
440 nsresult Decoder::FinishWithErrorInternal() {
441 MOZ_ASSERT(!mInFrame);
442 return NS_OK;
446 * Progress Notifications
449 void Decoder::PostSize(int32_t aWidth, int32_t aHeight,
450 Orientation aOrientation, Resolution aResolution) {
451 // Validate.
452 MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
453 MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
455 // Set our intrinsic size.
456 mImageMetadata.SetSize(aWidth, aHeight, aOrientation, aResolution);
458 // Verify it is the expected size, if given. Note that this is only used by
459 // the ICO decoder for embedded image types, so only its subdecoders are
460 // required to handle failures in PostSize.
461 if (!IsExpectedSize()) {
462 PostError();
463 return;
466 // Set our output size if it's not already set.
467 if (!mOutputSize) {
468 mOutputSize = Some(mImageMetadata.GetSize());
471 MOZ_ASSERT(mOutputSize->width <= mImageMetadata.GetSize().width &&
472 mOutputSize->height <= mImageMetadata.GetSize().height,
473 "Output size will result in upscaling");
475 // Record this notification.
476 mProgress |= FLAG_SIZE_AVAILABLE;
479 void Decoder::PostHasTransparency() { mProgress |= FLAG_HAS_TRANSPARENCY; }
481 void Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout) {
482 mProgress |= FLAG_IS_ANIMATED;
483 mImageMetadata.SetHasAnimation();
484 mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
487 void Decoder::PostFrameCount(uint32_t aFrameCount) {
488 mImageMetadata.SetFrameCount(aFrameCount);
491 void Decoder::PostFrameStop(Opacity aFrameOpacity) {
492 // We should be mid-frame
493 MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
494 MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
495 MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
497 // Update our state.
498 mInFrame = false;
499 mFinishedNewFrame = true;
501 mCurrentFrame->Finish(
502 aFrameOpacity, mFinalizeFrames,
503 /* aOrientationSwapsWidthAndHeight = */ mImageMetadata.HasOrientation() &&
504 mImageMetadata.GetOrientation().SwapsWidthAndHeight());
506 mProgress |= FLAG_FRAME_COMPLETE;
508 mLoopLength += mCurrentFrame->GetTimeout();
510 if (mFrameCount == 1) {
511 // If we're not sending partial invalidations, then we send an invalidation
512 // here when the first frame is complete.
513 if (!ShouldSendPartialInvalidations()) {
514 mInvalidRect.UnionRect(mInvalidRect,
515 OrientedIntRect(OrientedIntPoint(), Size()));
518 // If we dispose of the first frame by clearing it, then the first frame's
519 // refresh area is all of itself. RESTORE_PREVIOUS is invalid (assumed to
520 // be DISPOSE_CLEAR).
521 switch (mCurrentFrame->GetDisposalMethod()) {
522 default:
523 MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
524 case DisposalMethod::CLEAR:
525 case DisposalMethod::CLEAR_ALL:
526 case DisposalMethod::RESTORE_PREVIOUS:
527 mFirstFrameRefreshArea = IntRect(IntPoint(), Size().ToUnknownSize());
528 break;
529 case DisposalMethod::KEEP:
530 case DisposalMethod::NOT_SPECIFIED:
531 break;
533 } else {
534 // Some GIFs are huge but only have a small area that they animate. We only
535 // need to refresh that small area when frame 0 comes around again.
536 mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea,
537 mCurrentFrame->GetBoundedBlendRect());
541 void Decoder::PostInvalidation(const OrientedIntRect& aRect,
542 const Maybe<OrientedIntRect>& aRectAtOutputSize
543 /* = Nothing() */) {
544 // We should be mid-frame
545 MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!");
546 MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!");
548 // Record this invalidation, unless we're not sending partial invalidations
549 // or we're past the first frame.
550 if (ShouldSendPartialInvalidations() && mFrameCount == 1) {
551 mInvalidRect.UnionRect(mInvalidRect, aRect);
552 mCurrentFrame->ImageUpdated(
553 aRectAtOutputSize.valueOr(aRect).ToUnknownRect());
557 void Decoder::PostLoopCount(int32_t aLoopCount) {
558 mImageMetadata.SetLoopCount(aLoopCount);
561 void Decoder::PostDecodeDone() {
562 MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
563 MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
564 MOZ_ASSERT(!mDecodeDone, "Decode already done!");
565 mDecodeDone = true;
567 // Some metadata that we track should take into account every frame in the
568 // image. If this is a first-frame-only decode, our accumulated loop length
569 // and first frame refresh area only includes the first frame, so it's not
570 // correct and we don't record it.
571 if (!IsFirstFrameDecode()) {
572 mImageMetadata.SetLoopLength(mLoopLength);
573 mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea);
576 mProgress |= FLAG_DECODE_COMPLETE;
579 void Decoder::PostError() {
580 mError = true;
582 if (mInFrame) {
583 MOZ_ASSERT(mCurrentFrame);
584 MOZ_ASSERT(mFrameCount > 0);
585 mCurrentFrame->Abort();
586 mInFrame = false;
587 --mFrameCount;
588 mHasFrameToTake = false;
592 } // namespace image
593 } // namespace mozilla