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 "GeckoProfiler.h"
10 #include "IDecodingTask.h"
11 #include "ISurfaceProvider.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/gfx/Point.h"
14 #include "mozilla/Telemetry.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsProxyRelease.h"
17 #include "nsServiceManagerUtils.h"
19 using mozilla::gfx::IntPoint
;
20 using mozilla::gfx::IntRect
;
21 using mozilla::gfx::IntSize
;
22 using mozilla::gfx::SurfaceFormat
;
27 class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final
{
29 explicit AutoRecordDecoderTelemetry(Decoder
* aDecoder
) : mDecoder(aDecoder
) {
32 // Begin recording telemetry data.
33 mStartTime
= TimeStamp::Now();
36 ~AutoRecordDecoderTelemetry() {
38 mDecoder
->mDecodeTime
+= (TimeStamp::Now() - mStartTime
);
46 Decoder::Decoder(RasterImage
* aImage
)
47 : mInProfile(nullptr),
52 mFrameRecycler(nullptr),
53 mProgress(NoProgress
),
55 mLoopLength(FrameTimeout::Zero()),
56 mDecoderFlags(DefaultDecoderFlags()),
57 mSurfaceFlags(DefaultSurfaceFlags()),
59 mMetadataDecode(false),
60 mHaveExplicitOutputSize(false),
62 mFinishedNewFrame(false),
63 mHasFrameToTake(false),
64 mReachedTerminalState(false),
67 mShouldReportError(false),
68 mFinalizeFrames(true) {}
71 MOZ_ASSERT(mProgress
== NoProgress
|| !mImage
,
72 "Destroying Decoder without taking all its progress changes");
73 MOZ_ASSERT(mInvalidRect
.IsEmpty() || !mImage
,
74 "Destroying Decoder without taking all its invalidations");
78 // mTransform belongs to us only if mInProfile is non-null
80 qcms_transform_release(mTransform
);
82 qcms_profile_release(mInProfile
);
85 if (mImage
&& !NS_IsMainThread()) {
86 // Dispatch mImage to main thread to prevent it from being destructed by the
88 NS_ReleaseOnMainThreadSystemGroup(mImage
.forget());
93 * Common implementation of the decoder interface.
96 nsresult
Decoder::Init() {
98 MOZ_ASSERT(!mInitialized
, "Can't re-initialize a decoder!");
100 // All decoders must have a SourceBufferIterator.
101 MOZ_ASSERT(mIterator
);
103 // Metadata decoders must not set an output size.
104 MOZ_ASSERT_IF(mMetadataDecode
, !mHaveExplicitOutputSize
);
106 // All decoders must be anonymous except for metadata decoders.
107 // XXX(seth): Soon that exception will be removed.
108 MOZ_ASSERT_IF(mImage
, IsMetadataDecode());
110 // Implementation-specific initialization.
111 nsresult rv
= InitInternal();
118 LexerResult
Decoder::Decode(IResumable
* aOnResume
/* = nullptr */) {
119 MOZ_ASSERT(mInitialized
, "Should be initialized here");
120 MOZ_ASSERT(mIterator
, "Should have a SourceBufferIterator");
122 // If we're already done, don't attempt to keep decoding.
123 if (GetDecodeDone()) {
124 return LexerResult(HasError() ? TerminalState::FAILURE
125 : TerminalState::SUCCESS
);
128 LexerResult
lexerResult(TerminalState::FAILURE
);
130 AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_ImageDecoding
);
131 AutoRecordDecoderTelemetry
telemetry(this);
133 lexerResult
= DoDecode(*mIterator
, aOnResume
);
136 if (lexerResult
.is
<Yield
>()) {
137 // We either need more data to continue (in which case either @aOnResume or
138 // the caller will reschedule us to run again later), or the decoder is
139 // yielding to allow the caller access to some intermediate output.
143 // We reached a terminal state; we're now done decoding.
144 MOZ_ASSERT(lexerResult
.is
<TerminalState
>());
145 mReachedTerminalState
= true;
147 // If decoding failed, record that fact.
148 if (lexerResult
.as
<TerminalState
>() == TerminalState::FAILURE
) {
152 // Perform final cleanup.
155 return LexerResult(HasError() ? TerminalState::FAILURE
156 : TerminalState::SUCCESS
);
159 LexerResult
Decoder::TerminateFailure() {
162 // Perform final cleanup if need be.
163 if (!mReachedTerminalState
) {
164 mReachedTerminalState
= true;
168 return LexerResult(TerminalState::FAILURE
);
171 bool Decoder::ShouldSyncDecode(size_t aByteLimit
) {
172 MOZ_ASSERT(aByteLimit
> 0);
173 MOZ_ASSERT(mIterator
, "Should have a SourceBufferIterator");
175 return mIterator
->RemainingBytesIsNoMoreThan(aByteLimit
);
178 void Decoder::CompleteDecode() {
179 // Implementation-specific finalization.
180 nsresult rv
= BeforeFinishInternal();
185 rv
= HasError() ? FinishWithErrorInternal() : FinishInternal();
190 if (IsMetadataDecode()) {
191 // If this was a metadata decode and we never got a size, the decode failed.
198 // If the implementation left us mid-frame, finish that up. Note that it may
199 // have left us transparent.
201 PostHasTransparency();
205 // If PostDecodeDone() has not been called, we may need to send teardown
206 // notifications if it is unrecoverable.
208 // We should always report an error to the console in this case.
209 mShouldReportError
= true;
211 if (GetCompleteFrameCount() > 0) {
212 // We're usable if we have at least one complete frame, so do exactly
213 // what we should have when the decoder completed.
214 PostHasTransparency();
217 // We're not usable. Record some final progress indicating the error.
218 mProgress
|= FLAG_DECODE_COMPLETE
| FLAG_HAS_ERROR
;
223 MOZ_ASSERT(HasError() || mCurrentFrame
, "Should have an error or a frame");
225 // If this image wasn't animated and isn't a transient image, mark its frame
226 // as optimizable. We don't support optimizing animated images and
227 // optimizing transient images isn't worth it.
228 if (!HasAnimation() &&
229 !(mDecoderFlags
& DecoderFlags::IMAGE_IS_TRANSIENT
) && mCurrentFrame
) {
230 mCurrentFrame
->SetOptimizable();
235 void Decoder::SetOutputSize(const gfx::IntSize
& aSize
) {
236 mOutputSize
= Some(aSize
);
237 mHaveExplicitOutputSize
= true;
240 Maybe
<gfx::IntSize
> Decoder::ExplicitOutputSize() const {
241 MOZ_ASSERT_IF(mHaveExplicitOutputSize
, mOutputSize
);
242 return mHaveExplicitOutputSize
? mOutputSize
: Nothing();
245 Maybe
<uint32_t> Decoder::TakeCompleteFrameCount() {
246 const bool finishedNewFrame
= mFinishedNewFrame
;
247 mFinishedNewFrame
= false;
248 return finishedNewFrame
? Some(GetCompleteFrameCount()) : Nothing();
251 DecoderFinalStatus
Decoder::FinalStatus() const {
252 return DecoderFinalStatus(IsMetadataDecode(), GetDecodeDone(), HasError(),
253 ShouldReportError());
256 DecoderTelemetry
Decoder::Telemetry() const {
257 MOZ_ASSERT(mIterator
);
258 return DecoderTelemetry(SpeedHistogram(),
259 mIterator
? mIterator
->ByteCount() : 0,
260 mIterator
? mIterator
->ChunkCount() : 0, mDecodeTime
);
263 nsresult
Decoder::AllocateFrame(const gfx::IntSize
& aOutputSize
,
264 gfx::SurfaceFormat aFormat
,
265 const Maybe
<AnimationParams
>& aAnimParams
) {
266 mCurrentFrame
= AllocateFrameInternal(aOutputSize
, aFormat
, aAnimParams
,
267 std::move(mCurrentFrame
));
270 mHasFrameToTake
= true;
272 // Gather the raw pointers the decoders will use.
273 mCurrentFrame
->GetImageData(&mImageData
, &mImageDataLength
);
275 // We should now be on |aFrameNum|. (Note that we're comparing the frame
276 // number, which is zero-based, with the frame count, which is one-based.)
277 MOZ_ASSERT_IF(aAnimParams
, aAnimParams
->mFrameNum
+ 1 == mFrameCount
);
279 // If we're past the first frame, PostIsAnimated() should've been called.
280 MOZ_ASSERT_IF(mFrameCount
> 1, HasAnimation());
282 // Update our state to reflect the new frame.
283 MOZ_ASSERT(!mInFrame
, "Starting new frame but not done with old one!");
287 return mCurrentFrame
? NS_OK
: NS_ERROR_FAILURE
;
290 RawAccessFrameRef
Decoder::AllocateFrameInternal(
291 const gfx::IntSize
& aOutputSize
, SurfaceFormat aFormat
,
292 const Maybe
<AnimationParams
>& aAnimParams
,
293 RawAccessFrameRef
&& aPreviousFrame
) {
295 return RawAccessFrameRef();
298 uint32_t frameNum
= aAnimParams
? aAnimParams
->mFrameNum
: 0;
299 if (frameNum
!= mFrameCount
) {
300 MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
301 return RawAccessFrameRef();
304 if (aOutputSize
.width
<= 0 || aOutputSize
.height
<= 0) {
305 NS_WARNING("Trying to add frame with zero or negative size");
306 return RawAccessFrameRef();
310 MOZ_ASSERT(aPreviousFrame
, "Must provide a previous frame when animated");
311 aPreviousFrame
->SetRawAccessOnly();
315 if (aPreviousFrame
->GetDisposalMethod() !=
316 DisposalMethod::RESTORE_PREVIOUS
) {
317 // If the new restore frame is the direct previous frame, then we know
318 // the dirty rect is composed only of the current frame's blend rect and
319 // the restore frame's clear rect (if applicable) which are handled in
321 mRestoreFrame
= std::move(aPreviousFrame
);
322 mRestoreDirtyRect
.SetBox(0, 0, 0, 0);
324 // We only need the previous frame's dirty rect, because while there may
325 // have been several frames between us and mRestoreFrame, the only areas
326 // that changed are the restore frame's clear rect, the current frame
327 // blending rect, and the previous frame's blending rect. All else is
328 // forgotten due to us restoring the same frame again.
329 mRestoreDirtyRect
= aPreviousFrame
->GetBoundedBlendRect();
333 RawAccessFrameRef ref
;
335 // If we have a frame recycler, it must be for an animated image producing
336 // full frames. If the higher layers are discarding frames because of the
337 // memory footprint, then the recycler will allow us to reuse the buffers.
338 // Each frame should be the same size and have mostly the same properties.
339 if (mFrameRecycler
) {
340 MOZ_ASSERT(aAnimParams
);
342 ref
= mFrameRecycler
->RecycleFrame(mRecycleRect
);
344 // If the recycled frame is actually the current restore frame, we cannot
345 // use it. If the next restore frame is the new frame we are creating, in
346 // theory we could reuse it, but we would need to store the restore frame
347 // animation parameters elsewhere. For now we just drop it.
348 bool blocked
= ref
.get() == mRestoreFrame
.get();
350 blocked
= NS_FAILED(ref
->InitForDecoderRecycle(aAnimParams
.ref()));
359 // Either the recycler had nothing to give us, or we don't have a recycler.
360 // Produce a new frame to store the data.
362 // There is no underlying data to reuse, so reset the recycle rect to be
363 // the full frame, to ensure the restore frame is fully copied.
364 mRecycleRect
= IntRect(IntPoint(0, 0), aOutputSize
);
366 bool nonPremult
= bool(mSurfaceFlags
& SurfaceFlags::NO_PREMULTIPLY_ALPHA
);
367 auto frame
= MakeNotNull
<RefPtr
<imgFrame
>>();
368 if (NS_FAILED(frame
->InitForDecoder(aOutputSize
, aFormat
, nonPremult
,
369 aAnimParams
, bool(mFrameRecycler
)))) {
370 NS_WARNING("imgFrame::Init should succeed");
371 return RawAccessFrameRef();
374 ref
= frame
->RawAccessRef();
377 return RawAccessFrameRef();
381 frame
->SetRawAccessOnly();
391 * Hook stubs. Override these as necessary in decoder implementations.
394 nsresult
Decoder::InitInternal() { return NS_OK
; }
395 nsresult
Decoder::BeforeFinishInternal() { return NS_OK
; }
396 nsresult
Decoder::FinishInternal() { return NS_OK
; }
398 nsresult
Decoder::FinishWithErrorInternal() {
399 MOZ_ASSERT(!mInFrame
);
404 * Progress Notifications
407 void Decoder::PostSize(int32_t aWidth
, int32_t aHeight
,
408 Orientation aOrientation
/* = Orientation()*/) {
410 MOZ_ASSERT(aWidth
>= 0, "Width can't be negative!");
411 MOZ_ASSERT(aHeight
>= 0, "Height can't be negative!");
413 // Set our intrinsic size.
414 mImageMetadata
.SetSize(aWidth
, aHeight
, aOrientation
);
416 // Verify it is the expected size, if given. Note that this is only used by
417 // the ICO decoder for embedded image types, so only its subdecoders are
418 // required to handle failures in PostSize.
419 if (!IsExpectedSize()) {
424 // Set our output size if it's not already set.
426 mOutputSize
= Some(IntSize(aWidth
, aHeight
));
429 MOZ_ASSERT(mOutputSize
->width
<= aWidth
&& mOutputSize
->height
<= aHeight
,
430 "Output size will result in upscaling");
432 // Create a downscaler if we need to downscale. This is used by legacy
433 // decoders that haven't been converted to use SurfacePipe yet.
434 // XXX(seth): Obviously, we'll remove this once all decoders use SurfacePipe.
435 if (mOutputSize
->width
< aWidth
|| mOutputSize
->height
< aHeight
) {
436 mDownscaler
.emplace(*mOutputSize
);
439 // Record this notification.
440 mProgress
|= FLAG_SIZE_AVAILABLE
;
443 void Decoder::PostHasTransparency() { mProgress
|= FLAG_HAS_TRANSPARENCY
; }
445 void Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout
) {
446 mProgress
|= FLAG_IS_ANIMATED
;
447 mImageMetadata
.SetHasAnimation();
448 mImageMetadata
.SetFirstFrameTimeout(aFirstFrameTimeout
);
451 void Decoder::PostFrameStop(Opacity aFrameOpacity
) {
452 // We should be mid-frame
453 MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
454 MOZ_ASSERT(mInFrame
, "Stopping frame when we didn't start one");
455 MOZ_ASSERT(mCurrentFrame
, "Stopping frame when we don't have one");
459 mFinishedNewFrame
= true;
461 mCurrentFrame
->Finish(aFrameOpacity
, mFinalizeFrames
);
463 mProgress
|= FLAG_FRAME_COMPLETE
;
465 mLoopLength
+= mCurrentFrame
->GetTimeout();
467 if (mFrameCount
== 1) {
468 // If we're not sending partial invalidations, then we send an invalidation
469 // here when the first frame is complete.
470 if (!ShouldSendPartialInvalidations()) {
471 mInvalidRect
.UnionRect(mInvalidRect
, IntRect(IntPoint(), Size()));
474 // If we dispose of the first frame by clearing it, then the first frame's
475 // refresh area is all of itself. RESTORE_PREVIOUS is invalid (assumed to
476 // be DISPOSE_CLEAR).
477 switch (mCurrentFrame
->GetDisposalMethod()) {
479 MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
480 case DisposalMethod::CLEAR
:
481 case DisposalMethod::CLEAR_ALL
:
482 case DisposalMethod::RESTORE_PREVIOUS
:
483 mFirstFrameRefreshArea
= IntRect(IntPoint(), Size());
485 case DisposalMethod::KEEP
:
486 case DisposalMethod::NOT_SPECIFIED
:
490 // Some GIFs are huge but only have a small area that they animate. We only
491 // need to refresh that small area when frame 0 comes around again.
492 mFirstFrameRefreshArea
.UnionRect(mFirstFrameRefreshArea
,
493 mCurrentFrame
->GetBoundedBlendRect());
497 void Decoder::PostInvalidation(const gfx::IntRect
& aRect
,
498 const Maybe
<gfx::IntRect
>& aRectAtOutputSize
500 // We should be mid-frame
501 MOZ_ASSERT(mInFrame
, "Can't invalidate when not mid-frame!");
502 MOZ_ASSERT(mCurrentFrame
, "Can't invalidate when not mid-frame!");
504 // Record this invalidation, unless we're not sending partial invalidations
505 // or we're past the first frame.
506 if (ShouldSendPartialInvalidations() && mFrameCount
== 1) {
507 mInvalidRect
.UnionRect(mInvalidRect
, aRect
);
508 mCurrentFrame
->ImageUpdated(aRectAtOutputSize
.valueOr(aRect
));
512 void Decoder::PostDecodeDone(int32_t aLoopCount
/* = 0 */) {
513 MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
514 MOZ_ASSERT(!mInFrame
, "Can't be done decoding if we're mid-frame!");
515 MOZ_ASSERT(!mDecodeDone
, "Decode already done!");
518 mImageMetadata
.SetLoopCount(aLoopCount
);
520 // Some metadata that we track should take into account every frame in the
521 // image. If this is a first-frame-only decode, our accumulated loop length
522 // and first frame refresh area only includes the first frame, so it's not
523 // correct and we don't record it.
524 if (!IsFirstFrameDecode()) {
525 mImageMetadata
.SetLoopLength(mLoopLength
);
526 mImageMetadata
.SetFirstFrameRefreshArea(mFirstFrameRefreshArea
);
529 mProgress
|= FLAG_DECODE_COMPLETE
;
532 void Decoder::PostError() {
536 MOZ_ASSERT(mCurrentFrame
);
537 MOZ_ASSERT(mFrameCount
> 0);
538 mCurrentFrame
->Abort();
541 mHasFrameToTake
= false;
546 } // namespace mozilla