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