Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2012 The Android Open Source Project
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkImageFilter.h"
      9 #include "SkImageFilterCacheKey.h"
     10 
     11 #include "SkBitmap.h"
     12 #include "SkBitmapDevice.h"
     13 #include "SkChecksum.h"
     14 #include "SkDevice.h"
     15 #include "SkLocalMatrixImageFilter.h"
     16 #include "SkMatrixImageFilter.h"
     17 #include "SkOncePtr.h"
     18 #include "SkReadBuffer.h"
     19 #include "SkRect.h"
     20 #include "SkSpecialImage.h"
     21 #include "SkTDynamicHash.h"
     22 #include "SkTInternalLList.h"
     23 #include "SkValidationUtils.h"
     24 #include "SkWriteBuffer.h"
     25 #if SK_SUPPORT_GPU
     26 #include "GrContext.h"
     27 #include "GrDrawContext.h"
     28 #include "SkGrPixelRef.h"
     29 #include "SkGr.h"
     30 #endif
     31 
     32 #ifdef SK_BUILD_FOR_IOS
     33   enum { kDefaultCacheSize = 2 * 1024 * 1024 };
     34 #else
     35   enum { kDefaultCacheSize = 128 * 1024 * 1024 };
     36 #endif
     37 
     38 #ifndef SK_IGNORE_TO_STRING
     39 void SkImageFilter::CropRect::toString(SkString* str) const {
     40     if (!fFlags) {
     41         return;
     42     }
     43 
     44     str->appendf("cropRect (");
     45     if (fFlags & CropRect::kHasLeft_CropEdge) {
     46         str->appendf("%.2f, ", fRect.fLeft);
     47     } else {
     48         str->appendf("X, ");
     49     }
     50     if (fFlags & CropRect::kHasTop_CropEdge) {
     51         str->appendf("%.2f, ", fRect.fTop);
     52     } else {
     53         str->appendf("X, ");
     54     }
     55     if (fFlags & CropRect::kHasWidth_CropEdge) {
     56         str->appendf("%.2f, ", fRect.width());
     57     } else {
     58         str->appendf("X, ");
     59     }
     60     if (fFlags & CropRect::kHasHeight_CropEdge) {
     61         str->appendf("%.2f", fRect.height());
     62     } else {
     63         str->appendf("X");
     64     }
     65     str->appendf(") ");
     66 }
     67 #endif
     68 
     69 void SkImageFilter::CropRect::applyTo(const SkIRect& imageBounds,
     70                                       const SkMatrix& ctm,
     71                                       SkIRect* cropped) const {
     72     *cropped = imageBounds;
     73     if (fFlags) {
     74         SkRect devCropR;
     75         ctm.mapRect(&devCropR, fRect);
     76         const SkIRect devICropR = devCropR.roundOut();
     77 
     78         // Compute the left/top first, in case we have to read them to compute right/bottom
     79         if (fFlags & kHasLeft_CropEdge) {
     80             cropped->fLeft = devICropR.fLeft;
     81         }
     82         if (fFlags & kHasTop_CropEdge) {
     83             cropped->fTop = devICropR.fTop;
     84         }
     85         if (fFlags & kHasWidth_CropEdge) {
     86             cropped->fRight = cropped->fLeft + devICropR.width();
     87         }
     88         if (fFlags & kHasHeight_CropEdge) {
     89             cropped->fBottom = cropped->fTop + devICropR.height();
     90         }
     91     }
     92 }
     93 
     94 ///////////////////////////////////////////////////////////////////////////////////////////////////
     95 
     96 static int32_t next_image_filter_unique_id() {
     97     static int32_t gImageFilterUniqueID;
     98 
     99     // Never return 0.
    100     int32_t id;
    101     do {
    102         id = sk_atomic_inc(&gImageFilterUniqueID) + 1;
    103     } while (0 == id);
    104     return id;
    105 }
    106 
    107 SkImageFilter::Common::~Common() {
    108     for (int i = 0; i < fInputs.count(); ++i) {
    109         SkSafeUnref(fInputs[i]);
    110     }
    111 }
    112 
    113 void SkImageFilter::Common::allocInputs(int count) {
    114     const size_t size = count * sizeof(SkImageFilter*);
    115     fInputs.reset(count);
    116     sk_bzero(fInputs.get(), size);
    117 }
    118 
    119 void SkImageFilter::Common::detachInputs(SkImageFilter** inputs) {
    120     const size_t size = fInputs.count() * sizeof(SkImageFilter*);
    121     memcpy(inputs, fInputs.get(), size);
    122     sk_bzero(fInputs.get(), size);
    123 }
    124 
    125 bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) {
    126     const int count = buffer.readInt();
    127     if (!buffer.validate(count >= 0)) {
    128         return false;
    129     }
    130     if (!buffer.validate(expectedCount < 0 || count == expectedCount)) {
    131         return false;
    132     }
    133 
    134     this->allocInputs(count);
    135     for (int i = 0; i < count; i++) {
    136         if (buffer.readBool()) {
    137             fInputs[i] = buffer.readImageFilter();
    138         }
    139         if (!buffer.isValid()) {
    140             return false;
    141         }
    142     }
    143     SkRect rect;
    144     buffer.readRect(&rect);
    145     if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) {
    146         return false;
    147     }
    148 
    149     uint32_t flags = buffer.readUInt();
    150     fCropRect = CropRect(rect, flags);
    151     if (buffer.isVersionLT(SkReadBuffer::kImageFilterNoUniqueID_Version)) {
    152 
    153         (void) buffer.readUInt();
    154     }
    155     return buffer.isValid();
    156 }
    157 
    158 ///////////////////////////////////////////////////////////////////////////////////////////////////
    159 
    160 SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect)
    161   : fInputCount(inputCount),
    162     fInputs(new SkImageFilter*[inputCount]),
    163     fUsesSrcInput(false),
    164     fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)),
    165     fUniqueID(next_image_filter_unique_id()) {
    166     for (int i = 0; i < inputCount; ++i) {
    167         if (nullptr == inputs[i] || inputs[i]->usesSrcInput()) {
    168             fUsesSrcInput = true;
    169         }
    170         fInputs[i] = inputs[i];
    171         SkSafeRef(fInputs[i]);
    172     }
    173 }
    174 
    175 SkImageFilter::~SkImageFilter() {
    176     for (int i = 0; i < fInputCount; i++) {
    177         SkSafeUnref(fInputs[i]);
    178     }
    179     delete[] fInputs;
    180     Cache::Get()->purgeByKeys(fCacheKeys.begin(), fCacheKeys.count());
    181 }
    182 
    183 SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer)
    184   : fUsesSrcInput(false)
    185   , fUniqueID(next_image_filter_unique_id()) {
    186     Common common;
    187     if (common.unflatten(buffer, inputCount)) {
    188         fCropRect = common.cropRect();
    189         fInputCount = common.inputCount();
    190         fInputs = new SkImageFilter* [fInputCount];
    191         common.detachInputs(fInputs);
    192         for (int i = 0; i < fInputCount; ++i) {
    193             if (nullptr == fInputs[i] || fInputs[i]->usesSrcInput()) {
    194                 fUsesSrcInput = true;
    195             }
    196         }
    197     } else {
    198         fInputCount = 0;
    199         fInputs = nullptr;
    200     }
    201 }
    202 
    203 void SkImageFilter::flatten(SkWriteBuffer& buffer) const {
    204     buffer.writeInt(fInputCount);
    205     for (int i = 0; i < fInputCount; i++) {
    206         SkImageFilter* input = this->getInput(i);
    207         buffer.writeBool(input != nullptr);
    208         if (input != nullptr) {
    209             buffer.writeFlattenable(input);
    210         }
    211     }
    212     buffer.writeRect(fCropRect.rect());
    213     buffer.writeUInt(fCropRect.flags());
    214 }
    215 
    216 bool SkImageFilter::filterImageDeprecated(Proxy* proxy, const SkBitmap& src,
    217                                           const Context& context,
    218                                           SkBitmap* result, SkIPoint* offset) const {
    219     SkASSERT(result);
    220     SkASSERT(offset);
    221     uint32_t srcGenID = fUsesSrcInput ? src.getGenerationID() : 0;
    222     Cache::Key key(fUniqueID, context.ctm(), context.clipBounds(),
    223                    srcGenID, SkIRect::MakeWH(0, 0));
    224     if (context.cache()) {
    225         if (context.cache()->get(key, result, offset)) {
    226             return true;
    227         }
    228     }
    229     /*
    230      *  Give the proxy first shot at the filter. If it returns false, ask
    231      *  the filter to do it.
    232      */
    233     if ((proxy && proxy->filterImage(this, src, context, result, offset)) ||
    234         this->onFilterImageDeprecated(proxy, src, context, result, offset)) {
    235         if (context.cache()) {
    236             context.cache()->set(key, *result, *offset);
    237             SkAutoMutexAcquire mutex(fMutex);
    238             fCacheKeys.push_back(key);
    239         }
    240         return true;
    241     }
    242     return false;
    243 }
    244 
    245 bool SkImageFilter::filterInputDeprecated(int index, Proxy* proxy, const SkBitmap& src,
    246                                           const Context& ctx,
    247                                           SkBitmap* result, SkIPoint* offset) const {
    248     SkImageFilter* input = this->getInput(index);
    249     if (!input) {
    250         return true;
    251     }
    252     return input->filterImageDeprecated(proxy, src, this->mapContext(ctx), result, offset);
    253 }
    254 
    255 bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst,
    256                                  MapDirection direction) const {
    257     SkASSERT(dst);
    258     SkIRect bounds;
    259     if (kReverse_MapDirection == direction) {
    260         this->onFilterNodeBounds(src, ctm, &bounds, direction);
    261         return this->onFilterBounds(bounds, ctm, dst, direction);
    262     } else {
    263         SkIRect temp;
    264         if (!this->onFilterBounds(src, ctm, &bounds, direction)) {
    265             return false;
    266         }
    267         this->onFilterNodeBounds(bounds, ctm, &temp, direction);
    268         this->getCropRect().applyTo(temp, ctm, dst);
    269         return true;
    270     }
    271 }
    272 
    273 void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
    274     if (0 == fInputCount) {
    275         *dst = src;
    276         return;
    277     }
    278     if (this->getInput(0)) {
    279         this->getInput(0)->computeFastBounds(src, dst);
    280     } else {
    281         *dst = src;
    282     }
    283     for (int i = 1; i < fInputCount; i++) {
    284         SkImageFilter* input = this->getInput(i);
    285         if (input) {
    286             SkRect bounds;
    287             input->computeFastBounds(src, &bounds);
    288             dst->join(bounds);
    289         } else {
    290             dst->join(src);
    291         }
    292     }
    293 }
    294 
    295 bool SkImageFilter::canComputeFastBounds() const {
    296     for (int i = 0; i < fInputCount; i++) {
    297         SkImageFilter* input = this->getInput(i);
    298         if (input && !input->canComputeFastBounds()) {
    299             return false;
    300         }
    301     }
    302     return true;
    303 }
    304 
    305 bool SkImageFilter::onFilterImageDeprecated(Proxy*, const SkBitmap&, const Context&,
    306                                             SkBitmap*, SkIPoint*) const {
    307     return false;
    308 }
    309 
    310 bool SkImageFilter::canFilterImageGPU() const {
    311     return this->asFragmentProcessor(nullptr, nullptr, SkMatrix::I(), SkIRect());
    312 }
    313 
    314 bool SkImageFilter::filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx,
    315                                              SkBitmap* result, SkIPoint* offset) const {
    316 #if SK_SUPPORT_GPU
    317     SkBitmap input = src;
    318     SkASSERT(fInputCount == 1);
    319     SkIPoint srcOffset = SkIPoint::Make(0, 0);
    320     if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &input, &srcOffset)) {
    321         return false;
    322     }
    323     GrTexture* srcTexture = input.getTexture();
    324     SkIRect bounds;
    325     if (!this->applyCropRectDeprecated(ctx, proxy, input, &srcOffset, &bounds, &input)) {
    326         return false;
    327     }
    328     GrContext* context = srcTexture->getContext();
    329 
    330     GrSurfaceDesc desc;
    331     desc.fFlags = kRenderTarget_GrSurfaceFlag,
    332     desc.fWidth = bounds.width();
    333     desc.fHeight = bounds.height();
    334     desc.fConfig = kRGBA_8888_GrPixelConfig;
    335 
    336     SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc));
    337     if (!dst) {
    338         return false;
    339     }
    340 
    341     GrFragmentProcessor* fp;
    342     offset->fX = bounds.left();
    343     offset->fY = bounds.top();
    344     bounds.offset(-srcOffset);
    345     SkMatrix matrix(ctx.ctm());
    346     matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
    347     GrPaint paint;
    348     if (this->asFragmentProcessor(&fp, srcTexture, matrix, bounds)) {
    349         SkASSERT(fp);
    350         paint.addColorFragmentProcessor(fp)->unref();
    351         paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
    352 
    353         SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget()));
    354         if (drawContext) {
    355             SkRect srcRect = SkRect::Make(bounds);
    356             SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
    357             GrClip clip(dstRect);
    358             drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect);
    359 
    360             GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result);
    361             return true;
    362         }
    363     }
    364 #endif
    365     return false;
    366 }
    367 
    368 bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const {
    369     SkASSERT(nullptr != filterPtr);
    370     if (!this->isColorFilterNode(filterPtr)) {
    371         return false;
    372     }
    373     if (nullptr != this->getInput(0) || (*filterPtr)->affectsTransparentBlack()) {
    374         (*filterPtr)->unref();
    375         return false;
    376     }
    377     return true;
    378 }
    379 
    380 bool SkImageFilter::applyCropRect(const Context& ctx, const SkIRect& srcBounds,
    381                                   SkIRect* dstBounds) const {
    382     this->onFilterNodeBounds(srcBounds, ctx.ctm(), dstBounds, kForward_MapDirection);
    383     fCropRect.applyTo(*dstBounds, ctx.ctm(), dstBounds);
    384     // Intersect against the clip bounds, in case the crop rect has
    385     // grown the bounds beyond the original clip. This can happen for
    386     // example in tiling, where the clip is much smaller than the filtered
    387     // primitive. If we didn't do this, we would be processing the filter
    388     // at the full crop rect size in every tile.
    389     return dstBounds->intersect(ctx.clipBounds());
    390 }
    391 
    392 bool SkImageFilter::applyCropRectDeprecated(const Context& ctx, Proxy* proxy, const SkBitmap& src,
    393                                             SkIPoint* srcOffset, SkIRect* bounds,
    394                                             SkBitmap* dst) const {
    395     SkIRect srcBounds;
    396     src.getBounds(&srcBounds);
    397     srcBounds.offset(*srcOffset);
    398     SkIRect dstBounds;
    399     this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection);
    400     fCropRect.applyTo(dstBounds, ctx.ctm(), bounds);
    401     if (!bounds->intersect(ctx.clipBounds())) {
    402         return false;
    403     }
    404 
    405     if (srcBounds.contains(*bounds)) {
    406         *dst = src;
    407         return true;
    408     } else {
    409         SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height()));
    410         if (!device) {
    411             return false;
    412         }
    413         SkCanvas canvas(device);
    414         canvas.clear(0x00000000);
    415         canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y());
    416         *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
    417         *dst = device->accessBitmap(false);
    418         return true;
    419     }
    420 }
    421 
    422 bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
    423                                    SkIRect* dst, MapDirection direction) const {
    424     if (fInputCount < 1) {
    425         *dst = src;
    426         return true;
    427     }
    428 
    429     SkIRect totalBounds;
    430     for (int i = 0; i < fInputCount; ++i) {
    431         SkImageFilter* filter = this->getInput(i);
    432         SkIRect rect = src;
    433         if (filter && !filter->filterBounds(src, ctm, &rect, direction)) {
    434             return false;
    435         }
    436         if (0 == i) {
    437             totalBounds = rect;
    438         } else {
    439             totalBounds.join(rect);
    440         }
    441     }
    442 
    443     // don't modify dst until now, so we don't accidentally change it in the
    444     // loop, but then return false on the next filter.
    445     *dst = totalBounds;
    446     return true;
    447 }
    448 
    449 void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
    450                                        SkIRect* dst, MapDirection) const {
    451     *dst = src;
    452 }
    453 
    454 
    455 SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const {
    456     SkIRect clipBounds;
    457     this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds,
    458                              MapDirection::kReverse_MapDirection);
    459     return Context(ctx.ctm(), clipBounds, ctx.cache());
    460 }
    461 
    462 bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*,
    463                                         const SkMatrix&, const SkIRect&) const {
    464     return false;
    465 }
    466 
    467 SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix,
    468                                                  SkFilterQuality filterQuality,
    469                                                  SkImageFilter* input) {
    470     return SkMatrixImageFilter::Create(matrix, filterQuality, input);
    471 }
    472 
    473 SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const {
    474     // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter
    475     // is *always* treated as a const ptr. Hence the const-cast here.
    476     //
    477     return SkLocalMatrixImageFilter::Create(matrix, const_cast<SkImageFilter*>(this));
    478 }
    479 
    480 #if SK_SUPPORT_GPU
    481 
    482 bool SkImageFilter::filterInputGPUDeprecated(int index, SkImageFilter::Proxy* proxy,
    483                                              const SkBitmap& src, const Context& ctx,
    484                                              SkBitmap* result, SkIPoint* offset) const {
    485     SkImageFilter* input = this->getInput(index);
    486     if (!input) {
    487         return true;
    488     }
    489     // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity
    490     // matrix with no clip and that the matrix, clip, and render target set before this function was
    491     // called are restored before we return to the caller.
    492     GrContext* context = src.getTexture()->getContext();
    493     if (input->filterImageDeprecated(proxy, src, this->mapContext(ctx), result, offset)) {
    494         if (!result->getTexture()) {
    495             const SkImageInfo info = result->info();
    496             if (kUnknown_SkColorType == info.colorType()) {
    497                 return false;
    498             }
    499             SkAutoTUnref<GrTexture> resultTex(
    500                 GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter()));
    501             if (!resultTex) {
    502                 return false;
    503             }
    504             result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref();
    505         }
    506         return true;
    507     } else {
    508         return false;
    509     }
    510 }
    511 #endif
    512 
    513 namespace {
    514 
    515 class CacheImpl : public SkImageFilter::Cache {
    516 public:
    517     CacheImpl(size_t maxBytes) : fMaxBytes(maxBytes), fCurrentBytes(0) { }
    518     ~CacheImpl() override {
    519         SkTDynamicHash<Value, Key>::Iter iter(&fLookup);
    520 
    521         while (!iter.done()) {
    522             Value* v = &*iter;
    523             ++iter;
    524             delete v;
    525         }
    526     }
    527     struct Value {
    528         Value(const Key& key, const SkBitmap& bitmap, const SkIPoint& offset)
    529             : fKey(key), fBitmap(bitmap), fOffset(offset) {}
    530         Value(const Key& key, SkSpecialImage* image, const SkIPoint& offset)
    531             : fKey(key), fImage(SkRef(image)), fOffset(offset) {}
    532 
    533         Key fKey;
    534         SkBitmap fBitmap;
    535         SkAutoTUnref<SkSpecialImage> fImage;
    536         SkIPoint fOffset;
    537         static const Key& GetKey(const Value& v) {
    538             return v.fKey;
    539         }
    540         static uint32_t Hash(const Key& key) {
    541             return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
    542         }
    543         SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value);
    544     };
    545 
    546     bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const override {
    547         SkAutoMutexAcquire mutex(fMutex);
    548         if (Value* v = fLookup.find(key)) {
    549             *result = v->fBitmap;
    550             *offset = v->fOffset;
    551             if (v != fLRU.head()) {
    552                 fLRU.remove(v);
    553                 fLRU.addToHead(v);
    554             }
    555             return true;
    556         }
    557         return false;
    558     }
    559 
    560     SkSpecialImage* get(const Key& key, SkIPoint* offset) const override {
    561         SkAutoMutexAcquire mutex(fMutex);
    562         if (Value* v = fLookup.find(key)) {
    563             *offset = v->fOffset;
    564             if (v != fLRU.head()) {
    565                 fLRU.remove(v);
    566                 fLRU.addToHead(v);
    567             }
    568             return v->fImage;
    569         }
    570         return nullptr;
    571     }
    572 
    573     void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) override {
    574         SkAutoMutexAcquire mutex(fMutex);
    575         if (Value* v = fLookup.find(key)) {
    576             this->removeInternal(v);
    577         }
    578         Value* v = new Value(key, result, offset);
    579         fLookup.add(v);
    580         fLRU.addToHead(v);
    581         fCurrentBytes += result.getSize();
    582         while (fCurrentBytes > fMaxBytes) {
    583             Value* tail = fLRU.tail();
    584             SkASSERT(tail);
    585             if (tail == v) {
    586                 break;
    587             }
    588             this->removeInternal(tail);
    589         }
    590     }
    591 
    592     void set(const Key& key, SkSpecialImage* image, const SkIPoint& offset) override {
    593         SkAutoMutexAcquire mutex(fMutex);
    594         if (Value* v = fLookup.find(key)) {
    595             this->removeInternal(v);
    596         }
    597         Value* v = new Value(key, image, offset);
    598         fLookup.add(v);
    599         fLRU.addToHead(v);
    600         fCurrentBytes += image->getSize();
    601         while (fCurrentBytes > fMaxBytes) {
    602             Value* tail = fLRU.tail();
    603             SkASSERT(tail);
    604             if (tail == v) {
    605                 break;
    606             }
    607             this->removeInternal(tail);
    608         }
    609     }
    610 
    611     void purge() override {
    612         SkAutoMutexAcquire mutex(fMutex);
    613         while (fCurrentBytes > 0) {
    614             Value* tail = fLRU.tail();
    615             SkASSERT(tail);
    616             this->removeInternal(tail);
    617         }
    618     }
    619 
    620     void purgeByKeys(const Key keys[], int count) override {
    621         SkAutoMutexAcquire mutex(fMutex);
    622         for (int i = 0; i < count; i++) {
    623             if (Value* v = fLookup.find(keys[i])) {
    624                 this->removeInternal(v);
    625             }
    626         }
    627     }
    628 
    629 private:
    630     void removeInternal(Value* v) {
    631         if (v->fImage) {
    632             fCurrentBytes -= v->fImage->getSize();
    633         } else {
    634             fCurrentBytes -= v->fBitmap.getSize();
    635         }
    636         fLRU.remove(v);
    637         fLookup.remove(v->fKey);
    638         delete v;
    639     }
    640 private:
    641     SkTDynamicHash<Value, Key>            fLookup;
    642     mutable SkTInternalLList<Value>       fLRU;
    643     size_t                                fMaxBytes;
    644     size_t                                fCurrentBytes;
    645     mutable SkMutex                       fMutex;
    646 };
    647 
    648 } // namespace
    649 
    650 SkImageFilter::Cache* SkImageFilter::Cache::Create(size_t maxBytes) {
    651     return new CacheImpl(maxBytes);
    652 }
    653 
    654 SK_DECLARE_STATIC_ONCE_PTR(SkImageFilter::Cache, cache);
    655 SkImageFilter::Cache* SkImageFilter::Cache::Get() {
    656     return cache.get([]{ return SkImageFilter::Cache::Create(kDefaultCacheSize); });
    657 }
    658 
    659 void SkImageFilter::PurgeCache() {
    660     Cache::Get()->purge();
    661 }
    662 
    663 ///////////////////////////////////////////////////////////////////////////////////////////////////
    664 
    665 SkBaseDevice* SkImageFilter::DeviceProxy::createDevice(int w, int h, TileUsage usage) {
    666     SkBaseDevice::CreateInfo cinfo(SkImageInfo::MakeN32Premul(w, h),
    667                                    kPossible_TileUsage == usage ? SkBaseDevice::kPossible_TileUsage
    668                                                                 : SkBaseDevice::kNever_TileUsage,
    669                                    kUnknown_SkPixelGeometry,
    670                                    false,   /* preserveLCDText */
    671                                    true /*forImageFilter*/);
    672     SkBaseDevice* dev = fDevice->onCreateDevice(cinfo, nullptr);
    673     if (nullptr == dev) {
    674         const SkSurfaceProps surfaceProps(fDevice->fSurfaceProps.flags(),
    675                                           kUnknown_SkPixelGeometry);
    676         dev = SkBitmapDevice::Create(cinfo.fInfo, surfaceProps);
    677     }
    678     return dev;
    679 }
    680 
    681 bool SkImageFilter::DeviceProxy::filterImage(const SkImageFilter* filter, const SkBitmap& src,
    682                                        const SkImageFilter::Context& ctx,
    683                                        SkBitmap* result, SkIPoint* offset) {
    684     return fDevice->filterImage(filter, src, ctx, result, offset);
    685 }
    686 
    687