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