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::Optimize() { MOZ_ASSERT(NS_IsMainThread()); mMonitor.AssertCurrentThreadOwns(); MOZ_ASSERT(mLockCount == 1, "Should only optimize when holding the lock exclusively"); // 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 (!mOptimizable || gDisableOptimize) { return NS_OK; } if (mPalettedImageData || mOptSurface || mSinglePixel) { return NS_OK; } // Don't do single-color opts on non-premult data. // Cairo doesn't support non-premult single-colors. if (mNonPremult) { return NS_OK; } /* Figure out if the entire image is a constant color */ if (gfxPrefs::ImageSingleColorOptimizationEnabled() && mImageSurface->Stride() == mFrameRect.width * 4) { uint32_t* imgData = (uint32_t*) ((uint8_t*) mVBufPtr); uint32_t firstPixel = * (uint32_t*) imgData; uint32_t pixelCount = mFrameRect.Area() + 1; while (--pixelCount && *imgData++ == firstPixel) ; if (pixelCount == 0) { // all pixels were the same if (mFormat == SurfaceFormat::B8G8R8A8 || mFormat == SurfaceFormat::B8G8R8X8) { mSinglePixel = true; mSinglePixelColor.a = ((firstPixel >> 24) & 0xFF) * (1.0f / 255.0f); mSinglePixelColor.r = ((firstPixel >> 16) & 0xFF) * (1.0f / 255.0f); mSinglePixelColor.g = ((firstPixel >> 8) & 0xFF) * (1.0f / 255.0f); mSinglePixelColor.b = ((firstPixel >> 0) & 0xFF) * (1.0f / 255.0f); mSinglePixelColor.r /= mSinglePixelColor.a; mSinglePixelColor.g /= mSinglePixelColor.a; mSinglePixelColor.b /= mSinglePixelColor.a; // blow away the older surfaces (if they exist), to release their memory mVBuf = nullptr; mVBufPtr = nullptr; mImageSurface = nullptr; mOptSurface = nullptr; 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; }
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; mFrameRect = aRect; 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; }