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; }
nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */) { // assert for properties that should be verified by decoders, warn for properties related to bad content if (!AllowedImageSize(aWidth, aHeight)) { NS_WARNING("Should have legal image size"); return NS_ERROR_FAILURE; } mOffset.MoveTo(aX, aY); mSize.SizeTo(aWidth, aHeight); mFormat = aFormat; mPaletteDepth = aPaletteDepth; 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"); return NS_ERROR_FAILURE; } // Use the fallible allocator here mPalettedImageData = (uint8_t*)moz_malloc(PaletteDataLength() + GetImageDataLength()); if (!mPalettedImageData) NS_WARNING("moz_malloc for paletted image data should succeed"); NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY); } else { // Inform the discard tracker that we are going to allocate some memory. if (!DiscardTracker::TryAllocation(4 * mSize.width * mSize.height)) { NS_WARNING("Exceed the hard limit of decode image size"); return NS_ERROR_OUT_OF_MEMORY; } if (!mImageSurface) { mVBuf = AllocateBufferForImage(mSize, mFormat); if (!mVBuf) { return NS_ERROR_OUT_OF_MEMORY; } if (mVBuf->OnHeap()) { int32_t stride = VolatileSurfaceStride(mSize, mFormat); VolatileBufferPtr<uint8_t> ptr(mVBuf); memset(ptr, 0, stride * mSize.height); } mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat); } if (!mImageSurface) { NS_WARNING("Failed to create VolatileDataSourceSurface"); // Image surface allocation is failed, need to return // the booked buffer size. DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height); return NS_ERROR_OUT_OF_MEMORY; } mInformedDiscardTracker = true; } return NS_OK; }
nsresult imgFrame::InitWithDrawable(gfxDrawable* aDrawable, const nsIntSize& aSize, const SurfaceFormat aFormat, GraphicsFilter aFilter, uint32_t aImageFlags) { // 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; mOffset.MoveTo(0, 0); mSize.SizeTo(aSize.width, aSize.height); 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(mSize, mFormat); if (!mVBuf) { mAborted = true; return NS_ERROR_OUT_OF_MEMORY; } int32_t stride = VolatileSurfaceStride(mSize, mFormat); VolatileBufferPtr<uint8_t> ptr(mVBuf); if (!ptr) { mAborted = true; return NS_ERROR_OUT_OF_MEMORY; } if (mVBuf->OnHeap()) { memset(ptr, 0, stride * mSize.height); } mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat); target = gfxPlatform::GetPlatform()-> CreateDrawTargetForData(ptr, mSize, 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?"); target = gfxPlatform::GetPlatform()-> CreateOffscreenContentDrawTarget(mSize, mFormat); } if (!target) { mAborted = true; return NS_ERROR_OUT_OF_MEMORY; } // Draw using the drawable the caller provided. nsIntRect imageRect(0, 0, mSize.width, mSize.height); nsRefPtr<gfxContext> ctx = new gfxContext(target); gfxUtils::DrawPixelSnapped(ctx, aDrawable, mSize, ImageRegion::Create(imageRect), mFormat, aFilter, 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(); MOZ_ASSERT(IsImageComplete()); return NS_OK; }
nsresult imgFrame::InitForDecoder(const nsIntSize& aImageSize, const nsIntRect& aRect, SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */, bool aNonPremult /* = false */) { // 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; mOffset.MoveTo(aRect.x, aRect.y); mSize.SizeTo(aRect.width, aRect.height); 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. mPalettedImageData = static_cast<uint8_t*>(malloc(PaletteDataLength() + (mSize.width * mSize.height))); if (!mPalettedImageData) { NS_WARNING("malloc 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(mSize, mFormat); if (!mVBuf) { mAborted = true; return NS_ERROR_OUT_OF_MEMORY; } if (mVBuf->OnHeap()) { int32_t stride = VolatileSurfaceStride(mSize, mFormat); VolatileBufferPtr<uint8_t> ptr(mVBuf); memset(ptr, 0, stride * mSize.height); } mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat); if (!mImageSurface) { NS_WARNING("Failed to create VolatileDataSourceSurface"); 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) { // 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::GetPlatform()-> 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?"); 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; }