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