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 "SkMorphologyImageFilter.h" 9 10#include "SkBitmap.h" 11#include "SkColorPriv.h" 12#include "SkOpts.h" 13#include "SkReadBuffer.h" 14#include "SkRect.h" 15#include "SkSpecialImage.h" 16#include "SkWriteBuffer.h" 17 18#if SK_SUPPORT_GPU 19#include "GrContext.h" 20#include "GrFixedClip.h" 21#include "GrRenderTargetContext.h" 22#include "GrTexture.h" 23#include "GrTextureProxy.h" 24 25#include "SkGr.h" 26#include "effects/Gr1DKernelEffect.h" 27#include "effects/GrProxyMove.h" 28#include "glsl/GrGLSLFragmentProcessor.h" 29#include "glsl/GrGLSLFragmentShaderBuilder.h" 30#include "glsl/GrGLSLProgramDataManager.h" 31#include "glsl/GrGLSLUniformHandler.h" 32#include "../private/GrGLSL.h" 33#endif 34 35sk_sp<SkImageFilter> SkDilateImageFilter::Make(int radiusX, int radiusY, 36 sk_sp<SkImageFilter> input, 37 const CropRect* cropRect) { 38 if (radiusX < 0 || radiusY < 0) { 39 return nullptr; 40 } 41 return sk_sp<SkImageFilter>(new SkDilateImageFilter(radiusX, radiusY, 42 std::move(input), 43 cropRect)); 44} 45 46 47sk_sp<SkImageFilter> SkErodeImageFilter::Make(int radiusX, int radiusY, 48 sk_sp<SkImageFilter> input, 49 const CropRect* cropRect) { 50 if (radiusX < 0 || radiusY < 0) { 51 return nullptr; 52 } 53 return sk_sp<SkImageFilter>(new SkErodeImageFilter(radiusX, radiusY, 54 std::move(input), 55 cropRect)); 56} 57 58SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, 59 int radiusY, 60 sk_sp<SkImageFilter> input, 61 const CropRect* cropRect) 62 : INHERITED(&input, 1, cropRect) 63 , fRadius(SkISize::Make(radiusX, radiusY)) { 64} 65 66void SkMorphologyImageFilter::flatten(SkWriteBuffer& buffer) const { 67 this->INHERITED::flatten(buffer); 68 buffer.writeInt(fRadius.fWidth); 69 buffer.writeInt(fRadius.fHeight); 70} 71 72static void call_proc_X(SkMorphologyImageFilter::Proc procX, 73 const SkBitmap& src, SkBitmap* dst, 74 int radiusX, const SkIRect& bounds) { 75 procX(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0), 76 radiusX, bounds.width(), bounds.height(), 77 src.rowBytesAsPixels(), dst->rowBytesAsPixels()); 78} 79 80static void call_proc_Y(SkMorphologyImageFilter::Proc procY, 81 const SkPMColor* src, int srcRowBytesAsPixels, SkBitmap* dst, 82 int radiusY, const SkIRect& bounds) { 83 procY(src, dst->getAddr32(0, 0), 84 radiusY, bounds.height(), bounds.width(), 85 srcRowBytesAsPixels, dst->rowBytesAsPixels()); 86} 87 88SkRect SkMorphologyImageFilter::computeFastBounds(const SkRect& src) const { 89 SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src; 90 bounds.outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height())); 91 return bounds; 92} 93 94SkIRect SkMorphologyImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, 95 MapDirection) const { 96 SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), 97 SkIntToScalar(this->radius().height())); 98 ctm.mapVectors(&radius, 1); 99 return src.makeOutset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y())); 100} 101 102sk_sp<SkFlattenable> SkErodeImageFilter::CreateProc(SkReadBuffer& buffer) { 103 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 104 const int width = buffer.readInt(); 105 const int height = buffer.readInt(); 106 return Make(width, height, common.getInput(0), &common.cropRect()); 107} 108 109sk_sp<SkFlattenable> SkDilateImageFilter::CreateProc(SkReadBuffer& buffer) { 110 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 111 const int width = buffer.readInt(); 112 const int height = buffer.readInt(); 113 return Make(width, height, common.getInput(0), &common.cropRect()); 114} 115 116#ifndef SK_IGNORE_TO_STRING 117void SkErodeImageFilter::toString(SkString* str) const { 118 str->appendf("SkErodeImageFilter: ("); 119 str->appendf("radius: (%d,%d)", this->radius().fWidth, this->radius().fHeight); 120 str->append(")"); 121} 122#endif 123 124#ifndef SK_IGNORE_TO_STRING 125void SkDilateImageFilter::toString(SkString* str) const { 126 str->appendf("SkDilateImageFilter: ("); 127 str->appendf("radius: (%d,%d)", this->radius().fWidth, this->radius().fHeight); 128 str->append(")"); 129} 130#endif 131 132#if SK_SUPPORT_GPU 133 134/////////////////////////////////////////////////////////////////////////////// 135/** 136 * Morphology effects. Depending upon the type of morphology, either the 137 * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the 138 * kernel is selected as the new color. The new color is modulated by the input 139 * color. 140 */ 141class GrMorphologyEffect : public Gr1DKernelEffect { 142public: 143 enum MorphologyType { 144 kErode_MorphologyType, 145 kDilate_MorphologyType, 146 }; 147 148 static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider, 149 sk_sp<GrTextureProxy> proxy, 150 Direction dir, int radius, MorphologyType type) { 151 return sk_sp<GrFragmentProcessor>(new GrMorphologyEffect(resourceProvider, std::move(proxy), 152 dir, radius, type)); 153 } 154 155 static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider, 156 sk_sp<GrTextureProxy> proxy, Direction dir, int radius, 157 MorphologyType type, const float bounds[2]) { 158 return sk_sp<GrFragmentProcessor>(new GrMorphologyEffect(resourceProvider, std::move(proxy), 159 dir, radius, type, bounds)); 160 } 161 162 ~GrMorphologyEffect() override; 163 164 MorphologyType type() const { return fType; } 165 bool useRange() const { return fUseRange; } 166 const float* range() const { return fRange; } 167 168 const char* name() const override { return "Morphology"; } 169 170protected: 171 172 MorphologyType fType; 173 bool fUseRange; 174 float fRange[2]; 175 176private: 177 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 178 179 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; 180 181 bool onIsEqual(const GrFragmentProcessor&) const override; 182 183 GrMorphologyEffect(GrResourceProvider*, sk_sp<GrTextureProxy>, 184 Direction, int radius, MorphologyType); 185 GrMorphologyEffect(GrResourceProvider*, sk_sp<GrTextureProxy>, 186 Direction, int radius, MorphologyType, const float bounds[2]); 187 188 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 189 190 typedef Gr1DKernelEffect INHERITED; 191}; 192 193/////////////////////////////////////////////////////////////////////////////// 194 195class GrGLMorphologyEffect : public GrGLSLFragmentProcessor { 196public: 197 void emitCode(EmitArgs&) override; 198 199 static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*); 200 201protected: 202 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override; 203 204private: 205 GrGLSLProgramDataManager::UniformHandle fPixelSizeUni; 206 GrGLSLProgramDataManager::UniformHandle fRangeUni; 207 208 typedef GrGLSLFragmentProcessor INHERITED; 209}; 210 211void GrGLMorphologyEffect::emitCode(EmitArgs& args) { 212 const GrMorphologyEffect& me = args.fFp.cast<GrMorphologyEffect>(); 213 214 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 215 fPixelSizeUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 216 kFloat_GrSLType, kDefault_GrSLPrecision, 217 "PixelSize"); 218 const char* pixelSizeInc = uniformHandler->getUniformCStr(fPixelSizeUni); 219 fRangeUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 220 kVec2f_GrSLType, kDefault_GrSLPrecision, 221 "Range"); 222 const char* range = uniformHandler->getUniformCStr(fRangeUni); 223 224 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 225 SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); 226 const char* func; 227 switch (me.type()) { 228 case GrMorphologyEffect::kErode_MorphologyType: 229 fragBuilder->codeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", args.fOutputColor); 230 func = "min"; 231 break; 232 case GrMorphologyEffect::kDilate_MorphologyType: 233 fragBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", args.fOutputColor); 234 func = "max"; 235 break; 236 default: 237 SkFAIL("Unexpected type"); 238 func = ""; // suppress warning 239 break; 240 } 241 242 const char* dir; 243 switch (me.direction()) { 244 case Gr1DKernelEffect::kX_Direction: 245 dir = "x"; 246 break; 247 case Gr1DKernelEffect::kY_Direction: 248 dir = "y"; 249 break; 250 default: 251 SkFAIL("Unknown filter direction."); 252 dir = ""; // suppress warning 253 } 254 255 int width = GrMorphologyEffect::WidthFromRadius(me.radius()); 256 257 // vec2 coord = coord2D; 258 fragBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); 259 // coord.x -= radius * pixelSize; 260 fragBuilder->codeAppendf("\t\tcoord.%s -= %d.0 * %s; \n", dir, me.radius(), pixelSizeInc); 261 if (me.useRange()) { 262 // highBound = min(highBound, coord.x + (width-1) * pixelSize); 263 fragBuilder->codeAppendf("\t\tfloat highBound = min(%s.y, coord.%s + %f * %s);", 264 range, dir, float(width - 1), pixelSizeInc); 265 // coord.x = max(lowBound, coord.x); 266 fragBuilder->codeAppendf("\t\tcoord.%s = max(%s.x, coord.%s);", dir, range, dir); 267 } 268 fragBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", width); 269 fragBuilder->codeAppendf("\t\t\t%s = %s(%s, ", args.fOutputColor, func, args.fOutputColor); 270 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coord"); 271 fragBuilder->codeAppend(");\n"); 272 // coord.x += pixelSize; 273 fragBuilder->codeAppendf("\t\t\tcoord.%s += %s;\n", dir, pixelSizeInc); 274 if (me.useRange()) { 275 // coord.x = min(highBound, coord.x); 276 fragBuilder->codeAppendf("\t\t\tcoord.%s = min(highBound, coord.%s);", dir, dir); 277 } 278 fragBuilder->codeAppend("\t\t}\n"); 279 SkString modulate; 280 GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); 281 fragBuilder->codeAppend(modulate.c_str()); 282} 283 284void GrGLMorphologyEffect::GenKey(const GrProcessor& proc, 285 const GrShaderCaps&, GrProcessorKeyBuilder* b) { 286 const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>(); 287 uint32_t key = static_cast<uint32_t>(m.radius()); 288 key |= (m.type() << 8); 289 key |= (m.direction() << 9); 290 if (m.useRange()) { 291 key |= 1 << 10; 292 } 293 b->add32(key); 294} 295 296void GrGLMorphologyEffect::onSetData(const GrGLSLProgramDataManager& pdman, 297 const GrProcessor& proc) { 298 const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>(); 299 GrTexture& texture = *m.textureSampler(0).texture(); 300 301 float pixelSize = 0.0f; 302 switch (m.direction()) { 303 case Gr1DKernelEffect::kX_Direction: 304 pixelSize = 1.0f / texture.width(); 305 break; 306 case Gr1DKernelEffect::kY_Direction: 307 pixelSize = 1.0f / texture.height(); 308 break; 309 default: 310 SkFAIL("Unknown filter direction."); 311 } 312 pdman.set1f(fPixelSizeUni, pixelSize); 313 314 if (m.useRange()) { 315 const float* range = m.range(); 316 if (Gr1DKernelEffect::kY_Direction == m.direction() && 317 texture.origin() == kBottomLeft_GrSurfaceOrigin) { 318 pdman.set2f(fRangeUni, 1.0f - (range[1]*pixelSize), 1.0f - (range[0]*pixelSize)); 319 } else { 320 pdman.set2f(fRangeUni, range[0] * pixelSize, range[1] * pixelSize); 321 } 322 } 323} 324 325/////////////////////////////////////////////////////////////////////////////// 326 327GrMorphologyEffect::GrMorphologyEffect(GrResourceProvider* resourceProvider, 328 sk_sp<GrTextureProxy> proxy, 329 Direction direction, 330 int radius, 331 MorphologyType type) 332 : INHERITED{resourceProvider, 333 ModulationFlags(proxy->config()), 334 GR_PROXY_MOVE(proxy), 335 direction, radius} 336 , fType(type) 337 , fUseRange(false) { 338 this->initClassID<GrMorphologyEffect>(); 339} 340 341GrMorphologyEffect::GrMorphologyEffect(GrResourceProvider* resourceProvider, 342 sk_sp<GrTextureProxy> proxy, 343 Direction direction, 344 int radius, 345 MorphologyType type, 346 const float range[2]) 347 : INHERITED{resourceProvider, 348 ModulationFlags(proxy->config()), 349 GR_PROXY_MOVE(proxy), 350 direction, radius} 351 , fType(type) 352 , fUseRange(true) { 353 this->initClassID<GrMorphologyEffect>(); 354 fRange[0] = range[0]; 355 fRange[1] = range[1]; 356} 357 358GrMorphologyEffect::~GrMorphologyEffect() { 359} 360 361void GrMorphologyEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, 362 GrProcessorKeyBuilder* b) const { 363 GrGLMorphologyEffect::GenKey(*this, caps, b); 364} 365 366GrGLSLFragmentProcessor* GrMorphologyEffect::onCreateGLSLInstance() const { 367 return new GrGLMorphologyEffect; 368} 369bool GrMorphologyEffect::onIsEqual(const GrFragmentProcessor& sBase) const { 370 const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>(); 371 return (this->radius() == s.radius() && 372 this->direction() == s.direction() && 373 this->useRange() == s.useRange() && 374 this->type() == s.type()); 375} 376 377/////////////////////////////////////////////////////////////////////////////// 378 379GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMorphologyEffect); 380 381#if GR_TEST_UTILS 382sk_sp<GrFragmentProcessor> GrMorphologyEffect::TestCreate(GrProcessorTestData* d) { 383 int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx 384 : GrProcessorUnitTest::kAlphaTextureIdx; 385 sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx); 386 387 Direction dir = d->fRandom->nextBool() ? kX_Direction : kY_Direction; 388 static const int kMaxRadius = 10; 389 int radius = d->fRandom->nextRangeU(1, kMaxRadius); 390 MorphologyType type = d->fRandom->nextBool() ? GrMorphologyEffect::kErode_MorphologyType 391 : GrMorphologyEffect::kDilate_MorphologyType; 392 393 return GrMorphologyEffect::Make(d->resourceProvider(), 394 std::move(proxy), dir, radius, type); 395} 396#endif 397 398 399static void apply_morphology_rect(GrRenderTargetContext* renderTargetContext, 400 const GrClip& clip, 401 sk_sp<GrTextureProxy> proxy, 402 const SkIRect& srcRect, 403 const SkIRect& dstRect, 404 int radius, 405 GrMorphologyEffect::MorphologyType morphType, 406 const float bounds[2], 407 Gr1DKernelEffect::Direction direction) { 408 GrPaint paint; 409 paint.setGammaCorrect(renderTargetContext->isGammaCorrect()); 410 411 GrResourceProvider* resourceProvider = renderTargetContext->resourceProvider(); 412 413 paint.addColorFragmentProcessor(GrMorphologyEffect::Make(resourceProvider, std::move(proxy), 414 direction, radius, morphType, 415 bounds)); 416 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 417 renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), 418 SkRect::Make(dstRect), SkRect::Make(srcRect)); 419} 420 421static void apply_morphology_rect_no_bounds(GrRenderTargetContext* renderTargetContext, 422 const GrClip& clip, 423 sk_sp<GrTextureProxy> proxy, 424 const SkIRect& srcRect, 425 const SkIRect& dstRect, 426 int radius, 427 GrMorphologyEffect::MorphologyType morphType, 428 Gr1DKernelEffect::Direction direction) { 429 GrPaint paint; 430 paint.setGammaCorrect(renderTargetContext->isGammaCorrect()); 431 432 GrResourceProvider* resourceProvider = renderTargetContext->resourceProvider(); 433 434 paint.addColorFragmentProcessor(GrMorphologyEffect::Make(resourceProvider, std::move(proxy), 435 direction, radius, morphType)); 436 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 437 renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), 438 SkRect::Make(dstRect), SkRect::Make(srcRect)); 439} 440 441static void apply_morphology_pass(GrRenderTargetContext* renderTargetContext, 442 const GrClip& clip, 443 sk_sp<GrTextureProxy> textureProxy, 444 const SkIRect& srcRect, 445 const SkIRect& dstRect, 446 int radius, 447 GrMorphologyEffect::MorphologyType morphType, 448 Gr1DKernelEffect::Direction direction) { 449 float bounds[2] = { 0.0f, 1.0f }; 450 SkIRect lowerSrcRect = srcRect, lowerDstRect = dstRect; 451 SkIRect middleSrcRect = srcRect, middleDstRect = dstRect; 452 SkIRect upperSrcRect = srcRect, upperDstRect = dstRect; 453 if (direction == Gr1DKernelEffect::kX_Direction) { 454 bounds[0] = SkIntToScalar(srcRect.left()) + 0.5f; 455 bounds[1] = SkIntToScalar(srcRect.right()) - 0.5f; 456 lowerSrcRect.fRight = srcRect.left() + radius; 457 lowerDstRect.fRight = dstRect.left() + radius; 458 upperSrcRect.fLeft = srcRect.right() - radius; 459 upperDstRect.fLeft = dstRect.right() - radius; 460 middleSrcRect.inset(radius, 0); 461 middleDstRect.inset(radius, 0); 462 } else { 463 bounds[0] = SkIntToScalar(srcRect.top()) + 0.5f; 464 bounds[1] = SkIntToScalar(srcRect.bottom()) - 0.5f; 465 lowerSrcRect.fBottom = srcRect.top() + radius; 466 lowerDstRect.fBottom = dstRect.top() + radius; 467 upperSrcRect.fTop = srcRect.bottom() - radius; 468 upperDstRect.fTop = dstRect.bottom() - radius; 469 middleSrcRect.inset(0, radius); 470 middleDstRect.inset(0, radius); 471 } 472 if (middleSrcRect.fLeft - middleSrcRect.fRight >= 0) { 473 // radius covers srcRect; use bounds over entire draw 474 apply_morphology_rect(renderTargetContext, clip, std::move(textureProxy), 475 srcRect, dstRect, radius, morphType, bounds, direction); 476 } else { 477 // Draw upper and lower margins with bounds; middle without. 478 apply_morphology_rect(renderTargetContext, clip, textureProxy, 479 lowerSrcRect, lowerDstRect, radius, morphType, bounds, direction); 480 apply_morphology_rect(renderTargetContext, clip, textureProxy, 481 upperSrcRect, upperDstRect, radius, morphType, bounds, direction); 482 apply_morphology_rect_no_bounds(renderTargetContext, clip, std::move(textureProxy), 483 middleSrcRect, middleDstRect, radius, morphType, direction); 484 } 485} 486 487static sk_sp<SkSpecialImage> apply_morphology( 488 GrContext* context, 489 SkSpecialImage* input, 490 const SkIRect& rect, 491 GrMorphologyEffect::MorphologyType morphType, 492 SkISize radius, 493 const SkImageFilter::OutputProperties& outputProperties) { 494 sk_sp<GrTextureProxy> srcTexture(input->asTextureProxyRef(context)); 495 SkASSERT(srcTexture); 496 sk_sp<SkColorSpace> colorSpace = sk_ref_sp(outputProperties.colorSpace()); 497 GrPixelConfig config = GrRenderableConfigForColorSpace(colorSpace.get()); 498 499 // setup new clip 500 const GrFixedClip clip(SkIRect::MakeWH(srcTexture->width(), srcTexture->height())); 501 502 const SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height()); 503 SkIRect srcRect = rect; 504 505 SkASSERT(radius.width() > 0 || radius.height() > 0); 506 507 if (radius.fWidth > 0) { 508 sk_sp<GrRenderTargetContext> dstRTContext(context->makeDeferredRenderTargetContext( 509 SkBackingFit::kApprox, rect.width(), rect.height(), config, colorSpace)); 510 if (!dstRTContext) { 511 return nullptr; 512 } 513 514 apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), 515 srcRect, dstRect, radius.fWidth, morphType, 516 Gr1DKernelEffect::kX_Direction); 517 SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom, 518 dstRect.width(), radius.fHeight); 519 GrColor clearColor = GrMorphologyEffect::kErode_MorphologyType == morphType 520 ? SK_ColorWHITE 521 : SK_ColorTRANSPARENT; 522 dstRTContext->clear(&clearRect, clearColor, false); 523 524 srcTexture = dstRTContext->asTextureProxyRef(); 525 srcRect = dstRect; 526 } 527 if (radius.fHeight > 0) { 528 sk_sp<GrRenderTargetContext> dstRTContext(context->makeDeferredRenderTargetContext( 529 SkBackingFit::kApprox, rect.width(), rect.height(), config, colorSpace)); 530 if (!dstRTContext) { 531 return nullptr; 532 } 533 534 apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), 535 srcRect, dstRect, radius.fHeight, morphType, 536 Gr1DKernelEffect::kY_Direction); 537 538 srcTexture = dstRTContext->asTextureProxyRef(); 539 } 540 541 return SkSpecialImage::MakeDeferredFromGpu(context, 542 SkIRect::MakeWH(rect.width(), rect.height()), 543 kNeedNewImageUniqueID_SpecialImage, 544 std::move(srcTexture), std::move(colorSpace), 545 &input->props()); 546} 547#endif 548 549sk_sp<SkSpecialImage> SkMorphologyImageFilter::onFilterImage(SkSpecialImage* source, 550 const Context& ctx, 551 SkIPoint* offset) const { 552 SkIPoint inputOffset = SkIPoint::Make(0, 0); 553 sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); 554 if (!input) { 555 return nullptr; 556 } 557 558 SkIRect bounds; 559 input = this->applyCropRect(this->mapContext(ctx), input.get(), &inputOffset, &bounds); 560 if (!input) { 561 return nullptr; 562 } 563 564 SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), 565 SkIntToScalar(this->radius().height())); 566 ctx.ctm().mapVectors(&radius, 1); 567 int width = SkScalarFloorToInt(radius.fX); 568 int height = SkScalarFloorToInt(radius.fY); 569 570 if (width < 0 || height < 0) { 571 return nullptr; 572 } 573 574 SkIRect srcBounds = bounds; 575 srcBounds.offset(-inputOffset); 576 577 if (0 == width && 0 == height) { 578 offset->fX = bounds.left(); 579 offset->fY = bounds.top(); 580 return input->makeSubset(srcBounds); 581 } 582 583#if SK_SUPPORT_GPU 584 if (source->isTextureBacked()) { 585 GrContext* context = source->getContext(); 586 587 // Ensure the input is in the destination color space. Typically applyCropRect will have 588 // called pad_image to account for our dilation of bounds, so the result will already be 589 // moved to the destination color space. If a filter DAG avoids that, then we use this 590 // fall-back, which saves us from having to do the xform during the filter itself. 591 input = ImageToColorSpace(input.get(), ctx.outputProperties()); 592 593 auto type = (kDilate_Op == this->op()) ? GrMorphologyEffect::kDilate_MorphologyType 594 : GrMorphologyEffect::kErode_MorphologyType; 595 sk_sp<SkSpecialImage> result(apply_morphology(context, input.get(), srcBounds, type, 596 SkISize::Make(width, height), 597 ctx.outputProperties())); 598 if (result) { 599 offset->fX = bounds.left(); 600 offset->fY = bounds.top(); 601 } 602 return result; 603 } 604#endif 605 606 SkBitmap inputBM; 607 608 if (!input->getROPixels(&inputBM)) { 609 return nullptr; 610 } 611 612 if (inputBM.colorType() != kN32_SkColorType) { 613 return nullptr; 614 } 615 616 SkImageInfo info = SkImageInfo::Make(bounds.width(), bounds.height(), 617 inputBM.colorType(), inputBM.alphaType()); 618 619 SkBitmap dst; 620 if (!dst.tryAllocPixels(info)) { 621 return nullptr; 622 } 623 624 SkAutoLockPixels inputLock(inputBM), dstLock(dst); 625 626 SkMorphologyImageFilter::Proc procX, procY; 627 628 if (kDilate_Op == this->op()) { 629 procX = SkOpts::dilate_x; 630 procY = SkOpts::dilate_y; 631 } else { 632 procX = SkOpts::erode_x; 633 procY = SkOpts::erode_y; 634 } 635 636 if (width > 0 && height > 0) { 637 SkBitmap tmp; 638 if (!tmp.tryAllocPixels(info)) { 639 return nullptr; 640 } 641 642 SkAutoLockPixels tmpLock(tmp); 643 644 call_proc_X(procX, inputBM, &tmp, width, srcBounds); 645 SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height()); 646 call_proc_Y(procY, 647 tmp.getAddr32(tmpBounds.left(), tmpBounds.top()), tmp.rowBytesAsPixels(), 648 &dst, height, tmpBounds); 649 } else if (width > 0) { 650 call_proc_X(procX, inputBM, &dst, width, srcBounds); 651 } else if (height > 0) { 652 call_proc_Y(procY, 653 inputBM.getAddr32(srcBounds.left(), srcBounds.top()), 654 inputBM.rowBytesAsPixels(), 655 &dst, height, srcBounds); 656 } 657 offset->fX = bounds.left(); 658 offset->fY = bounds.top(); 659 660 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), 661 dst, &source->props()); 662} 663