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