Bug 1116733 (Part 1) - Allocate frames off-main-thread. r=tn

This commit is contained in:
Seth Fowler 2015-01-08 00:04:31 -08:00
parent 5d761e1f76
commit a704fc2527
9 changed files with 537 additions and 292 deletions

View File

@ -199,12 +199,6 @@ nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
format);
} else {
// Our preallocated frame matches up, with the possible exception
// of alpha.
if (format == gfx::SurfaceFormat::B8G8R8X8) {
currentFrame->SetHasNoAlpha();
}
}
}
@ -233,8 +227,12 @@ nsGIFDecoder2::EndImageFrame()
mGIFStruct.screen_height - realFrameHeight);
PostInvalidation(r);
}
// This transparency check is only valid for first frame
if (mGIFStruct.is_transparent && !mSawTransparency) {
// The first frame was preallocated with alpha; if it wasn't transparent, we
// should fix that. We can also mark it opaque unconditionally if we didn't
// actually see any transparent pixels - this test is only valid for the
// first frame.
if (!mGIFStruct.is_transparent || !mSawTransparency) {
opacity = Opacity::OPAQUE;
}
}

View File

@ -165,11 +165,6 @@ void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
} else if (mNumFrames != 0) {
NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
} else {
// Our preallocated frame matches up, with the possible exception of alpha.
if (format == gfx::SurfaceFormat::B8G8R8X8) {
currentFrame->SetHasNoAlpha();
}
}
mFrameRect = neededRect;
@ -204,11 +199,9 @@ nsPNGDecoder::EndImageFrame()
mNumFrames++;
Opacity opacity;
if (mFrameHasNoAlpha) {
Opacity opacity = Opacity::SOME_TRANSPARENCY;
if (format == gfx::SurfaceFormat::B8G8R8X8 || mFrameHasNoAlpha) {
opacity = Opacity::OPAQUE;
} else {
opacity = Opacity::SOME_TRANSPARENCY;
}
#ifdef PNG_APNG_SUPPORTED

View File

@ -134,19 +134,18 @@ Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
// Pass the data along to the implementation
WriteInternal(aBuffer, aCount, aStrategy);
// If we're a synchronous decoder and we need a new frame to proceed, let's
// create one and call it again.
if (aStrategy == DecodeStrategy::SYNC) {
while (NeedsNewFrame() && !HasDataError()) {
nsresult rv = AllocateFrame();
// If we need a new frame to proceed, let's create one and call it again.
while (NeedsNewFrame() && !HasDataError()) {
MOZ_ASSERT(!IsSizeDecode(), "Shouldn't need new frame for size decode");
if (NS_SUCCEEDED(rv)) {
// Use the data we saved when we asked for a new frame.
WriteInternal(nullptr, 0, aStrategy);
}
nsresult rv = AllocateFrame();
mNeedsToFlushData = false;
if (NS_SUCCEEDED(rv)) {
// Use the data we saved when we asked for a new frame.
WriteInternal(nullptr, 0, aStrategy);
}
mNeedsToFlushData = false;
}
// Finish telemetry.
@ -237,7 +236,6 @@ nsresult
Decoder::AllocateFrame()
{
MOZ_ASSERT(mNeedsNewFrame);
MOZ_ASSERT(NS_IsMainThread());
mCurrentFrame = EnsureFrame(mNewFrameData.mFrameNum,
mNewFrameData.mFrameRect,
@ -279,8 +277,6 @@ Decoder::EnsureFrame(uint32_t aFrameNum,
uint8_t aPaletteDepth,
imgFrame* aPreviousFrame)
{
MOZ_ASSERT(NS_IsMainThread());
if (mDataError || NS_FAILED(mFailCode)) {
return RawAccessFrameRef();
}
@ -352,13 +348,13 @@ Decoder::InternalAddFrame(uint32_t aFrameNum,
}
nsRefPtr<imgFrame> frame = new imgFrame();
bool nonPremult =
aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
if (NS_FAILED(frame->InitForDecoder(imageSize, aFrameRect, aFormat,
aPaletteDepth))) {
aPaletteDepth, nonPremult))) {
NS_WARNING("imgFrame::Init should succeed");
return RawAccessFrameRef();
}
frame->SetAsNonPremult(aDecodeFlags &
imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
RawAccessFrameRef ref = frame->RawAccessRef();
if (!ref) {
@ -384,11 +380,11 @@ Decoder::InternalAddFrame(uint32_t aFrameNum,
// If we dispose of the first frame by clearing it, then the first frame's
// refresh area is all of itself.
// RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
DisposalMethod disposalMethod = aPreviousFrame->GetDisposalMethod();
if (disposalMethod == DisposalMethod::CLEAR ||
disposalMethod == DisposalMethod::CLEAR_ALL ||
disposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
refreshArea = aPreviousFrame->GetRect();
AnimationData previousFrameData = aPreviousFrame->GetAnimationData();
if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR ||
previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL ||
previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
refreshArea = previousFrameData.mRect;
}
}
@ -481,14 +477,7 @@ Decoder::PostFrameStop(Opacity aFrameOpacity /* = Opacity::TRANSPARENT */,
// Update our state
mInFrame = false;
if (aFrameOpacity == Opacity::OPAQUE) {
mCurrentFrame->SetHasNoAlpha();
}
mCurrentFrame->SetDisposalMethod(aDisposalMethod);
mCurrentFrame->SetRawTimeout(aTimeout);
mCurrentFrame->SetBlendMethod(aBlendMethod);
mCurrentFrame->ImageUpdated(mCurrentFrame->GetRect());
mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod);
mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
}

View File

@ -289,7 +289,7 @@ int32_t
FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
{
RawAccessFrameRef frame = GetRawFrame(aFrameNum);
const int32_t timeout = frame->GetRawTimeout();
AnimationData data = frame->GetAnimationData();
// Ensure a minimal time between updates so we don't throttle the UI thread.
// consider 0 == unspecified and make it fast but not too fast. Unless we
@ -304,11 +304,11 @@ FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
// It seems that there are broken tools out there that set a 0ms or 10ms
// timeout when they really want a "default" one. So munge values in that
// range.
if (timeout >= 0 && timeout <= 10 && mLoopCount != 0) {
if (data.mRawTimeout >= 0 && data.mRawTimeout <= 10 && mLoopCount != 0) {
return 100;
}
return timeout;
return data.mRawTimeout;
}
size_t
@ -352,34 +352,34 @@ FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");
DisposalMethod prevFrameDisposalMethod = prevFrame->GetDisposalMethod();
if (prevFrameDisposalMethod == DisposalMethod::RESTORE_PREVIOUS &&
AnimationData prevFrameData = prevFrame->GetAnimationData();
if (prevFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS &&
!mCompositingPrevFrame) {
prevFrameDisposalMethod = DisposalMethod::CLEAR;
prevFrameData.mDisposalMethod = DisposalMethod::CLEAR;
}
nsIntRect prevFrameRect = prevFrame->GetRect();
bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
prevFrameRect.width == mSize.width &&
prevFrameRect.height == mSize.height);
bool isFullPrevFrame = prevFrameData.mRect.x == 0 &&
prevFrameData.mRect.y == 0 &&
prevFrameData.mRect.width == mSize.width &&
prevFrameData.mRect.height == mSize.height;
// Optimization: DisposeClearAll if the previous frame is the same size as
// container and it's clearing itself
if (isFullPrevFrame &&
(prevFrameDisposalMethod == DisposalMethod::CLEAR)) {
prevFrameDisposalMethod = DisposalMethod::CLEAR_ALL;
(prevFrameData.mDisposalMethod == DisposalMethod::CLEAR)) {
prevFrameData.mDisposalMethod = DisposalMethod::CLEAR_ALL;
}
DisposalMethod nextFrameDisposalMethod = nextFrame->GetDisposalMethod();
nsIntRect nextFrameRect = nextFrame->GetRect();
bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
nextFrameRect.width == mSize.width &&
nextFrameRect.height == mSize.height);
AnimationData nextFrameData = nextFrame->GetAnimationData();
bool isFullNextFrame = nextFrameData.mRect.x == 0 &&
nextFrameData.mRect.y == 0 &&
nextFrameData.mRect.width == mSize.width &&
nextFrameData.mRect.height == mSize.height;
if (!nextFrame->GetIsPaletted()) {
// Optimization: Skip compositing if the previous frame wants to clear the
// whole image
if (prevFrameDisposalMethod == DisposalMethod::CLEAR_ALL) {
if (prevFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL) {
aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
return true;
}
@ -387,19 +387,19 @@ FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
// Optimization: Skip compositing if this frame is the same size as the
// container and it's fully drawing over prev frame (no alpha)
if (isFullNextFrame &&
(nextFrameDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) &&
!nextFrame->GetHasAlpha()) {
(nextFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) &&
!nextFrameData.mHasAlpha) {
aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
return true;
}
}
// Calculate area that needs updating
switch (prevFrameDisposalMethod) {
switch (prevFrameData.mDisposalMethod) {
default:
case DisposalMethod::NOT_SPECIFIED:
case DisposalMethod::KEEP:
*aDirtyRect = nextFrameRect;
*aDirtyRect = nextFrameData.mRect;
break;
case DisposalMethod::CLEAR_ALL:
@ -415,7 +415,7 @@ FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
// way at the bottom, and both frames being small, we'd be
// telling framechanged to refresh the whole image when only two
// small areas are needed.
aDirtyRect->UnionRect(nextFrameRect, prevFrameRect);
aDirtyRect->UnionRect(nextFrameData.mRect, prevFrameData.mRect);
break;
case DisposalMethod::RESTORE_PREVIOUS:
@ -453,6 +453,8 @@ FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
needToBlankComposite = true;
}
AnimationData compositingFrameData = mCompositingFrame->GetAnimationData();
// More optimizations possible when next frame is not transparent
// But if the next frame has DisposalMethod::RESTORE_PREVIOUS,
// this "no disposal" optimization is not possible,
@ -460,8 +462,8 @@ FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
// needs to be stored in compositingFrame, so it can be
// copied into compositingPrevFrame later.
bool doDisposal = true;
if (!nextFrame->GetHasAlpha() &&
nextFrameDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) {
if (!nextFrameData.mHasAlpha &&
nextFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) {
if (isFullNextFrame) {
// Optimization: No need to dispose prev.frame when
// next frame is full frame and not transparent.
@ -469,12 +471,12 @@ FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
// No need to blank the composite frame
needToBlankComposite = false;
} else {
if ((prevFrameRect.x >= nextFrameRect.x) &&
(prevFrameRect.y >= nextFrameRect.y) &&
(prevFrameRect.x + prevFrameRect.width <=
nextFrameRect.x + nextFrameRect.width) &&
(prevFrameRect.y + prevFrameRect.height <=
nextFrameRect.y + nextFrameRect.height)) {
if ((prevFrameData.mRect.x >= nextFrameData.mRect.x) &&
(prevFrameData.mRect.y >= nextFrameData.mRect.y) &&
(prevFrameData.mRect.x + prevFrameData.mRect.width <=
nextFrameData.mRect.x + nextFrameData.mRect.width) &&
(prevFrameData.mRect.y + prevFrameData.mRect.height <=
nextFrameData.mRect.y + nextFrameData.mRect.height)) {
// Optimization: No need to dispose prev.frame when
// next frame fully overlaps previous frame.
doDisposal = false;
@ -484,43 +486,46 @@ FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
if (doDisposal) {
// Dispose of previous: clear, restore, or keep (copy)
switch (prevFrameDisposalMethod) {
switch (prevFrameData.mDisposalMethod) {
case DisposalMethod::CLEAR:
if (needToBlankComposite) {
// If we just created the composite, it could have anything in its
// buffer. Clear whole frame
ClearFrame(mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect());
ClearFrame(compositingFrameData.mRawData,
compositingFrameData.mRect);
} else {
// Only blank out previous frame area (both color & Mask/Alpha)
ClearFrame(mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect(),
prevFrameRect);
ClearFrame(compositingFrameData.mRawData,
compositingFrameData.mRect,
prevFrameData.mRect);
}
break;
case DisposalMethod::CLEAR_ALL:
ClearFrame(mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect());
ClearFrame(compositingFrameData.mRawData,
compositingFrameData.mRect);
break;
case DisposalMethod::RESTORE_PREVIOUS:
// It would be better to copy only the area changed back to
// compositingFrame.
if (mCompositingPrevFrame) {
CopyFrameImage(mCompositingPrevFrame->GetRawData(),
mCompositingPrevFrame->GetRect(),
mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect());
AnimationData compositingPrevFrameData =
mCompositingPrevFrame->GetAnimationData();
CopyFrameImage(compositingPrevFrameData.mRawData,
compositingPrevFrameData.mRect,
compositingFrameData.mRawData,
compositingFrameData.mRect);
// destroy only if we don't need it for this frame's disposal
if (nextFrameDisposalMethod !=
if (nextFrameData.mDisposalMethod !=
DisposalMethod::RESTORE_PREVIOUS) {
mCompositingPrevFrame.reset();
}
} else {
ClearFrame(mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect());
ClearFrame(compositingFrameData.mRawData,
compositingFrameData.mRect);
}
break;
@ -535,39 +540,39 @@ FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
if (mLastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
// Just copy the bits
CopyFrameImage(prevFrame->GetRawData(),
prevFrame->GetRect(),
mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect());
CopyFrameImage(prevFrameData.mRawData,
prevFrameData.mRect,
compositingFrameData.mRawData,
compositingFrameData.mRect);
} else {
if (needToBlankComposite) {
// Only blank composite when prev is transparent or not full.
if (prevFrame->GetHasAlpha() || !isFullPrevFrame) {
ClearFrame(mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect());
if (prevFrameData.mHasAlpha || !isFullPrevFrame) {
ClearFrame(compositingFrameData.mRawData,
compositingFrameData.mRect);
}
}
DrawFrameTo(prevFrame->GetRawData(), prevFrameRect,
prevFrame->PaletteDataLength(),
prevFrame->GetHasAlpha(),
mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect(),
prevFrame->GetBlendMethod());
DrawFrameTo(prevFrameData.mRawData, prevFrameData.mRect,
prevFrameData.mPaletteDataLength,
prevFrameData.mHasAlpha,
compositingFrameData.mRawData,
compositingFrameData.mRect,
prevFrameData.mBlendMethod);
}
}
}
} else if (needToBlankComposite) {
// If we just created the composite, it could have anything in its
// buffers. Clear them
ClearFrame(mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect());
ClearFrame(compositingFrameData.mRawData,
compositingFrameData.mRect);
}
// Check if the frame we are composing wants the previous image restored after
// it is done. Don't store it (again) if last frame wanted its image restored
// too
if ((nextFrameDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) &&
(prevFrameDisposalMethod != DisposalMethod::RESTORE_PREVIOUS)) {
if ((nextFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) &&
(prevFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS)) {
// We are storing the whole image.
// It would be better if we just stored the area that nextFrame is going to
// overwrite.
@ -583,28 +588,26 @@ FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
mCompositingPrevFrame = newFrame->RawAccessRef();
}
CopyFrameImage(mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect(),
mCompositingPrevFrame->GetRawData(),
mCompositingPrevFrame->GetRect());
AnimationData compositingPrevFrameData =
mCompositingPrevFrame->GetAnimationData();
CopyFrameImage(compositingFrameData.mRawData,
compositingFrameData.mRect,
compositingPrevFrameData.mRawData,
compositingPrevFrameData.mRect);
}
// blit next frame into it's correct spot
DrawFrameTo(nextFrame->GetRawData(), nextFrameRect,
nextFrame->PaletteDataLength(),
nextFrame->GetHasAlpha(),
mCompositingFrame->GetRawData(),
mCompositingFrame->GetRect(),
nextFrame->GetBlendMethod());
// Set timeout of CompositeFrame to timeout of frame we just composed
// Bug 177948
int32_t timeout = nextFrame->GetRawTimeout();
mCompositingFrame->SetRawTimeout(timeout);
DrawFrameTo(nextFrameData.mRawData, nextFrameData.mRect,
nextFrameData.mPaletteDataLength,
nextFrameData.mHasAlpha,
compositingFrameData.mRawData,
compositingFrameData.mRect,
nextFrameData.mBlendMethod);
// Tell the image that it is fully 'downloaded'.
nsresult rv =
mCompositingFrame->ImageUpdated(mCompositingFrame->GetRect());
mCompositingFrame->ImageUpdated(compositingFrameData.mRect);
if (NS_FAILED(rv)) {
return false;
}

View File

@ -199,18 +199,14 @@ public:
{
if (mState == eReady) {
// Collect information from the frames that we need to scale.
uint8_t* srcData = mSrcRef->GetImageData();
IntSize srcSize = mSrcRef->GetSize();
uint32_t srcStride = mSrcRef->GetImageBytesPerRow();
uint8_t* dstData = mDstRef->GetImageData();
uint32_t dstStride = mDstRef->GetImageBytesPerRow();
SurfaceFormat srcFormat = mSrcRef->GetFormat();
ScalingData srcData = mSrcRef->GetScalingData();
ScalingData dstData = mDstRef->GetScalingData();
// Actually do the scaling.
bool succeeded =
gfx::Scale(srcData, srcSize.width, srcSize.height, srcStride,
dstData, mDstSize.width, mDstSize.height, dstStride,
srcFormat);
gfx::Scale(srcData.mRawData, srcData.mSize.width, srcData.mSize.height,
srcData.mBytesPerRow, dstData.mRawData, mDstSize.width,
mDstSize.height, dstData.mBytesPerRow, srcData.mFormat);
if (succeeded) {
// Mark the frame as complete and discardable.
@ -657,6 +653,13 @@ RasterImage::IsOpaque()
void
RasterImage::OnSurfaceDiscarded()
{
if (!NS_IsMainThread()) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &RasterImage::OnSurfaceDiscarded);
NS_DispatchToMainThread(runnable);
return;
}
if (mProgressTracker) {
mProgressTracker->OnDiscard();
}
@ -930,11 +933,42 @@ RasterImage::SizeOfDecoded(gfxMemoryLocation aLocation,
return n;
}
class OnAddedFrameRunnable : public nsRunnable
{
public:
OnAddedFrameRunnable(RasterImage* aImage,
uint32_t aNewFrameCount,
const nsIntRect& aNewRefreshArea)
: mImage(aImage)
, mNewFrameCount(aNewFrameCount)
, mNewRefreshArea(aNewRefreshArea)
{
MOZ_ASSERT(aImage);
}
NS_IMETHOD Run()
{
mImage->OnAddedFrame(mNewFrameCount, mNewRefreshArea);
return NS_OK;
}
private:
nsRefPtr<RasterImage> mImage;
uint32_t mNewFrameCount;
nsIntRect mNewRefreshArea;
};
void
RasterImage::OnAddedFrame(uint32_t aNewFrameCount,
const nsIntRect& aNewRefreshArea)
{
MOZ_ASSERT(NS_IsMainThread());
if (!NS_IsMainThread()) {
nsCOMPtr<nsIRunnable> runnable =
new OnAddedFrameRunnable(this, aNewFrameCount, aNewRefreshArea);
NS_DispatchToMainThread(runnable);
return;
}
MOZ_ASSERT((mFrameCount == 1 && aNewFrameCount == 1) ||
mFrameCount < aNewFrameCount,
"Frame count running backwards");

View File

@ -15,6 +15,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/Likely.h"
#include "mozilla/Move.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPtr.h"
#include "nsIMemoryReporter.h"
@ -119,7 +120,8 @@ class CachedSurface
{
~CachedSurface() {}
public:
NS_INLINE_DECL_REFCOUNTING(CachedSurface)
MOZ_DECLARE_REFCOUNTED_TYPENAME(CachedSurface)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CachedSurface)
CachedSurface(imgFrame* aSurface,
const Cost aCost,
@ -215,7 +217,8 @@ class ImageSurfaceCache
public:
ImageSurfaceCache() : mLocked(false) { }
NS_INLINE_DECL_REFCOUNTING(ImageSurfaceCache)
MOZ_DECLARE_REFCOUNTED_TYPENAME(ImageSurfaceCache)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageSurfaceCache)
typedef nsRefPtrHashtable<nsGenericHashKey<SurfaceKey>, CachedSurface> SurfaceTable;
@ -277,6 +280,7 @@ public:
: mExpirationTracker(MOZ_THIS_IN_INITIALIZER_LIST(),
aSurfaceCacheExpirationTimeMS)
, mMemoryPressureObserver(new MemoryPressureObserver)
, mMutex("SurfaceCache")
, mDiscardFactor(aSurfaceCacheDiscardFactor)
, mMaxCost(aSurfaceCacheSize)
, mAvailableCost(aSurfaceCacheSize)
@ -298,9 +302,9 @@ private:
}
public:
void InitMemoryReporter() {
RegisterWeakMemoryReporter(this);
}
void InitMemoryReporter() { RegisterWeakMemoryReporter(this); }
Mutex& GetMutex() { return mMutex; }
bool Insert(imgFrame* aSurface,
const Cost aCost,
@ -705,6 +709,7 @@ private:
nsRefPtrHashtable<nsPtrHashKey<Image>, ImageSurfaceCache> mImageCaches;
SurfaceTracker mExpirationTracker;
nsRefPtr<MemoryPressureObserver> mMemoryPressureObserver;
Mutex mMutex;
const uint32_t mDiscardFactor;
const Cost mMaxCost;
Cost mAvailableCost;
@ -722,6 +727,7 @@ NS_IMPL_ISUPPORTS(SurfaceCacheImpl::MemoryPressureObserver, nsIObserver)
SurfaceCache::Initialize()
{
// Initialize preferences.
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!sInstance, "Shouldn't initialize more than once");
// See gfxPrefs for the default values of these preferences.
@ -775,6 +781,7 @@ SurfaceCache::Initialize()
/* static */ void
SurfaceCache::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?");
sInstance = nullptr;
}
@ -783,11 +790,11 @@ SurfaceCache::Shutdown()
SurfaceCache::Lookup(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sInstance) {
return DrawableFrameRef();
}
MutexAutoLock lock(sInstance->GetMutex());
return sInstance->Lookup(aImageKey, aSurfaceKey);
}
@ -797,11 +804,11 @@ SurfaceCache::Insert(imgFrame* aSurface,
const SurfaceKey& aSurfaceKey,
Lifetime aLifetime)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sInstance) {
return false;
}
MutexAutoLock lock(sInstance->GetMutex());
Cost cost = ComputeCost(aSurfaceKey.Size());
return sInstance->Insert(aSurface, cost, aImageKey, aSurfaceKey, aLifetime);
}
@ -809,7 +816,6 @@ SurfaceCache::Insert(imgFrame* aSurface,
/* static */ bool
SurfaceCache::CanHold(const IntSize& aSize)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sInstance) {
return false;
}
@ -821,8 +827,8 @@ SurfaceCache::CanHold(const IntSize& aSize)
/* static */ void
SurfaceCache::LockImage(Image* aImageKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
MutexAutoLock lock(sInstance->GetMutex());
return sInstance->LockImage(aImageKey);
}
}
@ -830,8 +836,8 @@ SurfaceCache::LockImage(Image* aImageKey)
/* static */ void
SurfaceCache::UnlockImage(Image* aImageKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
MutexAutoLock lock(sInstance->GetMutex());
return sInstance->UnlockImage(aImageKey);
}
}
@ -840,8 +846,8 @@ SurfaceCache::UnlockImage(Image* aImageKey)
SurfaceCache::RemoveSurface(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
MutexAutoLock lock(sInstance->GetMutex());
sInstance->RemoveSurface(aImageKey, aSurfaceKey);
}
}
@ -849,8 +855,8 @@ SurfaceCache::RemoveSurface(const ImageKey aImageKey,
/* static */ void
SurfaceCache::RemoveImage(Image* aImageKey)
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
MutexAutoLock lock(sInstance->GetMutex());
sInstance->RemoveImage(aImageKey);
}
}
@ -858,8 +864,8 @@ SurfaceCache::RemoveImage(Image* aImageKey)
/* static */ void
SurfaceCache::DiscardAll()
{
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
MutexAutoLock lock(sInstance->GetMutex());
sInstance->DiscardAll();
}
}
@ -869,11 +875,11 @@ SurfaceCache::SizeOfSurfaces(const ImageKey aImageKey,
gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sInstance) {
return 0;
}
MutexAutoLock lock(sInstance->GetMutex());
return sInstance->SizeOfSurfaces(aImageKey, aLocation, aMallocSizeOf);
}

View File

@ -22,6 +22,7 @@ static bool gDisableOptimize = false;
#include "MainThreadUtils.h"
#include "mozilla/MemoryReporting.h"
#include "nsMargin.h"
#include "nsThreadUtils.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/gfx/Tools.h"
@ -128,19 +129,20 @@ static bool AllowedImageAndFrameDimensions(const nsIntSize& aImageSize,
}
imgFrame::imgFrame() :
mDecoded(0, 0, 0, 0),
mDecodedMutex("imgFrame::mDecoded"),
mPalettedImageData(nullptr),
mTimeout(100),
mLockCount(0),
mDisposalMethod(DisposalMethod::NOT_SPECIFIED),
mBlendMethod(BlendMethod::OVER),
mSinglePixel(false),
mCompositingFailed(false),
mHasNoAlpha(false),
mNonPremult(false),
mOptimizable(false)
imgFrame::imgFrame()
: mMutex("imgFrame")
, mDecoded(0, 0, 0, 0)
, mLockCount(0)
, mTimeout(100)
, mDisposalMethod(DisposalMethod::NOT_SPECIFIED)
, mBlendMethod(BlendMethod::OVER)
, mHasNoAlpha(false)
, mPalettedImageData(nullptr)
, mPaletteDepth(0)
, mNonPremult(false)
, mSinglePixel(false)
, mCompositingFailed(false)
, mOptimizable(false)
{
static bool hasCheckedOptimize = false;
if (!hasCheckedOptimize) {
@ -161,7 +163,8 @@ nsresult
imgFrame::InitForDecoder(const nsIntSize& aImageSize,
const nsIntRect& aRect,
SurfaceFormat aFormat,
uint8_t aPaletteDepth /* = 0 */)
uint8_t aPaletteDepth /* = 0 */,
bool aNonPremult /* = false */)
{
// Assert for properties that should be verified by decoders,
// warn for properties related to bad content.
@ -176,6 +179,7 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize,
mFormat = aFormat;
mPaletteDepth = aPaletteDepth;
mNonPremult = aNonPremult;
if (aPaletteDepth != 0) {
// We're creating for a paletted image.
@ -185,8 +189,11 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize,
return NS_ERROR_FAILURE;
}
// Use the fallible allocator here
mPalettedImageData = (uint8_t*)moz_malloc(PaletteDataLength() + GetImageDataLength());
// 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*>(moz_malloc(PaletteDataLength() +
(mSize.width * mSize.height)));
if (!mPalettedImageData)
NS_WARNING("moz_malloc for paletted image data should succeed");
NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
@ -300,6 +307,7 @@ imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
nsresult imgFrame::Optimize()
{
MOZ_ASSERT(NS_IsMainThread());
mMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(mLockCount == 1,
"Should only optimize when holding the lock exclusively");
@ -359,7 +367,8 @@ nsresult imgFrame::Optimize()
SurfaceFormat optFormat =
gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR);
if (!GetHasAlpha() && optFormat == SurfaceFormat::R5G6B5) {
if (mFormat != SurfaceFormat::B8G8R8A8 &&
optFormat == SurfaceFormat::R5G6B5) {
RefPtr<VolatileBuffer> buf =
AllocateBufferForImage(mSize, optFormat);
if (!buf)
@ -428,7 +437,8 @@ imgFrame::RawAccessRef()
void
imgFrame::SetRawAccessOnly()
{
MOZ_ASSERT(mLockCount > 0, "Must hold a RawAccessFrameRef");
AssertImageDataLocked();
// Lock our data and throw away the key.
LockImageData();
}
@ -444,6 +454,9 @@ imgFrame::SurfaceForDrawing(bool aDoPadding,
ImageRegion& aRegion,
SourceSurface* aSurface)
{
MOZ_ASSERT(NS_IsMainThread());
mMutex.AssertCurrentThreadOwns();
IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
if (!aDoPadding && !aDoPartialDecode) {
NS_ASSERTION(!mSinglePixel, "This should already have been handled");
@ -496,19 +509,22 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
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!");
NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");
MutexAutoLock lock(mMutex);
nsIntMargin padding(mOffset.y,
mImageSize.width - (mOffset.x + mSize.width),
mImageSize.height - (mOffset.y + mSize.height),
mOffset.x);
bool doPadding = padding != nsIntMargin(0,0,0,0);
bool doPartialDecode = !ImageComplete();
bool doPartialDecode = !ImageCompleteInternal();
if (mSinglePixel && !doPadding && !doPartialDecode) {
if (mSinglePixelColor.a == 0.0) {
@ -522,7 +538,7 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
return true;
}
RefPtr<SourceSurface> surf = GetSurface();
RefPtr<SourceSurface> surf = GetSurfaceInternal();
if (!surf && !mSinglePixel) {
return false;
}
@ -550,10 +566,17 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
return true;
}
// This can be called from any thread, but not simultaneously.
nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
nsresult
imgFrame::ImageUpdated(const nsIntRect& aUpdateRect)
{
MutexAutoLock lock(mDecodedMutex);
MutexAutoLock lock(mMutex);
return ImageUpdatedInternal(aUpdateRect);
}
nsresult
imgFrame::ImageUpdatedInternal(const nsIntRect& aUpdateRect)
{
mMutex.AssertCurrentThreadOwns();
mDecoded.UnionRect(mDecoded, aUpdateRect);
@ -565,6 +588,23 @@ nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
return NS_OK;
}
void
imgFrame::Finish(Opacity aFrameOpacity, DisposalMethod aDisposalMethod,
int32_t aRawTimeout, BlendMethod aBlendMethod)
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
if (aFrameOpacity == Opacity::OPAQUE) {
mHasNoAlpha = true;
}
mDisposalMethod = aDisposalMethod;
mTimeout = aRawTimeout;
mBlendMethod = aBlendMethod;
ImageUpdatedInternal(GetRect());
}
nsIntRect imgFrame::GetRect() const
{
return nsIntRect(mOffset, nsIntSize(mSize.width, mSize.height));
@ -573,6 +613,8 @@ nsIntRect imgFrame::GetRect() const
int32_t
imgFrame::GetStride() const
{
mMutex.AssertCurrentThreadOwns();
if (mImageSurface) {
return mImageSurface->Stride();
}
@ -582,11 +624,14 @@ imgFrame::GetStride() const
SurfaceFormat imgFrame::GetFormat() const
{
MutexAutoLock lock(mMutex);
return mFormat;
}
uint32_t imgFrame::GetImageBytesPerRow() const
{
mMutex.AssertCurrentThreadOwns();
if (mVBuf)
return mSize.width * BytesPerPixel(mFormat);
@ -601,18 +646,31 @@ uint32_t imgFrame::GetImageDataLength() const
return GetImageBytesPerRow() * mSize.height;
}
void imgFrame::GetImageData(uint8_t **aData, uint32_t *length) const
void
imgFrame::GetImageData(uint8_t** aData, uint32_t* aLength) const
{
NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetImageData unless frame is locked");
MutexAutoLock lock(mMutex);
GetImageDataInternal(aData, aLength);
}
if (mImageSurface)
void
imgFrame::GetImageDataInternal(uint8_t** aData, uint32_t* aLength) const
{
mMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
if (mImageSurface) {
*aData = mVBufPtr;
else if (mPalettedImageData)
MOZ_ASSERT(*aData, "mImageSurface is non-null, but mVBufPtr is null in GetImageData");
} else if (mPalettedImageData) {
*aData = mPalettedImageData + PaletteDataLength();
else
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;
}
*length = GetImageDataLength();
*aLength = GetImageDataLength();
}
uint8_t* imgFrame::GetImageData() const
@ -628,14 +686,9 @@ bool imgFrame::GetIsPaletted() const
return mPalettedImageData != nullptr;
}
bool imgFrame::GetHasAlpha() const
{
return mFormat == SurfaceFormat::B8G8R8A8;
}
void imgFrame::GetPaletteData(uint32_t **aPalette, uint32_t *length) const
{
NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetPaletteData unless frame is locked");
AssertImageDataLocked();
if (!mPalettedImageData) {
*aPalette = nullptr;
@ -654,21 +707,12 @@ uint32_t* imgFrame::GetPaletteData() const
return data;
}
uint8_t*
imgFrame::GetRawData() const
nsresult
imgFrame::LockImageData()
{
MOZ_ASSERT(mLockCount, "Should be locked to call GetRawData()");
if (mPalettedImageData) {
return mPalettedImageData;
}
return GetImageData();
}
MutexAutoLock lock(mMutex);
nsresult imgFrame::LockImageData()
{
MOZ_ASSERT(NS_IsMainThread());
NS_ABORT_IF_FALSE(mLockCount >= 0, "Unbalanced locks and unlocks");
MOZ_ASSERT(mLockCount >= 0, "Unbalanced locks and unlocks");
if (mLockCount < 0) {
return NS_ERROR_FAILURE;
}
@ -680,9 +724,26 @@ nsresult imgFrame::LockImageData()
return NS_OK;
}
// Paletted images don't have surfaces, so there's nothing to do.
if (mPalettedImageData)
// 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;
}
return Deoptimize();
}
nsresult
imgFrame::Deoptimize()
{
MOZ_ASSERT(NS_IsMainThread());
mMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(!mImageSurface);
if (!mImageSurface) {
if (mVBuf) {
@ -746,9 +807,34 @@ nsresult imgFrame::LockImageData()
return NS_OK;
}
nsresult imgFrame::UnlockImageData()
void
imgFrame::AssertImageDataLocked() const
{
MOZ_ASSERT(NS_IsMainThread());
#ifdef DEBUG
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
#endif
}
class UnlockImageDataRunnable : public nsRunnable
{
public:
UnlockImageDataRunnable(imgFrame* aTarget)
: mTarget(aTarget)
{
MOZ_ASSERT(mTarget);
}
NS_IMETHOD Run() { return mTarget->UnlockImageData(); }
private:
nsRefPtr<imgFrame> mTarget;
};
nsresult
imgFrame::UnlockImageData()
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mLockCount > 0, "Unlocking an unlocked image!");
if (mLockCount <= 0) {
@ -759,6 +845,13 @@ nsresult imgFrame::UnlockImageData()
// surface anymore. (But we don't need to do anything for paletted images,
// which don't have surfaces.)
if (mLockCount == 1 && !mPalettedImageData) {
// We can't safely optimize off-main-thread, so create a runnable to do it.
if (!NS_IsMainThread()) {
nsCOMPtr<nsIRunnable> runnable = new UnlockImageDataRunnable(this);
NS_DispatchToMainThread(runnable);
return NS_OK;
}
// 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.
@ -783,13 +876,37 @@ nsresult imgFrame::UnlockImageData()
void
imgFrame::SetOptimizable()
{
MOZ_ASSERT(mLockCount, "Expected to be locked when SetOptimizable is called");
MOZ_ASSERT(NS_IsMainThread());
AssertImageDataLocked();
mOptimizable = true;
}
Color
imgFrame::SinglePixelColor() const
{
MOZ_ASSERT(NS_IsMainThread());
return mSinglePixelColor;
}
bool
imgFrame::IsSinglePixel() const
{
MOZ_ASSERT(NS_IsMainThread());
return mSinglePixel;
}
TemporaryRef<SourceSurface>
imgFrame::GetSurface()
{
MutexAutoLock lock(mMutex);
return GetSurfaceInternal();
}
TemporaryRef<SourceSurface>
imgFrame::GetSurfaceInternal()
{
mMutex.AssertCurrentThreadOwns();
if (mOptSurface) {
if (mOptSurface->IsValid())
return mOptSurface;
@ -813,9 +930,11 @@ imgFrame::GetSurface()
TemporaryRef<DrawTarget>
imgFrame::GetDrawTarget()
{
MOZ_ASSERT(mLockCount >= 1, "Should lock before requesting a DrawTarget");
MutexAutoLock lock(mMutex);
uint8_t* data = GetImageData();
uint8_t* data;
uint32_t length;
GetImageDataInternal(&data, &length);
if (!data) {
return nullptr;
}
@ -825,45 +944,64 @@ imgFrame::GetDrawTarget()
CreateDrawTargetForData(data, mSize, stride, mFormat);
}
int32_t imgFrame::GetRawTimeout() const
AnimationData
imgFrame::GetAnimationData() const
{
return mTimeout;
MutexAutoLock lock(mMutex);
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, mDisposalMethod, hasAlpha);
}
void imgFrame::SetRawTimeout(int32_t aTimeout)
ScalingData
imgFrame::GetScalingData() const
{
mTimeout = aTimeout;
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
MOZ_ASSERT(!GetIsPaletted(), "GetScalingData can't handle paletted images");
uint8_t* data;
uint32_t length;
GetImageDataInternal(&data, &length);
return ScalingData(data, mSize, GetImageBytesPerRow(), mFormat);
}
// This can be called from any thread.
bool imgFrame::ImageComplete() const
bool
imgFrame::ImageComplete() const
{
MutexAutoLock lock(mDecodedMutex);
MutexAutoLock lock(mMutex);
return ImageCompleteInternal();
}
bool
imgFrame::ImageCompleteInternal() const
{
mMutex.AssertCurrentThreadOwns();
return mDecoded.IsEqualInterior(nsIntRect(mOffset.x, mOffset.y,
mSize.width, mSize.height));
}
// A hint from the image decoders that this image has no alpha, even
// though we're decoding it as B8G8R8A8.
void imgFrame::SetHasNoAlpha()
{
MOZ_ASSERT(mLockCount, "Expected to be locked when SetHasNoAlpha is called");
mHasNoAlpha = true;
}
void imgFrame::SetAsNonPremult(bool aIsNonPremult)
{
mNonPremult = aIsNonPremult;
}
bool imgFrame::GetCompositingFailed() const
{
MOZ_ASSERT(NS_IsMainThread());
return mCompositingFailed;
}
void imgFrame::SetCompositingFailed(bool val)
{
MOZ_ASSERT(NS_IsMainThread());
mCompositingFailed = val;
}
@ -871,6 +1009,8 @@ size_t
imgFrame::SizeOfExcludingThis(gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf) const
{
MutexAutoLock lock(mMutex);
// aMallocSizeOf is only used if aLocation==gfxMemoryLocation::IN_PROCESS_HEAP. It
// should be nullptr otherwise.
NS_ABORT_IF_FALSE(

View File

@ -14,6 +14,7 @@
#include "mozilla/VolatileBuffer.h"
#include "gfxDrawable.h"
#include "imgIContainer.h"
#include "MainThreadUtils.h"
namespace mozilla {
namespace image {
@ -45,6 +46,65 @@ MOZ_BEGIN_ENUM_CLASS(Opacity, uint8_t)
SOME_TRANSPARENCY
MOZ_END_ENUM_CLASS(Opacity)
/**
* AnimationData contains all of the information necessary for using an imgFrame
* as part of an animation.
*
* It includes pointers to the raw image data of the underlying imgFrame, but
* does not own that data. A RawAccessFrameRef for the underlying imgFrame must
* outlive the AnimationData for it to remain valid.
*/
struct AnimationData
{
AnimationData(uint8_t* aRawData, uint32_t aPaletteDataLength,
int32_t aRawTimeout, const nsIntRect& aRect,
BlendMethod aBlendMethod, DisposalMethod aDisposalMethod,
bool aHasAlpha)
: mRawData(aRawData)
, mPaletteDataLength(aPaletteDataLength)
, mRawTimeout(aRawTimeout)
, mRect(aRect)
, mBlendMethod(aBlendMethod)
, mDisposalMethod(aDisposalMethod)
, mHasAlpha(aHasAlpha)
{ }
uint8_t* mRawData;
uint32_t mPaletteDataLength;
int32_t mRawTimeout;
nsIntRect mRect;
BlendMethod mBlendMethod;
DisposalMethod mDisposalMethod;
bool mHasAlpha;
};
/**
* ScalingData contains all of the information necessary for performing
* high-quality (CPU-based) scaling an imgFrame.
*
* It includes pointers to the raw image data of the underlying imgFrame, but
* does not own that data. A RawAccessFrameRef for the underlying imgFrame must
* outlive the ScalingData for it to remain valid.
*/
struct ScalingData
{
ScalingData(uint8_t* aRawData,
gfx::IntSize aSize,
uint32_t aBytesPerRow,
gfx::SurfaceFormat aFormat)
: mRawData(aRawData)
, mSize(aSize)
, mBytesPerRow(aBytesPerRow)
, mFormat(aFormat)
{ }
uint8_t* mRawData;
gfx::IntSize mSize;
uint32_t mBytesPerRow;
gfx::SurfaceFormat mFormat;
};
class imgFrame
{
typedef gfx::Color Color;
@ -71,7 +131,8 @@ public:
nsresult InitForDecoder(const nsIntSize& aImageSize,
const nsIntRect& aRect,
SurfaceFormat aFormat,
uint8_t aPaletteDepth = 0);
uint8_t aPaletteDepth = 0,
bool aNonPremult = false);
nsresult InitForDecoder(const nsIntSize& aSize,
SurfaceFormat aFormat,
@ -118,71 +179,61 @@ public:
nsresult ImageUpdated(const nsIntRect &aUpdateRect);
/**
* Mark this imgFrame as completely decoded, and set final options.
*
* @param aFrameOpacity Whether this imgFrame is opaque.
* @param aDisposalMethod For animation frames, how this imgFrame is cleared
* from the compositing frame before the next frame is
* displayed.
* @param aRawTimeout For animation frames, the timeout in milliseconds
* before the next frame is displayed. This timeout is
* not necessarily the timeout that will actually be
* used; see FrameAnimator::GetTimeoutForFrame.
* @param aBlendMethod For animation frames, a blending method to be used
* when compositing this frame.
*/
void Finish(Opacity aFrameOpacity, DisposalMethod aDisposalMethod,
int32_t aRawTimeout, BlendMethod aBlendMethod);
IntSize GetImageSize() { return mImageSize; }
nsIntRect GetRect() const;
IntSize GetSize() const { return mSize; }
bool NeedsPadding() const { return mOffset != nsIntPoint(0, 0); }
int32_t GetStride() const;
SurfaceFormat GetFormat() const;
uint32_t GetImageBytesPerRow() const;
uint32_t GetImageDataLength() const;
bool GetIsPaletted() const;
bool GetHasAlpha() const;
void GetImageData(uint8_t **aData, uint32_t *length) const;
uint8_t* GetImageData() const;
bool GetIsPaletted() const;
void GetPaletteData(uint32_t **aPalette, uint32_t *length) const;
uint32_t* GetPaletteData() const;
uint8_t* GetRawData() const;
uint8_t GetPaletteDepth() const { return mPaletteDepth; }
int32_t GetRawTimeout() const;
void SetRawTimeout(int32_t aTimeout);
/**
* Get the SurfaceFormat for this imgFrame.
*
* This should only be used for assertions.
*/
SurfaceFormat GetFormat() const;
DisposalMethod GetDisposalMethod() const { return mDisposalMethod; }
void SetDisposalMethod(DisposalMethod aDisposalMethod)
{
mDisposalMethod = aDisposalMethod;
}
BlendMethod GetBlendMethod() const { return mBlendMethod; }
void SetBlendMethod(BlendMethod aBlendMethod) { mBlendMethod = aBlendMethod; }
AnimationData GetAnimationData() const;
ScalingData GetScalingData() const;
bool ImageComplete() const;
void SetHasNoAlpha();
void SetAsNonPremult(bool aIsNonPremult);
bool GetCompositingFailed() const;
void SetCompositingFailed(bool val);
void SetOptimizable();
Color SinglePixelColor() const;
bool IsSinglePixel() const;
TemporaryRef<SourceSurface> GetSurface();
TemporaryRef<DrawTarget> GetDrawTarget();
Color
SinglePixelColor()
{
return mSinglePixelColor;
}
bool IsSinglePixel()
{
return mSinglePixel;
}
TemporaryRef<SourceSurface> CachedSurface();
size_t SizeOfExcludingThis(gfxMemoryLocation aLocation,
MallocSizeOf aMallocSizeOf) const;
uint8_t GetPaletteDepth() const { return mPaletteDepth; }
uint32_t PaletteDataLength() const {
if (!mPaletteDepth)
return 0;
return ((1 << mPaletteDepth) * sizeof(uint32_t));
}
private: // methods
~imgFrame();
@ -190,6 +241,23 @@ private: // methods
nsresult LockImageData();
nsresult UnlockImageData();
nsresult Optimize();
nsresult Deoptimize();
void AssertImageDataLocked() const;
bool ImageCompleteInternal() const;
nsresult ImageUpdatedInternal(const nsIntRect& aUpdateRect);
void GetImageDataInternal(uint8_t **aData, uint32_t *length) const;
uint32_t GetImageBytesPerRow() const;
uint32_t GetImageDataLength() const;
int32_t GetStride() const;
TemporaryRef<SourceSurface> GetSurfaceInternal();
uint32_t PaletteDataLength() const
{
return mPaletteDepth ? (1 << mPaletteDepth) * sizeof(uint32_t)
: 0;
}
struct SurfaceWithFormat {
nsRefPtr<gfxDrawable> mDrawable;
@ -210,46 +278,65 @@ private: // methods
SourceSurface* aSurface);
private: // data
friend class DrawableFrameRef;
friend class RawAccessFrameRef;
friend class UnlockImageDataRunnable;
//////////////////////////////////////////////////////////////////////////////
// Thread-safe mutable data, protected by mMutex.
//////////////////////////////////////////////////////////////////////////////
mutable Mutex mMutex;
RefPtr<DataSourceSurface> mImageSurface;
RefPtr<SourceSurface> mOptSurface;
RefPtr<VolatileBuffer> mVBuf;
VolatileBufferPtr<uint8_t> mVBufPtr;
nsIntRect mDecoded;
//! Number of RawAccessFrameRefs currently alive for this imgFrame.
int32_t mLockCount;
//! Raw timeout for this frame. (See FrameAnimator::GetTimeoutForFrame.)
int32_t mTimeout; // -1 means display forever.
DisposalMethod mDisposalMethod;
BlendMethod mBlendMethod;
SurfaceFormat mFormat;
bool mHasNoAlpha;
//////////////////////////////////////////////////////////////////////////////
// Effectively const data, only mutated in the Init methods.
//////////////////////////////////////////////////////////////////////////////
IntSize mImageSize;
IntSize mSize;
nsIntPoint mOffset;
nsIntRect mDecoded;
mutable Mutex mDecodedMutex;
// The palette and image data for images that are paletted, since Cairo
// doesn't support these images.
// The paletted data comes first, then the image data itself.
// Total length is PaletteDataLength() + GetImageDataLength().
uint8_t* mPalettedImageData;
uint8_t mPaletteDepth;
bool mNonPremult;
//////////////////////////////////////////////////////////////////////////////
// Main-thread-only mutable data.
//////////////////////////////////////////////////////////////////////////////
// Note that the data stored in gfx::Color is *non-alpha-premultiplied*.
Color mSinglePixelColor;
int32_t mTimeout; // -1 means display forever
/** Indicates how many readers currently have locked this frame */
int32_t mLockCount;
RefPtr<VolatileBuffer> mVBuf;
VolatileBufferPtr<uint8_t> mVBufPtr;
SurfaceFormat mFormat;
uint8_t mPaletteDepth;
DisposalMethod mDisposalMethod;
BlendMethod mBlendMethod;
bool mSinglePixel;
bool mCompositingFailed;
bool mHasNoAlpha;
bool mNonPremult;
bool mOptimizable;
friend class DrawableFrameRef;
friend class RawAccessFrameRef;
};
/**

View File

@ -1,5 +0,0 @@
[2d.drawImage.broken.html]
type: testharness
[Canvas test: 2d.drawImage.broken]
expected: FAIL