Bug 781053 - Part 2 - Trigger empty transactions when an animated image in an ImageLayer changes frame. r=roc

This commit is contained in:
Matt Woodrow 2012-09-25 08:31:30 +12:00
parent 28ef42bcbf
commit 3fb0e93449
6 changed files with 101 additions and 29 deletions

View File

@ -171,7 +171,8 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
mHasBeenDecoded(false),
mInDecoder(false),
mAnimationFinished(false),
mFinishing(false)
mFinishing(false),
mInUpdateImageContainer(false)
{
// Set up the discard tracker node.
mDiscardTrackerNode.img = this;
@ -292,7 +293,6 @@ RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
uint32_t currentFrameIndex = mAnim->currentAnimationFrameIndex;
uint32_t nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
uint32_t timeout = 0;
mImageContainer = nullptr;
// Figure out if we have the next full frame. This is more complicated than
// just checking for mFrames.Length() because decoders append their frames
@ -439,6 +439,7 @@ RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
mFramesNotified++;
#endif
UpdateImageContainer();
observer->FrameChanged(nullptr, this, &dirtyRect);
}
}
@ -862,6 +863,28 @@ RasterImage::GetFrame(uint32_t aWhichFrame,
return rv;
}
already_AddRefed<layers::Image>
RasterImage::GetCurrentImage()
{
nsRefPtr<gfxASurface> imageSurface;
nsresult rv = GetFrame(FRAME_CURRENT, FLAG_SYNC_DECODE, getter_AddRefs(imageSurface));
NS_ENSURE_SUCCESS(rv, nullptr);
CairoImage::Data cairoData;
cairoData.mSurface = imageSurface;
GetWidth(&cairoData.mSize.width);
GetHeight(&cairoData.mSize.height);
ImageFormat cairoFormat = CAIRO_SURFACE;
nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&cairoFormat, 1);
NS_ASSERTION(image, "Failed to create Image");
NS_ASSERTION(image->GetFormat() == cairoFormat, "Wrong format");
static_cast<CairoImage*>(image.get())->SetData(cairoData);
return image.forget();
}
NS_IMETHODIMP
RasterImage::GetImageContainer(ImageContainer **_retval)
@ -872,24 +895,12 @@ RasterImage::GetImageContainer(ImageContainer **_retval)
return NS_OK;
}
CairoImage::Data cairoData;
nsRefPtr<gfxASurface> imageSurface;
nsresult rv = GetFrame(FRAME_CURRENT, FLAG_SYNC_DECODE, getter_AddRefs(imageSurface));
NS_ENSURE_SUCCESS(rv, rv);
cairoData.mSurface = imageSurface;
GetWidth(&cairoData.mSize.width);
GetHeight(&cairoData.mSize.height);
mImageContainer = LayerManager::CreateImageContainer();
// Now create a CairoImage to display the surface.
ImageFormat cairoFormat = CAIRO_SURFACE;
nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&cairoFormat, 1);
NS_ASSERTION(image, "Failed to create Image");
NS_ASSERTION(image->GetFormat() == cairoFormat, "Wrong format");
static_cast<CairoImage*>(image.get())->SetData(cairoData);
nsRefPtr<layers::Image> image = GetCurrentImage();
if (!image) {
return NS_ERROR_FAILURE;
}
mImageContainer->SetCurrentImageInTransaction(image);
*_retval = mImageContainer;
@ -897,6 +908,23 @@ RasterImage::GetImageContainer(ImageContainer **_retval)
return NS_OK;
}
void
RasterImage::UpdateImageContainer()
{
if (!mImageContainer || IsInUpdateImageContainer()) {
return;
}
SetInUpdateImageContainer(true);
nsRefPtr<layers::Image> image = GetCurrentImage();
if (!image) {
return;
}
mImageContainer->SetCurrentImage(image);
SetInUpdateImageContainer(false);
}
size_t
RasterImage::HeapSizeOfSourceWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const
{
@ -1185,8 +1213,11 @@ RasterImage::FrameUpdated(uint32_t aFrameNum, nsIntRect &aUpdatedRect)
NS_ABORT_IF_FALSE(frame, "Calling FrameUpdated on frame that doesn't exist!");
frame->ImageUpdated(aUpdatedRect);
// The image has changed, so we need to invalidate our cached ImageContainer.
mImageContainer = NULL;
if (aFrameNum == GetCurrentImgFrameIndex()) {
// The image has changed, so we need to invalidate our cached ImageContainer.
UpdateImageContainer();
}
}
nsresult
@ -1380,7 +1411,7 @@ RasterImage::ResetAnimation()
mAnim->lastCompositedFrameIndex = -1;
mAnim->currentAnimationFrameIndex = 0;
mImageContainer = nullptr;
UpdateImageContainer();
// Note - We probably want to kick off a redecode somewhere around here when
// we fix bug 500402.

View File

