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