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