Bug 764299 (Part 4) - Cache rasterized surfaces in VectorImage. r=dholbert

This commit is contained in:
Seth Fowler 2013-10-22 13:38:08 +02:00
Родитель 0f7250223b
Коммит 6581f44a7f
2 изменённых файлов: 186 добавлений и 44 удалений

Просмотреть файл

@ -7,6 +7,7 @@
#include "gfxContext.h" #include "gfxContext.h"
#include "gfxDrawable.h" #include "gfxDrawable.h"
#include "gfxPlatform.h"
#include "gfxUtils.h" #include "gfxUtils.h"
#include "imgDecoderObserver.h" #include "imgDecoderObserver.h"
#include "mozilla/AutoRestore.h" #include "mozilla/AutoRestore.h"
@ -23,6 +24,7 @@
#include "Orientation.h" #include "Orientation.h"
#include "SVGDocumentWrapper.h" #include "SVGDocumentWrapper.h"
#include "nsIDOMEventListener.h" #include "nsIDOMEventListener.h"
#include "SurfaceCache.h"
namespace mozilla { namespace mozilla {
@ -316,6 +318,7 @@ VectorImage::VectorImage(imgStatusTracker* aStatusTracker,
VectorImage::~VectorImage() VectorImage::~VectorImage()
{ {
CancelAllListeners(); CancelAllListeners();
SurfaceCache::Discard(this);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -487,6 +490,9 @@ VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime)
// under WillRefresh() (which would be called by our own refresh driver) so // under WillRefresh() (which would be called by our own refresh driver) so
// that we only send these notifications if we actually have a document // that we only send these notifications if we actually have a document
// that is observing us. // that is observing us.
// XXX(seth): We may need to duplicate this code so that non-animated images
// get handled correctly. See bug 922899.
SurfaceCache::Discard(this);
mStatusTracker->FrameChanged(&nsIntRect::GetMaxSizedIntRect()); mStatusTracker->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
mStatusTracker->OnStopFrame(); mStatusTracker->OnStopFrame();
} }
@ -676,6 +682,65 @@ VectorImage::GetImageContainer(LayerManager* aManager,
return NS_OK; return NS_OK;
} }
struct SVGDrawingParameters
{
SVGDrawingParameters(gfxContext* aContext,
GraphicsFilter aFilter,
const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFill,
const nsIntRect& aSubimage,
const nsIntSize& aViewportSize,
const SVGImageContext* aSVGContext,
float aAnimationTime,
uint32_t aFlags)
: context(aContext)
, filter(aFilter)
, fill(aFill)
, viewportSize(aViewportSize)
, animationTime(aAnimationTime)
, svgContext(aSVGContext)
, flags(aFlags)
{
// gfxUtils::DrawPixelSnapped may rasterize this image to a temporary surface
// if we hit the tiling path. Unfortunately, the temporary surface isn't
// created at the size at which we'll ultimately draw, causing fuzzy output.
// To fix this we pre-apply the transform's scaling to the drawing parameters
// and remove the scaling from the transform, so the fact that temporary
// surfaces won't take the scaling into account doesn't matter. (Bug 600207.)
scale = aUserSpaceToImageSpace.ScaleFactors(true);
gfxPoint translation(aUserSpaceToImageSpace.GetTranslation());
// Remove the scaling from the transform.
gfxMatrix unscale;
unscale.Translate(gfxPoint(translation.x / scale.width,
translation.y / scale.height));
unscale.Scale(1.0 / scale.width, 1.0 / scale.height);
unscale.Translate(-translation);
userSpaceToImageSpace = aUserSpaceToImageSpace * unscale;
// Rescale drawing parameters.
gfxIntSize drawableSize(aViewportSize.width / scale.width,
aViewportSize.height / scale.height);
sourceRect = userSpaceToImageSpace.Transform(aFill);
imageRect = nsIntRect(0, 0, drawableSize.width, drawableSize.height);
subimage = gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
subimage.ScaleRoundOut(1.0 / scale.width, 1.0 / scale.height);
}
gfxContext* context;
GraphicsFilter filter;
gfxMatrix userSpaceToImageSpace;
gfxRect fill;
gfxRect subimage;
gfxRect sourceRect;
nsIntRect imageRect;
nsIntSize viewportSize;
gfxSize scale;
float animationTime;
const SVGImageContext* svgContext;
uint32_t flags;
};
//****************************************************************************** //******************************************************************************
/* [noscript] void draw(in gfxContext aContext, /* [noscript] void draw(in gfxContext aContext,
* in gfxGraphicsFilter aFilter, * in gfxGraphicsFilter aFilter,
@ -716,58 +781,130 @@ VectorImage::Draw(gfxContext* aContext,
AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing); AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
mIsDrawing = true; mIsDrawing = true;
float time = aWhichFrame == FRAME_FIRST ? 0.0f float animTime = (aWhichFrame == FRAME_FIRST) ? 0.0f
: mSVGDocumentWrapper->GetCurrentTime(); : mSVGDocumentWrapper->GetCurrentTime();
AutoSVGRenderingState autoSVGState(aSVGContext, AutoSVGRenderingState autoSVGState(aSVGContext, animTime,
time,
mSVGDocumentWrapper->GetRootSVGElem()); mSVGDocumentWrapper->GetRootSVGElem());
// gfxUtils::DrawPixelSnapped may rasterize this image to a temporary surface // Pack up the drawing parameters.
// if we hit the tiling path. Unfortunately, the temporary surface isn't SVGDrawingParameters params(aContext, aFilter, aUserSpaceToImageSpace, aFill,
// created at the size at which we'll ultimately draw, causing fuzzy output. aSubimage, aViewportSize, aSVGContext, animTime, aFlags);
// To fix this we pre-apply the transform's scaling to the drawing parameters
// and remove the scaling from the transform, so the fact that temporary
// surfaces won't take the scaling into account doesn't matter. (Bug 600207.)
gfxSize scale(aUserSpaceToImageSpace.ScaleFactors(true));
gfxPoint translation(aUserSpaceToImageSpace.GetTranslation());
// Remove the scaling from the transform. // Check the cache.
gfxMatrix unscale; nsRefPtr<gfxDrawable> drawable =
unscale.Translate(gfxPoint(translation.x / scale.width, SurfaceCache::Lookup(ImageKey(this),
translation.y / scale.height)); SurfaceKey(params.imageRect.Size(), params.scale,
unscale.Scale(1.0 / scale.width, 1.0 / scale.height); aSVGContext, animTime, aFlags));
unscale.Translate(-translation);
gfxMatrix unscaledTransform(aUserSpaceToImageSpace * unscale);
mSVGDocumentWrapper->UpdateViewportBounds(aViewportSize); // Draw.
if (drawable) {
Show(drawable, params);
} else {
CreateDrawableAndShow(params);
}
return NS_OK;
}
void
VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
{
mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
mSVGDocumentWrapper->FlushImageTransformInvalidation(); mSVGDocumentWrapper->FlushImageTransformInvalidation();
// Rescale drawing parameters.
gfxIntSize drawableSize(aViewportSize.width / scale.width,
aViewportSize.height / scale.height);
gfxRect drawableSourceRect = unscaledTransform.Transform(aFill);
gfxRect drawableImageRect(0, 0, drawableSize.width, drawableSize.height);
gfxRect drawableSubimage(aSubimage.x, aSubimage.y,
aSubimage.width, aSubimage.height);
drawableSubimage.ScaleRoundOut(1.0 / scale.width, 1.0 / scale.height);
nsRefPtr<gfxDrawingCallback> cb = nsRefPtr<gfxDrawingCallback> cb =
new SVGDrawingCallback(mSVGDocumentWrapper, new SVGDrawingCallback(mSVGDocumentWrapper,
nsIntRect(nsIntPoint(0, 0), aViewportSize), nsIntRect(nsIntPoint(0, 0), aParams.viewportSize),
scale, aParams.scale,
aFlags); aParams.flags);
nsRefPtr<gfxDrawable> drawable = new gfxCallbackDrawable(cb, drawableSize); nsRefPtr<gfxDrawable> svgDrawable = new gfxCallbackDrawable(cb, aParams.imageRect.Size());
gfxUtils::DrawPixelSnapped(aContext, drawable, unscaledTransform, // Refuse to cache animated images.
drawableSubimage, drawableSourceRect, // XXX(seth): We may remove this restriction in bug 922893.
drawableImageRect, aFill, if (mHaveAnimations)
gfxImageFormatARGB32, aFilter, aFlags); return Show(svgDrawable, aParams);
// If the image is too big to fit in the cache, don't go any further.
if (!SurfaceCache::CanHold(aParams.imageRect.Size()))
return Show(svgDrawable, aParams);
// Try to create an offscreen surface.
mozilla::RefPtr<mozilla::gfx::DrawTarget> target;
nsRefPtr<gfxContext> ctx;
nsRefPtr<gfxASurface> surface;
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
target = gfxPlatform::GetPlatform()->
CreateOffscreenContentDrawTarget(aParams.imageRect.Size(), gfx::FORMAT_B8G8R8A8);
// XXX(seth): All of this ugliness (the backend check, |surface|, the
// GetThebesSurfaceForDrawTarget call) is needed because we can't use Moz2D
// for drawing in all cases yet. See bug 923341.
if (target) {
switch (target->GetType()) {
case mozilla::gfx::BACKEND_DIRECT2D:
case mozilla::gfx::BACKEND_DIRECT2D1_1:
case mozilla::gfx::BACKEND_SKIA:
// Can't cache on this backend.
return Show(svgDrawable, aParams);
default:
surface = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(target);
ctx = new gfxContext(target);
}
}
} else {
target = gfxPlatform::GetPlatform()->
CreateOffscreenCanvasDrawTarget(aParams.imageRect.Size(), gfx::FORMAT_B8G8R8A8);
surface = target ? gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(target)
: nullptr;
ctx = surface ? new gfxContext(surface) : nullptr;
}
// If we couldn't create the draw target, it was probably because it would end
// up way too big. Generally it also wouldn't fit in the cache, but the prefs
// could be set such that the cache isn't the limiting factor. If we couldn't
// create the surface, we are on a platform that does not support
// GetThebesSurfaceForDrawTarget. This will be fixed in bug 923341.
if (!target || !surface)
return Show(svgDrawable, aParams);
// Actually draw. (We use FILTER_NEAREST since we never scale here.)
gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(),
aParams.imageRect, aParams.imageRect,
aParams.imageRect, aParams.imageRect,
gfxImageFormatARGB32,
GraphicsFilter::FILTER_NEAREST, aParams.flags);
// Attempt to cache the resulting surface.
SurfaceCache::Insert(target,
ImageKey(this),
SurfaceKey(aParams.imageRect.Size(), aParams.scale,
aParams.svgContext, aParams.animationTime,
aParams.flags));
// Draw. Note that if SurfaceCache::Insert failed for whatever reason,
// then |target| is all that is keeping the pixel data alive, so we have
// to draw before returning from this function.
nsRefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, aParams.imageRect.Size());
Show(drawable, aParams);
}
void
VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
{
MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
aParams.userSpaceToImageSpace,
aParams.subimage, aParams.sourceRect,
aParams.imageRect, aParams.fill,
gfxImageFormatARGB32,
aParams.filter, aParams.flags);
MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now"); MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
mRenderingObserver->ResumeHonoringInvalidations(); mRenderingObserver->ResumeHonoringInvalidations();
return NS_OK;
} }
//****************************************************************************** //******************************************************************************
@ -815,7 +952,7 @@ VectorImage::UnlockImage()
NS_IMETHODIMP NS_IMETHODIMP
VectorImage::RequestDiscard() VectorImage::RequestDiscard()
{ {
// This method is for image-discarding, which only applies to RasterImages. SurfaceCache::Discard(this);
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
class nsIRequest; class nsIRequest;
class gfxDrawable;
namespace mozilla { namespace mozilla {
namespace layers { namespace layers {
@ -19,6 +20,7 @@ class ImageContainer;
} }
namespace image { namespace image {
struct SVGDrawingParameters;
class SVGDocumentWrapper; class SVGDocumentWrapper;
class SVGRootRenderingObserver; class SVGRootRenderingObserver;
class SVGLoadEventListener; class SVGLoadEventListener;
@ -82,6 +84,9 @@ protected:
virtual nsresult StopAnimation(); virtual nsresult StopAnimation();
virtual bool ShouldAnimate(); virtual bool ShouldAnimate();
void CreateDrawableAndShow(const SVGDrawingParameters& aParams);
void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
private: private:
void CancelAllListeners(); void CancelAllListeners();