Bug 1457047 [wpt PR 10645] - Update webidl2.js to v10.2.1, a=testonly
[gecko.git] / image / Decoder.cpp
blob626840b2ef6fec370cc9f17d46fa933e7ce8a96d
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 "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::IntSize;
21 using mozilla::gfx::IntRect;
22 using mozilla::gfx::SurfaceFormat;
24 namespace mozilla {
25 namespace image {
27 class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final
29 public:
30 explicit AutoRecordDecoderTelemetry(Decoder* aDecoder)
31 : mDecoder(aDecoder)
33 MOZ_ASSERT(mDecoder);
35 // Begin recording telemetry data.
36 mStartTime = TimeStamp::Now();
39 ~AutoRecordDecoderTelemetry()
41 // Finish telemetry.
42 mDecoder->mDecodeTime += (TimeStamp::Now() - mStartTime);
45 private:
46 Decoder* mDecoder;
47 TimeStamp mStartTime;
50 Decoder::Decoder(RasterImage* aImage)
51 : mImageData(nullptr)
52 , mImageDataLength(0)
53 , mColormap(nullptr)
54 , mColormapSize(0)
55 , mImage(aImage)
56 , mProgress(NoProgress)
57 , mFrameCount(0)
58 , mLoopLength(FrameTimeout::Zero())
59 , mDecoderFlags(DefaultDecoderFlags())
60 , mSurfaceFlags(DefaultSurfaceFlags())
61 , mInitialized(false)
62 , mMetadataDecode(false)
63 , mHaveExplicitOutputSize(false)
64 , mInFrame(false)
65 , mFinishedNewFrame(false)
66 , mHasFrameToTake(false)
67 , mReachedTerminalState(false)
68 , mDecodeDone(false)
69 , mError(false)
70 , mShouldReportError(false)
71 , mFinalizeFrames(true)
72 { }
74 Decoder::~Decoder()
76 MOZ_ASSERT(mProgress == NoProgress || !mImage,
77 "Destroying Decoder without taking all its progress changes");
78 MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage,
79 "Destroying Decoder without taking all its invalidations");
80 mInitialized = false;
82 if (mImage && !NS_IsMainThread()) {
83 // Dispatch mImage to main thread to prevent it from being destructed by the
84 // decode thread.
85 NS_ReleaseOnMainThreadSystemGroup(mImage.forget());
90 * Common implementation of the decoder interface.
93 nsresult
94 Decoder::Init()
96 // No re-initializing
97 MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
99 // All decoders must have a SourceBufferIterator.
100 MOZ_ASSERT(mIterator);
102 // Metadata decoders must not set an output size.
103 MOZ_ASSERT_IF(mMetadataDecode, !mHaveExplicitOutputSize);
105 // All decoders must be anonymous except for metadata decoders.
106 // XXX(seth): Soon that exception will be removed.
107 MOZ_ASSERT_IF(mImage, IsMetadataDecode());
109 // Implementation-specific initialization.
110 nsresult rv = InitInternal();
112 mInitialized = true;
114 return rv;
117 LexerResult
118 Decoder::Decode(IResumable* aOnResume /* = nullptr */)
120 MOZ_ASSERT(mInitialized, "Should be initialized here");
121 MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
123 // If we're already done, don't attempt to keep decoding.
124 if (GetDecodeDone()) {
125 return LexerResult(HasError() ? TerminalState::FAILURE
126 : TerminalState::SUCCESS);
129 LexerResult lexerResult(TerminalState::FAILURE);
131 AUTO_PROFILER_LABEL("Decoder::Decode", GRAPHICS);
132 AutoRecordDecoderTelemetry telemetry(this);
134 lexerResult = DoDecode(*mIterator, aOnResume);
137 if (lexerResult.is<Yield>()) {
138 // We either need more data to continue (in which case either @aOnResume or
139 // the caller will reschedule us to run again later), or the decoder is
140 // yielding to allow the caller access to some intermediate output.
141 return lexerResult;
144 // We reached a terminal state; we're now done decoding.
145 MOZ_ASSERT(lexerResult.is<TerminalState>());
146 mReachedTerminalState = true;
148 // If decoding failed, record that fact.
149 if (lexerResult.as<TerminalState>() == TerminalState::FAILURE) {
150 PostError();
153 // Perform final cleanup.
154 CompleteDecode();
156 return LexerResult(HasError() ? TerminalState::FAILURE
157 : TerminalState::SUCCESS);
160 LexerResult
161 Decoder::TerminateFailure()
163 PostError();
165 // Perform final cleanup if need be.
166 if (!mReachedTerminalState) {
167 mReachedTerminalState = true;
168 CompleteDecode();
171 return LexerResult(TerminalState::FAILURE);
174 bool
175 Decoder::ShouldSyncDecode(size_t aByteLimit)
177 MOZ_ASSERT(aByteLimit > 0);
178 MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
180 return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
183 void
184 Decoder::CompleteDecode()
186 // Implementation-specific finalization.
187 nsresult rv = BeforeFinishInternal();
188 if (NS_FAILED(rv)) {
189 PostError();
192 rv = HasError() ? FinishWithErrorInternal()
193 : FinishInternal();
194 if (NS_FAILED(rv)) {
195 PostError();
198 if (IsMetadataDecode()) {
199 // If this was a metadata decode and we never got a size, the decode failed.
200 if (!HasSize()) {
201 PostError();
203 return;
206 // If the implementation left us mid-frame, finish that up. Note that it may
207 // have left us transparent.
208 if (mInFrame) {
209 PostHasTransparency();
210 PostFrameStop();
213 // If PostDecodeDone() has not been called, we may need to send teardown
214 // notifications if it is unrecoverable.
215 if (!mDecodeDone) {
216 // We should always report an error to the console in this case.
217 mShouldReportError = true;
219 if (GetCompleteFrameCount() > 0) {
220 // We're usable if we have at least one complete frame, so do exactly
221 // what we should have when the decoder completed.
222 PostHasTransparency();
223 PostDecodeDone();
224 } else {
225 // We're not usable. Record some final progress indicating the error.
226 mProgress |= FLAG_DECODE_COMPLETE | FLAG_HAS_ERROR;
230 if (mDecodeDone) {
231 MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
233 // If this image wasn't animated and isn't a transient image, mark its frame
234 // as optimizable. We don't support optimizing animated images and
235 // optimizing transient images isn't worth it.
236 if (!HasAnimation() &&
237 !(mDecoderFlags & DecoderFlags::IMAGE_IS_TRANSIENT) &&
238 mCurrentFrame) {
239 mCurrentFrame->SetOptimizable();
244 void
245 Decoder::SetOutputSize(const gfx::IntSize& aSize)
247 mOutputSize = Some(aSize);
248 mHaveExplicitOutputSize = true;
251 Maybe<gfx::IntSize>
252 Decoder::ExplicitOutputSize() const
254 MOZ_ASSERT_IF(mHaveExplicitOutputSize, mOutputSize);
255 return mHaveExplicitOutputSize ? mOutputSize : Nothing();
258 Maybe<uint32_t>
259 Decoder::TakeCompleteFrameCount()
261 const bool finishedNewFrame = mFinishedNewFrame;
262 mFinishedNewFrame = false;
263 return finishedNewFrame ? Some(GetCompleteFrameCount()) : Nothing();
266 DecoderFinalStatus
267 Decoder::FinalStatus() const
269 return DecoderFinalStatus(IsMetadataDecode(),
270 GetDecodeDone(),
271 HasError(),
272 ShouldReportError());
275 DecoderTelemetry
276 Decoder::Telemetry() const
278 MOZ_ASSERT(mIterator);
279 return DecoderTelemetry(SpeedHistogram(),
280 mIterator->ByteCount(),
281 mIterator->ChunkCount(),
282 mDecodeTime);
285 nsresult
286 Decoder::AllocateFrame(uint32_t aFrameNum,
287 const gfx::IntSize& aOutputSize,
288 const gfx::IntRect& aFrameRect,
289 gfx::SurfaceFormat aFormat,
290 uint8_t aPaletteDepth)
292 mCurrentFrame = AllocateFrameInternal(aFrameNum, aOutputSize, aFrameRect,
293 aFormat, aPaletteDepth,
294 mCurrentFrame.get());
296 if (mCurrentFrame) {
297 mHasFrameToTake = true;
299 // Gather the raw pointers the decoders will use.
300 mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
301 mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
303 // We should now be on |aFrameNum|. (Note that we're comparing the frame
304 // number, which is zero-based, with the frame count, which is one-based.)
305 MOZ_ASSERT(aFrameNum + 1 == mFrameCount);
307 // If we're past the first frame, PostIsAnimated() should've been called.
308 MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
310 // Update our state to reflect the new frame.
311 MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
312 mInFrame = true;
315 return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
318 RawAccessFrameRef
319 Decoder::AllocateFrameInternal(uint32_t aFrameNum,
320 const gfx::IntSize& aOutputSize,
321 const gfx::IntRect& aFrameRect,
322 SurfaceFormat aFormat,
323 uint8_t aPaletteDepth,
324 imgFrame* aPreviousFrame)
326 if (HasError()) {
327 return RawAccessFrameRef();
330 if (aFrameNum != mFrameCount) {
331 MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
332 return RawAccessFrameRef();
335 if (aOutputSize.width <= 0 || aOutputSize.height <= 0 ||
336 aFrameRect.Width() <= 0 || aFrameRect.Height() <= 0) {
337 NS_WARNING("Trying to add frame with zero or negative size");
338 return RawAccessFrameRef();
341 auto frame = MakeNotNull<RefPtr<imgFrame>>();
342 bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
343 if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFrameRect, aFormat,
344 aPaletteDepth, nonPremult,
345 aFrameNum > 0))) {
346 NS_WARNING("imgFrame::Init should succeed");
347 return RawAccessFrameRef();
350 RawAccessFrameRef ref = frame->RawAccessRef();
351 if (!ref) {
352 frame->Abort();
353 return RawAccessFrameRef();
356 if (aFrameNum == 1) {
357 MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
358 aPreviousFrame->SetRawAccessOnly();
360 // If we dispose of the first frame by clearing it, then the first frame's
361 // refresh area is all of itself.
362 // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
363 AnimationData previousFrameData = aPreviousFrame->GetAnimationData();
364 if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR ||
365 previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL ||
366 previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
367 mFirstFrameRefreshArea = previousFrameData.mRect;
371 if (aFrameNum > 0) {
372 ref->SetRawAccessOnly();
374 // Some GIFs are huge but only have a small area that they animate. We only
375 // need to refresh that small area when frame 0 comes around again.
376 mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, frame->GetRect());
379 mFrameCount++;
381 return ref;
385 * Hook stubs. Override these as necessary in decoder implementations.
388 nsresult Decoder::InitInternal() { return NS_OK; }
389 nsresult Decoder::BeforeFinishInternal() { return NS_OK; }
390 nsresult Decoder::FinishInternal() { return NS_OK; }
392 nsresult Decoder::FinishWithErrorInternal()
394 MOZ_ASSERT(!mInFrame);
395 return NS_OK;
399 * Progress Notifications
402 void
403 Decoder::PostSize(int32_t aWidth,
404 int32_t aHeight,
405 Orientation aOrientation /* = Orientation()*/)
407 // Validate.
408 MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
409 MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
411 // Set our intrinsic size.
412 mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
414 // Verify it is the expected size, if given. Note that this is only used by
415 // the ICO decoder for embedded image types, so only its subdecoders are
416 // required to handle failures in PostSize.
417 if (!IsExpectedSize()) {
418 PostError();
419 return;
422 // Set our output size if it's not already set.
423 if (!mOutputSize) {
424 mOutputSize = Some(IntSize(aWidth, aHeight));
427 MOZ_ASSERT(mOutputSize->width <= aWidth && mOutputSize->height <= aHeight,
428 "Output size will result in upscaling");
430 // Create a downscaler if we need to downscale. This is used by legacy
431 // decoders that haven't been converted to use SurfacePipe yet.
432 // XXX(seth): Obviously, we'll remove this once all decoders use SurfacePipe.
433 if (mOutputSize->width < aWidth || mOutputSize->height < aHeight) {
434 mDownscaler.emplace(*mOutputSize);
437 // Record this notification.
438 mProgress |= FLAG_SIZE_AVAILABLE;
441 void
442 Decoder::PostHasTransparency()
444 mProgress |= FLAG_HAS_TRANSPARENCY;
447 void
448 Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout)
450 mProgress |= FLAG_IS_ANIMATED;
451 mImageMetadata.SetHasAnimation();
452 mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
455 void
456 Decoder::PostFrameStop(Opacity aFrameOpacity
457 /* = Opacity::SOME_TRANSPARENCY */,
458 DisposalMethod aDisposalMethod
459 /* = DisposalMethod::KEEP */,
460 FrameTimeout aTimeout /* = FrameTimeout::Forever() */,
461 BlendMethod aBlendMethod /* = BlendMethod::OVER */,
462 const Maybe<nsIntRect>& aBlendRect /* = Nothing() */)
464 // We should be mid-frame
465 MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
466 MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
467 MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
469 // Update our state.
470 mInFrame = false;
471 mFinishedNewFrame = true;
473 mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout,
474 aBlendMethod, aBlendRect, mFinalizeFrames);
476 mProgress |= FLAG_FRAME_COMPLETE;
478 mLoopLength += aTimeout;
480 // If we're not sending partial invalidations, then we send an invalidation
481 // here when the first frame is complete.
482 if (!ShouldSendPartialInvalidations() && mFrameCount == 1) {
483 mInvalidRect.UnionRect(mInvalidRect,
484 IntRect(IntPoint(), Size()));
488 void
489 Decoder::PostInvalidation(const gfx::IntRect& aRect,
490 const Maybe<gfx::IntRect>& aRectAtOutputSize
491 /* = Nothing() */)
493 // We should be mid-frame
494 MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!");
495 MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!");
497 // Record this invalidation, unless we're not sending partial invalidations
498 // or we're past the first frame.
499 if (ShouldSendPartialInvalidations() && mFrameCount == 1) {
500 mInvalidRect.UnionRect(mInvalidRect, aRect);
501 mCurrentFrame->ImageUpdated(aRectAtOutputSize.valueOr(aRect));
505 void
506 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
508 MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
509 MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
510 MOZ_ASSERT(!mDecodeDone, "Decode already done!");
511 mDecodeDone = true;
513 mImageMetadata.SetLoopCount(aLoopCount);
515 // Some metadata that we track should take into account every frame in the
516 // image. If this is a first-frame-only decode, our accumulated loop length
517 // and first frame refresh area only includes the first frame, so it's not
518 // correct and we don't record it.
519 if (!IsFirstFrameDecode()) {
520 mImageMetadata.SetLoopLength(mLoopLength);
521 mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea);
524 mProgress |= FLAG_DECODE_COMPLETE;
527 void
528 Decoder::PostError()
530 mError = true;
532 if (mInFrame) {
533 MOZ_ASSERT(mCurrentFrame);
534 MOZ_ASSERT(mFrameCount > 0);
535 mCurrentFrame->Abort();
536 mInFrame = false;
537 --mFrameCount;
538 mHasFrameToTake = false;
542 } // namespace image
543 } // namespace mozilla