• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 The Android Open Source Project
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/SkRect.h"
10 #include "include/effects/SkImageFilters.h"
11 #include "include/private/SkColorData.h"
12 #include "include/private/SkVx.h"
13 #include "src/core/SkImageFilter_Base.h"
14 #include "src/core/SkReadBuffer.h"
15 #include "src/core/SkSpecialImage.h"
16 #include "src/core/SkWriteBuffer.h"
17 
18 #if SK_SUPPORT_GPU
19 #include "include/gpu/GrRecordingContext.h"
20 #include "src/gpu/GrDirectContextPriv.h"
21 #include "src/gpu/GrFragmentProcessor.h"
22 #include "src/gpu/GrRecordingContextPriv.h"
23 #include "src/gpu/GrTexture.h"
24 #include "src/gpu/GrTextureProxy.h"
25 #include "src/gpu/SkGr.h"
26 #include "src/gpu/SurfaceFillContext.h"
27 #include "src/gpu/effects/GrTextureEffect.h"
28 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
29 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
30 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
31 #endif
32 
33 namespace {
34 
35 enum class MorphType {
36     kErode,
37     kDilate,
38     kLastType = kDilate
39 };
40 
41 enum class MorphDirection { kX, kY };
42 
43 class SkMorphologyImageFilter final : public SkImageFilter_Base {
44 public:
SkMorphologyImageFilter(MorphType type,SkScalar radiusX,SkScalar radiusY,sk_sp<SkImageFilter> input,const SkRect * cropRect)45     SkMorphologyImageFilter(MorphType type, SkScalar radiusX, SkScalar radiusY,
46                             sk_sp<SkImageFilter> input, const SkRect* cropRect)
47             : INHERITED(&input, 1, cropRect)
48             , fType(type)
49             , fRadius(SkSize::Make(radiusX, radiusY)) {}
50 
51     SkRect computeFastBounds(const SkRect& src) const override;
52     SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
53                                MapDirection, const SkIRect* inputRect) const override;
54 
55     /**
56      * All morphology procs have the same signature: src is the source buffer, dst the
57      * destination buffer, radius is the morphology radius, width and height are the bounds
58      * of the destination buffer (in pixels), and srcStride and dstStride are the
59      * number of pixels per row in each buffer. All buffers are 8888.
60      */
61 
62     typedef void (*Proc)(const SkPMColor* src, SkPMColor* dst, int radius,
63                          int width, int height, int srcStride, int dstStride);
64 
65 protected:
66     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
67     void flatten(SkWriteBuffer&) const override;
68 
mappedRadius(const SkMatrix & ctm) const69     SkSize mappedRadius(const SkMatrix& ctm) const {
70       SkVector radiusVector = SkVector::Make(fRadius.width(), fRadius.height());
71       ctm.mapVectors(&radiusVector, 1);
72       radiusVector.setAbs(radiusVector);
73       return SkSize::Make(radiusVector.x(), radiusVector.y());
74     }
75 
76 private:
77     friend void ::SkRegisterMorphologyImageFilterFlattenables();
78 
79     SK_FLATTENABLE_HOOKS(SkMorphologyImageFilter)
80 
81     MorphType fType;
82     SkSize    fRadius;
83 
84     using INHERITED = SkImageFilter_Base;
85 };
86 
87 } // end namespace
88 
Dilate(SkScalar radiusX,SkScalar radiusY,sk_sp<SkImageFilter> input,const CropRect & cropRect)89 sk_sp<SkImageFilter> SkImageFilters::Dilate(SkScalar radiusX, SkScalar radiusY,
90                                             sk_sp<SkImageFilter> input,
91                                             const CropRect& cropRect) {
92     if (radiusX < 0 || radiusY < 0) {
93         return nullptr;
94     }
95     return sk_sp<SkImageFilter>(new SkMorphologyImageFilter(
96             MorphType::kDilate, radiusX, radiusY, std::move(input), cropRect));
97 }
98 
Erode(SkScalar radiusX,SkScalar radiusY,sk_sp<SkImageFilter> input,const CropRect & cropRect)99 sk_sp<SkImageFilter> SkImageFilters::Erode(SkScalar radiusX, SkScalar radiusY,
100                                            sk_sp<SkImageFilter> input,
101                                            const CropRect& cropRect) {
102     if (radiusX < 0 || radiusY < 0) {
103         return nullptr;
104     }
105     return sk_sp<SkImageFilter>(new SkMorphologyImageFilter(
106             MorphType::kErode, radiusX, radiusY,  std::move(input), cropRect));
107 }
108 
SkRegisterMorphologyImageFilterFlattenables()109 void SkRegisterMorphologyImageFilterFlattenables() {
110     SK_REGISTER_FLATTENABLE(SkMorphologyImageFilter);
111     // TODO (michaelludwig): Remove after grace period for SKPs to stop using old name
112     SkFlattenable::Register("SkMorphologyImageFilterImpl", SkMorphologyImageFilter::CreateProc);
113 }
114 
CreateProc(SkReadBuffer & buffer)115 sk_sp<SkFlattenable> SkMorphologyImageFilter::CreateProc(SkReadBuffer& buffer) {
116     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
117 
118     SkScalar width = buffer.readScalar();
119     SkScalar height = buffer.readScalar();
120     MorphType filterType = buffer.read32LE(MorphType::kLastType);
121 
122     if (filterType == MorphType::kDilate) {
123         return SkImageFilters::Dilate(width, height, common.getInput(0), common.cropRect());
124     } else if (filterType == MorphType::kErode) {
125         return SkImageFilters::Erode(width, height, common.getInput(0), common.cropRect());
126     } else {
127         return nullptr;
128     }
129 }
130 
flatten(SkWriteBuffer & buffer) const131 void SkMorphologyImageFilter::flatten(SkWriteBuffer& buffer) const {
132     this->INHERITED::flatten(buffer);
133     buffer.writeScalar(fRadius.fWidth);
134     buffer.writeScalar(fRadius.fHeight);
135     buffer.writeInt(static_cast<int>(fType));
136 }
137 
138 ///////////////////////////////////////////////////////////////////////////////
139 
call_proc_X(SkMorphologyImageFilter::Proc procX,const SkBitmap & src,SkBitmap * dst,int radiusX,const SkIRect & bounds)140 static void call_proc_X(SkMorphologyImageFilter::Proc procX,
141                         const SkBitmap& src, SkBitmap* dst,
142                         int radiusX, const SkIRect& bounds) {
143     procX(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
144           radiusX, bounds.width(), bounds.height(),
145           src.rowBytesAsPixels(), dst->rowBytesAsPixels());
146 }
147 
call_proc_Y(SkMorphologyImageFilter::Proc procY,const SkPMColor * src,int srcRowBytesAsPixels,SkBitmap * dst,int radiusY,const SkIRect & bounds)148 static void call_proc_Y(SkMorphologyImageFilter::Proc procY,
149                         const SkPMColor* src, int srcRowBytesAsPixels, SkBitmap* dst,
150                         int radiusY, const SkIRect& bounds) {
151     procY(src, dst->getAddr32(0, 0),
152           radiusY, bounds.height(), bounds.width(),
153           srcRowBytesAsPixels, dst->rowBytesAsPixels());
154 }
155 
computeFastBounds(const SkRect & src) const156 SkRect SkMorphologyImageFilter::computeFastBounds(const SkRect& src) const {
157     SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
158     bounds.outset(fRadius.width(), fRadius.height());
159     return bounds;
160 }
161 
onFilterNodeBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection,const SkIRect * inputRect) const162 SkIRect SkMorphologyImageFilter::onFilterNodeBounds(
163         const SkIRect& src, const SkMatrix& ctm, MapDirection, const SkIRect* inputRect) const {
164     SkSize radius = mappedRadius(ctm);
165     return src.makeOutset(SkScalarCeilToInt(radius.width()), SkScalarCeilToInt(radius.height()));
166 }
167 
168 #if SK_SUPPORT_GPU
169 
170 ///////////////////////////////////////////////////////////////////////////////
171 /**
172  * Morphology effects. Depending upon the type of morphology, either the
173  * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
174  * kernel is selected as the new color. The new color is modulated by the input
175  * color.
176  */
177 class GrMorphologyEffect : public GrFragmentProcessor {
178 public:
Make(std::unique_ptr<GrFragmentProcessor> inputFP,GrSurfaceProxyView view,SkAlphaType srcAlphaType,MorphDirection dir,int radius,MorphType type)179     static std::unique_ptr<GrFragmentProcessor> Make(
180             std::unique_ptr<GrFragmentProcessor> inputFP, GrSurfaceProxyView view,
181             SkAlphaType srcAlphaType, MorphDirection dir, int radius, MorphType type) {
182         return std::unique_ptr<GrFragmentProcessor>(
183                 new GrMorphologyEffect(std::move(inputFP), std::move(view), srcAlphaType, dir,
184                                        radius, type, /*range=*/nullptr));
185     }
186 
Make(std::unique_ptr<GrFragmentProcessor> inputFP,GrSurfaceProxyView view,SkAlphaType srcAlphaType,MorphDirection dir,int radius,MorphType type,const float range[2])187     static std::unique_ptr<GrFragmentProcessor> Make(
188             std::unique_ptr<GrFragmentProcessor> inputFP, GrSurfaceProxyView view,
189             SkAlphaType srcAlphaType, MorphDirection dir, int radius, MorphType type,
190             const float range[2]) {
191         return std::unique_ptr<GrFragmentProcessor>(new GrMorphologyEffect(
192                 std::move(inputFP), std::move(view), srcAlphaType, dir, radius, type, range));
193     }
194 
name() const195     const char* name() const override { return "Morphology"; }
196 
197     SkString getShaderDfxInfo() const override;
198 
clone() const199     std::unique_ptr<GrFragmentProcessor> clone() const override {
200         return std::unique_ptr<GrFragmentProcessor>(new GrMorphologyEffect(*this));
201     }
202 
203 private:
204     MorphDirection fDirection;
205     int fRadius;
206     MorphType fType;
207     bool fUseRange;
208     float fRange[2];
209 
210     std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
211 
212     void onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
213 
214     bool onIsEqual(const GrFragmentProcessor&) const override;
215     GrMorphologyEffect(std::unique_ptr<GrFragmentProcessor> inputFP, GrSurfaceProxyView,
216                        SkAlphaType srcAlphaType, MorphDirection, int radius, MorphType,
217                        const float range[2]);
218     explicit GrMorphologyEffect(const GrMorphologyEffect&);
219 
220     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
221 
222     using INHERITED = GrFragmentProcessor;
223 };
224 
onMakeProgramImpl() const225 std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrMorphologyEffect::onMakeProgramImpl() const {
226     class Impl : public ProgramImpl {
227     public:
228         void emitCode(EmitArgs& args) override {
229             constexpr int kInputFPIndex = 0;
230             constexpr int kTexEffectIndex = 1;
231 
232             const GrMorphologyEffect& me = args.fFp.cast<GrMorphologyEffect>();
233 
234             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
235             fRangeUni = uniformHandler->addUniform(&me, kFragment_GrShaderFlag, kFloat2_GrSLType,
236                                                    "Range");
237             const char* range = uniformHandler->getUniformCStr(fRangeUni);
238 
239             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
240 
241             const char* func = me.fType == MorphType::kErode ? "min" : "max";
242 
243             char initialValue = me.fType == MorphType::kErode ? '1' : '0';
244             fragBuilder->codeAppendf("half4 color = half4(%c);", initialValue);
245 
246             char dir = me.fDirection == MorphDirection::kX ? 'x' : 'y';
247 
248             int width = 2 * me.fRadius + 1;
249 
250             // float2 coord = coord2D;
251             fragBuilder->codeAppendf("float2 coord = %s;", args.fSampleCoord);
252             // coord.x -= radius;
253             fragBuilder->codeAppendf("coord.%c -= %d;", dir, me.fRadius);
254             if (me.fUseRange) {
255                 // highBound = min(highBound, coord.x + (width-1));
256                 fragBuilder->codeAppendf("float highBound = min(%s.y, coord.%c + %f);", range, dir,
257                                          float(width - 1));
258                 // coord.x = max(lowBound, coord.x);
259                 fragBuilder->codeAppendf("coord.%c = max(%s.x, coord.%c);", dir, range, dir);
260             }
261             fragBuilder->codeAppendf("for (int i = 0; i < %d; i++) {", width);
262             SkString sample = this->invokeChild(kTexEffectIndex, args, "coord");
263             fragBuilder->codeAppendf("    color = %s(color, %s);", func, sample.c_str());
264             // coord.x += 1;
265             fragBuilder->codeAppendf("    coord.%c += 1;", dir);
266             if (me.fUseRange) {
267                 // coord.x = min(highBound, coord.x);
268                 fragBuilder->codeAppendf("    coord.%c = min(highBound, coord.%c);", dir, dir);
269             }
270             fragBuilder->codeAppend("}");
271 
272             SkString inputColor = this->invokeChild(kInputFPIndex, args);
273             fragBuilder->codeAppendf("return color * %s;", inputColor.c_str());
274         }
275 
276     private:
277         void onSetData(const GrGLSLProgramDataManager& pdman,
278                        const GrFragmentProcessor& proc) override {
279             const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
280             if (m.fUseRange) {
281                 pdman.set2f(fRangeUni, m.fRange[0], m.fRange[1]);
282             }
283         }
284 
285         GrGLSLProgramDataManager::UniformHandle fRangeUni;
286     };
287 
288     return std::make_unique<Impl>();
289 }
290 
getShaderDfxInfo() const291 SkString GrMorphologyEffect::getShaderDfxInfo() const
292 {
293     SkString format;
294     format.printf("ShaderDfx_GrMorphologyEffect_%d_%d_%d_%d", fRadius, fType, fDirection, fUseRange);
295     return format;
296 }
297 
onAddToKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const298 void GrMorphologyEffect::onAddToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
299     uint32_t key = static_cast<uint32_t>(fRadius);
300     key |= (static_cast<uint32_t>(fType) << 8);
301     key |= (static_cast<uint32_t>(fDirection) << 9);
302     if (fUseRange) {
303         key |= 1 << 10;
304     }
305     b->add32(key);
306 }
307 
GrMorphologyEffect(std::unique_ptr<GrFragmentProcessor> inputFP,GrSurfaceProxyView view,SkAlphaType srcAlphaType,MorphDirection direction,int radius,MorphType type,const float range[2])308 GrMorphologyEffect::GrMorphologyEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
309                                        GrSurfaceProxyView view,
310                                        SkAlphaType srcAlphaType,
311                                        MorphDirection direction,
312                                        int radius,
313                                        MorphType type,
314                                        const float range[2])
315         : INHERITED(kGrMorphologyEffect_ClassID, ModulateForClampedSamplerOptFlags(srcAlphaType))
316         , fDirection(direction)
317         , fRadius(radius)
318         , fType(type)
319         , fUseRange(SkToBool(range)) {
320     this->setUsesSampleCoordsDirectly();
321     this->registerChild(std::move(inputFP));
322     this->registerChild(GrTextureEffect::Make(std::move(view), srcAlphaType),
323                         SkSL::SampleUsage::Explicit());
324     if (fUseRange) {
325         fRange[0] = range[0];
326         fRange[1] = range[1];
327     }
328 }
329 
GrMorphologyEffect(const GrMorphologyEffect & that)330 GrMorphologyEffect::GrMorphologyEffect(const GrMorphologyEffect& that)
331         : INHERITED(that)
332         , fDirection(that.fDirection)
333         , fRadius(that.fRadius)
334         , fType(that.fType)
335         , fUseRange(that.fUseRange) {
336     if (that.fUseRange) {
337         fRange[0] = that.fRange[0];
338         fRange[1] = that.fRange[1];
339     }
340 }
341 
onIsEqual(const GrFragmentProcessor & sBase) const342 bool GrMorphologyEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
343     const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>();
344     return this->fRadius    == s.fRadius    &&
345            this->fDirection == s.fDirection &&
346            this->fUseRange  == s.fUseRange  &&
347            this->fType      == s.fType;
348 }
349 
350 ///////////////////////////////////////////////////////////////////////////////
351 
352 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMorphologyEffect);
353 
354 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)355 std::unique_ptr<GrFragmentProcessor> GrMorphologyEffect::TestCreate(GrProcessorTestData* d) {
356     auto [view, ct, at] = d->randomView();
357 
358     MorphDirection dir = d->fRandom->nextBool() ? MorphDirection::kX : MorphDirection::kY;
359     static const int kMaxRadius = 10;
360     int radius = d->fRandom->nextRangeU(1, kMaxRadius);
361     MorphType type = d->fRandom->nextBool() ? MorphType::kErode : MorphType::kDilate;
362     return GrMorphologyEffect::Make(d->inputFP(), std::move(view), at, dir, radius, type);
363 }
364 #endif
365 
apply_morphology_rect(skgpu::SurfaceFillContext * sfc,GrSurfaceProxyView view,SkAlphaType srcAlphaType,const SkIRect & srcRect,const SkIRect & dstRect,int radius,MorphType morphType,const float range[2],MorphDirection direction)366 static void apply_morphology_rect(skgpu::SurfaceFillContext* sfc,
367                                   GrSurfaceProxyView view,
368                                   SkAlphaType srcAlphaType,
369                                   const SkIRect& srcRect,
370                                   const SkIRect& dstRect,
371                                   int radius,
372                                   MorphType morphType,
373                                   const float range[2],
374                                   MorphDirection direction) {
375     auto fp = GrMorphologyEffect::Make(/*inputFP=*/nullptr,
376                                        std::move(view),
377                                        srcAlphaType,
378                                        direction,
379                                        radius,
380                                        morphType,
381                                        range);
382     sfc->fillRectToRectWithFP(srcRect, dstRect, std::move(fp));
383 }
384 
apply_morphology_rect_no_bounds(skgpu::SurfaceFillContext * sfc,GrSurfaceProxyView view,SkAlphaType srcAlphaType,const SkIRect & srcRect,const SkIRect & dstRect,int radius,MorphType morphType,MorphDirection direction)385 static void apply_morphology_rect_no_bounds(skgpu::SurfaceFillContext* sfc,
386                                             GrSurfaceProxyView view,
387                                             SkAlphaType srcAlphaType,
388                                             const SkIRect& srcRect,
389                                             const SkIRect& dstRect,
390                                             int radius,
391                                             MorphType morphType,
392                                             MorphDirection direction) {
393     auto fp = GrMorphologyEffect::Make(
394             /*inputFP=*/nullptr, std::move(view), srcAlphaType, direction, radius, morphType);
395     sfc->fillRectToRectWithFP(srcRect, dstRect, std::move(fp));
396 }
397 
apply_morphology_pass(skgpu::SurfaceFillContext * sfc,GrSurfaceProxyView view,SkAlphaType srcAlphaType,const SkIRect & srcRect,const SkIRect & dstRect,int radius,MorphType morphType,MorphDirection direction)398 static void apply_morphology_pass(skgpu::SurfaceFillContext* sfc,
399                                   GrSurfaceProxyView view,
400                                   SkAlphaType srcAlphaType,
401                                   const SkIRect& srcRect,
402                                   const SkIRect& dstRect,
403                                   int radius,
404                                   MorphType morphType,
405                                   MorphDirection direction) {
406     float bounds[2] = { 0.0f, 1.0f };
407     SkIRect lowerSrcRect = srcRect, lowerDstRect = dstRect;
408     SkIRect middleSrcRect = srcRect, middleDstRect = dstRect;
409     SkIRect upperSrcRect = srcRect, upperDstRect = dstRect;
410     if (direction == MorphDirection::kX) {
411         bounds[0] = SkIntToScalar(srcRect.left()) + 0.5f;
412         bounds[1] = SkIntToScalar(srcRect.right()) - 0.5f;
413         lowerSrcRect.fRight = srcRect.left() + radius;
414         lowerDstRect.fRight = dstRect.left() + radius;
415         upperSrcRect.fLeft = srcRect.right() - radius;
416         upperDstRect.fLeft = dstRect.right() - radius;
417         middleSrcRect.inset(radius, 0);
418         middleDstRect.inset(radius, 0);
419     } else {
420         bounds[0] = SkIntToScalar(srcRect.top()) + 0.5f;
421         bounds[1] = SkIntToScalar(srcRect.bottom()) - 0.5f;
422         lowerSrcRect.fBottom = srcRect.top() + radius;
423         lowerDstRect.fBottom = dstRect.top() + radius;
424         upperSrcRect.fTop = srcRect.bottom() - radius;
425         upperDstRect.fTop = dstRect.bottom() - radius;
426         middleSrcRect.inset(0, radius);
427         middleDstRect.inset(0, radius);
428     }
429     if (middleSrcRect.width() <= 0) {
430         // radius covers srcRect; use bounds over entire draw
431         apply_morphology_rect(sfc, std::move(view), srcAlphaType, srcRect,
432                               dstRect, radius, morphType, bounds, direction);
433     } else {
434         // Draw upper and lower margins with bounds; middle without.
435         apply_morphology_rect(sfc, view, srcAlphaType, lowerSrcRect,
436                               lowerDstRect, radius, morphType, bounds, direction);
437         apply_morphology_rect(sfc, view, srcAlphaType, upperSrcRect,
438                               upperDstRect, radius, morphType, bounds, direction);
439         apply_morphology_rect_no_bounds(sfc, std::move(view), srcAlphaType,
440                                         middleSrcRect, middleDstRect, radius, morphType, direction);
441     }
442 }
443 
apply_morphology(GrRecordingContext * rContext,SkSpecialImage * input,const SkIRect & rect,MorphType morphType,SkISize radius,const SkImageFilter_Base::Context & ctx)444 static sk_sp<SkSpecialImage> apply_morphology(
445         GrRecordingContext* rContext, SkSpecialImage* input, const SkIRect& rect,
446         MorphType morphType, SkISize radius, const SkImageFilter_Base::Context& ctx) {
447     GrSurfaceProxyView srcView = input->view(rContext);
448     SkAlphaType srcAlphaType = input->alphaType();
449     SkASSERT(srcView.asTextureProxy());
450     sk_sp<SkColorSpace> colorSpace = ctx.refColorSpace();
451     GrColorType colorType = ctx.grColorType();
452 
453     GrSurfaceProxy* proxy = srcView.proxy();
454 
455     const SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
456     SkIRect srcRect = rect;
457     // Map into proxy space
458     srcRect.offset(input->subset().x(), input->subset().y());
459     SkASSERT(radius.width() > 0 || radius.height() > 0);
460 
461     if (radius.fWidth > 0) {
462         GrImageInfo info(colorType, kPremul_SkAlphaType, colorSpace, rect.size());
463         auto dstFillContext = rContext->priv().makeSFC(info,
464                                                        SkBackingFit::kApprox,
465                                                        1,
466                                                        GrMipmapped::kNo,
467                                                        proxy->isProtected(),
468                                                        kBottomLeft_GrSurfaceOrigin);
469         if (!dstFillContext) {
470             return nullptr;
471         }
472 
473         apply_morphology_pass(dstFillContext.get(), std::move(srcView), srcAlphaType,
474                               srcRect, dstRect, radius.fWidth, morphType, MorphDirection::kX);
475         SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
476                                               dstRect.width(), radius.fHeight);
477         SkPMColor4f clearColor = MorphType::kErode == morphType
478                 ? SK_PMColor4fWHITE : SK_PMColor4fTRANSPARENT;
479         dstFillContext->clear(clearRect, clearColor);
480 
481         srcView = dstFillContext->readSurfaceView();
482         srcAlphaType = dstFillContext->colorInfo().alphaType();
483         srcRect = dstRect;
484     }
485     if (radius.fHeight > 0) {
486         GrImageInfo info(colorType, kPremul_SkAlphaType, colorSpace, rect.size());
487         auto dstFillContext = rContext->priv().makeSFC(info,
488                                                        SkBackingFit::kApprox,
489                                                        1,
490                                                        GrMipmapped::kNo,
491                                                        srcView.proxy()->isProtected(),
492                                                        kBottomLeft_GrSurfaceOrigin);
493         if (!dstFillContext) {
494             return nullptr;
495         }
496 
497         apply_morphology_pass(dstFillContext.get(), std::move(srcView), srcAlphaType,
498                               srcRect, dstRect, radius.fHeight, morphType, MorphDirection::kY);
499 
500         srcView = dstFillContext->readSurfaceView();
501     }
502 
503     return SkSpecialImage::MakeDeferredFromGpu(rContext,
504                                                SkIRect::MakeWH(rect.width(), rect.height()),
505                                                kNeedNewImageUniqueID_SpecialImage,
506                                                std::move(srcView), colorType,
507                                                std::move(colorSpace), input->props());
508 }
509 #endif
510 
511 namespace {
512 
513 #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
514     template<MorphType type, MorphDirection direction>
morph(const SkPMColor * src,SkPMColor * dst,int radius,int width,int height,int srcStride,int dstStride)515     static void morph(const SkPMColor* src, SkPMColor* dst,
516                       int radius, int width, int height, int srcStride, int dstStride) {
517         const int srcStrideX = direction == MorphDirection::kX ? 1 : srcStride;
518         const int dstStrideX = direction == MorphDirection::kX ? 1 : dstStride;
519         const int srcStrideY = direction == MorphDirection::kX ? srcStride : 1;
520         const int dstStrideY = direction == MorphDirection::kX ? dstStride : 1;
521         radius = std::min(radius, width - 1);
522         const SkPMColor* upperSrc = src + radius * srcStrideX;
523         for (int x = 0; x < width; ++x) {
524             const SkPMColor* lp = src;
525             const SkPMColor* up = upperSrc;
526             SkPMColor* dptr = dst;
527             for (int y = 0; y < height; ++y) {
528                 __m128i extreme = (type == MorphType::kDilate) ? _mm_setzero_si128()
529                                                                : _mm_set1_epi32(0xFFFFFFFF);
530                 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
531                     __m128i src_pixel = _mm_cvtsi32_si128(*p);
532                     extreme = (type == MorphType::kDilate) ? _mm_max_epu8(src_pixel, extreme)
533                                                            : _mm_min_epu8(src_pixel, extreme);
534                 }
535                 *dptr = _mm_cvtsi128_si32(extreme);
536                 dptr += dstStrideY;
537                 lp += srcStrideY;
538                 up += srcStrideY;
539             }
540             if (x >= radius) { src += srcStrideX; }
541             if (x + radius < width - 1) { upperSrc += srcStrideX; }
542             dst += dstStrideX;
543         }
544     }
545 
546 #elif defined(SK_ARM_HAS_NEON)
547     template<MorphType type, MorphDirection direction>
548     static void morph(const SkPMColor* src, SkPMColor* dst,
549                       int radius, int width, int height, int srcStride, int dstStride) {
550         const int srcStrideX = direction == MorphDirection::kX ? 1 : srcStride;
551         const int dstStrideX = direction == MorphDirection::kX ? 1 : dstStride;
552         const int srcStrideY = direction == MorphDirection::kX ? srcStride : 1;
553         const int dstStrideY = direction == MorphDirection::kX ? dstStride : 1;
554         radius = std::min(radius, width - 1);
555         const SkPMColor* upperSrc = src + radius * srcStrideX;
556         for (int x = 0; x < width; ++x) {
557             const SkPMColor* lp = src;
558             const SkPMColor* up = upperSrc;
559             SkPMColor* dptr = dst;
560             for (int y = 0; y < height; ++y) {
561                 uint8x8_t extreme = vdup_n_u8(type == MorphType::kDilate ? 0 : 255);
562                 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
563                     uint8x8_t src_pixel = vreinterpret_u8_u32(vdup_n_u32(*p));
564                     extreme = (type == MorphType::kDilate) ? vmax_u8(src_pixel, extreme)
565                                                            : vmin_u8(src_pixel, extreme);
566                 }
567                 *dptr = vget_lane_u32(vreinterpret_u32_u8(extreme), 0);
568                 dptr += dstStrideY;
569                 lp += srcStrideY;
570                 up += srcStrideY;
571             }
572             if (x >= radius) src += srcStrideX;
573             if (x + radius < width - 1) upperSrc += srcStrideX;
574             dst += dstStrideX;
575         }
576     }
577 
578 #else
579     template<MorphType type, MorphDirection direction>
580     static void morph(const SkPMColor* src, SkPMColor* dst,
581                       int radius, int width, int height, int srcStride, int dstStride) {
582         const int srcStrideX = direction == MorphDirection::kX ? 1 : srcStride;
583         const int dstStrideX = direction == MorphDirection::kX ? 1 : dstStride;
584         const int srcStrideY = direction == MorphDirection::kX ? srcStride : 1;
585         const int dstStrideY = direction == MorphDirection::kX ? dstStride : 1;
586         radius = std::min(radius, width - 1);
587         const SkPMColor* upperSrc = src + radius * srcStrideX;
588         for (int x = 0; x < width; ++x) {
589             const SkPMColor* lp = src;
590             const SkPMColor* up = upperSrc;
591             SkPMColor* dptr = dst;
592             for (int y = 0; y < height; ++y) {
593                 // If we're maxing (dilate), start from 0; if minning (erode), start from 255.
594                 const int start = (type == MorphType::kDilate) ? 0 : 255;
595                 int B = start, G = start, R = start, A = start;
596                 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
597                     int b = SkGetPackedB32(*p),
598                         g = SkGetPackedG32(*p),
599                         r = SkGetPackedR32(*p),
600                         a = SkGetPackedA32(*p);
601                     if (type == MorphType::kDilate) {
602                         B = std::max(b, B);
603                         G = std::max(g, G);
604                         R = std::max(r, R);
605                         A = std::max(a, A);
606                     } else {
607                         B = std::min(b, B);
608                         G = std::min(g, G);
609                         R = std::min(r, R);
610                         A = std::min(a, A);
611                     }
612                 }
613                 *dptr = SkPackARGB32(A, R, G, B);
614                 dptr += dstStrideY;
615                 lp += srcStrideY;
616                 up += srcStrideY;
617             }
618             if (x >= radius) { src += srcStrideX; }
619             if (x + radius < width - 1) { upperSrc += srcStrideX; }
620             dst += dstStrideX;
621         }
622     }
623 #endif
624 }  // namespace
625 
onFilterImage(const Context & ctx,SkIPoint * offset) const626 sk_sp<SkSpecialImage> SkMorphologyImageFilter::onFilterImage(const Context& ctx,
627                                                              SkIPoint* offset) const {
628     SkIPoint inputOffset = SkIPoint::Make(0, 0);
629     sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
630     if (!input) {
631         return nullptr;
632     }
633 
634     SkIRect bounds;
635     input = this->applyCropRectAndPad(this->mapContext(ctx), input.get(), &inputOffset, &bounds);
636     if (!input) {
637         return nullptr;
638     }
639 
640     SkSize radius = mappedRadius(ctx.ctm());
641     int width = SkScalarRoundToInt(radius.width());
642     int height = SkScalarRoundToInt(radius.height());
643 
644     // Width (or height) must fit in a signed 32-bit int to avoid UBSAN issues (crbug.com/1018190)
645     // Further, we limit the radius to something much smaller, to avoid extremely slow draw calls:
646     // (crbug.com/1123035):
647     constexpr int kMaxRadius = 100; // (std::numeric_limits<int>::max() - 1) / 2;
648 
649     if (width < 0 || height < 0 || width > kMaxRadius || height > kMaxRadius) {
650         return nullptr;
651     }
652 
653     SkIRect srcBounds = bounds;
654     srcBounds.offset(-inputOffset);
655 
656     if (0 == width && 0 == height) {
657         offset->fX = bounds.left();
658         offset->fY = bounds.top();
659         return input->makeSubset(srcBounds);
660     }
661 
662 #if SK_SUPPORT_GPU
663     if (ctx.gpuBacked()) {
664         auto context = ctx.getContext();
665 
666         // Ensure the input is in the destination color space. Typically applyCropRect will have
667         // called pad_image to account for our dilation of bounds, so the result will already be
668         // moved to the destination color space. If a filter DAG avoids that, then we use this
669         // fall-back, which saves us from having to do the xform during the filter itself.
670         input = ImageToColorSpace(input.get(), ctx.colorType(), ctx.colorSpace(),
671                                   ctx.surfaceProps());
672 
673         sk_sp<SkSpecialImage> result(apply_morphology(context, input.get(), srcBounds, fType,
674                                                       SkISize::Make(width, height), ctx));
675         if (result) {
676             offset->fX = bounds.left();
677             offset->fY = bounds.top();
678         }
679         return result;
680     }
681 #endif
682 
683     SkBitmap inputBM;
684 
685     if (!input->getROPixels(&inputBM)) {
686         return nullptr;
687     }
688 
689     if (inputBM.colorType() != kN32_SkColorType) {
690         return nullptr;
691     }
692 
693     SkImageInfo info = SkImageInfo::Make(bounds.size(), inputBM.colorType(), inputBM.alphaType());
694 
695     SkBitmap dst;
696     if (!dst.tryAllocPixels(info)) {
697         return nullptr;
698     }
699 
700     SkMorphologyImageFilter::Proc procX, procY;
701 
702     if (MorphType::kDilate == fType) {
703         procX = &morph<MorphType::kDilate, MorphDirection::kX>;
704         procY = &morph<MorphType::kDilate, MorphDirection::kY>;
705     } else {
706         procX = &morph<MorphType::kErode,  MorphDirection::kX>;
707         procY = &morph<MorphType::kErode,  MorphDirection::kY>;
708     }
709 
710     if (width > 0 && height > 0) {
711         SkBitmap tmp;
712         if (!tmp.tryAllocPixels(info)) {
713             return nullptr;
714         }
715 
716         call_proc_X(procX, inputBM, &tmp, width, srcBounds);
717         SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
718         call_proc_Y(procY,
719                     tmp.getAddr32(tmpBounds.left(), tmpBounds.top()), tmp.rowBytesAsPixels(),
720                     &dst, height, tmpBounds);
721     } else if (width > 0) {
722         call_proc_X(procX, inputBM, &dst, width, srcBounds);
723     } else if (height > 0) {
724         call_proc_Y(procY,
725                     inputBM.getAddr32(srcBounds.left(), srcBounds.top()),
726                     inputBM.rowBytesAsPixels(),
727                     &dst, height, srcBounds);
728     }
729     offset->fX = bounds.left();
730     offset->fY = bounds.top();
731 
732     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
733                                           dst, ctx.surfaceProps());
734 }
735