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