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