1 /*
2  * Copyright 2012 The Android Open Source Project
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkMorphologyImageFilter.h"
9 #include "SkBitmap.h"
10 #include "SkColorPriv.h"
11 #include "SkReadBuffer.h"
12 #include "SkWriteBuffer.h"
13 #include "SkRect.h"
14 #include "SkMorphology_opts.h"
15 #if SK_SUPPORT_GPU
16 #include "GrContext.h"
17 #include "GrInvariantOutput.h"
18 #include "GrTexture.h"
19 #include "effects/Gr1DKernelEffect.h"
20 #include "gl/GrGLProcessor.h"
21 #include "gl/builders/GrGLProgramBuilder.h"
22 #endif
23 
SkMorphologyImageFilter(int radiusX,int radiusY,SkImageFilter * input,const CropRect * cropRect)24 SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX,
25                                                  int radiusY,
26                                                  SkImageFilter* input,
27                                                  const CropRect* cropRect)
28     : INHERITED(1, &input, cropRect), fRadius(SkISize::Make(radiusX, radiusY)) {
29 }
30 
flatten(SkWriteBuffer & buffer) const31 void SkMorphologyImageFilter::flatten(SkWriteBuffer& buffer) const {
32     this->INHERITED::flatten(buffer);
33     buffer.writeInt(fRadius.fWidth);
34     buffer.writeInt(fRadius.fHeight);
35 }
36 
37 enum MorphDirection {
38     kX, kY
39 };
40 
41 template<MorphDirection direction>
erode(const SkPMColor * src,SkPMColor * dst,int radius,int width,int height,int srcStride,int dstStride)42 static void erode(const SkPMColor* src, SkPMColor* dst,
43                   int radius, int width, int height,
44                   int srcStride, int dstStride)
45 {
46     const int srcStrideX = direction == kX ? 1 : srcStride;
47     const int dstStrideX = direction == kX ? 1 : dstStride;
48     const int srcStrideY = direction == kX ? srcStride : 1;
49     const int dstStrideY = direction == kX ? dstStride : 1;
50     radius = SkMin32(radius, width - 1);
51     const SkPMColor* upperSrc = src + radius * srcStrideX;
52     for (int x = 0; x < width; ++x) {
53         const SkPMColor* lp = src;
54         const SkPMColor* up = upperSrc;
55         SkPMColor* dptr = dst;
56         for (int y = 0; y < height; ++y) {
57             int minB = 255, minG = 255, minR = 255, minA = 255;
58             for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
59                 int b = SkGetPackedB32(*p);
60                 int g = SkGetPackedG32(*p);
61                 int r = SkGetPackedR32(*p);
62                 int a = SkGetPackedA32(*p);
63                 if (b < minB) minB = b;
64                 if (g < minG) minG = g;
65                 if (r < minR) minR = r;
66                 if (a < minA) minA = a;
67             }
68             *dptr = SkPackARGB32(minA, minR, minG, minB);
69             dptr += dstStrideY;
70             lp += srcStrideY;
71             up += srcStrideY;
72         }
73         if (x >= radius) src += srcStrideX;
74         if (x + radius < width - 1) upperSrc += srcStrideX;
75         dst += dstStrideX;
76     }
77 }
78 
79 template<MorphDirection direction>
dilate(const SkPMColor * src,SkPMColor * dst,int radius,int width,int height,int srcStride,int dstStride)80 static void dilate(const SkPMColor* src, SkPMColor* dst,
81                    int radius, int width, int height,
82                    int srcStride, int dstStride)
83 {
84     const int srcStrideX = direction == kX ? 1 : srcStride;
85     const int dstStrideX = direction == kX ? 1 : dstStride;
86     const int srcStrideY = direction == kX ? srcStride : 1;
87     const int dstStrideY = direction == kX ? dstStride : 1;
88     radius = SkMin32(radius, width - 1);
89     const SkPMColor* upperSrc = src + radius * srcStrideX;
90     for (int x = 0; x < width; ++x) {
91         const SkPMColor* lp = src;
92         const SkPMColor* up = upperSrc;
93         SkPMColor* dptr = dst;
94         for (int y = 0; y < height; ++y) {
95             int maxB = 0, maxG = 0, maxR = 0, maxA = 0;
96             for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
97                 int b = SkGetPackedB32(*p);
98                 int g = SkGetPackedG32(*p);
99                 int r = SkGetPackedR32(*p);
100                 int a = SkGetPackedA32(*p);
101                 if (b > maxB) maxB = b;
102                 if (g > maxG) maxG = g;
103                 if (r > maxR) maxR = r;
104                 if (a > maxA) maxA = a;
105             }
106             *dptr = SkPackARGB32(maxA, maxR, maxG, maxB);
107             dptr += dstStrideY;
108             lp += srcStrideY;
109             up += srcStrideY;
110         }
111         if (x >= radius) src += srcStrideX;
112         if (x + radius < width - 1) upperSrc += srcStrideX;
113         dst += dstStrideX;
114     }
115 }
116 
callProcX(SkMorphologyImageFilter::Proc procX,const SkBitmap & src,SkBitmap * dst,int radiusX,const SkIRect & bounds)117 static void callProcX(SkMorphologyImageFilter::Proc procX, const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds)
118 {
119     procX(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
120           radiusX, bounds.width(), bounds.height(),
121           src.rowBytesAsPixels(), dst->rowBytesAsPixels());
122 }
123 
callProcY(SkMorphologyImageFilter::Proc procY,const SkBitmap & src,SkBitmap * dst,int radiusY,const SkIRect & bounds)124 static void callProcY(SkMorphologyImageFilter::Proc procY, const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds)
125 {
126     procY(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
127           radiusY, bounds.height(), bounds.width(),
128           src.rowBytesAsPixels(), dst->rowBytesAsPixels());
129 }
130 
filterImageGeneric(SkMorphologyImageFilter::Proc procX,SkMorphologyImageFilter::Proc procY,Proxy * proxy,const SkBitmap & source,const Context & ctx,SkBitmap * dst,SkIPoint * offset) const131 bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc procX,
132                                                  SkMorphologyImageFilter::Proc procY,
133                                                  Proxy* proxy,
134                                                  const SkBitmap& source,
135                                                  const Context& ctx,
136                                                  SkBitmap* dst,
137                                                  SkIPoint* offset) const {
138     SkBitmap src = source;
139     SkIPoint srcOffset = SkIPoint::Make(0, 0);
140     if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
141         return false;
142     }
143 
144     if (src.colorType() != kN32_SkColorType) {
145         return false;
146     }
147 
148     SkIRect bounds;
149     if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
150         return false;
151     }
152 
153     SkAutoLockPixels alp(src);
154     if (!src.getPixels()) {
155         return false;
156     }
157 
158     if (!dst->tryAllocPixels(src.info().makeWH(bounds.width(), bounds.height()))) {
159         return false;
160     }
161 
162     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
163                                      SkIntToScalar(this->radius().height()));
164     ctx.ctm().mapVectors(&radius, 1);
165     int width = SkScalarFloorToInt(radius.fX);
166     int height = SkScalarFloorToInt(radius.fY);
167 
168     if (width < 0 || height < 0) {
169         return false;
170     }
171 
172     SkIRect srcBounds = bounds;
173     srcBounds.offset(-srcOffset);
174 
175     if (width == 0 && height == 0) {
176         src.extractSubset(dst, srcBounds);
177         offset->fX = bounds.left();
178         offset->fY = bounds.top();
179         return true;
180     }
181 
182     SkBitmap temp;
183     if (!temp.tryAllocPixels(dst->info())) {
184         return false;
185     }
186 
187     if (width > 0 && height > 0) {
188         callProcX(procX, src, &temp, width, srcBounds);
189         SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
190         callProcY(procY, temp, dst, height, tmpBounds);
191     } else if (width > 0) {
192         callProcX(procX, src, dst, width, srcBounds);
193     } else if (height > 0) {
194         callProcY(procY, src, dst, height, srcBounds);
195     }
196     offset->fX = bounds.left();
197     offset->fY = bounds.top();
198     return true;
199 }
200 
onFilterImage(Proxy * proxy,const SkBitmap & source,const Context & ctx,SkBitmap * dst,SkIPoint * offset) const201 bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
202                                        const SkBitmap& source, const Context& ctx,
203                                        SkBitmap* dst, SkIPoint* offset) const {
204     Proc erodeXProc = SkMorphologyGetPlatformProc(kErodeX_SkMorphologyProcType);
205     if (!erodeXProc) {
206         erodeXProc = erode<kX>;
207     }
208     Proc erodeYProc = SkMorphologyGetPlatformProc(kErodeY_SkMorphologyProcType);
209     if (!erodeYProc) {
210         erodeYProc = erode<kY>;
211     }
212     return this->filterImageGeneric(erodeXProc, erodeYProc, proxy, source, ctx, dst, offset);
213 }
214 
onFilterImage(Proxy * proxy,const SkBitmap & source,const Context & ctx,SkBitmap * dst,SkIPoint * offset) const215 bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
216                                         const SkBitmap& source, const Context& ctx,
217                                         SkBitmap* dst, SkIPoint* offset) const {
218     Proc dilateXProc = SkMorphologyGetPlatformProc(kDilateX_SkMorphologyProcType);
219     if (!dilateXProc) {
220         dilateXProc = dilate<kX>;
221     }
222     Proc dilateYProc = SkMorphologyGetPlatformProc(kDilateY_SkMorphologyProcType);
223     if (!dilateYProc) {
224         dilateYProc = dilate<kY>;
225     }
226     return this->filterImageGeneric(dilateXProc, dilateYProc, proxy, source, ctx, dst, offset);
227 }
228 
computeFastBounds(const SkRect & src,SkRect * dst) const229 void SkMorphologyImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
230     if (getInput(0)) {
231         getInput(0)->computeFastBounds(src, dst);
232     } else {
233         *dst = src;
234     }
235     dst->outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height()));
236 }
237 
onFilterBounds(const SkIRect & src,const SkMatrix & ctm,SkIRect * dst) const238 bool SkMorphologyImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
239                                              SkIRect* dst) const {
240     SkIRect bounds = src;
241     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
242                                      SkIntToScalar(this->radius().height()));
243     ctm.mapVectors(&radius, 1);
244     bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
245     if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
246         return false;
247     }
248     *dst = bounds;
249     return true;
250 }
251 
CreateProc(SkReadBuffer & buffer)252 SkFlattenable* SkErodeImageFilter::CreateProc(SkReadBuffer& buffer) {
253     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
254     const int width = buffer.readInt();
255     const int height = buffer.readInt();
256     return Create(width, height, common.getInput(0), &common.cropRect());
257 }
258 
CreateProc(SkReadBuffer & buffer)259 SkFlattenable* SkDilateImageFilter::CreateProc(SkReadBuffer& buffer) {
260     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
261     const int width = buffer.readInt();
262     const int height = buffer.readInt();
263     return Create(width, height, common.getInput(0), &common.cropRect());
264 }
265 
266 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const267 void SkErodeImageFilter::toString(SkString* str) const {
268     str->appendf("SkErodeImageFilter: (");
269     str->appendf("radius: (%d,%d)", this->radius().fWidth, this->radius().fHeight);
270     str->append(")");
271 }
272 #endif
273 
274 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const275 void SkDilateImageFilter::toString(SkString* str) const {
276     str->appendf("SkDilateImageFilter: (");
277     str->appendf("radius: (%d,%d)", this->radius().fWidth, this->radius().fHeight);
278     str->append(")");
279 }
280 #endif
281 
282 #if SK_SUPPORT_GPU
283 
284 ///////////////////////////////////////////////////////////////////////////////
285 /**
286  * Morphology effects. Depending upon the type of morphology, either the
287  * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
288  * kernel is selected as the new color. The new color is modulated by the input
289  * color.
290  */
291 class GrMorphologyEffect : public Gr1DKernelEffect {
292 
293 public:
294 
295     enum MorphologyType {
296         kErode_MorphologyType,
297         kDilate_MorphologyType,
298     };
299 
Create(GrTexture * tex,Direction dir,int radius,MorphologyType type)300     static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius,
301                                        MorphologyType type) {
302         return SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type));
303     }
304 
Create(GrTexture * tex,Direction dir,int radius,MorphologyType type,float bounds[2])305     static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius,
306                                        MorphologyType type, float bounds[2]) {
307         return SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type, bounds));
308     }
309 
310     virtual ~GrMorphologyEffect();
311 
type() const312     MorphologyType type() const { return fType; }
useRange() const313     bool useRange() const { return fUseRange; }
range() const314     const float* range() const { return fRange; }
315 
name() const316     const char* name() const override { return "Morphology"; }
317 
318     void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
319 
320     GrGLFragmentProcessor* createGLInstance() const override;
321 
322 protected:
323 
324     MorphologyType fType;
325     bool fUseRange;
326     float fRange[2];
327 
328 private:
329     bool onIsEqual(const GrFragmentProcessor&) const override;
330 
331     void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
332 
333     GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
334     GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType, float bounds[2]);
335 
336     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
337 
338     typedef Gr1DKernelEffect INHERITED;
339 };
340 
341 ///////////////////////////////////////////////////////////////////////////////
342 
343 class GrGLMorphologyEffect : public GrGLFragmentProcessor {
344 public:
345     GrGLMorphologyEffect(const GrProcessor&);
346 
347     virtual void emitCode(GrGLFPBuilder*,
348                           const GrFragmentProcessor&,
349                           const char* outputColor,
350                           const char* inputColor,
351                           const TransformedCoordsArray&,
352                           const TextureSamplerArray&) override;
353 
354     static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b);
355 
356     void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
357 
358 private:
width() const359     int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
360 
361     int                                   fRadius;
362     Gr1DKernelEffect::Direction           fDirection;
363     bool                                  fUseRange;
364     GrMorphologyEffect::MorphologyType    fType;
365     GrGLProgramDataManager::UniformHandle fPixelSizeUni;
366     GrGLProgramDataManager::UniformHandle fRangeUni;
367 
368     typedef GrGLFragmentProcessor INHERITED;
369 };
370 
GrGLMorphologyEffect(const GrProcessor & proc)371 GrGLMorphologyEffect::GrGLMorphologyEffect(const GrProcessor& proc) {
372     const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
373     fRadius = m.radius();
374     fDirection = m.direction();
375     fUseRange = m.useRange();
376     fType = m.type();
377 }
378 
emitCode(GrGLFPBuilder * builder,const GrFragmentProcessor &,const char * outputColor,const char * inputColor,const TransformedCoordsArray & coords,const TextureSamplerArray & samplers)379 void GrGLMorphologyEffect::emitCode(GrGLFPBuilder* builder,
380                                     const GrFragmentProcessor&,
381                                     const char* outputColor,
382                                     const char* inputColor,
383                                     const TransformedCoordsArray& coords,
384                                     const TextureSamplerArray& samplers) {
385     fPixelSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
386                                             kFloat_GrSLType, kDefault_GrSLPrecision,
387                                             "PixelSize");
388     const char* pixelSizeInc = builder->getUniformCStr(fPixelSizeUni);
389     fRangeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
390                                             kVec2f_GrSLType, kDefault_GrSLPrecision,
391                                             "Range");
392     const char* range = builder->getUniformCStr(fRangeUni);
393 
394     GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
395     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
396     const char* func;
397     switch (fType) {
398         case GrMorphologyEffect::kErode_MorphologyType:
399             fsBuilder->codeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor);
400             func = "min";
401             break;
402         case GrMorphologyEffect::kDilate_MorphologyType:
403             fsBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
404             func = "max";
405             break;
406         default:
407             SkFAIL("Unexpected type");
408             func = ""; // suppress warning
409             break;
410     }
411 
412     const char* dir;
413     switch (fDirection) {
414         case Gr1DKernelEffect::kX_Direction:
415             dir = "x";
416             break;
417         case Gr1DKernelEffect::kY_Direction:
418             dir = "y";
419             break;
420         default:
421             SkFAIL("Unknown filter direction.");
422             dir = ""; // suppress warning
423     }
424 
425     // vec2 coord = coord2D;
426     fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
427     // coord.x -= radius * pixelSize;
428     fsBuilder->codeAppendf("\t\tcoord.%s -= %d.0 * %s; \n", dir, fRadius, pixelSizeInc);
429     if (fUseRange) {
430         // highBound = min(highBound, coord.x + (width-1) * pixelSize);
431         fsBuilder->codeAppendf("\t\tfloat highBound = min(%s.y, coord.%s + %f * %s);",
432                                range, dir, float(width() - 1), pixelSizeInc);
433         // coord.x = max(lowBound, coord.x);
434         fsBuilder->codeAppendf("\t\tcoord.%s = max(%s.x, coord.%s);", dir, range, dir);
435     }
436     fsBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", width());
437     fsBuilder->codeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
438     fsBuilder->appendTextureLookup(samplers[0], "coord");
439     fsBuilder->codeAppend(");\n");
440     // coord.x += pixelSize;
441     fsBuilder->codeAppendf("\t\t\tcoord.%s += %s;\n", dir, pixelSizeInc);
442     if (fUseRange) {
443         // coord.x = min(highBound, coord.x);
444         fsBuilder->codeAppendf("\t\t\tcoord.%s = min(highBound, coord.%s);", dir, dir);
445     }
446     fsBuilder->codeAppend("\t\t}\n");
447     SkString modulate;
448     GrGLSLMulVarBy4f(&modulate, outputColor, inputColor);
449     fsBuilder->codeAppend(modulate.c_str());
450 }
451 
GenKey(const GrProcessor & proc,const GrGLSLCaps &,GrProcessorKeyBuilder * b)452 void GrGLMorphologyEffect::GenKey(const GrProcessor& proc,
453                                   const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
454     const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
455     uint32_t key = static_cast<uint32_t>(m.radius());
456     key |= (m.type() << 8);
457     key |= (m.direction() << 9);
458     if (m.useRange()) key |= 1 << 10;
459     b->add32(key);
460 }
461 
setData(const GrGLProgramDataManager & pdman,const GrProcessor & proc)462 void GrGLMorphologyEffect::setData(const GrGLProgramDataManager& pdman,
463                                    const GrProcessor& proc) {
464     const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
465     GrTexture& texture = *m.texture(0);
466     // the code we generated was for a specific kernel radius, direction and bound usage
467     SkASSERT(m.radius() == fRadius);
468     SkASSERT(m.direction() == fDirection);
469     SkASSERT(m.useRange() == fUseRange);
470 
471     float pixelSize = 0.0f;
472     switch (fDirection) {
473         case Gr1DKernelEffect::kX_Direction:
474             pixelSize = 1.0f / texture.width();
475             break;
476         case Gr1DKernelEffect::kY_Direction:
477             pixelSize = 1.0f / texture.height();
478             break;
479         default:
480             SkFAIL("Unknown filter direction.");
481     }
482     pdman.set1f(fPixelSizeUni, pixelSize);
483 
484     if (fUseRange) {
485         const float* range = m.range();
486         if (fDirection && texture.origin() == kBottomLeft_GrSurfaceOrigin) {
487             pdman.set2f(fRangeUni, 1.0f - range[1], 1.0f - range[0]);
488         } else {
489             pdman.set2f(fRangeUni, range[0], range[1]);
490         }
491     }
492 }
493 
494 ///////////////////////////////////////////////////////////////////////////////
495 
GrMorphologyEffect(GrTexture * texture,Direction direction,int radius,MorphologyType type)496 GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
497                                        Direction direction,
498                                        int radius,
499                                        MorphologyType type)
500     : Gr1DKernelEffect(texture, direction, radius)
501     , fType(type), fUseRange(false) {
502     this->initClassID<GrMorphologyEffect>();
503 }
504 
GrMorphologyEffect(GrTexture * texture,Direction direction,int radius,MorphologyType type,float range[2])505 GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
506                                        Direction direction,
507                                        int radius,
508                                        MorphologyType type,
509                                        float range[2])
510     : Gr1DKernelEffect(texture, direction, radius)
511     , fType(type), fUseRange(true) {
512     this->initClassID<GrMorphologyEffect>();
513     fRange[0] = range[0];
514     fRange[1] = range[1];
515 }
516 
~GrMorphologyEffect()517 GrMorphologyEffect::~GrMorphologyEffect() {
518 }
519 
getGLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const520 void GrMorphologyEffect::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
521     GrGLMorphologyEffect::GenKey(*this, caps, b);
522 }
523 
createGLInstance() const524 GrGLFragmentProcessor* GrMorphologyEffect::createGLInstance() const {
525     return SkNEW_ARGS(GrGLMorphologyEffect, (*this));
526 }
onIsEqual(const GrFragmentProcessor & sBase) const527 bool GrMorphologyEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
528     const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>();
529     return (this->radius() == s.radius() &&
530             this->direction() == s.direction() &&
531             this->useRange() == s.useRange() &&
532             this->type() == s.type());
533 }
534 
onComputeInvariantOutput(GrInvariantOutput * inout) const535 void GrMorphologyEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
536     // This is valid because the color components of the result of the kernel all come
537     // exactly from existing values in the source texture.
538     this->updateInvariantOutputForModulation(inout);
539 }
540 
541 ///////////////////////////////////////////////////////////////////////////////
542 
543 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMorphologyEffect);
544 
TestCreate(SkRandom * random,GrContext *,const GrDrawTargetCaps &,GrTexture * textures[])545 GrFragmentProcessor* GrMorphologyEffect::TestCreate(SkRandom* random,
546                                                     GrContext*,
547                                                     const GrDrawTargetCaps&,
548                                                     GrTexture* textures[]) {
549     int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
550                                       GrProcessorUnitTest::kAlphaTextureIdx;
551     Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
552     static const int kMaxRadius = 10;
553     int radius = random->nextRangeU(1, kMaxRadius);
554     MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
555                                                GrMorphologyEffect::kDilate_MorphologyType;
556 
557     return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type);
558 }
559 
560 namespace {
561 
562 
apply_morphology_rect(GrContext * context,GrRenderTarget * rt,const GrClip & clip,GrTexture * texture,const SkIRect & srcRect,const SkIRect & dstRect,int radius,GrMorphologyEffect::MorphologyType morphType,float bounds[2],Gr1DKernelEffect::Direction direction)563 void apply_morphology_rect(GrContext* context,
564                            GrRenderTarget* rt,
565                            const GrClip& clip,
566                            GrTexture* texture,
567                            const SkIRect& srcRect,
568                            const SkIRect& dstRect,
569                            int radius,
570                            GrMorphologyEffect::MorphologyType morphType,
571                            float bounds[2],
572                            Gr1DKernelEffect::Direction direction) {
573     GrPaint paint;
574     paint.addColorProcessor(GrMorphologyEffect::Create(texture,
575                                                        direction,
576                                                        radius,
577                                                        morphType,
578                                                        bounds))->unref();
579     context->drawNonAARectToRect(rt, clip, paint, SkMatrix::I(), SkRect::Make(dstRect),
580                                  SkRect::Make(srcRect));
581 }
582 
apply_morphology_rect_no_bounds(GrContext * context,GrRenderTarget * rt,const GrClip & clip,GrTexture * texture,const SkIRect & srcRect,const SkIRect & dstRect,int radius,GrMorphologyEffect::MorphologyType morphType,Gr1DKernelEffect::Direction direction)583 void apply_morphology_rect_no_bounds(GrContext* context,
584                                      GrRenderTarget* rt,
585                                      const GrClip& clip,
586                                      GrTexture* texture,
587                                      const SkIRect& srcRect,
588                                      const SkIRect& dstRect,
589                                      int radius,
590                                      GrMorphologyEffect::MorphologyType morphType,
591                                      Gr1DKernelEffect::Direction direction) {
592     GrPaint paint;
593     paint.addColorProcessor(GrMorphologyEffect::Create(texture,
594                                                        direction,
595                                                        radius,
596                                                        morphType))->unref();
597     context->drawNonAARectToRect(rt, clip, paint, SkMatrix::I(), SkRect::Make(dstRect),
598                                  SkRect::Make(srcRect));
599 }
600 
apply_morphology_pass(GrContext * context,GrRenderTarget * rt,const GrClip & clip,GrTexture * texture,const SkIRect & srcRect,const SkIRect & dstRect,int radius,GrMorphologyEffect::MorphologyType morphType,Gr1DKernelEffect::Direction direction)601 void apply_morphology_pass(GrContext* context,
602                            GrRenderTarget* rt,
603                            const GrClip& clip,
604                            GrTexture* texture,
605                            const SkIRect& srcRect,
606                            const SkIRect& dstRect,
607                            int radius,
608                            GrMorphologyEffect::MorphologyType morphType,
609                            Gr1DKernelEffect::Direction direction) {
610     float bounds[2] = { 0.0f, 1.0f };
611     SkIRect lowerSrcRect = srcRect, lowerDstRect = dstRect;
612     SkIRect middleSrcRect = srcRect, middleDstRect = dstRect;
613     SkIRect upperSrcRect = srcRect, upperDstRect = dstRect;
614     if (direction == Gr1DKernelEffect::kX_Direction) {
615         bounds[0] = (SkIntToScalar(srcRect.left()) + 0.5f) / texture->width();
616         bounds[1] = (SkIntToScalar(srcRect.right()) - 0.5f) / texture->width();
617         lowerSrcRect.fRight = srcRect.left() + radius;
618         lowerDstRect.fRight = dstRect.left() + radius;
619         upperSrcRect.fLeft = srcRect.right() - radius;
620         upperDstRect.fLeft = dstRect.right() - radius;
621         middleSrcRect.inset(radius, 0);
622         middleDstRect.inset(radius, 0);
623     } else {
624         bounds[0] = (SkIntToScalar(srcRect.top()) + 0.5f) / texture->height();
625         bounds[1] = (SkIntToScalar(srcRect.bottom()) - 0.5f) / texture->height();
626         lowerSrcRect.fBottom = srcRect.top() + radius;
627         lowerDstRect.fBottom = dstRect.top() + radius;
628         upperSrcRect.fTop = srcRect.bottom() - radius;
629         upperDstRect.fTop = dstRect.bottom() - radius;
630         middleSrcRect.inset(0, radius);
631         middleDstRect.inset(0, radius);
632     }
633     if (middleSrcRect.fLeft - middleSrcRect.fRight >= 0) {
634         // radius covers srcRect; use bounds over entire draw
635         apply_morphology_rect(context, rt, clip, texture, srcRect, dstRect, radius,
636                               morphType, bounds, direction);
637     } else {
638         // Draw upper and lower margins with bounds; middle without.
639         apply_morphology_rect(context, rt, clip, texture, lowerSrcRect, lowerDstRect, radius,
640                               morphType, bounds, direction);
641         apply_morphology_rect(context, rt, clip, texture, upperSrcRect, upperDstRect, radius,
642                               morphType, bounds, direction);
643         apply_morphology_rect_no_bounds(context, rt, clip, texture, middleSrcRect, middleDstRect,
644                                         radius, morphType, direction);
645     }
646 }
647 
apply_morphology(const SkBitmap & input,const SkIRect & rect,GrMorphologyEffect::MorphologyType morphType,SkISize radius,SkBitmap * dst)648 bool apply_morphology(const SkBitmap& input,
649                       const SkIRect& rect,
650                       GrMorphologyEffect::MorphologyType morphType,
651                       SkISize radius,
652                       SkBitmap* dst) {
653     SkAutoTUnref<GrTexture> srcTexture(SkRef(input.getTexture()));
654     SkASSERT(srcTexture);
655     GrContext* context = srcTexture->getContext();
656 
657     // setup new clip
658     GrClip clip(SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
659                                SkIntToScalar(srcTexture->height())));
660 
661     SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
662     GrSurfaceDesc desc;
663     desc.fFlags = kRenderTarget_GrSurfaceFlag;
664     desc.fWidth = rect.width();
665     desc.fHeight = rect.height();
666     desc.fConfig = kSkia8888_GrPixelConfig;
667     SkIRect srcRect = rect;
668 
669     if (radius.fWidth > 0) {
670         GrTexture* texture = context->textureProvider()->refScratchTexture(
671             desc, GrTextureProvider::kApprox_ScratchTexMatch);
672         if (NULL == texture) {
673             return false;
674         }
675         apply_morphology_pass(context, texture->asRenderTarget(), clip, srcTexture,
676                               srcRect, dstRect, radius.fWidth, morphType,
677                               Gr1DKernelEffect::kX_Direction);
678         SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
679                                               dstRect.width(), radius.fHeight);
680         GrColor clearColor = GrMorphologyEffect::kErode_MorphologyType == morphType ?
681                                 SK_ColorWHITE :
682                                 SK_ColorTRANSPARENT;
683         context->clear(&clearRect, clearColor, false, texture->asRenderTarget());
684         srcTexture.reset(texture);
685         srcRect = dstRect;
686     }
687     if (radius.fHeight > 0) {
688         GrTexture* texture = context->textureProvider()->refScratchTexture(desc,
689             GrTextureProvider::kApprox_ScratchTexMatch);
690         if (NULL == texture) {
691             return false;
692         }
693         apply_morphology_pass(context, texture->asRenderTarget(), clip, srcTexture,
694                               srcRect, dstRect, radius.fHeight, morphType,
695                               Gr1DKernelEffect::kY_Direction);
696         srcTexture.reset(texture);
697     }
698     SkImageFilter::WrapTexture(srcTexture, rect.width(), rect.height(), dst);
699     return true;
700 }
701 
702 };
703 
filterImageGPUGeneric(bool dilate,Proxy * proxy,const SkBitmap & src,const Context & ctx,SkBitmap * result,SkIPoint * offset) const704 bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate,
705                                                     Proxy* proxy,
706                                                     const SkBitmap& src,
707                                                     const Context& ctx,
708                                                     SkBitmap* result,
709                                                     SkIPoint* offset) const {
710     SkBitmap input = src;
711     SkIPoint srcOffset = SkIPoint::Make(0, 0);
712     if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
713         return false;
714     }
715     SkIRect bounds;
716     if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
717         return false;
718     }
719     SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
720                                      SkIntToScalar(this->radius().height()));
721     ctx.ctm().mapVectors(&radius, 1);
722     int width = SkScalarFloorToInt(radius.fX);
723     int height = SkScalarFloorToInt(radius.fY);
724 
725     if (width < 0 || height < 0) {
726         return false;
727     }
728 
729     SkIRect srcBounds = bounds;
730     srcBounds.offset(-srcOffset);
731     if (width == 0 && height == 0) {
732         input.extractSubset(result, srcBounds);
733         offset->fX = bounds.left();
734         offset->fY = bounds.top();
735         return true;
736     }
737 
738     GrMorphologyEffect::MorphologyType type = dilate ? GrMorphologyEffect::kDilate_MorphologyType : GrMorphologyEffect::kErode_MorphologyType;
739     if (!apply_morphology(input, srcBounds, type,
740                           SkISize::Make(width, height), result)) {
741         return false;
742     }
743     offset->fX = bounds.left();
744     offset->fY = bounds.top();
745     return true;
746 }
747 
filterImageGPU(Proxy * proxy,const SkBitmap & src,const Context & ctx,SkBitmap * result,SkIPoint * offset) const748 bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
749                                          SkBitmap* result, SkIPoint* offset) const {
750     return this->filterImageGPUGeneric(true, proxy, src, ctx, result, offset);
751 }
752 
filterImageGPU(Proxy * proxy,const SkBitmap & src,const Context & ctx,SkBitmap * result,SkIPoint * offset) const753 bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
754                                         SkBitmap* result, SkIPoint* offset) const {
755     return this->filterImageGPUGeneric(false, proxy, src, ctx, result, offset);
756 }
757 
758 #endif
759