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