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