Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2013 Google Inc.
      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 "SkAlphaThresholdFilter.h"
      9 #include "SkBitmap.h"
     10 #include "SkReadBuffer.h"
     11 #include "SkWriteBuffer.h"
     12 #include "SkRegion.h"
     13 
     14 class SK_API SkAlphaThresholdFilterImpl : public SkImageFilter {
     15 public:
     16     SkAlphaThresholdFilterImpl(const SkRegion& region, SkScalar innerThreshold, SkScalar outerThreshold);
     17 
     18     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAlphaThresholdFilterImpl)
     19 
     20 protected:
     21     explicit SkAlphaThresholdFilterImpl(SkReadBuffer& buffer);
     22     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
     23 
     24     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
     25                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
     26 #if SK_SUPPORT_GPU
     27     virtual bool asNewEffect(GrEffectRef** effect, GrTexture* texture,
     28                              const SkMatrix& matrix, const SkIRect& bounds) const SK_OVERRIDE;
     29 #endif
     30 
     31 private:
     32     SkRegion fRegion;
     33     SkScalar fInnerThreshold;
     34     SkScalar fOuterThreshold;
     35     typedef SkImageFilter INHERITED;
     36 };
     37 
     38 SkImageFilter* SkAlphaThresholdFilter::Create(const SkRegion& region,
     39                                               SkScalar innerThreshold,
     40                                               SkScalar outerThreshold) {
     41     return SkNEW_ARGS(SkAlphaThresholdFilterImpl, (region, innerThreshold, outerThreshold));
     42 }
     43 
     44 #if SK_SUPPORT_GPU
     45 #include "GrContext.h"
     46 #include "GrCoordTransform.h"
     47 #include "GrEffect.h"
     48 #include "gl/GrGLEffect.h"
     49 #include "GrTBackendEffectFactory.h"
     50 #include "GrTextureAccess.h"
     51 
     52 #include "SkGr.h"
     53 
     54 class GrGLAlphaThresholdEffect;
     55 
     56 class AlphaThresholdEffect : public GrEffect {
     57 
     58 public:
     59     static GrEffectRef* Create(GrTexture* texture,
     60                                GrTexture* maskTexture,
     61                                float innerThreshold,
     62                                float outerThreshold) {
     63         AutoEffectUnref effect(SkNEW_ARGS(AlphaThresholdEffect, (texture,
     64                                                                  maskTexture,
     65                                                                  innerThreshold,
     66                                                                  outerThreshold)));
     67         return CreateEffectRef(effect);
     68     }
     69 
     70     virtual ~AlphaThresholdEffect() {};
     71 
     72     static const char* Name() { return "Alpha Threshold"; }
     73 
     74     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
     75     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
     76 
     77     float innerThreshold() const { return fInnerThreshold; }
     78     float outerThreshold() const { return fOuterThreshold; }
     79 
     80     typedef GrGLAlphaThresholdEffect GLEffect;
     81 
     82 private:
     83     AlphaThresholdEffect(GrTexture* texture,
     84                          GrTexture* maskTexture,
     85                          float innerThreshold,
     86                          float outerThreshold)
     87         : fInnerThreshold(innerThreshold)
     88         , fOuterThreshold(outerThreshold)
     89         , fImageCoordTransform(kLocal_GrCoordSet, MakeDivByTextureWHMatrix(texture), texture)
     90         , fImageTextureAccess(texture)
     91         , fMaskCoordTransform(kLocal_GrCoordSet, MakeDivByTextureWHMatrix(maskTexture), maskTexture)
     92         , fMaskTextureAccess(maskTexture) {
     93         this->addCoordTransform(&fImageCoordTransform);
     94         this->addTextureAccess(&fImageTextureAccess);
     95         this->addCoordTransform(&fMaskCoordTransform);
     96         this->addTextureAccess(&fMaskTextureAccess);
     97     }
     98 
     99     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
    100 
    101     GR_DECLARE_EFFECT_TEST;
    102 
    103     float fInnerThreshold;
    104     float fOuterThreshold;
    105     GrCoordTransform fImageCoordTransform;
    106     GrTextureAccess  fImageTextureAccess;
    107     GrCoordTransform fMaskCoordTransform;
    108     GrTextureAccess  fMaskTextureAccess;
    109 
    110     typedef GrEffect INHERITED;
    111 };
    112 
    113 class GrGLAlphaThresholdEffect : public GrGLEffect {
    114 public:
    115     GrGLAlphaThresholdEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
    116 
    117     virtual void emitCode(GrGLShaderBuilder*,
    118                           const GrDrawEffect&,
    119                           EffectKey,
    120                           const char* outputColor,
    121                           const char* inputColor,
    122                           const TransformedCoordsArray&,
    123                           const TextureSamplerArray&) SK_OVERRIDE;
    124 
    125     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    126 
    127 private:
    128 
    129     GrGLUniformManager::UniformHandle       fInnerThresholdVar;
    130     GrGLUniformManager::UniformHandle       fOuterThresholdVar;
    131 
    132     typedef GrGLEffect INHERITED;
    133 };
    134 
    135 GrGLAlphaThresholdEffect::GrGLAlphaThresholdEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
    136     : INHERITED(factory) {
    137 }
    138 
    139 void GrGLAlphaThresholdEffect::emitCode(GrGLShaderBuilder* builder,
    140                                         const GrDrawEffect&,
    141                                         EffectKey key,
    142                                         const char* outputColor,
    143                                         const char* inputColor,
    144                                         const TransformedCoordsArray& coords,
    145                                         const TextureSamplerArray& samplers) {
    146     SkString coords2D = builder->ensureFSCoords2D(coords, 0);
    147     SkString maskCoords2D = builder->ensureFSCoords2D(coords, 1);
    148     fInnerThresholdVar = builder->addUniform(
    149         GrGLShaderBuilder::kFragment_Visibility,
    150         kFloat_GrSLType, "inner_threshold");
    151     fOuterThresholdVar = builder->addUniform(
    152         GrGLShaderBuilder::kFragment_Visibility,
    153         kFloat_GrSLType, "outer_threshold");
    154 
    155     builder->fsCodeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
    156     builder->fsCodeAppendf("\t\tvec2 mask_coord = %s;\n", maskCoords2D.c_str());
    157     builder->fsCodeAppend("\t\tvec4 input_color = ");
    158     builder->fsAppendTextureLookup(samplers[0], "coord");
    159     builder->fsCodeAppend(";\n");
    160     builder->fsCodeAppend("\t\tvec4 mask_color = ");
    161     builder->fsAppendTextureLookup(samplers[1], "mask_coord");
    162     builder->fsCodeAppend(";\n");
    163 
    164     builder->fsCodeAppendf("\t\tfloat inner_thresh = %s;\n",
    165                            builder->getUniformCStr(fInnerThresholdVar));
    166     builder->fsCodeAppendf("\t\tfloat outer_thresh = %s;\n",
    167                            builder->getUniformCStr(fOuterThresholdVar));
    168     builder->fsCodeAppend("\t\tfloat mask = mask_color.a;\n");
    169 
    170     builder->fsCodeAppend("vec4 color = input_color;\n");
    171     builder->fsCodeAppend("\t\tif (mask < 0.5) {\n"
    172                           "\t\t\tif (color.a > outer_thresh) {\n"
    173                           "\t\t\t\tfloat scale = outer_thresh / color.a;\n"
    174                           "\t\t\t\tcolor.rgb *= scale;\n"
    175                           "\t\t\t\tcolor.a = outer_thresh;\n"
    176                           "\t\t\t}\n"
    177                           "\t\t} else if (color.a < inner_thresh) {\n"
    178                           "\t\t\tfloat scale = inner_thresh / max(0.001, color.a);\n"
    179                           "\t\t\tcolor.rgb *= scale;\n"
    180                           "\t\t\tcolor.a = inner_thresh;\n"
    181                           "\t\t}\n");
    182 
    183     builder->fsCodeAppendf("%s = %s;\n", outputColor,
    184                            (GrGLSLExpr4(inputColor) * GrGLSLExpr4("color")).c_str());
    185 }
    186 
    187 void GrGLAlphaThresholdEffect::setData(const GrGLUniformManager& uman,
    188                                   const GrDrawEffect& drawEffect) {
    189     const AlphaThresholdEffect& alpha_threshold =
    190         drawEffect.castEffect<AlphaThresholdEffect>();
    191     uman.set1f(fInnerThresholdVar, alpha_threshold.innerThreshold());
    192     uman.set1f(fOuterThresholdVar, alpha_threshold.outerThreshold());
    193 }
    194 
    195 /////////////////////////////////////////////////////////////////////
    196 
    197 GR_DEFINE_EFFECT_TEST(AlphaThresholdEffect);
    198 
    199 GrEffectRef* AlphaThresholdEffect::TestCreate(SkRandom* random,
    200                                               GrContext* context,
    201                                               const GrDrawTargetCaps&,
    202                                               GrTexture** textures) {
    203     GrTexture* bmpTex = textures[GrEffectUnitTest::kSkiaPMTextureIdx];
    204     GrTexture* maskTex = textures[GrEffectUnitTest::kAlphaTextureIdx];
    205     float inner_thresh = random->nextUScalar1();
    206     float outer_thresh = random->nextUScalar1();
    207     return AlphaThresholdEffect::Create(bmpTex, maskTex, inner_thresh, outer_thresh);
    208 }
    209 
    210 ///////////////////////////////////////////////////////////////////////////////
    211 
    212 const GrBackendEffectFactory& AlphaThresholdEffect::getFactory() const {
    213     return GrTBackendEffectFactory<AlphaThresholdEffect>::getInstance();
    214 }
    215 
    216 bool AlphaThresholdEffect::onIsEqual(const GrEffect& sBase) const {
    217     const AlphaThresholdEffect& s = CastEffect<AlphaThresholdEffect>(sBase);
    218     return (this->texture(0) == s.texture(0) &&
    219             this->fInnerThreshold == s.fInnerThreshold &&
    220             this->fOuterThreshold == s.fOuterThreshold);
    221 }
    222 
    223 void AlphaThresholdEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
    224     if ((*validFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
    225         GrPixelConfigIsOpaque(this->texture(0)->config())) {
    226         *validFlags = kA_GrColorComponentFlag;
    227     } else {
    228         *validFlags = 0;
    229     }
    230 }
    231 
    232 #endif
    233 
    234 SkAlphaThresholdFilterImpl::SkAlphaThresholdFilterImpl(SkReadBuffer& buffer)
    235   : INHERITED(1, buffer) {
    236     fInnerThreshold = buffer.readScalar();
    237     fOuterThreshold = buffer.readScalar();
    238     buffer.readRegion(&fRegion);
    239 }
    240 
    241 SkAlphaThresholdFilterImpl::SkAlphaThresholdFilterImpl(const SkRegion& region,
    242                                                        SkScalar innerThreshold,
    243                                                        SkScalar outerThreshold)
    244     : INHERITED(0)
    245     , fRegion(region)
    246     , fInnerThreshold(innerThreshold)
    247     , fOuterThreshold(outerThreshold) {
    248 }
    249 
    250 #if SK_SUPPORT_GPU
    251 bool SkAlphaThresholdFilterImpl::asNewEffect(GrEffectRef** effect, GrTexture* texture,
    252                                              const SkMatrix& in_matrix, const SkIRect&) const {
    253     if (effect) {
    254         GrContext* context = texture->getContext();
    255         GrTextureDesc maskDesc;
    256         if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
    257             maskDesc.fConfig = kAlpha_8_GrPixelConfig;
    258         } else {
    259             maskDesc.fConfig = kRGBA_8888_GrPixelConfig;
    260         }
    261         maskDesc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
    262         // Add one pixel of border to ensure that clamp mode will be all zeros
    263         // the outside.
    264         maskDesc.fWidth = texture->width();
    265         maskDesc.fHeight = texture->height();
    266         GrAutoScratchTexture ast(context, maskDesc, GrContext::kApprox_ScratchTexMatch);
    267         GrTexture*  maskTexture = ast.texture();
    268         if (NULL == maskTexture) {
    269             return false;
    270         }
    271 
    272         {
    273             GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
    274             GrPaint grPaint;
    275             grPaint.setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff);
    276             SkRegion::Iterator iter(fRegion);
    277             context->clear(NULL, 0x0, true);
    278 
    279             SkMatrix old_matrix = context->getMatrix();
    280             context->setMatrix(in_matrix);
    281 
    282             while (!iter.done()) {
    283                 SkRect rect = SkRect::Make(iter.rect());
    284                 context->drawRect(grPaint, rect);
    285                 iter.next();
    286             }
    287             context->setMatrix(old_matrix);
    288         }
    289 
    290         *effect = AlphaThresholdEffect::Create(texture,
    291                                                maskTexture,
    292                                                fInnerThreshold,
    293                                                fOuterThreshold);
    294     }
    295     return true;
    296 }
    297 #endif
    298 
    299 void SkAlphaThresholdFilterImpl::flatten(SkWriteBuffer& buffer) const {
    300     this->INHERITED::flatten(buffer);
    301     buffer.writeScalar(fInnerThreshold);
    302     buffer.writeScalar(fOuterThreshold);
    303     buffer.writeRegion(fRegion);
    304 }
    305 
    306 bool SkAlphaThresholdFilterImpl::onFilterImage(Proxy*, const SkBitmap& src,
    307                                                const Context& ctx, SkBitmap* dst,
    308                                                SkIPoint* offset) const {
    309     SkASSERT(src.colorType() == kN32_SkColorType);
    310 
    311     if (src.colorType() != kN32_SkColorType) {
    312         return false;
    313     }
    314 
    315     SkMatrix localInverse;
    316     if (!ctx.ctm().invert(&localInverse)) {
    317         return false;
    318     }
    319 
    320     SkAutoLockPixels alp(src);
    321     SkASSERT(src.getPixels());
    322     if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) {
    323         return false;
    324     }
    325 
    326     if (!dst->allocPixels(src.info())) {
    327         return false;
    328     }
    329 
    330     U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF);
    331     U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF);
    332     SkColor* sptr = src.getAddr32(0, 0);
    333     SkColor* dptr = dst->getAddr32(0, 0);
    334     int width = src.width(), height = src.height();
    335     for (int y = 0; y < height; ++y) {
    336         for (int x = 0; x < width; ++x) {
    337             const SkColor& source = sptr[y * width + x];
    338             SkColor output_color(source);
    339             SkPoint position;
    340             localInverse.mapXY((SkScalar)x, (SkScalar)y, &position);
    341             if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) {
    342                 if (SkColorGetA(source) < innerThreshold) {
    343                     U8CPU alpha = SkColorGetA(source);
    344                     if (alpha == 0)
    345                         alpha = 1;
    346                     float scale = (float)innerThreshold / alpha;
    347                     output_color = SkColorSetARGB(innerThreshold,
    348                                                   (U8CPU)(SkColorGetR(source) * scale),
    349                                                   (U8CPU)(SkColorGetG(source) * scale),
    350                                                   (U8CPU)(SkColorGetB(source) * scale));
    351                 }
    352             } else {
    353                 if (SkColorGetA(source) > outerThreshold) {
    354                     float scale = (float)outerThreshold / SkColorGetA(source);
    355                     output_color = SkColorSetARGB(outerThreshold,
    356                                                   (U8CPU)(SkColorGetR(source) * scale),
    357                                                   (U8CPU)(SkColorGetG(source) * scale),
    358                                                   (U8CPU)(SkColorGetB(source) * scale));
    359                 }
    360             }
    361             dptr[y * dst->width() + x] = output_color;
    362         }
    363     }
    364 
    365     return true;
    366 }
    367