Coverage Report

Created: 2018-09-25 14:53

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