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/. */
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
;
28 class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final
{
30 explicit AutoRecordDecoderTelemetry(Decoder
* aDecoder
) : mDecoder(aDecoder
) {
33 // Begin recording telemetry data.
34 mStartTime
= TimeStamp::Now();
37 ~AutoRecordDecoderTelemetry() {
39 mDecoder
->mDecodeTime
+= (TimeStamp::Now() - mStartTime
);
47 Decoder::Decoder(RasterImage
* aImage
)
48 : mInProfile(nullptr),
52 mCMSMode(gfxPlatform::GetCMSMode()),
54 mFrameRecycler(nullptr),
55 mProgress(NoProgress
),
57 mLoopLength(FrameTimeout::Zero()),
58 mDecoderFlags(DefaultDecoderFlags()),
59 mSurfaceFlags(DefaultSurfaceFlags()),
61 mMetadataDecode(false),
62 mHaveExplicitOutputSize(false),
64 mFinishedNewFrame(false),
65 mHasFrameToTake(false),
66 mReachedTerminalState(false),
69 mShouldReportError(false),
70 mFinalizeFrames(true) {}
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");
80 // mTransform belongs to us only if mInProfile is non-null
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
90 SurfaceCache::ReleaseImageOnMainThread(mImage
.forget());
94 void Decoder::SetSurfaceFlags(SurfaceFlags aSurfaceFlags
) {
95 MOZ_ASSERT(!mInitialized
);
96 mSurfaceFlags
= aSurfaceFlags
;
97 if (mSurfaceFlags
& SurfaceFlags::NO_COLORSPACE_CONVERSION
) {
98 mCMSMode
= CMSMode::Off
;
102 qcms_profile
* Decoder::GetCMSOutputProfile() const {
103 if (mSurfaceFlags
& SurfaceFlags::TO_SRGB_COLORSPACE
) {
104 return gfxPlatform::GetCMSsRGBProfile();
106 return gfxPlatform::GetCMSOutputProfile();
109 qcms_transform
* Decoder::GetCMSsRGBTransform(SurfaceFormat aFormat
) const {
110 if (mSurfaceFlags
& SurfaceFlags::TO_SRGB_COLORSPACE
) {
111 // We want a transform to convert from sRGB to device space, but we are
112 // already using sRGB as our device space. That means we can skip
113 // color management entirely.
116 if (qcms_profile_is_sRGB(gfxPlatform::GetCMSOutputProfile())) {
117 // Device space is sRGB so we can skip color management as well.
122 case SurfaceFormat::B8G8R8A8
:
123 case SurfaceFormat::B8G8R8X8
:
124 return gfxPlatform::GetCMSBGRATransform();
125 case SurfaceFormat::R8G8B8A8
:
126 case SurfaceFormat::R8G8B8X8
:
127 return gfxPlatform::GetCMSRGBATransform();
128 case SurfaceFormat::R8G8B8
:
129 return gfxPlatform::GetCMSRGBTransform();
131 MOZ_ASSERT_UNREACHABLE("Unsupported surface format!");
137 * Common implementation of the decoder interface.
140 nsresult
Decoder::Init() {
141 // No re-initializing
142 MOZ_ASSERT(!mInitialized
, "Can't re-initialize a decoder!");
144 // All decoders must have a SourceBufferIterator.
145 MOZ_ASSERT(mIterator
);
147 // Metadata decoders must not set an output size.
148 MOZ_ASSERT_IF(mMetadataDecode
, !mHaveExplicitOutputSize
);
150 // All decoders must be anonymous except for metadata decoders.
151 // XXX(seth): Soon that exception will be removed.
152 MOZ_ASSERT_IF(mImage
, IsMetadataDecode());
154 // We can only request the frame count for metadata decoders.
155 MOZ_ASSERT_IF(WantsFrameCount(), IsMetadataDecode());
157 // Implementation-specific initialization.
158 nsresult rv
= InitInternal();
165 LexerResult
Decoder::Decode(IResumable
* aOnResume
/* = nullptr */) {
166 MOZ_ASSERT(mInitialized
, "Should be initialized here");
167 MOZ_ASSERT(mIterator
, "Should have a SourceBufferIterator");
169 // If we're already done, don't attempt to keep decoding.
170 if (GetDecodeDone()) {
171 return LexerResult(HasError() ? TerminalState::FAILURE
172 : TerminalState::SUCCESS
);
175 LexerResult
lexerResult(TerminalState::FAILURE
);
177 AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(GRAPHICS_ImageDecoding
);
178 AutoRecordDecoderTelemetry
telemetry(this);
180 lexerResult
= DoDecode(*mIterator
, aOnResume
);
183 if (lexerResult
.is
<Yield
>()) {
184 // We either need more data to continue (in which case either @aOnResume or
185 // the caller will reschedule us to run again later), or the decoder is
186 // yielding to allow the caller access to some intermediate output.
190 // We reached a terminal state; we're now done decoding.
191 MOZ_ASSERT(lexerResult
.is
<TerminalState
>());
192 mReachedTerminalState
= true;
194 // If decoding failed, record that fact.
195 if (lexerResult
.as
<TerminalState
>() == TerminalState::FAILURE
) {
199 // Perform final cleanup.
202 return LexerResult(HasError() ? TerminalState::FAILURE
203 : TerminalState::SUCCESS
);
206 LexerResult
Decoder::TerminateFailure() {
209 // Perform final cleanup if need be.
210 if (!mReachedTerminalState
) {
211 mReachedTerminalState
= true;
215 return LexerResult(TerminalState::FAILURE
);
218 bool Decoder::ShouldSyncDecode(size_t aByteLimit
) {
219 MOZ_ASSERT(aByteLimit
> 0);
220 MOZ_ASSERT(mIterator
, "Should have a SourceBufferIterator");
222 return mIterator
->RemainingBytesIsNoMoreThan(aByteLimit
);
225 void Decoder::CompleteDecode() {
226 // Implementation-specific finalization.
227 nsresult rv
= BeforeFinishInternal();
232 rv
= HasError() ? FinishWithErrorInternal() : FinishInternal();
237 if (IsMetadataDecode()) {
238 // If this was a metadata decode and we never got a size, the decode failed.
245 // If the implementation left us mid-frame, finish that up. Note that it may
246 // have left us transparent.
248 PostHasTransparency();
252 // If PostDecodeDone() has not been called, we may need to send teardown
253 // notifications if it is unrecoverable.
255 MOZ_ASSERT(HasError() || mCurrentFrame
, "Should have an error or a frame");
257 // We should always report an error to the console in this case.
258 mShouldReportError
= true;
260 if (GetCompleteFrameCount() > 0) {
261 // We're usable if we have at least one complete frame, so do exactly
262 // what we should have when the decoder completed.
263 PostHasTransparency();
266 // We're not usable. Record some final progress indicating the error.
267 mProgress
|= FLAG_DECODE_COMPLETE
| FLAG_HAS_ERROR
;
272 void Decoder::SetOutputSize(const OrientedIntSize
& aSize
) {
273 mOutputSize
= Some(aSize
);
274 mHaveExplicitOutputSize
= true;
277 Maybe
<OrientedIntSize
> Decoder::ExplicitOutputSize() const {
278 MOZ_ASSERT_IF(mHaveExplicitOutputSize
, mOutputSize
);
279 return mHaveExplicitOutputSize
? mOutputSize
: Nothing();
282 Maybe
<uint32_t> Decoder::TakeCompleteFrameCount() {
283 const bool finishedNewFrame
= mFinishedNewFrame
;
284 mFinishedNewFrame
= false;
285 return finishedNewFrame
? Some(GetCompleteFrameCount()) : Nothing();
288 DecoderFinalStatus
Decoder::FinalStatus() const {
289 return DecoderFinalStatus(IsMetadataDecode(), GetDecodeDone(), HasError(),
290 ShouldReportError());
293 DecoderTelemetry
Decoder::Telemetry() const {
294 MOZ_ASSERT(mIterator
);
295 return DecoderTelemetry(SpeedHistogram(),
296 mIterator
? mIterator
->ByteCount() : 0,
297 mIterator
? mIterator
->ChunkCount() : 0, mDecodeTime
);
300 nsresult
Decoder::AllocateFrame(const gfx::IntSize
& aOutputSize
,
301 gfx::SurfaceFormat aFormat
,
302 const Maybe
<AnimationParams
>& aAnimParams
) {
303 mCurrentFrame
= AllocateFrameInternal(aOutputSize
, aFormat
, aAnimParams
,
304 std::move(mCurrentFrame
));
307 mHasFrameToTake
= true;
309 // Gather the raw pointers the decoders will use.
310 mCurrentFrame
->GetImageData(&mImageData
, &mImageDataLength
);
312 // We should now be on |aFrameNum|. (Note that we're comparing the frame
313 // number, which is zero-based, with the frame count, which is one-based.)
314 MOZ_ASSERT_IF(aAnimParams
, aAnimParams
->mFrameNum
+ 1 == mFrameCount
);
316 // If we're past the first frame, PostIsAnimated() should've been called.
317 MOZ_ASSERT_IF(mFrameCount
> 1, HasAnimation());
319 // Update our state to reflect the new frame.
320 MOZ_ASSERT(!mInFrame
, "Starting new frame but not done with old one!");
324 return mCurrentFrame
? NS_OK
: NS_ERROR_FAILURE
;
327 RawAccessFrameRef
Decoder::AllocateFrameInternal(
328 const gfx::IntSize
& aOutputSize
, SurfaceFormat aFormat
,
329 const Maybe
<AnimationParams
>& aAnimParams
,
330 RawAccessFrameRef
&& aPreviousFrame
) {
332 return RawAccessFrameRef();
335 uint32_t frameNum
= aAnimParams
? aAnimParams
->mFrameNum
: 0;
336 if (frameNum
!= mFrameCount
) {
337 MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
338 return RawAccessFrameRef();
341 if (aOutputSize
.width
<= 0 || aOutputSize
.height
<= 0) {
342 NS_WARNING("Trying to add frame with zero or negative size");
343 return RawAccessFrameRef();
347 if (aPreviousFrame
->GetDisposalMethod() !=
348 DisposalMethod::RESTORE_PREVIOUS
) {
349 // If the new restore frame is the direct previous frame, then we know
350 // the dirty rect is composed only of the current frame's blend rect and
351 // the restore frame's clear rect (if applicable) which are handled in
353 mRestoreFrame
= std::move(aPreviousFrame
);
354 mRestoreDirtyRect
.SetBox(0, 0, 0, 0);
356 // We only need the previous frame's dirty rect, because while there may
357 // have been several frames between us and mRestoreFrame, the only areas
358 // that changed are the restore frame's clear rect, the current frame
359 // blending rect, and the previous frame's blending rect. All else is
360 // forgotten due to us restoring the same frame again.
361 mRestoreDirtyRect
= aPreviousFrame
->GetBoundedBlendRect();
365 RawAccessFrameRef ref
;
367 // If we have a frame recycler, it must be for an animated image producing
368 // full frames. If the higher layers are discarding frames because of the
369 // memory footprint, then the recycler will allow us to reuse the buffers.
370 // Each frame should be the same size and have mostly the same properties.
371 if (mFrameRecycler
) {
372 MOZ_ASSERT(aAnimParams
);
374 ref
= mFrameRecycler
->RecycleFrame(mRecycleRect
);
376 // If the recycled frame is actually the current restore frame, we cannot
377 // use it. If the next restore frame is the new frame we are creating, in
378 // theory we could reuse it, but we would need to store the restore frame
379 // animation parameters elsewhere. For now we just drop it.
380 bool blocked
= ref
.get() == mRestoreFrame
.get();
382 blocked
= NS_FAILED(ref
->InitForDecoderRecycle(aAnimParams
.ref()));
391 // Either the recycler had nothing to give us, or we don't have a recycler.
392 // Produce a new frame to store the data.
394 // There is no underlying data to reuse, so reset the recycle rect to be
395 // the full frame, to ensure the restore frame is fully copied.
396 mRecycleRect
= IntRect(IntPoint(0, 0), aOutputSize
);
398 bool nonPremult
= bool(mSurfaceFlags
& SurfaceFlags::NO_PREMULTIPLY_ALPHA
);
399 auto frame
= MakeNotNull
<RefPtr
<imgFrame
>>();
400 if (NS_FAILED(frame
->InitForDecoder(aOutputSize
, aFormat
, nonPremult
,
401 aAnimParams
, bool(mFrameRecycler
)))) {
402 NS_WARNING("imgFrame::Init should succeed");
403 return RawAccessFrameRef();
406 ref
= frame
->RawAccessRef();
409 return RawAccessFrameRef();
419 * Hook stubs. Override these as necessary in decoder implementations.
422 nsresult
Decoder::InitInternal() { return NS_OK
; }
423 nsresult
Decoder::BeforeFinishInternal() { return NS_OK
; }
424 nsresult
Decoder::FinishInternal() { return NS_OK
; }
426 nsresult
Decoder::FinishWithErrorInternal() {
427 MOZ_ASSERT(!mInFrame
);
432 * Progress Notifications
435 void Decoder::PostSize(int32_t aWidth
, int32_t aHeight
,
436 Orientation aOrientation
, Resolution aResolution
) {
438 MOZ_ASSERT(aWidth
>= 0, "Width can't be negative!");
439 MOZ_ASSERT(aHeight
>= 0, "Height can't be negative!");
441 // Set our intrinsic size.
442 mImageMetadata
.SetSize(aWidth
, aHeight
, aOrientation
, aResolution
);
444 // Verify it is the expected size, if given. Note that this is only used by
445 // the ICO decoder for embedded image types, so only its subdecoders are
446 // required to handle failures in PostSize.
447 if (!IsExpectedSize()) {
452 // Set our output size if it's not already set.
454 mOutputSize
= Some(mImageMetadata
.GetSize());
457 MOZ_ASSERT(mOutputSize
->width
<= mImageMetadata
.GetSize().width
&&
458 mOutputSize
->height
<= mImageMetadata
.GetSize().height
,
459 "Output size will result in upscaling");
461 // Record this notification.
462 mProgress
|= FLAG_SIZE_AVAILABLE
;
465 void Decoder::PostHasTransparency() { mProgress
|= FLAG_HAS_TRANSPARENCY
; }
467 void Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout
) {
468 mProgress
|= FLAG_IS_ANIMATED
;
469 mImageMetadata
.SetHasAnimation();
470 mImageMetadata
.SetFirstFrameTimeout(aFirstFrameTimeout
);
473 void Decoder::PostFrameCount(uint32_t aFrameCount
) {
474 mImageMetadata
.SetFrameCount(aFrameCount
);
477 void Decoder::PostFrameStop(Opacity aFrameOpacity
) {
478 // We should be mid-frame
479 MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
480 MOZ_ASSERT(mInFrame
, "Stopping frame when we didn't start one");
481 MOZ_ASSERT(mCurrentFrame
, "Stopping frame when we don't have one");
485 mFinishedNewFrame
= true;
487 mCurrentFrame
->Finish(
488 aFrameOpacity
, mFinalizeFrames
,
489 /* aOrientationSwapsWidthAndHeight = */ mImageMetadata
.HasOrientation() &&
490 mImageMetadata
.GetOrientation().SwapsWidthAndHeight());
492 mProgress
|= FLAG_FRAME_COMPLETE
;
494 mLoopLength
+= mCurrentFrame
->GetTimeout();
496 if (mFrameCount
== 1) {
497 // If we're not sending partial invalidations, then we send an invalidation
498 // here when the first frame is complete.
499 if (!ShouldSendPartialInvalidations()) {
500 mInvalidRect
.UnionRect(mInvalidRect
,
501 OrientedIntRect(OrientedIntPoint(), Size()));
504 // If we dispose of the first frame by clearing it, then the first frame's
505 // refresh area is all of itself. RESTORE_PREVIOUS is invalid (assumed to
506 // be DISPOSE_CLEAR).
507 switch (mCurrentFrame
->GetDisposalMethod()) {
509 MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
510 case DisposalMethod::CLEAR
:
511 case DisposalMethod::CLEAR_ALL
:
512 case DisposalMethod::RESTORE_PREVIOUS
:
513 mFirstFrameRefreshArea
= IntRect(IntPoint(), Size().ToUnknownSize());
515 case DisposalMethod::KEEP
:
516 case DisposalMethod::NOT_SPECIFIED
:
520 // Some GIFs are huge but only have a small area that they animate. We only
521 // need to refresh that small area when frame 0 comes around again.
522 mFirstFrameRefreshArea
.UnionRect(mFirstFrameRefreshArea
,
523 mCurrentFrame
->GetBoundedBlendRect());
527 void Decoder::PostInvalidation(const OrientedIntRect
& aRect
,
528 const Maybe
<OrientedIntRect
>& aRectAtOutputSize
530 // We should be mid-frame
531 MOZ_ASSERT(mInFrame
, "Can't invalidate when not mid-frame!");
532 MOZ_ASSERT(mCurrentFrame
, "Can't invalidate when not mid-frame!");
534 // Record this invalidation, unless we're not sending partial invalidations
535 // or we're past the first frame.
536 if (ShouldSendPartialInvalidations() && mFrameCount
== 1) {
537 mInvalidRect
.UnionRect(mInvalidRect
, aRect
);
538 mCurrentFrame
->ImageUpdated(
539 aRectAtOutputSize
.valueOr(aRect
).ToUnknownRect());
543 void Decoder::PostLoopCount(int32_t aLoopCount
) {
544 mImageMetadata
.SetLoopCount(aLoopCount
);
547 void Decoder::PostDecodeDone() {
548 MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
549 MOZ_ASSERT(!mInFrame
, "Can't be done decoding if we're mid-frame!");
550 MOZ_ASSERT(!mDecodeDone
, "Decode already done!");
553 // Some metadata that we track should take into account every frame in the
554 // image. If this is a first-frame-only decode, our accumulated loop length
555 // and first frame refresh area only includes the first frame, so it's not
556 // correct and we don't record it.
557 if (!IsFirstFrameDecode()) {
558 mImageMetadata
.SetLoopLength(mLoopLength
);
559 mImageMetadata
.SetFirstFrameRefreshArea(mFirstFrameRefreshArea
);
562 mProgress
|= FLAG_DECODE_COMPLETE
;
565 void Decoder::PostError() {
569 MOZ_ASSERT(mCurrentFrame
);
570 MOZ_ASSERT(mFrameCount
> 0);
571 mCurrentFrame
->Abort();
574 mHasFrameToTake
= false;
579 } // namespace mozilla