LCOV - code coverage report
Current view: top level - image - imgFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 260 416 62.5 %
Date: 2017-07-14 16:53:18 Functions: 39 46 84.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "imgFrame.h"
       8             : #include "ImageRegion.h"
       9             : #include "ShutdownTracker.h"
      10             : 
      11             : #include "prenv.h"
      12             : 
      13             : #include "gfx2DGlue.h"
      14             : #include "gfxPlatform.h"
      15             : #include "gfxPrefs.h"
      16             : #include "gfxUtils.h"
      17             : #include "gfxAlphaRecovery.h"
      18             : 
      19             : #include "GeckoProfiler.h"
      20             : #include "MainThreadUtils.h"
      21             : #include "mozilla/CheckedInt.h"
      22             : #include "mozilla/gfx/Tools.h"
      23             : #include "mozilla/layers/SourceSurfaceSharedData.h"
      24             : #include "mozilla/layers/SourceSurfaceVolatileData.h"
      25             : #include "mozilla/Likely.h"
      26             : #include "mozilla/MemoryReporting.h"
      27             : #include "nsMargin.h"
      28             : #include "nsThreadUtils.h"
      29             : 
      30             : 
      31             : namespace mozilla {
      32             : 
      33             : using namespace gfx;
      34             : 
      35             : namespace image {
      36             : 
      37             : static void
      38          28 : ScopedMapRelease(void* aMap)
      39             : {
      40          28 :   delete static_cast<DataSourceSurface::ScopedMap*>(aMap);
      41          28 : }
      42             : 
      43             : static int32_t
      44          50 : VolatileSurfaceStride(const IntSize& size, SurfaceFormat format)
      45             : {
      46             :   // Stride must be a multiple of four or cairo will complain.
      47          50 :   return (size.width * BytesPerPixel(format) + 0x3) & ~0x3;
      48             : }
      49             : 
      50             : static already_AddRefed<DataSourceSurface>
      51          74 : CreateLockedSurface(DataSourceSurface *aSurface,
      52             :                     const IntSize& size,
      53             :                     SurfaceFormat format)
      54             : {
      55             :   // Shared memory is never released until the surface itself is released
      56          74 :   if (aSurface->GetType() == SurfaceType::DATA_SHARED) {
      57           0 :     RefPtr<DataSourceSurface> surf(aSurface);
      58           0 :     return surf.forget();
      59             :   }
      60             : 
      61             :   DataSourceSurface::ScopedMap* smap =
      62          74 :     new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE);
      63          74 :   if (smap->IsMapped()) {
      64             :     // The ScopedMap is held by this DataSourceSurface.
      65             :     RefPtr<DataSourceSurface> surf =
      66         148 :       Factory::CreateWrappingDataSourceSurface(smap->GetData(),
      67          74 :                                                aSurface->Stride(),
      68             :                                                size,
      69             :                                                format,
      70             :                                                &ScopedMapRelease,
      71          74 :                                                static_cast<void*>(smap));
      72          74 :     if (surf) {
      73          74 :       return surf.forget();
      74             :     }
      75             :   }
      76             : 
      77           0 :   delete smap;
      78           0 :   return nullptr;
      79             : }
      80             : 
      81             : static already_AddRefed<DataSourceSurface>
      82          50 : AllocateBufferForImage(const IntSize& size,
      83             :                        SurfaceFormat format,
      84             :                        bool aIsAnimated = false)
      85             : {
      86          50 :   int32_t stride = VolatileSurfaceStride(size, format);
      87          50 :   if (!aIsAnimated && gfxPrefs::ImageMemShared()) {
      88           0 :     RefPtr<SourceSurfaceSharedData> newSurf = new SourceSurfaceSharedData();
      89           0 :     if (newSurf->Init(size, stride, format)) {
      90           0 :       return newSurf.forget();
      91             :     }
      92             :   } else {
      93          50 :     RefPtr<SourceSurfaceVolatileData> newSurf= new SourceSurfaceVolatileData();
      94          50 :     if (newSurf->Init(size, stride, format)) {
      95          50 :       return newSurf.forget();
      96             :     }
      97             :   }
      98           0 :   return nullptr;
      99             : }
     100             : 
     101             : static bool
     102          50 : ClearSurface(DataSourceSurface* aSurface, const IntSize& aSize, SurfaceFormat aFormat)
     103             : {
     104          50 :   int32_t stride = aSurface->Stride();
     105          50 :   uint8_t* data = aSurface->GetData();
     106          50 :   MOZ_ASSERT(data);
     107             : 
     108          50 :   if (aFormat == SurfaceFormat::B8G8R8X8) {
     109             :     // Skia doesn't support RGBX surfaces, so ensure the alpha value is set
     110             :     // to opaque white. While it would be nice to only do this for Skia,
     111             :     // imgFrame can run off main thread and past shutdown where
     112             :     // we might not have gfxPlatform, so just memset everytime instead.
     113           0 :     memset(data, 0xFF, stride * aSize.height);
     114          50 :   } else if (aSurface->OnHeap()) {
     115             :     // We only need to memset it if the buffer was allocated on the heap.
     116             :     // Otherwise, it's allocated via mmap and refers to a zeroed page and will
     117             :     // be COW once it's written to.
     118          50 :     memset(data, 0, stride * aSize.height);
     119             :   }
     120             : 
     121          50 :   return true;
     122             : }
     123             : 
     124             : void
     125          35 : MarkSurfaceShared(SourceSurface* aSurface)
     126             : {
     127             :   // Depending on what requested the image decoding, the buffer may or may not
     128             :   // end up being shared with another process (e.g. put in a painted layer,
     129             :   // used inside a canvas). If not shared, we should ensure are not keeping the
     130             :   // handle only because we have yet to share it.
     131          35 :   if (aSurface && aSurface->GetType() == SurfaceType::DATA_SHARED) {
     132           0 :     auto sharedSurface = static_cast<SourceSurfaceSharedData*>(aSurface);
     133           0 :     sharedSurface->FinishedSharing();
     134             :   }
     135          35 : }
     136             : 
     137             : // Returns true if an image of aWidth x aHeight is allowed and legal.
     138             : static bool
     139         118 : AllowedImageSize(int32_t aWidth, int32_t aHeight)
     140             : {
     141             :   // reject over-wide or over-tall images
     142         118 :   const int32_t k64KLimit = 0x0000FFFF;
     143         118 :   if (MOZ_UNLIKELY(aWidth > k64KLimit || aHeight > k64KLimit )) {
     144           0 :     NS_WARNING("image too big");
     145           0 :     return false;
     146             :   }
     147             : 
     148             :   // protect against invalid sizes
     149         118 :   if (MOZ_UNLIKELY(aHeight <= 0 || aWidth <= 0)) {
     150           0 :     return false;
     151             :   }
     152             : 
     153             :   // check to make sure we don't overflow a 32-bit
     154         118 :   CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * 4;
     155         118 :   if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
     156           0 :     NS_WARNING("width or height too large");
     157           0 :     return false;
     158             :   }
     159         118 :   return true;
     160             : }
     161             : 
     162          50 : static bool AllowedImageAndFrameDimensions(const nsIntSize& aImageSize,
     163             :                                            const nsIntRect& aFrameRect)
     164             : {
     165          50 :   if (!AllowedImageSize(aImageSize.width, aImageSize.height)) {
     166           0 :     return false;
     167             :   }
     168          50 :   if (!AllowedImageSize(aFrameRect.width, aFrameRect.height)) {
     169           0 :     return false;
     170             :   }
     171          50 :   nsIntRect imageRect(0, 0, aImageSize.width, aImageSize.height);
     172          50 :   if (!imageRect.Contains(aFrameRect)) {
     173           0 :     NS_WARNING("Animated image frame does not fit inside bounds of image");
     174             :   }
     175          50 :   return true;
     176             : }
     177             : 
     178          68 : imgFrame::imgFrame()
     179             :   : mMonitor("imgFrame")
     180             :   , mDecoded(0, 0, 0, 0)
     181             :   , mLockCount(0)
     182             :   , mTimeout(FrameTimeout::FromRawMilliseconds(100))
     183             :   , mDisposalMethod(DisposalMethod::NOT_SPECIFIED)
     184             :   , mBlendMethod(BlendMethod::OVER)
     185             :   , mAborted(false)
     186             :   , mFinished(false)
     187             :   , mOptimizable(false)
     188             :   , mPalettedImageData(nullptr)
     189             :   , mPaletteDepth(0)
     190             :   , mNonPremult(false)
     191          68 :   , mCompositingFailed(false)
     192             : {
     193          68 : }
     194             : 
     195           0 : imgFrame::~imgFrame()
     196             : {
     197             : #ifdef DEBUG
     198           0 :   MonitorAutoLock lock(mMonitor);
     199           0 :   MOZ_ASSERT(mAborted || AreAllPixelsWritten());
     200           0 :   MOZ_ASSERT(mAborted || mFinished);
     201             : #endif
     202             : 
     203           0 :   free(mPalettedImageData);
     204           0 :   mPalettedImageData = nullptr;
     205           0 : }
     206             : 
     207             : nsresult
     208          50 : imgFrame::InitForDecoder(const nsIntSize& aImageSize,
     209             :                          const nsIntRect& aRect,
     210             :                          SurfaceFormat aFormat,
     211             :                          uint8_t aPaletteDepth /* = 0 */,
     212             :                          bool aNonPremult /* = false */,
     213             :                          bool aIsAnimated /* = false */)
     214             : {
     215             :   // Assert for properties that should be verified by decoders,
     216             :   // warn for properties related to bad content.
     217          50 :   if (!AllowedImageAndFrameDimensions(aImageSize, aRect)) {
     218           0 :     NS_WARNING("Should have legal image size");
     219           0 :     mAborted = true;
     220           0 :     return NS_ERROR_FAILURE;
     221             :   }
     222             : 
     223          50 :   mImageSize = aImageSize;
     224          50 :   mFrameRect = aRect;
     225             : 
     226             :   // We only allow a non-trivial frame rect (i.e., a frame rect that doesn't
     227             :   // cover the entire image) for paletted animation frames. We never draw those
     228             :   // frames directly; we just use FrameAnimator to composite them and produce a
     229             :   // BGRA surface that we actually draw. We enforce this here to make sure that
     230             :   // imgFrame::Draw(), which is responsible for drawing all other kinds of
     231             :   // frames, never has to deal with a non-trivial frame rect.
     232         200 :   if (aPaletteDepth == 0 &&
     233         200 :       !mFrameRect.IsEqualEdges(IntRect(IntPoint(), mImageSize))) {
     234           0 :     MOZ_ASSERT_UNREACHABLE("Creating a non-paletted imgFrame with a "
     235             :                            "non-trivial frame rect");
     236             :     return NS_ERROR_FAILURE;
     237             :   }
     238             : 
     239          50 :   mFormat = aFormat;
     240          50 :   mPaletteDepth = aPaletteDepth;
     241          50 :   mNonPremult = aNonPremult;
     242             : 
     243          50 :   if (aPaletteDepth != 0) {
     244             :     // We're creating for a paletted image.
     245           0 :     if (aPaletteDepth > 8) {
     246           0 :       NS_WARNING("Should have legal palette depth");
     247           0 :       NS_ERROR("This Depth is not supported");
     248           0 :       mAborted = true;
     249           0 :       return NS_ERROR_FAILURE;
     250             :     }
     251             : 
     252             :     // Use the fallible allocator here. Paletted images always use 1 byte per
     253             :     // pixel, so calculating the amount of memory we need is straightforward.
     254           0 :     size_t dataSize = PaletteDataLength() + mFrameRect.Area();
     255           0 :     mPalettedImageData = static_cast<uint8_t*>(calloc(dataSize, sizeof(uint8_t)));
     256           0 :     if (!mPalettedImageData) {
     257           0 :       NS_WARNING("Call to calloc for paletted image data should succeed");
     258             :     }
     259           0 :     NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
     260             :   } else {
     261          50 :     MOZ_ASSERT(!mLockedSurface, "Called imgFrame::InitForDecoder() twice?");
     262             : 
     263          50 :     mRawSurface = AllocateBufferForImage(mFrameRect.Size(), mFormat, aIsAnimated);
     264          50 :     if (!mRawSurface) {
     265           0 :       mAborted = true;
     266           0 :       return NS_ERROR_OUT_OF_MEMORY;
     267             :     }
     268             : 
     269          50 :     mLockedSurface = CreateLockedSurface(mRawSurface, mFrameRect.Size(), mFormat);
     270          50 :     if (!mLockedSurface) {
     271           0 :       NS_WARNING("Failed to create LockedSurface");
     272           0 :       mAborted = true;
     273           0 :       return NS_ERROR_OUT_OF_MEMORY;
     274             :     }
     275             : 
     276          50 :     if (!ClearSurface(mRawSurface, mFrameRect.Size(), mFormat)) {
     277           0 :       NS_WARNING("Could not clear allocated buffer");
     278           0 :       mAborted = true;
     279           0 :       return NS_ERROR_OUT_OF_MEMORY;
     280             :     }
     281             :   }
     282             : 
     283          50 :   return NS_OK;
     284             : }
     285             : 
     286             : nsresult
     287          18 : imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
     288             :                            const nsIntSize& aSize,
     289             :                            const SurfaceFormat aFormat,
     290             :                            SamplingFilter aSamplingFilter,
     291             :                            uint32_t aImageFlags,
     292             :                            gfx::BackendType aBackend)
     293             : {
     294             :   // Assert for properties that should be verified by decoders,
     295             :   // warn for properties related to bad content.
     296          18 :   if (!AllowedImageSize(aSize.width, aSize.height)) {
     297           0 :     NS_WARNING("Should have legal image size");
     298           0 :     mAborted = true;
     299           0 :     return NS_ERROR_FAILURE;
     300             :   }
     301             : 
     302          18 :   mImageSize = aSize;
     303          18 :   mFrameRect = IntRect(IntPoint(0, 0), aSize);
     304             : 
     305          18 :   mFormat = aFormat;
     306          18 :   mPaletteDepth = 0;
     307             : 
     308          36 :   RefPtr<DrawTarget> target;
     309             : 
     310             :   bool canUseDataSurface =
     311          18 :     gfxPlatform::GetPlatform()->CanRenderContentToDataSurface();
     312             : 
     313          18 :   if (canUseDataSurface) {
     314             :     // It's safe to use data surfaces for content on this platform, so we can
     315             :     // get away with using volatile buffers.
     316           0 :     MOZ_ASSERT(!mLockedSurface, "Called imgFrame::InitWithDrawable() twice?");
     317             : 
     318           0 :     mRawSurface = AllocateBufferForImage(mFrameRect.Size(), mFormat);
     319           0 :     if (!mRawSurface) {
     320           0 :       mAborted = true;
     321           0 :       return NS_ERROR_OUT_OF_MEMORY;
     322             :     }
     323             : 
     324           0 :     mLockedSurface = CreateLockedSurface(mRawSurface, mFrameRect.Size(), mFormat);
     325           0 :     if (!mLockedSurface) {
     326           0 :       NS_WARNING("Failed to create LockedSurface");
     327           0 :       mAborted = true;
     328           0 :       return NS_ERROR_OUT_OF_MEMORY;
     329             :     }
     330             : 
     331           0 :     if (!ClearSurface(mRawSurface, mFrameRect.Size(), mFormat)) {
     332           0 :       NS_WARNING("Could not clear allocated buffer");
     333           0 :       mAborted = true;
     334           0 :       return NS_ERROR_OUT_OF_MEMORY;
     335             :     }
     336             : 
     337           0 :     target = gfxPlatform::CreateDrawTargetForData(
     338           0 :                             mLockedSurface->GetData(),
     339           0 :                             mFrameRect.Size(),
     340           0 :                             mLockedSurface->Stride(),
     341           0 :                             mFormat);
     342             :   } else {
     343             :     // We can't use data surfaces for content, so we'll create an offscreen
     344             :     // surface instead.  This means if someone later calls RawAccessRef(), we
     345             :     // may have to do an expensive readback, but we warned callers about that in
     346             :     // the documentation for this method.
     347          18 :     MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?");
     348             : 
     349          18 :     if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(aBackend)) {
     350             :       target = gfxPlatform::GetPlatform()->
     351          18 :         CreateDrawTargetForBackend(aBackend, mFrameRect.Size(), mFormat);
     352             :     } else {
     353             :       target = gfxPlatform::GetPlatform()->
     354           0 :         CreateOffscreenContentDrawTarget(mFrameRect.Size(), mFormat);
     355             :     }
     356             :   }
     357             : 
     358          18 :   if (!target || !target->IsValid()) {
     359           0 :     mAborted = true;
     360           0 :     return NS_ERROR_OUT_OF_MEMORY;
     361             :   }
     362             : 
     363             :   // Draw using the drawable the caller provided.
     364          36 :   RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
     365          18 :   MOZ_ASSERT(ctx);  // Already checked the draw target above.
     366          54 :   gfxUtils::DrawPixelSnapped(ctx, aDrawable, SizeDouble(mFrameRect.Size()),
     367          36 :                              ImageRegion::Create(ThebesRect(mFrameRect)),
     368          18 :                              mFormat, aSamplingFilter, aImageFlags);
     369             : 
     370          18 :   if (canUseDataSurface && !mLockedSurface) {
     371           0 :     NS_WARNING("Failed to create VolatileDataSourceSurface");
     372           0 :     mAborted = true;
     373           0 :     return NS_ERROR_OUT_OF_MEMORY;
     374             :   }
     375             : 
     376          18 :   if (!canUseDataSurface) {
     377             :     // We used an offscreen surface, which is an "optimized" surface from
     378             :     // imgFrame's perspective.
     379          18 :     mOptSurface = target->Snapshot();
     380             :   } else {
     381           0 :     FinalizeSurface();
     382             :   }
     383             : 
     384             :   // If we reach this point, we should regard ourselves as complete.
     385          18 :   mDecoded = GetRect();
     386          18 :   mFinished = true;
     387             : 
     388             : #ifdef DEBUG
     389          36 :   MonitorAutoLock lock(mMonitor);
     390          18 :   MOZ_ASSERT(AreAllPixelsWritten());
     391             : #endif
     392             : 
     393          18 :   return NS_OK;
     394             : }
     395             : 
     396             : nsresult
     397          35 : imgFrame::Optimize(DrawTarget* aTarget)
     398             : {
     399          35 :   MOZ_ASSERT(NS_IsMainThread());
     400          35 :   mMonitor.AssertCurrentThreadOwns();
     401             : 
     402          35 :   if (mLockCount > 0 || !mOptimizable) {
     403             :     // Don't optimize right now.
     404          31 :     return NS_OK;
     405             :   }
     406             : 
     407             :   // Check whether image optimization is disabled -- not thread safe!
     408             :   static bool gDisableOptimize = false;
     409             :   static bool hasCheckedOptimize = false;
     410           4 :   if (!hasCheckedOptimize) {
     411           1 :     if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
     412           0 :       gDisableOptimize = true;
     413             :     }
     414           1 :     hasCheckedOptimize = true;
     415             :   }
     416             : 
     417             :   // Don't optimize during shutdown because gfxPlatform may not be available.
     418           4 :   if (ShutdownTracker::ShutdownHasStarted()) {
     419           0 :     return NS_OK;
     420             :   }
     421             : 
     422           4 :   if (gDisableOptimize) {
     423           0 :     return NS_OK;
     424             :   }
     425             : 
     426           4 :   if (mPalettedImageData || mOptSurface) {
     427           0 :     return NS_OK;
     428             :   }
     429             : 
     430             :   // XXX(seth): It's currently unclear if there's any reason why we can't
     431             :   // optimize non-premult surfaces. We should look into removing this.
     432           4 :   if (mNonPremult) {
     433           0 :     return NS_OK;
     434             :   }
     435             : 
     436             :   mOptSurface = gfxPlatform::GetPlatform()
     437           4 :     ->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mLockedSurface);
     438           4 :   if (mOptSurface == mLockedSurface) {
     439           4 :     mOptSurface = nullptr;
     440             :   }
     441             : 
     442           4 :   if (mOptSurface) {
     443             :     // There's no reason to keep our original surface around if we have an
     444             :     // optimized surface. Release our reference to it. This will leave
     445             :     // |mLockedSurface| as the only thing keeping it alive, so it'll get freed
     446             :     // below.
     447           0 :     mRawSurface = nullptr;
     448             :   }
     449             : 
     450             :   // Release all strong references to the surface's memory. If the underlying
     451             :   // surface is volatile, this will allow the operating system to free the
     452             :   // memory if it needs to.
     453           4 :   mLockedSurface = nullptr;
     454           4 :   mOptimizable = false;
     455             : 
     456           4 :   return NS_OK;
     457             : }
     458             : 
     459             : DrawableFrameRef
     460         284 : imgFrame::DrawableRef()
     461             : {
     462         284 :   return DrawableFrameRef(this);
     463             : }
     464             : 
     465             : RawAccessFrameRef
     466         240 : imgFrame::RawAccessRef()
     467             : {
     468         240 :   return RawAccessFrameRef(this);
     469             : }
     470             : 
     471             : void
     472          36 : imgFrame::SetRawAccessOnly()
     473             : {
     474          36 :   AssertImageDataLocked();
     475             : 
     476             :   // Lock our data and throw away the key.
     477          36 :   LockImageData();
     478          36 : }
     479             : 
     480             : 
     481             : imgFrame::SurfaceWithFormat
     482          35 : imgFrame::SurfaceForDrawing(bool               aDoPartialDecode,
     483             :                             bool               aDoTile,
     484             :                             ImageRegion&       aRegion,
     485             :                             SourceSurface*     aSurface)
     486             : {
     487          35 :   MOZ_ASSERT(NS_IsMainThread());
     488          35 :   mMonitor.AssertCurrentThreadOwns();
     489             : 
     490          35 :   if (!aDoPartialDecode) {
     491         105 :     return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, mImageSize),
     492         105 :                              mFormat);
     493             :   }
     494             : 
     495           0 :   gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width,
     496           0 :                               mDecoded.height);
     497             : 
     498           0 :   if (aDoTile) {
     499             :     // Create a temporary surface.
     500             :     // Give this surface an alpha channel because there are
     501             :     // transparent pixels in the padding or undecoded area
     502             :     RefPtr<DrawTarget> target =
     503             :       gfxPlatform::GetPlatform()->
     504           0 :         CreateOffscreenContentDrawTarget(mImageSize, SurfaceFormat::B8G8R8A8);
     505           0 :     if (!target) {
     506           0 :       return SurfaceWithFormat();
     507             :     }
     508             : 
     509             :     SurfacePattern pattern(aSurface,
     510             :                            aRegion.GetExtendMode(),
     511           0 :                            Matrix::Translation(mDecoded.x, mDecoded.y));
     512           0 :     target->FillRect(ToRect(aRegion.Intersect(available).Rect()), pattern);
     513             : 
     514           0 :     RefPtr<SourceSurface> newsurf = target->Snapshot();
     515           0 :     return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, mImageSize),
     516           0 :                              target->GetFormat());
     517             :   }
     518             : 
     519             :   // Not tiling, and we have a surface, so we can account for
     520             :   // a partial decode just by twiddling parameters.
     521           0 :   aRegion = aRegion.Intersect(available);
     522           0 :   IntSize availableSize(mDecoded.width, mDecoded.height);
     523             : 
     524           0 :   return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize),
     525           0 :                            mFormat);
     526             : }
     527             : 
     528          35 : bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
     529             :                     SamplingFilter aSamplingFilter, uint32_t aImageFlags,
     530             :                     float aOpacity)
     531             : {
     532          70 :   AUTO_PROFILER_LABEL("imgFrame::Draw", GRAPHICS);
     533             : 
     534          35 :   MOZ_ASSERT(NS_IsMainThread());
     535          35 :   NS_ASSERTION(!aRegion.Rect().IsEmpty(), "Drawing empty region!");
     536          35 :   NS_ASSERTION(!aRegion.IsRestricted() ||
     537             :                !aRegion.Rect().Intersect(aRegion.Restriction()).IsEmpty(),
     538             :                "We must be allowed to sample *some* source pixels!");
     539          35 :   MOZ_ASSERT(mFrameRect.IsEqualEdges(IntRect(IntPoint(), mImageSize)),
     540             :              "Directly drawing an image with a non-trivial frame rect!");
     541             : 
     542          35 :   if (mPalettedImageData) {
     543           0 :     MOZ_ASSERT_UNREACHABLE("Directly drawing a paletted image!");
     544             :     return false;
     545             :   }
     546             : 
     547          70 :   MonitorAutoLock lock(mMonitor);
     548             : 
     549             :   // Possibly convert this image into a GPU texture, this may also cause our
     550             :   // mLockedSurface to be released and the OS to release the underlying memory.
     551          35 :   Optimize(aContext->GetDrawTarget());
     552             : 
     553          35 :   bool doPartialDecode = !AreAllPixelsWritten();
     554             : 
     555          70 :   RefPtr<SourceSurface> surf = GetSourceSurfaceInternal();
     556          35 :   if (!surf) {
     557           0 :     return false;
     558             :   }
     559             : 
     560          35 :   gfxRect imageRect(0, 0, mImageSize.width, mImageSize.height);
     561          43 :   bool doTile = !imageRect.Contains(aRegion.Rect()) &&
     562          43 :                 !(aImageFlags & imgIContainer::FLAG_CLAMP);
     563             : 
     564          35 :   ImageRegion region(aRegion);
     565             :   SurfaceWithFormat surfaceResult =
     566          70 :     SurfaceForDrawing(doPartialDecode, doTile, region, surf);
     567             : 
     568          35 :   if (surfaceResult.IsValid()) {
     569          70 :     gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
     570          70 :                                imageRect.Size(), region, surfaceResult.mFormat,
     571          35 :                                aSamplingFilter, aImageFlags, aOpacity);
     572             :   }
     573             : 
     574             :   // Image got put into a painted layer, it will not be shared with another
     575             :   // process.
     576          35 :   MarkSurfaceShared(surf);
     577          35 :   return true;
     578             : }
     579             : 
     580             : nsresult
     581         387 : imgFrame::ImageUpdated(const nsIntRect& aUpdateRect)
     582             : {
     583         775 :   MonitorAutoLock lock(mMonitor);
     584         776 :   return ImageUpdatedInternal(aUpdateRect);
     585             : }
     586             : 
     587             : nsresult
     588         458 : imgFrame::ImageUpdatedInternal(const nsIntRect& aUpdateRect)
     589             : {
     590         458 :   mMonitor.AssertCurrentThreadOwns();
     591             : 
     592         458 :   mDecoded.UnionRect(mDecoded, aUpdateRect);
     593             : 
     594             :   // Clamp to the frame rect to ensure that decoder bugs don't result in a
     595             :   // decoded rect that extends outside the bounds of the frame rect.
     596         458 :   mDecoded.IntersectRect(mDecoded, mFrameRect);
     597             : 
     598         458 :   return NS_OK;
     599             : }
     600             : 
     601             : void
     602          70 : imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */,
     603             :                  DisposalMethod aDisposalMethod /* = DisposalMethod::KEEP */,
     604             :                  FrameTimeout aTimeout
     605             :                    /* = FrameTimeout::FromRawMilliseconds(0) */,
     606             :                  BlendMethod aBlendMethod /* = BlendMethod::OVER */,
     607             :                  const Maybe<IntRect>& aBlendRect /* = Nothing() */,
     608             :                  bool aFinalize /* = true */)
     609             : {
     610         140 :   MonitorAutoLock lock(mMonitor);
     611          70 :   MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
     612             : 
     613          70 :   mDisposalMethod = aDisposalMethod;
     614          70 :   mTimeout = aTimeout;
     615          70 :   mBlendMethod = aBlendMethod;
     616          70 :   mBlendRect = aBlendRect;
     617          70 :   ImageUpdatedInternal(GetRect());
     618             : 
     619          70 :   if (aFinalize) {
     620          70 :     FinalizeSurfaceInternal();
     621             :   }
     622             : 
     623          70 :   mFinished = true;
     624             : 
     625             :   // The image is now complete, wake up anyone who's waiting.
     626          70 :   mMonitor.NotifyAll();
     627          70 : }
     628             : 
     629             : uint32_t
     630         192 : imgFrame::GetImageBytesPerRow() const
     631             : {
     632         192 :   mMonitor.AssertCurrentThreadOwns();
     633             : 
     634         192 :   if (mRawSurface) {
     635         192 :     return mFrameRect.width * BytesPerPixel(mFormat);
     636             :   }
     637             : 
     638           0 :   if (mPaletteDepth) {
     639           0 :     return mFrameRect.width;
     640             :   }
     641             : 
     642           0 :   return 0;
     643             : }
     644             : 
     645             : uint32_t
     646         192 : imgFrame::GetImageDataLength() const
     647             : {
     648         192 :   return GetImageBytesPerRow() * mFrameRect.height;
     649             : }
     650             : 
     651             : void
     652          48 : imgFrame::GetImageData(uint8_t** aData, uint32_t* aLength) const
     653             : {
     654          96 :   MonitorAutoLock lock(mMonitor);
     655          48 :   GetImageDataInternal(aData, aLength);
     656          48 : }
     657             : 
     658             : void
     659         192 : imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const
     660             : {
     661         192 :   mMonitor.AssertCurrentThreadOwns();
     662         192 :   MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
     663             : 
     664         192 :   if (mLockedSurface) {
     665             :     // TODO: This is okay for now because we only realloc shared surfaces on
     666             :     // the main thread after decoding has finished, but if animations want to
     667             :     // read frame data off the main thread, we will need to reconsider this.
     668         192 :     *aData = mLockedSurface->GetData();
     669         192 :     MOZ_ASSERT(*aData,
     670             :       "mLockedSurface is non-null, but GetData is null in GetImageData");
     671           0 :   } else if (mPalettedImageData) {
     672           0 :     *aData = mPalettedImageData + PaletteDataLength();
     673           0 :     MOZ_ASSERT(*aData,
     674             :       "mPalettedImageData is non-null, but result is null in GetImageData");
     675             :   } else {
     676           0 :     MOZ_ASSERT(false,
     677             :       "Have neither mLockedSurface nor mPalettedImageData in GetImageData");
     678             :     *aData = nullptr;
     679             :   }
     680             : 
     681         192 :   *aLength = GetImageDataLength();
     682         192 : }
     683             : 
     684             : uint8_t*
     685           0 : imgFrame::GetImageData() const
     686             : {
     687             :   uint8_t* data;
     688             :   uint32_t length;
     689           0 :   GetImageData(&data, &length);
     690           0 :   return data;
     691             : }
     692             : 
     693             : bool
     694         109 : imgFrame::GetIsPaletted() const
     695             : {
     696         109 :   return mPalettedImageData != nullptr;
     697             : }
     698             : 
     699             : void
     700          48 : imgFrame::GetPaletteData(uint32_t** aPalette, uint32_t* length) const
     701             : {
     702          48 :   AssertImageDataLocked();
     703             : 
     704          48 :   if (!mPalettedImageData) {
     705          48 :     *aPalette = nullptr;
     706          48 :     *length = 0;
     707             :   } else {
     708           0 :     *aPalette = (uint32_t*) mPalettedImageData;
     709           0 :     *length = PaletteDataLength();
     710             :   }
     711          48 : }
     712             : 
     713             : uint32_t*
     714           0 : imgFrame::GetPaletteData() const
     715             : {
     716             :   uint32_t* data;
     717             :   uint32_t length;
     718           0 :   GetPaletteData(&data, &length);
     719           0 :   return data;
     720             : }
     721             : 
     722             : nsresult
     723         276 : imgFrame::LockImageData()
     724             : {
     725         552 :   MonitorAutoLock lock(mMonitor);
     726             : 
     727         276 :   MOZ_ASSERT(mLockCount >= 0, "Unbalanced locks and unlocks");
     728         276 :   if (mLockCount < 0) {
     729           0 :     return NS_ERROR_FAILURE;
     730             :   }
     731             : 
     732         276 :   mLockCount++;
     733             : 
     734             :   // If we are not the first lock, there's nothing to do.
     735         276 :   if (mLockCount != 1) {
     736         226 :     return NS_OK;
     737             :   }
     738             : 
     739             :   // If we're the first lock, but have the locked surface, we're OK.
     740          50 :   if (mLockedSurface) {
     741          50 :     return NS_OK;
     742             :   }
     743             : 
     744             :   // Paletted images don't have surfaces, so there's nothing to do.
     745           0 :   if (mPalettedImageData) {
     746           0 :     return NS_OK;
     747             :   }
     748             : 
     749           0 :   MOZ_ASSERT_UNREACHABLE("It's illegal to re-lock an optimized imgFrame");
     750             :   return NS_ERROR_FAILURE;
     751             : }
     752             : 
     753             : void
     754          96 : imgFrame::AssertImageDataLocked() const
     755             : {
     756             : #ifdef DEBUG
     757         192 :   MonitorAutoLock lock(mMonitor);
     758          96 :   MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
     759             : #endif
     760          96 : }
     761             : 
     762             : nsresult
     763         202 : imgFrame::UnlockImageData()
     764             : {
     765         404 :   MonitorAutoLock lock(mMonitor);
     766             : 
     767         202 :   MOZ_ASSERT(mLockCount > 0, "Unlocking an unlocked image!");
     768         202 :   if (mLockCount <= 0) {
     769           0 :     return NS_ERROR_FAILURE;
     770             :   }
     771             : 
     772         202 :   MOZ_ASSERT(mLockCount > 1 || mFinished || mAborted,
     773             :              "Should have Finish()'d or aborted before unlocking");
     774             : 
     775         202 :   mLockCount--;
     776             : 
     777         202 :   return NS_OK;
     778             : }
     779             : 
     780             : void
     781          12 : imgFrame::SetOptimizable()
     782             : {
     783          12 :   AssertImageDataLocked();
     784          24 :   MonitorAutoLock lock(mMonitor);
     785          12 :   mOptimizable = true;
     786          12 : }
     787             : 
     788             : void
     789           0 : imgFrame::FinalizeSurface()
     790             : {
     791           0 :   MonitorAutoLock lock(mMonitor);
     792           0 :   FinalizeSurfaceInternal();
     793           0 : }
     794             : 
     795             : void
     796          70 : imgFrame::FinalizeSurfaceInternal()
     797             : {
     798          70 :   mMonitor.AssertCurrentThreadOwns();
     799             : 
     800             :   // Not all images will have mRawSurface to finalize (i.e. paletted images).
     801          70 :   if (!mRawSurface || mRawSurface->GetType() != SurfaceType::DATA_SHARED) {
     802          70 :     return;
     803             :   }
     804             : 
     805           0 :   auto sharedSurf = static_cast<SourceSurfaceSharedData*>(mRawSurface.get());
     806           0 :   sharedSurf->Finalize();
     807             : }
     808             : 
     809             : already_AddRefed<SourceSurface>
     810          73 : imgFrame::GetSourceSurface()
     811             : {
     812         146 :   MonitorAutoLock lock(mMonitor);
     813         146 :   return GetSourceSurfaceInternal();
     814             : }
     815             : 
     816             : already_AddRefed<SourceSurface>
     817         108 : imgFrame::GetSourceSurfaceInternal()
     818             : {
     819         108 :   mMonitor.AssertCurrentThreadOwns();
     820             : 
     821         108 :   if (mOptSurface) {
     822          73 :     if (mOptSurface->IsValid()) {
     823         146 :       RefPtr<SourceSurface> surf(mOptSurface);
     824          73 :       return surf.forget();
     825             :     } else {
     826           0 :       mOptSurface = nullptr;
     827             :     }
     828             :   }
     829             : 
     830          35 :   if (mLockedSurface) {
     831          22 :     RefPtr<SourceSurface> surf(mLockedSurface);
     832          11 :     return surf.forget();
     833             :   }
     834             : 
     835          24 :   if (!mRawSurface) {
     836           0 :     return nullptr;
     837             :   }
     838             : 
     839          24 :   return CreateLockedSurface(mRawSurface, mFrameRect.Size(), mFormat);
     840             : }
     841             : 
     842             : AnimationData
     843         144 : imgFrame::GetAnimationData() const
     844             : {
     845         288 :   MonitorAutoLock lock(mMonitor);
     846         144 :   MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
     847             : 
     848             :   uint8_t* data;
     849         144 :   if (mPalettedImageData) {
     850           0 :     data = mPalettedImageData;
     851             :   } else {
     852             :     uint32_t length;
     853         144 :     GetImageDataInternal(&data, &length);
     854             :   }
     855             : 
     856         144 :   bool hasAlpha = mFormat == SurfaceFormat::B8G8R8A8;
     857             : 
     858         288 :   return AnimationData(data, PaletteDataLength(), mTimeout, GetRect(),
     859         432 :                        mBlendMethod, mBlendRect, mDisposalMethod, hasAlpha);
     860             : }
     861             : 
     862             : void
     863           0 : imgFrame::Abort()
     864             : {
     865           0 :   MonitorAutoLock lock(mMonitor);
     866             : 
     867           0 :   mAborted = true;
     868             : 
     869             :   // Wake up anyone who's waiting.
     870           0 :   mMonitor.NotifyAll();
     871           0 : }
     872             : 
     873             : bool
     874          24 : imgFrame::IsAborted() const
     875             : {
     876          48 :   MonitorAutoLock lock(mMonitor);
     877          48 :   return mAborted;
     878             : }
     879             : 
     880             : bool
     881         101 : imgFrame::IsFinished() const
     882             : {
     883         202 :   MonitorAutoLock lock(mMonitor);
     884         202 :   return mFinished;
     885             : }
     886             : 
     887             : void
     888           0 : imgFrame::WaitUntilFinished() const
     889             : {
     890           0 :   MonitorAutoLock lock(mMonitor);
     891             : 
     892             :   while (true) {
     893             :     // Return if we're aborted or complete.
     894           0 :     if (mAborted || mFinished) {
     895           0 :       return;
     896             :     }
     897             : 
     898             :     // Not complete yet, so we'll have to wait.
     899           0 :     mMonitor.Wait();
     900             :   }
     901             : }
     902             : 
     903             : bool
     904          53 : imgFrame::AreAllPixelsWritten() const
     905             : {
     906          53 :   mMonitor.AssertCurrentThreadOwns();
     907          53 :   return mDecoded.IsEqualInterior(mFrameRect);
     908             : }
     909             : 
     910          46 : bool imgFrame::GetCompositingFailed() const
     911             : {
     912          46 :   MOZ_ASSERT(NS_IsMainThread());
     913          46 :   return mCompositingFailed;
     914             : }
     915             : 
     916             : void
     917          22 : imgFrame::SetCompositingFailed(bool val)
     918             : {
     919          22 :   MOZ_ASSERT(NS_IsMainThread());
     920          22 :   mCompositingFailed = val;
     921          22 : }
     922             : 
     923             : void
     924           0 : imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
     925             :                                  size_t& aHeapSizeOut,
     926             :                                  size_t& aNonHeapSizeOut,
     927             :                                  size_t& aSharedHandlesOut) const
     928             : {
     929           0 :   MonitorAutoLock lock(mMonitor);
     930             : 
     931           0 :   if (mPalettedImageData) {
     932           0 :     aHeapSizeOut += aMallocSizeOf(mPalettedImageData);
     933             :   }
     934           0 :   if (mLockedSurface) {
     935           0 :     aHeapSizeOut += aMallocSizeOf(mLockedSurface);
     936             :   }
     937           0 :   if (mOptSurface) {
     938           0 :     aHeapSizeOut += aMallocSizeOf(mOptSurface);
     939             :   }
     940           0 :   if (mRawSurface) {
     941           0 :     aHeapSizeOut += aMallocSizeOf(mRawSurface);
     942           0 :     mRawSurface->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
     943           0 :                                         aNonHeapSizeOut);
     944             : 
     945           0 :     if (mRawSurface->GetType() == SurfaceType::DATA_SHARED) {
     946             :       auto sharedSurface =
     947           0 :         static_cast<SourceSurfaceSharedData*>(mRawSurface.get());
     948           0 :       if (sharedSurface->CanShare()) {
     949           0 :         ++aSharedHandlesOut;
     950             :       }
     951             :     }
     952             :   }
     953           0 : }
     954             : 
     955             : } // namespace image
     956             : } // namespace mozilla

Generated by: LCOV version 1.13