@ -127,6 +127,7 @@ namespace mozilla {
namespace layers {
class LayerManager;
class ImageContainer;
class Image;
}
namespace image {
@ -574,6 +575,12 @@ private:
bool ApplyDecodeFlags(uint32_t aNewFlags);
already_AddRefed<layers::Image> GetCurrentImage();
void UpdateImageContainer();
void SetInUpdateImageContainer(bool aInUpdate) { mInUpdateImageContainer = aInUpdate; }
bool IsInUpdateImageContainer() { return mInUpdateImageContainer; }
private: // data
nsIntSize mSize;
@ -654,6 +661,8 @@ private: // data
// Whether we're calling Decoder::Finish() from ShutdownDecoder.
bool mFinishing:1;
bool mInUpdateImageContainer:1;
// Decoding
nsresult WantDecodedFrames();
nsresult SyncDecode();

View File

@ -874,6 +874,25 @@ FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager* aManager)
}
}
void
FrameLayerBuilder::StoreOptimizedLayerForFrame(nsIFrame* aFrame, uint32_t aDisplayItemKey, Layer* aImage)
{
DisplayItemDataEntry *entry = mNewDisplayItemData.GetEntry(aFrame);
if (!entry)
return;
nsTArray<nsRefPtr<DisplayItemData> > *array = &entry->mData;
if (!array)
return;
for (uint32_t i = 0; i < array->Length(); ++i) {
if (array->ElementAt(i)->mDisplayItemKey == aDisplayItemKey) {
array->ElementAt(i)->mOptLayer = aImage;
return;
}
}
}
void
FrameLayerBuilder::DidEndTransaction()
{
@ -1610,6 +1629,9 @@ ContainerState::PopThebesLayerData()
imageLayer->IntersectClipRect(clip);
}
layer = imageLayer;
mLayerBuilder->StoreOptimizedLayerForFrame(data->mImage->GetUnderlyingFrame(),
data->mImage->GetPerFrameKey(),
imageLayer);
} else {
nsRefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer(data->mLayer);
colorLayer->SetIsFixedPosition(data->mLayer->GetIsFixedPosition());
@ -2991,6 +3013,10 @@ FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, uint32_t aDisplayItemKey)
return nullptr;
}
if (data->mOptLayer) {
return data->mOptLayer;
}
Layer* layer = data->mLayer;
if (!layer->HasUserData(&gColorLayerUserData) &&
!layer->HasUserData(&gImageLayerUserData) &&

View File

@ -13,6 +13,7 @@
#include "nsIFrame.h"
#include "nsDisplayListInvalidation.h"
#include "LayerTreeInvalidation.h"
#include "ImageLayers.h"
class nsDisplayListBuilder;
class nsDisplayList;
@ -100,6 +101,7 @@ public:
typedef layers::ContainerLayer ContainerLayer;
typedef layers::Layer Layer;
typedef layers::ThebesLayer ThebesLayer;
typedef layers::ImageLayer ImageLayer;
typedef layers::LayerManager LayerManager;
FrameLayerBuilder() :
@ -410,6 +412,14 @@ public:
*/
static gfxSize GetThebesLayerScaleForFrame(nsIFrame* aFrame);
/**
* Stores a Layer as the dedicated layer in the DisplayItemData for a given frame/key pair.
*
* Used when we optimize a ThebesLayer into an ImageLayer and want to retroactively update the
* DisplayItemData so we can retrieve the layer from within layout.
*/
void StoreOptimizedLayerForFrame(nsIFrame* aFrame, uint32_t aDisplayItemKey, Layer* aImage);
/**
* Clip represents the intersection of an optional rectangle with a
* list of rounded rectangles.
@ -534,6 +544,7 @@ protected:
bool FrameListMatches(nsDisplayItem* aOther);
nsRefPtr<Layer> mLayer;
nsRefPtr<Layer> mOptLayer;
nsRefPtr<LayerManager> mInactiveManager;
nsAutoTArray<nsIFrame*, 2> mFrameList;
nsAutoPtr<nsDisplayItemGeometry> mGeometry;

View File

@ -679,11 +679,7 @@ nsImageFrame::FrameChanged(imgIRequest *aRequest,
return NS_OK;
}
if (aDirtyRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
InvalidateFrame();
} else {
InvalidateFrameWithRect(SourceRectToDest(*aDirtyRect));
}
InvalidateLayer(nsDisplayItem::TYPE_IMAGE);
return NS_OK;
}

View File

@ -638,9 +638,8 @@ NS_IMETHODIMP nsImageBoxFrame::FrameChanged(imgIRequest *aRequest,
if ((0 == mRect.width) || (0 == mRect.height)) {
return NS_OK;
}
nsBoxLayoutState state(PresContext());
this->Redraw(state);
InvalidateLayer(nsDisplayItem::TYPE_XUL_IMAGE);
return NS_OK;
}