1 /* 2 * Copyright 2016 Google Inc. 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 "SkRRectsGaussianEdgeMaskFilter.h" 9 #include "SkReadBuffer.h" 10 #include "SkRRect.h" 11 #include "SkWriteBuffer.h" 12 13 #if SK_SUPPORT_GPU 14 #include "GrFragmentProcessor.h" 15 #endif 16 17 /** \class SkRRectsGaussianEdgeMaskFilterImpl 18 * This mask filter applies a gaussian edge to the intersection of two round rects. 19 * The round rects must have the same radii at each corner and the x&y radii 20 * must also be equal. 21 */ 22 class SkRRectsGaussianEdgeMaskFilterImpl : public SkMaskFilter { 23 public: 24 SkRRectsGaussianEdgeMaskFilterImpl(const SkRRect& first, const SkRRect& second, 25 SkScalar radius) 26 : fFirst(first) 27 , fSecond(second) 28 , fRadius(radius) { 29 } 30 31 SkMask::Format getFormat() const override { return SkMask::kA8_Format; } 32 bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, 33 SkIPoint* margin) const override; 34 35 #if SK_SUPPORT_GPU 36 bool asFragmentProcessor(GrFragmentProcessor**) const override; 37 #endif 38 39 SK_TO_STRING_OVERRIDE() 40 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRRectsGaussianEdgeMaskFilterImpl) 41 42 protected: 43 void flatten(SkWriteBuffer&) const override; 44 45 private: 46 SkRRect fFirst; 47 SkRRect fSecond; 48 SkScalar fRadius; 49 50 friend class SkRRectsGaussianEdgeMaskFilter; // for serialization registration system 51 52 typedef SkMaskFilter INHERITED; 53 }; 54 55 // x & y are in device space 56 static SkScalar compute_rrect_normalized_dist(const SkRRect& rr, const SkPoint& p, SkScalar rad) { 57 SkASSERT(rr.getType() == SkRRect::kOval_Type || rr.getType() == SkRRect::kRect_Type || 58 rr.getType() == SkRRect::kSimple_Type); 59 SkASSERT(rad > 0.0f); 60 61 SkVector delta = { SkTAbs(p.fX - rr.rect().centerX()), SkTAbs(p.fY - rr.rect().centerY()) }; 62 63 SkScalar halfW = 0.5f * rr.rect().width(); 64 SkScalar halfH = 0.5f * rr.rect().height(); 65 SkScalar invRad = 1.0f/rad; 66 67 const SkVector& radii = rr.getSimpleRadii(); 68 SkASSERT(SkScalarNearlyEqual(radii.fX, radii.fY)); 69 70 switch (rr.getType()) { 71 case SkRRect::kOval_Type: { 72 float scaledDist = delta.length() * invRad; 73 return SkTPin(halfW * invRad - scaledDist, 0.0f, 1.0f); 74 } 75 case SkRRect::kRect_Type: { 76 SkScalar xDist = (halfW - delta.fX) * invRad; 77 SkScalar yDist = (halfH - delta.fY) * invRad; 78 79 SkVector v = { 1.0f - SkTPin(xDist, 0.0f, 1.0f), 1.0f - SkTPin(yDist, 0.0f, 1.0f) }; 80 return SkTPin(1.0f - v.length(), 0.0f, 1.0f); 81 } 82 case SkRRect::kSimple_Type: { 83 84 //---------------- 85 // ice-cream-cone fractional distance computation 86 87 // When the blurRadius is larger than the corner radius we want to use it to 88 // compute the pointy end of the ice cream cone. If it smaller we just want to use 89 // the center of the corner's circle. When using the blurRadius the inset amount 90 // can't exceed the halfwidths of the RRect. 91 SkScalar insetDist = SkTMin(SkTMax(rad, radii.fX), SkTMin(halfW, halfH)); 92 93 // "maxValue" is a correction term for if the blurRadius is larger than the 94 // size of the RRect. In that case we don't want to go all the way to black. 95 SkScalar maxValue = insetDist * invRad; 96 97 SkVector coneBottom = { halfW - insetDist, halfH - insetDist }; 98 SkVector ptInConeSpace = delta - coneBottom; 99 100 SkVector cornerTop = { halfW - radii.fX - coneBottom.fX, halfH - coneBottom.fY }; 101 SkVector cornerRight = { halfW - coneBottom.fX, halfH - radii.fY - coneBottom.fY }; 102 103 SkScalar cross1 = ptInConeSpace.cross(cornerTop); 104 SkScalar cross2 = cornerRight.cross(ptInConeSpace); 105 bool inCone = cross1 > 0.0f && cross2 > 0.0f; 106 107 if (!inCone) { 108 SkScalar xDist = (halfW - delta.fX) * invRad; 109 SkScalar yDist = (halfH - delta.fY) * invRad; 110 111 return SkTPin(SkTMin(xDist, yDist), 0.0f, 1.0f); // perpendicular distance 112 } 113 114 SkVector cornerCenterInConeSpace = { insetDist - radii.fX, insetDist - radii.fY }; 115 116 SkVector connectingVec = ptInConeSpace - cornerCenterInConeSpace; 117 float distToPtInConeSpace = SkPoint::Normalize(&ptInConeSpace); 118 119 // "a" (i.e., dot(ptInConeSpace, ptInConeSpace) should always be 1.0f since 120 // ptInConeSpace is now normalized 121 SkScalar b = 2.0f * ptInConeSpace.dot(connectingVec); 122 SkScalar c = connectingVec.dot(connectingVec) - radii.fX * radii.fY; 123 124 // lop off negative values that are outside the cone 125 SkScalar coneDist = SkTMax(0.0f, 0.5f * (-b + SkScalarSqrt(b*b - 4*c))); 126 127 // make the coneDist a fraction of how far it is from the edge to the cone's base 128 coneDist = (maxValue*coneDist) / (coneDist+distToPtInConeSpace); 129 return SkTPin(coneDist, 0.0f, 1.0f); 130 } 131 default: 132 return 0.0f; 133 } 134 } 135 136 bool SkRRectsGaussianEdgeMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, 137 const SkMatrix& matrix, 138 SkIPoint* margin) const { 139 140 if (src.fFormat != SkMask::kA8_Format) { 141 return false; 142 } 143 144 if (margin) { 145 margin->set(0, 0); 146 } 147 148 dst->fBounds = src.fBounds; 149 dst->fRowBytes = dst->fBounds.width(); 150 dst->fFormat = SkMask::kA8_Format; 151 dst->fImage = nullptr; 152 153 if (src.fImage) { 154 size_t dstSize = dst->computeImageSize(); 155 if (0 == dstSize) { 156 return false; // too big to allocate, abort 157 } 158 159 const uint8_t* srcPixels = src.fImage; 160 uint8_t* dstPixels = dst->fImage = SkMask::AllocImage(dstSize); 161 162 SkPoint basePt = { SkIntToScalar(src.fBounds.fLeft), SkIntToScalar(src.fBounds.fTop) }; 163 164 for (int y = 0; y < dst->fBounds.height(); ++y) { 165 const uint8_t* srcRow = srcPixels + y * dst->fRowBytes; 166 uint8_t* dstRow = dstPixels + y*dst->fRowBytes; 167 168 for (int x = 0; x < dst->fBounds.width(); ++x) { 169 SkPoint curPt = { basePt.fX + x, basePt.fY + y }; 170 171 SkVector vec; 172 vec.fX = 1.0f - compute_rrect_normalized_dist(fFirst, curPt, fRadius); 173 vec.fY = 1.0f - compute_rrect_normalized_dist(fSecond, curPt, fRadius); 174 175 SkScalar factor = SkTPin(vec.length(), 0.0f, 1.0f); 176 factor = exp(-factor * factor * 4.0f) - 0.018f; 177 SkASSERT(factor >= 0.0f && factor <= 1.0f); 178 179 dstRow[x] = (uint8_t) (factor * srcRow[x]); 180 } 181 } 182 } 183 184 return true; 185 } 186 187 //////////////////////////////////////////////////////////////////////////// 188 189 #if SK_SUPPORT_GPU 190 191 #include "GrCoordTransform.h" 192 #include "GrFragmentProcessor.h" 193 #include "glsl/GrGLSLFragmentProcessor.h" 194 #include "glsl/GrGLSLFragmentShaderBuilder.h" 195 #include "glsl/GrGLSLProgramDataManager.h" 196 #include "glsl/GrGLSLUniformHandler.h" 197 #include "SkGr.h" 198 199 class RRectsGaussianEdgeFP : public GrFragmentProcessor { 200 public: 201 enum Mode { 202 kCircle_Mode, 203 kRect_Mode, 204 kSimpleCircular_Mode, 205 }; 206 207 static sk_sp<GrFragmentProcessor> Make(const SkRRect& first, const SkRRect& second, 208 SkScalar radius) { 209 return sk_sp<GrFragmentProcessor>(new RRectsGaussianEdgeFP(first, second, radius)); 210 } 211 212 class GLSLRRectsGaussianEdgeFP : public GrGLSLFragmentProcessor { 213 public: 214 GLSLRRectsGaussianEdgeFP() { } 215 216 // This method emits code so that, for each shape, the distance from the edge is returned 217 // in 'outputName' clamped to 0..1 with positive distance being towards the center of the 218 // shape. The distance will have been normalized by the radius. 219 void emitModeCode(Mode mode, 220 GrGLSLFPFragmentBuilder* fragBuilder, 221 const char* posName, 222 const char* sizesName, 223 const char* radiiName, 224 const char* radName, 225 const char* outputName, 226 const char indices[2]) { // how to access the params for the 2 rrects 227 228 // Positive distance is towards the center of the circle. 229 // Map all the cases to the lower right quadrant. 230 fragBuilder->codeAppendf("vec2 delta = abs(sk_FragCoord.xy - %s.%s);", 231 posName, indices); 232 233 switch (mode) { 234 case kCircle_Mode: 235 // When a shadow circle gets large we can have some precision issues if 236 // we do "length(delta)/radius". The scaleDist temporary cuts the 237 // delta vector down a bit before invoking length. 238 fragBuilder->codeAppendf("float scaledDist = length(delta/%s);", radName); 239 fragBuilder->codeAppendf("%s = clamp((%s.%c/%s - scaledDist), 0.0, 1.0);", 240 outputName, sizesName, indices[0], radName); 241 break; 242 case kRect_Mode: 243 fragBuilder->codeAppendf( 244 "vec2 rectDist = vec2(1.0 - clamp((%s.%c - delta.x)/%s, 0.0, 1.0)," 245 "1.0 - clamp((%s.%c - delta.y)/%s, 0.0, 1.0));", 246 sizesName, indices[0], radName, 247 sizesName, indices[1], radName); 248 fragBuilder->codeAppendf("%s = clamp(1.0 - length(rectDist), 0.0, 1.0);", 249 outputName); 250 break; 251 case kSimpleCircular_Mode: 252 // For the circular round rect we combine 2 distances: 253 // the fractional position from the corner inset point to the corner's circle 254 // the minimum perpendicular distance to the bounding rectangle 255 // The first distance is used when the pixel is inside the ice-cream-cone-shaped 256 // portion of a corner. The second is used everywhere else. 257 // This is intended to approximate the interpolation pattern if we had 258 // tessellated this geometry into a RRect outside and a rect inside. 259 260 //---------------- 261 // rect distance computation 262 fragBuilder->codeAppendf("float xDist = (%s.%c - delta.x) / %s;", 263 sizesName, indices[0], radName); 264 fragBuilder->codeAppendf("float yDist = (%s.%c - delta.y) / %s;", 265 sizesName, indices[1], radName); 266 fragBuilder->codeAppend("float rectDist = clamp(min(xDist, yDist), 0.0, 1.0);"); 267 268 //---------------- 269 // ice-cream-cone fractional distance computation 270 271 // When the blurRadius is larger than the corner radius we want to use it to 272 // compute the pointy end of the ice cream cone. If it smaller we just want to 273 // use the center of the corner's circle. When using the blurRadius the inset 274 // amount can't exceed the halfwidths of the RRect. 275 fragBuilder->codeAppendf("float insetDist = min(max(%s, %s.%c)," 276 "min(%s.%c, %s.%c));", 277 radName, radiiName, indices[0], 278 sizesName, indices[0], sizesName, indices[1]); 279 // "maxValue" is a correction term for if the blurRadius is larger than the 280 // size of the RRect. In that case we don't want to go all the way to black. 281 fragBuilder->codeAppendf("float maxValue = insetDist/%s;", radName); 282 283 fragBuilder->codeAppendf("vec2 coneBottom = vec2(%s.%c - insetDist," 284 "%s.%c - insetDist);", 285 sizesName, indices[0], sizesName, indices[1]); 286 287 fragBuilder->codeAppendf("vec2 cornerTop = vec2(%s.%c - %s.%c, %s.%c) -" 288 "coneBottom;", 289 sizesName, indices[0], radiiName, indices[0], 290 sizesName, indices[1]); 291 fragBuilder->codeAppendf("vec2 cornerRight = vec2(%s.%c, %s.%c - %s.%c) -" 292 "coneBottom;", 293 sizesName, indices[0], 294 sizesName, indices[1], radiiName, indices[1]); 295 296 fragBuilder->codeAppend("vec2 ptInConeSpace = delta - coneBottom;"); 297 fragBuilder->codeAppend("float distToPtInConeSpace = length(ptInConeSpace);"); 298 299 fragBuilder->codeAppend("float cross1 = ptInConeSpace.x * cornerTop.y -" 300 "ptInConeSpace.y * cornerTop.x;"); 301 fragBuilder->codeAppend("float cross2 = -ptInConeSpace.x * cornerRight.y + " 302 "ptInConeSpace.y * cornerRight.x;"); 303 304 fragBuilder->codeAppend("float inCone = step(0.0, cross1) *" 305 "step(0.0, cross2);"); 306 307 fragBuilder->codeAppendf("vec2 cornerCenterInConeSpace = vec2(insetDist -" 308 "%s.%c);", 309 radiiName, indices[0]); 310 311 fragBuilder->codeAppend("vec2 connectingVec = ptInConeSpace -" 312 "cornerCenterInConeSpace;"); 313 fragBuilder->codeAppend("ptInConeSpace = normalize(ptInConeSpace);"); 314 315 // "a" (i.e., dot(ptInConeSpace, ptInConeSpace) should always be 1.0f since 316 // ptInConeSpace is now normalized 317 fragBuilder->codeAppend("float b = 2.0 * dot(ptInConeSpace, connectingVec);"); 318 fragBuilder->codeAppendf("float c = dot(connectingVec, connectingVec) - " 319 "%s.%c * %s.%c;", 320 radiiName, indices[0], radiiName, indices[0]); 321 322 fragBuilder->codeAppend("float fourAC = 4*c;"); 323 // This max prevents sqrt(-1) when outside the cone 324 fragBuilder->codeAppend("float bSq = max(b*b, fourAC);"); 325 326 // lop off negative values that are outside the cone 327 fragBuilder->codeAppend("float coneDist = " 328 "max(0.0, 0.5 * (-b + sqrt(bSq - fourAC)));"); 329 // make the coneDist a fraction of how far it is from the edge to the 330 // cone's base 331 fragBuilder->codeAppend("coneDist = (maxValue*coneDist) /" 332 "(coneDist+distToPtInConeSpace);"); 333 fragBuilder->codeAppend("coneDist = clamp(coneDist, 0.0, 1.0);"); 334 335 //---------------- 336 fragBuilder->codeAppendf("%s = mix(rectDist, coneDist, inCone);", outputName); 337 break; 338 } 339 } 340 341 void emitCode(EmitArgs& args) override { 342 const RRectsGaussianEdgeFP& fp = args.fFp.cast<RRectsGaussianEdgeFP>(); 343 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 344 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 345 346 const char* positionsUniName = nullptr; 347 fPositionsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 348 kVec4f_GrSLType, kDefault_GrSLPrecision, 349 "Positions", &positionsUniName); 350 const char* sizesUniName = nullptr; 351 fSizesUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 352 kVec4f_GrSLType, kDefault_GrSLPrecision, 353 "Sizes", &sizesUniName); 354 const char* radiiUniName = nullptr; 355 if (fp.fFirstMode == kSimpleCircular_Mode || fp.fSecondMode == kSimpleCircular_Mode) { 356 fRadiiUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 357 kVec4f_GrSLType, kDefault_GrSLPrecision, 358 "Radii", &radiiUniName); 359 } 360 const char* radUniName = nullptr; 361 fRadiusUni = uniformHandler->addUniform(kFragment_GrShaderFlag, 362 kFloat_GrSLType, kDefault_GrSLPrecision, 363 "Radius", &radUniName); 364 365 fragBuilder->codeAppend("float firstDist;"); 366 fragBuilder->codeAppend("{"); 367 this->emitModeCode(fp.firstMode(), fragBuilder, 368 positionsUniName, sizesUniName, radiiUniName, 369 radUniName, "firstDist", "xy"); 370 fragBuilder->codeAppend("}"); 371 372 fragBuilder->codeAppend("float secondDist;"); 373 fragBuilder->codeAppend("{"); 374 this->emitModeCode(fp.secondMode(), fragBuilder, 375 positionsUniName, sizesUniName, radiiUniName, 376 radUniName, "secondDist", "zw"); 377 fragBuilder->codeAppend("}"); 378 379 fragBuilder->codeAppend("vec2 distVec = vec2(1.0 - firstDist, 1.0 - secondDist);"); 380 381 // Finally use the distance to apply the Gaussian edge 382 fragBuilder->codeAppend("float factor = clamp(length(distVec), 0.0, 1.0);"); 383 fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;"); 384 fragBuilder->codeAppendf("%s = factor*%s;", 385 args.fOutputColor, args.fInputColor); 386 } 387 388 static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) { 389 const RRectsGaussianEdgeFP& fp = proc.cast<RRectsGaussianEdgeFP>(); 390 391 b->add32(fp.firstMode() | (fp.secondMode() << 4)); 392 } 393 394 protected: 395 void onSetData(const GrGLSLProgramDataManager& pdman, 396 const GrFragmentProcessor& proc) override { 397 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>(); 398 399 const SkRRect& first = edgeFP.first(); 400 const SkRRect& second = edgeFP.second(); 401 402 pdman.set4f(fPositionsUni, 403 first.getBounds().centerX(), 404 first.getBounds().centerY(), 405 second.getBounds().centerX(), 406 second.getBounds().centerY()); 407 408 pdman.set4f(fSizesUni, 409 0.5f * first.rect().width(), 410 0.5f * first.rect().height(), 411 0.5f * second.rect().width(), 412 0.5f * second.rect().height()); 413 414 if (edgeFP.firstMode() == kSimpleCircular_Mode || 415 edgeFP.secondMode() == kSimpleCircular_Mode) { 416 // This is a bit of overkill since fX should equal fY for both round rects but it 417 // makes the shader code simpler. 418 pdman.set4f(fRadiiUni, 419 first.getSimpleRadii().fX, first.getSimpleRadii().fY, 420 second.getSimpleRadii().fX, second.getSimpleRadii().fY); 421 } 422 423 pdman.set1f(fRadiusUni, edgeFP.radius()); 424 } 425 426 private: 427 // The centers of the two round rects (x1, y1, x2, y2) 428 GrGLSLProgramDataManager::UniformHandle fPositionsUni; 429 430 // The half widths and half heights of the two round rects (w1/2, h1/2, w2/2, h2/2) 431 // For circles we still upload both width & height to simplify things 432 GrGLSLProgramDataManager::UniformHandle fSizesUni; 433 434 // The corner radii of the two round rects (rx1, ry1, rx2, ry2) 435 // We upload both the x&y radii (although they are currently always the same) to make 436 // the indexing in the shader code simpler. In some future world we could also support 437 // non-circular corner round rects & ellipses. 438 GrGLSLProgramDataManager::UniformHandle fRadiiUni; 439 440 // The radius parameters (radius) 441 GrGLSLProgramDataManager::UniformHandle fRadiusUni; 442 443 typedef GrGLSLFragmentProcessor INHERITED; 444 }; 445 446 void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 447 GLSLRRectsGaussianEdgeFP::GenKey(*this, caps, b); 448 } 449 450 const char* name() const override { return "RRectsGaussianEdgeFP"; } 451 452 const SkRRect& first() const { return fFirst; } 453 Mode firstMode() const { return fFirstMode; } 454 const SkRRect& second() const { return fSecond; } 455 Mode secondMode() const { return fSecondMode; } 456 SkScalar radius() const { return fRadius; } 457 458 private: 459 RRectsGaussianEdgeFP(const SkRRect& first, const SkRRect& second, SkScalar radius) 460 : INHERITED(kCompatibleWithCoverageAsAlpha_OptimizationFlag) 461 , fFirst(first) 462 , fSecond(second) 463 , fRadius(radius) { 464 this->initClassID<RRectsGaussianEdgeFP>(); 465 466 fFirstMode = ComputeMode(fFirst); 467 fSecondMode = ComputeMode(fSecond); 468 } 469 470 static Mode ComputeMode(const SkRRect& rr) { 471 if (rr.isCircle()) { 472 return kCircle_Mode; 473 } else if (rr.isRect()) { 474 return kRect_Mode; 475 } else { 476 SkASSERT(rr.isSimpleCircular()); 477 return kSimpleCircular_Mode; 478 } 479 } 480 481 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { 482 return new GLSLRRectsGaussianEdgeFP; 483 } 484 485 bool onIsEqual(const GrFragmentProcessor& proc) const override { 486 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>(); 487 return fFirst == edgeFP.fFirst && 488 fSecond == edgeFP.fSecond && 489 fRadius == edgeFP.fRadius; 490 } 491 492 SkRRect fFirst; 493 Mode fFirstMode; 494 SkRRect fSecond; 495 Mode fSecondMode; 496 SkScalar fRadius; 497 498 typedef GrFragmentProcessor INHERITED; 499 }; 500 501 //////////////////////////////////////////////////////////////////////////// 502 bool SkRRectsGaussianEdgeMaskFilterImpl::asFragmentProcessor(GrFragmentProcessor** fp) const { 503 if (fp) { 504 *fp = RRectsGaussianEdgeFP::Make(fFirst, fSecond, fRadius).release(); 505 } 506 507 return true; 508 } 509 510 #endif 511 512 //////////////////////////////////////////////////////////////////////////// 513 514 #ifndef SK_IGNORE_TO_STRING 515 void SkRRectsGaussianEdgeMaskFilterImpl::toString(SkString* str) const { 516 str->appendf("RRectsGaussianEdgeMaskFilter: ()"); 517 } 518 #endif 519 520 sk_sp<SkFlattenable> SkRRectsGaussianEdgeMaskFilterImpl::CreateProc(SkReadBuffer& buf) { 521 SkRect rect1, rect2; 522 523 buf.readRect(&rect1); 524 SkScalar xRad1 = buf.readScalar(); 525 SkScalar yRad1 = buf.readScalar(); 526 527 buf.readRect(&rect2); 528 SkScalar xRad2 = buf.readScalar(); 529 SkScalar yRad2 = buf.readScalar(); 530 531 SkScalar radius = buf.readScalar(); 532 533 return sk_make_sp<SkRRectsGaussianEdgeMaskFilterImpl>(SkRRect::MakeRectXY(rect1, xRad1, yRad1), 534 SkRRect::MakeRectXY(rect2, xRad2, yRad2), 535 radius); 536 } 537 538 void SkRRectsGaussianEdgeMaskFilterImpl::flatten(SkWriteBuffer& buf) const { 539 INHERITED::flatten(buf); 540 541 SkASSERT(fFirst.isRect() || fFirst.isCircle() || fFirst.isSimpleCircular()); 542 buf.writeRect(fFirst.rect()); 543 const SkVector& radii1 = fFirst.getSimpleRadii(); 544 buf.writeScalar(radii1.fX); 545 buf.writeScalar(radii1.fY); 546 547 SkASSERT(fSecond.isRect() || fSecond.isCircle() || fSecond.isSimpleCircular()); 548 buf.writeRect(fSecond.rect()); 549 const SkVector& radii2 = fSecond.getSimpleRadii(); 550 buf.writeScalar(radii2.fX); 551 buf.writeScalar(radii2.fY); 552 553 buf.writeScalar(fRadius); 554 } 555 556 /////////////////////////////////////////////////////////////////////////////// 557 558 sk_sp<SkMaskFilter> SkRRectsGaussianEdgeMaskFilter::Make(const SkRRect& first, 559 const SkRRect& second, 560 SkScalar radius) { 561 if ((!first.isRect() && !first.isCircle() && !first.isSimpleCircular()) || 562 (!second.isRect() && !second.isCircle() && !second.isSimpleCircular())) { 563 // we only deal with the shapes where the x & y radii are equal 564 // and the same for all four corners 565 return nullptr; 566 } 567 568 return sk_make_sp<SkRRectsGaussianEdgeMaskFilterImpl>(first, second, radius); 569 } 570 571 /////////////////////////////////////////////////////////////////////////////// 572 573 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkRRectsGaussianEdgeMaskFilter) 574 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRRectsGaussianEdgeMaskFilterImpl) 575 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 576 577 /////////////////////////////////////////////////////////////////////////////// 578