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