LCOV - code coverage report
Current view: top level - image - Decoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 168 213 78.9 %
Date: 2017-07-14 16:53:18 Functions: 22 28 78.6 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13