Bug 1054079 (Part 2) - Make it possible to initialize an imgFrame with a gfxDrawable. r=tn,mwu,mattwoodrow

This commit is contained in:
Seth Fowler 2014-09-14 15:22:45 -07:00
parent c636e71b32
commit 6e8296e45b
4 changed files with 166 additions and 32 deletions

View File

@ -11,6 +11,9 @@
#include "pixman.h"
namespace mozilla {
using namespace gfx;
namespace image {
FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */)
@ -258,8 +261,8 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// Create the Compositing Frame
if (!mAnim->compositingFrame) {
mAnim->compositingFrame.SetFrame(new imgFrame());
nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
gfx::SurfaceFormat::B8G8R8A8);
nsresult rv =
mAnim->compositingFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
if (NS_FAILED(rv)) {
mAnim->compositingFrame.SetFrame(nullptr);
return false;
@ -388,8 +391,9 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
// overwrite.
if (!mAnim->compositingPrevFrame) {
mAnim->compositingPrevFrame.SetFrame(new imgFrame());
nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
gfx::SurfaceFormat::B8G8R8A8);
nsresult rv =
mAnim->compositingPrevFrame->InitForDecoder(mSize,
SurfaceFormat::B8G8R8A8);
if (NS_FAILED(rv)) {
mAnim->compositingPrevFrame.SetFrame(nullptr);
return false;

View File

@ -223,8 +223,7 @@ public:
// that's what the scaler outputs.
nsRefPtr<imgFrame> tentativeDstFrame = new imgFrame();
nsresult rv =
tentativeDstFrame->Init(0, 0, dstSize.width, dstSize.height,
SurfaceFormat::B8G8R8A8);
tentativeDstFrame->InitForDecoder(dstSize, SurfaceFormat::B8G8R8A8);
if (NS_FAILED(rv)) {
return false;
}
@ -1174,7 +1173,8 @@ RasterImage::InternalAddFrame(uint32_t framenum,
nsRefPtr<imgFrame> frame(new imgFrame());
nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
nsIntRect frameRect(aX, aY, aWidth, aHeight);
nsresult rv = frame->InitForDecoder(frameRect, aFormat, aPaletteDepth);
if (!(mSize.width > 0 && mSize.height > 0))
NS_WARNING("Shouldn't call InternalAddFrame with zero size");
if (!NS_SUCCEEDED(rv))
@ -1358,7 +1358,8 @@ RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
mFrameBlender.RemoveFrame(aFrameNum);
nsRefPtr<imgFrame> newFrame(new imgFrame());
nsresult rv = newFrame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
nsIntRect frameRect(aX, aY, aWidth, aHeight);
nsresult rv = newFrame->InitForDecoder(frameRect, aFormat, aPaletteDepth);
NS_ENSURE_SUCCESS(rv, rv);
return InternalAddFrameHelper(aFrameNum, newFrame, imageData, imageLength,
paletteData, paletteLength, aRetFrame);

View File

@ -143,17 +143,20 @@ imgFrame::~imgFrame()
}
}
nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
nsresult
imgFrame::InitForDecoder(const nsIntRect& aRect,
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)) {
// Assert for properties that should be verified by decoders,
// warn for properties related to bad content.
if (!AllowedImageSize(aRect.width, aRect.height)) {
NS_WARNING("Should have legal image size");
return NS_ERROR_FAILURE;
}
mOffset.MoveTo(aX, aY);
mSize.SizeTo(aWidth, aHeight);
mOffset.MoveTo(aRect.x, aRect.y);
mSize.SizeTo(aRect.width, aRect.height);
mFormat = aFormat;
mPaletteDepth = aPaletteDepth;
@ -172,32 +175,121 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
NS_WARNING("moz_malloc for paletted image data should succeed");
NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
} else {
MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitForDecoder() twice?");
// 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");
mInformedDiscardTracker =
DiscardTracker::TryAllocation(4 * mSize.width * mSize.height);
if (!mInformedDiscardTracker) {
NS_WARNING("Exceeded the image decode size hard limit");
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);
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");
return NS_ERROR_FAILURE;
}
mOffset.MoveTo(0, 0);
mSize.SizeTo(aSize.width, aSize.height);
mFormat = aFormat;
mPaletteDepth = 0;
// Inform the discard tracker that we are going to allocate some memory.
mInformedDiscardTracker =
DiscardTracker::TryAllocation(4 * mSize.width * mSize.height);
if (!mInformedDiscardTracker) {
NS_WARNING("Exceed the image decode size hard limit");
return NS_ERROR_OUT_OF_MEMORY;
}
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) {
return NS_ERROR_OUT_OF_MEMORY;
}
int32_t stride = VolatileSurfaceStride(mSize, mFormat);
VolatileBufferPtr<uint8_t> ptr(mVBuf);
if (!ptr) {
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) {
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, ThebesIntSize(mSize),
ImageRegion::Create(imageRect),
mFormat, aFilter, aImageFlags);
if (canUseDataSurface && !mImageSurface) {
NS_WARNING("Failed to create VolatileDataSourceSurface");
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();
}
return NS_OK;

View File

@ -36,7 +36,44 @@ public:
imgFrame();
nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
/**
* Initialize this imgFrame with an empty surface and prepare it for being
* written to by a decoder.
*
* This is appropriate for use with decoded images, but it should not be used
* when drawing content into an imgFrame, as it may use a different graphics
* backend than normal content drawing.
*/
nsresult InitForDecoder(const nsIntRect& aRect,
SurfaceFormat aFormat,
uint8_t aPaletteDepth = 0);
nsresult InitForDecoder(const nsIntSize& aSize,
SurfaceFormat aFormat,
uint8_t aPaletteDepth = 0)
{
return InitForDecoder(nsIntRect(0, 0, aSize.width, aSize.height),
aFormat, aPaletteDepth);
}
/**
* Initialize this imgFrame with a new surface and draw the provided
* gfxDrawable into it.
*
* This is appropriate to use when drawing content into an imgFrame, as it
* uses the same graphics backend as normal content drawing. The downside is
* that the underlying surface may not be stored in a volatile buffer on all
* platforms, and raw access to the surface (using RawAccessRef() or
* LockImageData()) may be much more expensive than in the InitForDecoder()
* case.
*/
nsresult InitWithDrawable(gfxDrawable* aDrawable,
const nsIntSize& aSize,
const SurfaceFormat aFormat,
GraphicsFilter aFilter,
uint32_t aImageFlags);
nsresult Optimize();
DrawableFrameRef DrawableRef();