• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/core/SkBitmap.h"
9 #include "include/core/SkRegion.h"
10 #include "include/effects/SkImageFilters.h"
11 #include "include/private/SkTPin.h"
12 #include "src/core/SkImageFilter_Base.h"
13 #include "src/core/SkReadBuffer.h"
14 #include "src/core/SkSpecialImage.h"
15 #include "src/core/SkWriteBuffer.h"
16 
17 #if SK_SUPPORT_GPU
18 #include "include/gpu/GrRecordingContext.h"
19 #include "src/core/SkRuntimeEffectPriv.h"
20 #include "src/gpu/GrCaps.h"
21 #include "src/gpu/GrColorSpaceXform.h"
22 #include "src/gpu/GrRecordingContextPriv.h"
23 #include "src/gpu/GrTextureProxy.h"
24 #include "src/gpu/effects/GrSkSLFP.h"
25 #include "src/gpu/effects/GrTextureEffect.h"
26 #if SK_GPU_V1
27 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
28 #endif // SK_GPU_V1
29 #endif // SK_SUPPORT_GPU
30 
31 namespace {
32 
33 class SkAlphaThresholdImageFilter final : public SkImageFilter_Base {
34 public:
SkAlphaThresholdImageFilter(const SkRegion & region,SkScalar innerThreshold,SkScalar outerThreshold,sk_sp<SkImageFilter> input,const SkRect * cropRect=nullptr)35     SkAlphaThresholdImageFilter(const SkRegion& region, SkScalar innerThreshold,
36                                 SkScalar outerThreshold, sk_sp<SkImageFilter> input,
37                                 const SkRect* cropRect = nullptr)
38             : INHERITED(&input, 1, cropRect)
39             , fRegion(region)
40             , fInnerThreshold(innerThreshold)
41             , fOuterThreshold(outerThreshold) {}
42 
43 protected:
44     void flatten(SkWriteBuffer&) const override;
45 
46     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
47 
48 #if SK_SUPPORT_GPU
49     GrSurfaceProxyView createMaskTexture(GrRecordingContext*,
50                                          const SkMatrix&,
51                                          const SkIRect& bounds,
52                                          const SkSurfaceProps&) const;
53 #endif
54 
55 private:
56     friend void ::SkRegisterAlphaThresholdImageFilterFlattenable();
57     SK_FLATTENABLE_HOOKS(SkAlphaThresholdImageFilter)
58 
59     SkRegion fRegion;
60     SkScalar fInnerThreshold;
61     SkScalar fOuterThreshold;
62 
63     using INHERITED = SkImageFilter_Base;
64 };
65 
66 }; // end namespace
67 
AlphaThreshold(const SkRegion & region,SkScalar innerMin,SkScalar outerMax,sk_sp<SkImageFilter> input,const CropRect & cropRect)68 sk_sp<SkImageFilter> SkImageFilters::AlphaThreshold(
69         const SkRegion& region, SkScalar innerMin, SkScalar outerMax, sk_sp<SkImageFilter> input,
70         const CropRect& cropRect) {
71     innerMin = SkTPin(innerMin, 0.f, 1.f);
72     outerMax = SkTPin(outerMax, 0.f, 1.f);
73     if (!SkScalarIsFinite(innerMin) || !SkScalarIsFinite(outerMax)) {
74         return nullptr;
75     }
76     return sk_sp<SkImageFilter>(new SkAlphaThresholdImageFilter(
77             region, innerMin, outerMax, std::move(input), cropRect));
78 }
79 
SkRegisterAlphaThresholdImageFilterFlattenable()80 void SkRegisterAlphaThresholdImageFilterFlattenable() {
81     SK_REGISTER_FLATTENABLE(SkAlphaThresholdImageFilter);
82     SkFlattenable::Register("SkAlphaThresholdFilterImpl", SkAlphaThresholdImageFilter::CreateProc);
83 }
84 
CreateProc(SkReadBuffer & buffer)85 sk_sp<SkFlattenable> SkAlphaThresholdImageFilter::CreateProc(SkReadBuffer& buffer) {
86     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
87     SkScalar inner = buffer.readScalar();
88     SkScalar outer = buffer.readScalar();
89     SkRegion rgn;
90     buffer.readRegion(&rgn);
91     return SkImageFilters::AlphaThreshold(rgn, inner, outer, common.getInput(0), common.cropRect());
92 }
93 
flatten(SkWriteBuffer & buffer) const94 void SkAlphaThresholdImageFilter::flatten(SkWriteBuffer& buffer) const {
95     this->INHERITED::flatten(buffer);
96     buffer.writeScalar(fInnerThreshold);
97     buffer.writeScalar(fOuterThreshold);
98     buffer.writeRegion(fRegion);
99 }
100 
101 ///////////////////////////////////////////////////////////////////////////////////////////////////
102 
103 #if SK_SUPPORT_GPU
createMaskTexture(GrRecordingContext * rContext,const SkMatrix & inMatrix,const SkIRect & bounds,const SkSurfaceProps & surfaceProps) const104 GrSurfaceProxyView SkAlphaThresholdImageFilter::createMaskTexture(
105         GrRecordingContext* rContext,
106         const SkMatrix& inMatrix,
107         const SkIRect& bounds,
108         const SkSurfaceProps& surfaceProps) const {
109 #if SK_GPU_V1
110     auto sdc = skgpu::v1::SurfaceDrawContext::MakeWithFallback(
111             rContext, GrColorType::kAlpha_8, nullptr, SkBackingFit::kApprox, bounds.size(),
112             surfaceProps);
113     if (!sdc) {
114         return {};
115     }
116 
117     SkRegion::Iterator iter(fRegion);
118     sdc->clear(SK_PMColor4fTRANSPARENT);
119 
120     while (!iter.done()) {
121         GrPaint paint;
122         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
123 
124         SkRect rect = SkRect::Make(iter.rect());
125 
126         sdc->drawRect(nullptr, std::move(paint), GrAA::kNo, inMatrix, rect);
127 
128         iter.next();
129     }
130 
131     return sdc->readSurfaceView();
132 #else
133     return {};
134 #endif
135 }
136 
make_alpha_threshold_fp(std::unique_ptr<GrFragmentProcessor> inputFP,std::unique_ptr<GrFragmentProcessor> maskFP,float innerThreshold,float outerThreshold)137 static std::unique_ptr<GrFragmentProcessor> make_alpha_threshold_fp(
138         std::unique_ptr<GrFragmentProcessor> inputFP,
139         std::unique_ptr<GrFragmentProcessor> maskFP,
140         float innerThreshold,
141         float outerThreshold) {
142     static auto effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, R"(
143         uniform shader maskFP;
144         uniform half innerThreshold;
145         uniform half outerThreshold;
146 
147         half4 main(float2 xy, half4 color) {
148             half4 mask_color = maskFP.eval(xy);
149             if (mask_color.a < 0.5) {
150                 if (color.a > outerThreshold) {
151                     half scale = outerThreshold / color.a;
152                     color.rgb *= scale;
153                     color.a = outerThreshold;
154                 }
155             } else if (color.a < innerThreshold) {
156                 half scale = innerThreshold / max(0.001, color.a);
157                 color.rgb *= scale;
158                 color.a = innerThreshold;
159             }
160             return color;
161         }
162     )");
163 
164     return GrSkSLFP::Make(effect, "AlphaThreshold", std::move(inputFP),
165                           (outerThreshold >= 1.0f) ? GrSkSLFP::OptFlags::kPreservesOpaqueInput
166                                                    : GrSkSLFP::OptFlags::kNone,
167                           "maskFP", GrSkSLFP::IgnoreOptFlags(std::move(maskFP)),
168                           "innerThreshold", innerThreshold,
169                           "outerThreshold", outerThreshold);
170 }
171 #endif
172 
onFilterImage(const Context & ctx,SkIPoint * offset) const173 sk_sp<SkSpecialImage> SkAlphaThresholdImageFilter::onFilterImage(const Context& ctx,
174                                                                  SkIPoint* offset) const {
175     SkIPoint inputOffset = SkIPoint::Make(0, 0);
176     sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
177     if (!input) {
178         return nullptr;
179     }
180 
181     const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
182                                                   input->width(), input->height());
183 
184     SkIRect bounds;
185     if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
186         return nullptr;
187     }
188 
189 #if SK_SUPPORT_GPU
190     if (ctx.gpuBacked()) {
191         auto context = ctx.getContext();
192 
193         GrSurfaceProxyView inputView = (input->view(context));
194         SkASSERT(inputView.asTextureProxy());
195         const GrProtected isProtected = inputView.proxy()->isProtected();
196 
197         offset->fX = bounds.left();
198         offset->fY = bounds.top();
199 
200         bounds.offset(-inputOffset);
201 
202         SkMatrix matrix(ctx.ctm());
203         matrix.postTranslate(SkIntToScalar(-offset->fX), SkIntToScalar(-offset->fY));
204 
205         GrSurfaceProxyView maskView = this->createMaskTexture(context, matrix, bounds,
206                                                               ctx.surfaceProps());
207         if (!maskView) {
208             return nullptr;
209         }
210         auto maskFP = GrTextureEffect::Make(std::move(maskView), kPremul_SkAlphaType,
211                                             SkMatrix::Translate(-bounds.x(), -bounds.y()));
212 
213         auto textureFP = GrTextureEffect::Make(
214                 std::move(inputView), input->alphaType(),
215                 SkMatrix::Translate(input->subset().x(), input->subset().y()));
216         textureFP = GrColorSpaceXformEffect::Make(std::move(textureFP),
217                                                   input->getColorSpace(), input->alphaType(),
218                                                   ctx.colorSpace(), kPremul_SkAlphaType);
219         if (!textureFP) {
220             return nullptr;
221         }
222 
223         auto thresholdFP = make_alpha_threshold_fp(
224                 std::move(textureFP), std::move(maskFP), fInnerThreshold, fOuterThreshold);
225         if (!thresholdFP) {
226             return nullptr;
227         }
228 
229         return DrawWithFP(context, std::move(thresholdFP), bounds, ctx.colorType(),
230                           ctx.colorSpace(), ctx.surfaceProps(), isProtected);
231     }
232 #endif
233 
234     SkBitmap inputBM;
235 
236     if (!input->getROPixels(&inputBM)) {
237         return nullptr;
238     }
239 
240     if (inputBM.colorType() != kN32_SkColorType) {
241         return nullptr;
242     }
243 
244     if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
245         return nullptr;
246     }
247 
248 
249     SkMatrix localInverse;
250     if (!ctx.ctm().invert(&localInverse)) {
251         return nullptr;
252     }
253 
254     SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
255                                             kPremul_SkAlphaType);
256 
257     SkBitmap dst;
258     if (!dst.tryAllocPixels(info)) {
259         return nullptr;
260     }
261 
262     U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF);
263     U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF);
264     SkColor* dptr = dst.getAddr32(0, 0);
265     int dstWidth = dst.width(), dstHeight = dst.height();
266     SkIPoint srcOffset = { bounds.fLeft - inputOffset.fX, bounds.fTop - inputOffset.fY };
267     for (int y = 0; y < dstHeight; ++y) {
268         const SkColor* sptr = inputBM.getAddr32(srcOffset.fX, srcOffset.fY+y);
269 
270         for (int x = 0; x < dstWidth; ++x) {
271             const SkColor& source = sptr[x];
272             SkColor outputColor(source);
273             SkPoint position;
274             localInverse.mapXY((SkScalar)x + bounds.fLeft, (SkScalar)y + bounds.fTop, &position);
275             if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) {
276                 if (SkColorGetA(source) < innerThreshold) {
277                     U8CPU alpha = SkColorGetA(source);
278                     if (alpha == 0) {
279                         alpha = 1;
280                     }
281                     float scale = (float)innerThreshold / alpha;
282                     outputColor = SkColorSetARGB(innerThreshold,
283                                                   (U8CPU)(SkColorGetR(source) * scale),
284                                                   (U8CPU)(SkColorGetG(source) * scale),
285                                                   (U8CPU)(SkColorGetB(source) * scale));
286                 }
287             } else {
288                 if (SkColorGetA(source) > outerThreshold) {
289                     float scale = (float)outerThreshold / SkColorGetA(source);
290                     outputColor = SkColorSetARGB(outerThreshold,
291                                                   (U8CPU)(SkColorGetR(source) * scale),
292                                                   (U8CPU)(SkColorGetG(source) * scale),
293                                                   (U8CPU)(SkColorGetB(source) * scale));
294                 }
295             }
296             dptr[y * dstWidth + x] = outputColor;
297         }
298     }
299 
300     offset->fX = bounds.left();
301     offset->fY = bounds.top();
302     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
303                                           dst, ctx.surfaceProps());
304 }
305