mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Bug 1267260 - Change CanvasImageCache to lookup cache images based on imgIContainer instead of imgIRequest. r=seth
--HG-- extra : rebase_source : 872ee783a05d0319ed69ce3c494dbf2a3206e26d
This commit is contained in:
parent
275c209d7d
commit
376575c614
@ -20,60 +20,65 @@ namespace mozilla {
|
|||||||
using namespace dom;
|
using namespace dom;
|
||||||
using namespace gfx;
|
using namespace gfx;
|
||||||
|
|
||||||
struct ImageCacheKey {
|
/**
|
||||||
ImageCacheKey(Element* aImage,
|
* Used for images specific to this one canvas. Required
|
||||||
|
* due to CORS security.
|
||||||
|
*/
|
||||||
|
struct ImageCacheKey
|
||||||
|
{
|
||||||
|
ImageCacheKey(imgIContainer* aImage,
|
||||||
HTMLCanvasElement* aCanvas,
|
HTMLCanvasElement* aCanvas,
|
||||||
bool aIsAccelerated)
|
bool aIsAccelerated)
|
||||||
: mImage(aImage)
|
: mImage(aImage)
|
||||||
, mCanvas(aCanvas)
|
, mCanvas(aCanvas)
|
||||||
, mIsAccelerated(aIsAccelerated)
|
, mIsAccelerated(aIsAccelerated)
|
||||||
{}
|
{}
|
||||||
Element* mImage;
|
nsCOMPtr<imgIContainer> mImage;
|
||||||
HTMLCanvasElement* mCanvas;
|
HTMLCanvasElement* mCanvas;
|
||||||
bool mIsAccelerated;
|
bool mIsAccelerated;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ImageCacheEntryData {
|
/**
|
||||||
|
* Cache data needs to be separate from the entry
|
||||||
|
* for nsExpirationTracker.
|
||||||
|
*/
|
||||||
|
struct ImageCacheEntryData
|
||||||
|
{
|
||||||
ImageCacheEntryData(const ImageCacheEntryData& aOther)
|
ImageCacheEntryData(const ImageCacheEntryData& aOther)
|
||||||
: mImage(aOther.mImage)
|
: mImage(aOther.mImage)
|
||||||
, mILC(aOther.mILC)
|
|
||||||
, mCanvas(aOther.mCanvas)
|
, mCanvas(aOther.mCanvas)
|
||||||
, mIsAccelerated(aOther.mIsAccelerated)
|
, mIsAccelerated(aOther.mIsAccelerated)
|
||||||
, mRequest(aOther.mRequest)
|
|
||||||
, mSourceSurface(aOther.mSourceSurface)
|
, mSourceSurface(aOther.mSourceSurface)
|
||||||
, mSize(aOther.mSize)
|
, mSize(aOther.mSize)
|
||||||
{}
|
{}
|
||||||
explicit ImageCacheEntryData(const ImageCacheKey& aKey)
|
explicit ImageCacheEntryData(const ImageCacheKey& aKey)
|
||||||
: mImage(aKey.mImage)
|
: mImage(aKey.mImage)
|
||||||
, mILC(nullptr)
|
|
||||||
, mCanvas(aKey.mCanvas)
|
, mCanvas(aKey.mCanvas)
|
||||||
, mIsAccelerated(aKey.mIsAccelerated)
|
, mIsAccelerated(aKey.mIsAccelerated)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
nsExpirationState* GetExpirationState() { return &mState; }
|
nsExpirationState* GetExpirationState() { return &mState; }
|
||||||
|
|
||||||
size_t SizeInBytes() { return mSize.width * mSize.height * 4; }
|
size_t SizeInBytes() { return mSize.width * mSize.height * 4; }
|
||||||
|
|
||||||
// Key
|
// Key
|
||||||
RefPtr<Element> mImage;
|
nsCOMPtr<imgIContainer> mImage;
|
||||||
nsIImageLoadingContent* mILC;
|
HTMLCanvasElement* mCanvas;
|
||||||
RefPtr<HTMLCanvasElement> mCanvas;
|
|
||||||
bool mIsAccelerated;
|
bool mIsAccelerated;
|
||||||
// Value
|
// Value
|
||||||
nsCOMPtr<imgIRequest> mRequest;
|
|
||||||
RefPtr<SourceSurface> mSourceSurface;
|
RefPtr<SourceSurface> mSourceSurface;
|
||||||
IntSize mSize;
|
IntSize mSize;
|
||||||
nsExpirationState mState;
|
nsExpirationState mState;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImageCacheEntry : public PLDHashEntryHdr {
|
class ImageCacheEntry : public PLDHashEntryHdr
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
typedef ImageCacheKey KeyType;
|
typedef ImageCacheKey KeyType;
|
||||||
typedef const ImageCacheKey* KeyTypePointer;
|
typedef const ImageCacheKey* KeyTypePointer;
|
||||||
|
|
||||||
explicit ImageCacheEntry(const KeyType* aKey) :
|
explicit ImageCacheEntry(const KeyType* aKey) :
|
||||||
mData(new ImageCacheEntryData(*aKey)) {}
|
mData(new ImageCacheEntryData(*aKey)) {}
|
||||||
ImageCacheEntry(const ImageCacheEntry &toCopy) :
|
ImageCacheEntry(const ImageCacheEntry& toCopy) :
|
||||||
mData(new ImageCacheEntryData(*toCopy.mData)) {}
|
mData(new ImageCacheEntryData(*toCopy.mData)) {}
|
||||||
~ImageCacheEntry() {}
|
~ImageCacheEntry() {}
|
||||||
|
|
||||||
@ -87,52 +92,61 @@ public:
|
|||||||
static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
|
static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
|
||||||
static PLDHashNumber HashKey(KeyTypePointer key)
|
static PLDHashNumber HashKey(KeyTypePointer key)
|
||||||
{
|
{
|
||||||
return HashGeneric(key->mImage, key->mCanvas, key->mIsAccelerated);
|
return HashGeneric(key->mImage.get(), key->mCanvas, key->mIsAccelerated);
|
||||||
}
|
}
|
||||||
enum { ALLOW_MEMMOVE = true };
|
enum { ALLOW_MEMMOVE = true };
|
||||||
|
|
||||||
nsAutoPtr<ImageCacheEntryData> mData;
|
nsAutoPtr<ImageCacheEntryData> mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SimpleImageCacheKey {
|
|
||||||
SimpleImageCacheKey(const imgIRequest* aImage,
|
/**
|
||||||
bool aIsAccelerated)
|
* Used for all images across all canvases.
|
||||||
|
*/
|
||||||
|
struct AllCanvasImageCacheKey
|
||||||
|
{
|
||||||
|
AllCanvasImageCacheKey(imgIContainer* aImage,
|
||||||
|
bool aIsAccelerated)
|
||||||
: mImage(aImage)
|
: mImage(aImage)
|
||||||
, mIsAccelerated(aIsAccelerated)
|
, mIsAccelerated(aIsAccelerated)
|
||||||
{}
|
{}
|
||||||
const imgIRequest* mImage;
|
|
||||||
|
nsCOMPtr<imgIContainer> mImage;
|
||||||
bool mIsAccelerated;
|
bool mIsAccelerated;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SimpleImageCacheEntry : public PLDHashEntryHdr {
|
class AllCanvasImageCacheEntry : public PLDHashEntryHdr {
|
||||||
public:
|
public:
|
||||||
typedef SimpleImageCacheKey KeyType;
|
typedef AllCanvasImageCacheKey KeyType;
|
||||||
typedef const SimpleImageCacheKey* KeyTypePointer;
|
typedef const AllCanvasImageCacheKey* KeyTypePointer;
|
||||||
|
|
||||||
explicit SimpleImageCacheEntry(KeyTypePointer aKey)
|
explicit AllCanvasImageCacheEntry(const KeyType* aKey)
|
||||||
: mRequest(const_cast<imgIRequest*>(aKey->mImage))
|
: mImage(aKey->mImage)
|
||||||
, mIsAccelerated(aKey->mIsAccelerated)
|
, mIsAccelerated(aKey->mIsAccelerated)
|
||||||
{}
|
{}
|
||||||
SimpleImageCacheEntry(const SimpleImageCacheEntry &toCopy)
|
|
||||||
: mRequest(toCopy.mRequest)
|
AllCanvasImageCacheEntry(const AllCanvasImageCacheEntry &toCopy)
|
||||||
|
: mImage(toCopy.mImage)
|
||||||
, mIsAccelerated(toCopy.mIsAccelerated)
|
, mIsAccelerated(toCopy.mIsAccelerated)
|
||||||
, mSourceSurface(toCopy.mSourceSurface)
|
, mSourceSurface(toCopy.mSourceSurface)
|
||||||
{}
|
{}
|
||||||
~SimpleImageCacheEntry() {}
|
|
||||||
|
~AllCanvasImageCacheEntry() {}
|
||||||
|
|
||||||
bool KeyEquals(KeyTypePointer key) const
|
bool KeyEquals(KeyTypePointer key) const
|
||||||
{
|
{
|
||||||
return key->mImage == mRequest && key->mIsAccelerated == mIsAccelerated;
|
return mImage == key->mImage &&
|
||||||
|
mIsAccelerated == key->mIsAccelerated;
|
||||||
}
|
}
|
||||||
|
|
||||||
static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
|
static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
|
||||||
static PLDHashNumber HashKey(KeyTypePointer key)
|
static PLDHashNumber HashKey(KeyTypePointer key)
|
||||||
{
|
{
|
||||||
return HashGeneric(key->mImage, key->mIsAccelerated);
|
return HashGeneric(key->mImage.get(), key->mIsAccelerated);
|
||||||
}
|
}
|
||||||
enum { ALLOW_MEMMOVE = true };
|
enum { ALLOW_MEMMOVE = true };
|
||||||
|
|
||||||
nsCOMPtr<imgIRequest> mRequest;
|
nsCOMPtr<imgIContainer> mImage;
|
||||||
bool mIsAccelerated;
|
bool mIsAccelerated;
|
||||||
RefPtr<SourceSurface> mSourceSurface;
|
RefPtr<SourceSurface> mSourceSurface;
|
||||||
};
|
};
|
||||||
@ -142,7 +156,8 @@ static int32_t sCanvasImageCacheLimit = 0;
|
|||||||
|
|
||||||
class ImageCacheObserver;
|
class ImageCacheObserver;
|
||||||
|
|
||||||
class ImageCache final : public nsExpirationTracker<ImageCacheEntryData,4> {
|
class ImageCache final : public nsExpirationTracker<ImageCacheEntryData,4>
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
// We use 3 generations of 1 second each to get a 2-3 seconds timeout.
|
// We use 3 generations of 1 second each to get a 2-3 seconds timeout.
|
||||||
enum { GENERATION_MS = 1000 };
|
enum { GENERATION_MS = 1000 };
|
||||||
@ -153,20 +168,24 @@ public:
|
|||||||
{
|
{
|
||||||
mTotal -= aObject->SizeInBytes();
|
mTotal -= aObject->SizeInBytes();
|
||||||
RemoveObject(aObject);
|
RemoveObject(aObject);
|
||||||
// Deleting the entry will delete aObject since the entry owns aObject
|
|
||||||
mSimpleCache.RemoveEntry(SimpleImageCacheKey(aObject->mRequest, aObject->mIsAccelerated));
|
// Remove from the all canvas cache entry first since nsExpirationTracker
|
||||||
|
// will delete aObject.
|
||||||
|
mAllCanvasCache.RemoveEntry(AllCanvasImageCacheKey(aObject->mImage, aObject->mIsAccelerated));
|
||||||
|
|
||||||
|
// Deleting the entry will delete aObject since the entry owns aObject.
|
||||||
mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas, aObject->mIsAccelerated));
|
mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas, aObject->mIsAccelerated));
|
||||||
}
|
}
|
||||||
|
|
||||||
nsTHashtable<ImageCacheEntry> mCache;
|
nsTHashtable<ImageCacheEntry> mCache;
|
||||||
nsTHashtable<SimpleImageCacheEntry> mSimpleCache;
|
nsTHashtable<AllCanvasImageCacheEntry> mAllCanvasCache;
|
||||||
size_t mTotal;
|
size_t mTotal;
|
||||||
RefPtr<ImageCacheObserver> mImageCacheObserver;
|
RefPtr<ImageCacheObserver> mImageCacheObserver;
|
||||||
};
|
};
|
||||||
|
|
||||||
static ImageCache* gImageCache = nullptr;
|
static ImageCache* gImageCache = nullptr;
|
||||||
|
|
||||||
// Listen memory-pressure event for image cache purge
|
// Listen memory-pressure event for image cache purge.
|
||||||
class ImageCacheObserver final : public nsIObserver
|
class ImageCacheObserver final : public nsIObserver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -256,10 +275,33 @@ ImageCache::~ImageCache() {
|
|||||||
mImageCacheObserver->Destroy();
|
mImageCacheObserver->Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static already_AddRefed<imgIContainer>
|
||||||
|
GetImageContainer(dom::Element* aImage)
|
||||||
|
{
|
||||||
|
nsCOMPtr<imgIRequest> request;
|
||||||
|
nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage);
|
||||||
|
if (!ilc) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
||||||
|
getter_AddRefs(request));
|
||||||
|
if (!request) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<imgIContainer> imgContainer;
|
||||||
|
request->GetImage(getter_AddRefs(imgContainer));
|
||||||
|
if (!imgContainer) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return imgContainer.forget();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CanvasImageCache::NotifyDrawImage(Element* aImage,
|
CanvasImageCache::NotifyDrawImage(Element* aImage,
|
||||||
HTMLCanvasElement* aCanvas,
|
HTMLCanvasElement* aCanvas,
|
||||||
imgIRequest* aRequest,
|
|
||||||
SourceSurface* aSource,
|
SourceSurface* aSource,
|
||||||
const IntSize& aSize,
|
const IntSize& aSize,
|
||||||
bool aIsAccelerated)
|
bool aIsAccelerated)
|
||||||
@ -269,32 +311,31 @@ CanvasImageCache::NotifyDrawImage(Element* aImage,
|
|||||||
nsContentUtils::RegisterShutdownObserver(new CanvasImageCacheShutdownObserver());
|
nsContentUtils::RegisterShutdownObserver(new CanvasImageCacheShutdownObserver());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageCacheEntry* entry =
|
nsCOMPtr<imgIContainer> imgContainer = GetImageContainer(aImage);
|
||||||
gImageCache->mCache.PutEntry(ImageCacheKey(aImage, aCanvas, aIsAccelerated));
|
if (!imgContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AllCanvasImageCacheKey allCanvasCacheKey(imgContainer, aIsAccelerated);
|
||||||
|
ImageCacheKey canvasCacheKey(imgContainer, aCanvas, aIsAccelerated);
|
||||||
|
ImageCacheEntry* entry = gImageCache->mCache.PutEntry(canvasCacheKey);
|
||||||
|
|
||||||
if (entry) {
|
if (entry) {
|
||||||
if (entry->mData->mSourceSurface) {
|
if (entry->mData->mSourceSurface) {
|
||||||
// We are overwriting an existing entry.
|
// We are overwriting an existing entry.
|
||||||
gImageCache->mTotal -= entry->mData->SizeInBytes();
|
gImageCache->mTotal -= entry->mData->SizeInBytes();
|
||||||
gImageCache->RemoveObject(entry->mData);
|
gImageCache->RemoveObject(entry->mData);
|
||||||
gImageCache->mSimpleCache.RemoveEntry(SimpleImageCacheKey(entry->mData->mRequest, entry->mData->mIsAccelerated));
|
gImageCache->mAllCanvasCache.RemoveEntry(allCanvasCacheKey);
|
||||||
}
|
}
|
||||||
gImageCache->AddObject(entry->mData);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage);
|
gImageCache->AddObject(entry->mData);
|
||||||
if (ilc) {
|
|
||||||
ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
|
||||||
getter_AddRefs(entry->mData->mRequest));
|
|
||||||
}
|
|
||||||
entry->mData->mILC = ilc;
|
|
||||||
entry->mData->mSourceSurface = aSource;
|
entry->mData->mSourceSurface = aSource;
|
||||||
entry->mData->mSize = aSize;
|
entry->mData->mSize = aSize;
|
||||||
|
|
||||||
gImageCache->mTotal += entry->mData->SizeInBytes();
|
gImageCache->mTotal += entry->mData->SizeInBytes();
|
||||||
|
|
||||||
if (entry->mData->mRequest) {
|
AllCanvasImageCacheEntry* allEntry = gImageCache->mAllCanvasCache.PutEntry(allCanvasCacheKey);
|
||||||
SimpleImageCacheEntry* simpleentry =
|
if (allEntry) {
|
||||||
gImageCache->mSimpleCache.PutEntry(SimpleImageCacheKey(entry->mData->mRequest, aIsAccelerated));
|
allEntry->mSourceSurface = aSource;
|
||||||
simpleentry->mSourceSurface = aSource;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,54 +348,56 @@ CanvasImageCache::NotifyDrawImage(Element* aImage,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SourceSurface*
|
SourceSurface*
|
||||||
CanvasImageCache::Lookup(Element* aImage,
|
CanvasImageCache::LookupAllCanvas(Element* aImage,
|
||||||
HTMLCanvasElement* aCanvas,
|
bool aIsAccelerated)
|
||||||
gfx::IntSize* aSize,
|
|
||||||
bool aIsAccelerated)
|
|
||||||
{
|
{
|
||||||
if (!gImageCache)
|
if (!gImageCache) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
ImageCacheEntry* entry =
|
nsCOMPtr<imgIContainer> imgContainer = GetImageContainer(aImage);
|
||||||
gImageCache->mCache.GetEntry(ImageCacheKey(aImage, aCanvas, aIsAccelerated));
|
if (!imgContainer) {
|
||||||
if (!entry || !entry->mData->mILC)
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<imgIRequest> request;
|
AllCanvasImageCacheEntry* entry =
|
||||||
entry->mData->mILC->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(request));
|
gImageCache->mAllCanvasCache.GetEntry(AllCanvasImageCacheKey(imgContainer, aIsAccelerated));
|
||||||
if (request != entry->mData->mRequest)
|
if (!entry) {
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
gImageCache->MarkUsed(entry->mData);
|
|
||||||
|
|
||||||
*aSize = entry->mData->mSize;
|
|
||||||
return entry->mData->mSourceSurface;
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceSurface*
|
|
||||||
CanvasImageCache::SimpleLookup(Element* aImage,
|
|
||||||
bool aIsAccelerated)
|
|
||||||
{
|
|
||||||
if (!gImageCache)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
nsCOMPtr<imgIRequest> request;
|
|
||||||
nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage);
|
|
||||||
if (!ilc)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
|
||||||
getter_AddRefs(request));
|
|
||||||
if (!request)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
SimpleImageCacheEntry* entry = gImageCache->mSimpleCache.GetEntry(SimpleImageCacheKey(request, aIsAccelerated));
|
|
||||||
if (!entry)
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return entry->mSourceSurface;
|
return entry->mSourceSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SourceSurface*
|
||||||
|
CanvasImageCache::LookupCanvas(Element* aImage,
|
||||||
|
HTMLCanvasElement* aCanvas,
|
||||||
|
IntSize* aSizeOut,
|
||||||
|
bool aIsAccelerated)
|
||||||
|
{
|
||||||
|
if (!gImageCache) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<imgIContainer> imgContainer = GetImageContainer(aImage);
|
||||||
|
if (!imgContainer) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageCacheEntry* entry =
|
||||||
|
gImageCache->mCache.GetEntry(ImageCacheKey(imgContainer, aCanvas, aIsAccelerated));
|
||||||
|
if (!entry) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(aSizeOut);
|
||||||
|
|
||||||
|
gImageCache->MarkUsed(entry->mData);
|
||||||
|
*aSizeOut = entry->mData->mSize;
|
||||||
|
return entry->mData->mSourceSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(CanvasImageCacheShutdownObserver, nsIObserver)
|
NS_IMPL_ISUPPORTS(CanvasImageCacheShutdownObserver, nsIObserver)
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#ifndef CANVASIMAGECACHE_H_
|
#ifndef CANVASIMAGECACHE_H_
|
||||||
#define CANVASIMAGECACHE_H_
|
#define CANVASIMAGECACHE_H_
|
||||||
|
|
||||||
|
#include "mozilla/RefPtr.h"
|
||||||
#include "nsSize.h"
|
#include "nsSize.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
@ -17,7 +18,7 @@ namespace gfx {
|
|||||||
class SourceSurface;
|
class SourceSurface;
|
||||||
} // namespace gfx
|
} // namespace gfx
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
class imgIRequest;
|
class imgIContainer;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
@ -25,34 +26,30 @@ class CanvasImageCache {
|
|||||||
typedef mozilla::gfx::SourceSurface SourceSurface;
|
typedef mozilla::gfx::SourceSurface SourceSurface;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Notify that image element aImage was (or is about to be) drawn to aCanvas
|
* Notify that image element aImage was drawn to aCanvas element
|
||||||
* using the first frame of aRequest's image. The data for the surface is
|
* using the first frame of aRequest's image. The data for the surface is
|
||||||
* in aSurface, and the image size is in aSize.
|
* in aSurface, and the image size is in aSize.
|
||||||
*/
|
*/
|
||||||
static void NotifyDrawImage(dom::Element* aImage,
|
static void NotifyDrawImage(dom::Element* aImage,
|
||||||
dom::HTMLCanvasElement* aCanvas,
|
dom::HTMLCanvasElement* aCanvas,
|
||||||
imgIRequest* aRequest,
|
|
||||||
SourceSurface* aSource,
|
SourceSurface* aSource,
|
||||||
const gfx::IntSize& aSize,
|
const gfx::IntSize& aSize,
|
||||||
bool aIsAccelerated);
|
bool aIsAccelerated);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether aImage has recently been drawn into aCanvas. If we return
|
* Check whether aImage has recently been drawn any canvas. If we return
|
||||||
* a non-null surface, then the image was recently drawn into the canvas
|
* a non-null surface, then the same image was recently drawn into a canvas.
|
||||||
* (with the same image request) and the returned surface contains the image
|
|
||||||
* data, and the image size will be returned in aSize.
|
|
||||||
*/
|
*/
|
||||||
static SourceSurface* Lookup(dom::Element* aImage,
|
static SourceSurface* LookupAllCanvas(dom::Element* aImage,
|
||||||
dom::HTMLCanvasElement* aCanvas,
|
bool aIsAccelerated);
|
||||||
gfx::IntSize* aSize,
|
|
||||||
bool aIsAccelerated);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the same as Lookup, except it works on any image recently drawn
|
* Like the top above, but restricts the lookup to only aCanvas. This is
|
||||||
* into any canvas. Security checks need to be done again if using the
|
* required for CORS security.
|
||||||
* results from this.
|
|
||||||
*/
|
*/
|
||||||
static SourceSurface* SimpleLookup(dom::Element* aImage,
|
static SourceSurface* LookupCanvas(dom::Element* aImage,
|
||||||
|
dom::HTMLCanvasElement* aCanvas,
|
||||||
|
gfx::IntSize* aSizeOut,
|
||||||
bool aIsAccelerated);
|
bool aIsAccelerated);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4361,57 +4361,6 @@ ExtractSubrect(SourceSurface* aSurface, gfx::Rect* aSourceRect, DrawTarget* aTar
|
|||||||
return subrectDT->Snapshot();
|
return subrectDT->Snapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acts like nsLayoutUtils::SurfaceFromElement, but it'll attempt
|
|
||||||
// to pull a SourceSurface from our cache. This allows us to avoid
|
|
||||||
// reoptimizing surfaces if content and canvas backends are different.
|
|
||||||
nsLayoutUtils::SurfaceFromElementResult
|
|
||||||
CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement)
|
|
||||||
{
|
|
||||||
nsLayoutUtils::SurfaceFromElementResult res;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
|
|
||||||
if (!imageLoader) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<imgIRequest> imgRequest;
|
|
||||||
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
|
||||||
getter_AddRefs(imgRequest));
|
|
||||||
if (!imgRequest) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t status;
|
|
||||||
if (NS_FAILED(imgRequest->GetImageStatus(&status)) ||
|
|
||||||
!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIPrincipal> principal;
|
|
||||||
if (NS_FAILED(imgRequest->GetImagePrincipal(getter_AddRefs(principal))) ||
|
|
||||||
!principal) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.mSourceSurface =
|
|
||||||
CanvasImageCache::SimpleLookup(aElement, mIsSkiaGL);
|
|
||||||
if (!res.mSourceSurface) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t corsmode = imgIRequest::CORS_NONE;
|
|
||||||
if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
|
|
||||||
res.mCORSUsed = corsmode != imgIRequest::CORS_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.mSize = res.mSourceSurface->GetSize();
|
|
||||||
res.mPrincipal = principal.forget();
|
|
||||||
res.mIsWriteOnly = false;
|
|
||||||
res.mImageRequest = imgRequest.forget();
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// image
|
// image
|
||||||
//
|
//
|
||||||
@ -4435,6 +4384,56 @@ ClipImageDimension(double& aSourceCoord, double& aSourceSize, int32_t aImageSize
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Acts like nsLayoutUtils::SurfaceFromElement, but it'll attempt
|
||||||
|
// to pull a SourceSurface from our cache. This allows us to avoid
|
||||||
|
// reoptimizing surfaces if content and canvas backends are different.
|
||||||
|
nsLayoutUtils::SurfaceFromElementResult
|
||||||
|
CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement)
|
||||||
|
{
|
||||||
|
nsLayoutUtils::SurfaceFromElementResult res;
|
||||||
|
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
|
||||||
|
if (!imageLoader) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<imgIRequest> imgRequest;
|
||||||
|
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
||||||
|
getter_AddRefs(imgRequest));
|
||||||
|
if (!imgRequest) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t status = 0;
|
||||||
|
if (NS_FAILED(imgRequest->GetImageStatus(&status)) ||
|
||||||
|
!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIPrincipal> principal;
|
||||||
|
if (NS_FAILED(imgRequest->GetImagePrincipal(getter_AddRefs(principal))) ||
|
||||||
|
!principal) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.mSourceSurface =
|
||||||
|
CanvasImageCache::LookupAllCanvas(aElement, mIsSkiaGL);
|
||||||
|
if (!res.mSourceSurface) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t corsmode = imgIRequest::CORS_NONE;
|
||||||
|
if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
|
||||||
|
res.mCORSUsed = corsmode != imgIRequest::CORS_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.mSize = res.mSourceSurface->GetSize();
|
||||||
|
res.mPrincipal = principal.forget();
|
||||||
|
res.mIsWriteOnly = false;
|
||||||
|
res.mImageRequest = imgRequest.forget();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
// drawImage(in HTMLImageElement image, in float dx, in float dy);
|
// drawImage(in HTMLImageElement image, in float dx, in float dy);
|
||||||
// -- render image from 0,0 at dx,dy top-left coords
|
// -- render image from 0,0 at dx,dy top-left coords
|
||||||
// drawImage(in HTMLImageElement image, in float dx, in float dy, in float dw, in float dh);
|
// drawImage(in HTMLImageElement image, in float dx, in float dy, in float dw, in float dh);
|
||||||
@ -4504,7 +4503,7 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||||||
}
|
}
|
||||||
|
|
||||||
srcSurf =
|
srcSurf =
|
||||||
CanvasImageCache::Lookup(element, mCanvasElement, &imgSize, mIsSkiaGL);
|
CanvasImageCache::LookupCanvas(element, mCanvasElement, &imgSize, mIsSkiaGL);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsLayoutUtils::DirectDrawInfo drawInfo;
|
nsLayoutUtils::DirectDrawInfo drawInfo;
|
||||||
@ -4613,15 +4612,13 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||||||
// of animated images. We also don't want to rasterize vector images.
|
// of animated images. We also don't want to rasterize vector images.
|
||||||
uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME |
|
uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME |
|
||||||
nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS;
|
nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS;
|
||||||
// The cache lookup can miss even if the image is already in the cache
|
|
||||||
// if the image is coming from a different element or cached for a
|
|
||||||
// different canvas. This covers the case when we miss due to caching
|
|
||||||
// for a different canvas, but CanvasImageCache should be fixed if we
|
|
||||||
// see misses due to different elements drawing the same image.
|
|
||||||
nsLayoutUtils::SurfaceFromElementResult res =
|
nsLayoutUtils::SurfaceFromElementResult res =
|
||||||
CachedSurfaceFromElement(element);
|
CanvasRenderingContext2D::CachedSurfaceFromElement(element);
|
||||||
if (!res.mSourceSurface)
|
|
||||||
|
if (!res.mSourceSurface) {
|
||||||
res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
|
res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
|
||||||
|
}
|
||||||
|
|
||||||
if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
|
if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
|
||||||
// The spec says to silently do nothing in the following cases:
|
// The spec says to silently do nothing in the following cases:
|
||||||
@ -4653,10 +4650,8 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||||||
|
|
||||||
if (res.mSourceSurface) {
|
if (res.mSourceSurface) {
|
||||||
if (res.mImageRequest) {
|
if (res.mImageRequest) {
|
||||||
CanvasImageCache::NotifyDrawImage(element, mCanvasElement, res.mImageRequest,
|
CanvasImageCache::NotifyDrawImage(element, mCanvasElement, res.mSourceSurface, imgSize, mIsSkiaGL);
|
||||||
res.mSourceSurface, imgSize, mIsSkiaGL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srcSurf = res.mSourceSurface;
|
srcSurf = res.mSourceSurface;
|
||||||
} else {
|
} else {
|
||||||
drawInfo = res.mDrawInfo;
|
drawInfo = res.mDrawInfo;
|
||||||
|
@ -7281,8 +7281,9 @@ nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement,
|
|||||||
nsCOMPtr<imgIRequest> imgRequest;
|
nsCOMPtr<imgIRequest> imgRequest;
|
||||||
rv = aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
rv = aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
||||||
getter_AddRefs(imgRequest));
|
getter_AddRefs(imgRequest));
|
||||||
if (NS_FAILED(rv) || !imgRequest)
|
if (NS_FAILED(rv) || !imgRequest) {
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t status;
|
uint32_t status;
|
||||||
imgRequest->GetImageStatus(&status);
|
imgRequest->GetImageStatus(&status);
|
||||||
@ -7297,13 +7298,15 @@ nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement,
|
|||||||
|
|
||||||
nsCOMPtr<nsIPrincipal> principal;
|
nsCOMPtr<nsIPrincipal> principal;
|
||||||
rv = imgRequest->GetImagePrincipal(getter_AddRefs(principal));
|
rv = imgRequest->GetImagePrincipal(getter_AddRefs(principal));
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv)) {
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<imgIContainer> imgContainer;
|
nsCOMPtr<imgIContainer> imgContainer;
|
||||||
rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
|
rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv)) {
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS;
|
uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS;
|
||||||
|
|
||||||
@ -7368,7 +7371,6 @@ nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement,
|
|||||||
// no images, including SVG images, can load content from another domain.
|
// no images, including SVG images, can load content from another domain.
|
||||||
result.mIsWriteOnly = false;
|
result.mIsWriteOnly = false;
|
||||||
result.mImageRequest = imgRequest.forget();
|
result.mImageRequest = imgRequest.forget();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user