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