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