content/canvas/src/CanvasImageCache.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/canvas/src/CanvasImageCache.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,207 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "CanvasImageCache.h"
    1.10 +#include "nsIImageLoadingContent.h"
    1.11 +#include "nsExpirationTracker.h"
    1.12 +#include "imgIRequest.h"
    1.13 +#include "mozilla/dom/Element.h"
    1.14 +#include "nsTHashtable.h"
    1.15 +#include "mozilla/dom/HTMLCanvasElement.h"
    1.16 +#include "nsContentUtils.h"
    1.17 +#include "mozilla/Preferences.h"
    1.18 +#include "mozilla/gfx/2D.h"
    1.19 +
    1.20 +namespace mozilla {
    1.21 +
    1.22 +using namespace dom;
    1.23 +using namespace gfx;
    1.24 +
    1.25 +struct ImageCacheKey {
    1.26 +  ImageCacheKey(Element* aImage, HTMLCanvasElement* aCanvas)
    1.27 +    : mImage(aImage), mCanvas(aCanvas) {}
    1.28 +  Element* mImage;
    1.29 +  HTMLCanvasElement* mCanvas;
    1.30 +};
    1.31 +
    1.32 +struct ImageCacheEntryData {
    1.33 +  ImageCacheEntryData(const ImageCacheEntryData& aOther)
    1.34 +    : mImage(aOther.mImage)
    1.35 +    , mILC(aOther.mILC)
    1.36 +    , mCanvas(aOther.mCanvas)
    1.37 +    , mRequest(aOther.mRequest)
    1.38 +    , mSourceSurface(aOther.mSourceSurface)
    1.39 +    , mSize(aOther.mSize)
    1.40 +  {}
    1.41 +  ImageCacheEntryData(const ImageCacheKey& aKey)
    1.42 +    : mImage(aKey.mImage)
    1.43 +    , mILC(nullptr)
    1.44 +    , mCanvas(aKey.mCanvas)
    1.45 +  {}
    1.46 +
    1.47 +  nsExpirationState* GetExpirationState() { return &mState; }
    1.48 +
    1.49 +  size_t SizeInBytes() { return mSize.width * mSize.height * 4; }
    1.50 +
    1.51 +  // Key
    1.52 +  nsRefPtr<Element> mImage;
    1.53 +  nsIImageLoadingContent* mILC;
    1.54 +  nsRefPtr<HTMLCanvasElement> mCanvas;
    1.55 +  // Value
    1.56 +  nsCOMPtr<imgIRequest> mRequest;
    1.57 +  RefPtr<SourceSurface> mSourceSurface;
    1.58 +  gfxIntSize mSize;
    1.59 +  nsExpirationState mState;
    1.60 +};
    1.61 +
    1.62 +class ImageCacheEntry : public PLDHashEntryHdr {
    1.63 +public:
    1.64 +  typedef ImageCacheKey KeyType;
    1.65 +  typedef const ImageCacheKey* KeyTypePointer;
    1.66 +
    1.67 +  ImageCacheEntry(const KeyType *key) :
    1.68 +      mData(new ImageCacheEntryData(*key)) {}
    1.69 +  ImageCacheEntry(const ImageCacheEntry &toCopy) :
    1.70 +      mData(new ImageCacheEntryData(*toCopy.mData)) {}
    1.71 +  ~ImageCacheEntry() {}
    1.72 +
    1.73 +  bool KeyEquals(KeyTypePointer key) const
    1.74 +  {
    1.75 +    return mData->mImage == key->mImage && mData->mCanvas == key->mCanvas;
    1.76 +  }
    1.77 +
    1.78 +  static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
    1.79 +  static PLDHashNumber HashKey(KeyTypePointer key)
    1.80 +  {
    1.81 +    return HashGeneric(key->mImage, key->mCanvas);
    1.82 +  }
    1.83 +  enum { ALLOW_MEMMOVE = true };
    1.84 +
    1.85 +  nsAutoPtr<ImageCacheEntryData> mData;
    1.86 +};
    1.87 +
    1.88 +static bool sPrefsInitialized = false;
    1.89 +static int32_t sCanvasImageCacheLimit = 0;
    1.90 +
    1.91 +class ImageCache MOZ_FINAL : public nsExpirationTracker<ImageCacheEntryData,4> {
    1.92 +public:
    1.93 +  // We use 3 generations of 1 second each to get a 2-3 seconds timeout.
    1.94 +  enum { GENERATION_MS = 1000 };
    1.95 +  ImageCache()
    1.96 +    : nsExpirationTracker<ImageCacheEntryData,4>(GENERATION_MS)
    1.97 +    , mTotal(0)
    1.98 +  {
    1.99 +    if (!sPrefsInitialized) {
   1.100 +      sPrefsInitialized = true;
   1.101 +      Preferences::AddIntVarCache(&sCanvasImageCacheLimit, "canvas.image.cache.limit", 0);
   1.102 +    }
   1.103 +  }
   1.104 +  ~ImageCache() {
   1.105 +    AgeAllGenerations();
   1.106 +  }
   1.107 +
   1.108 +  virtual void NotifyExpired(ImageCacheEntryData* aObject)
   1.109 +  {
   1.110 +    mTotal -= aObject->SizeInBytes();
   1.111 +    RemoveObject(aObject);
   1.112 +    // Deleting the entry will delete aObject since the entry owns aObject
   1.113 +    mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas));
   1.114 +  }
   1.115 +
   1.116 +  nsTHashtable<ImageCacheEntry> mCache;
   1.117 +  size_t mTotal;
   1.118 +};
   1.119 +
   1.120 +static ImageCache* gImageCache = nullptr;
   1.121 +
   1.122 +class CanvasImageCacheShutdownObserver MOZ_FINAL : public nsIObserver
   1.123 +{
   1.124 +public:
   1.125 +  NS_DECL_ISUPPORTS
   1.126 +  NS_DECL_NSIOBSERVER
   1.127 +};
   1.128 +
   1.129 +void
   1.130 +CanvasImageCache::NotifyDrawImage(Element* aImage,
   1.131 +                                  HTMLCanvasElement* aCanvas,
   1.132 +                                  imgIRequest* aRequest,
   1.133 +                                  SourceSurface* aSource,
   1.134 +                                  const gfxIntSize& aSize)
   1.135 +{
   1.136 +  if (!gImageCache) {
   1.137 +    gImageCache = new ImageCache();
   1.138 +    nsContentUtils::RegisterShutdownObserver(new CanvasImageCacheShutdownObserver());
   1.139 +  }
   1.140 +
   1.141 +  ImageCacheEntry* entry = gImageCache->mCache.PutEntry(ImageCacheKey(aImage, aCanvas));
   1.142 +  if (entry) {
   1.143 +    if (entry->mData->mSourceSurface) {
   1.144 +      // We are overwriting an existing entry.
   1.145 +      gImageCache->mTotal -= entry->mData->SizeInBytes();
   1.146 +      gImageCache->RemoveObject(entry->mData);
   1.147 +    }
   1.148 +    gImageCache->AddObject(entry->mData);
   1.149 +
   1.150 +    nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage);
   1.151 +    if (ilc) {
   1.152 +      ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   1.153 +                      getter_AddRefs(entry->mData->mRequest));
   1.154 +    }
   1.155 +    entry->mData->mILC = ilc;
   1.156 +    entry->mData->mSourceSurface = aSource;
   1.157 +    entry->mData->mSize = aSize;
   1.158 +
   1.159 +    gImageCache->mTotal += entry->mData->SizeInBytes();
   1.160 +  }
   1.161 +
   1.162 +  if (!sCanvasImageCacheLimit)
   1.163 +    return;
   1.164 +
   1.165 +  // Expire the image cache early if its larger than we want it to be.
   1.166 +  while (gImageCache->mTotal > size_t(sCanvasImageCacheLimit))
   1.167 +    gImageCache->AgeOneGeneration();
   1.168 +}
   1.169 +
   1.170 +SourceSurface*
   1.171 +CanvasImageCache::Lookup(Element* aImage,
   1.172 +                         HTMLCanvasElement* aCanvas,
   1.173 +                         gfxIntSize* aSize)
   1.174 +{
   1.175 +  if (!gImageCache)
   1.176 +    return nullptr;
   1.177 +
   1.178 +  ImageCacheEntry* entry = gImageCache->mCache.GetEntry(ImageCacheKey(aImage, aCanvas));
   1.179 +  if (!entry || !entry->mData->mILC)
   1.180 +    return nullptr;
   1.181 +
   1.182 +  nsCOMPtr<imgIRequest> request;
   1.183 +  entry->mData->mILC->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(request));
   1.184 +  if (request != entry->mData->mRequest)
   1.185 +    return nullptr;
   1.186 +
   1.187 +  gImageCache->MarkUsed(entry->mData);
   1.188 +
   1.189 +  *aSize = entry->mData->mSize;
   1.190 +  return entry->mData->mSourceSurface;
   1.191 +}
   1.192 +
   1.193 +NS_IMPL_ISUPPORTS(CanvasImageCacheShutdownObserver, nsIObserver)
   1.194 +
   1.195 +NS_IMETHODIMP
   1.196 +CanvasImageCacheShutdownObserver::Observe(nsISupports *aSubject,
   1.197 +                                          const char *aTopic,
   1.198 +                                          const char16_t *aData)
   1.199 +{
   1.200 +  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
   1.201 +    delete gImageCache;
   1.202 +    gImageCache = nullptr;
   1.203 +
   1.204 +    nsContentUtils::UnregisterShutdownObserver(this);
   1.205 +  }
   1.206 +
   1.207 +  return NS_OK;
   1.208 +}
   1.209 +
   1.210 +} // namespace mozilla

mercurial