зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 4 changesets (bug 1060200) for b2g reftest-1 orange
CLOSED TREE Backed out changeset e337e2f16a2f (bug 1060200) Backed out changeset 2bf7aaed0b8f (bug 1060200) Backed out changeset b6985b70d53e (bug 1060200) Backed out changeset af081b216b86 (bug 1060200)
This commit is contained in:
Родитель
85af53494d
Коммит
40821c35d1
|
@ -24,7 +24,6 @@
|
|||
#include "nsIThreadPool.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "SurfaceCache.h"
|
||||
#include "FrameAnimator.h"
|
||||
|
||||
#include "nsPNGDecoder.h"
|
||||
|
@ -68,12 +67,6 @@ using std::ceil;
|
|||
#define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
|
||||
#define DECODE_FLAGS_DEFAULT 0
|
||||
|
||||
static uint32_t
|
||||
DecodeFlags(uint32_t aFlags)
|
||||
{
|
||||
return aFlags & DECODE_FLAGS_MASK;
|
||||
}
|
||||
|
||||
/* Accounting for compressed data */
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo *
|
||||
|
@ -191,141 +184,184 @@ DiscardingEnabled()
|
|||
return enabled;
|
||||
}
|
||||
|
||||
class ScaleRunner : public nsRunnable
|
||||
class ScaleRequest
|
||||
{
|
||||
enum ScaleState
|
||||
{
|
||||
eNew,
|
||||
eReady,
|
||||
eFinish,
|
||||
eFinishWithError
|
||||
};
|
||||
|
||||
public:
|
||||
ScaleRunner(RasterImage* aImage,
|
||||
uint32_t aImageFlags,
|
||||
const nsIntSize& aSize,
|
||||
RawAccessFrameRef&& aSrcRef)
|
||||
: mImage(aImage)
|
||||
, mSrcRef(Move(aSrcRef))
|
||||
, mDstSize(aSize)
|
||||
, mImageFlags(aImageFlags)
|
||||
, mState(eNew)
|
||||
ScaleRequest(RasterImage* aImage,
|
||||
const nsIntSize& aSize,
|
||||
RawAccessFrameRef&& aSrcRef)
|
||||
: weakImage(aImage)
|
||||
, srcRef(Move(aSrcRef))
|
||||
, srcRect(srcRef->GetRect())
|
||||
, dstSize(aSize)
|
||||
, done(false)
|
||||
, stopped(false)
|
||||
{
|
||||
MOZ_ASSERT(!mSrcRef->GetIsPaletted());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!srcRef->GetIsPaletted());
|
||||
MOZ_ASSERT(aSize.width > 0 && aSize.height > 0);
|
||||
}
|
||||
|
||||
bool Init()
|
||||
// This can only be called on the main thread.
|
||||
bool AcquireResources()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mState == eNew, "Calling Init() twice?");
|
||||
|
||||
// We'll need a destination frame. It's unconditionally ARGB32 because
|
||||
// that's what the scaler outputs.
|
||||
nsRefPtr<imgFrame> tentativeDstFrame = new imgFrame();
|
||||
nsresult rv =
|
||||
tentativeDstFrame->InitForDecoder(mDstSize, SurfaceFormat::B8G8R8A8);
|
||||
if (NS_FAILED(rv)) {
|
||||
nsRefPtr<RasterImage> image = weakImage.get();
|
||||
if (!image) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need a strong reference to the raw data for the destination frame.
|
||||
// (We already got one for the source frame in the constructor.)
|
||||
RawAccessFrameRef tentativeDstRef = tentativeDstFrame->RawAccessRef();
|
||||
if (!tentativeDstRef) {
|
||||
return false;
|
||||
if (!dstFrame) {
|
||||
// We need to hold a lock onto the RasterImage object itself so that
|
||||
// it (and its associated imgFrames) aren't marked as discardable.
|
||||
if (NS_FAILED(image->LockImage())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We'll need a destination frame. It's unconditionally ARGB32 because
|
||||
// that's what the scaler outputs.
|
||||
nsRefPtr<imgFrame> tentativeDstFrame = new imgFrame();
|
||||
nsresult rv =
|
||||
tentativeDstFrame->InitForDecoder(dstSize, SurfaceFormat::B8G8R8A8);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need a strong reference to the raw data for the destination frame.
|
||||
// (We already got one for the source frame in the constructor.)
|
||||
RawAccessFrameRef tentativeDstRef = tentativeDstFrame->RawAccessRef();
|
||||
if (!tentativeDstRef) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dstFrame = tentativeDstFrame.forget();
|
||||
dstRef = Move(tentativeDstRef);
|
||||
}
|
||||
|
||||
// Everything worked, so commit to these objects and mark ourselves ready.
|
||||
mDstRef = Move(tentativeDstRef);
|
||||
mState = eReady;
|
||||
|
||||
// Insert the new surface into the cache immediately. We need to do this so
|
||||
// that we won't start multiple scaling jobs for the same size.
|
||||
SurfaceCache::Insert(mDstRef.get(), ImageKey(mImage.get()),
|
||||
RasterSurfaceKey(mDstSize.ToIntSize(), mImageFlags));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
// This can only be called on the main thread.
|
||||
void ReleaseResources()
|
||||
{
|
||||
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();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Actually do the scaling.
|
||||
bool succeeded =
|
||||
gfx::Scale(srcData, srcSize.width, srcSize.height, srcStride,
|
||||
dstData, mDstSize.width, mDstSize.height, dstStride,
|
||||
srcFormat);
|
||||
nsRefPtr<RasterImage> image = weakImage.get();
|
||||
if (image) {
|
||||
image->UnlockImage();
|
||||
}
|
||||
|
||||
if (succeeded) {
|
||||
// Mark the frame as complete and discardable.
|
||||
mDstRef->ImageUpdated(mDstRef->GetRect());
|
||||
MOZ_ASSERT(mDstRef->ImageComplete(),
|
||||
"Incomplete, but just updated the entire frame");
|
||||
if (DiscardingEnabled()) {
|
||||
mDstRef->SetDiscardable();
|
||||
}
|
||||
if (DiscardingEnabled() && dstFrame) {
|
||||
dstFrame->SetDiscardable();
|
||||
}
|
||||
|
||||
// Release everything except dstFrame, which we keep around for RasterImage
|
||||
// to retrieve.
|
||||
srcRef.reset();
|
||||
dstRef.reset();
|
||||
}
|
||||
|
||||
// These values may only be modified on the main thread.
|
||||
WeakPtr<RasterImage> weakImage;
|
||||
nsRefPtr<imgFrame> dstFrame;
|
||||
RawAccessFrameRef srcRef;
|
||||
RawAccessFrameRef dstRef;
|
||||
|
||||
// Below are the values that may be modified on the scaling thread.
|
||||
nsIntRect srcRect;
|
||||
nsIntSize dstSize;
|
||||
bool done;
|
||||
|
||||
// This boolean is accessed from both threads simultaneously without locking.
|
||||
// That's safe because stopping a ScaleRequest is strictly an optimization;
|
||||
// if we're not cache-coherent, at worst we'll do extra work.
|
||||
bool stopped;
|
||||
};
|
||||
|
||||
class DrawRunner : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit DrawRunner(ScaleRequest* request)
|
||||
: mScaleRequest(request)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
// Grab the weak image pointer before the request releases it.
|
||||
nsRefPtr<RasterImage> image = mScaleRequest->weakImage.get();
|
||||
|
||||
// ScaleWorker is finished with this request, so release everything that we
|
||||
// don't need anymore.
|
||||
mScaleRequest->ReleaseResources();
|
||||
|
||||
if (image) {
|
||||
RasterImage::ScaleStatus status;
|
||||
if (mScaleRequest->done) {
|
||||
status = RasterImage::SCALE_DONE;
|
||||
} else {
|
||||
status = RasterImage::SCALE_INVALID;
|
||||
}
|
||||
|
||||
// We need to send notifications and release our references on the main
|
||||
// thread, so finish up there.
|
||||
mState = succeeded ? eFinish : eFinishWithError;
|
||||
NS_DispatchToMainThread(this);
|
||||
} else if (mState == eFinish) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mDstRef, "Should have a valid scaled frame");
|
||||
|
||||
// Notify, so observers can redraw.
|
||||
nsRefPtr<RasterImage> image = mImage.get();
|
||||
if (image) {
|
||||
image->NotifyNewScaledFrame();
|
||||
}
|
||||
|
||||
// We're done, so release everything.
|
||||
mSrcRef.reset();
|
||||
mDstRef.reset();
|
||||
} else if (mState == eFinishWithError) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("HQ scaling failed");
|
||||
|
||||
// Remove the frame from the cache since we know we don't need it.
|
||||
SurfaceCache::RemoveIfPresent(ImageKey(mImage.get()),
|
||||
RasterSurfaceKey(mDstSize.ToIntSize(),
|
||||
mImageFlags));
|
||||
|
||||
// Release everything we're holding, too.
|
||||
mSrcRef.reset();
|
||||
mDstRef.reset();
|
||||
} else {
|
||||
// mState must be eNew, which is invalid in Run().
|
||||
MOZ_ASSERT(false, "Need to call Init() before dispatching");
|
||||
image->ScalingDone(mScaleRequest, status);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~ScaleRunner()
|
||||
private: /* members */
|
||||
nsAutoPtr<ScaleRequest> mScaleRequest;
|
||||
};
|
||||
|
||||
class ScaleRunner : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ScaleRunner(RasterImage* aImage,
|
||||
const nsIntSize& aSize,
|
||||
RawAccessFrameRef&& aSrcRef)
|
||||
{
|
||||
MOZ_ASSERT(!mSrcRef && !mDstRef,
|
||||
"Should have released strong refs in Run()");
|
||||
nsAutoPtr<ScaleRequest> req(new ScaleRequest(aImage, aSize, Move(aSrcRef)));
|
||||
if (!req->AcquireResources()) {
|
||||
return;
|
||||
}
|
||||
|
||||
aImage->ScalingStart(req);
|
||||
mScaleRequest = req;
|
||||
}
|
||||
|
||||
WeakPtr<RasterImage> mImage;
|
||||
RawAccessFrameRef mSrcRef;
|
||||
RawAccessFrameRef mDstRef;
|
||||
const nsIntSize mDstSize;
|
||||
uint32_t mImageFlags;
|
||||
ScaleState mState;
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
ScaleRequest* req = mScaleRequest.get();
|
||||
|
||||
if (!req->stopped) {
|
||||
// Collect information from the frames that we need to scale.
|
||||
uint8_t* srcData = req->srcRef->GetImageData();
|
||||
uint8_t* dstData = req->dstRef->GetImageData();
|
||||
uint32_t srcStride = req->srcRef->GetImageBytesPerRow();
|
||||
uint32_t dstStride = req->dstRef->GetImageBytesPerRow();
|
||||
SurfaceFormat srcFormat = req->srcRef->GetFormat();
|
||||
|
||||
// Actually do the scaling.
|
||||
req->done =
|
||||
gfx::Scale(srcData, req->srcRect.width, req->srcRect.height, srcStride,
|
||||
dstData, req->dstSize.width, req->dstSize.height, dstStride,
|
||||
srcFormat);
|
||||
} else {
|
||||
req->done = false;
|
||||
}
|
||||
|
||||
// OK, we've got a new scaled image. Let's get the main thread to unlock and
|
||||
// redraw it.
|
||||
nsRefPtr<DrawRunner> runner = new DrawRunner(mScaleRequest.forget());
|
||||
NS_DispatchToMainThread(runner);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool IsOK() const { return !!mScaleRequest; }
|
||||
|
||||
private:
|
||||
nsAutoPtr<ScaleRequest> mScaleRequest;
|
||||
};
|
||||
|
||||
/* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton;
|
||||
|
@ -367,7 +403,8 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
|
|||
mFinishing(false),
|
||||
mInUpdateImageContainer(false),
|
||||
mWantFullDecode(false),
|
||||
mPendingError(false)
|
||||
mPendingError(false),
|
||||
mScaleRequest(nullptr)
|
||||
{
|
||||
mStatusTrackerInit = new imgStatusTrackerInit(this, aStatusTracker);
|
||||
|
||||
|
@ -415,9 +452,6 @@ RasterImage::~RasterImage()
|
|||
}
|
||||
}
|
||||
|
||||
// Release any HQ scaled frames from the surface cache.
|
||||
SurfaceCache::Discard(this);
|
||||
|
||||
mAnim = nullptr;
|
||||
|
||||
// Total statistics
|
||||
|
@ -1034,7 +1068,13 @@ size_t
|
|||
RasterImage::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
|
||||
MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
return mFrameBlender.SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
|
||||
size_t n = mFrameBlender.SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
|
||||
|
||||
if (mScaleResult.status == SCALE_DONE) {
|
||||
n += mScaleResult.frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t
|
||||
|
@ -1905,6 +1945,10 @@ RasterImage::Discard(bool force)
|
|||
// Delete all the decoded frames
|
||||
mFrameBlender.Discard();
|
||||
|
||||
// Clear our downscaled frame.
|
||||
mScaleResult.status = SCALE_INVALID;
|
||||
mScaleResult.frame = nullptr;
|
||||
|
||||
// Clear the last decoded multipart frame.
|
||||
mMultipartDecodedFrame = nullptr;
|
||||
|
||||
|
@ -2477,90 +2521,105 @@ RasterImage::SyncDecode()
|
|||
}
|
||||
|
||||
bool
|
||||
RasterImage::CanScale(GraphicsFilter aFilter,
|
||||
const nsIntSize& aSize,
|
||||
uint32_t aFlags)
|
||||
RasterImage::CanQualityScale(const gfx::Size& scale)
|
||||
{
|
||||
#ifndef MOZ_ENABLE_SKIA
|
||||
// The high-quality scaler requires Skia.
|
||||
return false;
|
||||
#else
|
||||
// Check basic requirements: HQ downscaling is enabled, we're decoded, the
|
||||
// flags allow us to do it, and a 'good' filter is being used. The flags may
|
||||
// ask us not to scale because the caller isn't drawing to the window. If
|
||||
// we're drawing to something else (e.g. a canvas) we usually have no way of
|
||||
// updating what we've drawn, so HQ scaling is useless.
|
||||
if (!gHQDownscaling || !mDecoded ||
|
||||
!(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING) ||
|
||||
aFilter != GraphicsFilter::FILTER_GOOD) {
|
||||
// If target size is 1:1 with original, don't scale.
|
||||
if (scale.width == 1.0 && scale.height == 1.0)
|
||||
return false;
|
||||
|
||||
// To save memory don't quality upscale images bigger than the limit.
|
||||
if (scale.width > 1.0 || scale.height > 1.0) {
|
||||
uint32_t scaled_size = static_cast<uint32_t>(mSize.width * mSize.height * scale.width * scale.height);
|
||||
if (scaled_size > gHQUpscalingMaxSize)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RasterImage::CanScale(GraphicsFilter aFilter, gfx::Size aScale, uint32_t aFlags)
|
||||
{
|
||||
// The high-quality scaler requires Skia.
|
||||
#ifdef MOZ_ENABLE_SKIA
|
||||
// We don't use the scaler for animated or multipart images to avoid doing a
|
||||
// bunch of work on an image that just gets thrown away.
|
||||
if (mAnim || mMultipart) {
|
||||
return false;
|
||||
}
|
||||
// We only use the scaler when drawing to the window because, if we're not
|
||||
// drawing to a window (eg a canvas), updates to that image will be ignored.
|
||||
if (gHQDownscaling && aFilter == GraphicsFilter::FILTER_GOOD &&
|
||||
!mAnim && mDecoded && !mMultipart && CanQualityScale(aScale) &&
|
||||
(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
|
||||
gfxFloat factor = gHQDownscalingMinFactor / 1000.0;
|
||||
|
||||
// If target size is 1:1 with original, don't scale.
|
||||
if (aSize == mSize) {
|
||||
return false;
|
||||
return (aScale.width < factor || aScale.height < factor);
|
||||
}
|
||||
|
||||
// To save memory, don't quality upscale images bigger than the limit.
|
||||
if (aSize.width > mSize.width || aSize.height > mSize.height) {
|
||||
uint32_t scaledSize = static_cast<uint32_t>(aSize.width * aSize.height);
|
||||
if (scaledSize > gHQUpscalingMaxSize) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// There's no point in scaling if we can't store the result.
|
||||
if (!SurfaceCache::CanHold(aSize.ToIntSize())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// XXX(seth): It's not clear what this check buys us over gHQUpscalingMaxSize.
|
||||
// The default value of this pref is 1000, which means that we never upscale.
|
||||
// If that's all it's getting us, I'd rather we just forbid that explicitly.
|
||||
gfx::Size scale(double(aSize.width) / mSize.width,
|
||||
double(aSize.height) / mSize.height);
|
||||
gfxFloat minFactor = gHQDownscalingMinFactor / 1000.0;
|
||||
return (scale.width < minFactor || scale.height < minFactor);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::NotifyNewScaledFrame()
|
||||
RasterImage::ScalingStart(ScaleRequest* request)
|
||||
{
|
||||
if (mStatusTracker) {
|
||||
// Send an invalidation so observers will repaint and can take advantage of
|
||||
// the new scaled frame if possible.
|
||||
// XXX(seth): Why does FrameChanged take a pointer and not a reference?
|
||||
nsIntRect invalidationRect(0, 0, mSize.width, mSize.height);
|
||||
mStatusTracker->FrameChanged(&invalidationRect);
|
||||
MOZ_ASSERT(request);
|
||||
mScaleResult.scaledSize = request->dstSize;
|
||||
mScaleResult.status = SCALE_PENDING;
|
||||
mScaleRequest = request;
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
|
||||
{
|
||||
MOZ_ASSERT(status == SCALE_DONE || status == SCALE_INVALID);
|
||||
MOZ_ASSERT(request);
|
||||
|
||||
if (status == SCALE_DONE) {
|
||||
MOZ_ASSERT(request->done);
|
||||
|
||||
mScaleResult.status = SCALE_DONE;
|
||||
mScaleResult.frame = request->dstFrame.forget();
|
||||
mScaleResult.scaledSize = request->dstSize;
|
||||
|
||||
mScaleResult.frame->ImageUpdated(mScaleResult.frame->GetRect());
|
||||
|
||||
if (mStatusTracker) {
|
||||
mStatusTracker->FrameChanged(&request->srcRect);
|
||||
}
|
||||
} else {
|
||||
mScaleResult.status = SCALE_INVALID;
|
||||
mScaleResult.frame = nullptr;
|
||||
}
|
||||
|
||||
// If we were waiting for this scale to come through, forget the scale
|
||||
// request. Otherwise, we still have a scale outstanding that it's possible
|
||||
// for us to (want to) stop.
|
||||
if (mScaleRequest == request) {
|
||||
mScaleRequest = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::RequestScale(imgFrame* aFrame,
|
||||
uint32_t aFlags,
|
||||
const nsIntSize& aSize)
|
||||
RasterImage::RequestScale(imgFrame* aFrame, nsIntSize aSize)
|
||||
{
|
||||
// We can't scale if we can't lock the image data for this frame.
|
||||
// We can't store more than one scaled version of an image right now, so if
|
||||
// there's more than one instance of this image, bail.
|
||||
if (mLockCount != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We also can't scale if we can't lock the image data for this frame.
|
||||
RawAccessFrameRef frameRef = aFrame->RawAccessRef();
|
||||
if (!frameRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We also can't scale frames that need padding.
|
||||
if (aFrame->NeedsPadding()) {
|
||||
return;
|
||||
// If we have an outstanding request, signal it to stop (if it can).
|
||||
if (mScaleRequest) {
|
||||
mScaleRequest->stopped = true;
|
||||
}
|
||||
|
||||
nsRefPtr<ScaleRunner> runner =
|
||||
new ScaleRunner(this, DecodeFlags(aFlags), aSize, Move(frameRef));
|
||||
if (runner->Init()) {
|
||||
nsRefPtr<ScaleRunner> runner = new ScaleRunner(this, aSize, Move(frameRef));
|
||||
if (runner->IsOK()) {
|
||||
if (!sScaleWorkerThread) {
|
||||
NS_NewNamedThread("Image Scaler", getter_AddRefs(sScaleWorkerThread));
|
||||
ClearOnShutdown(&sScaleWorkerThread);
|
||||
|
@ -2579,20 +2638,24 @@ RasterImage::DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
|||
uint32_t aFlags)
|
||||
{
|
||||
DrawableFrameRef frameRef;
|
||||
gfx::Size scale(double(aSize.width) / mSize.width,
|
||||
double(aSize.height) / mSize.height);
|
||||
|
||||
if (CanScale(aFilter, aSize, aFlags) && !aFrameRef->IsSinglePixel()) {
|
||||
frameRef =
|
||||
SurfaceCache::Lookup(ImageKey(this),
|
||||
RasterSurfaceKey(aSize.ToIntSize(),
|
||||
DecodeFlags(aFlags)));
|
||||
if (!frameRef) {
|
||||
// We either didn't have a matching scaled frame or the OS threw it away.
|
||||
// Request a new one so we'll be ready next time. For now, we'll fall back
|
||||
// to aFrameRef below.
|
||||
RequestScale(aFrameRef.get(), aFlags, aSize);
|
||||
if (CanScale(aFilter, scale, aFlags) && !aFrameRef->IsSinglePixel()) {
|
||||
// FIXME: Current implementation doesn't support pre-downscale
|
||||
// mechanism for multiple sizes from same src, since we cache
|
||||
// pre-downscaled frame only for the latest requested scale.
|
||||
// The solution is to cache more than one scaled image frame
|
||||
// for each RasterImage.
|
||||
if (mScaleResult.status == SCALE_DONE && mScaleResult.scaledSize == aSize) {
|
||||
frameRef = mScaleResult.frame->DrawableRef();
|
||||
}
|
||||
if (frameRef && !frameRef->ImageComplete()) {
|
||||
frameRef.reset(); // We're still scaling, so we can't use this yet.
|
||||
if (!frameRef &&
|
||||
(mScaleResult.status != SCALE_PENDING || mScaleResult.scaledSize != aSize)) {
|
||||
// We either didn't have a complete scaled frame, it didn't match, or the
|
||||
// OS threw it away. Fall back to aFrame, and request a new scaled frame
|
||||
// if we're not already working on the one we need.
|
||||
RequestScale(aFrameRef.get(), aSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2606,8 +2669,6 @@ RasterImage::DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
|||
// adjust the drawing parameters accordingly.
|
||||
nsIntRect finalFrameRect = frameRef->GetRect();
|
||||
if (finalFrameRect.Size() != aSize) {
|
||||
gfx::Size scale(double(aSize.width) / mSize.width,
|
||||
double(aSize.height) / mSize.height);
|
||||
aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
|
||||
region.Scale(1.0 / scale.width, 1.0 / scale.height);
|
||||
}
|
||||
|
@ -2615,6 +2676,9 @@ RasterImage::DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
|||
// We can only use padding if we're using the original |aFrameRef|, unscaled.
|
||||
// (If so, we moved it into |frameRef|, so |aFrameRef| is empty.) Because of
|
||||
// this restriction, we don't scale frames that require padding.
|
||||
// XXX(seth): We actually do scale such frames right now though, if a single
|
||||
// frame of a non-animated image requires padding. We'll fix that in bug
|
||||
// 1060200, because dependencies between bugs make it hard to fix here.
|
||||
nsIntMargin padding(0, 0, 0, 0);
|
||||
if (!aFrameRef) {
|
||||
padding = nsIntMargin(finalFrameRect.y,
|
||||
|
@ -3614,23 +3678,23 @@ RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
|
|||
}
|
||||
|
||||
nsIntSize destSize(ceil(aDest.width), ceil(aDest.height));
|
||||
gfx::Size scale(double(destSize.width) / mSize.width,
|
||||
double(destSize.height) / mSize.height);
|
||||
|
||||
if (CanScale(aFilter, destSize, aFlags)) {
|
||||
DrawableFrameRef frameRef =
|
||||
SurfaceCache::Lookup(ImageKey(this),
|
||||
RasterSurfaceKey(destSize.ToIntSize(),
|
||||
DecodeFlags(aFlags)));
|
||||
|
||||
if (frameRef && frameRef->ImageComplete()) {
|
||||
if (CanScale(aFilter, scale, aFlags)) {
|
||||
if (mScaleResult.scaledSize == destSize) {
|
||||
if (mScaleResult.status == SCALE_DONE) {
|
||||
return destSize; // We have an existing HQ scale for this size.
|
||||
}
|
||||
if (!frameRef) {
|
||||
// We could HQ scale to this size, but we haven't. Request a scale now.
|
||||
frameRef = GetFrame(GetRequestedFrameIndex(aWhichFrame));
|
||||
if (frameRef) {
|
||||
RequestScale(frameRef.get(), aFlags, destSize);
|
||||
} else if (mScaleResult.status == SCALE_PENDING) {
|
||||
return mSize; // We're waiting for exactly this result.
|
||||
}
|
||||
}
|
||||
|
||||
// If there's only one instance of this image on this page, ask for a scale.
|
||||
DrawableFrameRef frameRef = GetFrame(GetRequestedFrameIndex(aWhichFrame));
|
||||
if (frameRef) {
|
||||
RequestScale(frameRef.get(), destSize);
|
||||
}
|
||||
}
|
||||
|
||||
// We either can't HQ scale to this size or the scaled version isn't ready
|
||||
|
|
|
@ -132,9 +132,9 @@ class Image;
|
|||
|
||||
namespace image {
|
||||
|
||||
class ScaleRequest;
|
||||
class Decoder;
|
||||
class FrameAnimator;
|
||||
class ScaleRunner;
|
||||
|
||||
class RasterImage MOZ_FINAL : public ImageResource
|
||||
, public nsIProperties
|
||||
|
@ -298,6 +298,23 @@ public:
|
|||
// Called from module startup. Sets up RasterImage to be used.
|
||||
static void Initialize();
|
||||
|
||||
enum ScaleStatus
|
||||
{
|
||||
SCALE_INVALID,
|
||||
SCALE_PENDING,
|
||||
SCALE_DONE
|
||||
};
|
||||
|
||||
// Call this with a new ScaleRequest to mark this RasterImage's scale result
|
||||
// as waiting for the results of this request. You call to ScalingDone before
|
||||
// request is destroyed!
|
||||
void ScalingStart(ScaleRequest* request);
|
||||
|
||||
// Call this with a finished ScaleRequest to set this RasterImage's scale
|
||||
// result. Give it a ScaleStatus of SCALE_DONE if everything succeeded, and
|
||||
// SCALE_INVALID otherwise.
|
||||
void ScalingDone(ScaleRequest* request, ScaleStatus status);
|
||||
|
||||
// Decoder shutdown
|
||||
enum eShutdownIntent {
|
||||
eShutdownIntent_Done = 0,
|
||||
|
@ -309,6 +326,9 @@ public:
|
|||
// Decode strategy
|
||||
|
||||
private:
|
||||
// Initiates an HQ scale for the given frame, if possible.
|
||||
void RequestScale(imgFrame* aFrame, nsIntSize aScale);
|
||||
|
||||
already_AddRefed<imgStatusTracker> CurrentStatusTracker()
|
||||
{
|
||||
mDecodingMonitor.AssertCurrentThreadIn();
|
||||
|
@ -704,28 +724,32 @@ private: // data
|
|||
bool IsDecodeFinished();
|
||||
TimeStamp mDrawStartTime;
|
||||
|
||||
inline bool CanQualityScale(const gfx::Size& scale);
|
||||
inline bool CanScale(GraphicsFilter aFilter, gfx::Size aScale, uint32_t aFlags);
|
||||
|
||||
struct ScaleResult
|
||||
{
|
||||
ScaleResult()
|
||||
: status(SCALE_INVALID)
|
||||
{}
|
||||
|
||||
nsIntSize scaledSize;
|
||||
nsRefPtr<imgFrame> frame;
|
||||
ScaleStatus status;
|
||||
};
|
||||
|
||||
ScaleResult mScaleResult;
|
||||
|
||||
// We hold on to a bare pointer to a ScaleRequest while it's outstanding so
|
||||
// we can mark it as stopped if necessary. The ScaleWorker/DrawWorker duo
|
||||
// will inform us when to let go of this pointer.
|
||||
ScaleRequest* mScaleRequest;
|
||||
|
||||
// Initializes imgStatusTracker and resets it on RasterImage destruction.
|
||||
nsAutoPtr<imgStatusTrackerInit> mStatusTrackerInit;
|
||||
|
||||
nsresult ShutdownDecoder(eShutdownIntent aIntent);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Scaling.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initiates an HQ scale for the given frame, if possible.
|
||||
void RequestScale(imgFrame* aFrame, uint32_t aFlags, const nsIntSize& aSize);
|
||||
|
||||
// Determines whether we can perform an HQ scale with the given parameters.
|
||||
bool CanScale(GraphicsFilter aFilter, const nsIntSize& aSize, uint32_t aFlags);
|
||||
|
||||
// Called by the HQ scaler when a new scaled frame is ready.
|
||||
void NotifyNewScaledFrame();
|
||||
|
||||
friend class ScaleRunner;
|
||||
|
||||
|
||||
// Error handling.
|
||||
void DoError();
|
||||
|
||||
|
|
|
@ -339,20 +339,6 @@ public:
|
|||
return ref;
|
||||
}
|
||||
|
||||
void RemoveIfPresent(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
|
||||
if (!cache)
|
||||
return; // No cached surfaces for this image.
|
||||
|
||||
nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
|
||||
if (!surface)
|
||||
return; // Lookup in the per-image cache missed.
|
||||
|
||||
Remove(surface);
|
||||
}
|
||||
|
||||
bool CanHold(const Cost aCost) const
|
||||
{
|
||||
return aCost <= mMaxCost;
|
||||
|
@ -522,10 +508,8 @@ SurfaceCache::Shutdown()
|
|||
SurfaceCache::Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
MOZ_ASSERT(sInstance, "Should be initialized");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sInstance) {
|
||||
return DrawableFrameRef();
|
||||
}
|
||||
|
||||
return sInstance->Lookup(aImageKey, aSurfaceKey);
|
||||
}
|
||||
|
@ -535,52 +519,42 @@ SurfaceCache::Insert(imgFrame* aSurface,
|
|||
const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
MOZ_ASSERT(sInstance, "Should be initialized");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (sInstance) {
|
||||
Cost cost = ComputeCost(aSurfaceKey.Size());
|
||||
sInstance->Insert(aSurface, aSurfaceKey.Size(), cost, aImageKey,
|
||||
aSurfaceKey);
|
||||
}
|
||||
|
||||
Cost cost = ComputeCost(aSurfaceKey.Size());
|
||||
return sInstance->Insert(aSurface, aSurfaceKey.Size(), cost, aImageKey,
|
||||
aSurfaceKey);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SurfaceCache::CanHold(const IntSize& aSize)
|
||||
{
|
||||
MOZ_ASSERT(sInstance, "Should be initialized");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sInstance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Cost cost = ComputeCost(aSize);
|
||||
return sInstance->CanHold(cost);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SurfaceCache::RemoveIfPresent(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (sInstance) {
|
||||
sInstance->RemoveIfPresent(aImageKey, aSurfaceKey);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SurfaceCache::Discard(Image* aImageKey)
|
||||
{
|
||||
MOZ_ASSERT(sInstance, "Should be initialized");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (sInstance) {
|
||||
sInstance->Discard(aImageKey);
|
||||
}
|
||||
|
||||
return sInstance->Discard(aImageKey);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SurfaceCache::DiscardAll()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sInstance) {
|
||||
sInstance->DiscardAll();
|
||||
}
|
||||
// nothing to discard
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
|
|
|
@ -36,14 +36,23 @@ typedef Image* ImageKey;
|
|||
* SurfaceKey contains the information we need to look up a specific cached
|
||||
* surface. Together with an ImageKey, this uniquely identifies the surface.
|
||||
*
|
||||
* Callers should construct a SurfaceKey using the appropriate helper function
|
||||
* for their image type - either RasterSurfaceKey or VectorSurfaceKey.
|
||||
* XXX(seth): Right now this is specialized to the needs of VectorImage. We'll
|
||||
* generalize it in bug 919071.
|
||||
*/
|
||||
class SurfaceKey
|
||||
{
|
||||
typedef gfx::IntSize IntSize;
|
||||
|
||||
public:
|
||||
SurfaceKey(const IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
const float aAnimationTime,
|
||||
const uint32_t aFlags)
|
||||
: mSize(aSize)
|
||||
, mSVGContext(aSVGContext)
|
||||
, mAnimationTime(aAnimationTime)
|
||||
, mFlags(aFlags)
|
||||
{ }
|
||||
|
||||
bool operator==(const SurfaceKey& aOther) const
|
||||
{
|
||||
return aOther.mSize == mSize &&
|
||||
|
@ -63,52 +72,16 @@ public:
|
|||
IntSize Size() const { return mSize; }
|
||||
|
||||
private:
|
||||
SurfaceKey(const IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
const float aAnimationTime,
|
||||
const uint32_t aFlags)
|
||||
: mSize(aSize)
|
||||
, mSVGContext(aSVGContext)
|
||||
, mAnimationTime(aAnimationTime)
|
||||
, mFlags(aFlags)
|
||||
{ }
|
||||
|
||||
static uint32_t HashSIC(const SVGImageContext& aSIC) {
|
||||
return aSIC.Hash();
|
||||
}
|
||||
|
||||
friend SurfaceKey RasterSurfaceKey(const IntSize&, const uint32_t);
|
||||
friend SurfaceKey VectorSurfaceKey(const IntSize&,
|
||||
const Maybe<SVGImageContext>&,
|
||||
const float);
|
||||
|
||||
IntSize mSize;
|
||||
Maybe<SVGImageContext> mSVGContext;
|
||||
float mAnimationTime;
|
||||
uint32_t mFlags;
|
||||
};
|
||||
|
||||
inline SurfaceKey
|
||||
RasterSurfaceKey(const gfx::IntSize& aSize,
|
||||
const uint32_t aFlags)
|
||||
{
|
||||
// We don't care about aAnimationTime for RasterImage because it's not
|
||||
// currently possible to store anything but the first frame in the
|
||||
// SurfaceCache.
|
||||
return SurfaceKey(aSize, Nothing(), 0.0f, aFlags);
|
||||
}
|
||||
|
||||
inline SurfaceKey
|
||||
VectorSurfaceKey(const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
const float aAnimationTime)
|
||||
{
|
||||
// We don't care about aFlags for VectorImage because none of the flags we
|
||||
// have right now influence VectorImage's rendering. If we add a new flag that
|
||||
// *does* affect how a VectorImage renders, we'll have to change this.
|
||||
return SurfaceKey(aSize, aSVGContext, aAnimationTime, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* SurfaceCache is an imagelib-global service that allows caching of temporary
|
||||
* surfaces. Surfaces expire from the cache automatically if they go too long
|
||||
|
@ -183,18 +156,6 @@ struct SurfaceCache
|
|||
*/
|
||||
static bool CanHold(const IntSize& aSize);
|
||||
|
||||
/*
|
||||
* Removes a surface from the cache, if it's present.
|
||||
*
|
||||
* Use this function to remove individual surfaces that have become invalid.
|
||||
* Prefer Discard() or DiscardAll() when they're applicable, as they have much
|
||||
* better performance than calling this function repeatedly.
|
||||
*
|
||||
* @param aImageKey Key data identifying which image the surface belongs to.
|
||||
* @param aSurfaceKey Key data which uniquely identifies the requested surface.
|
||||
*/
|
||||
static void RemoveIfPresent(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey);
|
||||
/*
|
||||
* Evicts any cached surfaces associated with the given image from the cache.
|
||||
* This MUST be called, at a minimum, when the image is destroyed. If
|
||||
|
|
|
@ -849,9 +849,8 @@ VectorImage::Draw(gfxContext* aContext,
|
|||
|
||||
DrawableFrameRef frameRef =
|
||||
SurfaceCache::Lookup(ImageKey(this),
|
||||
VectorSurfaceKey(params.size,
|
||||
params.svgContext,
|
||||
params.animationTime));
|
||||
SurfaceKey(params.size, aSVGContext,
|
||||
animTime, aFlags));
|
||||
|
||||
// Draw.
|
||||
if (frameRef) {
|
||||
|
@ -912,9 +911,8 @@ VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams)
|
|||
|
||||
// Attempt to cache the frame.
|
||||
SurfaceCache::Insert(frame, ImageKey(this),
|
||||
VectorSurfaceKey(aParams.size,
|
||||
aParams.svgContext,
|
||||
aParams.animationTime));
|
||||
SurfaceKey(aParams.size, aParams.svgContext,
|
||||
aParams.animationTime, aParams.flags));
|
||||
|
||||
// Draw.
|
||||
nsRefPtr<gfxDrawable> drawable =
|
||||
|
|
|
@ -87,7 +87,6 @@ public:
|
|||
|
||||
nsIntRect GetRect() const;
|
||||
IntSize GetSize() const { return mSize; }
|
||||
bool NeedsPadding() const { return mOffset != nsIntPoint(0, 0); }
|
||||
int32_t GetStride() const;
|
||||
SurfaceFormat GetFormat() const;
|
||||
bool GetNeedsBackground() const;
|
||||
|
|
Загрузка…
Ссылка в новой задаче