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#include "SkBitmap.h" 10#include "SkColorPriv.h" 11#include "SkFlattenableBuffers.h" 12#include "SkRect.h" 13#include "SkMorphology_opts.h" 14#if SK_SUPPORT_GPU 15#include "GrContext.h" 16#include "GrTexture.h" 17#include "GrTBackendEffectFactory.h" 18#include "gl/GrGLEffect.h" 19#include "effects/Gr1DKernelEffect.h" 20#include "SkImageFilterUtils.h" 21#endif 22 23SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer) 24 : INHERITED(1, buffer) { 25 fRadius.fWidth = buffer.readInt(); 26 fRadius.fHeight = buffer.readInt(); 27 buffer.validate((fRadius.fWidth >= 0) && 28 (fRadius.fHeight >= 0)); 29} 30 31SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input, const CropRect* cropRect) 32 : INHERITED(input, cropRect), fRadius(SkISize::Make(radiusX, radiusY)) { 33} 34 35 36void SkMorphologyImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { 37 this->INHERITED::flatten(buffer); 38 buffer.writeInt(fRadius.fWidth); 39 buffer.writeInt(fRadius.fHeight); 40} 41 42enum MorphDirection { 43 kX, kY 44}; 45 46template<MorphDirection direction> 47static void erode(const SkPMColor* src, SkPMColor* dst, 48 int radius, int width, int height, 49 int srcStride, int dstStride) 50{ 51 const int srcStrideX = direction == kX ? 1 : srcStride; 52 const int dstStrideX = direction == kX ? 1 : dstStride; 53 const int srcStrideY = direction == kX ? srcStride : 1; 54 const int dstStrideY = direction == kX ? dstStride : 1; 55 radius = SkMin32(radius, width - 1); 56 const SkPMColor* upperSrc = src + radius * srcStrideX; 57 for (int x = 0; x < width; ++x) { 58 const SkPMColor* lp = src; 59 const SkPMColor* up = upperSrc; 60 SkPMColor* dptr = dst; 61 for (int y = 0; y < height; ++y) { 62 int minB = 255, minG = 255, minR = 255, minA = 255; 63 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) { 64 int b = SkGetPackedB32(*p); 65 int g = SkGetPackedG32(*p); 66 int r = SkGetPackedR32(*p); 67 int a = SkGetPackedA32(*p); 68 if (b < minB) minB = b; 69 if (g < minG) minG = g; 70 if (r < minR) minR = r; 71 if (a < minA) minA = a; 72 } 73 *dptr = SkPackARGB32(minA, minR, minG, minB); 74 dptr += dstStrideY; 75 lp += srcStrideY; 76 up += srcStrideY; 77 } 78 if (x >= radius) src += srcStrideX; 79 if (x + radius < width - 1) upperSrc += srcStrideX; 80 dst += dstStrideX; 81 } 82} 83 84static void erodeX(const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds) 85{ 86 SkMorphologyProc erodeXProc = SkMorphologyGetPlatformProc(kErodeX_SkMorphologyProcType); 87 if (!erodeXProc) { 88 erodeXProc = erode<kX>; 89 } 90 erodeXProc(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0), 91 radiusX, bounds.width(), bounds.height(), 92 src.rowBytesAsPixels(), dst->rowBytesAsPixels()); 93} 94 95static void erodeY(const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds) 96{ 97 SkMorphologyProc erodeYProc = SkMorphologyGetPlatformProc(kErodeY_SkMorphologyProcType); 98 if (!erodeYProc) { 99 erodeYProc = erode<kY>; 100 } 101 erodeYProc(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0), 102 radiusY, bounds.height(), bounds.width(), 103 src.rowBytesAsPixels(), dst->rowBytesAsPixels()); 104} 105 106template<MorphDirection direction> 107static void dilate(const SkPMColor* src, SkPMColor* dst, 108 int radius, int width, int height, 109 int srcStride, int dstStride) 110{ 111 const int srcStrideX = direction == kX ? 1 : srcStride; 112 const int dstStrideX = direction == kX ? 1 : dstStride; 113 const int srcStrideY = direction == kX ? srcStride : 1; 114 const int dstStrideY = direction == kX ? dstStride : 1; 115 radius = SkMin32(radius, width - 1); 116 const SkPMColor* upperSrc = src + radius * srcStrideX; 117 for (int x = 0; x < width; ++x) { 118 const SkPMColor* lp = src; 119 const SkPMColor* up = upperSrc; 120 SkPMColor* dptr = dst; 121 for (int y = 0; y < height; ++y) { 122 int maxB = 0, maxG = 0, maxR = 0, maxA = 0; 123 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) { 124 int b = SkGetPackedB32(*p); 125 int g = SkGetPackedG32(*p); 126 int r = SkGetPackedR32(*p); 127 int a = SkGetPackedA32(*p); 128 if (b > maxB) maxB = b; 129 if (g > maxG) maxG = g; 130 if (r > maxR) maxR = r; 131 if (a > maxA) maxA = a; 132 } 133 *dptr = SkPackARGB32(maxA, maxR, maxG, maxB); 134 dptr += dstStrideY; 135 lp += srcStrideY; 136 up += srcStrideY; 137 } 138 if (x >= radius) src += srcStrideX; 139 if (x + radius < width - 1) upperSrc += srcStrideX; 140 dst += dstStrideX; 141 } 142} 143 144static void dilateX(const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds) 145{ 146 SkMorphologyProc dilateXProc = SkMorphologyGetPlatformProc(kDilateX_SkMorphologyProcType); 147 if (!dilateXProc) { 148 dilateXProc = dilate<kX>; 149 } 150 dilateXProc(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0), 151 radiusX, bounds.width(), bounds.height(), 152 src.rowBytesAsPixels(), dst->rowBytesAsPixels()); 153} 154 155static void dilateY(const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds) 156{ 157 SkMorphologyProc dilateYProc = SkMorphologyGetPlatformProc(kDilateY_SkMorphologyProcType); 158 if (!dilateYProc) { 159 dilateYProc = dilate<kY>; 160 } 161 dilateYProc(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0), 162 radiusY, bounds.height(), bounds.width(), 163 src.rowBytesAsPixels(), dst->rowBytesAsPixels()); 164} 165 166bool SkErodeImageFilter::onFilterImage(Proxy* proxy, 167 const SkBitmap& source, const SkMatrix& ctm, 168 SkBitmap* dst, SkIPoint* offset) { 169 SkBitmap src = source; 170 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) { 171 return false; 172 } 173 174 if (src.config() != SkBitmap::kARGB_8888_Config) { 175 return false; 176 } 177 178 SkIRect bounds; 179 src.getBounds(&bounds); 180 if (!this->applyCropRect(&bounds, ctm)) { 181 return false; 182 } 183 184 SkAutoLockPixels alp(src); 185 if (!src.getPixels()) { 186 return false; 187 } 188 189 dst->setConfig(src.config(), bounds.width(), bounds.height()); 190 dst->allocPixels(); 191 if (!dst->getPixels()) { 192 return false; 193 } 194 195 int width = radius().width(); 196 int height = radius().height(); 197 198 if (width < 0 || height < 0) { 199 return false; 200 } 201 202 if (width == 0 && height == 0) { 203 src.extractSubset(dst, bounds); 204 offset->fX += bounds.left(); 205 offset->fY += bounds.top(); 206 return true; 207 } 208 209 SkBitmap temp; 210 temp.setConfig(dst->config(), dst->width(), dst->height()); 211 if (!temp.allocPixels()) { 212 return false; 213 } 214 215 if (width > 0 && height > 0) { 216 erodeX(src, &temp, width, bounds); 217 SkIRect tmpBounds = SkIRect::MakeWH(bounds.width(), bounds.height()); 218 erodeY(temp, dst, height, tmpBounds); 219 } else if (width > 0) { 220 erodeX(src, dst, width, bounds); 221 } else if (height > 0) { 222 erodeY(src, dst, height, bounds); 223 } 224 offset->fX += bounds.left(); 225 offset->fY += bounds.top(); 226 return true; 227} 228 229bool SkDilateImageFilter::onFilterImage(Proxy* proxy, 230 const SkBitmap& source, const SkMatrix& ctm, 231 SkBitmap* dst, SkIPoint* offset) { 232 SkBitmap src = source; 233 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) { 234 return false; 235 } 236 if (src.config() != SkBitmap::kARGB_8888_Config) { 237 return false; 238 } 239 240 SkIRect bounds; 241 src.getBounds(&bounds); 242 if (!this->applyCropRect(&bounds, ctm)) { 243 return false; 244 } 245 246 SkAutoLockPixels alp(src); 247 if (!src.getPixels()) { 248 return false; 249 } 250 251 dst->setConfig(src.config(), bounds.width(), bounds.height()); 252 dst->allocPixels(); 253 if (!dst->getPixels()) { 254 return false; 255 } 256 257 int width = radius().width(); 258 int height = radius().height(); 259 260 if (width < 0 || height < 0) { 261 return false; 262 } 263 264 if (width == 0 && height == 0) { 265 src.extractSubset(dst, bounds); 266 offset->fX += bounds.left(); 267 offset->fY += bounds.top(); 268 return true; 269 } 270 271 SkBitmap temp; 272 temp.setConfig(dst->config(), dst->width(), dst->height()); 273 if (!temp.allocPixels()) { 274 return false; 275 } 276 277 if (width > 0 && height > 0) { 278 dilateX(src, &temp, width, bounds); 279 SkIRect tmpBounds = SkIRect::MakeWH(bounds.width(), bounds.height()); 280 dilateY(temp, dst, height, tmpBounds); 281 } else if (width > 0) { 282 dilateX(src, dst, width, bounds); 283 } else if (height > 0) { 284 dilateY(src, dst, height, bounds); 285 } 286 offset->fX += bounds.left(); 287 offset->fY += bounds.top(); 288 return true; 289} 290 291#if SK_SUPPORT_GPU 292 293/////////////////////////////////////////////////////////////////////////////// 294 295class GrGLMorphologyEffect; 296 297/** 298 * Morphology effects. Depending upon the type of morphology, either the 299 * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the 300 * kernel is selected as the new color. The new color is modulated by the input 301 * color. 302 */ 303class GrMorphologyEffect : public Gr1DKernelEffect { 304 305public: 306 307 enum MorphologyType { 308 kErode_MorphologyType, 309 kDilate_MorphologyType, 310 }; 311 312 static GrEffectRef* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type) { 313 AutoEffectUnref effect(SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type))); 314 return CreateEffectRef(effect); 315 } 316 317 virtual ~GrMorphologyEffect(); 318 319 MorphologyType type() const { return fType; } 320 321 static const char* Name() { return "Morphology"; } 322 323 typedef GrGLMorphologyEffect GLEffect; 324 325 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; 326 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; 327 328protected: 329 330 MorphologyType fType; 331 332private: 333 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; 334 335 GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType); 336 337 GR_DECLARE_EFFECT_TEST; 338 339 typedef Gr1DKernelEffect INHERITED; 340}; 341 342/////////////////////////////////////////////////////////////////////////////// 343 344class GrGLMorphologyEffect : public GrGLEffect { 345public: 346 GrGLMorphologyEffect (const GrBackendEffectFactory&, const GrDrawEffect&); 347 348 virtual void emitCode(GrGLShaderBuilder*, 349 const GrDrawEffect&, 350 EffectKey, 351 const char* outputColor, 352 const char* inputColor, 353 const TransformedCoordsArray&, 354 const TextureSamplerArray&) SK_OVERRIDE; 355 356 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); 357 358 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 359 360private: 361 int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); } 362 363 int fRadius; 364 GrMorphologyEffect::MorphologyType fType; 365 GrGLUniformManager::UniformHandle fImageIncrementUni; 366 367 typedef GrGLEffect INHERITED; 368}; 369 370GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendEffectFactory& factory, 371 const GrDrawEffect& drawEffect) 372 : INHERITED(factory) { 373 const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>(); 374 fRadius = m.radius(); 375 fType = m.type(); 376} 377 378void GrGLMorphologyEffect::emitCode(GrGLShaderBuilder* builder, 379 const GrDrawEffect&, 380 EffectKey key, 381 const char* outputColor, 382 const char* inputColor, 383 const TransformedCoordsArray& coords, 384 const TextureSamplerArray& samplers) { 385 SkString coords2D = builder->ensureFSCoords2D(coords, 0); 386 fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 387 kVec2f_GrSLType, "ImageIncrement"); 388 389 const char* func; 390 switch (fType) { 391 case GrMorphologyEffect::kErode_MorphologyType: 392 builder->fsCodeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor); 393 func = "min"; 394 break; 395 case GrMorphologyEffect::kDilate_MorphologyType: 396 builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor); 397 func = "max"; 398 break; 399 default: 400 GrCrash("Unexpected type"); 401 func = ""; // suppress warning 402 break; 403 } 404 const char* imgInc = builder->getUniformCStr(fImageIncrementUni); 405 406 builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc); 407 builder->fsCodeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width()); 408 builder->fsCodeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor); 409 builder->fsAppendTextureLookup(samplers[0], "coord"); 410 builder->fsCodeAppend(");\n"); 411 builder->fsCodeAppendf("\t\t\tcoord += %s;\n", imgInc); 412 builder->fsCodeAppend("\t\t}\n"); 413 SkString modulate; 414 GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor); 415 builder->fsCodeAppend(modulate.c_str()); 416} 417 418GrGLEffect::EffectKey GrGLMorphologyEffect::GenKey(const GrDrawEffect& drawEffect, 419 const GrGLCaps&) { 420 const GrMorphologyEffect& m = drawEffect.castEffect<GrMorphologyEffect>(); 421 EffectKey key = static_cast<EffectKey>(m.radius()); 422 key |= (m.type() << 8); 423 return key; 424} 425 426void GrGLMorphologyEffect::setData(const GrGLUniformManager& uman, 427 const GrDrawEffect& drawEffect) { 428 const Gr1DKernelEffect& kern = drawEffect.castEffect<Gr1DKernelEffect>(); 429 GrTexture& texture = *kern.texture(0); 430 // the code we generated was for a specific kernel radius 431 SkASSERT(kern.radius() == fRadius); 432 float imageIncrement[2] = { 0 }; 433 switch (kern.direction()) { 434 case Gr1DKernelEffect::kX_Direction: 435 imageIncrement[0] = 1.0f / texture.width(); 436 break; 437 case Gr1DKernelEffect::kY_Direction: 438 imageIncrement[1] = 1.0f / texture.height(); 439 break; 440 default: 441 GrCrash("Unknown filter direction."); 442 } 443 uman.set2fv(fImageIncrementUni, 1, imageIncrement); 444} 445 446/////////////////////////////////////////////////////////////////////////////// 447 448GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture, 449 Direction direction, 450 int radius, 451 MorphologyType type) 452 : Gr1DKernelEffect(texture, direction, radius) 453 , fType(type) { 454} 455 456GrMorphologyEffect::~GrMorphologyEffect() { 457} 458 459const GrBackendEffectFactory& GrMorphologyEffect::getFactory() const { 460 return GrTBackendEffectFactory<GrMorphologyEffect>::getInstance(); 461} 462 463bool GrMorphologyEffect::onIsEqual(const GrEffect& sBase) const { 464 const GrMorphologyEffect& s = CastEffect<GrMorphologyEffect>(sBase); 465 return (this->texture(0) == s.texture(0) && 466 this->radius() == s.radius() && 467 this->direction() == s.direction() && 468 this->type() == s.type()); 469} 470 471void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { 472 // This is valid because the color components of the result of the kernel all come 473 // exactly from existing values in the source texture. 474 this->updateConstantColorComponentsForModulation(color, validFlags); 475} 476 477/////////////////////////////////////////////////////////////////////////////// 478 479GR_DEFINE_EFFECT_TEST(GrMorphologyEffect); 480 481GrEffectRef* GrMorphologyEffect::TestCreate(SkRandom* random, 482 GrContext*, 483 const GrDrawTargetCaps&, 484 GrTexture* textures[]) { 485 int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : 486 GrEffectUnitTest::kAlphaTextureIdx; 487 Direction dir = random->nextBool() ? kX_Direction : kY_Direction; 488 static const int kMaxRadius = 10; 489 int radius = random->nextRangeU(1, kMaxRadius); 490 MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType : 491 GrMorphologyEffect::kDilate_MorphologyType; 492 493 return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type); 494} 495 496namespace { 497 498void apply_morphology_pass(GrContext* context, 499 GrTexture* texture, 500 const SkIRect& srcRect, 501 const SkIRect& dstRect, 502 int radius, 503 GrMorphologyEffect::MorphologyType morphType, 504 Gr1DKernelEffect::Direction direction) { 505 GrPaint paint; 506 paint.addColorEffect(GrMorphologyEffect::Create(texture, 507 direction, 508 radius, 509 morphType))->unref(); 510 context->drawRectToRect(paint, SkRect::Make(dstRect), SkRect::Make(srcRect)); 511} 512 513bool apply_morphology(const SkBitmap& input, 514 const SkIRect& rect, 515 GrMorphologyEffect::MorphologyType morphType, 516 SkISize radius, 517 SkBitmap* dst) { 518 GrTexture* srcTexture = input.getTexture(); 519 SkASSERT(NULL != srcTexture); 520 GrContext* context = srcTexture->getContext(); 521 srcTexture->ref(); 522 SkAutoTUnref<GrTexture> src(srcTexture); 523 524 GrContext::AutoMatrix am; 525 am.setIdentity(context); 526 527 GrContext::AutoClip acs(context, SkRect::MakeWH(SkIntToScalar(srcTexture->width()), 528 SkIntToScalar(srcTexture->height()))); 529 530 SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height()); 531 GrTextureDesc desc; 532 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; 533 desc.fWidth = rect.width(); 534 desc.fHeight = rect.height(); 535 desc.fConfig = kSkia8888_GrPixelConfig; 536 SkIRect srcRect = rect; 537 538 if (radius.fWidth > 0) { 539 GrAutoScratchTexture ast(context, desc); 540 GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget()); 541 apply_morphology_pass(context, src, srcRect, dstRect, radius.fWidth, 542 morphType, Gr1DKernelEffect::kX_Direction); 543 SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom, 544 dstRect.width(), radius.fHeight); 545 context->clear(&clearRect, GrMorphologyEffect::kErode_MorphologyType == morphType ? 546 SK_ColorWHITE : 547 SK_ColorTRANSPARENT, false); 548 src.reset(ast.detach()); 549 srcRect = dstRect; 550 } 551 if (radius.fHeight > 0) { 552 GrAutoScratchTexture ast(context, desc); 553 GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget()); 554 apply_morphology_pass(context, src, srcRect, dstRect, radius.fHeight, 555 morphType, Gr1DKernelEffect::kY_Direction); 556 src.reset(ast.detach()); 557 } 558 return SkImageFilterUtils::WrapTexture(src, rect.width(), rect.height(), dst); 559} 560 561}; 562 563bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, 564 SkBitmap* result, SkIPoint* offset) { 565 SkBitmap input; 566 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) { 567 return false; 568 } 569 SkIRect bounds; 570 src.getBounds(&bounds); 571 if (!this->applyCropRect(&bounds, ctm)) { 572 return false; 573 } 574 int width = radius().width(); 575 int height = radius().height(); 576 577 if (width < 0 || height < 0) { 578 return false; 579 } 580 581 if (width == 0 && height == 0) { 582 src.extractSubset(result, bounds); 583 offset->fX += bounds.left(); 584 offset->fY += bounds.top(); 585 return true; 586 } 587 588 if (!apply_morphology(input, bounds, GrMorphologyEffect::kDilate_MorphologyType, radius(), result)) { 589 return false; 590 } 591 offset->fX += bounds.left(); 592 offset->fY += bounds.top(); 593 return true; 594} 595 596bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, 597 SkBitmap* result, SkIPoint* offset) { 598 SkBitmap input; 599 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) { 600 return false; 601 } 602 SkIRect bounds; 603 src.getBounds(&bounds); 604 if (!this->applyCropRect(&bounds, ctm)) { 605 return false; 606 } 607 int width = radius().width(); 608 int height = radius().height(); 609 610 if (width < 0 || height < 0) { 611 return false; 612 } 613 614 if (width == 0 && height == 0) { 615 src.extractSubset(result, bounds); 616 offset->fX += bounds.left(); 617 offset->fY += bounds.top(); 618 return true; 619 } 620 621 if (!apply_morphology(input, bounds, GrMorphologyEffect::kErode_MorphologyType, radius(), result)) { 622 return false; 623 } 624 offset->fX += bounds.left(); 625 offset->fY += bounds.top(); 626 return true; 627} 628 629#endif 630