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