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)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAlphaThresholdFilterImpl)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
Make(const SkRegion & region,SkScalar innerThreshold,SkScalar outerThreshold,sk_sp<SkImageFilter> input,const SkImageFilter::CropRect * cropRect)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
CreateProc(SkReadBuffer & buffer)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
SkAlphaThresholdFilterImpl(const SkRegion & region,SkScalar innerThreshold,SkScalar outerThreshold,sk_sp<SkImageFilter> input,const CropRect * cropRect)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
createMaskTexture(GrContext * context,const SkMatrix & inMatrix,const SkIRect & bounds) const105 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
flatten(SkWriteBuffer & buffer) const131 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
onFilterImage(SkSpecialImage * source,const Context & ctx,SkIPoint * offset) const138 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
onMakeColorSpace(SkColorSpaceXformer * xformer) const272 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
toString(SkString * str) const284 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