Bug 859377 (Part 3) - Make ClippedImage cache temporary surfaces. r=joe

--HG--
extra : rebase_source : 060615904e1b416f84b4d6ecf6329841896f13da
This commit is contained in:
Seth Fowler 2013-04-25 15:58:32 -07:00
parent fa19988b4c
commit 38bbbc4266
2 changed files with 98 additions and 18 deletions

View File

@ -6,6 +6,7 @@
#include "gfxDrawable.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "mozilla/dom/SVGSVGElement.h"
#include "ClippedImage.h"
@ -15,6 +16,48 @@ using mozilla::layers::ImageContainer;
namespace mozilla {
namespace image {
class ClippedImageCachedSurface
{
public:
ClippedImageCachedSurface(gfxASurface* aSurface,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
float aFrame,
uint32_t aFlags)
: mSurface(aSurface)
, mViewportSize(aViewportSize)
, mFrame(aFrame)
, mFlags(aFlags)
{
MOZ_ASSERT(mSurface, "Must have a valid surface");
if (aSVGContext) {
mSVGContext.construct(*aSVGContext);
}
}
bool Matches(const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
float aFrame,
uint32_t aFlags)
{
bool matchesSVGContext = (!aSVGContext && mSVGContext.empty()) ||
*aSVGContext == mSVGContext.ref();
return mViewportSize == aViewportSize &&
matchesSVGContext &&
mFrame == aFrame &&
mFlags == aFlags;
}
gfxASurface* Surface() { return mSurface; }
private:
nsRefPtr<gfxASurface> mSurface;
const nsIntSize mViewportSize;
Maybe<SVGImageContext> mSVGContext;
const float mFrame;
const uint32_t mFlags;
};
class DrawSingleTileCallback : public gfxDrawingCallback
{
public:
@ -64,6 +107,9 @@ ClippedImage::ClippedImage(Image* aImage,
MOZ_ASSERT(aImage != nullptr, "ClippedImage requires an existing Image");
}
ClippedImage::~ClippedImage()
{ }
bool
ClippedImage::ShouldClip()
{
@ -175,25 +221,41 @@ ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize,
return InnerImage()->GetFrame(aWhichFrame, aFlags, _retval);
}
// Create a surface to draw into.
gfxImageSurface::gfxImageFormat format = gfxASurface::ImageFormatARGB32;
nsRefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()
->CreateOffscreenSurface(gfxIntSize(mClip.width, mClip.height),
gfxImageSurface::ContentFromFormat(format));
// Create our callback.
nsRefPtr<gfxDrawingCallback> drawTileCallback =
new DrawSingleTileCallback(this, mClip, aViewportSize, aSVGContext, aWhichFrame, aFlags);
nsRefPtr<gfxDrawable> drawable =
new gfxCallbackDrawable(drawTileCallback, mClip.Size());
float frameToDraw = InnerImage()->GetFrameIndex(aWhichFrame);
if (!mCachedSurface || !mCachedSurface->Matches(aViewportSize,
aSVGContext,
frameToDraw,
aFlags)) {
// Create a surface to draw into.
gfxImageSurface::gfxImageFormat format = gfxASurface::ImageFormatARGB32;
nsRefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()
->CreateOffscreenSurface(gfxIntSize(mClip.width, mClip.height),
gfxImageSurface::ContentFromFormat(format));
// Actually draw. The callback will end up invoking DrawSingleTile.
nsRefPtr<gfxContext> ctx = new gfxContext(surface);
gfxRect imageRect(0, 0, mClip.width, mClip.height);
gfxUtils::DrawPixelSnapped(ctx, drawable, gfxMatrix(),
imageRect, imageRect, imageRect, imageRect,
gfxASurface::ImageFormatARGB32, gfxPattern::FILTER_FAST);
// Create our callback.
nsRefPtr<gfxDrawingCallback> drawTileCallback =
new DrawSingleTileCallback(this, mClip, aViewportSize, aSVGContext, aWhichFrame, aFlags);
nsRefPtr<gfxDrawable> drawable =
new gfxCallbackDrawable(drawTileCallback, mClip.Size());
*_retval = surface.forget().get();
// Actually draw. The callback will end up invoking DrawSingleTile.
nsRefPtr<gfxContext> ctx = new gfxContext(surface);
gfxRect imageRect(0, 0, mClip.width, mClip.height);
gfxUtils::DrawPixelSnapped(ctx, drawable, gfxMatrix(),
imageRect, imageRect, imageRect, imageRect,
format, gfxPattern::FILTER_FAST);
// Cache the resulting surface.
mCachedSurface = new ClippedImageCachedSurface(surface,
aViewportSize,
aSVGContext,
frameToDraw,
aFlags);
}
MOZ_ASSERT(mCachedSurface, "Should have a cached surface now");
*_retval = mCachedSurface->Surface();
NS_ADDREF(*_retval);
return NS_OK;
}
@ -206,6 +268,10 @@ ClippedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval
// actually benefit from GetImageContainer, it would be a good idea to fix
// that method for performance reasons.
if (!ShouldClip()) {
return InnerImage()->GetImageContainer(aManager, _retval);
}
*_retval = nullptr;
return NS_OK;
}
@ -330,5 +396,14 @@ ClippedImage::DrawSingleTile(gfxContext* aContext,
viewportSize, aSVGContext, aWhichFrame, aFlags);
}
NS_IMETHODIMP
ClippedImage::RequestDiscard()
{
// We're very aggressive about discarding.
mCachedSurface = nullptr;
return InnerImage()->RequestDiscard();
}
} // namespace image
} // namespace mozilla

View File

@ -11,6 +11,7 @@
namespace mozilla {
namespace image {
class ClippedImageCachedSurface;
class DrawSingleTileCallback;
/**
@ -25,7 +26,7 @@ class ClippedImage : public ImageWrapper
public:
NS_DECL_ISUPPORTS
virtual ~ClippedImage() { }
virtual ~ClippedImage();
virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
@ -47,6 +48,7 @@ public:
const SVGImageContext* aSVGContext,
uint32_t aWhichFrame,
uint32_t aFlags) MOZ_OVERRIDE;
NS_IMETHOD RequestDiscard() MOZ_OVERRIDE;
protected:
ClippedImage(Image* aImage, nsIntRect aClip);
@ -74,6 +76,9 @@ private:
uint32_t aWhichFrame,
uint32_t aFlags);
// If we are forced to draw a temporary surface, we cache it here.
nsAutoPtr<ClippedImageCachedSurface> mCachedSurface;
nsIntRect mClip; // The region to clip to.
Maybe<bool> mShouldClip; // Memoized ShouldClip() if present.