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 "mozilla/gfx/2D.h"
9 #include "DecodePool.h"
10 #include "GeckoProfiler.h"
11 #include "IDecodingTask.h"
12 #include "ISurfaceProvider.h"
13 #include "nsProxyRelease.h"
14 #include "nsServiceManagerUtils.h"
15 #include "nsComponentManagerUtils.h"
16 #include "mozilla/Telemetry.h"
17 
18 using mozilla::gfx::IntSize;
19 using mozilla::gfx::SurfaceFormat;
20 
21 namespace mozilla {
22 namespace image {
23 
24 class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final
25 {
26 public:
AutoRecordDecoderTelemetry(Decoder * aDecoder)27   explicit AutoRecordDecoderTelemetry(Decoder* aDecoder)
28     : mDecoder(aDecoder)
29   {
30     MOZ_ASSERT(mDecoder);
31 
32     // Begin recording telemetry data.
33     mStartTime = TimeStamp::Now();
34   }
35 
~AutoRecordDecoderTelemetry()36   ~AutoRecordDecoderTelemetry()
37   {
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   : mImageData(nullptr)
49   , mImageDataLength(0)
50   , mColormap(nullptr)
51   , mColormapSize(0)
52   , mImage(aImage)
53   , mProgress(NoProgress)
54   , mFrameCount(0)
55   , mLoopLength(FrameTimeout::Zero())
56   , mDecoderFlags(DefaultDecoderFlags())
57   , mSurfaceFlags(DefaultSurfaceFlags())
58   , mInitialized(false)
59   , mMetadataDecode(false)
60   , mHaveExplicitOutputSize(false)
61   , mInFrame(false)
62   , mFinishedNewFrame(false)
63   , mReachedTerminalState(false)
64   , mDecodeDone(false)
65   , mError(false)
66   , mShouldReportError(false)
67 { }
68 
~Decoder()69 Decoder::~Decoder()
70 {
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");
75   mInitialized = false;
76 
77   if (mImage && !NS_IsMainThread()) {
78     // Dispatch mImage to main thread to prevent it from being destructed by the
79     // decode thread.
80     NS_ReleaseOnMainThread(mImage.forget());
81   }
82 }
83 
84 /*
85  * Common implementation of the decoder interface.
86  */
87 
88 nsresult
Init()89 Decoder::Init()
90 {
91   // No re-initializing
92   MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
93 
94   // All decoders must have a SourceBufferIterator.
95   MOZ_ASSERT(mIterator);
96 
97   // Metadata decoders must not set an output size.
98   MOZ_ASSERT_IF(mMetadataDecode, !mHaveExplicitOutputSize);
99 
100   // All decoders must be anonymous except for metadata decoders.
101   // XXX(seth): Soon that exception will be removed.
102   MOZ_ASSERT_IF(mImage, IsMetadataDecode());
103 
104   // Implementation-specific initialization.
105   nsresult rv = InitInternal();
106 
107   mInitialized = true;
108 
109   return rv;
110 }
111 
112 LexerResult
Decode(IResumable * aOnResume)113 Decoder::Decode(IResumable* aOnResume /* = nullptr */)
114 {
115   MOZ_ASSERT(mInitialized, "Should be initialized here");
116   MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
117 
118   // If we're already done, don't attempt to keep decoding.
119   if (GetDecodeDone()) {
120     return LexerResult(HasError() ? TerminalState::FAILURE
121                                   : TerminalState::SUCCESS);
122   }
123 
124   LexerResult lexerResult(TerminalState::FAILURE);
125   {
126     PROFILER_LABEL("ImageDecoder", "Decode", js::ProfileEntry::Category::GRAPHICS);
127     AutoRecordDecoderTelemetry telemetry(this);
128 
129     lexerResult =  DoDecode(*mIterator, aOnResume);
130   };
131 
132   if (lexerResult.is<Yield>()) {
133     // We either need more data to continue (in which case either @aOnResume or
134     // the caller will reschedule us to run again later), or the decoder is
135     // yielding to allow the caller access to some intermediate output.
136     return lexerResult;
137   }
138 
139   // We reached a terminal state; we're now done decoding.
140   MOZ_ASSERT(lexerResult.is<TerminalState>());
141   mReachedTerminalState = true;
142 
143   // If decoding failed, record that fact.
144   if (lexerResult.as<TerminalState>() == TerminalState::FAILURE) {
145     PostError();
146   }
147 
148   // Perform final cleanup.
149   CompleteDecode();
150 
151   return LexerResult(HasError() ? TerminalState::FAILURE
152                                 : TerminalState::SUCCESS);
153 }
154 
155 LexerResult
TerminateFailure()156 Decoder::TerminateFailure()
157 {
158   PostError();
159 
160   // Perform final cleanup if need be.
161   if (!mReachedTerminalState) {
162     mReachedTerminalState = true;
163     CompleteDecode();
164   }
165 
166   return LexerResult(TerminalState::FAILURE);
167 }
168 
169 bool
ShouldSyncDecode(size_t aByteLimit)170 Decoder::ShouldSyncDecode(size_t aByteLimit)
171 {
172   MOZ_ASSERT(aByteLimit > 0);
173   MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
174 
175   return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
176 }
177 
178 void
CompleteDecode()179 Decoder::CompleteDecode()
180 {
181   // Implementation-specific finalization.
182   nsresult rv = BeforeFinishInternal();
183   if (NS_FAILED(rv)) {
184     PostError();
185   }
186 
187   rv = HasError() ? FinishWithErrorInternal()
188                   : FinishInternal();
189   if (NS_FAILED(rv)) {
190     PostError();
191   }
192 
193   if (IsMetadataDecode()) {
194     // If this was a metadata decode and we never got a size, the decode failed.
195     if (!HasSize()) {
196       PostError();
197     }
198     return;
199   }
200 
201   // If the implementation left us mid-frame, finish that up. Note that it may
202   // have left us transparent.
203   if (mInFrame) {
204     PostHasTransparency();
205     PostFrameStop();
206   }
207 
208   // If PostDecodeDone() has not been called, we may need to send teardown
209   // notifications if it is unrecoverable.
210   if (!mDecodeDone) {
211     // We should always report an error to the console in this case.
212     mShouldReportError = true;
213 
214     if (GetCompleteFrameCount() > 0) {
215       // We're usable if we have at least one complete frame, so do exactly
216       // what we should have when the decoder completed.
217       PostHasTransparency();
218       PostDecodeDone();
219     } else {
220       // We're not usable. Record some final progress indicating the error.
221       mProgress |= FLAG_DECODE_COMPLETE | FLAG_HAS_ERROR;
222     }
223   }
224 
225   if (mDecodeDone) {
226     MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
227 
228     // If this image wasn't animated and isn't a transient image, mark its frame
229     // as optimizable. We don't support optimizing animated images and
230     // optimizing transient images isn't worth it.
231     if (!HasAnimation() &&
232         !(mDecoderFlags & DecoderFlags::IMAGE_IS_TRANSIENT) &&
233         mCurrentFrame) {
234       mCurrentFrame->SetOptimizable();
235     }
236   }
237 }
238 
239 void
SetOutputSize(const gfx::IntSize & aSize)240 Decoder::SetOutputSize(const gfx::IntSize& aSize)
241 {
242   mOutputSize = Some(aSize);
243   mHaveExplicitOutputSize = true;
244 }
245 
246 Maybe<gfx::IntSize>
ExplicitOutputSize() const247 Decoder::ExplicitOutputSize() const
248 {
249   MOZ_ASSERT_IF(mHaveExplicitOutputSize, mOutputSize);
250   return mHaveExplicitOutputSize ? mOutputSize : Nothing();
251 }
252 
253 Maybe<uint32_t>
TakeCompleteFrameCount()254 Decoder::TakeCompleteFrameCount()
255 {
256   const bool finishedNewFrame = mFinishedNewFrame;
257   mFinishedNewFrame = false;
258   return finishedNewFrame ? Some(GetCompleteFrameCount()) : Nothing();
259 }
260 
261 DecoderFinalStatus
FinalStatus() const262 Decoder::FinalStatus() const
263 {
264   return DecoderFinalStatus(IsMetadataDecode(),
265                             GetDecodeDone(),
266                             HasError(),
267                             ShouldReportError());
268 }
269 
270 DecoderTelemetry
Telemetry() const271 Decoder::Telemetry() const
272 {
273   MOZ_ASSERT(mIterator);
274   return DecoderTelemetry(SpeedHistogram(),
275                           mIterator->ByteCount(),
276                           mIterator->ChunkCount(),
277                           mDecodeTime);
278 }
279 
280 nsresult
AllocateFrame(uint32_t aFrameNum,const gfx::IntSize & aOutputSize,const gfx::IntRect & aFrameRect,gfx::SurfaceFormat aFormat,uint8_t aPaletteDepth)281 Decoder::AllocateFrame(uint32_t aFrameNum,
282                        const gfx::IntSize& aOutputSize,
283                        const gfx::IntRect& aFrameRect,
284                        gfx::SurfaceFormat aFormat,
285                        uint8_t aPaletteDepth)
286 {
287   mCurrentFrame = AllocateFrameInternal(aFrameNum, aOutputSize, aFrameRect,
288                                         aFormat, aPaletteDepth,
289                                         mCurrentFrame.get());
290 
291   if (mCurrentFrame) {
292     // Gather the raw pointers the decoders will use.
293     mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
294     mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
295 
296     // We should now be on |aFrameNum|. (Note that we're comparing the frame
297     // number, which is zero-based, with the frame count, which is one-based.)
298     MOZ_ASSERT(aFrameNum + 1 == mFrameCount);
299 
300     // If we're past the first frame, PostIsAnimated() should've been called.
301     MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
302 
303     // Update our state to reflect the new frame.
304     MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
305     mInFrame = true;
306   }
307 
308   return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
309 }
310 
311 RawAccessFrameRef
AllocateFrameInternal(uint32_t aFrameNum,const gfx::IntSize & aOutputSize,const gfx::IntRect & aFrameRect,SurfaceFormat aFormat,uint8_t aPaletteDepth,imgFrame * aPreviousFrame)312 Decoder::AllocateFrameInternal(uint32_t aFrameNum,
313                                const gfx::IntSize& aOutputSize,
314                                const gfx::IntRect& aFrameRect,
315                                SurfaceFormat aFormat,
316                                uint8_t aPaletteDepth,
317                                imgFrame* aPreviousFrame)
318 {
319   if (HasError()) {
320     return RawAccessFrameRef();
321   }
322 
323   if (aFrameNum != mFrameCount) {
324     MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
325     return RawAccessFrameRef();
326   }
327 
328   if (aOutputSize.width <= 0 || aOutputSize.height <= 0 ||
329       aFrameRect.width <= 0 || aFrameRect.height <= 0) {
330     NS_WARNING("Trying to add frame with zero or negative size");
331     return RawAccessFrameRef();
332   }
333 
334   NotNull<RefPtr<imgFrame>> frame = WrapNotNull(new imgFrame());
335   bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
336   if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFrameRect, aFormat,
337                                       aPaletteDepth, nonPremult))) {
338     NS_WARNING("imgFrame::Init should succeed");
339     return RawAccessFrameRef();
340   }
341 
342   RawAccessFrameRef ref = frame->RawAccessRef();
343   if (!ref) {
344     frame->Abort();
345     return RawAccessFrameRef();
346   }
347 
348   if (aFrameNum == 1) {
349     MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
350     aPreviousFrame->SetRawAccessOnly();
351 
352     // If we dispose of the first frame by clearing it, then the first frame's
353     // refresh area is all of itself.
354     // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
355     AnimationData previousFrameData = aPreviousFrame->GetAnimationData();
356     if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR ||
357         previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL ||
358         previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
359       mFirstFrameRefreshArea = previousFrameData.mRect;
360     }
361   }
362 
363   if (aFrameNum > 0) {
364     ref->SetRawAccessOnly();
365 
366     // Some GIFs are huge but only have a small area that they animate. We only
367     // need to refresh that small area when frame 0 comes around again.
368     mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, frame->GetRect());
369   }
370 
371   mFrameCount++;
372 
373   return ref;
374 }
375 
376 /*
377  * Hook stubs. Override these as necessary in decoder implementations.
378  */
379 
InitInternal()380 nsresult Decoder::InitInternal() { return NS_OK; }
BeforeFinishInternal()381 nsresult Decoder::BeforeFinishInternal() { return NS_OK; }
FinishInternal()382 nsresult Decoder::FinishInternal() { return NS_OK; }
FinishWithErrorInternal()383 nsresult Decoder::FinishWithErrorInternal() { return NS_OK; }
384 
385 /*
386  * Progress Notifications
387  */
388 
389 void
PostSize(int32_t aWidth,int32_t aHeight,Orientation aOrientation)390 Decoder::PostSize(int32_t aWidth,
391                   int32_t aHeight,
392                   Orientation aOrientation /* = Orientation()*/)
393 {
394   // Validate.
395   MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
396   MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
397 
398   // Set our intrinsic size.
399   mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
400 
401   // Set our output size if it's not already set.
402   if (!mOutputSize) {
403     mOutputSize = Some(IntSize(aWidth, aHeight));
404   }
405 
406   MOZ_ASSERT(mOutputSize->width <= aWidth && mOutputSize->height <= aHeight,
407              "Output size will result in upscaling");
408 
409   // Create a downscaler if we need to downscale. This is used by legacy
410   // decoders that haven't been converted to use SurfacePipe yet.
411   // XXX(seth): Obviously, we'll remove this once all decoders use SurfacePipe.
412   if (mOutputSize->width < aWidth || mOutputSize->height < aHeight) {
413     mDownscaler.emplace(*mOutputSize);
414   }
415 
416   // Record this notification.
417   mProgress |= FLAG_SIZE_AVAILABLE;
418 }
419 
420 void
PostHasTransparency()421 Decoder::PostHasTransparency()
422 {
423   mProgress |= FLAG_HAS_TRANSPARENCY;
424 }
425 
426 void
PostIsAnimated(FrameTimeout aFirstFrameTimeout)427 Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout)
428 {
429   mProgress |= FLAG_IS_ANIMATED;
430   mImageMetadata.SetHasAnimation();
431   mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
432 }
433 
434 void
PostFrameStop(Opacity aFrameOpacity,DisposalMethod aDisposalMethod,FrameTimeout aTimeout,BlendMethod aBlendMethod,const Maybe<nsIntRect> & aBlendRect)435 Decoder::PostFrameStop(Opacity aFrameOpacity
436                          /* = Opacity::SOME_TRANSPARENCY */,
437                        DisposalMethod aDisposalMethod
438                          /* = DisposalMethod::KEEP */,
439                        FrameTimeout aTimeout /* = FrameTimeout::Forever() */,
440                        BlendMethod aBlendMethod /* = BlendMethod::OVER */,
441                        const Maybe<nsIntRect>& aBlendRect /* = Nothing() */)
442 {
443   // We should be mid-frame
444   MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
445   MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
446   MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
447 
448   // Update our state.
449   mInFrame = false;
450   mFinishedNewFrame = true;
451 
452   mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout,
453                         aBlendMethod, aBlendRect);
454 
455   mProgress |= FLAG_FRAME_COMPLETE;
456 
457   mLoopLength += aTimeout;
458 
459   // If we're not sending partial invalidations, then we send an invalidation
460   // here when the first frame is complete.
461   if (!ShouldSendPartialInvalidations() && mFrameCount == 1) {
462     mInvalidRect.UnionRect(mInvalidRect,
463                            IntRect(IntPoint(), Size()));
464   }
465 }
466 
467 void
PostInvalidation(const gfx::IntRect & aRect,const Maybe<gfx::IntRect> & aRectAtOutputSize)468 Decoder::PostInvalidation(const gfx::IntRect& aRect,
469                           const Maybe<gfx::IntRect>& aRectAtOutputSize
470                             /* = Nothing() */)
471 {
472   // We should be mid-frame
473   MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!");
474   MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!");
475 
476   // Record this invalidation, unless we're not sending partial invalidations
477   // or we're past the first frame.
478   if (ShouldSendPartialInvalidations() && mFrameCount == 1) {
479     mInvalidRect.UnionRect(mInvalidRect, aRect);
480     mCurrentFrame->ImageUpdated(aRectAtOutputSize.valueOr(aRect));
481   }
482 }
483 
484 void
PostDecodeDone(int32_t aLoopCount)485 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
486 {
487   MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
488   MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
489   MOZ_ASSERT(!mDecodeDone, "Decode already done!");
490   mDecodeDone = true;
491 
492   mImageMetadata.SetLoopCount(aLoopCount);
493 
494   // Some metadata that we track should take into account every frame in the
495   // image. If this is a first-frame-only decode, our accumulated loop length
496   // and first frame refresh area only includes the first frame, so it's not
497   // correct and we don't record it.
498   if (!IsFirstFrameDecode()) {
499     mImageMetadata.SetLoopLength(mLoopLength);
500     mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea);
501   }
502 
503   mProgress |= FLAG_DECODE_COMPLETE;
504 }
505 
506 void
PostError()507 Decoder::PostError()
508 {
509   mError = true;
510 
511   if (mInFrame) {
512     MOZ_ASSERT(mCurrentFrame);
513     MOZ_ASSERT(mFrameCount > 0);
514     mCurrentFrame->Abort();
515     mInFrame = false;
516     --mFrameCount;
517   }
518 }
519 
520 } // namespace image
521 } // namespace mozilla
522