Home | History | Annotate | Download | only in effects
      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 "SkMorphologyImageFilter.h"
      9 #include "SkBitmap.h"
     10 #include "SkColorPriv.h"
     11 #include "SkReadBuffer.h"
     12 #include "SkWriteBuffer.h"
     13 #include "SkRect.h"
     14 #include "SkMorphology_opts.h"
     15 #if SK_SUPPORT_GPU
     16 #include "GrContext.h"
     17 #include "GrTexture.h"
     18 #include "GrTBackendProcessorFactory.h"
     19 #include "gl/GrGLProcessor.h"
     20 #include "gl/builders/GrGLProgramBuilder.h"
     21 #include "effects/Gr1DKernelEffect.h"
     22 #endif
     23 
     24 #ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
     25 SkMorphologyImageFilter::SkMorphologyImageFilter(SkReadBuffer& buffer)
     26   : INHERITED(1, buffer) {
     27     fRadius.fWidth = buffer.readInt();
     28     fRadius.fHeight = buffer.readInt();
     29     buffer.validate((fRadius.fWidth >= 0) &&
     30                     (fRadius.fHeight >= 0));
     31 }
     32 #endif
     33 
     34 SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX,
     35                                                  int radiusY,
     36                                                  SkImageFilter* input,
     37                                                  const CropRect* cropRect,
     38                                                  uint32_t uniqueID)
     39     : INHERITED(1, &input, cropRect, uniqueID), fRadius(SkISize::Make(radiusX, radiusY)) {
     40 }
     41 
     42 void SkMorphologyImageFilter::flatten(SkWriteBuffer& buffer) const {
     43     this->INHERITED::flatten(buffer);
     44     buffer.writeInt(fRadius.fWidth);
     45     buffer.writeInt(fRadius.fHeight);
     46 }
     47 
     48 enum MorphDirection {
     49     kX, kY
     50 };
     51 
     52 template<MorphDirection direction>
     53 static void erode(const SkPMColor* src, SkPMColor* dst,
     54                   int radius, int width, int height,
     55                   int srcStride, int dstStride)
     56 {
     57     const int srcStrideX = direction == kX ? 1 : srcStride;
     58     const int dstStrideX = direction == kX ? 1 : dstStride;
     59     const int srcStrideY = direction == kX ? srcStride : 1;
     60     const int dstStrideY = direction == kX ? dstStride : 1;
     61     radius = SkMin32(radius, width - 1);
     62     const SkPMColor* upperSrc = src + radius * srcStrideX;
     63     for (int x = 0; x < width; ++x) {
     64         const SkPMColor* lp = src;
     65         const SkPMColor* up = upperSrc;
     66         SkPMColor* dptr = dst;
     67         for (int y = 0; y < height; ++y) {
     68             int minB = 255, minG = 255, minR = 255, minA = 255;
     69             for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
     70                 int b = SkGetPackedB32(*p);
     71                 int g = SkGetPackedG32(*p);
     72                 int r = SkGetPackedR32(*p);
     73                 int a = SkGetPackedA32(*p);
     74                 if (b < minB) minB = b;
     75                 if (g < minG) minG = g;
     76                 if (r < minR) minR = r;
     77                 if (a < minA) minA = a;
     78             }
     79             *dptr = SkPackARGB32(minA, minR, minG, minB);
     80             dptr += dstStrideY;
     81             lp += srcStrideY;
     82             up += srcStrideY;
     83         }
     84         if (x >= radius) src += srcStrideX;
     85         if (x + radius < width - 1) upperSrc += srcStrideX;
     86         dst += dstStrideX;
     87     }
     88 }
     89 
     90 template<MorphDirection direction>
     91 static void dilate(const SkPMColor* src, SkPMColor* dst,
     92                    int radius, int width, int height,
     93                    int srcStride, int dstStride)
     94 {
     95     const int srcStrideX = direction == kX ? 1 : srcStride;
     96     const int dstStrideX = direction == kX ? 1 : dstStride;
     97     const int srcStrideY = direction == kX ? srcStride : 1;
     98     const int dstStrideY = direction == kX ? dstStride : 1;
     99     radius = SkMin32(radius, width - 1);
    100     const SkPMColor* upperSrc = src + radius * srcStrideX;
    101     for (int x = 0; x < width; ++x) {
    102         const SkPMColor* lp = src;
    103         const SkPMColor* up = upperSrc;
    104         SkPMColor* dptr = dst;
    105         for (int y = 0; y < height; ++y) {
    106             int maxB = 0, maxG = 0, maxR = 0, maxA = 0;
    107             for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
    108                 int b = SkGetPackedB32(*p);
    109                 int g = SkGetPackedG32(*p);
    110                 int r = SkGetPackedR32(*p);
    111                 int a = SkGetPackedA32(*p);
    112                 if (b > maxB) maxB = b;
    113                 if (g > maxG) maxG = g;
    114                 if (r > maxR) maxR = r;
    115                 if (a > maxA) maxA = a;
    116             }
    117             *dptr = SkPackARGB32(maxA, maxR, maxG, maxB);
    118             dptr += dstStrideY;
    119             lp += srcStrideY;
    120             up += srcStrideY;
    121         }
    122         if (x >= radius) src += srcStrideX;
    123         if (x + radius < width - 1) upperSrc += srcStrideX;
    124         dst += dstStrideX;
    125     }
    126 }
    127 
    128 static void callProcX(SkMorphologyImageFilter::Proc procX, const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds)
    129 {
    130     procX(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
    131           radiusX, bounds.width(), bounds.height(),
    132           src.rowBytesAsPixels(), dst->rowBytesAsPixels());
    133 }
    134 
    135 static void callProcY(SkMorphologyImageFilter::Proc procY, const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds)
    136 {
    137     procY(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
    138           radiusY, bounds.height(), bounds.width(),
    139           src.rowBytesAsPixels(), dst->rowBytesAsPixels());
    140 }
    141 
    142 bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc procX,
    143                                                  SkMorphologyImageFilter::Proc procY,
    144                                                  Proxy* proxy,
    145                                                  const SkBitmap& source,
    146                                                  const Context& ctx,
    147                                                  SkBitmap* dst,
    148                                                  SkIPoint* offset) const {
    149     SkBitmap src = source;
    150     SkIPoint srcOffset = SkIPoint::Make(0, 0);
    151     if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
    152         return false;
    153     }
    154 
    155     if (src.colorType() != kN32_SkColorType) {
    156         return false;
    157     }
    158 
    159     SkIRect bounds;
    160     if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
    161         return false;
    162     }
    163 
    164     SkAutoLockPixels alp(src);
    165     if (!src.getPixels()) {
    166         return false;
    167     }
    168 
    169     if (!dst->tryAllocPixels(src.info().makeWH(bounds.width(), bounds.height()))) {
    170         return false;
    171     }
    172 
    173     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
    174                                      SkIntToScalar(this->radius().height()));
    175     ctx.ctm().mapVectors(&radius, 1);
    176     int width = SkScalarFloorToInt(radius.fX);
    177     int height = SkScalarFloorToInt(radius.fY);
    178 
    179     if (width < 0 || height < 0) {
    180         return false;
    181     }
    182 
    183     SkIRect srcBounds = bounds;
    184     srcBounds.offset(-srcOffset);
    185 
    186     if (width == 0 && height == 0) {
    187         src.extractSubset(dst, srcBounds);
    188         offset->fX = bounds.left();
    189         offset->fY = bounds.top();
    190         return true;
    191     }
    192 
    193     SkBitmap temp;
    194     if (!temp.tryAllocPixels(dst->info())) {
    195         return false;
    196     }
    197 
    198     if (width > 0 && height > 0) {
    199         callProcX(procX, src, &temp, width, srcBounds);
    200         SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
    201         callProcY(procY, temp, dst, height, tmpBounds);
    202     } else if (width > 0) {
    203         callProcX(procX, src, dst, width, srcBounds);
    204     } else if (height > 0) {
    205         callProcY(procY, src, dst, height, srcBounds);
    206     }
    207     offset->fX = bounds.left();
    208     offset->fY = bounds.top();
    209     return true;
    210 }
    211 
    212 bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
    213                                        const SkBitmap& source, const Context& ctx,
    214                                        SkBitmap* dst, SkIPoint* offset) const {
    215     Proc erodeXProc = SkMorphologyGetPlatformProc(kErodeX_SkMorphologyProcType);
    216     if (!erodeXProc) {
    217         erodeXProc = erode<kX>;
    218     }
    219     Proc erodeYProc = SkMorphologyGetPlatformProc(kErodeY_SkMorphologyProcType);
    220     if (!erodeYProc) {
    221         erodeYProc = erode<kY>;
    222     }
    223     return this->filterImageGeneric(erodeXProc, erodeYProc, proxy, source, ctx, dst, offset);
    224 }
    225 
    226 bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
    227                                         const SkBitmap& source, const Context& ctx,
    228                                         SkBitmap* dst, SkIPoint* offset) const {
    229     Proc dilateXProc = SkMorphologyGetPlatformProc(kDilateX_SkMorphologyProcType);
    230     if (!dilateXProc) {
    231         dilateXProc = dilate<kX>;
    232     }
    233     Proc dilateYProc = SkMorphologyGetPlatformProc(kDilateY_SkMorphologyProcType);
    234     if (!dilateYProc) {
    235         dilateYProc = dilate<kY>;
    236     }
    237     return this->filterImageGeneric(dilateXProc, dilateYProc, proxy, source, ctx, dst, offset);
    238 }
    239 
    240 void SkMorphologyImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
    241     if (getInput(0)) {
    242         getInput(0)->computeFastBounds(src, dst);
    243     } else {
    244         *dst = src;
    245     }
    246     dst->outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height()));
    247 }
    248 
    249 bool SkMorphologyImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
    250                                              SkIRect* dst) const {
    251     SkIRect bounds = src;
    252     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
    253                                      SkIntToScalar(this->radius().height()));
    254     ctm.mapVectors(&radius, 1);
    255     bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
    256     if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
    257         return false;
    258     }
    259     *dst = bounds;
    260     return true;
    261 }
    262 
    263 SkFlattenable* SkErodeImageFilter::CreateProc(SkReadBuffer& buffer) {
    264     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
    265     const int width = buffer.readInt();
    266     const int height = buffer.readInt();
    267     return Create(width, height, common.getInput(0), &common.cropRect(), common.uniqueID());
    268 }
    269 
    270 SkFlattenable* SkDilateImageFilter::CreateProc(SkReadBuffer& buffer) {
    271     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
    272     const int width = buffer.readInt();
    273     const int height = buffer.readInt();
    274     return Create(width, height, common.getInput(0), &common.cropRect(), common.uniqueID());
    275 }
    276 
    277 #if SK_SUPPORT_GPU
    278 
    279 ///////////////////////////////////////////////////////////////////////////////
    280 
    281 class GrGLMorphologyEffect;
    282 
    283 /**
    284  * Morphology effects. Depending upon the type of morphology, either the
    285  * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
    286  * kernel is selected as the new color. The new color is modulated by the input
    287  * color.
    288  */
    289 class GrMorphologyEffect : public Gr1DKernelEffect {
    290 
    291 public:
    292 
    293     enum MorphologyType {
    294         kErode_MorphologyType,
    295         kDilate_MorphologyType,
    296     };
    297 
    298     static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius,
    299                                        MorphologyType type) {
    300         return SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type));
    301     }
    302 
    303     virtual ~GrMorphologyEffect();
    304 
    305     MorphologyType type() const { return fType; }
    306 
    307     static const char* Name() { return "Morphology"; }
    308 
    309     typedef GrGLMorphologyEffect GLProcessor;
    310 
    311     virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE;
    312     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
    313 
    314 protected:
    315 
    316     MorphologyType fType;
    317 
    318 private:
    319     virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE;
    320 
    321     GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
    322 
    323     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
    324 
    325     typedef Gr1DKernelEffect INHERITED;
    326 };
    327 
    328 ///////////////////////////////////////////////////////////////////////////////
    329 
    330 class GrGLMorphologyEffect : public GrGLFragmentProcessor {
    331 public:
    332     GrGLMorphologyEffect (const GrBackendProcessorFactory&, const GrProcessor&);
    333 
    334     virtual void emitCode(GrGLProgramBuilder*,
    335                           const GrFragmentProcessor&,
    336                           const GrProcessorKey&,
    337                           const char* outputColor,
    338                           const char* inputColor,
    339                           const TransformedCoordsArray&,
    340                           const TextureSamplerArray&) SK_OVERRIDE;
    341 
    342     static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b);
    343 
    344     virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
    345 
    346 private:
    347     int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
    348 
    349     int                                   fRadius;
    350     GrMorphologyEffect::MorphologyType    fType;
    351     GrGLProgramDataManager::UniformHandle fImageIncrementUni;
    352 
    353     typedef GrGLFragmentProcessor INHERITED;
    354 };
    355 
    356 GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendProcessorFactory& factory,
    357                                            const GrProcessor& proc)
    358     : INHERITED(factory) {
    359     const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
    360     fRadius = m.radius();
    361     fType = m.type();
    362 }
    363 
    364 void GrGLMorphologyEffect::emitCode(GrGLProgramBuilder* builder,
    365                                     const GrFragmentProcessor&,
    366                                     const GrProcessorKey& key,
    367                                     const char* outputColor,
    368                                     const char* inputColor,
    369                                     const TransformedCoordsArray& coords,
    370                                     const TextureSamplerArray& samplers) {
    371     fImageIncrementUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
    372                                              kVec2f_GrSLType, "ImageIncrement");
    373 
    374     GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
    375     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
    376     const char* func;
    377     switch (fType) {
    378         case GrMorphologyEffect::kErode_MorphologyType:
    379             fsBuilder->codeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor);
    380             func = "min";
    381             break;
    382         case GrMorphologyEffect::kDilate_MorphologyType:
    383             fsBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
    384             func = "max";
    385             break;
    386         default:
    387             SkFAIL("Unexpected type");
    388             func = ""; // suppress warning
    389             break;
    390     }
    391     const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
    392 
    393     fsBuilder->codeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc);
    394     fsBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
    395     fsBuilder->codeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
    396     fsBuilder->appendTextureLookup(samplers[0], "coord");
    397     fsBuilder->codeAppend(");\n");
    398     fsBuilder->codeAppendf("\t\t\tcoord += %s;\n", imgInc);
    399     fsBuilder->codeAppend("\t\t}\n");
    400     SkString modulate;
    401     GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
    402     fsBuilder->codeAppend(modulate.c_str());
    403 }
    404 
    405 void GrGLMorphologyEffect::GenKey(const GrProcessor& proc,
    406                                   const GrGLCaps&, GrProcessorKeyBuilder* b) {
    407     const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
    408     uint32_t key = static_cast<uint32_t>(m.radius());
    409     key |= (m.type() << 8);
    410     b->add32(key);
    411 }
    412 
    413 void GrGLMorphologyEffect::setData(const GrGLProgramDataManager& pdman,
    414                                    const GrProcessor& proc) {
    415     const Gr1DKernelEffect& kern = proc.cast<Gr1DKernelEffect>();
    416     GrTexture& texture = *kern.texture(0);
    417     // the code we generated was for a specific kernel radius
    418     SkASSERT(kern.radius() == fRadius);
    419     float imageIncrement[2] = { 0 };
    420     switch (kern.direction()) {
    421         case Gr1DKernelEffect::kX_Direction:
    422             imageIncrement[0] = 1.0f / texture.width();
    423             break;
    424         case Gr1DKernelEffect::kY_Direction:
    425             imageIncrement[1] = 1.0f / texture.height();
    426             break;
    427         default:
    428             SkFAIL("Unknown filter direction.");
    429     }
    430     pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
    431 }
    432 
    433 ///////////////////////////////////////////////////////////////////////////////
    434 
    435 GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
    436                                        Direction direction,
    437                                        int radius,
    438                                        MorphologyType type)
    439     : Gr1DKernelEffect(texture, direction, radius)
    440     , fType(type) {
    441 }
    442 
    443 GrMorphologyEffect::~GrMorphologyEffect() {
    444 }
    445 
    446 const GrBackendFragmentProcessorFactory& GrMorphologyEffect::getFactory() const {
    447     return GrTBackendFragmentProcessorFactory<GrMorphologyEffect>::getInstance();
    448 }
    449 
    450 bool GrMorphologyEffect::onIsEqual(const GrProcessor& sBase) const {
    451     const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>();
    452     return (this->texture(0) == s.texture(0) &&
    453             this->radius() == s.radius() &&
    454             this->direction() == s.direction() &&
    455             this->type() == s.type());
    456 }
    457 
    458 void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
    459     // This is valid because the color components of the result of the kernel all come
    460     // exactly from existing values in the source texture.
    461     this->updateConstantColorComponentsForModulation(color, validFlags);
    462 }
    463 
    464 ///////////////////////////////////////////////////////////////////////////////
    465 
    466 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMorphologyEffect);
    467 
    468 GrFragmentProcessor* GrMorphologyEffect::TestCreate(SkRandom* random,
    469                                                     GrContext*,
    470                                                     const GrDrawTargetCaps&,
    471                                                     GrTexture* textures[]) {
    472     int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
    473                                       GrProcessorUnitTest::kAlphaTextureIdx;
    474     Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
    475     static const int kMaxRadius = 10;
    476     int radius = random->nextRangeU(1, kMaxRadius);
    477     MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
    478                                                GrMorphologyEffect::kDilate_MorphologyType;
    479 
    480     return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type);
    481 }
    482 
    483 namespace {
    484 
    485 void apply_morphology_pass(GrContext* context,
    486                            GrTexture* texture,
    487                            const SkIRect& srcRect,
    488                            const SkIRect& dstRect,
    489                            int radius,
    490                            GrMorphologyEffect::MorphologyType morphType,
    491                            Gr1DKernelEffect::Direction direction) {
    492     GrPaint paint;
    493     paint.addColorProcessor(GrMorphologyEffect::Create(texture,
    494                                                        direction,
    495                                                        radius,
    496                                                        morphType))->unref();
    497     context->drawRectToRect(paint, SkRect::Make(dstRect), SkRect::Make(srcRect));
    498 }
    499 
    500 bool apply_morphology(const SkBitmap& input,
    501                       const SkIRect& rect,
    502                       GrMorphologyEffect::MorphologyType morphType,
    503                       SkISize radius,
    504                       SkBitmap* dst) {
    505     GrTexture* srcTexture = input.getTexture();
    506     SkASSERT(srcTexture);
    507     GrContext* context = srcTexture->getContext();
    508     srcTexture->ref();
    509     SkAutoTUnref<GrTexture> src(srcTexture);
    510 
    511     GrContext::AutoMatrix am;
    512     am.setIdentity(context);
    513 
    514     GrContext::AutoClip acs(context, SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
    515                                                     SkIntToScalar(srcTexture->height())));
    516 
    517     SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
    518     GrTextureDesc desc;
    519     desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
    520     desc.fWidth = rect.width();
    521     desc.fHeight = rect.height();
    522     desc.fConfig = kSkia8888_GrPixelConfig;
    523     SkIRect srcRect = rect;
    524 
    525     if (radius.fWidth > 0) {
    526         GrAutoScratchTexture ast(context, desc);
    527         if (NULL == ast.texture()) {
    528             return false;
    529         }
    530         GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
    531         apply_morphology_pass(context, src, srcRect, dstRect, radius.fWidth,
    532                               morphType, Gr1DKernelEffect::kX_Direction);
    533         SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
    534                                               dstRect.width(), radius.fHeight);
    535         context->clear(&clearRect, GrMorphologyEffect::kErode_MorphologyType == morphType ?
    536                                    SK_ColorWHITE :
    537                                    SK_ColorTRANSPARENT, false);
    538         src.reset(ast.detach());
    539         srcRect = dstRect;
    540     }
    541     if (radius.fHeight > 0) {
    542         GrAutoScratchTexture ast(context, desc);
    543         if (NULL == ast.texture()) {
    544             return false;
    545         }
    546         GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
    547         apply_morphology_pass(context, src, srcRect, dstRect, radius.fHeight,
    548                               morphType, Gr1DKernelEffect::kY_Direction);
    549         src.reset(ast.detach());
    550     }
    551     SkImageFilter::WrapTexture(src, rect.width(), rect.height(), dst);
    552     return true;
    553 }
    554 
    555 };
    556 
    557 bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate,
    558                                                     Proxy* proxy,
    559                                                     const SkBitmap& src,
    560                                                     const Context& ctx,
    561                                                     SkBitmap* result,
    562                                                     SkIPoint* offset) const {
    563     SkBitmap input = src;
    564     SkIPoint srcOffset = SkIPoint::Make(0, 0);
    565     if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
    566         return false;
    567     }
    568     SkIRect bounds;
    569     if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
    570         return false;
    571     }
    572     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
    573                                      SkIntToScalar(this->radius().height()));
    574     ctx.ctm().mapVectors(&radius, 1);
    575     int width = SkScalarFloorToInt(radius.fX);
    576     int height = SkScalarFloorToInt(radius.fY);
    577 
    578     if (width < 0 || height < 0) {
    579         return false;
    580     }
    581 
    582     SkIRect srcBounds = bounds;
    583     srcBounds.offset(-srcOffset);
    584     if (width == 0 && height == 0) {
    585         input.extractSubset(result, srcBounds);
    586         offset->fX = bounds.left();
    587         offset->fY = bounds.top();
    588         return true;
    589     }
    590 
    591     GrMorphologyEffect::MorphologyType type = dilate ? GrMorphologyEffect::kDilate_MorphologyType : GrMorphologyEffect::kErode_MorphologyType;
    592     if (!apply_morphology(input, srcBounds, type,
    593                           SkISize::Make(width, height), result)) {
    594         return false;
    595     }
    596     offset->fX = bounds.left();
    597     offset->fY = bounds.top();
    598     return true;
    599 }
    600 
    601 bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
    602                                          SkBitmap* result, SkIPoint* offset) const {
    603     return this->filterImageGPUGeneric(true, proxy, src, ctx, result, offset);
    604 }
    605 
    606 bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
    607                                         SkBitmap* result, SkIPoint* offset) const {
    608     return this->filterImageGPUGeneric(false, proxy, src, ctx, result, offset);
    609 }
    610 
    611 #endif
    612