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