OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkTwoPointConicalGradient.h" | 8 #include "SkTwoPointConicalGradient.h" |
9 | 9 |
| 10 #include "SkTwoPointConicalGradient_gpu.h" |
| 11 |
10 static int valid_divide(float numer, float denom, float* ratio) { | 12 static int valid_divide(float numer, float denom, float* ratio) { |
11 SkASSERT(ratio); | 13 SkASSERT(ratio); |
12 if (0 == denom) { | 14 if (0 == denom) { |
13 return 0; | 15 return 0; |
14 } | 16 } |
15 *ratio = numer / denom; | 17 *ratio = numer / denom; |
16 return 1; | 18 return 1; |
17 } | 19 } |
18 | 20 |
19 // Return the number of distinct real roots, and write them into roots[] in | 21 // Return the number of distinct real roots, and write them into roots[] in |
(...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 | 323 |
322 void SkTwoPointConicalGradient::flatten( | 324 void SkTwoPointConicalGradient::flatten( |
323 SkWriteBuffer& buffer) const { | 325 SkWriteBuffer& buffer) const { |
324 this->INHERITED::flatten(buffer); | 326 this->INHERITED::flatten(buffer); |
325 buffer.writePoint(fCenter1); | 327 buffer.writePoint(fCenter1); |
326 buffer.writePoint(fCenter2); | 328 buffer.writePoint(fCenter2); |
327 buffer.writeScalar(fRadius1); | 329 buffer.writeScalar(fRadius1); |
328 buffer.writeScalar(fRadius2); | 330 buffer.writeScalar(fRadius2); |
329 } | 331 } |
330 | 332 |
331 ///////////////////////////////////////////////////////////////////// | |
332 | |
333 #if SK_SUPPORT_GPU | 333 #if SK_SUPPORT_GPU |
334 | 334 |
335 #include "GrTBackendEffectFactory.h" | |
336 | |
337 // For brevity | |
338 typedef GrGLUniformManager::UniformHandle UniformHandle; | |
339 | |
340 class GrGLConical2Gradient : public GrGLGradientEffect { | |
341 public: | |
342 | |
343 GrGLConical2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffe
ct&); | |
344 virtual ~GrGLConical2Gradient() { } | |
345 | |
346 virtual void emitCode(GrGLShaderBuilder*, | |
347 const GrDrawEffect&, | |
348 EffectKey, | |
349 const char* outputColor, | |
350 const char* inputColor, | |
351 const TransformedCoordsArray&, | |
352 const TextureSamplerArray&) SK_OVERRIDE; | |
353 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVER
RIDE; | |
354 | |
355 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); | |
356 | |
357 protected: | |
358 | |
359 UniformHandle fParamUni; | |
360 | |
361 const char* fVSVaryingName; | |
362 const char* fFSVaryingName; | |
363 | |
364 bool fIsDegenerate; | |
365 | |
366 // @{ | |
367 /// Values last uploaded as uniforms | |
368 | |
369 SkScalar fCachedCenter; | |
370 SkScalar fCachedRadius; | |
371 SkScalar fCachedDiffRadius; | |
372 | |
373 // @} | |
374 | |
375 private: | |
376 | |
377 typedef GrGLGradientEffect INHERITED; | |
378 | |
379 }; | |
380 | |
381 ///////////////////////////////////////////////////////////////////// | |
382 | |
383 class GrConical2Gradient : public GrGradientEffect { | |
384 public: | |
385 | |
386 static GrEffectRef* Create(GrContext* ctx, | |
387 const SkTwoPointConicalGradient& shader, | |
388 const SkMatrix& matrix, | |
389 SkShader::TileMode tm) { | |
390 AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matr
ix, tm))); | |
391 return CreateEffectRef(effect); | |
392 } | |
393 | |
394 virtual ~GrConical2Gradient() { } | |
395 | |
396 static const char* Name() { return "Two-Point Conical Gradient"; } | |
397 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { | |
398 return GrTBackendEffectFactory<GrConical2Gradient>::getInstance(); | |
399 } | |
400 | |
401 // The radial gradient parameters can collapse to a linear (instead of quadr
atic) equation. | |
402 bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(f
CenterX1); } | |
403 SkScalar center() const { return fCenterX1; } | |
404 SkScalar diffRadius() const { return fDiffRadius; } | |
405 SkScalar radius() const { return fRadius0; } | |
406 | |
407 typedef GrGLConical2Gradient GLEffect; | |
408 | |
409 private: | |
410 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { | |
411 const GrConical2Gradient& s = CastEffect<GrConical2Gradient>(sBase); | |
412 return (INHERITED::onIsEqual(sBase) && | |
413 this->fCenterX1 == s.fCenterX1 && | |
414 this->fRadius0 == s.fRadius0 && | |
415 this->fDiffRadius == s.fDiffRadius); | |
416 } | |
417 | |
418 GrConical2Gradient(GrContext* ctx, | |
419 const SkTwoPointConicalGradient& shader, | |
420 const SkMatrix& matrix, | |
421 SkShader::TileMode tm) | |
422 : INHERITED(ctx, shader, matrix, tm) | |
423 , fCenterX1(shader.getCenterX1()) | |
424 , fRadius0(shader.getStartRadius()) | |
425 , fDiffRadius(shader.getDiffRadius()) { | |
426 // We pass the linear part of the quadratic as a varying. | |
427 // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) | |
428 fBTransform = this->getCoordTransform(); | |
429 SkMatrix& bMatrix = *fBTransform.accessMatrix(); | |
430 SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius); | |
431 bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMat
rix::kMScaleX]) + | |
432 SkScalarMul(r0dr, bMatrix[SkMatrix::
kMPersp0])); | |
433 bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatr
ix::kMSkewX]) + | |
434 SkScalarMul(r0dr, bMatrix[SkMatrix::k
MPersp1])); | |
435 bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMat
rix::kMTransX]) + | |
436 SkScalarMul(r0dr, bMatrix[SkMatrix::
kMPersp2])); | |
437 this->addCoordTransform(&fBTransform); | |
438 } | |
439 | |
440 GR_DECLARE_EFFECT_TEST; | |
441 | |
442 // @{ | |
443 // Cache of values - these can change arbitrarily, EXCEPT | |
444 // we shouldn't change between degenerate and non-degenerate?! | |
445 | |
446 GrCoordTransform fBTransform; | |
447 SkScalar fCenterX1; | |
448 SkScalar fRadius0; | |
449 SkScalar fDiffRadius; | |
450 | |
451 // @} | |
452 | |
453 typedef GrGradientEffect INHERITED; | |
454 }; | |
455 | |
456 GR_DEFINE_EFFECT_TEST(GrConical2Gradient); | |
457 | |
458 GrEffectRef* GrConical2Gradient::TestCreate(SkRandom* random, | |
459 GrContext* context, | |
460 const GrDrawTargetCaps&, | |
461 GrTexture**) { | |
462 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; | |
463 SkScalar radius1 = random->nextUScalar1(); | |
464 SkPoint center2; | |
465 SkScalar radius2; | |
466 do { | |
467 center2.set(random->nextUScalar1(), random->nextUScalar1()); | |
468 radius2 = random->nextUScalar1 (); | |
469 // If the circles are identical the factory will give us an empty shader
. | |
470 } while (radius1 == radius2 && center1 == center2); | |
471 | |
472 SkColor colors[kMaxRandomGradientColors]; | |
473 SkScalar stopsArray[kMaxRandomGradientColors]; | |
474 SkScalar* stops = stopsArray; | |
475 SkShader::TileMode tm; | |
476 int colorCount = RandomGradientParams(random, colors, &stops, &tm); | |
477 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center
1, radius1, | |
478 center
2, radius2, | |
479 colors
, stops, colorCount, | |
480 tm)); | |
481 SkPaint paint; | |
482 return shader->asNewEffect(context, paint); | |
483 } | |
484 | |
485 | |
486 ///////////////////////////////////////////////////////////////////// | |
487 | |
488 GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory
, | |
489 const GrDrawEffect& drawEffect) | |
490 : INHERITED(factory) | |
491 , fVSVaryingName(NULL) | |
492 , fFSVaryingName(NULL) | |
493 , fCachedCenter(SK_ScalarMax) | |
494 , fCachedRadius(-SK_ScalarMax) | |
495 , fCachedDiffRadius(-SK_ScalarMax) { | |
496 | |
497 const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>()
; | |
498 fIsDegenerate = data.isDegenerate(); | |
499 } | |
500 | |
501 void GrGLConical2Gradient::emitCode(GrGLShaderBuilder* builder, | |
502 const GrDrawEffect&, | |
503 EffectKey key, | |
504 const char* outputColor, | |
505 const char* inputColor, | |
506 const TransformedCoordsArray& coords, | |
507 const TextureSamplerArray& samplers) { | |
508 this->emitUniforms(builder, key); | |
509 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility
, | |
510 kFloat_GrSLType, "Conical2FSParams", 6)
; | |
511 | |
512 SkString cName("c"); | |
513 SkString ac4Name("ac4"); | |
514 SkString dName("d"); | |
515 SkString qName("q"); | |
516 SkString r0Name("r0"); | |
517 SkString r1Name("r1"); | |
518 SkString tName("t"); | |
519 SkString p0; // 4a | |
520 SkString p1; // 1/a | |
521 SkString p2; // distance between centers | |
522 SkString p3; // start radius | |
523 SkString p4; // start radius squared | |
524 SkString p5; // difference in radii (r1 - r0) | |
525 | |
526 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); | |
527 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); | |
528 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); | |
529 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3); | |
530 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4); | |
531 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5); | |
532 | |
533 // We interpolate the linear component in coords[1]. | |
534 SkASSERT(coords[0].type() == coords[1].type()); | |
535 const char* coords2D; | |
536 SkString bVar; | |
537 if (kVec3f_GrSLType == coords[0].type()) { | |
538 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\
n", | |
539 coords[0].c_str(), coords[1].c_str(), coords[0].c
_str()); | |
540 coords2D = "interpolants.xy"; | |
541 bVar = "interpolants.z"; | |
542 } else { | |
543 coords2D = coords[0].c_str(); | |
544 bVar.printf("%s.x", coords[1].c_str()); | |
545 } | |
546 | |
547 // output will default to transparent black (we simply won't write anything | |
548 // else to it if invalid, instead of discarding or returning prematurely) | |
549 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); | |
550 | |
551 // c = (x^2)+(y^2) - params[4] | |
552 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", | |
553 cName.c_str(), coords2D, coords2D, p4.c_str()); | |
554 | |
555 // Non-degenerate case (quadratic) | |
556 if (!fIsDegenerate) { | |
557 | |
558 // ac4 = params[0] * c | |
559 builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_
str(), | |
560 cName.c_str()); | |
561 | |
562 // d = b^2 - ac4 | |
563 builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(), | |
564 bVar.c_str(), bVar.c_str(), ac4Name.c_str()); | |
565 | |
566 // only proceed if discriminant is >= 0 | |
567 builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str()); | |
568 | |
569 // intermediate value we'll use to compute the roots | |
570 // q = -0.5 * (b +/- sqrt(d)) | |
571 builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1
.0)" | |
572 " * sqrt(%s));\n", qName.c_str(), bVar.c_str(), | |
573 bVar.c_str(), dName.c_str()); | |
574 | |
575 // compute both roots | |
576 // r0 = q * params[1] | |
577 builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(), | |
578 qName.c_str(), p1.c_str()); | |
579 // r1 = c / q | |
580 builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(), | |
581 cName.c_str(), qName.c_str()); | |
582 | |
583 // Note: If there are two roots that both generate radius(t) > 0, the | |
584 // Canvas spec says to choose the larger t. | |
585 | |
586 // so we'll look at the larger one first: | |
587 builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(), | |
588 r0Name.c_str(), r1Name.c_str()); | |
589 | |
590 // if r(t) > 0, then we're done; t will be our x coordinate | |
591 builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), | |
592 p5.c_str(), p3.c_str()); | |
593 | |
594 builder->fsCodeAppend("\t\t"); | |
595 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, sa
mplers); | |
596 | |
597 // otherwise, if r(t) for the larger root was <= 0, try the other root | |
598 builder->fsCodeAppend("\t\t} else {\n"); | |
599 builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(), | |
600 r0Name.c_str(), r1Name.c_str()); | |
601 | |
602 // if r(t) > 0 for the smaller root, then t will be our x coordinate | |
603 builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n", | |
604 tName.c_str(), p5.c_str(), p3.c_str()); | |
605 | |
606 builder->fsCodeAppend("\t\t\t"); | |
607 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, sa
mplers); | |
608 | |
609 // end if (r(t) > 0) for smaller root | |
610 builder->fsCodeAppend("\t\t\t}\n"); | |
611 // end if (r(t) > 0), else, for larger root | |
612 builder->fsCodeAppend("\t\t}\n"); | |
613 // end if (discriminant >= 0) | |
614 builder->fsCodeAppend("\t}\n"); | |
615 } else { | |
616 | |
617 // linear case: t = -c/b | |
618 builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(), | |
619 cName.c_str(), bVar.c_str()); | |
620 | |
621 // if r(t) > 0, then t will be the x coordinate | |
622 builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), | |
623 p5.c_str(), p3.c_str()); | |
624 builder->fsCodeAppend("\t"); | |
625 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, sa
mplers); | |
626 builder->fsCodeAppend("\t}\n"); | |
627 } | |
628 } | |
629 | |
630 void GrGLConical2Gradient::setData(const GrGLUniformManager& uman, | |
631 const GrDrawEffect& drawEffect) { | |
632 INHERITED::setData(uman, drawEffect); | |
633 const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>()
; | |
634 SkASSERT(data.isDegenerate() == fIsDegenerate); | |
635 SkScalar centerX1 = data.center(); | |
636 SkScalar radius0 = data.radius(); | |
637 SkScalar diffRadius = data.diffRadius(); | |
638 | |
639 if (fCachedCenter != centerX1 || | |
640 fCachedRadius != radius0 || | |
641 fCachedDiffRadius != diffRadius) { | |
642 | |
643 SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius; | |
644 | |
645 // When we're in the degenerate (linear) case, the second | |
646 // value will be INF but the program doesn't read it. (We | |
647 // use the same 6 uniforms even though we don't need them | |
648 // all in the linear case just to keep the code complexity | |
649 // down). | |
650 float values[6] = { | |
651 SkScalarToFloat(a * 4), | |
652 1.f / (SkScalarToFloat(a)), | |
653 SkScalarToFloat(centerX1), | |
654 SkScalarToFloat(radius0), | |
655 SkScalarToFloat(SkScalarMul(radius0, radius0)), | |
656 SkScalarToFloat(diffRadius) | |
657 }; | |
658 | |
659 uman.set1fv(fParamUni, 6, values); | |
660 fCachedCenter = centerX1; | |
661 fCachedRadius = radius0; | |
662 fCachedDiffRadius = diffRadius; | |
663 } | |
664 } | |
665 | |
666 GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrDrawEffect& drawEffec
t, | |
667 const GrGLCaps&) { | |
668 enum { | |
669 kIsDegenerate = 1 << kBaseKeyBitCnt, | |
670 }; | |
671 | |
672 EffectKey key = GenBaseGradientKey(drawEffect); | |
673 if (drawEffect.castEffect<GrConical2Gradient>().isDegenerate()) { | |
674 key |= kIsDegenerate; | |
675 } | |
676 return key; | |
677 } | |
678 | |
679 ///////////////////////////////////////////////////////////////////// | |
680 | |
681 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const Sk
Paint&) const { | 335 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const Sk
Paint&) const { |
682 SkASSERT(NULL != context); | 336 SkASSERT(NULL != context); |
683 SkASSERT(fPtsToUnit.isIdentity()); | 337 SkASSERT(fPtsToUnit.isIdentity()); |
684 // invert the localM, translate to center1, rotate so center2 is on x axis. | 338 // invert the localM, translate to center1, rotate so center2 is on x axis. |
685 SkMatrix matrix; | 339 SkMatrix matrix; |
686 if (!this->getLocalMatrix().invert(&matrix)) { | 340 if (!this->getLocalMatrix().invert(&matrix)) { |
687 return NULL; | 341 return NULL; |
688 } | 342 } |
689 matrix.postTranslate(-fCenter1.fX, -fCenter1.fY); | 343 matrix.postTranslate(-fCenter1.fX, -fCenter1.fY); |
690 | 344 |
691 SkPoint diff = fCenter2 - fCenter1; | 345 SkPoint diff = fCenter2 - fCenter1; |
692 SkScalar diffLen = diff.length(); | 346 SkScalar diffLen = diff.length(); |
693 if (0 != diffLen) { | 347 if (0 != diffLen) { |
694 SkScalar invDiffLen = SkScalarInvert(diffLen); | 348 SkScalar invDiffLen = SkScalarInvert(diffLen); |
695 SkMatrix rot; | 349 SkMatrix rot; |
696 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY), | 350 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY), |
697 SkScalarMul(invDiffLen, diff.fX)); | 351 SkScalarMul(invDiffLen, diff.fX)); |
698 matrix.postConcat(rot); | 352 matrix.postConcat(rot); |
699 } | 353 } |
700 | 354 |
701 return GrConical2Gradient::Create(context, *this, matrix, fTileMode); | 355 return Gr2PtConicalGradientEffect::Create(context, *this, matrix, fTileMode)
; |
702 } | 356 } |
703 | 357 |
704 #else | 358 #else |
705 | 359 |
706 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext*, const SkPaint&)
const { | 360 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext*, const SkPaint&)
const { |
707 SkDEBUGFAIL("Should not call in GPU-less build"); | 361 SkDEBUGFAIL("Should not call in GPU-less build"); |
708 return NULL; | 362 return NULL; |
709 } | 363 } |
710 | 364 |
711 #endif | 365 #endif |
(...skipping 16 matching lines...) Expand all Loading... |
728 str->appendScalar(fCenter2.fY); | 382 str->appendScalar(fCenter2.fY); |
729 str->append(") radius2: "); | 383 str->append(") radius2: "); |
730 str->appendScalar(fRadius2); | 384 str->appendScalar(fRadius2); |
731 str->append(" "); | 385 str->append(" "); |
732 | 386 |
733 this->INHERITED::toString(str); | 387 this->INHERITED::toString(str); |
734 | 388 |
735 str->append(")"); | 389 str->append(")"); |
736 } | 390 } |
737 #endif | 391 #endif |
OLD | NEW |