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