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(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     // We should always report an error to the console in this case.
253     mShouldReportError = true;
254 
255     if (GetCompleteFrameCount() > 0) {
256       // We're usable if we have at least one complete frame, so do exactly
257       // what we should have when the decoder completed.
258       PostHasTransparency();
259       PostDecodeDone();
260     } else {
261       // We're not usable. Record some final progress indicating the error.
262       mProgress |= FLAG_DECODE_COMPLETE | FLAG_HAS_ERROR;
263     }
264   }
265 
266   if (mDecodeDone) {
267     MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
268 
269     // If this image wasn't animated and isn't a transient image, mark its frame
270     // as optimizable. We don't support optimizing animated images and
271     // optimizing transient images isn't worth it.
272     if (!HasAnimation() &&
273         !(mDecoderFlags & DecoderFlags::IMAGE_IS_TRANSIENT) && mCurrentFrame) {
274       mCurrentFrame->SetOptimizable();
275     }
276   }
277 }
278 
SetOutputSize(const gfx::IntSize & aSize)279 void Decoder::SetOutputSize(const gfx::IntSize& aSize) {
280   mOutputSize = Some(aSize);
281   mHaveExplicitOutputSize = true;
282 }
283 
ExplicitOutputSize() const284 Maybe<gfx::IntSize> Decoder::ExplicitOutputSize() const {
285   MOZ_ASSERT_IF(mHaveExplicitOutputSize, mOutputSize);
286   return mHaveExplicitOutputSize ? mOutputSize : Nothing();
287 }
288 
TakeCompleteFrameCount()289 Maybe<uint32_t> Decoder::TakeCompleteFrameCount() {
290   const bool finishedNewFrame = mFinishedNewFrame;
291   mFinishedNewFrame = false;
292   return finishedNewFrame ? Some(GetCompleteFrameCount()) : Nothing();
293 }
294 
FinalStatus() const295 DecoderFinalStatus Decoder::FinalStatus() const {
296   return DecoderFinalStatus(IsMetadataDecode(), GetDecodeDone(), HasError(),
297                             ShouldReportError());
298 }
299 
Telemetry() const300 DecoderTelemetry Decoder::Telemetry() const {
301   MOZ_ASSERT(mIterator);
302   return DecoderTelemetry(SpeedHistogram(),
303                           mIterator ? mIterator->ByteCount() : 0,
304                           mIterator ? mIterator->ChunkCount() : 0, mDecodeTime);
305 }
306 
AllocateFrame(const gfx::IntSize & aOutputSize,gfx::SurfaceFormat aFormat,const Maybe<AnimationParams> & aAnimParams)307 nsresult Decoder::AllocateFrame(const gfx::IntSize& aOutputSize,
308                                 gfx::SurfaceFormat aFormat,
309                                 const Maybe<AnimationParams>& aAnimParams) {
310   mCurrentFrame = AllocateFrameInternal(aOutputSize, aFormat, aAnimParams,
311                                         std::move(mCurrentFrame));
312 
313   if (mCurrentFrame) {
314     mHasFrameToTake = true;
315 
316     // Gather the raw pointers the decoders will use.
317     mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
318 
319     // We should now be on |aFrameNum|. (Note that we're comparing the frame
320     // number, which is zero-based, with the frame count, which is one-based.)
321     MOZ_ASSERT_IF(aAnimParams, aAnimParams->mFrameNum + 1 == mFrameCount);
322 
323     // If we're past the first frame, PostIsAnimated() should've been called.
324     MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
325 
326     // Update our state to reflect the new frame.
327     MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
328     mInFrame = true;
329   }
330 
331   return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
332 }
333 
AllocateFrameInternal(const gfx::IntSize & aOutputSize,SurfaceFormat aFormat,const Maybe<AnimationParams> & aAnimParams,RawAccessFrameRef && aPreviousFrame)334 RawAccessFrameRef Decoder::AllocateFrameInternal(
335     const gfx::IntSize& aOutputSize, SurfaceFormat aFormat,
336     const Maybe<AnimationParams>& aAnimParams,
337     RawAccessFrameRef&& aPreviousFrame) {
338   if (HasError()) {
339     return RawAccessFrameRef();
340   }
341 
342   uint32_t frameNum = aAnimParams ? aAnimParams->mFrameNum : 0;
343   if (frameNum != mFrameCount) {
344     MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
345     return RawAccessFrameRef();
346   }
347 
348   if (aOutputSize.width <= 0 || aOutputSize.height <= 0) {
349     NS_WARNING("Trying to add frame with zero or negative size");
350     return RawAccessFrameRef();
351   }
352 
353   if (frameNum == 1) {
354     MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
355     aPreviousFrame->SetRawAccessOnly();
356   }
357 
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();
374     }
375   }
376 
377   RawAccessFrameRef ref;
378 
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);
385 
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(ref->InitForDecoderRecycle(aAnimParams.ref()));
395       }
396 
397       if (blocked) {
398         ref.reset();
399       }
400     }
401   }
402 
403   // Either the recycler had nothing to give us, or we don't have a recycler.
404   // Produce a new frame to store the data.
405   if (!ref) {
406     // There is no underlying data to reuse, so reset the recycle rect to be
407     // the full frame, to ensure the restore frame is fully copied.
408     mRecycleRect = IntRect(IntPoint(0, 0), aOutputSize);
409 
410     bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
411     auto frame = MakeNotNull<RefPtr<imgFrame>>();
412     if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFormat, nonPremult,
413                                         aAnimParams, bool(mFrameRecycler)))) {
414       NS_WARNING("imgFrame::Init should succeed");
415       return RawAccessFrameRef();
416     }
417 
418     ref = frame->RawAccessRef();
419     if (!ref) {
420       frame->Abort();
421       return RawAccessFrameRef();
422     }
423 
424     if (frameNum > 0) {
425       frame->SetRawAccessOnly();
426     }
427   }
428 
429   mFrameCount++;
430 
431   return ref;
432 }
433 
434 /*
435  * Hook stubs. Override these as necessary in decoder implementations.
436  */
437 
InitInternal()438 nsresult Decoder::InitInternal() { return NS_OK; }
BeforeFinishInternal()439 nsresult Decoder::BeforeFinishInternal() { return NS_OK; }
FinishInternal()440 nsresult Decoder::FinishInternal() { return NS_OK; }
441 
FinishWithErrorInternal()442 nsresult Decoder::FinishWithErrorInternal() {
443   MOZ_ASSERT(!mInFrame);
444   return NS_OK;
445 }
446 
447 /*
448  * Progress Notifications
449  */
450 
PostSize(int32_t aWidth,int32_t aHeight,Orientation aOrientation,Resolution aResolution)451 void Decoder::PostSize(int32_t aWidth, int32_t aHeight,
452                        Orientation aOrientation, Resolution aResolution) {
453   // Validate.
454   MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
455   MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
456 
457   // Set our intrinsic size.
458   mImageMetadata.SetSize(aWidth, aHeight, aOrientation, aResolution);
459 
460   // Verify it is the expected size, if given. Note that this is only used by
461   // the ICO decoder for embedded image types, so only its subdecoders are
462   // required to handle failures in PostSize.
463   if (!IsExpectedSize()) {
464     PostError();
465     return;
466   }
467 
468   // Set our output size if it's not already set.
469   if (!mOutputSize) {
470     mOutputSize = Some(IntSize(aWidth, aHeight));
471   }
472 
473   MOZ_ASSERT(mOutputSize->width <= aWidth && mOutputSize->height <= aHeight,
474              "Output size will result in upscaling");
475 
476   // Record this notification.
477   mProgress |= FLAG_SIZE_AVAILABLE;
478 }
479 
PostHasTransparency()480 void Decoder::PostHasTransparency() { mProgress |= FLAG_HAS_TRANSPARENCY; }
481 
PostIsAnimated(FrameTimeout aFirstFrameTimeout)482 void Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout) {
483   mProgress |= FLAG_IS_ANIMATED;
484   mImageMetadata.SetHasAnimation();
485   mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
486 }
487 
PostFrameStop(Opacity aFrameOpacity)488 void Decoder::PostFrameStop(Opacity aFrameOpacity) {
489   // We should be mid-frame
490   MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
491   MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
492   MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
493 
494   // Update our state.
495   mInFrame = false;
496   mFinishedNewFrame = true;
497 
498   mCurrentFrame->Finish(aFrameOpacity, mFinalizeFrames);
499 
500   mProgress |= FLAG_FRAME_COMPLETE;
501 
502   mLoopLength += mCurrentFrame->GetTimeout();
503 
504   if (mFrameCount == 1) {
505     // If we're not sending partial invalidations, then we send an invalidation
506     // here when the first frame is complete.
507     if (!ShouldSendPartialInvalidations()) {
508       mInvalidRect.UnionRect(mInvalidRect, IntRect(IntPoint(), Size()));
509     }
510 
511     // If we dispose of the first frame by clearing it, then the first frame's
512     // refresh area is all of itself. RESTORE_PREVIOUS is invalid (assumed to
513     // be DISPOSE_CLEAR).
514     switch (mCurrentFrame->GetDisposalMethod()) {
515       default:
516         MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
517       case DisposalMethod::CLEAR:
518       case DisposalMethod::CLEAR_ALL:
519       case DisposalMethod::RESTORE_PREVIOUS:
520         mFirstFrameRefreshArea = IntRect(IntPoint(), Size());
521         break;
522       case DisposalMethod::KEEP:
523       case DisposalMethod::NOT_SPECIFIED:
524         break;
525     }
526   } else {
527     // Some GIFs are huge but only have a small area that they animate. We only
528     // need to refresh that small area when frame 0 comes around again.
529     mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea,
530                                      mCurrentFrame->GetBoundedBlendRect());
531   }
532 }
533 
PostInvalidation(const gfx::IntRect & aRect,const Maybe<gfx::IntRect> & aRectAtOutputSize)534 void Decoder::PostInvalidation(const gfx::IntRect& aRect,
535                                const Maybe<gfx::IntRect>& aRectAtOutputSize
536                                /* = Nothing() */) {
537   // We should be mid-frame
538   MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!");
539   MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!");
540 
541   // Record this invalidation, unless we're not sending partial invalidations
542   // or we're past the first frame.
543   if (ShouldSendPartialInvalidations() && mFrameCount == 1) {
544     mInvalidRect.UnionRect(mInvalidRect, aRect);
545     mCurrentFrame->ImageUpdated(aRectAtOutputSize.valueOr(aRect));
546   }
547 }
548 
PostDecodeDone(int32_t aLoopCount)549 void Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */) {
550   MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
551   MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
552   MOZ_ASSERT(!mDecodeDone, "Decode already done!");
553   mDecodeDone = true;
554 
555   mImageMetadata.SetLoopCount(aLoopCount);
556 
557   // Some metadata that we track should take into account every frame in the
558   // image. If this is a first-frame-only decode, our accumulated loop length
559   // and first frame refresh area only includes the first frame, so it's not
560   // correct and we don't record it.
561   if (!IsFirstFrameDecode()) {
562     mImageMetadata.SetLoopLength(mLoopLength);
563     mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea);
564   }
565 
566   mProgress |= FLAG_DECODE_COMPLETE;
567 }
568 
PostError()569 void Decoder::PostError() {
570   mError = true;
571 
572   if (mInFrame) {
573     MOZ_ASSERT(mCurrentFrame);
574     MOZ_ASSERT(mFrameCount > 0);
575     mCurrentFrame->Abort();
576     mInFrame = false;
577     --mFrameCount;
578     mHasFrameToTake = false;
579   }
580 }
581 
582 }  // namespace image
583 }  // namespace mozilla
584