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 "GrTexture.h"
18#include "GrTBackendEffectFactory.h"
19#include "gl/GrGLEffect.h"
20#include "effects/Gr1DKernelEffect.h"
21#endif
22
23SkMorphologyImageFilter::SkMorphologyImageFilter(SkReadBuffer& buffer)
24  : INHERITED(1, buffer) {
25    fRadius.fWidth = buffer.readInt();
26    fRadius.fHeight = buffer.readInt();
27    buffer.validate((fRadius.fWidth >= 0) &&
28                    (fRadius.fHeight >= 0));
29}
30
31SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX,
32                                                 int radiusY,
33                                                 SkImageFilter* input,
34                                                 const CropRect* cropRect)
35    : INHERITED(input, cropRect), fRadius(SkISize::Make(radiusX, radiusY)) {
36}
37
38
39void SkMorphologyImageFilter::flatten(SkWriteBuffer& buffer) const {
40    this->INHERITED::flatten(buffer);
41    buffer.writeInt(fRadius.fWidth);
42    buffer.writeInt(fRadius.fHeight);
43}
44
45enum MorphDirection {
46    kX, kY
47};
48
49template<MorphDirection direction>
50static void erode(const SkPMColor* src, SkPMColor* dst,
51                  int radius, int width, int height,
52                  int srcStride, int dstStride)
53{
54    const int srcStrideX = direction == kX ? 1 : srcStride;
55    const int dstStrideX = direction == kX ? 1 : dstStride;
56    const int srcStrideY = direction == kX ? srcStride : 1;
57    const int dstStrideY = direction == kX ? dstStride : 1;
58    radius = SkMin32(radius, width - 1);
59    const SkPMColor* upperSrc = src + radius * srcStrideX;
60    for (int x = 0; x < width; ++x) {
61        const SkPMColor* lp = src;
62        const SkPMColor* up = upperSrc;
63        SkPMColor* dptr = dst;
64        for (int y = 0; y < height; ++y) {
65            int minB = 255, minG = 255, minR = 255, minA = 255;
66            for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
67                int b = SkGetPackedB32(*p);
68                int g = SkGetPackedG32(*p);
69                int r = SkGetPackedR32(*p);
70                int a = SkGetPackedA32(*p);
71                if (b < minB) minB = b;
72                if (g < minG) minG = g;
73                if (r < minR) minR = r;
74                if (a < minA) minA = a;
75            }
76            *dptr = SkPackARGB32(minA, minR, minG, minB);
77            dptr += dstStrideY;
78            lp += srcStrideY;
79            up += srcStrideY;
80        }
81        if (x >= radius) src += srcStrideX;
82        if (x + radius < width - 1) upperSrc += srcStrideX;
83        dst += dstStrideX;
84    }
85}
86
87template<MorphDirection direction>
88static void dilate(const SkPMColor* src, SkPMColor* dst,
89                   int radius, int width, int height,
90                   int srcStride, int dstStride)
91{
92    const int srcStrideX = direction == kX ? 1 : srcStride;
93    const int dstStrideX = direction == kX ? 1 : dstStride;
94    const int srcStrideY = direction == kX ? srcStride : 1;
95    const int dstStrideY = direction == kX ? dstStride : 1;
96    radius = SkMin32(radius, width - 1);
97    const SkPMColor* upperSrc = src + radius * srcStrideX;
98    for (int x = 0; x < width; ++x) {
99        const SkPMColor* lp = src;
100        const SkPMColor* up = upperSrc;
101        SkPMColor* dptr = dst;
102        for (int y = 0; y < height; ++y) {
103            int maxB = 0, maxG = 0, maxR = 0, maxA = 0;
104            for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
105                int b = SkGetPackedB32(*p);
106                int g = SkGetPackedG32(*p);
107                int r = SkGetPackedR32(*p);
108                int a = SkGetPackedA32(*p);
109                if (b > maxB) maxB = b;
110                if (g > maxG) maxG = g;
111                if (r > maxR) maxR = r;
112                if (a > maxA) maxA = a;
113            }
114            *dptr = SkPackARGB32(maxA, maxR, maxG, maxB);
115            dptr += dstStrideY;
116            lp += srcStrideY;
117            up += srcStrideY;
118        }
119        if (x >= radius) src += srcStrideX;
120        if (x + radius < width - 1) upperSrc += srcStrideX;
121        dst += dstStrideX;
122    }
123}
124
125static void callProcX(SkMorphologyImageFilter::Proc procX, const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds)
126{
127    procX(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
128          radiusX, bounds.width(), bounds.height(),
129          src.rowBytesAsPixels(), dst->rowBytesAsPixels());
130}
131
132static void callProcY(SkMorphologyImageFilter::Proc procY, const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds)
133{
134    procY(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
135          radiusY, bounds.height(), bounds.width(),
136          src.rowBytesAsPixels(), dst->rowBytesAsPixels());
137}
138
139bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc procX,
140                                                 SkMorphologyImageFilter::Proc procY,
141                                                 Proxy* proxy,
142                                                 const SkBitmap& source,
143                                                 const Context& ctx,
144                                                 SkBitmap* dst,
145                                                 SkIPoint* offset) const {
146    SkBitmap src = source;
147    SkIPoint srcOffset = SkIPoint::Make(0, 0);
148    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
149        return false;
150    }
151
152    if (src.colorType() != kN32_SkColorType) {
153        return false;
154    }
155
156    SkIRect bounds;
157    if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
158        return false;
159    }
160
161    SkAutoLockPixels alp(src);
162    if (!src.getPixels()) {
163        return false;
164    }
165
166    if (!dst->allocPixels(src.info().makeWH(bounds.width(), bounds.height()))) {
167        return false;
168    }
169
170    SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
171                                     SkIntToScalar(this->radius().height()));
172    ctx.ctm().mapVectors(&radius, 1);
173    int width = SkScalarFloorToInt(radius.fX);
174    int height = SkScalarFloorToInt(radius.fY);
175
176    if (width < 0 || height < 0) {
177        return false;
178    }
179
180    SkIRect srcBounds = bounds;
181    srcBounds.offset(-srcOffset);
182
183    if (width == 0 && height == 0) {
184        src.extractSubset(dst, srcBounds);
185        offset->fX = bounds.left();
186        offset->fY = bounds.top();
187        return true;
188    }
189
190    SkBitmap temp;
191    if (!temp.allocPixels(dst->info())) {
192        return false;
193    }
194
195    if (width > 0 && height > 0) {
196        callProcX(procX, src, &temp, width, srcBounds);
197        SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
198        callProcY(procY, temp, dst, height, tmpBounds);
199    } else if (width > 0) {
200        callProcX(procX, src, dst, width, srcBounds);
201    } else if (height > 0) {
202        callProcY(procY, src, dst, height, srcBounds);
203    }
204    offset->fX = bounds.left();
205    offset->fY = bounds.top();
206    return true;
207}
208
209bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
210                                       const SkBitmap& source, const Context& ctx,
211                                       SkBitmap* dst, SkIPoint* offset) const {
212    Proc erodeXProc = SkMorphologyGetPlatformProc(kErodeX_SkMorphologyProcType);
213    if (!erodeXProc) {
214        erodeXProc = erode<kX>;
215    }
216    Proc erodeYProc = SkMorphologyGetPlatformProc(kErodeY_SkMorphologyProcType);
217    if (!erodeYProc) {
218        erodeYProc = erode<kY>;
219    }
220    return this->filterImageGeneric(erodeXProc, erodeYProc, proxy, source, ctx, dst, offset);
221}
222
223bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
224                                        const SkBitmap& source, const Context& ctx,
225                                        SkBitmap* dst, SkIPoint* offset) const {
226    Proc dilateXProc = SkMorphologyGetPlatformProc(kDilateX_SkMorphologyProcType);
227    if (!dilateXProc) {
228        dilateXProc = dilate<kX>;
229    }
230    Proc dilateYProc = SkMorphologyGetPlatformProc(kDilateY_SkMorphologyProcType);
231    if (!dilateYProc) {
232        dilateYProc = dilate<kY>;
233    }
234    return this->filterImageGeneric(dilateXProc, dilateYProc, proxy, source, ctx, dst, offset);
235}
236
237void SkMorphologyImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
238    if (getInput(0)) {
239        getInput(0)->computeFastBounds(src, dst);
240    } else {
241        *dst = src;
242    }
243    dst->outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height()));
244}
245
246bool SkMorphologyImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
247                                             SkIRect* dst) const {
248    SkIRect bounds = src;
249    if (getInput(0) && !getInput(0)->filterBounds(src, ctm, &bounds)) {
250        return false;
251    }
252    SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
253                                     SkIntToScalar(this->radius().height()));
254    ctm.mapVectors(&radius, 1);
255    bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
256    *dst = bounds;
257    return true;
258}
259
260#if SK_SUPPORT_GPU
261
262///////////////////////////////////////////////////////////////////////////////
263
264class GrGLMorphologyEffect;
265
266/**
267 * Morphology effects. Depending upon the type of morphology, either the
268 * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
269 * kernel is selected as the new color. The new color is modulated by the input
270 * color.
271 */
272class GrMorphologyEffect : public Gr1DKernelEffect {
273
274public:
275
276    enum MorphologyType {
277        kErode_MorphologyType,
278        kDilate_MorphologyType,
279    };
280
281    static GrEffectRef* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type) {
282        AutoEffectUnref effect(SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type)));
283        return CreateEffectRef(effect);
284    }
285
286    virtual ~GrMorphologyEffect();
287
288    MorphologyType type() const { return fType; }
289
290    static const char* Name() { return "Morphology"; }
291
292    typedef GrGLMorphologyEffect GLEffect;
293
294    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
295    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
296
297protected:
298
299    MorphologyType fType;
300
301private:
302    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
303
304    GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
305
306    GR_DECLARE_EFFECT_TEST;
307
308    typedef Gr1DKernelEffect INHERITED;
309};
310
311///////////////////////////////////////////////////////////////////////////////
312
313class GrGLMorphologyEffect : public GrGLEffect {
314public:
315    GrGLMorphologyEffect (const GrBackendEffectFactory&, const GrDrawEffect&);
316
317    virtual void emitCode(GrGLShaderBuilder*,
318                          const GrDrawEffect&,
319                          EffectKey,
320                          const char* outputColor,
321                          const char* inputColor,
322                          const TransformedCoordsArray&,
323                          const TextureSamplerArray&) SK_OVERRIDE;
324
325    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
326
327    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
328
329private:
330    int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
331
332    int                                 fRadius;
333    GrMorphologyEffect::MorphologyType  fType;
334    GrGLUniformManager::UniformHandle   fImageIncrementUni;
335
336    typedef GrGLEffect INHERITED;
337};
338
339GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendEffectFactory& factory,
340                                           const GrDrawEffect& drawEffect)
341    : INHERITED(factory) {
342    const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>();
343    fRadius = m.radius();
344    fType = m.type();
345}
346
347void GrGLMorphologyEffect::emitCode(GrGLShaderBuilder* builder,
348                                    const GrDrawEffect&,
349                                    EffectKey key,
350                                    const char* outputColor,
351                                    const char* inputColor,
352                                    const TransformedCoordsArray& coords,
353                                    const TextureSamplerArray& samplers) {
354    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
355    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
356                                             kVec2f_GrSLType, "ImageIncrement");
357
358    const char* func;
359    switch (fType) {
360        case GrMorphologyEffect::kErode_MorphologyType:
361            builder->fsCodeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor);
362            func = "min";
363            break;
364        case GrMorphologyEffect::kDilate_MorphologyType:
365            builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
366            func = "max";
367            break;
368        default:
369            SkFAIL("Unexpected type");
370            func = ""; // suppress warning
371            break;
372    }
373    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
374
375    builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc);
376    builder->fsCodeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
377    builder->fsCodeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
378    builder->fsAppendTextureLookup(samplers[0], "coord");
379    builder->fsCodeAppend(");\n");
380    builder->fsCodeAppendf("\t\t\tcoord += %s;\n", imgInc);
381    builder->fsCodeAppend("\t\t}\n");
382    SkString modulate;
383    GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
384    builder->fsCodeAppend(modulate.c_str());
385}
386
387GrGLEffect::EffectKey GrGLMorphologyEffect::GenKey(const GrDrawEffect& drawEffect,
388                                                   const GrGLCaps&) {
389    const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>();
390    EffectKey key = static_cast<EffectKey>(m.radius());
391    key |= (m.type() << 8);
392    return key;
393}
394
395void GrGLMorphologyEffect::setData(const GrGLUniformManager& uman,
396                                   const GrDrawEffect& drawEffect) {
397    const Gr1DKernelEffect& kern = drawEffect.castEffect<Gr1DKernelEffect>();
398    GrTexture& texture = *kern.texture(0);
399    // the code we generated was for a specific kernel radius
400    SkASSERT(kern.radius() == fRadius);
401    float imageIncrement[2] = { 0 };
402    switch (kern.direction()) {
403        case Gr1DKernelEffect::kX_Direction:
404            imageIncrement[0] = 1.0f / texture.width();
405            break;
406        case Gr1DKernelEffect::kY_Direction:
407            imageIncrement[1] = 1.0f / texture.height();
408            break;
409        default:
410            SkFAIL("Unknown filter direction.");
411    }
412    uman.set2fv(fImageIncrementUni, 1, imageIncrement);
413}
414
415///////////////////////////////////////////////////////////////////////////////
416
417GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
418                                       Direction direction,
419                                       int radius,
420                                       MorphologyType type)
421    : Gr1DKernelEffect(texture, direction, radius)
422    , fType(type) {
423}
424
425GrMorphologyEffect::~GrMorphologyEffect() {
426}
427
428const GrBackendEffectFactory& GrMorphologyEffect::getFactory() const {
429    return GrTBackendEffectFactory<GrMorphologyEffect>::getInstance();
430}
431
432bool GrMorphologyEffect::onIsEqual(const GrEffect& sBase) const {
433    const GrMorphologyEffect& s = CastEffect<GrMorphologyEffect>(sBase);
434    return (this->texture(0) == s.texture(0) &&
435            this->radius() == s.radius() &&
436            this->direction() == s.direction() &&
437            this->type() == s.type());
438}
439
440void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
441    // This is valid because the color components of the result of the kernel all come
442    // exactly from existing values in the source texture.
443    this->updateConstantColorComponentsForModulation(color, validFlags);
444}
445
446///////////////////////////////////////////////////////////////////////////////
447
448GR_DEFINE_EFFECT_TEST(GrMorphologyEffect);
449
450GrEffectRef* GrMorphologyEffect::TestCreate(SkRandom* random,
451                                            GrContext*,
452                                            const GrDrawTargetCaps&,
453                                            GrTexture* textures[]) {
454    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
455                                      GrEffectUnitTest::kAlphaTextureIdx;
456    Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
457    static const int kMaxRadius = 10;
458    int radius = random->nextRangeU(1, kMaxRadius);
459    MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
460                                               GrMorphologyEffect::kDilate_MorphologyType;
461
462    return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type);
463}
464
465namespace {
466
467void apply_morphology_pass(GrContext* context,
468                           GrTexture* texture,
469                           const SkIRect& srcRect,
470                           const SkIRect& dstRect,
471                           int radius,
472                           GrMorphologyEffect::MorphologyType morphType,
473                           Gr1DKernelEffect::Direction direction) {
474    GrPaint paint;
475    paint.addColorEffect(GrMorphologyEffect::Create(texture,
476                                                    direction,
477                                                    radius,
478                                                    morphType))->unref();
479    context->drawRectToRect(paint, SkRect::Make(dstRect), SkRect::Make(srcRect));
480}
481
482bool apply_morphology(const SkBitmap& input,
483                      const SkIRect& rect,
484                      GrMorphologyEffect::MorphologyType morphType,
485                      SkISize radius,
486                      SkBitmap* dst) {
487    GrTexture* srcTexture = input.getTexture();
488    SkASSERT(NULL != srcTexture);
489    GrContext* context = srcTexture->getContext();
490    srcTexture->ref();
491    SkAutoTUnref<GrTexture> src(srcTexture);
492
493    GrContext::AutoMatrix am;
494    am.setIdentity(context);
495
496    GrContext::AutoClip acs(context, SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
497                                                    SkIntToScalar(srcTexture->height())));
498
499    SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
500    GrTextureDesc desc;
501    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
502    desc.fWidth = rect.width();
503    desc.fHeight = rect.height();
504    desc.fConfig = kSkia8888_GrPixelConfig;
505    SkIRect srcRect = rect;
506
507    if (radius.fWidth > 0) {
508        GrAutoScratchTexture ast(context, desc);
509        GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
510        apply_morphology_pass(context, src, srcRect, dstRect, radius.fWidth,
511                              morphType, Gr1DKernelEffect::kX_Direction);
512        SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
513                                              dstRect.width(), radius.fHeight);
514        context->clear(&clearRect, GrMorphologyEffect::kErode_MorphologyType == morphType ?
515                                   SK_ColorWHITE :
516                                   SK_ColorTRANSPARENT, false);
517        src.reset(ast.detach());
518        srcRect = dstRect;
519    }
520    if (radius.fHeight > 0) {
521        GrAutoScratchTexture ast(context, desc);
522        GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
523        apply_morphology_pass(context, src, srcRect, dstRect, radius.fHeight,
524                              morphType, Gr1DKernelEffect::kY_Direction);
525        src.reset(ast.detach());
526    }
527    SkImageFilter::WrapTexture(src, rect.width(), rect.height(), dst);
528    return true;
529}
530
531};
532
533bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate,
534                                                    Proxy* proxy,
535                                                    const SkBitmap& src,
536                                                    const Context& ctx,
537                                                    SkBitmap* result,
538                                                    SkIPoint* offset) const {
539    SkBitmap input = src;
540    SkIPoint srcOffset = SkIPoint::Make(0, 0);
541    if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
542        return false;
543    }
544    SkIRect bounds;
545    if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
546        return false;
547    }
548    SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
549                                     SkIntToScalar(this->radius().height()));
550    ctx.ctm().mapVectors(&radius, 1);
551    int width = SkScalarFloorToInt(radius.fX);
552    int height = SkScalarFloorToInt(radius.fY);
553
554    if (width < 0 || height < 0) {
555        return false;
556    }
557
558    SkIRect srcBounds = bounds;
559    srcBounds.offset(-srcOffset);
560    if (width == 0 && height == 0) {
561        input.extractSubset(result, srcBounds);
562        offset->fX = bounds.left();
563        offset->fY = bounds.top();
564        return true;
565    }
566
567    GrMorphologyEffect::MorphologyType type = dilate ? GrMorphologyEffect::kDilate_MorphologyType : GrMorphologyEffect::kErode_MorphologyType;
568    if (!apply_morphology(input, srcBounds, type,
569                          SkISize::Make(width, height), result)) {
570        return false;
571    }
572    offset->fX = bounds.left();
573    offset->fY = bounds.top();
574    return true;
575}
576
577bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
578                                         SkBitmap* result, SkIPoint* offset) const {
579    return this->filterImageGPUGeneric(true, proxy, src, ctx, result, offset);
580}
581
582bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
583                                        SkBitmap* result, SkIPoint* offset) const {
584    return this->filterImageGPUGeneric(false, proxy, src, ctx, result, offset);
585}
586
587#endif
588