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