| Index: src/core/SkImageFilter.cpp | 
| diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp | 
| index 13401af3789d08e51884aee9e8989e8c07a5e09d..a9473210dc5840ba8735174e5818b7d529865c89 100644 | 
| --- a/src/core/SkImageFilter.cpp | 
| +++ b/src/core/SkImageFilter.cpp | 
| @@ -18,6 +18,7 @@ | 
| #include "SkReadBuffer.h" | 
| #include "SkRect.h" | 
| #include "SkSpecialImage.h" | 
| +#include "SkSpecialSurface.h" | 
| #include "SkTDynamicHash.h" | 
| #include "SkTInternalLList.h" | 
| #include "SkValidationUtils.h" | 
| @@ -213,6 +214,28 @@ void SkImageFilter::flatten(SkWriteBuffer& buffer) const { | 
| buffer.writeUInt(fCropRect.flags()); | 
| } | 
|  | 
| +SkSpecialImage* SkImageFilter::filterImage(SkSpecialImage* src, const Context& context, | 
| +                                           SkIPoint* offset) const { | 
| +    SkASSERT(src && offset); | 
| + | 
| +    uint32_t srcGenID = fUsesSrcInput ? src->uniqueID() : 0; | 
| +    const SkIRect srcSubset = fUsesSrcInput ? src->subset() : SkIRect::MakeWH(0, 0); | 
| +    Cache::Key key(fUniqueID, context.ctm(), context.clipBounds(), srcGenID, srcSubset); | 
| +    if (context.cache()) { | 
| +        SkSpecialImage* result = context.cache()->get(key, offset); | 
| +        if (result) { | 
| +            return SkRef(result); | 
| +        } | 
| +    } | 
| + | 
| +    SkSpecialImage* result = this->onFilterImage(src, context, offset); | 
| +    if (result && context.cache()) { | 
| +        context.cache()->set(key, result, *offset); | 
| +    } | 
| + | 
| +    return result; | 
| +} | 
| + | 
| bool SkImageFilter::filterImageDeprecated(Proxy* proxy, const SkBitmap& src, | 
| const Context& context, | 
| SkBitmap* result, SkIPoint* offset) const { | 
| @@ -307,6 +330,22 @@ bool SkImageFilter::onFilterImageDeprecated(Proxy*, const SkBitmap&, const Conte | 
| return false; | 
| } | 
|  | 
| +SkSpecialImage* SkImageFilter::onFilterImage(SkSpecialImage* src, const Context& ctx, | 
| +                                             SkIPoint* offset) const { | 
| +    SkBitmap tmpSrc, result; | 
| + | 
| +    if (!src->internal_getBM(&tmpSrc)) { | 
| +        return nullptr; | 
| +    } | 
| + | 
| +    if (!this->filterImageDeprecated(src->internal_getProxy(), tmpSrc, | 
| +                                     this->mapContext(ctx), &result, offset)) { | 
| +        return nullptr; | 
| +    } | 
| + | 
| +    return SkSpecialImage::internal_fromBM(src->internal_getProxy(), result); | 
| +} | 
| + | 
| bool SkImageFilter::canFilterImageGPU() const { | 
| return this->asFragmentProcessor(nullptr, nullptr, SkMatrix::I(), SkIRect()); | 
| } | 
| @@ -419,6 +458,55 @@ bool SkImageFilter::applyCropRectDeprecated(const Context& ctx, Proxy* proxy, co | 
| } | 
| } | 
|  | 
| +// Return a larger (newWidth x newHeight) copy of 'src' with black padding | 
| +// around it. | 
| +static SkSpecialImage* pad_image(SkSpecialImage* src, | 
| +                                 int newWidth, int newHeight, int offX, int offY) { | 
| + | 
| +    SkImageInfo info = SkImageInfo::MakeN32Premul(newWidth, newHeight); | 
| +    SkAutoTUnref<SkSpecialSurface> surf(src->newSurface(info)); | 
| +    if (!surf) { | 
| +        return nullptr; | 
| +    } | 
| + | 
| +    SkCanvas* canvas = surf->getCanvas(); | 
| +    if (!canvas) { | 
| +        return nullptr; | 
| +    } | 
| + | 
| +    canvas->clear(0x0); | 
| + | 
| +    src->draw(canvas, offX, offY, nullptr); | 
| + | 
| +    return surf->newImageSnapshot(); | 
| +} | 
| + | 
| +SkSpecialImage* SkImageFilter::applyCropRect(const Context& ctx, | 
| +                                             SkSpecialImage* src, | 
| +                                             SkIPoint* srcOffset, | 
| +                                             SkIRect* bounds) const { | 
| +    SkIRect srcBounds; | 
| +    srcBounds = SkIRect::MakeXYWH(srcOffset->fX, srcOffset->fY, src->width(), src->height()); | 
| + | 
| +    SkIRect dstBounds; | 
| +    this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection); | 
| +    fCropRect.applyTo(dstBounds, ctx.ctm(), bounds); | 
| +    if (!bounds->intersect(ctx.clipBounds())) { | 
| +        return nullptr; | 
| +    } | 
| + | 
| +    if (srcBounds.contains(*bounds)) { | 
| +        return SkRef(src); | 
| +    } else { | 
| +        SkSpecialImage* img = pad_image(src, | 
| +                                        bounds->width(), bounds->height(), | 
| +                                        srcOffset->x() - bounds->x(), | 
| +                                        srcOffset->y() - bounds->y()); | 
| +        *srcOffset = SkIPoint::Make(bounds->x(), bounds->y()); | 
| +        return img; | 
| +    } | 
| +} | 
| + | 
| bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, | 
| SkIRect* dst, MapDirection direction) const { | 
| if (fInputCount < 1) { | 
| @@ -477,6 +565,18 @@ SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const { | 
| return SkLocalMatrixImageFilter::Create(matrix, const_cast<SkImageFilter*>(this)); | 
| } | 
|  | 
| +SkSpecialImage* SkImageFilter::filterInput(int index, | 
| +                                           SkSpecialImage* src, | 
| +                                           const Context& ctx, | 
| +                                           SkIPoint* offset) const { | 
| +    SkImageFilter* input = this->getInput(index); | 
| +    if (!input) { | 
| +        return SkRef(src); | 
| +    } | 
| + | 
| +    return input->filterImage(src, this->mapContext(ctx), offset); | 
| +} | 
| + | 
| #if SK_SUPPORT_GPU | 
|  | 
| bool SkImageFilter::filterInputGPUDeprecated(int index, SkImageFilter::Proxy* proxy, | 
|  |