Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 "SkRRectsGaussianEdgeShader.h" | |
| 9 #include "SkReadBuffer.h" | |
| 10 #include "SkWriteBuffer.h" | |
| 11 | |
| 12 /** \class SkRRectsGaussianEdgeShaderImpl | |
| 13 * This shader applies a gaussian edge to the intersection of two round rects. | |
| 14 * The round rects must have the same radii at each corner and the x&y radii | |
| 15 * must also be equal. | |
| 16 */ | |
| 17 class SkRRectsGaussianEdgeShaderImpl : public SkShader { | |
| 18 public: | |
| 19 SkRRectsGaussianEdgeShaderImpl(const SkRRect& first, const SkRRect& second, | |
| 20 SkScalar radius, SkScalar pad) | |
| 21 : fFirst(first) | |
| 22 , fSecond(second) | |
| 23 , fRadius(radius) | |
| 24 , fPad(pad) { | |
| 25 } | |
| 26 | |
| 27 bool isOpaque() const override { return false; } | |
| 28 | |
| 29 #if SK_SUPPORT_GPU | |
| 30 sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const overri de; | |
| 31 #endif | |
| 32 | |
| 33 class GaussianEdgeShaderContext : public SkShader::Context { | |
| 34 public: | |
| 35 GaussianEdgeShaderContext(const SkRRectsGaussianEdgeShaderImpl&, const C ontextRec&); | |
| 36 | |
| 37 ~GaussianEdgeShaderContext() override { } | |
| 38 | |
| 39 void shadeSpan(int x, int y, SkPMColor[], int count) override; | |
| 40 | |
| 41 uint32_t getFlags() const override { return 0; } | |
| 42 | |
| 43 private: | |
| 44 SkColor fPaintColor; | |
| 45 | |
| 46 typedef SkShader::Context INHERITED; | |
| 47 }; | |
| 48 | |
| 49 SK_TO_STRING_OVERRIDE() | |
| 50 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRRectsGaussianEdgeShad erImpl) | |
| 51 | |
| 52 protected: | |
| 53 void flatten(SkWriteBuffer&) const override; | |
| 54 size_t onContextSize(const ContextRec&) const override; | |
| 55 Context* onCreateContext(const ContextRec&, void*) const override; | |
| 56 | |
| 57 private: | |
| 58 SkRRect fFirst; | |
| 59 SkRRect fSecond; | |
| 60 SkScalar fRadius; | |
| 61 SkScalar fPad; | |
| 62 | |
| 63 friend class SkRRectsGaussianEdgeShader; // for serialization registration s ystem | |
| 64 | |
| 65 typedef SkShader INHERITED; | |
| 66 }; | |
| 67 | |
| 68 //////////////////////////////////////////////////////////////////////////// | |
| 69 | |
| 70 #if SK_SUPPORT_GPU | |
| 71 | |
| 72 #include "GrCoordTransform.h" | |
| 73 #include "GrFragmentProcessor.h" | |
| 74 #include "GrInvariantOutput.h" | |
| 75 #include "glsl/GrGLSLFragmentProcessor.h" | |
| 76 #include "glsl/GrGLSLFragmentShaderBuilder.h" | |
| 77 #include "glsl/GrGLSLProgramDataManager.h" | |
| 78 #include "glsl/GrGLSLUniformHandler.h" | |
| 79 #include "SkGr.h" | |
| 80 #include "SkGrPriv.h" | |
| 81 | |
| 82 class RRectsGaussianEdgeFP : public GrFragmentProcessor { | |
| 83 public: | |
| 84 enum Mode { | |
| 85 kCircle_Mode, | |
| 86 kRect_Mode, | |
| 87 kSimpleCircular_Mode, | |
| 88 }; | |
| 89 | |
| 90 RRectsGaussianEdgeFP(const SkRRect& first, const SkRRect& second, | |
| 91 SkScalar radius, SkScalar pad) | |
| 92 : fFirst(first) | |
| 93 , fSecond(second) | |
| 94 , fRadius(radius) | |
| 95 , fPad(pad) { | |
| 96 this->initClassID<RRectsGaussianEdgeFP>(); | |
| 97 this->setWillReadFragmentPosition(); | |
| 98 | |
| 99 fFirstMode = ComputeMode(fFirst); | |
| 100 fSecondMode = ComputeMode(fSecond); | |
| 101 } | |
| 102 | |
| 103 class GLSLRRectsGaussianEdgeFP : public GrGLSLFragmentProcessor { | |
| 104 public: | |
| 105 GLSLRRectsGaussianEdgeFP() { | |
| 106 fCachedPositions[0] = fCachedPositions[1] = | |
| 107 fCachedPositions[2] = fCachedPositions[3] = SK _ScalarMax; | |
| 108 fCachedSizes[0] = fCachedSizes[1] = fCachedSizes[2] = fCachedSizes[3 ] = -1.0f; | |
| 109 fCachedRadii[0] = fCachedRadii[1] = fCachedRadii[2] = fCachedRadii[3 ] = -1.0f; | |
| 110 fCachedPadRad.set(-1.0f, -1.0f); | |
| 111 } | |
| 112 | |
| 113 void emitModeCode(Mode mode, | |
| 114 GrGLSLFPFragmentBuilder* fragBuilder, | |
| 115 const char* posName, | |
| 116 const char* sizesName, | |
| 117 const char* radiiName, | |
| 118 const char* outputName, | |
| 119 const char indices[2]) { // how to access the params for the 2 rrects | |
| 120 | |
| 121 // positive distance is towards the center of the circle | |
| 122 fragBuilder->codeAppendf("vec2 delta = %s.xy - %s.%s;", | |
| 123 fragBuilder->fragmentPosition(), | |
| 124 posName, indices); | |
| 125 | |
| 126 switch (mode) { | |
| 127 case kCircle_Mode: | |
| 128 fragBuilder->codeAppendf("%s = %s.%c - length(delta);", | |
| 129 outputName, | |
| 130 sizesName, indices[0]); | |
| 131 break; | |
| 132 case kRect_Mode: | |
| 133 fragBuilder->codeAppendf("float xDist = %s.%c - abs(delta.x);", | |
| 134 sizesName, indices[0]); | |
| 135 fragBuilder->codeAppendf("float yDist = %s.%c - abs(delta.y);", | |
| 136 sizesName, indices[1]); | |
| 137 fragBuilder->codeAppendf("%s = min(xDist, yDist);", outputName); | |
| 138 break; | |
| 139 case kSimpleCircular_Mode: | |
| 140 // For the circular round rect we first compute the distance | |
| 141 // to the rect. Then we compute a multiplier that is 1 if the | |
| 142 // point is in one of the circular corners. We then compute the | |
| 143 // distance from the corner and then use the multiplier to mask | |
| 144 // between the two distances. | |
| 145 fragBuilder->codeAppendf("float xDist = %s.%c - abs(delta.x);", | |
| 146 sizesName, indices[0]); | |
| 147 fragBuilder->codeAppendf("float yDist = %s.%c - abs(delta.y);", | |
| 148 sizesName, indices[1]); | |
| 149 fragBuilder->codeAppend("float rectDist = min(xDist, yDist);"); | |
| 150 | |
| 151 fragBuilder->codeAppendf("vec2 cornerCenter = %s.%s - %s.%s;", | |
| 152 sizesName, indices, | |
| 153 radiiName, indices); | |
| 154 fragBuilder->codeAppend("delta = vec2(abs(delta.x) - cornerCente r.x," | |
| 155 "abs(delta.y) - cornerCente r.y);"); | |
| 156 fragBuilder->codeAppendf("xDist = %s.%c - abs(delta.x);", | |
| 157 radiiName, indices[0]); | |
| 158 fragBuilder->codeAppendf("yDist = %s.%c - abs(delta.y);", | |
| 159 radiiName, indices[1]); | |
| 160 fragBuilder->codeAppend("float cornerDist = min(xDist, yDist);") ; | |
| 161 fragBuilder->codeAppend("float multiplier = step(0.0, cornerDist );"); | |
| 162 | |
| 163 fragBuilder->codeAppendf("delta += %s.%s;", radiiName, indices); | |
| 164 | |
| 165 fragBuilder->codeAppendf("cornerDist = 2.0 * %s.%c - length(delt a);", | |
| 166 radiiName, indices[0]); | |
| 167 | |
| 168 fragBuilder->codeAppendf("%s = (multiplier * cornerDist) +" | |
| 169 "((1.0-multiplier) * rectDist);", | |
| 170 outputName); | |
| 171 break; | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 void emitCode(EmitArgs& args) override { | |
| 176 const RRectsGaussianEdgeFP& fp = args.fFp.cast<RRectsGaussianEdgeFP> (); | |
| 177 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; | |
| 178 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; | |
| 179 | |
| 180 const char* positionsUniName = nullptr; | |
| 181 fPositionsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, | |
| 182 kVec4f_GrSLType, kDefault _GrSLPrecision, | |
| 183 "Positions", &positionsUn iName); | |
| 184 const char* sizesUniName = nullptr; | |
| 185 fSizesUni = uniformHandler->addUniform(kFragment_GrShaderFlag, | |
| 186 kVec4f_GrSLType, kDefault_GrS LPrecision, | |
| 187 "Sizes", &sizesUniName); | |
| 188 const char* radiiUniName = nullptr; | |
| 189 fRadiiUni = uniformHandler->addUniform(kFragment_GrShaderFlag, | |
| 190 kVec4f_GrSLType, kDefault_GrS LPrecision, | |
| 191 "Radii", &radiiUniName); | |
| 192 const char* padRadUniName = nullptr; | |
| 193 fPadRadUni = uniformHandler->addUniform(kFragment_GrShaderFlag, | |
| 194 kVec2f_GrSLType, kDefault_Gr SLPrecision, | |
| 195 "PadRad", &padRadUniName); | |
| 196 | |
| 197 fragBuilder->codeAppend("float firstDist;"); | |
| 198 fragBuilder->codeAppend("{"); | |
| 199 this->emitModeCode(fp.firstMode(), fragBuilder, | |
| 200 positionsUniName, sizesUniName, radiiUniName, "fi rstDist", "xy"); | |
| 201 fragBuilder->codeAppend("}"); | |
| 202 | |
| 203 fragBuilder->codeAppend("float secondDist;"); | |
| 204 fragBuilder->codeAppend("{"); | |
| 205 this->emitModeCode(fp.secondMode(), fragBuilder, | |
| 206 positionsUniName, sizesUniName, radiiUniName, "se condDist", "zw"); | |
| 207 fragBuilder->codeAppend("}"); | |
| 208 | |
| 209 // Here use the sign of the distance to the two round rects to mask off the different | |
| 210 // cases. | |
| 211 fragBuilder->codeAppend("float in1 = step(0.0f, firstDist);"); | |
| 212 fragBuilder->codeAppend("float in2 = step(0.0f, secondDist);"); | |
| 213 fragBuilder->codeAppend("float dist = " | |
| 214 "in1*in2 * min(firstDis t, secondDist);" | |
| 215 "in1*(1.0-in2) * firstDist +" | |
| 216 "(1.0-in1)*in2 * secondDist;" ); | |
| 217 | |
| 218 // Finally use the distance to apply the Gaussian edge | |
| 219 fragBuilder->codeAppendf("float factor = 1.0 - clamp((dist - %s.x)/% s.y, 0.0, 1.0);", | |
| 220 padRadUniName, padRadUniName); | |
| 221 fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.01 8;"); | |
| 222 fragBuilder->codeAppendf("%s = vec4(%s.rgb, factor);", | |
| 223 args.fOutputColor, args.fInputColor); | |
| 224 } | |
| 225 | |
| 226 static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, | |
| 227 GrProcessorKeyBuilder* b) { | |
| 228 const RRectsGaussianEdgeFP& fp = proc.cast<RRectsGaussianEdgeFP>(); | |
| 229 | |
| 230 b->add32(fp.firstMode() | (fp.secondMode() << 4)); | |
| 231 } | |
| 232 | |
| 233 protected: | |
| 234 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { | |
| 235 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP> (); | |
| 236 | |
| 237 const SkRRect& first = edgeFP.first(); | |
| 238 const SkRRect& second = edgeFP.second(); | |
| 239 | |
| 240 float positions[4] = { | |
| 241 first.getBounds().centerX(), | |
| 242 first.getBounds().centerY(), | |
| 243 second.getBounds().centerX(), | |
| 244 second.getBounds().centerY(), | |
| 245 }; | |
| 246 | |
| 247 if (positions[0] != fCachedPositions[0] || positions[1] != fCachedPo sitions[1] || | |
|
bsalomon
2016/09/12 14:56:52
Given the use case wonder if this is worth doing.
robertphillips
2016/09/12 15:13:49
Yeah I was wondering that too. Removed.
| |
| 248 positions[2] != fCachedPositions[2] || positions[3] != fCachedPo sitions[3]) { | |
| 249 pdman.set4fv(fPositionsUni, 1, positions); | |
| 250 memcpy(fCachedPositions, positions, sizeof(fCachedPositions)); | |
| 251 } | |
| 252 | |
| 253 float sizes[4] = { | |
| 254 0.5f * first.rect().width(), | |
| 255 0.5f * first.rect().height(), | |
| 256 0.5f * second.rect().width(), | |
| 257 0.5f * second.rect().height() | |
| 258 }; | |
| 259 | |
| 260 if (sizes[0] != fCachedSizes[0] || sizes[1] != fCachedSizes[1] || | |
| 261 sizes[2] != fCachedSizes[2] || sizes[3] != fCachedSizes[3]) { | |
| 262 pdman.set4fv(fSizesUni, 1, sizes); | |
| 263 memcpy(fCachedSizes, sizes, sizeof(fCachedSizes)); | |
| 264 } | |
| 265 | |
| 266 // This is a bit of overkill since fX should equal fY for both round rects but it | |
| 267 // makes the shader code simpler. | |
| 268 float radii[4] = { | |
| 269 0.5f * first.getSimpleRadii().fX, | |
| 270 0.5f * first.getSimpleRadii().fY, | |
| 271 0.5f * second.getSimpleRadii().fX, | |
| 272 0.5f * second.getSimpleRadii().fY | |
| 273 }; | |
| 274 | |
| 275 if (radii[0] != fCachedRadii[0] || radii[1] != fCachedRadii[1] || | |
| 276 radii[2] != fCachedRadii[2] || radii[3] != fCachedRadii[3]) { | |
| 277 pdman.set4fv(fRadiiUni, 1, radii); | |
| 278 memcpy(fCachedRadii, radii, sizeof(fCachedRadii)); | |
| 279 } | |
| 280 | |
| 281 SkPoint padRad = { edgeFP.pad(), edgeFP.radius() }; | |
| 282 | |
| 283 if (padRad != fCachedPadRad) { | |
| 284 pdman.set2fv(fPadRadUni, 1, &padRad.fX); | |
| 285 fCachedPadRad = padRad; | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 private: | |
| 290 // The centers of the two round rects (x1, y1, x2, y2) | |
| 291 GrGLSLProgramDataManager::UniformHandle fPositionsUni; | |
| 292 SkScalar fCachedPositions[4]; | |
| 293 | |
| 294 // The half widths and half heights of the two round rects (w1/2, h1/2, w2/2, h2/2) | |
| 295 // For circles we still upload both width & height to simplify things | |
| 296 GrGLSLProgramDataManager::UniformHandle fSizesUni; | |
| 297 SkScalar fCachedSizes[4]; | |
| 298 | |
| 299 // The half corner radii of the two round rects (rx1/2, ry1/2, rx2/2, ry 2/2) | |
| 300 // We upload both the x&y radii (although they are currently always the same) to make | |
| 301 // the indexing in the shader code simpler. In some future world we coul d also support | |
| 302 // non-circular corner round rects & ellipses. | |
| 303 GrGLSLProgramDataManager::UniformHandle fRadiiUni; | |
| 304 SkScalar fCachedRadii[4]; | |
| 305 | |
| 306 // The pad and radius parameters (padding, radius) | |
| 307 GrGLSLProgramDataManager::UniformHandle fPadRadUni; | |
| 308 SkPoint fCachedPadRad; | |
| 309 | |
| 310 typedef GrGLSLFragmentProcessor INHERITED; | |
| 311 }; | |
| 312 | |
| 313 void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { | |
| 314 GLSLRRectsGaussianEdgeFP::GenKey(*this, caps, b); | |
| 315 } | |
| 316 | |
| 317 const char* name() const override { return "RRectsGaussianEdgeFP"; } | |
| 318 | |
| 319 void onComputeInvariantOutput(GrInvariantOutput* inout) const override { | |
| 320 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); | |
| 321 } | |
| 322 | |
| 323 const SkRRect& first() const { return fFirst; } | |
| 324 Mode firstMode() const { return fFirstMode; } | |
| 325 const SkRRect& second() const { return fSecond; } | |
| 326 Mode secondMode() const { return fSecondMode; } | |
| 327 SkScalar radius() const { return fRadius; } | |
| 328 SkScalar pad() const { return fPad; } | |
| 329 | |
| 330 private: | |
| 331 static Mode ComputeMode(const SkRRect& rr) { | |
| 332 if (rr.isCircle()) { | |
| 333 return kCircle_Mode; | |
| 334 } else if (rr.isRect()) { | |
| 335 return kRect_Mode; | |
| 336 } else { | |
| 337 SkASSERT(rr.isSimpleCircular()); | |
| 338 return kSimpleCircular_Mode; | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { | |
| 343 return new GLSLRRectsGaussianEdgeFP; | |
| 344 } | |
| 345 | |
| 346 bool onIsEqual(const GrFragmentProcessor& proc) const override { | |
| 347 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>(); | |
| 348 return fFirst == edgeFP.fFirst && fSecond == edgeFP.fSecond && | |
| 349 fRadius == edgeFP.fRadius && fPad == edgeFP.fPad; | |
| 350 } | |
| 351 | |
| 352 SkRRect fFirst; | |
| 353 Mode fFirstMode; | |
| 354 SkRRect fSecond; | |
| 355 Mode fSecondMode; | |
| 356 SkScalar fRadius; | |
| 357 SkScalar fPad; | |
| 358 | |
| 359 typedef GrFragmentProcessor INHERITED; | |
| 360 }; | |
| 361 | |
| 362 //////////////////////////////////////////////////////////////////////////// | |
| 363 | |
| 364 sk_sp<GrFragmentProcessor> SkRRectsGaussianEdgeShaderImpl::asFragmentProcessor( | |
| 365 const AsFPAr gs& args) const { | |
| 366 return sk_make_sp<RRectsGaussianEdgeFP>(fFirst, fSecond, fRadius, fPad); | |
| 367 } | |
| 368 | |
| 369 #endif | |
| 370 | |
| 371 //////////////////////////////////////////////////////////////////////////// | |
| 372 | |
| 373 SkRRectsGaussianEdgeShaderImpl::GaussianEdgeShaderContext::GaussianEdgeShaderCon text( | |
| 374 const SkRRectsGaussianEdgeSh aderImpl& shader, | |
| 375 const ContextRec& rec) | |
| 376 : INHERITED(shader, rec) { | |
| 377 | |
| 378 fPaintColor = rec.fPaint->getColor(); | |
| 379 } | |
| 380 | |
| 381 void SkRRectsGaussianEdgeShaderImpl::GaussianEdgeShaderContext::shadeSpan(int x, int y, | |
| 382 SkPMCo lor result[], | |
| 383 int co unt) { | |
| 384 // TODO: implement | |
| 385 for (int i = 0; i < count; ++i) { | |
| 386 result[i] = fPaintColor; | |
| 387 } | |
| 388 } | |
| 389 | |
| 390 //////////////////////////////////////////////////////////////////////////// | |
| 391 | |
| 392 #ifndef SK_IGNORE_TO_STRING | |
| 393 void SkRRectsGaussianEdgeShaderImpl::toString(SkString* str) const { | |
| 394 str->appendf("RRectsGaussianEdgeShader: ()"); | |
| 395 } | |
| 396 #endif | |
| 397 | |
| 398 sk_sp<SkFlattenable> SkRRectsGaussianEdgeShaderImpl::CreateProc(SkReadBuffer& bu f) { | |
| 399 // Discarding SkShader flattenable params | |
| 400 bool hasLocalMatrix = buf.readBool(); | |
| 401 SkAssertResult(!hasLocalMatrix); | |
| 402 | |
| 403 SkRect rect1, rect2; | |
| 404 | |
| 405 buf.readRect(&rect1); | |
| 406 SkScalar xRad1 = buf.readScalar(); | |
| 407 SkScalar yRad1 = buf.readScalar(); | |
| 408 | |
| 409 buf.readRect(&rect2); | |
| 410 SkScalar xRad2 = buf.readScalar(); | |
| 411 SkScalar yRad2 = buf.readScalar(); | |
| 412 | |
| 413 SkScalar radius = buf.readScalar(); | |
| 414 SkScalar pad = buf.readScalar(); | |
| 415 | |
| 416 return sk_make_sp<SkRRectsGaussianEdgeShaderImpl>(SkRRect::MakeRectXY(rect1, xRad1, yRad1), | |
| 417 SkRRect::MakeRectXY(rect2, xRad2, yRad2), | |
| 418 radius, pad); | |
| 419 } | |
| 420 | |
| 421 void SkRRectsGaussianEdgeShaderImpl::flatten(SkWriteBuffer& buf) const { | |
| 422 INHERITED::flatten(buf); | |
| 423 | |
| 424 SkASSERT(fFirst.isRect() || fFirst.isCircle() || fFirst.isSimpleCircular()); | |
| 425 buf.writeRect(fFirst.rect()); | |
| 426 const SkVector& radii1 = fFirst.getSimpleRadii(); | |
| 427 buf.writeScalar(radii1.fX); | |
| 428 buf.writeScalar(radii1.fY); | |
| 429 | |
| 430 SkASSERT(fSecond.isRect() || fSecond.isCircle() || fSecond.isSimpleCircular( )); | |
| 431 buf.writeRect(fSecond.rect()); | |
| 432 const SkVector& radii2 = fSecond.getSimpleRadii(); | |
| 433 buf.writeScalar(radii2.fX); | |
| 434 buf.writeScalar(radii2.fY); | |
| 435 | |
| 436 buf.writeScalar(fRadius); | |
| 437 buf.writeScalar(fPad); | |
| 438 } | |
| 439 | |
| 440 size_t SkRRectsGaussianEdgeShaderImpl::onContextSize(const ContextRec& rec) cons t { | |
| 441 return sizeof(GaussianEdgeShaderContext); | |
| 442 } | |
| 443 | |
| 444 SkShader::Context* SkRRectsGaussianEdgeShaderImpl::onCreateContext(const Context Rec& rec, | |
| 445 void* storage ) const { | |
| 446 return new (storage) GaussianEdgeShaderContext(*this, rec); | |
| 447 } | |
| 448 | |
| 449 /////////////////////////////////////////////////////////////////////////////// | |
| 450 | |
| 451 sk_sp<SkShader> SkRRectsGaussianEdgeShader::Make(const SkRRect& first, | |
| 452 const SkRRect& second, | |
| 453 SkScalar radius, | |
| 454 SkScalar pad) { | |
| 455 if ((!first.isRect() && !first.isCircle() && !first.isSimpleCircular()) || | |
| 456 (!second.isRect() && !second.isCircle() && !second.isSimpleCircular())) { | |
| 457 // we only deal with the shapes where the x & y radii are equal | |
| 458 // and the same for all four corners | |
| 459 return nullptr; | |
| 460 } | |
| 461 | |
| 462 return sk_make_sp<SkRRectsGaussianEdgeShaderImpl>(first, second, radius, pad ); | |
| 463 } | |
| 464 | |
| 465 /////////////////////////////////////////////////////////////////////////////// | |
| 466 | |
| 467 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkRRectsGaussianEdgeShader) | |
| 468 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRRectsGaussianEdgeShaderImpl) | |
| 469 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END | |
| 470 | |
| 471 /////////////////////////////////////////////////////////////////////////////// | |
| OLD | NEW |