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 = this->getInputResult(proxy, source, ctm, offset); 140 if (src.config() != SkBitmap::kARGB_8888_Config) { 141 return false; 142 } 143 144 SkAutoLockPixels alp(src); 145 if (!src.getPixels()) { 146 return false; 147 } 148 149 dst->setConfig(src.config(), src.width(), src.height()); 150 dst->allocPixels(); 151 152 int width = radius().width(); 153 int height = radius().height(); 154 155 if (width < 0 || height < 0) { 156 return false; 157 } 158 159 if (width == 0 && height == 0) { 160 src.copyTo(dst, dst->config()); 161 return true; 162 } 163 164 SkBitmap temp; 165 temp.setConfig(dst->config(), dst->width(), dst->height()); 166 if (!temp.allocPixels()) { 167 return false; 168 } 169 170 if (width > 0 && height > 0) { 171 erodeX(src, &temp, width); 172 erodeY(temp, dst, height); 173 } else if (width > 0) { 174 erodeX(src, dst, width); 175 } else if (height > 0) { 176 erodeY(src, dst, height); 177 } 178 return true; 179 } 180 181 bool SkDilateImageFilter::onFilterImage(Proxy* proxy, 182 const SkBitmap& source, const SkMatrix& ctm, 183 SkBitmap* dst, SkIPoint* offset) { 184 SkBitmap src = this->getInputResult(proxy, source, ctm, offset); 185 if (src.config() != SkBitmap::kARGB_8888_Config) { 186 return false; 187 } 188 189 SkAutoLockPixels alp(src); 190 if (!src.getPixels()) { 191 return false; 192 } 193 194 dst->setConfig(src.config(), src.width(), src.height()); 195 dst->allocPixels(); 196 197 int width = radius().width(); 198 int height = radius().height(); 199 200 if (width < 0 || height < 0) { 201 return false; 202 } 203 204 if (width == 0 && height == 0) { 205 src.copyTo(dst, dst->config()); 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 dilateX(src, &temp, width); 217 dilateY(temp, dst, height); 218 } else if (width > 0) { 219 dilateX(src, dst, width); 220 } else if (height > 0) { 221 dilateY(src, dst, height); 222 } 223 return true; 224 } 225 226 #if SK_SUPPORT_GPU 227 228 /////////////////////////////////////////////////////////////////////////////// 229 230 class GrGLMorphologyEffect; 231 232 /** 233 * Morphology effects. Depending upon the type of morphology, either the 234 * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the 235 * kernel is selected as the new color. The new color is modulated by the input 236 * color. 237 */ 238 class GrMorphologyEffect : public Gr1DKernelEffect { 239 240 public: 241 242 enum MorphologyType { 243 kErode_MorphologyType, 244 kDilate_MorphologyType, 245 }; 246 247 static GrEffectRef* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type) { 248 AutoEffectUnref effect(SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type))); 249 return CreateEffectRef(effect); 250 } 251 252 virtual ~GrMorphologyEffect(); 253 254 MorphologyType type() const { return fType; } 255 256 static const char* Name() { return "Morphology"; } 257 258 typedef GrGLMorphologyEffect GLEffect; 259 260 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; 261 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; 262 263 protected: 264 265 MorphologyType fType; 266 267 private: 268 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; 269 270 GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType); 271 272 GR_DECLARE_EFFECT_TEST; 273 274 typedef Gr1DKernelEffect INHERITED; 275 }; 276 277 /////////////////////////////////////////////////////////////////////////////// 278 279 class GrGLMorphologyEffect : public GrGLEffect { 280 public: 281 GrGLMorphologyEffect (const GrBackendEffectFactory&, const GrEffectRef&); 282 283 virtual void emitCode(GrGLShaderBuilder*, 284 const GrEffectStage&, 285 EffectKey, 286 const char* vertexCoords, 287 const char* outputColor, 288 const char* inputColor, 289 const TextureSamplerArray&) SK_OVERRIDE; 290 291 static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&); 292 293 virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE; 294 295 private: 296 int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); } 297 298 int fRadius; 299 GrMorphologyEffect::MorphologyType fType; 300 GrGLUniformManager::UniformHandle fImageIncrementUni; 301 GrGLEffectMatrix fEffectMatrix; 302 303 typedef GrGLEffect INHERITED; 304 }; 305 306 GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendEffectFactory& factory, 307 const GrEffectRef& effect) 308 : INHERITED(factory) 309 , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle) { 310 const GrMorphologyEffect& m = CastEffect<GrMorphologyEffect>(effect); 311 fRadius = m.radius(); 312 fType = m.type(); 313 } 314 315 void GrGLMorphologyEffect::emitCode(GrGLShaderBuilder* builder, 316 const GrEffectStage&, 317 EffectKey key, 318 const char* vertexCoords, 319 const char* outputColor, 320 const char* inputColor, 321 const TextureSamplerArray& samplers) { 322 const char* coords; 323 fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords); 324 fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, 325 kVec2f_GrSLType, "ImageIncrement"); 326 327 SkString* code = &builder->fFSCode; 328 329 const char* func; 330 switch (fType) { 331 case GrMorphologyEffect::kErode_MorphologyType: 332 code->appendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor); 333 func = "min"; 334 break; 335 case GrMorphologyEffect::kDilate_MorphologyType: 336 code->appendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor); 337 func = "max"; 338 break; 339 default: 340 GrCrash("Unexpected type"); 341 func = ""; // suppress warning 342 break; 343 } 344 const char* imgInc = builder->getUniformCStr(fImageIncrementUni); 345 346 code->appendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc); 347 code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width()); 348 code->appendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor); 349 builder->appendTextureLookup(&builder->fFSCode, samplers[0], "coord"); 350 code->appendf(");\n"); 351 code->appendf("\t\t\tcoord += %s;\n", imgInc); 352 code->appendf("\t\t}\n"); 353 GrGLSLMulVarBy4f(code, 2, outputColor, inputColor); 354 } 355 356 GrGLEffect::EffectKey GrGLMorphologyEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) { 357 const GrMorphologyEffect& m = GetEffectFromStage<GrMorphologyEffect>(s); 358 EffectKey key = static_cast<EffectKey>(m.radius()); 359 key |= (m.type() << 8); 360 key <<= GrGLEffectMatrix::kKeyBits; 361 EffectKey matrixKey = GrGLEffectMatrix::GenKey(m.getMatrix(), 362 s.getCoordChangeMatrix(), 363 m.texture(0)); 364 return key | matrixKey; 365 } 366 367 void GrGLMorphologyEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) { 368 const Gr1DKernelEffect& kern = GetEffectFromStage<Gr1DKernelEffect>(stage); 369 GrTexture& texture = *kern.texture(0); 370 // the code we generated was for a specific kernel radius 371 GrAssert(kern.radius() == fRadius); 372 float imageIncrement[2] = { 0 }; 373 switch (kern.direction()) { 374 case Gr1DKernelEffect::kX_Direction: 375 imageIncrement[0] = 1.0f / texture.width(); 376 break; 377 case Gr1DKernelEffect::kY_Direction: 378 imageIncrement[1] = 1.0f / texture.height(); 379 break; 380 default: 381 GrCrash("Unknown filter direction."); 382 } 383 uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement); 384 fEffectMatrix.setData(uman, kern.getMatrix(), stage.getCoordChangeMatrix(), kern.texture(0)); 385 } 386 387 /////////////////////////////////////////////////////////////////////////////// 388 389 GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture, 390 Direction direction, 391 int radius, 392 MorphologyType type) 393 : Gr1DKernelEffect(texture, direction, radius) 394 , fType(type) { 395 } 396 397 GrMorphologyEffect::~GrMorphologyEffect() { 398 } 399 400 const GrBackendEffectFactory& GrMorphologyEffect::getFactory() const { 401 return GrTBackendEffectFactory<GrMorphologyEffect>::getInstance(); 402 } 403 404 bool GrMorphologyEffect::onIsEqual(const GrEffect& sBase) const { 405 const GrMorphologyEffect& s = CastEffect<GrMorphologyEffect>(sBase); 406 return (this->texture(0) == s.texture(0) && 407 this->radius() == s.radius() && 408 this->direction() == s.direction() && 409 this->type() == s.type()); 410 } 411 412 void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { 413 // This is valid because the color components of the result of the kernel all come 414 // exactly from existing values in the source texture. 415 this->updateConstantColorComponentsForModulation(color, validFlags); 416 } 417 418 /////////////////////////////////////////////////////////////////////////////// 419 420 GR_DEFINE_EFFECT_TEST(GrMorphologyEffect); 421 422 GrEffectRef* GrMorphologyEffect::TestCreate(SkRandom* random, 423 GrContext* context, 424 GrTexture* textures[]) { 425 int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : 426 GrEffectUnitTest::kAlphaTextureIdx; 427 Direction dir = random->nextBool() ? kX_Direction : kY_Direction; 428 static const int kMaxRadius = 10; 429 int radius = random->nextRangeU(1, kMaxRadius); 430 MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType : 431 GrMorphologyEffect::kDilate_MorphologyType; 432 433 return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type); 434 } 435 436 namespace { 437 438 void apply_morphology_pass(GrContext* context, 439 GrTexture* texture, 440 const SkIRect& rect, 441 int radius, 442 GrMorphologyEffect::MorphologyType morphType, 443 Gr1DKernelEffect::Direction direction) { 444 GrPaint paint; 445 paint.colorStage(0)->setEffect(GrMorphologyEffect::Create(texture, 446 direction, 447 radius, 448 morphType))->unref(); 449 context->drawRect(paint, SkRect::MakeFromIRect(rect)); 450 } 451 452 GrTexture* apply_morphology(GrTexture* srcTexture, 453 const SkIRect& rect, 454 GrMorphologyEffect::MorphologyType morphType, 455 SkISize radius) { 456 GrContext* context = srcTexture->getContext(); 457 srcTexture->ref(); 458 459 GrContext::AutoMatrix am; 460 am.setIdentity(context); 461 462 GrContext::AutoClip acs(context, GrRect::MakeWH(SkIntToScalar(srcTexture->width()), 463 SkIntToScalar(srcTexture->height()))); 464 465 GrTextureDesc desc; 466 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; 467 desc.fWidth = rect.width(); 468 desc.fHeight = rect.height(); 469 desc.fConfig = kSkia8888_GrPixelConfig; 470 471 if (radius.fWidth > 0) { 472 GrAutoScratchTexture ast(context, desc); 473 GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget()); 474 apply_morphology_pass(context, srcTexture, rect, radius.fWidth, 475 morphType, Gr1DKernelEffect::kX_Direction); 476 SkIRect clearRect = SkIRect::MakeXYWH(rect.fLeft, rect.fBottom, 477 rect.width(), radius.fHeight); 478 context->clear(&clearRect, 0x0); 479 srcTexture->unref(); 480 srcTexture = ast.detach(); 481 } 482 if (radius.fHeight > 0) { 483 GrAutoScratchTexture ast(context, desc); 484 GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget()); 485 apply_morphology_pass(context, srcTexture, rect, radius.fHeight, 486 morphType, Gr1DKernelEffect::kY_Direction); 487 srcTexture->unref(); 488 srcTexture = ast.detach(); 489 } 490 return srcTexture; 491 } 492 493 }; 494 495 bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) { 496 SkBitmap inputBM; 497 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM)) { 498 return false; 499 } 500 GrTexture* input = (GrTexture*) inputBM.getTexture(); 501 SkIRect bounds; 502 src.getBounds(&bounds); 503 SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds, 504 GrMorphologyEffect::kDilate_MorphologyType, radius())); 505 return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result); 506 } 507 508 bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) { 509 SkBitmap inputBM; 510 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM)) { 511 return false; 512 } 513 GrTexture* input = (GrTexture*) inputBM.getTexture(); 514 SkIRect bounds; 515 src.getBounds(&bounds); 516 SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds, 517 GrMorphologyEffect::kErode_MorphologyType, radius())); 518 return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result); 519 } 520 521 #endif 522