123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "imgFrame.h"
- #include "ImageRegion.h"
- #include "ShutdownTracker.h"
- #include "prenv.h"
- #include "gfx2DGlue.h"
- #include "gfxPlatform.h"
- #include "gfxPrefs.h"
- #include "gfxUtils.h"
- #include "gfxAlphaRecovery.h"
- #include "GeckoProfiler.h"
- #include "MainThreadUtils.h"
- #include "mozilla/CheckedInt.h"
- #include "mozilla/gfx/Tools.h"
- #include "mozilla/Likely.h"
- #include "mozilla/MemoryReporting.h"
- #include "nsMargin.h"
- #include "nsThreadUtils.h"
- namespace mozilla {
- using namespace gfx;
- namespace image {
- static void
- VolatileBufferRelease(void* vbuf)
- {
- delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf);
- }
- static int32_t
- VolatileSurfaceStride(const IntSize& size, SurfaceFormat format)
- {
- // Stride must be a multiple of four or cairo will complain.
- return (size.width * BytesPerPixel(format) + 0x3) & ~0x3;
- }
- static already_AddRefed<DataSourceSurface>
- CreateLockedSurface(VolatileBuffer* vbuf,
- const IntSize& size,
- SurfaceFormat format)
- {
- VolatileBufferPtr<unsigned char>* vbufptr =
- new VolatileBufferPtr<unsigned char>(vbuf);
- MOZ_ASSERT(!vbufptr->WasBufferPurged(), "Expected image data!");
- const int32_t stride = VolatileSurfaceStride(size, format);
- // The VolatileBufferPtr is held by this DataSourceSurface.
- RefPtr<DataSourceSurface> surf =
- Factory::CreateWrappingDataSourceSurface(*vbufptr, stride, size, format,
- &VolatileBufferRelease,
- static_cast<void*>(vbufptr));
- if (!surf) {
- delete vbufptr;
- return nullptr;
- }
- return surf.forget();
- }
- static already_AddRefed<VolatileBuffer>
- AllocateBufferForImage(const IntSize& size, SurfaceFormat format)
- {
- int32_t stride = VolatileSurfaceStride(size, format);
- RefPtr<VolatileBuffer> buf = new VolatileBuffer();
- if (buf->Init(stride * size.height,
- size_t(1) << gfxAlphaRecovery::GoodAlignmentLog2())) {
- return buf.forget();
- }
- return nullptr;
- }
- static bool
- ClearSurface(VolatileBuffer* aVBuf, const IntSize& aSize, SurfaceFormat aFormat)
- {
- VolatileBufferPtr<unsigned char> vbufptr(aVBuf);
- if (vbufptr.WasBufferPurged()) {
- NS_WARNING("VolatileBuffer was purged");
- return false;
- }
- int32_t stride = VolatileSurfaceStride(aSize, aFormat);
- if (aFormat == SurfaceFormat::B8G8R8X8) {
- // Skia doesn't support RGBX surfaces, so ensure the alpha value is set
- // to opaque white. While it would be nice to only do this for Skia,
- // imgFrame can run off main thread and past shutdown where
- // we might not have gfxPlatform, so just memset everytime instead.
- memset(vbufptr, 0xFF, stride * aSize.height);
- } else if (aVBuf->OnHeap()) {
- // We only need to memset it if the buffer was allocated on the heap.
- // Otherwise, it's allocated via mmap and refers to a zeroed page and will
- // be COW once it's written to.
- memset(vbufptr, 0, stride * aSize.height);
- }
- return true;
- }
- // Returns true if an image of aWidth x aHeight is allowed and legal.
- static bool
- AllowedImageSize(int32_t aWidth, int32_t aHeight)
- {
- // reject over-wide or over-tall images
- const int32_t k64KLimit = 0x0000FFFF;
- if (MOZ_UNLIKELY(aWidth > k64KLimit || aHeight > k64KLimit )) {
- NS_WARNING("image too big");
- return false;
- }
- // protect against invalid sizes
- if (MOZ_UNLIKELY(aHeight <= 0 || aWidth <= 0)) {
- return false;
- }
- // check to make sure we don't overflow 32-bit size for RGBA
- CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * 4;
- if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
- NS_WARNING("width or height too large");
- return false;
- }
- return true;
- }
- static bool AllowedImageAndFrameDimensions(const nsIntSize& aImageSize,
- const nsIntRect& aFrameRect)
- {
- if (!AllowedImageSize(aImageSize.width, aImageSize.height)) {
- return false;
- }
- if (!AllowedImageSize(aFrameRect.width, aFrameRect.height)) {
- return false;
- }
- nsIntRect imageRect(0, 0, aImageSize.width, aImageSize.height);
- if (!imageRect.Contains(aFrameRect)) {
- NS_WARNING("Animated image frame does not fit inside bounds of image");
- }
- return true;
- }
- imgFrame::imgFrame()
- : mMonitor("imgFrame")
- , mDecoded(0, 0, 0, 0)
- , mLockCount(0)
- , mHasNoAlpha(false)
- , mAborted(false)
- , mFinished(false)
- , mOptimizable(false)
- , mTimeout(FrameTimeout::FromRawMilliseconds(100))
- , mDisposalMethod(DisposalMethod::NOT_SPECIFIED)
- , mBlendMethod(BlendMethod::OVER)
- , mPalettedImageData(nullptr)
- , mPaletteDepth(0)
- , mNonPremult(false)
- , mCompositingFailed(false)
- {
- }
- imgFrame::~imgFrame()
- {
- #ifdef DEBUG
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(mAborted || AreAllPixelsWritten());
- MOZ_ASSERT(mAborted || mFinished);
- #endif
- free(mPalettedImageData);
- mPalettedImageData = nullptr;
- }
- nsresult
- imgFrame::InitForDecoder(const nsIntSize& aImageSize,
- const nsIntRect& aRect,
- SurfaceFormat aFormat,
- uint8_t aPaletteDepth /* = 0 */,
- bool aNonPremult /* = false */,
- const Maybe<AnimationParams>& aAnimParams /* = Nothing() */)
- {
- // Assert for properties that should be verified by decoders,
- // warn for properties related to bad content.
- if (!AllowedImageAndFrameDimensions(aImageSize, aRect)) {
- NS_WARNING("Should have legal image size");
- mAborted = true;
- return NS_ERROR_FAILURE;
- }
- mImageSize = aImageSize;
- mFrameRect = aRect;
- if (aAnimParams) {
- mBlendRect = aAnimParams->mBlendRect;
- mTimeout = aAnimParams->mTimeout;
- mBlendMethod = aAnimParams->mBlendMethod;
- mDisposalMethod = aAnimParams->mDisposalMethod;
- } else {
- mBlendRect = aRect;
- }
- // We only allow a non-trivial frame rect (i.e., a frame rect that doesn't
- // cover the entire image) for paletted animation frames. We never draw those
- // frames directly; we just use FrameAnimator to composite them and produce a
- // BGRA surface that we actually draw. We enforce this here to make sure that
- // imgFrame::Draw(), which is responsible for drawing all other kinds of
- // frames, never has to deal with a non-trivial frame rect.
- if (aPaletteDepth == 0 &&
- !mFrameRect.IsEqualEdges(IntRect(IntPoint(), mImageSize))) {
- MOZ_ASSERT_UNREACHABLE("Creating a non-paletted imgFrame with a "
- "non-trivial frame rect");
- return NS_ERROR_FAILURE;
- }
- mFormat = aFormat;
- mPaletteDepth = aPaletteDepth;
- mNonPremult = aNonPremult;
- if (aPaletteDepth != 0) {
- // We're creating for a paletted image.
- if (aPaletteDepth > 8) {
- NS_WARNING("Should have legal palette depth");
- NS_ERROR("This Depth is not supported");
- mAborted = true;
- return NS_ERROR_FAILURE;
- }
- // Use the fallible allocator here. Paletted images always use 1 byte per
- // pixel, so calculating the amount of memory we need is straightforward.
- size_t dataSize = PaletteDataLength() + mFrameRect.Area();
- mPalettedImageData = static_cast<uint8_t*>(calloc(dataSize, sizeof(uint8_t)));
- if (!mPalettedImageData) {
- NS_WARNING("Call to calloc for paletted image data should succeed");
- }
- NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
- } else {
- MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitForDecoder() twice?");
- mVBuf = AllocateBufferForImage(mFrameRect.Size(), mFormat);
- if (!mVBuf) {
- mAborted = true;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- mImageSurface = CreateLockedSurface(mVBuf, mFrameRect.Size(), mFormat);
- if (!mImageSurface) {
- NS_WARNING("Failed to create ImageSurface");
- mAborted = true;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- if (!ClearSurface(mVBuf, mFrameRect.Size(), mFormat)) {
- NS_WARNING("Could not clear allocated buffer");
- mAborted = true;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- return NS_OK;
- }
- nsresult
- imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
- const nsIntSize& aSize,
- const SurfaceFormat aFormat,
- SamplingFilter aSamplingFilter,
- uint32_t aImageFlags,
- gfx::BackendType aBackend)
- {
- // Assert for properties that should be verified by decoders,
- // warn for properties related to bad content.
- if (!AllowedImageSize(aSize.width, aSize.height)) {
- NS_WARNING("Should have legal image size");
- mAborted = true;
- return NS_ERROR_FAILURE;
- }
- mImageSize = aSize;
- mFrameRect = IntRect(IntPoint(0, 0), aSize);
- mFormat = aFormat;
- mPaletteDepth = 0;
- RefPtr<DrawTarget> target;
- bool canUseDataSurface =
- gfxPlatform::GetPlatform()->CanRenderContentToDataSurface();
- if (canUseDataSurface) {
- // It's safe to use data surfaces for content on this platform, so we can
- // get away with using volatile buffers.
- MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitWithDrawable() twice?");
- mVBuf = AllocateBufferForImage(mFrameRect.Size(), mFormat);
- if (!mVBuf) {
- mAborted = true;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- int32_t stride = VolatileSurfaceStride(mFrameRect.Size(), mFormat);
- VolatileBufferPtr<uint8_t> ptr(mVBuf);
- if (!ptr) {
- mAborted = true;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- mImageSurface = CreateLockedSurface(mVBuf, mFrameRect.Size(), mFormat);
- if (!mImageSurface) {
- NS_WARNING("Failed to create ImageSurface");
- mAborted = true;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- if (!ClearSurface(mVBuf, mFrameRect.Size(), mFormat)) {
- NS_WARNING("Could not clear allocated buffer");
- mAborted = true;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- target = gfxPlatform::CreateDrawTargetForData(
- ptr,
- mFrameRect.Size(),
- stride,
- mFormat);
- } else {
- // We can't use data surfaces for content, so we'll create an offscreen
- // surface instead. This means if someone later calls RawAccessRef(), we
- // may have to do an expensive readback, but we warned callers about that in
- // the documentation for this method.
- MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?");
- if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(aBackend)) {
- target = gfxPlatform::GetPlatform()->
- CreateDrawTargetForBackend(aBackend, mFrameRect.Size(), mFormat);
- } else {
- target = gfxPlatform::GetPlatform()->
- CreateOffscreenContentDrawTarget(mFrameRect.Size(), mFormat);
- }
- }
- if (!target || !target->IsValid()) {
- mAborted = true;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- // Draw using the drawable the caller provided.
- RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
- MOZ_ASSERT(ctx); // Already checked the draw target above.
- gfxUtils::DrawPixelSnapped(ctx, aDrawable, mFrameRect.Size(),
- ImageRegion::Create(ThebesRect(mFrameRect)),
- mFormat, aSamplingFilter, aImageFlags);
- if (canUseDataSurface && !mImageSurface) {
- NS_WARNING("Failed to create VolatileDataSourceSurface");
- mAborted = true;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- if (!canUseDataSurface) {
- // We used an offscreen surface, which is an "optimized" surface from
- // imgFrame's perspective.
- mOptSurface = target->Snapshot();
- }
- // If we reach this point, we should regard ourselves as complete.
- mDecoded = GetRect();
- mFinished = true;
- #ifdef DEBUG
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(AreAllPixelsWritten());
- #endif
- return NS_OK;
- }
- bool
- imgFrame::CanOptimizeOpaqueImage()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(!ShutdownTracker::ShutdownHasStarted());
- mMonitor.AssertCurrentThreadOwns();
- // If we're using a surface format with alpha but the image has no alpha,
- // change the format. This doesn't change the underlying data at all, but
- // allows DrawTargets to avoid blending when drawing known opaque images.
- // This optimization is free and safe, so we always do it when we can except
- // if we have a Skia backend. Skia doesn't support RGBX so ensure we don't
- // optimize to a RGBX surface.
- return mHasNoAlpha && mFormat == SurfaceFormat::B8G8R8A8 && mImageSurface &&
- (gfxPlatform::GetPlatform()->GetDefaultContentBackend() != BackendType::SKIA);
- }
- nsresult
- imgFrame::Optimize(DrawTarget* aTarget)
- {
- MOZ_ASSERT(NS_IsMainThread());
- mMonitor.AssertCurrentThreadOwns();
-
- if (mLockCount > 0 || !mOptimizable) {
- // Don't optimize right now.
- return NS_OK;
- }
- // Check whether image optimization is disabled -- not thread safe!
- static bool gDisableOptimize = false;
- static bool hasCheckedOptimize = false;
- if (!hasCheckedOptimize) {
- if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
- gDisableOptimize = true;
- }
- hasCheckedOptimize = true;
- }
- // Don't optimize during shutdown because gfxPlatform may not be available.
- if (ShutdownTracker::ShutdownHasStarted()) {
- return NS_OK;
- }
- // This optimization is basically free, so we perform it even if optimization is disabled.
- if (CanOptimizeOpaqueImage()) {
- mFormat = SurfaceFormat::B8G8R8X8;
- mImageSurface = CreateLockedSurface(mVBuf, mFrameRect.Size(), mFormat);
- }
- if (gDisableOptimize) {
- return NS_OK;
- }
- if (mPalettedImageData || mOptSurface) {
- return NS_OK;
- }
- // XXX(seth): It's currently unclear if there's any reason why we can't
- // optimize non-premult surfaces. We should look into removing this.
- if (mNonPremult) {
- return NS_OK;
- }
- mOptSurface = gfxPlatform::GetPlatform()
- ->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mImageSurface);
- if (mOptSurface == mImageSurface) {
- mOptSurface = nullptr;
- }
- if (mOptSurface) {
- // There's no reason to keep our volatile buffer around at all if we have an
- // optimized surface. Release our reference to it. This will leave
- // |mVBufPtr| and |mImageSurface| as the only things keeping it alive, so
- // it'll get freed below.
- mVBuf = nullptr;
- }
- // Release all strong references to our volatile buffer's memory. This will
- // allow the operating system to free the memory if it needs to.
- mVBufPtr = nullptr;
- mImageSurface = nullptr;
- mOptimizable = false;
- return NS_OK;
- }
- DrawableFrameRef
- imgFrame::DrawableRef()
- {
- return DrawableFrameRef(this);
- }
- RawAccessFrameRef
- imgFrame::RawAccessRef()
- {
- return RawAccessFrameRef(this);
- }
- void
- imgFrame::SetRawAccessOnly()
- {
- AssertImageDataLocked();
- // Lock our data and throw away the key.
- LockImageData();
- }
- imgFrame::SurfaceWithFormat
- imgFrame::SurfaceForDrawing(bool aDoPartialDecode,
- bool aDoTile,
- ImageRegion& aRegion,
- SourceSurface* aSurface)
- {
- MOZ_ASSERT(NS_IsMainThread());
- mMonitor.AssertCurrentThreadOwns();
- if (!aDoPartialDecode) {
- return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, mImageSize),
- mFormat);
- }
- gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width,
- mDecoded.height);
- if (aDoTile) {
- // Create a temporary surface.
- // Give this surface an alpha channel because there are
- // transparent pixels in the padding or undecoded area
- RefPtr<DrawTarget> target =
- gfxPlatform::GetPlatform()->
- CreateOffscreenContentDrawTarget(mImageSize, SurfaceFormat::B8G8R8A8);
- if (!target) {
- return SurfaceWithFormat();
- }
- SurfacePattern pattern(aSurface,
- aRegion.GetExtendMode(),
- Matrix::Translation(mDecoded.x, mDecoded.y));
- target->FillRect(ToRect(aRegion.Intersect(available).Rect()), pattern);
- RefPtr<SourceSurface> newsurf = target->Snapshot();
- return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, mImageSize),
- target->GetFormat());
- }
- // Not tiling, and we have a surface, so we can account for
- // a partial decode just by twiddling parameters.
- aRegion = aRegion.Intersect(available);
- IntSize availableSize(mDecoded.width, mDecoded.height);
- return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize),
- mFormat);
- }
- bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
- SamplingFilter aSamplingFilter, uint32_t aImageFlags)
- {
- PROFILER_LABEL("imgFrame", "Draw",
- js::ProfileEntry::Category::GRAPHICS);
- MOZ_ASSERT(NS_IsMainThread());
- NS_ASSERTION(!aRegion.Rect().IsEmpty(), "Drawing empty region!");
- NS_ASSERTION(!aRegion.IsRestricted() ||
- !aRegion.Rect().Intersect(aRegion.Restriction()).IsEmpty(),
- "We must be allowed to sample *some* source pixels!");
- MOZ_ASSERT(mFrameRect.IsEqualEdges(IntRect(IntPoint(), mImageSize)),
- "Directly drawing an image with a non-trivial frame rect!");
- if (mPalettedImageData) {
- MOZ_ASSERT_UNREACHABLE("Directly drawing a paletted image!");
- return false;
- }
- MonitorAutoLock lock(mMonitor);
- // Possibly convert this image into a GPU texture, this may also cause our
- // mImageSurface to be released and the OS to release the underlying memory.
- Optimize(aContext->GetDrawTarget());
- bool doPartialDecode = !AreAllPixelsWritten();
- RefPtr<SourceSurface> surf = GetSourceSurfaceInternal();
- if (!surf) {
- return false;
- }
- gfxRect imageRect(0, 0, mImageSize.width, mImageSize.height);
- bool doTile = !imageRect.Contains(aRegion.Rect()) &&
- !(aImageFlags & imgIContainer::FLAG_CLAMP);
- ImageRegion region(aRegion);
- SurfaceWithFormat surfaceResult =
- SurfaceForDrawing(doPartialDecode, doTile, region, surf);
- if (surfaceResult.IsValid()) {
- gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
- imageRect.Size(), region, surfaceResult.mFormat,
- aSamplingFilter, aImageFlags);
- }
- return true;
- }
- nsresult
- imgFrame::ImageUpdated(const nsIntRect& aUpdateRect)
- {
- MonitorAutoLock lock(mMonitor);
- return ImageUpdatedInternal(aUpdateRect);
- }
- nsresult
- imgFrame::ImageUpdatedInternal(const nsIntRect& aUpdateRect)
- {
- mMonitor.AssertCurrentThreadOwns();
- mDecoded.UnionRect(mDecoded, aUpdateRect);
- // Clamp to the frame rect to ensure that decoder bugs don't result in a
- // decoded rect that extends outside the bounds of the frame rect.
- mDecoded.IntersectRect(mDecoded, mFrameRect);
- return NS_OK;
- }
- void
- imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */)
- {
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
- if (aFrameOpacity == Opacity::FULLY_OPAQUE) {
- mHasNoAlpha = true;
- }
- ImageUpdatedInternal(GetRect());
- mFinished = true;
- // The image is now complete, wake up anyone who's waiting.
- mMonitor.NotifyAll();
- }
- uint32_t
- imgFrame::GetImageBytesPerRow() const
- {
- mMonitor.AssertCurrentThreadOwns();
- if (mVBuf) {
- return mFrameRect.width * BytesPerPixel(mFormat);
- }
- if (mPaletteDepth) {
- return mFrameRect.width;
- }
- return 0;
- }
- uint32_t
- imgFrame::GetImageDataLength() const
- {
- return GetImageBytesPerRow() * mFrameRect.height;
- }
- void
- imgFrame::GetImageData(uint8_t** aData, uint32_t* aLength) const
- {
- MonitorAutoLock lock(mMonitor);
- GetImageDataInternal(aData, aLength);
- }
- void
- imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const
- {
- mMonitor.AssertCurrentThreadOwns();
- MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
- if (mImageSurface) {
- *aData = mVBufPtr;
- MOZ_ASSERT(*aData,
- "mImageSurface is non-null, but mVBufPtr is null in GetImageData");
- } else if (mPalettedImageData) {
- *aData = mPalettedImageData + PaletteDataLength();
- MOZ_ASSERT(*aData,
- "mPalettedImageData is non-null, but result is null in GetImageData");
- } else {
- MOZ_ASSERT(false,
- "Have neither mImageSurface nor mPalettedImageData in GetImageData");
- *aData = nullptr;
- }
- *aLength = GetImageDataLength();
- }
- uint8_t*
- imgFrame::GetImageData() const
- {
- uint8_t* data;
- uint32_t length;
- GetImageData(&data, &length);
- return data;
- }
- bool
- imgFrame::GetIsPaletted() const
- {
- return mPalettedImageData != nullptr;
- }
- void
- imgFrame::GetPaletteData(uint32_t** aPalette, uint32_t* length) const
- {
- AssertImageDataLocked();
- if (!mPalettedImageData) {
- *aPalette = nullptr;
- *length = 0;
- } else {
- *aPalette = (uint32_t*) mPalettedImageData;
- *length = PaletteDataLength();
- }
- }
- uint32_t*
- imgFrame::GetPaletteData() const
- {
- uint32_t* data;
- uint32_t length;
- GetPaletteData(&data, &length);
- return data;
- }
- nsresult
- imgFrame::LockImageData()
- {
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(mLockCount >= 0, "Unbalanced locks and unlocks");
- if (mLockCount < 0) {
- return NS_ERROR_FAILURE;
- }
- mLockCount++;
- // If we are not the first lock, there's nothing to do.
- if (mLockCount != 1) {
- return NS_OK;
- }
- // If we're the first lock, but have an image surface, we're OK.
- if (mImageSurface) {
- mVBufPtr = mVBuf;
- return NS_OK;
- }
- // Paletted images don't have surfaces, so there's nothing to do.
- if (mPalettedImageData) {
- return NS_OK;
- }
- MOZ_ASSERT_UNREACHABLE("It's illegal to re-lock an optimized imgFrame");
- return NS_ERROR_FAILURE;
- }
- void
- imgFrame::AssertImageDataLocked() const
- {
- #ifdef DEBUG
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
- #endif
- }
- nsresult
- imgFrame::UnlockImageData()
- {
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(mLockCount > 0, "Unlocking an unlocked image!");
- if (mLockCount <= 0) {
- return NS_ERROR_FAILURE;
- }
- MOZ_ASSERT(mLockCount > 1 || mFinished || mAborted,
- "Should have Finish()'d or aborted before unlocking");
- mLockCount--;
- return NS_OK;
- }
- void
- imgFrame::SetOptimizable()
- {
- AssertImageDataLocked();
- MonitorAutoLock lock(mMonitor);
- mOptimizable = true;
- }
- already_AddRefed<SourceSurface>
- imgFrame::GetSourceSurface()
- {
- MonitorAutoLock lock(mMonitor);
- return GetSourceSurfaceInternal();
- }
- already_AddRefed<SourceSurface>
- imgFrame::GetSourceSurfaceInternal()
- {
- mMonitor.AssertCurrentThreadOwns();
- if (mOptSurface) {
- if (mOptSurface->IsValid()) {
- RefPtr<SourceSurface> surf(mOptSurface);
- return surf.forget();
- } else {
- mOptSurface = nullptr;
- }
- }
- if (mImageSurface) {
- RefPtr<SourceSurface> surf(mImageSurface);
- return surf.forget();
- }
- if (!mVBuf) {
- return nullptr;
- }
- VolatileBufferPtr<char> buf(mVBuf);
- if (buf.WasBufferPurged()) {
- return nullptr;
- }
- return CreateLockedSurface(mVBuf, mFrameRect.Size(), mFormat);
- }
- AnimationData
- imgFrame::GetAnimationData() const
- {
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
- uint8_t* data;
- if (mPalettedImageData) {
- data = mPalettedImageData;
- } else {
- uint32_t length;
- GetImageDataInternal(&data, &length);
- }
- bool hasAlpha = mFormat == SurfaceFormat::B8G8R8A8;
- return AnimationData(data, PaletteDataLength(), mTimeout, GetRect(),
- mBlendMethod, Some(mBlendRect), mDisposalMethod, hasAlpha);
- }
- void
- imgFrame::Abort()
- {
- MonitorAutoLock lock(mMonitor);
- mAborted = true;
- // Wake up anyone who's waiting.
- mMonitor.NotifyAll();
- }
- bool
- imgFrame::IsAborted() const
- {
- MonitorAutoLock lock(mMonitor);
- return mAborted;
- }
- bool
- imgFrame::IsFinished() const
- {
- MonitorAutoLock lock(mMonitor);
- return mFinished;
- }
- void
- imgFrame::WaitUntilFinished() const
- {
- MonitorAutoLock lock(mMonitor);
- while (true) {
- // Return if we're aborted or complete.
- if (mAborted || mFinished) {
- return;
- }
- // Not complete yet, so we'll have to wait.
- mMonitor.Wait();
- }
- }
- bool
- imgFrame::AreAllPixelsWritten() const
- {
- mMonitor.AssertCurrentThreadOwns();
- return mDecoded.IsEqualInterior(mFrameRect);
- }
- bool imgFrame::GetCompositingFailed() const
- {
- MOZ_ASSERT(NS_IsMainThread());
- return mCompositingFailed;
- }
- void
- imgFrame::SetCompositingFailed(bool val)
- {
- MOZ_ASSERT(NS_IsMainThread());
- mCompositingFailed = val;
- }
- void
- imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
- size_t& aHeapSizeOut,
- size_t& aNonHeapSizeOut) const
- {
- MonitorAutoLock lock(mMonitor);
- if (mPalettedImageData) {
- aHeapSizeOut += aMallocSizeOf(mPalettedImageData);
- }
- if (mImageSurface) {
- aHeapSizeOut += aMallocSizeOf(mImageSurface);
- }
- if (mOptSurface) {
- aHeapSizeOut += aMallocSizeOf(mOptSurface);
- }
- if (mVBuf) {
- aHeapSizeOut += aMallocSizeOf(mVBuf);
- aHeapSizeOut += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
- aNonHeapSizeOut += mVBuf->NonHeapSizeOfExcludingThis();
- }
- }
- } // namespace image
- } // namespace mozilla
|