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 "SkDevice.h" 11 #include "SkReadBuffer.h" 12 #include "SkWriteBuffer.h" 13 #include "SkRegion.h" 14 #if SK_SUPPORT_GPU 15 #include "GrDrawContext.h" 16 #endif 17 18 class SK_API SkAlphaThresholdFilterImpl : public SkImageFilter { 19 public: 20 SkAlphaThresholdFilterImpl(const SkRegion& region, SkScalar innerThreshold, 21 SkScalar outerThreshold, SkImageFilter* input); 22 23 SK_TO_STRING_OVERRIDE() 24 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAlphaThresholdFilterImpl) 25 friend void SkAlphaThresholdFilter::InitializeFlattenables(); 26 27 protected: 28 void flatten(SkWriteBuffer&) const override; 29 30 bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, 31 SkBitmap* result, SkIPoint* offset) const override; 32 #if SK_SUPPORT_GPU 33 bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&, 34 const SkIRect& bounds) const override; 35 #endif 36 37 private: 38 SkRegion fRegion; 39 SkScalar fInnerThreshold; 40 SkScalar fOuterThreshold; 41 typedef SkImageFilter INHERITED; 42 }; 43 44 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAlphaThresholdFilter) 45 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAlphaThresholdFilterImpl) 46 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 47 48 static SkScalar pin_0_1(SkScalar x) { 49 return SkMinScalar(SkMaxScalar(x, 0), 1); 50 } 51 52 SkImageFilter* SkAlphaThresholdFilter::Create(const SkRegion& region, 53 SkScalar innerThreshold, 54 SkScalar outerThreshold, 55 SkImageFilter* input) { 56 innerThreshold = pin_0_1(innerThreshold); 57 outerThreshold = pin_0_1(outerThreshold); 58 if (!SkScalarIsFinite(innerThreshold) || !SkScalarIsFinite(outerThreshold)) { 59 return nullptr; 60 } 61 return new SkAlphaThresholdFilterImpl(region, innerThreshold, outerThreshold, input); 62 } 63 64 #if SK_SUPPORT_GPU 65 #include "GrContext.h" 66 #include "GrCoordTransform.h" 67 #include "GrFragmentProcessor.h" 68 #include "GrInvariantOutput.h" 69 #include "GrTextureAccess.h" 70 #include "effects/GrPorterDuffXferProcessor.h" 71 72 #include "SkGr.h" 73 74 #include "glsl/GrGLSLFragmentProcessor.h" 75 #include "glsl/GrGLSLFragmentShaderBuilder.h" 76 #include "glsl/GrGLSLProgramDataManager.h" 77 #include "glsl/GrGLSLUniformHandler.h" 78 79 namespace { 80 81 SkMatrix make_div_and_translate_matrix(GrTexture* texture, int x, int y) { 82 SkMatrix matrix = GrCoordTransform::MakeDivByTextureWHMatrix(texture); 83 matrix.preTranslate(SkIntToScalar(x), SkIntToScalar(y)); 84 return matrix; 85 } 86 87 }; 88 89 class AlphaThresholdEffect : public GrFragmentProcessor { 90 91 public: 92 static GrFragmentProcessor* Create(GrTexture* texture, 93 GrTexture* maskTexture, 94 float innerThreshold, 95 float outerThreshold, 96 const SkIRect& bounds) { 97 return new AlphaThresholdEffect(texture, maskTexture, innerThreshold, outerThreshold, 98 bounds); 99 } 100 101 virtual ~AlphaThresholdEffect() {}; 102 103 const char* name() const override { return "Alpha Threshold"; } 104 105 float innerThreshold() const { return fInnerThreshold; } 106 float outerThreshold() const { return fOuterThreshold; } 107 108 private: 109 AlphaThresholdEffect(GrTexture* texture, 110 GrTexture* maskTexture, 111 float innerThreshold, 112 float outerThreshold, 113 const SkIRect& bounds) 114 : fInnerThreshold(innerThreshold) 115 , fOuterThreshold(outerThreshold) 116 , fImageCoordTransform(kLocal_GrCoordSet, 117 GrCoordTransform::MakeDivByTextureWHMatrix(texture), texture, 118 GrTextureParams::kNone_FilterMode) 119 , fImageTextureAccess(texture) 120 , fMaskCoordTransform(kLocal_GrCoordSet, 121 make_div_and_translate_matrix(maskTexture, -bounds.x(), -bounds.y()), 122 maskTexture, 123 GrTextureParams::kNone_FilterMode) 124 , fMaskTextureAccess(maskTexture) { 125 this->initClassID<AlphaThresholdEffect>(); 126 this->addCoordTransform(&fImageCoordTransform); 127 this->addTextureAccess(&fImageTextureAccess); 128 this->addCoordTransform(&fMaskCoordTransform); 129 this->addTextureAccess(&fMaskTextureAccess); 130 } 131 132 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 133 134 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override; 135 136 bool onIsEqual(const GrFragmentProcessor&) const override; 137 138 void onComputeInvariantOutput(GrInvariantOutput* inout) const override; 139 140 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 141 142 float fInnerThreshold; 143 float fOuterThreshold; 144 GrCoordTransform fImageCoordTransform; 145 GrTextureAccess fImageTextureAccess; 146 GrCoordTransform fMaskCoordTransform; 147 GrTextureAccess fMaskTextureAccess; 148 149 typedef GrFragmentProcessor INHERITED; 150 }; 151 152 class GrGLAlphaThresholdEffect : public GrGLSLFragmentProcessor { 153 public: 154 void emitCode(EmitArgs&) override; 155 156 protected: 157 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override; 158 159 private: 160 GrGLSLProgramDataManager::UniformHandle fInnerThresholdVar; 161 GrGLSLProgramDataManager::UniformHandle fOuterThresholdVar; 162 163 typedef GrGLSLFragmentProcessor INHERITED; 164 }; 165 166 void GrGLAlphaThresholdEffect::emitCode(EmitArgs& args) { 167 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 168 fInnerThresholdVar = uniformHandler->addUniform(kFragment_GrShaderFlag, 169 kFloat_GrSLType, kDefault_GrSLPrecision, 170 "inner_threshold"); 171 fOuterThresholdVar = uniformHandler->addUniform(kFragment_GrShaderFlag, 172 kFloat_GrSLType, kDefault_GrSLPrecision, 173 "outer_threshold"); 174 175 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 176 SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); 177 SkString maskCoords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 1); 178 179 fragBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); 180 fragBuilder->codeAppendf("\t\tvec2 mask_coord = %s;\n", maskCoords2D.c_str()); 181 fragBuilder->codeAppend("\t\tvec4 input_color = "); 182 fragBuilder->appendTextureLookup(args.fSamplers[0], "coord"); 183 fragBuilder->codeAppend(";\n"); 184 fragBuilder->codeAppend("\t\tvec4 mask_color = "); 185 fragBuilder->appendTextureLookup(args.fSamplers[1], "mask_coord"); 186 fragBuilder->codeAppend(";\n"); 187 188 fragBuilder->codeAppendf("\t\tfloat inner_thresh = %s;\n", 189 uniformHandler->getUniformCStr(fInnerThresholdVar)); 190 fragBuilder->codeAppendf("\t\tfloat outer_thresh = %s;\n", 191 uniformHandler->getUniformCStr(fOuterThresholdVar)); 192 fragBuilder->codeAppend("\t\tfloat mask = mask_color.a;\n"); 193 194 fragBuilder->codeAppend("vec4 color = input_color;\n"); 195 fragBuilder->codeAppend("\t\tif (mask < 0.5) {\n" 196 "\t\t\tif (color.a > outer_thresh) {\n" 197 "\t\t\t\tfloat scale = outer_thresh / color.a;\n" 198 "\t\t\t\tcolor.rgb *= scale;\n" 199 "\t\t\t\tcolor.a = outer_thresh;\n" 200 "\t\t\t}\n" 201 "\t\t} else if (color.a < inner_thresh) {\n" 202 "\t\t\tfloat scale = inner_thresh / max(0.001, color.a);\n" 203 "\t\t\tcolor.rgb *= scale;\n" 204 "\t\t\tcolor.a = inner_thresh;\n" 205 "\t\t}\n"); 206 207 fragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, 208 (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr4("color")).c_str()); 209 } 210 211 void GrGLAlphaThresholdEffect::onSetData(const GrGLSLProgramDataManager& pdman, 212 const GrProcessor& proc) { 213 const AlphaThresholdEffect& alpha_threshold = proc.cast<AlphaThresholdEffect>(); 214 pdman.set1f(fInnerThresholdVar, alpha_threshold.innerThreshold()); 215 pdman.set1f(fOuterThresholdVar, alpha_threshold.outerThreshold()); 216 } 217 218 ///////////////////////////////////////////////////////////////////// 219 220 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(AlphaThresholdEffect); 221 222 const GrFragmentProcessor* AlphaThresholdEffect::TestCreate(GrProcessorTestData* d) { 223 GrTexture* bmpTex = d->fTextures[GrProcessorUnitTest::kSkiaPMTextureIdx]; 224 GrTexture* maskTex = d->fTextures[GrProcessorUnitTest::kAlphaTextureIdx]; 225 float innerThresh = d->fRandom->nextUScalar1(); 226 float outerThresh = d->fRandom->nextUScalar1(); 227 const int kMaxWidth = 1000; 228 const int kMaxHeight = 1000; 229 uint32_t width = d->fRandom->nextULessThan(kMaxWidth); 230 uint32_t height = d->fRandom->nextULessThan(kMaxHeight); 231 uint32_t x = d->fRandom->nextULessThan(kMaxWidth - width); 232 uint32_t y = d->fRandom->nextULessThan(kMaxHeight - height); 233 SkIRect bounds = SkIRect::MakeXYWH(x, y, width, height); 234 return AlphaThresholdEffect::Create(bmpTex, maskTex, innerThresh, outerThresh, bounds); 235 } 236 237 /////////////////////////////////////////////////////////////////////////////// 238 239 void AlphaThresholdEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps, 240 GrProcessorKeyBuilder* b) const { 241 GrGLAlphaThresholdEffect::GenKey(*this, caps, b); 242 } 243 244 GrGLSLFragmentProcessor* AlphaThresholdEffect::onCreateGLSLInstance() const { 245 return new GrGLAlphaThresholdEffect; 246 } 247 248 bool AlphaThresholdEffect::onIsEqual(const GrFragmentProcessor& sBase) const { 249 const AlphaThresholdEffect& s = sBase.cast<AlphaThresholdEffect>(); 250 return (this->fInnerThreshold == s.fInnerThreshold && 251 this->fOuterThreshold == s.fOuterThreshold); 252 } 253 254 void AlphaThresholdEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { 255 if (GrPixelConfigIsAlphaOnly(this->texture(0)->config())) { 256 inout->mulByUnknownSingleComponent(); 257 } else if (GrPixelConfigIsOpaque(this->texture(0)->config()) && fOuterThreshold >= 1.f) { 258 inout->mulByUnknownOpaqueFourComponents(); 259 } else { 260 inout->mulByUnknownFourComponents(); 261 } 262 } 263 264 #endif 265 266 SkFlattenable* SkAlphaThresholdFilterImpl::CreateProc(SkReadBuffer& buffer) { 267 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 268 SkScalar inner = buffer.readScalar(); 269 SkScalar outer = buffer.readScalar(); 270 SkRegion rgn; 271 buffer.readRegion(&rgn); 272 return SkAlphaThresholdFilter::Create(rgn, inner, outer, common.getInput(0)); 273 } 274 275 SkAlphaThresholdFilterImpl::SkAlphaThresholdFilterImpl(const SkRegion& region, 276 SkScalar innerThreshold, 277 SkScalar outerThreshold, 278 SkImageFilter* input) 279 : INHERITED(1, &input) 280 , fRegion(region) 281 , fInnerThreshold(innerThreshold) 282 , fOuterThreshold(outerThreshold) { 283 } 284 285 #if SK_SUPPORT_GPU 286 bool SkAlphaThresholdFilterImpl::asFragmentProcessor(GrFragmentProcessor** fp, 287 GrTexture* texture, 288 const SkMatrix& inMatrix, 289 const SkIRect& bounds) const { 290 if (fp) { 291 GrContext* context = texture->getContext(); 292 GrSurfaceDesc maskDesc; 293 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { 294 maskDesc.fConfig = kAlpha_8_GrPixelConfig; 295 } else { 296 maskDesc.fConfig = kRGBA_8888_GrPixelConfig; 297 } 298 maskDesc.fFlags = kRenderTarget_GrSurfaceFlag; 299 // Add one pixel of border to ensure that clamp mode will be all zeros 300 // the outside. 301 maskDesc.fWidth = bounds.width(); 302 maskDesc.fHeight = bounds.height(); 303 SkAutoTUnref<GrTexture> maskTexture( 304 context->textureProvider()->createApproxTexture(maskDesc)); 305 if (!maskTexture) { 306 return false; 307 } 308 309 SkAutoTUnref<GrDrawContext> drawContext( 310 context->drawContext(maskTexture->asRenderTarget())); 311 if (drawContext) { 312 GrPaint grPaint; 313 grPaint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); 314 SkRegion::Iterator iter(fRegion); 315 drawContext->clear(nullptr, 0x0, true); 316 317 GrClip clip(SkRect::Make(SkIRect::MakeWH(bounds.width(), bounds.height()))); 318 while (!iter.done()) { 319 SkRect rect = SkRect::Make(iter.rect()); 320 drawContext->drawRect(clip, grPaint, inMatrix, rect); 321 iter.next(); 322 } 323 } 324 325 *fp = AlphaThresholdEffect::Create(texture, 326 maskTexture, 327 fInnerThreshold, 328 fOuterThreshold, 329 bounds); 330 } 331 return true; 332 } 333 #endif 334 335 void SkAlphaThresholdFilterImpl::flatten(SkWriteBuffer& buffer) const { 336 this->INHERITED::flatten(buffer); 337 buffer.writeScalar(fInnerThreshold); 338 buffer.writeScalar(fOuterThreshold); 339 buffer.writeRegion(fRegion); 340 } 341 342 bool SkAlphaThresholdFilterImpl::onFilterImageDeprecated(Proxy* proxy, const SkBitmap& src, 343 const Context& ctx, SkBitmap* dst, 344 SkIPoint* offset) const { 345 346 if (src.colorType() != kN32_SkColorType) { 347 return false; 348 } 349 350 SkMatrix localInverse; 351 if (!ctx.ctm().invert(&localInverse)) { 352 return false; 353 } 354 355 SkAutoLockPixels alp(src); 356 SkASSERT(src.getPixels()); 357 if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) { 358 return false; 359 } 360 361 SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(src.width(), src.height())); 362 if (!device) { 363 return false; 364 } 365 *dst = device->accessBitmap(false); 366 SkAutoLockPixels alp_dst(*dst); 367 368 U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF); 369 U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF); 370 SkColor* sptr = src.getAddr32(0, 0); 371 SkColor* dptr = dst->getAddr32(0, 0); 372 int width = src.width(), height = src.height(); 373 for (int y = 0; y < height; ++y) { 374 for (int x = 0; x < width; ++x) { 375 const SkColor& source = sptr[y * width + x]; 376 SkColor output_color(source); 377 SkPoint position; 378 localInverse.mapXY((SkScalar)x, (SkScalar)y, &position); 379 if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) { 380 if (SkColorGetA(source) < innerThreshold) { 381 U8CPU alpha = SkColorGetA(source); 382 if (alpha == 0) 383 alpha = 1; 384 float scale = (float)innerThreshold / alpha; 385 output_color = SkColorSetARGB(innerThreshold, 386 (U8CPU)(SkColorGetR(source) * scale), 387 (U8CPU)(SkColorGetG(source) * scale), 388 (U8CPU)(SkColorGetB(source) * scale)); 389 } 390 } else { 391 if (SkColorGetA(source) > outerThreshold) { 392 float scale = (float)outerThreshold / SkColorGetA(source); 393 output_color = SkColorSetARGB(outerThreshold, 394 (U8CPU)(SkColorGetR(source) * scale), 395 (U8CPU)(SkColorGetG(source) * scale), 396 (U8CPU)(SkColorGetB(source) * scale)); 397 } 398 } 399 dptr[y * dst->width() + x] = output_color; 400 } 401 } 402 403 return true; 404 } 405 406 #ifndef SK_IGNORE_TO_STRING 407 void SkAlphaThresholdFilterImpl::toString(SkString* str) const { 408 str->appendf("SkAlphaThresholdImageFilter: ("); 409 str->appendf("inner: %f outer: %f", fInnerThreshold, fOuterThreshold); 410 str->append(")"); 411 } 412 #endif 413 414