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