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 |
| 107 void emitModeCode(Mode mode, |
| 108 GrGLSLFPFragmentBuilder* fragBuilder, |
| 109 const char* posName, |
| 110 const char* sizesName, |
| 111 const char* radiiName, |
| 112 const char* outputName, |
| 113 const char indices[2]) { // how to access the params
for the 2 rrects |
| 114 |
| 115 // positive distance is towards the center of the circle |
| 116 fragBuilder->codeAppendf("vec2 delta = %s.xy - %s.%s;", |
| 117 fragBuilder->fragmentPosition(), |
| 118 posName, indices); |
| 119 |
| 120 switch (mode) { |
| 121 case kCircle_Mode: |
| 122 fragBuilder->codeAppendf("%s = %s.%c - length(delta);", |
| 123 outputName, |
| 124 sizesName, indices[0]); |
| 125 break; |
| 126 case kRect_Mode: |
| 127 fragBuilder->codeAppendf("float xDist = %s.%c - abs(delta.x);", |
| 128 sizesName, indices[0]); |
| 129 fragBuilder->codeAppendf("float yDist = %s.%c - abs(delta.y);", |
| 130 sizesName, indices[1]); |
| 131 fragBuilder->codeAppendf("%s = min(xDist, yDist);", outputName); |
| 132 break; |
| 133 case kSimpleCircular_Mode: |
| 134 // For the circular round rect we first compute the distance |
| 135 // to the rect. Then we compute a multiplier that is 1 if the |
| 136 // point is in one of the circular corners. We then compute the |
| 137 // distance from the corner and then use the multiplier to mask |
| 138 // between the two distances. |
| 139 fragBuilder->codeAppendf("float xDist = %s.%c - abs(delta.x);", |
| 140 sizesName, indices[0]); |
| 141 fragBuilder->codeAppendf("float yDist = %s.%c - abs(delta.y);", |
| 142 sizesName, indices[1]); |
| 143 fragBuilder->codeAppend("float rectDist = min(xDist, yDist);"); |
| 144 |
| 145 fragBuilder->codeAppendf("vec2 cornerCenter = %s.%s - %s.%s;", |
| 146 sizesName, indices, |
| 147 radiiName, indices); |
| 148 fragBuilder->codeAppend("delta = vec2(abs(delta.x) - cornerCente
r.x," |
| 149 "abs(delta.y) - cornerCente
r.y);"); |
| 150 fragBuilder->codeAppendf("xDist = %s.%c - abs(delta.x);", |
| 151 radiiName, indices[0]); |
| 152 fragBuilder->codeAppendf("yDist = %s.%c - abs(delta.y);", |
| 153 radiiName, indices[1]); |
| 154 fragBuilder->codeAppend("float cornerDist = min(xDist, yDist);")
; |
| 155 fragBuilder->codeAppend("float multiplier = step(0.0, cornerDist
);"); |
| 156 |
| 157 fragBuilder->codeAppendf("delta += %s.%s;", radiiName, indices); |
| 158 |
| 159 fragBuilder->codeAppendf("cornerDist = 2.0 * %s.%c - length(delt
a);", |
| 160 radiiName, indices[0]); |
| 161 |
| 162 fragBuilder->codeAppendf("%s = (multiplier * cornerDist) +" |
| 163 "((1.0-multiplier) * rectDist);", |
| 164 outputName); |
| 165 break; |
| 166 } |
| 167 } |
| 168 |
| 169 void emitCode(EmitArgs& args) override { |
| 170 const RRectsGaussianEdgeFP& fp = args.fFp.cast<RRectsGaussianEdgeFP>
(); |
| 171 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
| 172 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
| 173 |
| 174 const char* positionsUniName = nullptr; |
| 175 fPositionsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, |
| 176 kVec4f_GrSLType, kDefault
_GrSLPrecision, |
| 177 "Positions", &positionsUn
iName); |
| 178 const char* sizesUniName = nullptr; |
| 179 fSizesUni = uniformHandler->addUniform(kFragment_GrShaderFlag, |
| 180 kVec4f_GrSLType, kDefault_GrS
LPrecision, |
| 181 "Sizes", &sizesUniName); |
| 182 const char* radiiUniName = nullptr; |
| 183 fRadiiUni = uniformHandler->addUniform(kFragment_GrShaderFlag, |
| 184 kVec4f_GrSLType, kDefault_GrS
LPrecision, |
| 185 "Radii", &radiiUniName); |
| 186 const char* padRadUniName = nullptr; |
| 187 fPadRadUni = uniformHandler->addUniform(kFragment_GrShaderFlag, |
| 188 kVec2f_GrSLType, kDefault_Gr
SLPrecision, |
| 189 "PadRad", &padRadUniName); |
| 190 |
| 191 fragBuilder->codeAppend("float firstDist;"); |
| 192 fragBuilder->codeAppend("{"); |
| 193 this->emitModeCode(fp.firstMode(), fragBuilder, |
| 194 positionsUniName, sizesUniName, radiiUniName, "fi
rstDist", "xy"); |
| 195 fragBuilder->codeAppend("}"); |
| 196 |
| 197 fragBuilder->codeAppend("float secondDist;"); |
| 198 fragBuilder->codeAppend("{"); |
| 199 this->emitModeCode(fp.secondMode(), fragBuilder, |
| 200 positionsUniName, sizesUniName, radiiUniName, "se
condDist", "zw"); |
| 201 fragBuilder->codeAppend("}"); |
| 202 |
| 203 // Here use the sign of the distance to the two round rects to mask
off the different |
| 204 // cases. |
| 205 fragBuilder->codeAppend("float in1 = step(0.0f, firstDist);"); |
| 206 fragBuilder->codeAppend("float in2 = step(0.0f, secondDist);"); |
| 207 fragBuilder->codeAppend("float dist = " |
| 208 "in1*in2 * min(firstDis
t, secondDist);" |
| 209 "in1*(1.0-in2) * firstDist +" |
| 210 "(1.0-in1)*in2 * secondDist;"
); |
| 211 |
| 212 // Finally use the distance to apply the Gaussian edge |
| 213 fragBuilder->codeAppendf("float factor = 1.0 - clamp((dist - %s.x)/%
s.y, 0.0, 1.0);", |
| 214 padRadUniName, padRadUniName); |
| 215 fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.01
8;"); |
| 216 fragBuilder->codeAppendf("%s = vec4(%s.rgb, factor);", |
| 217 args.fOutputColor, args.fInputColor); |
| 218 } |
| 219 |
| 220 static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, |
| 221 GrProcessorKeyBuilder* b) { |
| 222 const RRectsGaussianEdgeFP& fp = proc.cast<RRectsGaussianEdgeFP>(); |
| 223 |
| 224 b->add32(fp.firstMode() | (fp.secondMode() << 4)); |
| 225 } |
| 226 |
| 227 protected: |
| 228 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor&
proc) override { |
| 229 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>
(); |
| 230 |
| 231 const SkRRect& first = edgeFP.first(); |
| 232 const SkRRect& second = edgeFP.second(); |
| 233 |
| 234 pdman.set4f(fPositionsUni, |
| 235 first.getBounds().centerX(), |
| 236 first.getBounds().centerY(), |
| 237 second.getBounds().centerX(), |
| 238 second.getBounds().centerY()); |
| 239 |
| 240 pdman.set4f(fSizesUni, |
| 241 0.5f * first.rect().width(), |
| 242 0.5f * first.rect().height(), |
| 243 0.5f * second.rect().width(), |
| 244 0.5f * second.rect().height()); |
| 245 |
| 246 // This is a bit of overkill since fX should equal fY for both round
rects but it |
| 247 // makes the shader code simpler. |
| 248 pdman.set4f(fRadiiUni, |
| 249 0.5f * first.getSimpleRadii().fX, |
| 250 0.5f * first.getSimpleRadii().fY, |
| 251 0.5f * second.getSimpleRadii().fX, |
| 252 0.5f * second.getSimpleRadii().fY); |
| 253 |
| 254 pdman.set2f(fPadRadUni, edgeFP.pad(), edgeFP.radius()); |
| 255 } |
| 256 |
| 257 private: |
| 258 // The centers of the two round rects (x1, y1, x2, y2) |
| 259 GrGLSLProgramDataManager::UniformHandle fPositionsUni; |
| 260 |
| 261 // The half widths and half heights of the two round rects (w1/2, h1/2,
w2/2, h2/2) |
| 262 // For circles we still upload both width & height to simplify things |
| 263 GrGLSLProgramDataManager::UniformHandle fSizesUni; |
| 264 |
| 265 // The half corner radii of the two round rects (rx1/2, ry1/2, rx2/2, ry
2/2) |
| 266 // We upload both the x&y radii (although they are currently always the
same) to make |
| 267 // the indexing in the shader code simpler. In some future world we coul
d also support |
| 268 // non-circular corner round rects & ellipses. |
| 269 GrGLSLProgramDataManager::UniformHandle fRadiiUni; |
| 270 |
| 271 // The pad and radius parameters (padding, radius) |
| 272 GrGLSLProgramDataManager::UniformHandle fPadRadUni; |
| 273 |
| 274 typedef GrGLSLFragmentProcessor INHERITED; |
| 275 }; |
| 276 |
| 277 void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b)
const override { |
| 278 GLSLRRectsGaussianEdgeFP::GenKey(*this, caps, b); |
| 279 } |
| 280 |
| 281 const char* name() const override { return "RRectsGaussianEdgeFP"; } |
| 282 |
| 283 void onComputeInvariantOutput(GrInvariantOutput* inout) const override { |
| 284 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); |
| 285 } |
| 286 |
| 287 const SkRRect& first() const { return fFirst; } |
| 288 Mode firstMode() const { return fFirstMode; } |
| 289 const SkRRect& second() const { return fSecond; } |
| 290 Mode secondMode() const { return fSecondMode; } |
| 291 SkScalar radius() const { return fRadius; } |
| 292 SkScalar pad() const { return fPad; } |
| 293 |
| 294 private: |
| 295 static Mode ComputeMode(const SkRRect& rr) { |
| 296 if (rr.isCircle()) { |
| 297 return kCircle_Mode; |
| 298 } else if (rr.isRect()) { |
| 299 return kRect_Mode; |
| 300 } else { |
| 301 SkASSERT(rr.isSimpleCircular()); |
| 302 return kSimpleCircular_Mode; |
| 303 } |
| 304 } |
| 305 |
| 306 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { |
| 307 return new GLSLRRectsGaussianEdgeFP; |
| 308 } |
| 309 |
| 310 bool onIsEqual(const GrFragmentProcessor& proc) const override { |
| 311 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>(); |
| 312 return fFirst == edgeFP.fFirst && fSecond == edgeFP.fSecond && |
| 313 fRadius == edgeFP.fRadius && fPad == edgeFP.fPad; |
| 314 } |
| 315 |
| 316 SkRRect fFirst; |
| 317 Mode fFirstMode; |
| 318 SkRRect fSecond; |
| 319 Mode fSecondMode; |
| 320 SkScalar fRadius; |
| 321 SkScalar fPad; |
| 322 |
| 323 typedef GrFragmentProcessor INHERITED; |
| 324 }; |
| 325 |
| 326 //////////////////////////////////////////////////////////////////////////// |
| 327 |
| 328 sk_sp<GrFragmentProcessor> SkRRectsGaussianEdgeShaderImpl::asFragmentProcessor( |
| 329 const AsFPAr
gs& args) const { |
| 330 return sk_make_sp<RRectsGaussianEdgeFP>(fFirst, fSecond, fRadius, fPad); |
| 331 } |
| 332 |
| 333 #endif |
| 334 |
| 335 //////////////////////////////////////////////////////////////////////////// |
| 336 |
| 337 SkRRectsGaussianEdgeShaderImpl::GaussianEdgeShaderContext::GaussianEdgeShaderCon
text( |
| 338 const SkRRectsGaussianEdgeSh
aderImpl& shader, |
| 339 const ContextRec& rec) |
| 340 : INHERITED(shader, rec) { |
| 341 |
| 342 fPaintColor = rec.fPaint->getColor(); |
| 343 } |
| 344 |
| 345 void SkRRectsGaussianEdgeShaderImpl::GaussianEdgeShaderContext::shadeSpan(int x,
int y, |
| 346 SkPMCo
lor result[], |
| 347 int co
unt) { |
| 348 // TODO: implement |
| 349 for (int i = 0; i < count; ++i) { |
| 350 result[i] = fPaintColor; |
| 351 } |
| 352 } |
| 353 |
| 354 //////////////////////////////////////////////////////////////////////////// |
| 355 |
| 356 #ifndef SK_IGNORE_TO_STRING |
| 357 void SkRRectsGaussianEdgeShaderImpl::toString(SkString* str) const { |
| 358 str->appendf("RRectsGaussianEdgeShader: ()"); |
| 359 } |
| 360 #endif |
| 361 |
| 362 sk_sp<SkFlattenable> SkRRectsGaussianEdgeShaderImpl::CreateProc(SkReadBuffer& bu
f) { |
| 363 // Discarding SkShader flattenable params |
| 364 bool hasLocalMatrix = buf.readBool(); |
| 365 SkAssertResult(!hasLocalMatrix); |
| 366 |
| 367 SkRect rect1, rect2; |
| 368 |
| 369 buf.readRect(&rect1); |
| 370 SkScalar xRad1 = buf.readScalar(); |
| 371 SkScalar yRad1 = buf.readScalar(); |
| 372 |
| 373 buf.readRect(&rect2); |
| 374 SkScalar xRad2 = buf.readScalar(); |
| 375 SkScalar yRad2 = buf.readScalar(); |
| 376 |
| 377 SkScalar radius = buf.readScalar(); |
| 378 SkScalar pad = buf.readScalar(); |
| 379 |
| 380 return sk_make_sp<SkRRectsGaussianEdgeShaderImpl>(SkRRect::MakeRectXY(rect1,
xRad1, yRad1), |
| 381 SkRRect::MakeRectXY(rect2,
xRad2, yRad2), |
| 382 radius, pad); |
| 383 } |
| 384 |
| 385 void SkRRectsGaussianEdgeShaderImpl::flatten(SkWriteBuffer& buf) const { |
| 386 INHERITED::flatten(buf); |
| 387 |
| 388 SkASSERT(fFirst.isRect() || fFirst.isCircle() || fFirst.isSimpleCircular()); |
| 389 buf.writeRect(fFirst.rect()); |
| 390 const SkVector& radii1 = fFirst.getSimpleRadii(); |
| 391 buf.writeScalar(radii1.fX); |
| 392 buf.writeScalar(radii1.fY); |
| 393 |
| 394 SkASSERT(fSecond.isRect() || fSecond.isCircle() || fSecond.isSimpleCircular(
)); |
| 395 buf.writeRect(fSecond.rect()); |
| 396 const SkVector& radii2 = fSecond.getSimpleRadii(); |
| 397 buf.writeScalar(radii2.fX); |
| 398 buf.writeScalar(radii2.fY); |
| 399 |
| 400 buf.writeScalar(fRadius); |
| 401 buf.writeScalar(fPad); |
| 402 } |
| 403 |
| 404 size_t SkRRectsGaussianEdgeShaderImpl::onContextSize(const ContextRec& rec) cons
t { |
| 405 return sizeof(GaussianEdgeShaderContext); |
| 406 } |
| 407 |
| 408 SkShader::Context* SkRRectsGaussianEdgeShaderImpl::onCreateContext(const Context
Rec& rec, |
| 409 void* storage
) const { |
| 410 return new (storage) GaussianEdgeShaderContext(*this, rec); |
| 411 } |
| 412 |
| 413 /////////////////////////////////////////////////////////////////////////////// |
| 414 |
| 415 sk_sp<SkShader> SkRRectsGaussianEdgeShader::Make(const SkRRect& first, |
| 416 const SkRRect& second, |
| 417 SkScalar radius, |
| 418 SkScalar pad) { |
| 419 if ((!first.isRect() && !first.isCircle() && !first.isSimpleCircular()) ||
|
| 420 (!second.isRect() && !second.isCircle() && !second.isSimpleCircular()))
{ |
| 421 // we only deal with the shapes where the x & y radii are equal |
| 422 // and the same for all four corners |
| 423 return nullptr; |
| 424 } |
| 425 |
| 426 return sk_make_sp<SkRRectsGaussianEdgeShaderImpl>(first, second, radius, pad
); |
| 427 } |
| 428 |
| 429 /////////////////////////////////////////////////////////////////////////////// |
| 430 |
| 431 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkRRectsGaussianEdgeShader) |
| 432 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRRectsGaussianEdgeShaderImpl) |
| 433 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END |
| 434 |
| 435 /////////////////////////////////////////////////////////////////////////////// |
OLD | NEW |