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