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