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 = source;
    140     if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) {
    141         return false;
    142     }
    143 
    144     if (src.config() != SkBitmap::kARGB_8888_Config) {
    145         return false;
    146     }
    147 
    148     SkAutoLockPixels alp(src);
    149     if (!src.getPixels()) {
    150         return false;
    151     }
    152 
    153     dst->setConfig(src.config(), src.width(), src.height());
    154     dst->allocPixels();
    155 
    156     int width = radius().width();
    157     int height = radius().height();
    158 
    159     if (width < 0 || height < 0) {
    160         return false;
    161     }
    162 
    163     if (width == 0 && height == 0) {
    164         src.copyTo(dst, dst->config());
    165         return true;
    166     }
    167 
    168     SkBitmap temp;
    169     temp.setConfig(dst->config(), dst->width(), dst->height());
    170     if (!temp.allocPixels()) {
    171         return false;
    172     }
    173 
    174     if (width > 0 && height > 0) {
    175         erodeX(src, &temp, width);
    176         erodeY(temp, dst, height);
    177     } else if (width > 0) {
    178         erodeX(src, dst, width);
    179     } else if (height > 0) {
    180         erodeY(src, dst, height);
    181     }
    182     return true;
    183 }
    184 
    185 bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
    186                                         const SkBitmap& source, const SkMatrix& ctm,
    187                                         SkBitmap* dst, SkIPoint* offset) {
    188     SkBitmap src = source;
    189     if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) {
    190         return false;
    191     }
    192     if (src.config() != SkBitmap::kARGB_8888_Config) {
    193         return false;
    194     }
    195 
    196     SkAutoLockPixels alp(src);
    197     if (!src.getPixels()) {
    198         return false;
    199     }
    200 
    201     dst->setConfig(src.config(), src.width(), src.height());
    202     dst->allocPixels();
    203 
    204     int width = radius().width();
    205     int height = radius().height();
    206 
    207     if (width < 0 || height < 0) {
    208         return false;
    209     }
    210 
    211     if (width == 0 && height == 0) {
    212         src.copyTo(dst, dst->config());
    213         return true;
    214     }
    215 
    216     SkBitmap temp;
    217     temp.setConfig(dst->config(), dst->width(), dst->height());
    218     if (!temp.allocPixels()) {
    219         return false;
    220     }
    221 
    222     if (width > 0 && height > 0) {
    223         dilateX(src, &temp, width);
    224         dilateY(temp, dst, height);
    225     } else if (width > 0) {
    226         dilateX(src, dst, width);
    227     } else if (height > 0) {
    228         dilateY(src, dst, height);
    229     }
    230     return true;
    231 }
    232 
    233 #if SK_SUPPORT_GPU
    234 
    235 ///////////////////////////////////////////////////////////////////////////////
    236 
    237 class GrGLMorphologyEffect;
    238 
    239 /**
    240  * Morphology effects. Depending upon the type of morphology, either the
    241  * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
    242  * kernel is selected as the new color. The new color is modulated by the input
    243  * color.
    244  */
    245 class GrMorphologyEffect : public Gr1DKernelEffect {
    246 
    247 public:
    248 
    249     enum MorphologyType {
    250         kErode_MorphologyType,
    251         kDilate_MorphologyType,
    252     };
    253 
    254     static GrEffectRef* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type) {
    255         AutoEffectUnref effect(SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type)));
    256         return CreateEffectRef(effect);
    257     }
    258 
    259     virtual ~GrMorphologyEffect();
    260 
    261     MorphologyType type() const { return fType; }
    262 
    263     static const char* Name() { return "Morphology"; }
    264 
    265     typedef GrGLMorphologyEffect GLEffect;
    266 
    267     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
    268     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
    269 
    270 protected:
    271 
    272     MorphologyType fType;
    273 
    274 private:
    275     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
    276 
    277     GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
    278 
    279     GR_DECLARE_EFFECT_TEST;
    280 
    281     typedef Gr1DKernelEffect INHERITED;
    282 };
    283 
    284 ///////////////////////////////////////////////////////////////////////////////
    285 
    286 class GrGLMorphologyEffect : public GrGLEffect {
    287 public:
    288     GrGLMorphologyEffect (const GrBackendEffectFactory&, const GrDrawEffect&);
    289 
    290     virtual void emitCode(GrGLShaderBuilder*,
    291                           const GrDrawEffect&,
    292                           EffectKey,
    293                           const char* outputColor,
    294                           const char* inputColor,
    295                           const TextureSamplerArray&) SK_OVERRIDE;
    296 
    297     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
    298 
    299     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    300 
    301 private:
    302     int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
    303 
    304     int                                 fRadius;
    305     GrMorphologyEffect::MorphologyType  fType;
    306     GrGLUniformManager::UniformHandle   fImageIncrementUni;
    307     GrGLEffectMatrix                    fEffectMatrix;
    308 
    309     typedef GrGLEffect INHERITED;
    310 };
    311 
    312 GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendEffectFactory& factory,
    313                                            const GrDrawEffect& drawEffect)
    314     : INHERITED(factory)
    315     , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle)
    316     , fEffectMatrix(drawEffect.castEffect<GrMorphologyEffect>().coordsType()) {
    317     const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>();
    318     fRadius = m.radius();
    319     fType = m.type();
    320 }
    321 
    322 void GrGLMorphologyEffect::emitCode(GrGLShaderBuilder* builder,
    323                                     const GrDrawEffect&,
    324                                     EffectKey key,
    325                                     const char* outputColor,
    326                                     const char* inputColor,
    327                                     const TextureSamplerArray& samplers) {
    328     const char* coords;
    329     fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
    330     fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
    331                                              kVec2f_GrSLType, "ImageIncrement");
    332 
    333     const char* func;
    334     switch (fType) {
    335         case GrMorphologyEffect::kErode_MorphologyType:
    336             builder->fsCodeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor);
    337             func = "min";
    338             break;
    339         case GrMorphologyEffect::kDilate_MorphologyType:
    340             builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
    341             func = "max";
    342             break;
    343         default:
    344             GrCrash("Unexpected type");
    345             func = ""; // suppress warning
    346             break;
    347     }
    348     const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
    349 
    350     builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc);
    351     builder->fsCodeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
    352     builder->fsCodeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
    353     builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, samplers[0], "coord");
    354     builder->fsCodeAppend(");\n");
    355     builder->fsCodeAppendf("\t\t\tcoord += %s;\n", imgInc);
    356     builder->fsCodeAppend("\t\t}\n");
    357     SkString modulate;
    358     GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
    359     builder->fsCodeAppend(modulate.c_str());
    360 }
    361 
    362 GrGLEffect::EffectKey GrGLMorphologyEffect::GenKey(const GrDrawEffect& drawEffect,
    363                                                    const GrGLCaps&) {
    364     const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>();
    365     EffectKey key = static_cast<EffectKey>(m.radius());
    366     key |= (m.type() << 8);
    367     key <<= GrGLEffectMatrix::kKeyBits;
    368     EffectKey matrixKey = GrGLEffectMatrix::GenKey(m.getMatrix(),
    369                                                    drawEffect,
    370                                                    m.coordsType(),
    371                                                    m.texture(0));
    372     return key | matrixKey;
    373 }
    374 
    375 void GrGLMorphologyEffect::setData(const GrGLUniformManager& uman,
    376                                    const GrDrawEffect& drawEffect) {
    377     const Gr1DKernelEffect& kern = drawEffect.castEffect<Gr1DKernelEffect>();
    378     GrTexture& texture = *kern.texture(0);
    379     // the code we generated was for a specific kernel radius
    380     GrAssert(kern.radius() == fRadius);
    381     float imageIncrement[2] = { 0 };
    382     switch (kern.direction()) {
    383         case Gr1DKernelEffect::kX_Direction:
    384             imageIncrement[0] = 1.0f / texture.width();
    385             break;
    386         case Gr1DKernelEffect::kY_Direction:
    387             imageIncrement[1] = 1.0f / texture.height();
    388             break;
    389         default:
    390             GrCrash("Unknown filter direction.");
    391     }
    392     uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
    393     fEffectMatrix.setData(uman, kern.getMatrix(), drawEffect, kern.texture(0));
    394 }
    395 
    396 ///////////////////////////////////////////////////////////////////////////////
    397 
    398 GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
    399                                        Direction direction,
    400                                        int radius,
    401                                        MorphologyType type)
    402     : Gr1DKernelEffect(texture, direction, radius)
    403     , fType(type) {
    404 }
    405 
    406 GrMorphologyEffect::~GrMorphologyEffect() {
    407 }
    408 
    409 const GrBackendEffectFactory& GrMorphologyEffect::getFactory() const {
    410     return GrTBackendEffectFactory<GrMorphologyEffect>::getInstance();
    411 }
    412 
    413 bool GrMorphologyEffect::onIsEqual(const GrEffect& sBase) const {
    414     const GrMorphologyEffect& s = CastEffect<GrMorphologyEffect>(sBase);
    415     return (this->texture(0) == s.texture(0) &&
    416             this->radius() == s.radius() &&
    417             this->direction() == s.direction() &&
    418             this->type() == s.type());
    419 }
    420 
    421 void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
    422     // This is valid because the color components of the result of the kernel all come
    423     // exactly from existing values in the source texture.
    424     this->updateConstantColorComponentsForModulation(color, validFlags);
    425 }
    426 
    427 ///////////////////////////////////////////////////////////////////////////////
    428 
    429 GR_DEFINE_EFFECT_TEST(GrMorphologyEffect);
    430 
    431 GrEffectRef* GrMorphologyEffect::TestCreate(SkMWCRandom* random,
    432                                             GrContext*,
    433                                             const GrDrawTargetCaps&,
    434                                             GrTexture* textures[]) {
    435     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
    436                                       GrEffectUnitTest::kAlphaTextureIdx;
    437     Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
    438     static const int kMaxRadius = 10;
    439     int radius = random->nextRangeU(1, kMaxRadius);
    440     MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
    441                                                GrMorphologyEffect::kDilate_MorphologyType;
    442 
    443     return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type);
    444 }
    445 
    446 namespace {
    447 
    448 void apply_morphology_pass(GrContext* context,
    449                            GrTexture* texture,
    450                            const SkIRect& rect,
    451                            int radius,
    452                            GrMorphologyEffect::MorphologyType morphType,
    453                            Gr1DKernelEffect::Direction direction) {
    454     GrPaint paint;
    455     paint.addColorEffect(GrMorphologyEffect::Create(texture,
    456                                                     direction,
    457                                                     radius,
    458                                                     morphType))->unref();
    459     context->drawRect(paint, SkRect::MakeFromIRect(rect));
    460 }
    461 
    462 GrTexture* apply_morphology(GrTexture* srcTexture,
    463                             const SkIRect& rect,
    464                             GrMorphologyEffect::MorphologyType morphType,
    465                             SkISize radius) {
    466     GrContext* context = srcTexture->getContext();
    467     srcTexture->ref();
    468 
    469     GrContext::AutoMatrix am;
    470     am.setIdentity(context);
    471 
    472     GrContext::AutoClip acs(context, SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
    473                                                     SkIntToScalar(srcTexture->height())));
    474 
    475     GrTextureDesc desc;
    476     desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
    477     desc.fWidth = rect.width();
    478     desc.fHeight = rect.height();
    479     desc.fConfig = kSkia8888_GrPixelConfig;
    480 
    481     if (radius.fWidth > 0) {
    482         GrAutoScratchTexture ast(context, desc);
    483         GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
    484         apply_morphology_pass(context, srcTexture, rect, radius.fWidth,
    485                               morphType, Gr1DKernelEffect::kX_Direction);
    486         SkIRect clearRect = SkIRect::MakeXYWH(rect.fLeft, rect.fBottom,
    487                                               rect.width(), radius.fHeight);
    488         context->clear(&clearRect, 0x0);
    489         srcTexture->unref();
    490         srcTexture = ast.detach();
    491     }
    492     if (radius.fHeight > 0) {
    493         GrAutoScratchTexture ast(context, desc);
    494         GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
    495         apply_morphology_pass(context, srcTexture, rect, radius.fHeight,
    496                               morphType, Gr1DKernelEffect::kY_Direction);
    497         srcTexture->unref();
    498         srcTexture = ast.detach();
    499     }
    500     return srcTexture;
    501 }
    502 
    503 };
    504 
    505 bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
    506                                          SkBitmap* result, SkIPoint* offset) {
    507     SkBitmap inputBM;
    508     if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &inputBM, offset)) {
    509         return false;
    510     }
    511     GrTexture* input = inputBM.getTexture();
    512     SkIRect bounds;
    513     src.getBounds(&bounds);
    514     SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds,
    515         GrMorphologyEffect::kDilate_MorphologyType, radius()));
    516     return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result);
    517 }
    518 
    519 bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
    520                                         SkBitmap* result, SkIPoint* offset) {
    521     SkBitmap inputBM;
    522     if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &inputBM, offset)) {
    523         return false;
    524     }
    525     GrTexture* input = inputBM.getTexture();
    526     SkIRect bounds;
    527     src.getBounds(&bounds);
    528     SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds,
    529         GrMorphologyEffect::kErode_MorphologyType, radius()));
    530     return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result);
    531 }
    532 
    533 #endif
    534