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 "SkBitmapAlphaThresholdShader.h"
      9 
     10 class BATShader : public SkShader {
     11 public:
     12     SK_DECLARE_INST_COUNT(BATShader);
     13 
     14     BATShader(const SkBitmap& bitmap, SkRegion region, U8CPU);
     15     BATShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
     16         // We should probably do something here.
     17     }
     18 
     19 
     20     virtual void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE {};
     21 
     22 #if SK_SUPPORT_GPU
     23     virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint& paint) const SK_OVERRIDE;
     24 #endif
     25 
     26     SK_DEVELOPER_TO_STRING();
     27     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(BATShader)
     28 
     29 private:
     30     SkBitmap fBitmap;
     31     SkRegion fRegion;
     32     U8CPU    fThreshold;
     33 
     34     typedef SkShader INHERITED;
     35 };
     36 
     37 SkShader* SkBitmapAlphaThresholdShader::Create(const SkBitmap& bitmap,
     38                                                const SkRegion& region,
     39                                                U8CPU threshold) {
     40     SkASSERT(threshold < 256);
     41     return SkNEW_ARGS(BATShader, (bitmap, region, threshold));
     42 }
     43 
     44 BATShader::BATShader(const SkBitmap& bitmap, SkRegion region, U8CPU threshold)
     45 : fBitmap(bitmap)
     46 , fRegion(region)
     47 , fThreshold(threshold) {
     48 };
     49 
     50 
     51 #ifdef SK_DEVELOPER
     52 void BATShader::toString(SkString* str) const {
     53     str->append("BATShader: (");
     54 
     55     fBitmap.toString(str);
     56 
     57     this->INHERITED::toString(str);
     58 
     59     str->append(")");
     60 }
     61 #endif
     62 
     63 #if SK_SUPPORT_GPU
     64 #include "GrContext.h"
     65 #include "GrCoordTransform.h"
     66 #include "GrEffect.h"
     67 #include "gl/GrGLEffect.h"
     68 #include "GrTBackendEffectFactory.h"
     69 #include "GrTextureAccess.h"
     70 
     71 #include "SkGr.h"
     72 
     73 /**
     74  * Could create specializations for some simple cases:
     75  *  - The region is empty.
     76  *  - The region fully contains the bitmap.
     77  *  - The regions is 1 rect (or maybe a small number of rects).
     78  */
     79 class ThresholdEffect : public GrEffect {
     80 public:
     81     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
     82         return GrTBackendEffectFactory<ThresholdEffect>::getInstance();
     83     }
     84 
     85     static GrEffectRef* Create(GrTexture* bmpTexture, const SkMatrix& bmpMatrix,
     86                                GrTexture* maskTexture, const SkMatrix& maskMatrix,
     87                                U8CPU threshold) {
     88         SkScalar thresh = SkIntToScalar(threshold) / 255;
     89 
     90         AutoEffectUnref effect(SkNEW_ARGS(ThresholdEffect, (bmpTexture, bmpMatrix,
     91                                                             maskTexture, maskMatrix,
     92                                                             thresh)));
     93         return CreateEffectRef(effect);
     94     }
     95 
     96     virtual void getConstantColorComponents(GrColor* color,
     97                                             uint32_t* validFlags) const SK_OVERRIDE {
     98         if ((kA_GrColorComponentFlag & *validFlags) && 0 == GrColorUnpackA(*color)) {
     99             return;
    100         }
    101         *validFlags = 0;
    102         return;
    103     }
    104 
    105     static const char* Name() { return "Bitmap Alpha Threshold"; }
    106 
    107     class GLEffect : public GrGLEffect {
    108     public:
    109         GLEffect(const GrBackendEffectFactory& factory,
    110                     const GrDrawEffect& e)
    111         : GrGLEffect(factory)
    112         , fPrevThreshold(-SK_Scalar1) {
    113         }
    114 
    115         virtual void emitCode(GrGLShaderBuilder* builder,
    116                               const GrDrawEffect& drawEffect,
    117                               EffectKey key,
    118                               const char* outputColor,
    119                               const char* inputColor,
    120                               const TransformedCoordsArray& coords,
    121                               const TextureSamplerArray& samplers) SK_OVERRIDE {
    122             // put bitmap color in "color"
    123             builder->fsCodeAppend("\t\tvec4 color = ");
    124             builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
    125             builder->fsCodeAppend(";\n");
    126 
    127             // put alpha from mask texture in "mask"
    128             builder->fsCodeAppend("\t\tfloat mask = ");
    129             builder->fsAppendTextureLookup(samplers[1], coords[1].c_str(), coords[1].type());
    130             builder->fsCodeAppend(".a;\n");
    131 
    132             const char* threshold;
    133 
    134             fThresholdUniHandle = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    135                                                       kFloat_GrSLType,
    136                                                       "threshold",
    137                                                       &threshold);
    138             builder->fsCodeAppendf("\t\tfloat thresh = %s;\n", threshold);
    139 
    140             builder->fsCodeAppend("\t\tif (mask < 0.5) {\n"
    141                                     "\t\t\tif (color.a > thresh) {\n"
    142                                     "\t\t\t\tfloat scale = thresh / color.a;\n"
    143                                     "\t\t\t\tcolor.rgb *= scale;\n"
    144                                     "\t\t\t\tcolor.a = thresh;\n"
    145                                     "\t\t\t}\n"
    146                                     "\t\t} else if (color.a < thresh) {\n"
    147                                     "\t\t\tfloat scale = thresh / color.a;\n"
    148                                     "\t\t\tcolor.rgb *= scale;\n"
    149                                     "\t\t\tcolor.a = thresh;\n"
    150                                     "\t\t}\n");
    151 
    152             builder->fsCodeAppendf("color = %s = %s;\n", outputColor,
    153                                    (GrGLSLExpr4(inputColor) * GrGLSLExpr4("color")).c_str());
    154         }
    155 
    156         virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& e) SK_OVERRIDE {
    157             const ThresholdEffect& effect = e.castEffect<ThresholdEffect>();
    158             if (fPrevThreshold != effect.fThreshold) {
    159                 uman.set1f(fThresholdUniHandle, effect.fThreshold);
    160             }
    161         }
    162 
    163     private:
    164         GrGLUniformManager::UniformHandle fThresholdUniHandle;
    165         SkScalar                          fPrevThreshold;
    166     };
    167 
    168     GR_DECLARE_EFFECT_TEST;
    169 
    170 private:
    171     ThresholdEffect(GrTexture* bmpTexture, const SkMatrix& bmpMatrix,
    172                     GrTexture* maskTexture, const SkMatrix& maskMatrix,
    173                     SkScalar threshold)
    174     : fBmpTransform(kLocal_GrCoordSet, bmpMatrix, bmpTexture)
    175     , fBmpAccess(bmpTexture, GrTextureParams())
    176     , fMaskTransform(kLocal_GrCoordSet, maskMatrix, maskTexture)
    177     , fMaskAccess(maskTexture, GrTextureParams())
    178     , fThreshold(threshold) {
    179         this->addCoordTransform(&fBmpTransform);
    180         this->addTextureAccess(&fBmpAccess);
    181         this->addCoordTransform(&fMaskTransform);
    182         this->addTextureAccess(&fMaskAccess);
    183     }
    184 
    185     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
    186         const ThresholdEffect& e = CastEffect<ThresholdEffect>(other);
    187         return e.fBmpAccess.getTexture() == fBmpAccess.getTexture() &&
    188                e.fMaskAccess.getTexture() == fMaskAccess.getTexture() &&
    189                e.fBmpTransform.getMatrix() == fBmpTransform.getMatrix() &&
    190                e.fMaskTransform.getMatrix() == fMaskTransform.getMatrix() &&
    191                e.fThreshold == fThreshold;
    192     }
    193 
    194     GrCoordTransform fBmpTransform;
    195     GrTextureAccess  fBmpAccess;
    196     GrCoordTransform fMaskTransform;
    197     GrTextureAccess  fMaskAccess;
    198 
    199     SkScalar fThreshold;
    200 };
    201 
    202 GR_DEFINE_EFFECT_TEST(ThresholdEffect);
    203 
    204 GrEffectRef* ThresholdEffect::TestCreate(SkRandom* rand,
    205                                          GrContext*,
    206                                          const GrDrawTargetCaps&,
    207                                          GrTexture* textures[]) {
    208     GrTexture* bmpTex = textures[GrEffectUnitTest::kSkiaPMTextureIdx];
    209     GrTexture* maskTex = textures[GrEffectUnitTest::kAlphaTextureIdx];
    210     U8CPU thresh = rand->nextU() % 0xff;
    211     return ThresholdEffect::Create(bmpTex, SkMatrix::I(), maskTex, SkMatrix::I(), thresh);
    212 }
    213 
    214 GrEffectRef* BATShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
    215     SkMatrix localInverse;
    216     if (!this->getLocalMatrix().invert(&localInverse)) {
    217         return NULL;
    218     }
    219 
    220     GrTextureDesc maskDesc;
    221     if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
    222         maskDesc.fConfig = kAlpha_8_GrPixelConfig;
    223     } else {
    224         maskDesc.fConfig = kRGBA_8888_GrPixelConfig;
    225     }
    226     maskDesc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
    227     const SkIRect& bounds = fRegion.getBounds();
    228     // Add one pixel of border to ensure that clamp mode will be all zeros
    229     // the outside.
    230     maskDesc.fWidth = bounds.width() + 2;
    231     maskDesc.fHeight = bounds.height() + 2;
    232     GrAutoScratchTexture ast(context, maskDesc, GrContext::kApprox_ScratchTexMatch);
    233     GrTexture*  maskTexture = ast.texture();
    234     if (NULL == maskTexture) {
    235         return NULL;
    236     }
    237 
    238     GrPaint grPaint;
    239     grPaint.setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff);
    240     SkRegion::Iterator iter(fRegion);
    241     context->setRenderTarget(maskTexture->asRenderTarget());
    242     context->clear(NULL, 0x0, true);
    243 
    244     // offset to ensure border is zero on top/left
    245     SkMatrix matrix;
    246     matrix.setTranslate(SK_Scalar1, SK_Scalar1);
    247     context->setMatrix(matrix);
    248 
    249     while (!iter.done()) {
    250         SkRect rect = SkRect::Make(iter.rect());
    251         context->drawRect(grPaint, rect);
    252         iter.next();
    253     }
    254 
    255     GrTexture* bmpTexture = GrLockAndRefCachedBitmapTexture(context, fBitmap, NULL);
    256     if (NULL == bmpTexture) {
    257         return NULL;
    258     }
    259 
    260     SkMatrix bmpMatrix = localInverse;
    261     bmpMatrix.postIDiv(bmpTexture->width(), bmpTexture->height());
    262 
    263     SkMatrix maskMatrix = localInverse;
    264     // compensate for the border
    265     maskMatrix.postTranslate(SK_Scalar1, SK_Scalar1);
    266     maskMatrix.postIDiv(maskTexture->width(), maskTexture->height());
    267 
    268     GrEffectRef* effect = ThresholdEffect::Create(bmpTexture, bmpMatrix,
    269                                                   maskTexture, maskMatrix,
    270                                                   fThreshold);
    271 
    272     GrUnlockAndUnrefCachedBitmapTexture(bmpTexture);
    273 
    274     return effect;
    275 }
    276 
    277 #endif
    278