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/effects/SkMorphologyImageFilter.h" |
9 | |
10 | #include "include/core/SkBitmap.h" |
11 | #include "include/core/SkRect.h" |
12 | #include "include/private/SkColorData.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/GrContext.h" |
20 | #include "include/private/GrRecordingContext.h" |
21 | #include "src/gpu/GrContextPriv.h" |
22 | #include "src/gpu/GrCoordTransform.h" |
23 | #include "src/gpu/GrFixedClip.h" |
24 | #include "src/gpu/GrRecordingContextPriv.h" |
25 | #include "src/gpu/GrRenderTargetContext.h" |
26 | #include "src/gpu/GrTexture.h" |
27 | #include "src/gpu/GrTextureProxy.h" |
28 | #include "src/gpu/SkGr.h" |
29 | #include "src/gpu/glsl/GrGLSLFragmentProcessor.h" |
30 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
31 | #include "src/gpu/glsl/GrGLSLProgramDataManager.h" |
32 | #include "src/gpu/glsl/GrGLSLUniformHandler.h" |
33 | #endif |
34 | |
35 | namespace { |
36 | |
37 | enum class MorphType { |
38 | kErode, |
39 | kDilate, |
40 | kLastType = kDilate |
41 | }; |
42 | |
43 | enum class MorphDirection { kX, kY }; |
44 | |
45 | class SkMorphologyImageFilterImpl final : public SkImageFilter_Base { |
46 | public: |
47 | SkMorphologyImageFilterImpl(MorphType type, SkScalar radiusX, SkScalar radiusY, |
48 | sk_sp<SkImageFilter> input, const CropRect* cropRect) |
49 | : INHERITED(&input, 1, cropRect) |
50 | , fType(type) |
51 | , fRadius(SkSize::Make(radiusX, radiusY)) {} |
52 | |
53 | SkRect computeFastBounds(const SkRect& src) const override; |
54 | SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, |
55 | MapDirection, const SkIRect* inputRect) const override; |
56 | |
57 | /** |
58 | * All morphology procs have the same signature: src is the source buffer, dst the |
59 | * destination buffer, radius is the morphology radius, width and height are the bounds |
60 | * of the destination buffer (in pixels), and srcStride and dstStride are the |
61 | * number of pixels per row in each buffer. All buffers are 8888. |
62 | */ |
63 | |
64 | typedef void (*Proc)(const SkPMColor* src, SkPMColor* dst, int radius, |
65 | int width, int height, int srcStride, int dstStride); |
66 | |
67 | protected: |
68 | sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override; |
69 | void flatten(SkWriteBuffer&) const override; |
70 | |
71 | SkSize mappedRadius(const SkMatrix& ctm) const { |
72 | SkVector radiusVector = SkVector::Make(fRadius.width(), fRadius.height()); |
73 | ctm.mapVectors(&radiusVector, 1); |
74 | radiusVector.setAbs(radiusVector); |
75 | return SkSize::Make(radiusVector.x(), radiusVector.y()); |
76 | } |
77 | |
78 | private: |
79 | friend void SkDilateImageFilter::RegisterFlattenables(); |
80 | |
81 | SK_FLATTENABLE_HOOKS(SkMorphologyImageFilterImpl) |
82 | // Historically the morphology op was implicitly encoded in the factory type used to decode |
83 | // the image filter, so provide backwards compatible functions for old SKPs. |
84 | static sk_sp<SkFlattenable> CreateProcWithType(SkReadBuffer&, const MorphType*); |
85 | static sk_sp<SkFlattenable> DilateCreateProc(SkReadBuffer& buffer) { |
86 | static const MorphType kType = MorphType::kDilate; |
87 | return CreateProcWithType(buffer, &kType); |
88 | } |
89 | static sk_sp<SkFlattenable> ErodeCreateProc(SkReadBuffer& buffer) { |
90 | static const MorphType kType = MorphType::kErode; |
91 | return CreateProcWithType(buffer, &kType); |
92 | } |
93 | |
94 | MorphType fType; |
95 | SkSize fRadius; |
96 | |
97 | typedef SkImageFilter_Base INHERITED; |
98 | }; |
99 | |
100 | } // end namespace |
101 | |
102 | sk_sp<SkImageFilter> SkDilateImageFilter::Make(SkScalar radiusX, SkScalar radiusY, |
103 | sk_sp<SkImageFilter> input, |
104 | const SkImageFilter::CropRect* cropRect) { |
105 | if (radiusX < 0 || radiusY < 0) { |
106 | return nullptr; |
107 | } |
108 | return sk_sp<SkImageFilter>(new SkMorphologyImageFilterImpl( |
109 | MorphType::kDilate, radiusX, radiusY, std::move(input), cropRect)); |
110 | } |
111 | |
112 | sk_sp<SkImageFilter> SkErodeImageFilter::Make(SkScalar radiusX, SkScalar radiusY, |
113 | sk_sp<SkImageFilter> input, |
114 | const SkImageFilter::CropRect* cropRect) { |
115 | if (radiusX < 0 || radiusY < 0) { |
116 | return nullptr; |
117 | } |
118 | return sk_sp<SkImageFilter>(new SkMorphologyImageFilterImpl( |
119 | MorphType::kErode, radiusX, radiusY, std::move(input), cropRect)); |
120 | } |
121 | |
122 | void SkDilateImageFilter::RegisterFlattenables() { |
123 | SK_REGISTER_FLATTENABLE(SkMorphologyImageFilterImpl); |
124 | // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old names |
125 | SkFlattenable::Register("SkDilateImageFilter" , SkMorphologyImageFilterImpl::DilateCreateProc); |
126 | SkFlattenable::Register( |
127 | "SkDilateImageFilterImpl" , SkMorphologyImageFilterImpl::DilateCreateProc); |
128 | SkFlattenable::Register("SkErodeImageFilter" , SkMorphologyImageFilterImpl::ErodeCreateProc); |
129 | SkFlattenable::Register("SkErodeImageFilterImpl" , SkMorphologyImageFilterImpl::ErodeCreateProc); |
130 | } |
131 | |
132 | /////////////////////////////////////////////////////////////////////////////// |
133 | |
134 | // 'type' acts as a signal that old-style deserialization is required. It is temporary. |
135 | sk_sp<SkFlattenable> SkMorphologyImageFilterImpl::CreateProcWithType(SkReadBuffer& buffer, |
136 | const MorphType* type) { |
137 | SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); |
138 | SkScalar width; |
139 | SkScalar height; |
140 | if (buffer.isVersionLT(SkPicturePriv::kMorphologyTakesScalar_Version)) { |
141 | width = buffer.readInt(); |
142 | height = buffer.readInt(); |
143 | } else { |
144 | width = buffer.readScalar(); |
145 | height = buffer.readScalar(); |
146 | } |
147 | |
148 | MorphType filterType; |
149 | if (type) { |
150 | // The old create procs that have an associated op should only be used on old SKPs |
151 | SkASSERT(buffer.isVersionLT(SkPicturePriv::kUnifyErodeDilateImpls_Version)); |
152 | filterType = *type; |
153 | } else { |
154 | filterType = buffer.read32LE(MorphType::kLastType); |
155 | } |
156 | |
157 | if (filterType == MorphType::kDilate) { |
158 | return SkDilateImageFilter::Make(width, height, common.getInput(0), &common.cropRect()); |
159 | } else if (filterType == MorphType::kErode) { |
160 | return SkErodeImageFilter::Make(width, height, common.getInput(0), &common.cropRect()); |
161 | } else { |
162 | return nullptr; |
163 | } |
164 | } |
165 | |
166 | sk_sp<SkFlattenable> SkMorphologyImageFilterImpl::CreateProc(SkReadBuffer& buffer) { |
167 | // Pass null to have the create proc read the op from the buffer |
168 | return CreateProcWithType(buffer, nullptr); |
169 | } |
170 | |
171 | void SkMorphologyImageFilterImpl::flatten(SkWriteBuffer& buffer) const { |
172 | this->INHERITED::flatten(buffer); |
173 | buffer.writeScalar(fRadius.fWidth); |
174 | buffer.writeScalar(fRadius.fHeight); |
175 | buffer.writeInt(static_cast<int>(fType)); |
176 | } |
177 | |
178 | static void call_proc_X(SkMorphologyImageFilterImpl::Proc procX, |
179 | const SkBitmap& src, SkBitmap* dst, |
180 | int radiusX, const SkIRect& bounds) { |
181 | procX(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0), |
182 | radiusX, bounds.width(), bounds.height(), |
183 | src.rowBytesAsPixels(), dst->rowBytesAsPixels()); |
184 | } |
185 | |
186 | static void call_proc_Y(SkMorphologyImageFilterImpl::Proc procY, |
187 | const SkPMColor* src, int srcRowBytesAsPixels, SkBitmap* dst, |
188 | int radiusY, const SkIRect& bounds) { |
189 | procY(src, dst->getAddr32(0, 0), |
190 | radiusY, bounds.height(), bounds.width(), |
191 | srcRowBytesAsPixels, dst->rowBytesAsPixels()); |
192 | } |
193 | |
194 | SkRect SkMorphologyImageFilterImpl::computeFastBounds(const SkRect& src) const { |
195 | SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src; |
196 | bounds.outset(fRadius.width(), fRadius.height()); |
197 | return bounds; |
198 | } |
199 | |
200 | SkIRect SkMorphologyImageFilterImpl::onFilterNodeBounds( |
201 | const SkIRect& src, const SkMatrix& ctm, MapDirection, const SkIRect* inputRect) const { |
202 | SkSize radius = mappedRadius(ctm); |
203 | return src.makeOutset(SkScalarCeilToInt(radius.width()), SkScalarCeilToInt(radius.height())); |
204 | } |
205 | |
206 | #if SK_SUPPORT_GPU |
207 | |
208 | /////////////////////////////////////////////////////////////////////////////// |
209 | /** |
210 | * Morphology effects. Depending upon the type of morphology, either the |
211 | * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the |
212 | * kernel is selected as the new color. The new color is modulated by the input |
213 | * color. |
214 | */ |
215 | class GrMorphologyEffect : public GrFragmentProcessor { |
216 | public: |
217 | static std::unique_ptr<GrFragmentProcessor> Make(GrSurfaceProxyView view, |
218 | SkAlphaType srcAlphaType, MorphDirection dir, |
219 | int radius, MorphType type) { |
220 | return std::unique_ptr<GrFragmentProcessor>( |
221 | new GrMorphologyEffect(std::move(view), srcAlphaType, dir, radius, type, nullptr)); |
222 | } |
223 | |
224 | static std::unique_ptr<GrFragmentProcessor> Make(GrSurfaceProxyView view, |
225 | SkAlphaType srcAlphaType, MorphDirection dir, |
226 | int radius, MorphType type, |
227 | const float bounds[2]) { |
228 | return std::unique_ptr<GrFragmentProcessor>( |
229 | new GrMorphologyEffect(std::move(view), srcAlphaType, dir, radius, type, bounds)); |
230 | } |
231 | |
232 | MorphType type() const { return fType; } |
233 | bool useRange() const { return fUseRange; } |
234 | const float* range() const { return fRange; } |
235 | MorphDirection direction() const { return fDirection; } |
236 | int radius() const { return fRadius; } |
237 | int width() const { return 2 * fRadius + 1; } |
238 | |
239 | const char* name() const override { return "Morphology" ; } |
240 | |
241 | std::unique_ptr<GrFragmentProcessor> clone() const override { |
242 | return std::unique_ptr<GrFragmentProcessor>(new GrMorphologyEffect(*this)); |
243 | } |
244 | |
245 | private: |
246 | GrCoordTransform fCoordTransform; |
247 | TextureSampler fTextureSampler; |
248 | MorphDirection fDirection; |
249 | int fRadius; |
250 | MorphType fType; |
251 | bool fUseRange; |
252 | float fRange[2]; |
253 | |
254 | GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; |
255 | |
256 | void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; |
257 | |
258 | bool onIsEqual(const GrFragmentProcessor&) const override; |
259 | |
260 | const TextureSampler& onTextureSampler(int i) const override { return fTextureSampler; } |
261 | |
262 | GrMorphologyEffect(GrSurfaceProxyView, SkAlphaType srcAlphaType, MorphDirection, int radius, |
263 | MorphType, const float range[2]); |
264 | explicit GrMorphologyEffect(const GrMorphologyEffect&); |
265 | |
266 | GR_DECLARE_FRAGMENT_PROCESSOR_TEST |
267 | |
268 | typedef GrFragmentProcessor INHERITED; |
269 | }; |
270 | |
271 | /////////////////////////////////////////////////////////////////////////////// |
272 | |
273 | class GrGLMorphologyEffect : public GrGLSLFragmentProcessor { |
274 | public: |
275 | void emitCode(EmitArgs&) override; |
276 | |
277 | static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*); |
278 | |
279 | protected: |
280 | void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; |
281 | |
282 | private: |
283 | GrGLSLProgramDataManager::UniformHandle fPixelSizeUni; |
284 | GrGLSLProgramDataManager::UniformHandle fRangeUni; |
285 | |
286 | typedef GrGLSLFragmentProcessor INHERITED; |
287 | }; |
288 | |
289 | void GrGLMorphologyEffect::emitCode(EmitArgs& args) { |
290 | const GrMorphologyEffect& me = args.fFp.cast<GrMorphologyEffect>(); |
291 | |
292 | GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
293 | fPixelSizeUni = uniformHandler->addUniform(&me, kFragment_GrShaderFlag, kHalf_GrSLType, |
294 | "PixelSize" ); |
295 | const char* pixelSizeInc = uniformHandler->getUniformCStr(fPixelSizeUni); |
296 | fRangeUni = uniformHandler->addUniform(&me, kFragment_GrShaderFlag, kFloat2_GrSLType, "Range" ); |
297 | const char* range = uniformHandler->getUniformCStr(fRangeUni); |
298 | |
299 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
300 | SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint); |
301 | const char* func; |
302 | switch (me.type()) { |
303 | case MorphType::kErode: |
304 | fragBuilder->codeAppendf("\t\t%s = half4(1, 1, 1, 1);\n" , args.fOutputColor); |
305 | func = "min" ; |
306 | break; |
307 | case MorphType::kDilate: |
308 | fragBuilder->codeAppendf("\t\t%s = half4(0, 0, 0, 0);\n" , args.fOutputColor); |
309 | func = "max" ; |
310 | break; |
311 | default: |
312 | SK_ABORT("Unexpected type" ); |
313 | func = "" ; // suppress warning |
314 | break; |
315 | } |
316 | |
317 | const char* dir; |
318 | switch (me.direction()) { |
319 | case MorphDirection::kX: |
320 | dir = "x" ; |
321 | break; |
322 | case MorphDirection::kY: |
323 | dir = "y" ; |
324 | break; |
325 | default: |
326 | SK_ABORT("Unknown filter direction." ); |
327 | dir = "" ; // suppress warning |
328 | } |
329 | |
330 | int width = me.width(); |
331 | |
332 | // float2 coord = coord2D; |
333 | fragBuilder->codeAppendf("\t\tfloat2 coord = %s;\n" , coords2D.c_str()); |
334 | // coord.x -= radius * pixelSize; |
335 | fragBuilder->codeAppendf("\t\tcoord.%s -= %d.0 * %s; \n" , dir, me.radius(), pixelSizeInc); |
336 | if (me.useRange()) { |
337 | // highBound = min(highBound, coord.x + (width-1) * pixelSize); |
338 | fragBuilder->codeAppendf("\t\tfloat highBound = min(%s.y, coord.%s + %f * %s);" , |
339 | range, dir, float(width - 1), pixelSizeInc); |
340 | // coord.x = max(lowBound, coord.x); |
341 | fragBuilder->codeAppendf("\t\tcoord.%s = max(%s.x, coord.%s);" , dir, range, dir); |
342 | } |
343 | fragBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n" , width); |
344 | fragBuilder->codeAppendf("\t\t\t%s = %s(%s, " , args.fOutputColor, func, args.fOutputColor); |
345 | fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coord" ); |
346 | fragBuilder->codeAppend(");\n" ); |
347 | // coord.x += pixelSize; |
348 | fragBuilder->codeAppendf("\t\t\tcoord.%s += %s;\n" , dir, pixelSizeInc); |
349 | if (me.useRange()) { |
350 | // coord.x = min(highBound, coord.x); |
351 | fragBuilder->codeAppendf("\t\t\tcoord.%s = min(highBound, coord.%s);" , dir, dir); |
352 | } |
353 | fragBuilder->codeAppend("\t\t}\n" ); |
354 | fragBuilder->codeAppendf("%s *= %s;\n" , args.fOutputColor, args.fInputColor); |
355 | } |
356 | |
357 | void GrGLMorphologyEffect::GenKey(const GrProcessor& proc, |
358 | const GrShaderCaps&, GrProcessorKeyBuilder* b) { |
359 | const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>(); |
360 | uint32_t key = static_cast<uint32_t>(m.radius()); |
361 | key |= (static_cast<uint32_t>(m.type()) << 8); |
362 | key |= (static_cast<uint32_t>(m.direction()) << 9); |
363 | if (m.useRange()) { |
364 | key |= 1 << 10; |
365 | } |
366 | b->add32(key); |
367 | } |
368 | |
369 | void GrGLMorphologyEffect::onSetData(const GrGLSLProgramDataManager& pdman, |
370 | const GrFragmentProcessor& proc) { |
371 | const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>(); |
372 | const auto& view = m.textureSampler(0).view(); |
373 | GrSurfaceProxy* proxy = view.proxy(); |
374 | GrTexture& texture = *proxy->peekTexture(); |
375 | |
376 | float pixelSize = 0.0f; |
377 | switch (m.direction()) { |
378 | case MorphDirection::kX: |
379 | pixelSize = 1.0f / texture.width(); |
380 | break; |
381 | case MorphDirection::kY: |
382 | pixelSize = 1.0f / texture.height(); |
383 | break; |
384 | default: |
385 | SK_ABORT("Unknown filter direction." ); |
386 | } |
387 | pdman.set1f(fPixelSizeUni, pixelSize); |
388 | |
389 | if (m.useRange()) { |
390 | const float* range = m.range(); |
391 | if (MorphDirection::kY == m.direction() && |
392 | view.origin() == kBottomLeft_GrSurfaceOrigin) { |
393 | pdman.set2f(fRangeUni, 1.0f - (range[1]*pixelSize), 1.0f - (range[0]*pixelSize)); |
394 | } else { |
395 | pdman.set2f(fRangeUni, range[0] * pixelSize, range[1] * pixelSize); |
396 | } |
397 | } |
398 | } |
399 | |
400 | /////////////////////////////////////////////////////////////////////////////// |
401 | |
402 | GrMorphologyEffect::GrMorphologyEffect(GrSurfaceProxyView view, |
403 | SkAlphaType srcAlphaType, |
404 | MorphDirection direction, |
405 | int radius, |
406 | MorphType type, |
407 | const float range[2]) |
408 | : INHERITED(kGrMorphologyEffect_ClassID, ModulateForClampedSamplerOptFlags(srcAlphaType)) |
409 | , fCoordTransform(view.proxy(), view.origin()) |
410 | , fTextureSampler(std::move(view)) |
411 | , fDirection(direction) |
412 | , fRadius(radius) |
413 | , fType(type) |
414 | , fUseRange(SkToBool(range)) { |
415 | // Make sure the sampler's ctor uses the clamp wrap mode |
416 | SkASSERT(fTextureSampler.samplerState().wrapModeX() == GrSamplerState::WrapMode::kClamp && |
417 | fTextureSampler.samplerState().wrapModeY() == GrSamplerState::WrapMode::kClamp); |
418 | this->addCoordTransform(&fCoordTransform); |
419 | this->setTextureSamplerCnt(1); |
420 | if (fUseRange) { |
421 | fRange[0] = range[0]; |
422 | fRange[1] = range[1]; |
423 | } |
424 | } |
425 | |
426 | GrMorphologyEffect::GrMorphologyEffect(const GrMorphologyEffect& that) |
427 | : INHERITED(kGrMorphologyEffect_ClassID, that.optimizationFlags()) |
428 | , fCoordTransform(that.fCoordTransform) |
429 | , fTextureSampler(that.fTextureSampler) |
430 | , fDirection(that.fDirection) |
431 | , fRadius(that.fRadius) |
432 | , fType(that.fType) |
433 | , fUseRange(that.fUseRange) { |
434 | this->addCoordTransform(&fCoordTransform); |
435 | this->setTextureSamplerCnt(1); |
436 | if (that.fUseRange) { |
437 | fRange[0] = that.fRange[0]; |
438 | fRange[1] = that.fRange[1]; |
439 | } |
440 | } |
441 | |
442 | void GrMorphologyEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, |
443 | GrProcessorKeyBuilder* b) const { |
444 | GrGLMorphologyEffect::GenKey(*this, caps, b); |
445 | } |
446 | |
447 | GrGLSLFragmentProcessor* GrMorphologyEffect::onCreateGLSLInstance() const { |
448 | return new GrGLMorphologyEffect; |
449 | } |
450 | bool GrMorphologyEffect::onIsEqual(const GrFragmentProcessor& sBase) const { |
451 | const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>(); |
452 | return (this->radius() == s.radius() && |
453 | this->direction() == s.direction() && |
454 | this->useRange() == s.useRange() && |
455 | this->type() == s.type()); |
456 | } |
457 | |
458 | /////////////////////////////////////////////////////////////////////////////// |
459 | |
460 | GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMorphologyEffect); |
461 | |
462 | #if GR_TEST_UTILS |
463 | std::unique_ptr<GrFragmentProcessor> GrMorphologyEffect::TestCreate(GrProcessorTestData* d) { |
464 | auto [view, ct, at] = d->randomView(); |
465 | |
466 | MorphDirection dir = d->fRandom->nextBool() ? MorphDirection::kX : MorphDirection::kY; |
467 | static const int kMaxRadius = 10; |
468 | int radius = d->fRandom->nextRangeU(1, kMaxRadius); |
469 | MorphType type = d->fRandom->nextBool() ? MorphType::kErode : MorphType::kDilate; |
470 | return GrMorphologyEffect::Make(std::move(view), at, dir, radius, type); |
471 | } |
472 | #endif |
473 | |
474 | static void apply_morphology_rect(GrRenderTargetContext* renderTargetContext, |
475 | const GrClip& clip, |
476 | GrSurfaceProxyView view, |
477 | SkAlphaType srcAlphaType, |
478 | const SkIRect& srcRect, |
479 | const SkIRect& dstRect, |
480 | int radius, |
481 | MorphType morphType, |
482 | const float bounds[2], |
483 | MorphDirection direction) { |
484 | GrPaint paint; |
485 | paint.addColorFragmentProcessor(GrMorphologyEffect::Make(std::move(view), srcAlphaType, |
486 | direction, radius, morphType, bounds)); |
487 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
488 | renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), |
489 | SkRect::Make(dstRect), SkRect::Make(srcRect)); |
490 | } |
491 | |
492 | static void apply_morphology_rect_no_bounds(GrRenderTargetContext* renderTargetContext, |
493 | const GrClip& clip, |
494 | GrSurfaceProxyView view, |
495 | SkAlphaType srcAlphaType, |
496 | const SkIRect& srcRect, |
497 | const SkIRect& dstRect, |
498 | int radius, |
499 | MorphType morphType, |
500 | MorphDirection direction) { |
501 | GrPaint paint; |
502 | paint.addColorFragmentProcessor( |
503 | GrMorphologyEffect::Make(std::move(view), srcAlphaType, direction, radius, morphType)); |
504 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
505 | renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), |
506 | SkRect::Make(dstRect), SkRect::Make(srcRect)); |
507 | } |
508 | |
509 | static void apply_morphology_pass(GrRenderTargetContext* renderTargetContext, |
510 | const GrClip& clip, |
511 | GrSurfaceProxyView view, |
512 | SkAlphaType srcAlphaType, |
513 | const SkIRect& srcRect, |
514 | const SkIRect& dstRect, |
515 | int radius, |
516 | MorphType morphType, |
517 | MorphDirection direction) { |
518 | float bounds[2] = { 0.0f, 1.0f }; |
519 | SkIRect lowerSrcRect = srcRect, lowerDstRect = dstRect; |
520 | SkIRect middleSrcRect = srcRect, middleDstRect = dstRect; |
521 | SkIRect upperSrcRect = srcRect, upperDstRect = dstRect; |
522 | if (direction == MorphDirection::kX) { |
523 | bounds[0] = SkIntToScalar(srcRect.left()) + 0.5f; |
524 | bounds[1] = SkIntToScalar(srcRect.right()) - 0.5f; |
525 | lowerSrcRect.fRight = srcRect.left() + radius; |
526 | lowerDstRect.fRight = dstRect.left() + radius; |
527 | upperSrcRect.fLeft = srcRect.right() - radius; |
528 | upperDstRect.fLeft = dstRect.right() - radius; |
529 | middleSrcRect.inset(radius, 0); |
530 | middleDstRect.inset(radius, 0); |
531 | } else { |
532 | bounds[0] = SkIntToScalar(srcRect.top()) + 0.5f; |
533 | bounds[1] = SkIntToScalar(srcRect.bottom()) - 0.5f; |
534 | lowerSrcRect.fBottom = srcRect.top() + radius; |
535 | lowerDstRect.fBottom = dstRect.top() + radius; |
536 | upperSrcRect.fTop = srcRect.bottom() - radius; |
537 | upperDstRect.fTop = dstRect.bottom() - radius; |
538 | middleSrcRect.inset(0, radius); |
539 | middleDstRect.inset(0, radius); |
540 | } |
541 | if (middleSrcRect.width() <= 0) { |
542 | // radius covers srcRect; use bounds over entire draw |
543 | apply_morphology_rect(renderTargetContext, clip, std::move(view), srcAlphaType, srcRect, |
544 | dstRect, radius, morphType, bounds, direction); |
545 | } else { |
546 | // Draw upper and lower margins with bounds; middle without. |
547 | apply_morphology_rect(renderTargetContext, clip, view, srcAlphaType, lowerSrcRect, |
548 | lowerDstRect, radius, morphType, bounds, direction); |
549 | apply_morphology_rect(renderTargetContext, clip, view, srcAlphaType, upperSrcRect, |
550 | upperDstRect, radius, morphType, bounds, direction); |
551 | apply_morphology_rect_no_bounds(renderTargetContext, clip, std::move(view), |
552 | srcAlphaType, middleSrcRect, middleDstRect, radius, |
553 | morphType, direction); |
554 | } |
555 | } |
556 | |
557 | static sk_sp<SkSpecialImage> apply_morphology( |
558 | GrRecordingContext* context, SkSpecialImage* input, const SkIRect& rect, |
559 | MorphType morphType, SkISize radius, const SkImageFilter_Base::Context& ctx) { |
560 | GrSurfaceProxyView srcView = input->view(context); |
561 | SkAlphaType srcAlphaType = input->alphaType(); |
562 | SkASSERT(srcView.asTextureProxy()); |
563 | sk_sp<SkColorSpace> colorSpace = ctx.refColorSpace(); |
564 | GrColorType colorType = ctx.grColorType(); |
565 | |
566 | GrSurfaceProxy* proxy = srcView.proxy(); |
567 | |
568 | // setup new clip |
569 | const GrFixedClip clip(SkIRect::MakeSize(proxy->dimensions())); |
570 | |
571 | const SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height()); |
572 | SkIRect srcRect = rect; |
573 | // Map into proxy space |
574 | srcRect.offset(input->subset().x(), input->subset().y()); |
575 | SkASSERT(radius.width() > 0 || radius.height() > 0); |
576 | |
577 | if (radius.fWidth > 0) { |
578 | auto dstRTContext = GrRenderTargetContext::Make( |
579 | context, colorType, colorSpace, SkBackingFit::kApprox, rect.size(), 1, |
580 | GrMipMapped::kNo, proxy->isProtected(), kBottomLeft_GrSurfaceOrigin); |
581 | if (!dstRTContext) { |
582 | return nullptr; |
583 | } |
584 | |
585 | apply_morphology_pass(dstRTContext.get(), clip, std::move(srcView), srcAlphaType, |
586 | srcRect, dstRect, radius.fWidth, morphType, MorphDirection::kX); |
587 | SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom, |
588 | dstRect.width(), radius.fHeight); |
589 | SkPMColor4f clearColor = MorphType::kErode == morphType |
590 | ? SK_PMColor4fWHITE : SK_PMColor4fTRANSPARENT; |
591 | dstRTContext->clear(&clearRect, clearColor, GrRenderTargetContext::CanClearFullscreen::kNo); |
592 | |
593 | srcView = dstRTContext->readSurfaceView(); |
594 | srcAlphaType = dstRTContext->colorInfo().alphaType(); |
595 | srcRect = dstRect; |
596 | } |
597 | if (radius.fHeight > 0) { |
598 | auto dstRTContext = GrRenderTargetContext::Make( |
599 | context, colorType, colorSpace, SkBackingFit::kApprox, rect.size(), 1, |
600 | GrMipMapped::kNo, srcView.proxy()->isProtected(), kBottomLeft_GrSurfaceOrigin); |
601 | if (!dstRTContext) { |
602 | return nullptr; |
603 | } |
604 | |
605 | apply_morphology_pass(dstRTContext.get(), clip, std::move(srcView), srcAlphaType, |
606 | srcRect, dstRect, radius.fHeight, morphType, MorphDirection::kY); |
607 | |
608 | srcView = dstRTContext->readSurfaceView(); |
609 | } |
610 | |
611 | return SkSpecialImage::MakeDeferredFromGpu(context, |
612 | SkIRect::MakeWH(rect.width(), rect.height()), |
613 | kNeedNewImageUniqueID_SpecialImage, |
614 | std::move(srcView), colorType, |
615 | std::move(colorSpace), &input->props()); |
616 | } |
617 | #endif |
618 | |
619 | namespace { |
620 | |
621 | #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 |
622 | template<MorphType type, MorphDirection direction> |
623 | static void morph(const SkPMColor* src, SkPMColor* dst, |
624 | int radius, int width, int height, int srcStride, int dstStride) { |
625 | const int srcStrideX = direction == MorphDirection::kX ? 1 : srcStride; |
626 | const int dstStrideX = direction == MorphDirection::kX ? 1 : dstStride; |
627 | const int srcStrideY = direction == MorphDirection::kX ? srcStride : 1; |
628 | const int dstStrideY = direction == MorphDirection::kX ? dstStride : 1; |
629 | radius = std::min(radius, width - 1); |
630 | const SkPMColor* upperSrc = src + radius * srcStrideX; |
631 | for (int x = 0; x < width; ++x) { |
632 | const SkPMColor* lp = src; |
633 | const SkPMColor* up = upperSrc; |
634 | SkPMColor* dptr = dst; |
635 | for (int y = 0; y < height; ++y) { |
636 | __m128i extreme = (type == MorphType::kDilate) ? _mm_setzero_si128() |
637 | : _mm_set1_epi32(0xFFFFFFFF); |
638 | for (const SkPMColor* p = lp; p <= up; p += srcStrideX) { |
639 | __m128i src_pixel = _mm_cvtsi32_si128(*p); |
640 | extreme = (type == MorphType::kDilate) ? _mm_max_epu8(src_pixel, extreme) |
641 | : _mm_min_epu8(src_pixel, extreme); |
642 | } |
643 | *dptr = _mm_cvtsi128_si32(extreme); |
644 | dptr += dstStrideY; |
645 | lp += srcStrideY; |
646 | up += srcStrideY; |
647 | } |
648 | if (x >= radius) { src += srcStrideX; } |
649 | if (x + radius < width - 1) { upperSrc += srcStrideX; } |
650 | dst += dstStrideX; |
651 | } |
652 | } |
653 | |
654 | #elif defined(SK_ARM_HAS_NEON) |
655 | template<MorphType type, MorphDirection direction> |
656 | static void morph(const SkPMColor* src, SkPMColor* dst, |
657 | int radius, int width, int height, int srcStride, int dstStride) { |
658 | const int srcStrideX = direction == MorphDirection::kX ? 1 : srcStride; |
659 | const int dstStrideX = direction == MorphDirection::kX ? 1 : dstStride; |
660 | const int srcStrideY = direction == MorphDirection::kX ? srcStride : 1; |
661 | const int dstStrideY = direction == MorphDirection::kX ? dstStride : 1; |
662 | radius = std::min(radius, width - 1); |
663 | const SkPMColor* upperSrc = src + radius * srcStrideX; |
664 | for (int x = 0; x < width; ++x) { |
665 | const SkPMColor* lp = src; |
666 | const SkPMColor* up = upperSrc; |
667 | SkPMColor* dptr = dst; |
668 | for (int y = 0; y < height; ++y) { |
669 | uint8x8_t extreme = vdup_n_u8(type == MorphType::kDilate ? 0 : 255); |
670 | for (const SkPMColor* p = lp; p <= up; p += srcStrideX) { |
671 | uint8x8_t src_pixel = vreinterpret_u8_u32(vdup_n_u32(*p)); |
672 | extreme = (type == MorphType::kDilate) ? vmax_u8(src_pixel, extreme) |
673 | : vmin_u8(src_pixel, extreme); |
674 | } |
675 | *dptr = vget_lane_u32(vreinterpret_u32_u8(extreme), 0); |
676 | dptr += dstStrideY; |
677 | lp += srcStrideY; |
678 | up += srcStrideY; |
679 | } |
680 | if (x >= radius) src += srcStrideX; |
681 | if (x + radius < width - 1) upperSrc += srcStrideX; |
682 | dst += dstStrideX; |
683 | } |
684 | } |
685 | |
686 | #else |
687 | template<MorphType type, MorphDirection direction> |
688 | static void morph(const SkPMColor* src, SkPMColor* dst, |
689 | int radius, int width, int height, int srcStride, int dstStride) { |
690 | const int srcStrideX = direction == MorphDirection::kX ? 1 : srcStride; |
691 | const int dstStrideX = direction == MorphDirection::kX ? 1 : dstStride; |
692 | const int srcStrideY = direction == MorphDirection::kX ? srcStride : 1; |
693 | const int dstStrideY = direction == MorphDirection::kX ? dstStride : 1; |
694 | radius = std::min(radius, width - 1); |
695 | const SkPMColor* upperSrc = src + radius * srcStrideX; |
696 | for (int x = 0; x < width; ++x) { |
697 | const SkPMColor* lp = src; |
698 | const SkPMColor* up = upperSrc; |
699 | SkPMColor* dptr = dst; |
700 | for (int y = 0; y < height; ++y) { |
701 | // If we're maxing (dilate), start from 0; if minning (erode), start from 255. |
702 | const int start = (type == MorphType::kDilate) ? 0 : 255; |
703 | int B = start, G = start, R = start, A = start; |
704 | for (const SkPMColor* p = lp; p <= up; p += srcStrideX) { |
705 | int b = SkGetPackedB32(*p), |
706 | g = SkGetPackedG32(*p), |
707 | r = SkGetPackedR32(*p), |
708 | a = SkGetPackedA32(*p); |
709 | if (type == MorphType::kDilate) { |
710 | B = std::max(b, B); |
711 | G = std::max(g, G); |
712 | R = std::max(r, R); |
713 | A = std::max(a, A); |
714 | } else { |
715 | B = std::min(b, B); |
716 | G = std::min(g, G); |
717 | R = std::min(r, R); |
718 | A = std::min(a, A); |
719 | } |
720 | } |
721 | *dptr = SkPackARGB32(A, R, G, B); |
722 | dptr += dstStrideY; |
723 | lp += srcStrideY; |
724 | up += srcStrideY; |
725 | } |
726 | if (x >= radius) { src += srcStrideX; } |
727 | if (x + radius < width - 1) { upperSrc += srcStrideX; } |
728 | dst += dstStrideX; |
729 | } |
730 | } |
731 | #endif |
732 | } // namespace |
733 | |
734 | sk_sp<SkSpecialImage> SkMorphologyImageFilterImpl::onFilterImage(const Context& ctx, |
735 | SkIPoint* offset) const { |
736 | SkIPoint inputOffset = SkIPoint::Make(0, 0); |
737 | sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset)); |
738 | if (!input) { |
739 | return nullptr; |
740 | } |
741 | |
742 | SkIRect bounds; |
743 | input = this->applyCropRectAndPad(this->mapContext(ctx), input.get(), &inputOffset, &bounds); |
744 | if (!input) { |
745 | return nullptr; |
746 | } |
747 | |
748 | SkSize radius = mappedRadius(ctx.ctm()); |
749 | int width = SkScalarRoundToInt(radius.width()); |
750 | int height = SkScalarRoundToInt(radius.height()); |
751 | |
752 | // Width (or height) must fit in a signed 32-bit int to avoid UBSAN issues (crbug.com/1018190) |
753 | constexpr int kMaxRadius = (std::numeric_limits<int>::max() - 1) / 2; |
754 | |
755 | if (width < 0 || height < 0 || width > kMaxRadius || height > kMaxRadius) { |
756 | return nullptr; |
757 | } |
758 | |
759 | SkIRect srcBounds = bounds; |
760 | srcBounds.offset(-inputOffset); |
761 | |
762 | if (0 == width && 0 == height) { |
763 | offset->fX = bounds.left(); |
764 | offset->fY = bounds.top(); |
765 | return input->makeSubset(srcBounds); |
766 | } |
767 | |
768 | #if SK_SUPPORT_GPU |
769 | if (ctx.gpuBacked()) { |
770 | auto context = ctx.getContext(); |
771 | |
772 | // Ensure the input is in the destination color space. Typically applyCropRect will have |
773 | // called pad_image to account for our dilation of bounds, so the result will already be |
774 | // moved to the destination color space. If a filter DAG avoids that, then we use this |
775 | // fall-back, which saves us from having to do the xform during the filter itself. |
776 | input = ImageToColorSpace(input.get(), ctx.colorType(), ctx.colorSpace()); |
777 | |
778 | sk_sp<SkSpecialImage> result(apply_morphology(context, input.get(), srcBounds, fType, |
779 | SkISize::Make(width, height), ctx)); |
780 | if (result) { |
781 | offset->fX = bounds.left(); |
782 | offset->fY = bounds.top(); |
783 | } |
784 | return result; |
785 | } |
786 | #endif |
787 | |
788 | SkBitmap inputBM; |
789 | |
790 | if (!input->getROPixels(&inputBM)) { |
791 | return nullptr; |
792 | } |
793 | |
794 | if (inputBM.colorType() != kN32_SkColorType) { |
795 | return nullptr; |
796 | } |
797 | |
798 | SkImageInfo info = SkImageInfo::Make(bounds.size(), inputBM.colorType(), inputBM.alphaType()); |
799 | |
800 | SkBitmap dst; |
801 | if (!dst.tryAllocPixels(info)) { |
802 | return nullptr; |
803 | } |
804 | |
805 | SkMorphologyImageFilterImpl::Proc procX, procY; |
806 | |
807 | if (MorphType::kDilate == fType) { |
808 | procX = &morph<MorphType::kDilate, MorphDirection::kX>; |
809 | procY = &morph<MorphType::kDilate, MorphDirection::kY>; |
810 | } else { |
811 | procX = &morph<MorphType::kErode, MorphDirection::kX>; |
812 | procY = &morph<MorphType::kErode, MorphDirection::kY>; |
813 | } |
814 | |
815 | if (width > 0 && height > 0) { |
816 | SkBitmap tmp; |
817 | if (!tmp.tryAllocPixels(info)) { |
818 | return nullptr; |
819 | } |
820 | |
821 | call_proc_X(procX, inputBM, &tmp, width, srcBounds); |
822 | SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height()); |
823 | call_proc_Y(procY, |
824 | tmp.getAddr32(tmpBounds.left(), tmpBounds.top()), tmp.rowBytesAsPixels(), |
825 | &dst, height, tmpBounds); |
826 | } else if (width > 0) { |
827 | call_proc_X(procX, inputBM, &dst, width, srcBounds); |
828 | } else if (height > 0) { |
829 | call_proc_Y(procY, |
830 | inputBM.getAddr32(srcBounds.left(), srcBounds.top()), |
831 | inputBM.rowBytesAsPixels(), |
832 | &dst, height, srcBounds); |
833 | } |
834 | offset->fX = bounds.left(); |
835 | offset->fY = bounds.top(); |
836 | |
837 | return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), |
838 | dst, ctx.surfaceProps()); |
839 | } |
840 | |