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 
24 class SK_API SkAlphaThresholdFilterImpl : public SkImageFilter {
25 public:
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 
34 protected:
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 
46 private:
47     SkRegion fRegion;
48     SkScalar fInnerThreshold;
49     SkScalar fOuterThreshold;
50     typedef SkImageFilter INHERITED;
51 };
52 
53 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAlphaThresholdFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAlphaThresholdFilterImpl)54     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAlphaThresholdFilterImpl)
55 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
56 
57 static SkScalar pin_0_1(SkScalar x) {
58     return SkMinScalar(SkMaxScalar(x, 0), 1);
59 }
60 
Make(const SkRegion & region,SkScalar innerThreshold,SkScalar outerThreshold,sk_sp<SkImageFilter> input,const SkImageFilter::CropRect * cropRect)61 sk_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 
CreateProc(SkReadBuffer & buffer)77 sk_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 
SkAlphaThresholdFilterImpl(const SkRegion & region,SkScalar innerThreshold,SkScalar outerThreshold,sk_sp<SkImageFilter> input,const CropRect * cropRect)87 SkAlphaThresholdFilterImpl::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
createMaskTexture(GrContext * context,const SkMatrix & inMatrix,const SkIRect & bounds) const99 sk_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 
flatten(SkWriteBuffer & buffer) const125 void SkAlphaThresholdFilterImpl::flatten(SkWriteBuffer& buffer) const {
126     this->INHERITED::flatten(buffer);
127     buffer.writeScalar(fInnerThreshold);
128     buffer.writeScalar(fOuterThreshold);
129     buffer.writeRegion(fRegion);
130 }
131 
onFilterImage(SkSpecialImage * source,const Context & ctx,SkIPoint * offset) const132 sk_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
toString(SkString * str) const266 void 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