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 
     10 #include "SkBitmap.h"
     11 #include "SkColorPriv.h"
     12 #include "SkColorSpaceXformer.h"
     13 #include "SkOpts.h"
     14 #include "SkReadBuffer.h"
     15 #include "SkRect.h"
     16 #include "SkSpecialImage.h"
     17 #include "SkWriteBuffer.h"
     18 
     19 #if SK_SUPPORT_GPU
     20 #include "GrContext.h"
     21 #include "GrFixedClip.h"
     22 #include "GrRenderTargetContext.h"
     23 #include "GrTexture.h"
     24 #include "GrTextureProxy.h"
     25 
     26 #include "SkGr.h"
     27 #include "effects/Gr1DKernelEffect.h"
     28 #include "effects/GrProxyMove.h"
     29 #include "glsl/GrGLSLFragmentProcessor.h"
     30 #include "glsl/GrGLSLFragmentShaderBuilder.h"
     31 #include "glsl/GrGLSLProgramDataManager.h"
     32 #include "glsl/GrGLSLUniformHandler.h"
     33 #include "../private/GrGLSL.h"
     34 #endif
     35 
     36 sk_sp<SkImageFilter> SkDilateImageFilter::Make(int radiusX, int radiusY,
     37                                                sk_sp<SkImageFilter> input,
     38                                                const CropRect* cropRect) {
     39     if (radiusX < 0 || radiusY < 0) {
     40         return nullptr;
     41     }
     42     return sk_sp<SkImageFilter>(new SkDilateImageFilter(radiusX, radiusY,
     43                                                         std::move(input),
     44                                                         cropRect));
     45 }
     46 
     47 
     48 sk_sp<SkImageFilter> SkErodeImageFilter::Make(int radiusX, int radiusY,
     49                                               sk_sp<SkImageFilter> input,
     50                                               const CropRect* cropRect) {
     51     if (radiusX < 0 || radiusY < 0) {
     52         return nullptr;
     53     }
     54     return sk_sp<SkImageFilter>(new SkErodeImageFilter(radiusX, radiusY,
     55                                                        std::move(input),
     56                                                        cropRect));
     57 }
     58 
     59 SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX,
     60                                                  int radiusY,
     61                                                  sk_sp<SkImageFilter> input,
     62                                                  const CropRect* cropRect)
     63     : INHERITED(&input, 1, cropRect)
     64     , fRadius(SkISize::Make(radiusX, radiusY)) {
     65 }
     66 
     67 void SkMorphologyImageFilter::flatten(SkWriteBuffer& buffer) const {
     68     this->INHERITED::flatten(buffer);
     69     buffer.writeInt(fRadius.fWidth);
     70     buffer.writeInt(fRadius.fHeight);
     71 }
     72 
     73 static void call_proc_X(SkMorphologyImageFilter::Proc procX,
     74                         const SkBitmap& src, SkBitmap* dst,
     75                         int radiusX, const SkIRect& bounds) {
     76     procX(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
     77           radiusX, bounds.width(), bounds.height(),
     78           src.rowBytesAsPixels(), dst->rowBytesAsPixels());
     79 }
     80 
     81 static void call_proc_Y(SkMorphologyImageFilter::Proc procY,
     82                         const SkPMColor* src, int srcRowBytesAsPixels, SkBitmap* dst,
     83                         int radiusY, const SkIRect& bounds) {
     84     procY(src, dst->getAddr32(0, 0),
     85           radiusY, bounds.height(), bounds.width(),
     86           srcRowBytesAsPixels, dst->rowBytesAsPixels());
     87 }
     88 
     89 SkRect SkMorphologyImageFilter::computeFastBounds(const SkRect& src) const {
     90     SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
     91     bounds.outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height()));
     92     return bounds;
     93 }
     94 
     95 SkIRect SkMorphologyImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
     96                                                     MapDirection) const {
     97     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
     98                                      SkIntToScalar(this->radius().height()));
     99     ctm.mapVectors(&radius, 1);
    100     return src.makeOutset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
    101 }
    102 
    103 sk_sp<SkFlattenable> SkErodeImageFilter::CreateProc(SkReadBuffer& buffer) {
    104     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
    105     const int width = buffer.readInt();
    106     const int height = buffer.readInt();
    107     return Make(width, height, common.getInput(0), &common.cropRect());
    108 }
    109 
    110 sk_sp<SkFlattenable> SkDilateImageFilter::CreateProc(SkReadBuffer& buffer) {
    111     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
    112     const int width = buffer.readInt();
    113     const int height = buffer.readInt();
    114     return Make(width, height, common.getInput(0), &common.cropRect());
    115 }
    116 
    117 #ifndef SK_IGNORE_TO_STRING
    118 void SkErodeImageFilter::toString(SkString* str) const {
    119     str->appendf("SkErodeImageFilter: (");
    120     str->appendf("radius: (%d,%d)", this->radius().fWidth, this->radius().fHeight);
    121     str->append(")");
    122 }
    123 #endif
    124 
    125 #ifndef SK_IGNORE_TO_STRING
    126 void SkDilateImageFilter::toString(SkString* str) const {
    127     str->appendf("SkDilateImageFilter: (");
    128     str->appendf("radius: (%d,%d)", this->radius().fWidth, this->radius().fHeight);
    129     str->append(")");
    130 }
    131 #endif
    132 
    133 #if SK_SUPPORT_GPU
    134 
    135 ///////////////////////////////////////////////////////////////////////////////
    136 /**
    137  * Morphology effects. Depending upon the type of morphology, either the
    138  * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
    139  * kernel is selected as the new color. The new color is modulated by the input
    140  * color.
    141  */
    142 class GrMorphologyEffect : public Gr1DKernelEffect {
    143 public:
    144     enum MorphologyType {
    145         kErode_MorphologyType,
    146         kDilate_MorphologyType,
    147     };
    148 
    149     static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
    150                                            Direction dir, int radius, MorphologyType type) {
    151         return sk_sp<GrFragmentProcessor>(new GrMorphologyEffect(std::move(proxy),
    152                                                                  dir, radius, type));
    153     }
    154 
    155     static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy, Direction dir, int radius,
    156                                            MorphologyType type, const float bounds[2]) {
    157         return sk_sp<GrFragmentProcessor>(new GrMorphologyEffect(std::move(proxy),
    158                                                                  dir, radius, type, bounds));
    159     }
    160 
    161     ~GrMorphologyEffect() override;
    162 
    163     MorphologyType type() const { return fType; }
    164     bool useRange() const { return fUseRange; }
    165     const float* range() const { return fRange; }
    166 
    167     const char* name() const override { return "Morphology"; }
    168 
    169 protected:
    170 
    171     MorphologyType fType;
    172     bool fUseRange;
    173     float fRange[2];
    174 
    175 private:
    176     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
    177 
    178     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
    179 
    180     bool onIsEqual(const GrFragmentProcessor&) const override;
    181 
    182     GrMorphologyEffect(sk_sp<GrTextureProxy>,
    183                        Direction, int radius, MorphologyType);
    184     GrMorphologyEffect(sk_sp<GrTextureProxy>,
    185                        Direction, int radius, MorphologyType, const float bounds[2]);
    186 
    187     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
    188 
    189     typedef Gr1DKernelEffect INHERITED;
    190 };
    191 
    192 ///////////////////////////////////////////////////////////////////////////////
    193 
    194 class GrGLMorphologyEffect : public GrGLSLFragmentProcessor {
    195 public:
    196     void emitCode(EmitArgs&) override;
    197 
    198     static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
    199 
    200 protected:
    201     void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
    202 
    203 private:
    204     GrGLSLProgramDataManager::UniformHandle fPixelSizeUni;
    205     GrGLSLProgramDataManager::UniformHandle fRangeUni;
    206 
    207     typedef GrGLSLFragmentProcessor INHERITED;
    208 };
    209 
    210 void GrGLMorphologyEffect::emitCode(EmitArgs& args) {
    211     const GrMorphologyEffect& me = args.fFp.cast<GrMorphologyEffect>();
    212 
    213     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
    214     fPixelSizeUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
    215                                                kFloat_GrSLType, kDefault_GrSLPrecision,
    216                                                "PixelSize");
    217     const char* pixelSizeInc = uniformHandler->getUniformCStr(fPixelSizeUni);
    218     fRangeUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
    219                                            kVec2f_GrSLType, kDefault_GrSLPrecision,
    220                                            "Range");
    221     const char* range = uniformHandler->getUniformCStr(fRangeUni);
    222 
    223     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
    224     SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
    225     const char* func;
    226     switch (me.type()) {
    227         case GrMorphologyEffect::kErode_MorphologyType:
    228             fragBuilder->codeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", args.fOutputColor);
    229             func = "min";
    230             break;
    231         case GrMorphologyEffect::kDilate_MorphologyType:
    232             fragBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", args.fOutputColor);
    233             func = "max";
    234             break;
    235         default:
    236             SkFAIL("Unexpected type");
    237             func = ""; // suppress warning
    238             break;
    239     }
    240 
    241     const char* dir;
    242     switch (me.direction()) {
    243         case Gr1DKernelEffect::kX_Direction:
    244             dir = "x";
    245             break;
    246         case Gr1DKernelEffect::kY_Direction:
    247             dir = "y";
    248             break;
    249         default:
    250             SkFAIL("Unknown filter direction.");
    251             dir = ""; // suppress warning
    252     }
    253 
    254     int width = GrMorphologyEffect::WidthFromRadius(me.radius());
    255 
    256     // vec2 coord = coord2D;
    257     fragBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
    258     // coord.x -= radius * pixelSize;
    259     fragBuilder->codeAppendf("\t\tcoord.%s -= %d.0 * %s; \n", dir, me.radius(), pixelSizeInc);
    260     if (me.useRange()) {
    261         // highBound = min(highBound, coord.x + (width-1) * pixelSize);
    262         fragBuilder->codeAppendf("\t\tfloat highBound = min(%s.y, coord.%s + %f * %s);",
    263                                  range, dir, float(width - 1), pixelSizeInc);
    264         // coord.x = max(lowBound, coord.x);
    265         fragBuilder->codeAppendf("\t\tcoord.%s = max(%s.x, coord.%s);", dir, range, dir);
    266     }
    267     fragBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", width);
    268     fragBuilder->codeAppendf("\t\t\t%s = %s(%s, ", args.fOutputColor, func, args.fOutputColor);
    269     fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coord");
    270     fragBuilder->codeAppend(");\n");
    271     // coord.x += pixelSize;
    272     fragBuilder->codeAppendf("\t\t\tcoord.%s += %s;\n", dir, pixelSizeInc);
    273     if (me.useRange()) {
    274         // coord.x = min(highBound, coord.x);
    275         fragBuilder->codeAppendf("\t\t\tcoord.%s = min(highBound, coord.%s);", dir, dir);
    276     }
    277     fragBuilder->codeAppend("\t\t}\n");
    278     fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
    279 }
    280 
    281 void GrGLMorphologyEffect::GenKey(const GrProcessor& proc,
    282                                   const GrShaderCaps&, GrProcessorKeyBuilder* b) {
    283     const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
    284     uint32_t key = static_cast<uint32_t>(m.radius());
    285     key |= (m.type() << 8);
    286     key |= (m.direction() << 9);
    287     if (m.useRange()) {
    288         key |= 1 << 10;
    289     }
    290     b->add32(key);
    291 }
    292 
    293 void GrGLMorphologyEffect::onSetData(const GrGLSLProgramDataManager& pdman,
    294                                      const GrFragmentProcessor& proc) {
    295     const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
    296     GrTexture& texture = *m.textureSampler(0).peekTexture();
    297 
    298     float pixelSize = 0.0f;
    299     switch (m.direction()) {
    300         case Gr1DKernelEffect::kX_Direction:
    301             pixelSize = 1.0f / texture.width();
    302             break;
    303         case Gr1DKernelEffect::kY_Direction:
    304             pixelSize = 1.0f / texture.height();
    305             break;
    306         default:
    307             SkFAIL("Unknown filter direction.");
    308     }
    309     pdman.set1f(fPixelSizeUni, pixelSize);
    310 
    311     if (m.useRange()) {
    312         const float* range = m.range();
    313         if (Gr1DKernelEffect::kY_Direction == m.direction() &&
    314             texture.origin() == kBottomLeft_GrSurfaceOrigin) {
    315             pdman.set2f(fRangeUni, 1.0f - (range[1]*pixelSize), 1.0f - (range[0]*pixelSize));
    316         } else {
    317             pdman.set2f(fRangeUni, range[0] * pixelSize, range[1] * pixelSize);
    318         }
    319     }
    320 }
    321 
    322 ///////////////////////////////////////////////////////////////////////////////
    323 
    324 GrMorphologyEffect::GrMorphologyEffect(sk_sp<GrTextureProxy> proxy,
    325                                        Direction direction,
    326                                        int radius,
    327                                        MorphologyType type)
    328         : INHERITED{ModulationFlags(proxy->config()), GR_PROXY_MOVE(proxy), direction, radius}
    329         , fType(type)
    330         , fUseRange(false) {
    331     this->initClassID<GrMorphologyEffect>();
    332 }
    333 
    334 GrMorphologyEffect::GrMorphologyEffect(sk_sp<GrTextureProxy> proxy,
    335                                        Direction direction,
    336                                        int radius,
    337                                        MorphologyType type,
    338                                        const float range[2])
    339         : INHERITED{ModulationFlags(proxy->config()), GR_PROXY_MOVE(proxy), direction, radius}
    340         , fType(type)
    341         , fUseRange(true) {
    342     this->initClassID<GrMorphologyEffect>();
    343     fRange[0] = range[0];
    344     fRange[1] = range[1];
    345 }
    346 
    347 GrMorphologyEffect::~GrMorphologyEffect() {
    348 }
    349 
    350 void GrMorphologyEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
    351                                                GrProcessorKeyBuilder* b) const {
    352     GrGLMorphologyEffect::GenKey(*this, caps, b);
    353 }
    354 
    355 GrGLSLFragmentProcessor* GrMorphologyEffect::onCreateGLSLInstance() const {
    356     return new GrGLMorphologyEffect;
    357 }
    358 bool GrMorphologyEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
    359     const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>();
    360     return (this->radius() == s.radius() &&
    361             this->direction() == s.direction() &&
    362             this->useRange() == s.useRange() &&
    363             this->type() == s.type());
    364 }
    365 
    366 ///////////////////////////////////////////////////////////////////////////////
    367 
    368 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMorphologyEffect);
    369 
    370 #if GR_TEST_UTILS
    371 sk_sp<GrFragmentProcessor> GrMorphologyEffect::TestCreate(GrProcessorTestData* d) {
    372     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
    373                                         : GrProcessorUnitTest::kAlphaTextureIdx;
    374     sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
    375 
    376     Direction dir = d->fRandom->nextBool() ? kX_Direction : kY_Direction;
    377     static const int kMaxRadius = 10;
    378     int radius = d->fRandom->nextRangeU(1, kMaxRadius);
    379     MorphologyType type = d->fRandom->nextBool() ? GrMorphologyEffect::kErode_MorphologyType
    380                                                  : GrMorphologyEffect::kDilate_MorphologyType;
    381 
    382     return GrMorphologyEffect::Make(std::move(proxy), dir, radius, type);
    383 }
    384 #endif
    385 
    386 
    387 static void apply_morphology_rect(GrRenderTargetContext* renderTargetContext,
    388                                   const GrClip& clip,
    389                                   sk_sp<GrTextureProxy> proxy,
    390                                   const SkIRect& srcRect,
    391                                   const SkIRect& dstRect,
    392                                   int radius,
    393                                   GrMorphologyEffect::MorphologyType morphType,
    394                                   const float bounds[2],
    395                                   Gr1DKernelEffect::Direction direction) {
    396     GrPaint paint;
    397     paint.setGammaCorrect(renderTargetContext->isGammaCorrect());
    398 
    399     paint.addColorFragmentProcessor(GrMorphologyEffect::Make(std::move(proxy),
    400                                                              direction, radius, morphType,
    401                                                              bounds));
    402     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    403     renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
    404                                         SkRect::Make(dstRect), SkRect::Make(srcRect));
    405 }
    406 
    407 static void apply_morphology_rect_no_bounds(GrRenderTargetContext* renderTargetContext,
    408                                             const GrClip& clip,
    409                                             sk_sp<GrTextureProxy> proxy,
    410                                             const SkIRect& srcRect,
    411                                             const SkIRect& dstRect,
    412                                             int radius,
    413                                             GrMorphologyEffect::MorphologyType morphType,
    414                                             Gr1DKernelEffect::Direction direction) {
    415     GrPaint paint;
    416     paint.setGammaCorrect(renderTargetContext->isGammaCorrect());
    417 
    418     paint.addColorFragmentProcessor(GrMorphologyEffect::Make(std::move(proxy),
    419                                                              direction, radius, morphType));
    420     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    421     renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
    422                                         SkRect::Make(dstRect), SkRect::Make(srcRect));
    423 }
    424 
    425 static void apply_morphology_pass(GrRenderTargetContext* renderTargetContext,
    426                                   const GrClip& clip,
    427                                   sk_sp<GrTextureProxy> textureProxy,
    428                                   const SkIRect& srcRect,
    429                                   const SkIRect& dstRect,
    430                                   int radius,
    431                                   GrMorphologyEffect::MorphologyType morphType,
    432                                   Gr1DKernelEffect::Direction direction) {
    433     float bounds[2] = { 0.0f, 1.0f };
    434     SkIRect lowerSrcRect = srcRect, lowerDstRect = dstRect;
    435     SkIRect middleSrcRect = srcRect, middleDstRect = dstRect;
    436     SkIRect upperSrcRect = srcRect, upperDstRect = dstRect;
    437     if (direction == Gr1DKernelEffect::kX_Direction) {
    438         bounds[0] = SkIntToScalar(srcRect.left()) + 0.5f;
    439         bounds[1] = SkIntToScalar(srcRect.right()) - 0.5f;
    440         lowerSrcRect.fRight = srcRect.left() + radius;
    441         lowerDstRect.fRight = dstRect.left() + radius;
    442         upperSrcRect.fLeft = srcRect.right() - radius;
    443         upperDstRect.fLeft = dstRect.right() - radius;
    444         middleSrcRect.inset(radius, 0);
    445         middleDstRect.inset(radius, 0);
    446     } else {
    447         bounds[0] = SkIntToScalar(srcRect.top()) + 0.5f;
    448         bounds[1] = SkIntToScalar(srcRect.bottom()) - 0.5f;
    449         lowerSrcRect.fBottom = srcRect.top() + radius;
    450         lowerDstRect.fBottom = dstRect.top() + radius;
    451         upperSrcRect.fTop = srcRect.bottom() - radius;
    452         upperDstRect.fTop = dstRect.bottom() - radius;
    453         middleSrcRect.inset(0, radius);
    454         middleDstRect.inset(0, radius);
    455     }
    456     if (middleSrcRect.fLeft - middleSrcRect.fRight >= 0) {
    457         // radius covers srcRect; use bounds over entire draw
    458         apply_morphology_rect(renderTargetContext, clip, std::move(textureProxy),
    459                               srcRect, dstRect, radius, morphType, bounds, direction);
    460     } else {
    461         // Draw upper and lower margins with bounds; middle without.
    462         apply_morphology_rect(renderTargetContext, clip, textureProxy,
    463                               lowerSrcRect, lowerDstRect, radius, morphType, bounds, direction);
    464         apply_morphology_rect(renderTargetContext, clip, textureProxy,
    465                               upperSrcRect, upperDstRect, radius, morphType, bounds, direction);
    466         apply_morphology_rect_no_bounds(renderTargetContext, clip, std::move(textureProxy),
    467                                         middleSrcRect, middleDstRect, radius, morphType, direction);
    468     }
    469 }
    470 
    471 static sk_sp<SkSpecialImage> apply_morphology(
    472                                           GrContext* context,
    473                                           SkSpecialImage* input,
    474                                           const SkIRect& rect,
    475                                           GrMorphologyEffect::MorphologyType morphType,
    476                                           SkISize radius,
    477                                           const SkImageFilter::OutputProperties& outputProperties) {
    478     sk_sp<GrTextureProxy> srcTexture(input->asTextureProxyRef(context));
    479     SkASSERT(srcTexture);
    480     sk_sp<SkColorSpace> colorSpace = sk_ref_sp(outputProperties.colorSpace());
    481     GrPixelConfig config = GrRenderableConfigForColorSpace(colorSpace.get());
    482 
    483     // setup new clip
    484     const GrFixedClip clip(SkIRect::MakeWH(srcTexture->width(), srcTexture->height()));
    485 
    486     const SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
    487     SkIRect srcRect = rect;
    488 
    489     SkASSERT(radius.width() > 0 || radius.height() > 0);
    490 
    491     if (radius.fWidth > 0) {
    492         sk_sp<GrRenderTargetContext> dstRTContext(context->makeDeferredRenderTargetContext(
    493             SkBackingFit::kApprox, rect.width(), rect.height(), config, colorSpace));
    494         if (!dstRTContext) {
    495             return nullptr;
    496         }
    497 
    498         apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture),
    499                               srcRect, dstRect, radius.fWidth, morphType,
    500                               Gr1DKernelEffect::kX_Direction);
    501         SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
    502                                               dstRect.width(), radius.fHeight);
    503         GrColor clearColor = GrMorphologyEffect::kErode_MorphologyType == morphType
    504                                 ? SK_ColorWHITE
    505                                 : SK_ColorTRANSPARENT;
    506         dstRTContext->clear(&clearRect, clearColor, false);
    507 
    508         srcTexture = dstRTContext->asTextureProxyRef();
    509         srcRect = dstRect;
    510     }
    511     if (radius.fHeight > 0) {
    512         sk_sp<GrRenderTargetContext> dstRTContext(context->makeDeferredRenderTargetContext(
    513             SkBackingFit::kApprox, rect.width(), rect.height(), config, colorSpace));
    514         if (!dstRTContext) {
    515             return nullptr;
    516         }
    517 
    518         apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture),
    519                               srcRect, dstRect, radius.fHeight, morphType,
    520                               Gr1DKernelEffect::kY_Direction);
    521 
    522         srcTexture = dstRTContext->asTextureProxyRef();
    523     }
    524 
    525     return SkSpecialImage::MakeDeferredFromGpu(context,
    526                                                SkIRect::MakeWH(rect.width(), rect.height()),
    527                                                kNeedNewImageUniqueID_SpecialImage,
    528                                                std::move(srcTexture), std::move(colorSpace),
    529                                                &input->props());
    530 }
    531 #endif
    532 
    533 sk_sp<SkSpecialImage> SkMorphologyImageFilter::onFilterImage(SkSpecialImage* source,
    534                                                              const Context& ctx,
    535                                                              SkIPoint* offset) const {
    536     SkIPoint inputOffset = SkIPoint::Make(0, 0);
    537     sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
    538     if (!input) {
    539         return nullptr;
    540     }
    541 
    542     SkIRect bounds;
    543     input = this->applyCropRect(this->mapContext(ctx), input.get(), &inputOffset, &bounds);
    544     if (!input) {
    545         return nullptr;
    546     }
    547 
    548     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
    549                                      SkIntToScalar(this->radius().height()));
    550     ctx.ctm().mapVectors(&radius, 1);
    551     int width = SkScalarFloorToInt(radius.fX);
    552     int height = SkScalarFloorToInt(radius.fY);
    553 
    554     if (width < 0 || height < 0) {
    555         return nullptr;
    556     }
    557 
    558     SkIRect srcBounds = bounds;
    559     srcBounds.offset(-inputOffset);
    560 
    561     if (0 == width && 0 == height) {
    562         offset->fX = bounds.left();
    563         offset->fY = bounds.top();
    564         return input->makeSubset(srcBounds);
    565     }
    566 
    567 #if SK_SUPPORT_GPU
    568     if (source->isTextureBacked()) {
    569         GrContext* context = source->getContext();
    570 
    571         // Ensure the input is in the destination color space. Typically applyCropRect will have
    572         // called pad_image to account for our dilation of bounds, so the result will already be
    573         // moved to the destination color space. If a filter DAG avoids that, then we use this
    574         // fall-back, which saves us from having to do the xform during the filter itself.
    575         input = ImageToColorSpace(input.get(), ctx.outputProperties());
    576 
    577         auto type = (kDilate_Op == this->op()) ? GrMorphologyEffect::kDilate_MorphologyType
    578                                                : GrMorphologyEffect::kErode_MorphologyType;
    579         sk_sp<SkSpecialImage> result(apply_morphology(context, input.get(), srcBounds, type,
    580                                                       SkISize::Make(width, height),
    581                                                       ctx.outputProperties()));
    582         if (result) {
    583             offset->fX = bounds.left();
    584             offset->fY = bounds.top();
    585         }
    586         return result;
    587     }
    588 #endif
    589 
    590     SkBitmap inputBM;
    591 
    592     if (!input->getROPixels(&inputBM)) {
    593         return nullptr;
    594     }
    595 
    596     if (inputBM.colorType() != kN32_SkColorType) {
    597         return nullptr;
    598     }
    599 
    600     SkImageInfo info = SkImageInfo::Make(bounds.width(), bounds.height(),
    601                                          inputBM.colorType(), inputBM.alphaType());
    602 
    603     SkBitmap dst;
    604     if (!dst.tryAllocPixels(info)) {
    605         return nullptr;
    606     }
    607 
    608     SkMorphologyImageFilter::Proc procX, procY;
    609 
    610     if (kDilate_Op == this->op()) {
    611         procX = SkOpts::dilate_x;
    612         procY = SkOpts::dilate_y;
    613     } else {
    614         procX = SkOpts::erode_x;
    615         procY = SkOpts::erode_y;
    616     }
    617 
    618     if (width > 0 && height > 0) {
    619         SkBitmap tmp;
    620         if (!tmp.tryAllocPixels(info)) {
    621             return nullptr;
    622         }
    623 
    624         call_proc_X(procX, inputBM, &tmp, width, srcBounds);
    625         SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
    626         call_proc_Y(procY,
    627                     tmp.getAddr32(tmpBounds.left(), tmpBounds.top()), tmp.rowBytesAsPixels(),
    628                     &dst, height, tmpBounds);
    629     } else if (width > 0) {
    630         call_proc_X(procX, inputBM, &dst, width, srcBounds);
    631     } else if (height > 0) {
    632         call_proc_Y(procY,
    633                     inputBM.getAddr32(srcBounds.left(), srcBounds.top()),
    634                     inputBM.rowBytesAsPixels(),
    635                     &dst, height, srcBounds);
    636     }
    637     offset->fX = bounds.left();
    638     offset->fY = bounds.top();
    639 
    640     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
    641                                           dst, &source->props());
    642 }
    643 
    644 sk_sp<SkImageFilter> SkMorphologyImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const{
    645     SkASSERT(1 == this->countInputs());
    646     auto input = xformer->apply(this->getInput(0));
    647     if (input.get() != this->getInput(0)) {
    648         return (SkMorphologyImageFilter::kDilate_Op == this->op())
    649                 ? SkDilateImageFilter::Make(fRadius.width(), fRadius.height(), std::move(input),
    650                                             this->getCropRectIfSet())
    651                 : SkErodeImageFilter::Make(fRadius.width(), fRadius.height(), std::move(input),
    652                                            this->getCropRectIfSet());
    653     }
    654     return this->refMe();
    655 }
    656