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