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 "Decoder.h"
7 
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"
19 
20 using mozilla::gfx::IntPoint;
21 using mozilla::gfx::IntRect;
22 using mozilla::gfx::IntSize;
23 using mozilla::gfx::SurfaceFormat;
24 
25 namespace mozilla {
26 namespace image {
27 
28 class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final {
29  public:
AutoRecordDecoderTelemetry(Decoder * aDecoder)30   explicit AutoRecordDecoderTelemetry(Decoder* aDecoder) : mDecoder(aDecoder) {
31     MOZ_ASSERT(mDecoder);
32 
33     // Begin recording telemetry data.
34     mStartTime = TimeStamp::Now();
35   }
36 
~AutoRecordDecoderTelemetry()37   ~AutoRecordDecoderTelemetry() {
38     // Finish telemetry.
39     mDecoder->mDecodeTime += (TimeStamp::Now() - mStartTime);
40   }
41 
42  private:
43   Decoder* mDecoder;
44   TimeStamp mStartTime;
45 };
46 
Decoder(RasterImage * aImage)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) {}
71 
~Decoder()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;
78 
79   if (mInProfile) {
80     // mTransform belongs to us only if mInProfile is non-null
81     if (mTransform) {
82       qcms_transform_release(mTransform);
83     }
84     qcms_profile_release(mInProfile);
85   }
86 
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());
91   }
92 }
93 
SetSurfaceFlags(SurfaceFlags aSurfaceFlags)94 void Decoder::SetSurfaceFlags(SurfaceFlags aSurfaceFlags) {
95   MOZ_ASSERT(!mInitialized);
96   mSurfaceFlags = aSurfaceFlags;
97   if (mSurfaceFlags & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
98     mCMSMode = CMSMode::Off;
99   }
100 }
101 
GetCMSOutputProfile() const102 qcms_profile* Decoder::GetCMSOutputProfile() const {
103   if (mSurfaceFlags & SurfaceFlags::TO_SRGB_COLORSPACE) {
104     return gfxPlatform::GetCMSsRGBProfile();
105   }
106   return gfxPlatform::GetCMSOutputProfile();
107 }
108 
GetCMSsRGBTransform(SurfaceFormat aFormat) const109 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.
114     return nullptr;
115   }
116   if (qcms_profile_is_sRGB(gfxPlatform::GetCMSOutputProfile())) {
117     // Device space is sRGB so we can skip color management as well.
118     return nullptr;
119   }
120 
121   switch (aFormat) {
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();
130     default:
131       MOZ_ASSERT_UNREACHABLE("Unsupported surface format!");
132       return nullptr;
133   }
134 }
135 
136 /*
137  * Common implementation of the decoder interface.
138  */
139 
Init()140 nsresult Decoder::Init() {
141   // No re-initializing
142   MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
143 
144   // All decoders must have a SourceBufferIterator.
145   MOZ_ASSERT(mIterator);
146 
147   // Metadata decoders must not set an output size.
148   MOZ_ASSERT_IF(mMetadataDecode, !mHaveExplicitOutputSize);
149 
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());
153 
154   // Implementation-specific initialization.
155   nsresult rv = InitInternal();
156 
157   mInitialized = true;
158 
159   return rv;
160 }
161 
Decode(IResumable * aOnResume)162 LexerResult Decoder::Decode(IResumable* aOnResume /* = nullptr */) {
163   MOZ_ASSERT(mInitialized, "Should be initialized here");
164   MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
165 
166   // If we're already done, don't attempt to keep decoding.
167   if (GetDecodeDone()) {
168     return LexerResult(HasError() ? TerminalState::FAILURE
169                                   : TerminalState::SUCCESS);
170   }
171 
172   LexerResult lexerResult(TerminalState::FAILURE);
173   {
174     AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(GRAPHICS_ImageDecoding);
175     AutoRecordDecoderTelemetry telemetry(this);
176 
177     lexerResult = DoDecode(*mIterator, aOnResume);
178   };
179 
180   if (lexerResult.is<Yield>()) {
181     // We either need more data to continue (in which case either @aOnResume or
182     // the caller will reschedule us to run again later), or the decoder is
183     // yielding to allow the caller access to some intermediate output.
184     return lexerResult;
185   }
186 
187   // We reached a terminal state; we're now done decoding.
188   MOZ_ASSERT(lexerResult.is<TerminalState>());
189   mReachedTerminalState = true;
190 
191   // If decoding failed, record that fact.
192   if (lexerResult.as<TerminalState>() == TerminalState::FAILURE) {
193     PostError();
194   }
195 
196   // Perform final cleanup.
197   CompleteDecode();
198 
199   return LexerResult(HasError() ? TerminalState::FAILURE
200                                 : TerminalState::SUCCESS);
201 }
202 
TerminateFailure()203 LexerResult Decoder::TerminateFailure() {
204   PostError();
205 
206   // Perform final cleanup if need be.
207   if (!mReachedTerminalState) {
208     mReachedTerminalState = true;
209     CompleteDecode();
210   }
211 
212   return LexerResult(TerminalState::FAILURE);
213 }
214 
ShouldSyncDecode(size_t aByteLimit)215 bool Decoder::ShouldSyncDecode(size_t aByteLimit) {
216   MOZ_ASSERT(aByteLimit > 0);
217   MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
218 
219   return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
220 }
221 
CompleteDecode()222 void Decoder::CompleteDecode() {
223   // Implementation-specific finalization.
224   nsresult rv = BeforeFinishInternal();
225   if (NS_FAILED(rv)) {
226     PostError();
227   }
228 
229   rv = HasError() ? FinishWithErrorInternal() : FinishInternal();
230   if (NS_FAILED(rv)) {
231     PostError();
232   }
233 
234   if (IsMetadataDecode()) {
235     // If this was a metadata decode and we never got a size, the decode failed.
236     if (!HasSize()) {
237       PostError();
238     }
239     return;
240   }
241 
242   // If the implementation left us mid-frame, finish that up. Note that it may
243   // have left us transparent.
244   if (mInFrame) {
245     PostHasTransparency();
246     PostFrameStop();
247   }
248 
249   // If PostDecodeDone() has not been called, we may need to send teardown
250   // notifications if it is unrecoverable.
251   if (mDecodeDone) {
252     MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
253   } else {
254     // We should always report an error to the console in this case.
255     mShouldReportError = true;
256 
257     if (GetCompleteFrameCount() > 0) {
258       // We're usable if we have at least one complete frame, so do exactly
259       // what we should have when the decoder completed.
260       PostHasTransparency();
261       PostDecodeDone();
262     } else {
263       // We're not usable. Record some final progress indicating the error.
264       mProgress |= FLAG_DECODE_COMPLETE | FLAG_HAS_ERROR;
265     }
266   }
267 }
268 
SetOutputSize(const OrientedIntSize & aSize)269 void Decoder::SetOutputSize(const OrientedIntSize& aSize) {
270   mOutputSize = Some(aSize);
271   mHaveExplicitOutputSize = true;
272 }
273 
ExplicitOutputSize() const274 Maybe<OrientedIntSize> Decoder::ExplicitOutputSize() const {
275   MOZ_ASSERT_IF(mHaveExplicitOutputSize, mOutputSize);
276   return mHaveExplicitOutputSize ? mOutputSize : Nothing();
277 }
278 
TakeCompleteFrameCount()279 Maybe<uint32_t> Decoder::TakeCompleteFrameCount() {
280   const bool finishedNewFrame = mFinishedNewFrame;
281   mFinishedNewFrame = false;
282   return finishedNewFrame ? Some(GetCompleteFrameCount()) : Nothing();
283 }
284 
FinalStatus() const285 DecoderFinalStatus Decoder::FinalStatus() const {
286   return DecoderFinalStatus(IsMetadataDecode(), GetDecodeDone(), HasError(),
287                             ShouldReportError());
288 }
289 
Telemetry() const290 DecoderTelemetry Decoder::Telemetry() const {
291   MOZ_ASSERT(mIterator);
292   return DecoderTelemetry(SpeedHistogram(),
293                           mIterator ? mIterator->ByteCount() : 0,
294                           mIterator ? mIterator->ChunkCount() : 0, mDecodeTime);
295 }
296 
AllocateFrame(const gfx::IntSize & aOutputSize,gfx::SurfaceFormat aFormat,const Maybe<AnimationParams> & aAnimParams)297 nsresult Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
298                                 gfx::SurfaceFormat aFormat,
299                                 const Maybe<AnimationParams>& aAnimParams) {
300   mCurrentFrame = AllocateFrameInternal(aOutputSize, aFormat, aAnimParams,
301                                         std::move(mCurrentFrame));
302 
303   if (mCurrentFrame) {
304     mHasFrameToTake = true;
305 
306     // Gather the raw pointers the decoders will use.
307     mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
308 
309     // We should now be on |aFrameNum|. (Note that we're comparing the frame
310     // number, which is zero-based, with the frame count, which is one-based.)
311     MOZ_ASSERT_IF(aAnimParams, aAnimParams->mFrameNum + 1 == mFrameCount);
312 
313     // If we're past the first frame, PostIsAnimated() should've been called.
314     MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
315 
316     // Update our state to reflect the new frame.
317     MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
318     mInFrame = true;
319   }
320 
321   return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
322 }
323 
AllocateFrameInternal(const gfx::IntSize & aOutputSize,SurfaceFormat aFormat,const Maybe<AnimationParams> & aAnimParams,RawAccessFrameRef && aPreviousFrame)324 RawAccessFrameRef Decoder::AllocateFrameInternal(
325     const gfx::IntSize& aOutputSize, SurfaceFormat aFormat,
326     const Maybe<AnimationParams>& aAnimParams,
327     RawAccessFrameRef&& aPreviousFrame) {
328   if (HasError()) {
329     return RawAccessFrameRef();
330   }
331 
332   uint32_t frameNum = aAnimParams ? aAnimParams->mFrameNum : 0;
333   if (frameNum != mFrameCount) {
334     MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
335     return RawAccessFrameRef();
336   }
337 
338   if (aOutputSize.width <= 0 || aOutputSize.height <= 0) {
339     NS_WARNING("Trying to add frame with zero or negative size");
340     return RawAccessFrameRef();
341   }
342 
343   if (frameNum > 0) {
344     if (aPreviousFrame->GetDisposalMethod() !=
345         DisposalMethod::RESTORE_PREVIOUS) {
346       // If the new restore frame is the direct previous frame, then we know
347       // the dirty rect is composed only of the current frame's blend rect and
348       // the restore frame's clear rect (if applicable) which are handled in
349       // filters.
350       mRestoreFrame = std::move(aPreviousFrame);
351       mRestoreDirtyRect.SetBox(0, 0, 0, 0);
352     } else {
353       // We only need the previous frame's dirty rect, because while there may
354       // have been several frames between us and mRestoreFrame, the only areas
355       // that changed are the restore frame's clear rect, the current frame
356       // blending rect, and the previous frame's blending rect. All else is
357       // forgotten due to us restoring the same frame again.
358       mRestoreDirtyRect = aPreviousFrame->GetBoundedBlendRect();
359     }
360   }
361 
362   RawAccessFrameRef ref;
363 
364   // If we have a frame recycler, it must be for an animated image producing
365   // full frames. If the higher layers are discarding frames because of the
366   // memory footprint, then the recycler will allow us to reuse the buffers.
367   // Each frame should be the same size and have mostly the same properties.
368   if (mFrameRecycler) {
369     MOZ_ASSERT(aAnimParams);
370 
371     ref = mFrameRecycler->RecycleFrame(mRecycleRect);
372     if (ref) {
373       // If the recycled frame is actually the current restore frame, we cannot
374       // use it. If the next restore frame is the new frame we are creating, in
375       // theory we could reuse it, but we would need to store the restore frame
376       // animation parameters elsewhere. For now we just drop it.
377       bool blocked = ref.get() == mRestoreFrame.get();
378       if (!blocked) {
379         blocked = NS_FAILED(ref->InitForDecoderRecycle(aAnimParams.ref()));
380       }
381 
382       if (blocked) {
383         ref.reset();
384       }
385     }
386   }
387 
388   // Either the recycler had nothing to give us, or we don't have a recycler.
389   // Produce a new frame to store the data.
390   if (!ref) {
391     // There is no underlying data to reuse, so reset the recycle rect to be
392     // the full frame, to ensure the restore frame is fully copied.
393     mRecycleRect = IntRect(IntPoint(0, 0), aOutputSize);
394 
395     bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
396     auto frame = MakeNotNull<RefPtr<imgFrame>>();
397     if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFormat, nonPremult,
398                                         aAnimParams, bool(mFrameRecycler)))) {
399       NS_WARNING("imgFrame::Init should succeed");
400       return RawAccessFrameRef();
401     }
402 
403     ref = frame->RawAccessRef();
404     if (!ref) {
405       frame->Abort();
406       return RawAccessFrameRef();
407     }
408   }
409 
410   mFrameCount++;
411 
412   return ref;
413 }
414 
415 /*
416  * Hook stubs. Override these as necessary in decoder implementations.
417  */
418 
InitInternal()419 nsresult Decoder::InitInternal() { return NS_OK; }
BeforeFinishInternal()420 nsresult Decoder::BeforeFinishInternal() { return NS_OK; }
FinishInternal()421 nsresult Decoder::FinishInternal() { return NS_OK; }
422 
FinishWithErrorInternal()423 nsresult Decoder::FinishWithErrorInternal() {
424   MOZ_ASSERT(!mInFrame);
425   return NS_OK;
426 }
427 
428 /*
429  * Progress Notifications
430  */
431 
PostSize(int32_t aWidth,int32_t aHeight,Orientation aOrientation,Resolution aResolution)432 void Decoder::PostSize(int32_t aWidth, int32_t aHeight,
433                        Orientation aOrientation, Resolution aResolution) {
434   // Validate.
435   MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
436   MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
437 
438   // Set our intrinsic size.
439   mImageMetadata.SetSize(aWidth, aHeight, aOrientation, aResolution);
440 
441   // Verify it is the expected size, if given. Note that this is only used by
442   // the ICO decoder for embedded image types, so only its subdecoders are
443   // required to handle failures in PostSize.
444   if (!IsExpectedSize()) {
445     PostError();
446     return;
447   }
448 
449   // Set our output size if it's not already set.
450   if (!mOutputSize) {
451     mOutputSize = Some(mImageMetadata.GetSize());
452   }
453 
454   MOZ_ASSERT(mOutputSize->width <= mImageMetadata.GetSize().width &&
455                  mOutputSize->height <= mImageMetadata.GetSize().height,
456              "Output size will result in upscaling");
457 
458   // Record this notification.
459   mProgress |= FLAG_SIZE_AVAILABLE;
460 }
461 
PostHasTransparency()462 void Decoder::PostHasTransparency() { mProgress |= FLAG_HAS_TRANSPARENCY; }
463 
PostIsAnimated(FrameTimeout aFirstFrameTimeout)464 void Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout) {
465   mProgress |= FLAG_IS_ANIMATED;
466   mImageMetadata.SetHasAnimation();
467   mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
468 }
469 
PostFrameStop(Opacity aFrameOpacity)470 void Decoder::PostFrameStop(Opacity aFrameOpacity) {
471   // We should be mid-frame
472   MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
473   MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
474   MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
475 
476   // Update our state.
477   mInFrame = false;
478   mFinishedNewFrame = true;
479 
480   mCurrentFrame->Finish(aFrameOpacity, mFinalizeFrames);
481 
482   mProgress |= FLAG_FRAME_COMPLETE;
483 
484   mLoopLength += mCurrentFrame->GetTimeout();
485 
486   if (mFrameCount == 1) {
487     // If we're not sending partial invalidations, then we send an invalidation
488     // here when the first frame is complete.
489     if (!ShouldSendPartialInvalidations()) {
490       mInvalidRect.UnionRect(mInvalidRect,
491                              OrientedIntRect(OrientedIntPoint(), Size()));
492     }
493 
494     // If we dispose of the first frame by clearing it, then the first frame's
495     // refresh area is all of itself. RESTORE_PREVIOUS is invalid (assumed to
496     // be DISPOSE_CLEAR).
497     switch (mCurrentFrame->GetDisposalMethod()) {
498       default:
499         MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
500       case DisposalMethod::CLEAR:
501       case DisposalMethod::CLEAR_ALL:
502       case DisposalMethod::RESTORE_PREVIOUS:
503         mFirstFrameRefreshArea = IntRect(IntPoint(), Size().ToUnknownSize());
504         break;
505       case DisposalMethod::KEEP:
506       case DisposalMethod::NOT_SPECIFIED:
507         break;
508     }
509   } else {
510     // Some GIFs are huge but only have a small area that they animate. We only
511     // need to refresh that small area when frame 0 comes around again.
512     mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea,
513                                      mCurrentFrame->GetBoundedBlendRect());
514   }
515 }
516 
PostInvalidation(const OrientedIntRect & aRect,const Maybe<OrientedIntRect> & aRectAtOutputSize)517 void Decoder::PostInvalidation(const OrientedIntRect& aRect,
518                                const Maybe<OrientedIntRect>& aRectAtOutputSize
519                                /* = Nothing() */) {
520   // We should be mid-frame
521   MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!");
522   MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!");
523 
524   // Record this invalidation, unless we're not sending partial invalidations
525   // or we're past the first frame.
526   if (ShouldSendPartialInvalidations() && mFrameCount == 1) {
527     mInvalidRect.UnionRect(mInvalidRect, aRect);
528     mCurrentFrame->ImageUpdated(
529         aRectAtOutputSize.valueOr(aRect).ToUnknownRect());
530   }
531 }
532 
PostDecodeDone(int32_t aLoopCount)533 void Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */) {
534   MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
535   MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
536   MOZ_ASSERT(!mDecodeDone, "Decode already done!");
537   mDecodeDone = true;
538 
539   mImageMetadata.SetLoopCount(aLoopCount);
540 
541   // Some metadata that we track should take into account every frame in the
542   // image. If this is a first-frame-only decode, our accumulated loop length
543   // and first frame refresh area only includes the first frame, so it's not
544   // correct and we don't record it.
545   if (!IsFirstFrameDecode()) {
546     mImageMetadata.SetLoopLength(mLoopLength);
547     mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea);
548   }
549 
550   mProgress |= FLAG_DECODE_COMPLETE;
551 }
552 
PostError()553 void Decoder::PostError() {
554   mError = true;
555 
556   if (mInFrame) {
557     MOZ_ASSERT(mCurrentFrame);
558     MOZ_ASSERT(mFrameCount > 0);
559     mCurrentFrame->Abort();
560     mInFrame = false;
561     --mFrameCount;
562     mHasFrameToTake = false;
563   }
564 }
565 
566 }  // namespace image
567 }  // namespace mozilla
568