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