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