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 10 #include "SkBitmap.h" 11 #include "SkColorSpaceXformer.h" 12 #include "SkImageFilterPriv.h" 13 #include "SkReadBuffer.h" 14 #include "SkSpecialImage.h" 15 #include "SkWriteBuffer.h" 16 #include "SkRegion.h" 17 18 #if SK_SUPPORT_GPU 19 #include "GrAlphaThresholdFragmentProcessor.h" 20 #include "GrColorSpaceXform.h" 21 #include "GrContext.h" 22 #include "GrFixedClip.h" 23 #include "GrRenderTargetContext.h" 24 #include "GrTextureProxy.h" 25 #include "effects/GrSimpleTextureEffect.h" 26 #endif 27 28 class SkAlphaThresholdFilterImpl : public SkImageFilter { 29 public: 30 SkAlphaThresholdFilterImpl(const SkRegion& region, SkScalar innerThreshold, 31 SkScalar outerThreshold, sk_sp<SkImageFilter> input, 32 const CropRect* cropRect = nullptr); 33 34 SK_TO_STRING_OVERRIDE() 35 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAlphaThresholdFilterImpl) 36 friend void SkAlphaThresholdFilter::InitializeFlattenables(); 37 38 protected: 39 void flatten(SkWriteBuffer&) const override; 40 41 sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&, 42 SkIPoint* offset) const override; 43 44 sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override; 45 46 #if SK_SUPPORT_GPU 47 sk_sp<GrTextureProxy> createMaskTexture(GrContext*, 48 const SkMatrix&, 49 const SkIRect& bounds) const; 50 #endif 51 52 private: 53 SkRegion fRegion; 54 SkScalar fInnerThreshold; 55 SkScalar fOuterThreshold; 56 typedef SkImageFilter INHERITED; 57 }; 58 59 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAlphaThresholdFilter) 60 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAlphaThresholdFilterImpl) 61 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 62 63 static SkScalar pin_0_1(SkScalar x) { 64 return SkMinScalar(SkMaxScalar(x, 0), 1); 65 } 66 67 sk_sp<SkImageFilter> SkAlphaThresholdFilter::Make(const SkRegion& region, 68 SkScalar innerThreshold, 69 SkScalar outerThreshold, 70 sk_sp<SkImageFilter> input, 71 const SkImageFilter::CropRect* cropRect) { 72 innerThreshold = pin_0_1(innerThreshold); 73 outerThreshold = pin_0_1(outerThreshold); 74 if (!SkScalarIsFinite(innerThreshold) || !SkScalarIsFinite(outerThreshold)) { 75 return nullptr; 76 } 77 return sk_sp<SkImageFilter>(new SkAlphaThresholdFilterImpl(region, innerThreshold, 78 outerThreshold, 79 std::move(input), 80 cropRect)); 81 } 82 83 sk_sp<SkFlattenable> SkAlphaThresholdFilterImpl::CreateProc(SkReadBuffer& buffer) { 84 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 85 SkScalar inner = buffer.readScalar(); 86 SkScalar outer = buffer.readScalar(); 87 SkRegion rgn; 88 buffer.readRegion(&rgn); 89 return SkAlphaThresholdFilter::Make(rgn, inner, outer, common.getInput(0), 90 &common.cropRect()); 91 } 92 93 SkAlphaThresholdFilterImpl::SkAlphaThresholdFilterImpl(const SkRegion& region, 94 SkScalar innerThreshold, 95 SkScalar outerThreshold, 96 sk_sp<SkImageFilter> input, 97 const CropRect* cropRect) 98 : INHERITED(&input, 1, cropRect) 99 , fRegion(region) 100 , fInnerThreshold(innerThreshold) 101 , fOuterThreshold(outerThreshold) { 102 } 103 104 #if SK_SUPPORT_GPU 105 sk_sp<GrTextureProxy> SkAlphaThresholdFilterImpl::createMaskTexture(GrContext* context, 106 const SkMatrix& inMatrix, 107 const SkIRect& bounds) const { 108 109 sk_sp<GrRenderTargetContext> rtContext(context->makeDeferredRenderTargetContextWithFallback( 110 SkBackingFit::kApprox, bounds.width(), bounds.height(), kAlpha_8_GrPixelConfig, nullptr)); 111 if (!rtContext) { 112 return nullptr; 113 } 114 115 GrPaint paint; 116 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 117 SkRegion::Iterator iter(fRegion); 118 rtContext->clear(nullptr, 0x0, GrRenderTargetContext::CanClearFullscreen::kYes); 119 120 GrFixedClip clip(SkIRect::MakeWH(bounds.width(), bounds.height())); 121 while (!iter.done()) { 122 SkRect rect = SkRect::Make(iter.rect()); 123 rtContext->drawRect(clip, std::move(paint), GrAA::kNo, inMatrix, rect); 124 iter.next(); 125 } 126 127 return rtContext->asTextureProxyRef(); 128 } 129 #endif 130 131 void SkAlphaThresholdFilterImpl::flatten(SkWriteBuffer& buffer) const { 132 this->INHERITED::flatten(buffer); 133 buffer.writeScalar(fInnerThreshold); 134 buffer.writeScalar(fOuterThreshold); 135 buffer.writeRegion(fRegion); 136 } 137 138 sk_sp<SkSpecialImage> SkAlphaThresholdFilterImpl::onFilterImage(SkSpecialImage* source, 139 const Context& ctx, 140 SkIPoint* offset) const { 141 SkIPoint inputOffset = SkIPoint::Make(0, 0); 142 sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); 143 if (!input) { 144 return nullptr; 145 } 146 147 const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(), 148 input->width(), input->height()); 149 150 SkIRect bounds; 151 if (!this->applyCropRect(ctx, inputBounds, &bounds)) { 152 return nullptr; 153 } 154 155 #if SK_SUPPORT_GPU 156 if (source->isTextureBacked()) { 157 GrContext* context = source->getContext(); 158 159 sk_sp<GrTextureProxy> inputProxy(input->asTextureProxyRef(context)); 160 SkASSERT(inputProxy); 161 162 offset->fX = bounds.left(); 163 offset->fY = bounds.top(); 164 165 bounds.offset(-inputOffset); 166 167 SkMatrix matrix(ctx.ctm()); 168 matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); 169 170 sk_sp<GrTextureProxy> maskProxy(this->createMaskTexture(context, matrix, bounds)); 171 if (!maskProxy) { 172 return nullptr; 173 } 174 175 const OutputProperties& outProps = ctx.outputProperties(); 176 GrPixelConfig inputConfig = inputProxy->config(); 177 auto textureFP = GrSimpleTextureEffect::Make(std::move(inputProxy), SkMatrix::I()); 178 textureFP = GrColorSpaceXformEffect::Make(std::move(textureFP), input->getColorSpace(), 179 inputConfig, outProps.colorSpace()); 180 if (!textureFP) { 181 return nullptr; 182 } 183 184 auto thresholdFP = GrAlphaThresholdFragmentProcessor::Make(std::move(maskProxy), 185 fInnerThreshold, 186 fOuterThreshold, 187 bounds); 188 if (!thresholdFP) { 189 return nullptr; 190 } 191 192 std::unique_ptr<GrFragmentProcessor> fpSeries[] = { std::move(textureFP), 193 std::move(thresholdFP) }; 194 auto fp = GrFragmentProcessor::RunInSeries(fpSeries, 2); 195 196 return DrawWithFP(context, std::move(fp), bounds, outProps); 197 } 198 #endif 199 200 SkBitmap inputBM; 201 202 if (!input->getROPixels(&inputBM)) { 203 return nullptr; 204 } 205 206 if (inputBM.colorType() != kN32_SkColorType) { 207 return nullptr; 208 } 209 210 if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) { 211 return nullptr; 212 } 213 214 215 SkMatrix localInverse; 216 if (!ctx.ctm().invert(&localInverse)) { 217 return nullptr; 218 } 219 220 SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(), 221 kPremul_SkAlphaType); 222 223 SkBitmap dst; 224 if (!dst.tryAllocPixels(info)) { 225 return nullptr; 226 } 227 228 U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF); 229 U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF); 230 SkColor* dptr = dst.getAddr32(0, 0); 231 int dstWidth = dst.width(), dstHeight = dst.height(); 232 SkIPoint srcOffset = { bounds.fLeft - inputOffset.fX, bounds.fTop - inputOffset.fY }; 233 for (int y = 0; y < dstHeight; ++y) { 234 const SkColor* sptr = inputBM.getAddr32(srcOffset.fX, srcOffset.fY+y); 235 236 for (int x = 0; x < dstWidth; ++x) { 237 const SkColor& source = sptr[x]; 238 SkColor outputColor(source); 239 SkPoint position; 240 localInverse.mapXY((SkScalar)x + bounds.fLeft, (SkScalar)y + bounds.fTop, &position); 241 if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) { 242 if (SkColorGetA(source) < innerThreshold) { 243 U8CPU alpha = SkColorGetA(source); 244 if (alpha == 0) { 245 alpha = 1; 246 } 247 float scale = (float)innerThreshold / alpha; 248 outputColor = SkColorSetARGB(innerThreshold, 249 (U8CPU)(SkColorGetR(source) * scale), 250 (U8CPU)(SkColorGetG(source) * scale), 251 (U8CPU)(SkColorGetB(source) * scale)); 252 } 253 } else { 254 if (SkColorGetA(source) > outerThreshold) { 255 float scale = (float)outerThreshold / SkColorGetA(source); 256 outputColor = SkColorSetARGB(outerThreshold, 257 (U8CPU)(SkColorGetR(source) * scale), 258 (U8CPU)(SkColorGetG(source) * scale), 259 (U8CPU)(SkColorGetB(source) * scale)); 260 } 261 } 262 dptr[y * dstWidth + x] = outputColor; 263 } 264 } 265 266 offset->fX = bounds.left(); 267 offset->fY = bounds.top(); 268 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), 269 dst); 270 } 271 272 sk_sp<SkImageFilter> SkAlphaThresholdFilterImpl::onMakeColorSpace(SkColorSpaceXformer* xformer) 273 const { 274 SkASSERT(1 == this->countInputs()); 275 sk_sp<SkImageFilter> input = xformer->apply(this->getInput(0)); 276 if (input.get() != this->getInput(0)) { 277 return SkAlphaThresholdFilter::Make(fRegion, fInnerThreshold, fOuterThreshold, 278 std::move(input), this->getCropRectIfSet()); 279 } 280 return this->refMe(); 281 } 282 283 #ifndef SK_IGNORE_TO_STRING 284 void SkAlphaThresholdFilterImpl::toString(SkString* str) const { 285 str->appendf("SkAlphaThresholdImageFilter: ("); 286 str->appendf("inner: %f outer: %f", fInnerThreshold, fOuterThreshold); 287 str->append(")"); 288 } 289 #endif 290