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