OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 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 "GrOvalRenderer.h" | 8 #include "GrOvalRenderer.h" |
9 | 9 |
10 #include "GrBatchFlushState.h" | 10 #include "GrBatchFlushState.h" |
(...skipping 506 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
517 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor); | 517 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor); |
518 | 518 |
519 sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTes
tData* d) { | 519 sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTes
tData* d) { |
520 return sk_sp<GrGeometryProcessor>( | 520 return sk_sp<GrGeometryProcessor>( |
521 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom), | 521 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom), |
522 (DIEllipseStyle)(d->fRandom->nextRangeU(0
,2)))); | 522 (DIEllipseStyle)(d->fRandom->nextRangeU(0
,2)))); |
523 } | 523 } |
524 | 524 |
525 /////////////////////////////////////////////////////////////////////////////// | 525 /////////////////////////////////////////////////////////////////////////////// |
526 | 526 |
| 527 GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color, |
| 528 const SkMatrix& viewMatrix, |
| 529 const SkRect& oval, |
| 530 const SkStrokeRec& stroke, |
| 531 GrShaderCaps* shaderCaps) { |
| 532 // we can draw circles |
| 533 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(
viewMatrix)) { |
| 534 return CreateCircleBatch(color, viewMatrix, oval, stroke); |
| 535 } |
| 536 |
| 537 // if we have shader derivative support, render as device-independent |
| 538 if (shaderCaps->shaderDerivativeSupport()) { |
| 539 return CreateDIEllipseBatch(color, viewMatrix, oval, stroke); |
| 540 } |
| 541 |
| 542 // otherwise axis-aligned ellipses only |
| 543 if (viewMatrix.rectStaysRect()) { |
| 544 return CreateEllipseBatch(color, viewMatrix, oval, stroke); |
| 545 } |
| 546 |
| 547 return nullptr; |
| 548 } |
| 549 |
| 550 /////////////////////////////////////////////////////////////////////////////// |
| 551 |
527 class CircleBatch : public GrVertexBatch { | 552 class CircleBatch : public GrVertexBatch { |
528 public: | 553 public: |
529 DEFINE_BATCH_CLASS_ID | 554 DEFINE_BATCH_CLASS_ID |
530 | 555 |
531 CircleBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& circle, | 556 struct Geometry { |
532 const SkStrokeRec& stroke) | 557 SkRect fDevBounds; |
533 : INHERITED(ClassID()) | 558 SkScalar fInnerRadius; |
534 , fViewMatrixIfUsingLocalCoords(viewMatrix) { | 559 SkScalar fOuterRadius; |
535 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY()); | 560 GrColor fColor; |
536 viewMatrix.mapPoints(¢er, 1); | 561 }; |
537 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width())); | |
538 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth()); | |
539 | 562 |
540 SkStrokeRec::Style style = stroke.getStyle(); | 563 CircleBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool strok
ed) |
541 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || | 564 : INHERITED(ClassID()) |
542 SkStrokeRec::kHairline_Style == style; | 565 , fStroked(stroked) |
543 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == st
yle; | 566 , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
544 | 567 fGeoData.push_back(geometry); |
545 SkScalar innerRadius = 0.0f; | 568 this->setBounds(geometry.fDevBounds); |
546 SkScalar outerRadius = radius; | |
547 SkScalar halfWidth = 0; | |
548 if (hasStroke) { | |
549 if (SkScalarNearlyZero(strokeWidth)) { | |
550 halfWidth = SK_ScalarHalf; | |
551 } else { | |
552 halfWidth = SkScalarHalf(strokeWidth); | |
553 } | |
554 | |
555 outerRadius += halfWidth; | |
556 if (isStrokeOnly) { | |
557 innerRadius = radius - halfWidth; | |
558 } | |
559 } | |
560 | |
561 // The radii are outset for two reasons. First, it allows the shader to
simply perform | |
562 // simpler computation because the computed alpha is zero, rather than 5
0%, at the radius. | |
563 // Second, the outer radius is used to compute the verts of the bounding
box that is | |
564 // rendered and the outset ensures the box will cover all partially cove
red by the circle. | |
565 outerRadius += SK_ScalarHalf; | |
566 innerRadius -= SK_ScalarHalf; | |
567 | |
568 fGeoData.emplace_back(Geometry { | |
569 color, | |
570 innerRadius, | |
571 outerRadius, | |
572 SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, | |
573 center.fX + outerRadius, center.fY + outerRadius) | |
574 }); | |
575 this->setBounds(fGeoData.back().fDevBounds); | |
576 fStroked = isStrokeOnly && innerRadius > 0; | |
577 } | 569 } |
578 | |
579 const char* name() const override { return "CircleBatch"; } | 570 const char* name() const override { return "CircleBatch"; } |
580 | 571 |
581 SkString dumpInfo() const override { | 572 SkString dumpInfo() const override { |
582 SkString string; | 573 SkString string; |
583 for (int i = 0; i < fGeoData.count(); ++i) { | 574 for (int i = 0; i < fGeoData.count(); ++i) { |
584 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.
2f]," | 575 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.
2f]," |
585 "InnerRad: %.2f, OuterRad: %.2f\n", | 576 "InnerRad: %.2f, OuterRad: %.2f\n", |
586 fGeoData[i].fColor, | 577 fGeoData[i].fColor, |
587 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.
fTop, | 578 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.
fTop, |
588 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds
.fBottom, | 579 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds
.fBottom, |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
682 | 673 |
683 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsing
LocalCoords)) { | 674 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsing
LocalCoords)) { |
684 return false; | 675 return false; |
685 } | 676 } |
686 | 677 |
687 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); | 678 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); |
688 this->joinBounds(that->bounds()); | 679 this->joinBounds(that->bounds()); |
689 return true; | 680 return true; |
690 } | 681 } |
691 | 682 |
692 struct Geometry { | |
693 GrColor fColor; | |
694 SkScalar fInnerRadius; | |
695 SkScalar fOuterRadius; | |
696 SkRect fDevBounds; | |
697 }; | |
698 | |
699 bool fStroked; | 683 bool fStroked; |
700 SkMatrix fViewMatrixIfUsingLocalCoords; | 684 SkMatrix fViewMatrixIfUsingLocalCoords; |
701 SkSTArray<1, Geometry, true> fGeoData; | 685 SkSTArray<1, Geometry, true> fGeoData; |
702 | 686 |
703 typedef GrVertexBatch INHERITED; | 687 typedef GrVertexBatch INHERITED; |
704 }; | 688 }; |
705 | 689 |
| 690 static GrDrawBatch* create_circle_batch(GrColor color, |
| 691 const SkMatrix& viewMatrix, |
| 692 const SkRect& circle, |
| 693 const SkStrokeRec& stroke) { |
| 694 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY()); |
| 695 viewMatrix.mapPoints(¢er, 1); |
| 696 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width())); |
| 697 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth()); |
| 698 |
| 699 SkStrokeRec::Style style = stroke.getStyle(); |
| 700 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
| 701 SkStrokeRec::kHairline_Style == style; |
| 702 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
| 703 |
| 704 SkScalar innerRadius = 0.0f; |
| 705 SkScalar outerRadius = radius; |
| 706 SkScalar halfWidth = 0; |
| 707 if (hasStroke) { |
| 708 if (SkScalarNearlyZero(strokeWidth)) { |
| 709 halfWidth = SK_ScalarHalf; |
| 710 } else { |
| 711 halfWidth = SkScalarHalf(strokeWidth); |
| 712 } |
| 713 |
| 714 outerRadius += halfWidth; |
| 715 if (isStrokeOnly) { |
| 716 innerRadius = radius - halfWidth; |
| 717 } |
| 718 } |
| 719 |
| 720 // The radii are outset for two reasons. First, it allows the shader to simp
ly perform simpler |
| 721 // computation because the computed alpha is zero, rather than 50%, at the r
adius. |
| 722 // Second, the outer radius is used to compute the verts of the bounding box
that is rendered |
| 723 // and the outset ensures the box will cover all partially covered by the ci
rcle. |
| 724 outerRadius += SK_ScalarHalf; |
| 725 innerRadius -= SK_ScalarHalf; |
| 726 |
| 727 CircleBatch::Geometry geometry; |
| 728 geometry.fColor = color; |
| 729 geometry.fInnerRadius = innerRadius; |
| 730 geometry.fOuterRadius = outerRadius; |
| 731 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY -
outerRadius, |
| 732 center.fX + outerRadius, center.fY +
outerRadius); |
| 733 |
| 734 return new CircleBatch(geometry, viewMatrix, isStrokeOnly && innerRadius > 0
); |
| 735 } |
| 736 |
| 737 GrDrawBatch* GrOvalRenderer::CreateCircleBatch(GrColor color, |
| 738 const SkMatrix& viewMatrix, |
| 739 const SkRect& circle, |
| 740 const SkStrokeRec& stroke) { |
| 741 return create_circle_batch(color, viewMatrix, circle, stroke); |
| 742 } |
| 743 |
706 /////////////////////////////////////////////////////////////////////////////// | 744 /////////////////////////////////////////////////////////////////////////////// |
707 | 745 |
708 class EllipseBatch : public GrVertexBatch { | 746 class EllipseBatch : public GrVertexBatch { |
709 public: | 747 public: |
710 DEFINE_BATCH_CLASS_ID | 748 DEFINE_BATCH_CLASS_ID |
711 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const
SkRect& ellipse, | |
712 const SkStrokeRec& stroke) { | |
713 SkASSERT(viewMatrix.rectStaysRect()); | |
714 | 749 |
715 // do any matrix crunching before we reset the draw state for device coo
rds | 750 struct Geometry { |
716 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); | 751 SkRect fDevBounds; |
717 viewMatrix.mapPoints(¢er, 1); | 752 SkScalar fXRadius; |
718 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); | 753 SkScalar fYRadius; |
719 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); | 754 SkScalar fInnerXRadius; |
720 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRa
dius + | 755 SkScalar fInnerYRadius; |
721 viewMatrix[SkMatrix::kMSkewY]*ellipseYRad
ius); | 756 GrColor fColor; |
722 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRad
ius + | 757 }; |
723 viewMatrix[SkMatrix::kMScaleY]*ellipseYRa
dius); | |
724 | 758 |
725 // do (potentially) anisotropic mapping of stroke | 759 EllipseBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stro
ked) |
726 SkVector scaledStroke; | 760 : INHERITED(ClassID()) |
727 SkScalar strokeWidth = stroke.getWidth(); | 761 , fStroked(stroked) |
728 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX
] + | 762 , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
729 viewMatrix[SkMatrix::kMSkewY]
)); | 763 fGeoData.push_back(geometry); |
730 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX]
+ | 764 this->setBounds(geometry.fDevBounds); |
731 viewMatrix[SkMatrix::kMScaleY
])); | |
732 | |
733 SkStrokeRec::Style style = stroke.getStyle(); | |
734 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || | |
735 SkStrokeRec::kHairline_Style == style; | |
736 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == st
yle; | |
737 | |
738 SkScalar innerXRadius = 0; | |
739 SkScalar innerYRadius = 0; | |
740 if (hasStroke) { | |
741 if (SkScalarNearlyZero(scaledStroke.length())) { | |
742 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); | |
743 } else { | |
744 scaledStroke.scale(SK_ScalarHalf); | |
745 } | |
746 | |
747 // we only handle thick strokes for near-circular ellipses | |
748 if (scaledStroke.length() > SK_ScalarHalf && | |
749 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRad
ius)) { | |
750 return nullptr; | |
751 } | |
752 | |
753 // we don't handle it if curvature of the stroke is less than curvat
ure of the ellipse | |
754 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStrok
e.fY)*xRadius || | |
755 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStrok
e.fX)*yRadius) { | |
756 return nullptr; | |
757 } | |
758 | |
759 // this is legit only if scale & translation (which should be the ca
se at the moment) | |
760 if (isStrokeOnly) { | |
761 innerXRadius = xRadius - scaledStroke.fX; | |
762 innerYRadius = yRadius - scaledStroke.fY; | |
763 } | |
764 | |
765 xRadius += scaledStroke.fX; | |
766 yRadius += scaledStroke.fY; | |
767 } | |
768 | |
769 // We've extended the outer x radius out half a pixel to antialias. | |
770 // This will also expand the rect so all the pixels will be captured. | |
771 // TODO: Consider if we should use sqrt(2)/2 instead | |
772 xRadius += SK_ScalarHalf; | |
773 yRadius += SK_ScalarHalf; | |
774 | |
775 EllipseBatch* batch = new EllipseBatch(); | |
776 batch->fGeoData.emplace_back(Geometry { | |
777 color, | |
778 xRadius, | |
779 yRadius, | |
780 innerXRadius, | |
781 innerYRadius, | |
782 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius, | |
783 center.fX + xRadius, center.fY + yRadius) | |
784 }); | |
785 batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0; | |
786 batch->setBounds(batch->fGeoData.back().fDevBounds); | |
787 return batch; | |
788 } | 765 } |
789 | 766 |
790 const char* name() const override { return "EllipseBatch"; } | 767 const char* name() const override { return "EllipseBatch"; } |
791 | 768 |
792 void computePipelineOptimizations(GrInitInvariantOutput* color, | 769 void computePipelineOptimizations(GrInitInvariantOutput* color, |
793 GrInitInvariantOutput* coverage, | 770 GrInitInvariantOutput* coverage, |
794 GrBatchToXPOverrides* overrides) const ove
rride { | 771 GrBatchToXPOverrides* overrides) const ove
rride { |
795 // When this is called on a batch, there is only one geometry bundle | 772 // When this is called on a batch, there is only one geometry bundle |
796 color->setKnownFourComponents(fGeoData[0].fColor); | 773 color->setKnownFourComponents(fGeoData[0].fColor); |
797 coverage->setUnknownSingleComponent(); | 774 coverage->setUnknownSingleComponent(); |
798 } | 775 } |
799 | 776 |
800 private: | 777 private: |
801 EllipseBatch() : INHERITED(ClassID()) {} | |
802 | |
803 void initBatchTracker(const GrXPOverridesForBatch& overrides) override { | 778 void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
804 // Handle any overrides that affect our GP. | 779 // Handle any overrides that affect our GP. |
805 if (!overrides.readsCoverage()) { | 780 if (!overrides.readsCoverage()) { |
806 fGeoData[0].fColor = GrColor_ILLEGAL; | 781 fGeoData[0].fColor = GrColor_ILLEGAL; |
807 } | 782 } |
808 if (!overrides.readsLocalCoords()) { | 783 if (!overrides.readsLocalCoords()) { |
809 fViewMatrixIfUsingLocalCoords.reset(); | 784 fViewMatrixIfUsingLocalCoords.reset(); |
810 } | 785 } |
811 } | 786 } |
812 | 787 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
888 | 863 |
889 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsing
LocalCoords)) { | 864 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsing
LocalCoords)) { |
890 return false; | 865 return false; |
891 } | 866 } |
892 | 867 |
893 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); | 868 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); |
894 this->joinBounds(that->bounds()); | 869 this->joinBounds(that->bounds()); |
895 return true; | 870 return true; |
896 } | 871 } |
897 | 872 |
898 struct Geometry { | |
899 GrColor fColor; | |
900 SkScalar fXRadius; | |
901 SkScalar fYRadius; | |
902 SkScalar fInnerXRadius; | |
903 SkScalar fInnerYRadius; | |
904 SkRect fDevBounds; | |
905 }; | |
906 | 873 |
907 bool fStroked; | 874 bool fStroked; |
908 SkMatrix fViewMatrixIfUsingLocalCoords; | 875 SkMatrix fViewMatrixIfUsingLocalCoords; |
909 SkSTArray<1, Geometry, true> fGeoData; | 876 SkSTArray<1, Geometry, true> fGeoData; |
910 | 877 |
911 typedef GrVertexBatch INHERITED; | 878 typedef GrVertexBatch INHERITED; |
912 }; | 879 }; |
913 | 880 |
| 881 static GrDrawBatch* create_ellipse_batch(GrColor color, |
| 882 const SkMatrix& viewMatrix, |
| 883 const SkRect& ellipse, |
| 884 const SkStrokeRec& stroke) { |
| 885 SkASSERT(viewMatrix.rectStaysRect()); |
| 886 |
| 887 // do any matrix crunching before we reset the draw state for device coords |
| 888 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); |
| 889 viewMatrix.mapPoints(¢er, 1); |
| 890 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); |
| 891 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); |
| 892 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius
+ |
| 893 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius)
; |
| 894 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius
+ |
| 895 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius
); |
| 896 |
| 897 // do (potentially) anisotropic mapping of stroke |
| 898 SkVector scaledStroke; |
| 899 SkScalar strokeWidth = stroke.getWidth(); |
| 900 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] + |
| 901 viewMatrix[SkMatrix::kMSkewY])); |
| 902 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] + |
| 903 viewMatrix[SkMatrix::kMScaleY])); |
| 904 |
| 905 SkStrokeRec::Style style = stroke.getStyle(); |
| 906 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
| 907 SkStrokeRec::kHairline_Style == style; |
| 908 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
| 909 |
| 910 SkScalar innerXRadius = 0; |
| 911 SkScalar innerYRadius = 0; |
| 912 if (hasStroke) { |
| 913 if (SkScalarNearlyZero(scaledStroke.length())) { |
| 914 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
| 915 } else { |
| 916 scaledStroke.scale(SK_ScalarHalf); |
| 917 } |
| 918 |
| 919 // we only handle thick strokes for near-circular ellipses |
| 920 if (scaledStroke.length() > SK_ScalarHalf && |
| 921 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)
) { |
| 922 return nullptr; |
| 923 } |
| 924 |
| 925 // we don't handle it if curvature of the stroke is less than curvature
of the ellipse |
| 926 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY
)*xRadius || |
| 927 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX
)*yRadius) { |
| 928 return nullptr; |
| 929 } |
| 930 |
| 931 // this is legit only if scale & translation (which should be the case a
t the moment) |
| 932 if (isStrokeOnly) { |
| 933 innerXRadius = xRadius - scaledStroke.fX; |
| 934 innerYRadius = yRadius - scaledStroke.fY; |
| 935 } |
| 936 |
| 937 xRadius += scaledStroke.fX; |
| 938 yRadius += scaledStroke.fY; |
| 939 } |
| 940 |
| 941 // We've extended the outer x radius out half a pixel to antialias. |
| 942 // This will also expand the rect so all the pixels will be captured. |
| 943 // TODO: Consider if we should use sqrt(2)/2 instead |
| 944 xRadius += SK_ScalarHalf; |
| 945 yRadius += SK_ScalarHalf; |
| 946 |
| 947 EllipseBatch::Geometry geometry; |
| 948 geometry.fColor = color; |
| 949 geometry.fXRadius = xRadius; |
| 950 geometry.fYRadius = yRadius; |
| 951 geometry.fInnerXRadius = innerXRadius; |
| 952 geometry.fInnerYRadius = innerYRadius; |
| 953 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRad
ius, |
| 954 center.fX + xRadius, center.fY + yRad
ius); |
| 955 |
| 956 return new EllipseBatch(geometry, viewMatrix, |
| 957 isStrokeOnly && innerXRadius > 0 && innerYRadius > 0
); |
| 958 } |
| 959 |
| 960 GrDrawBatch* GrOvalRenderer::CreateEllipseBatch(GrColor color, |
| 961 const SkMatrix& viewMatrix, |
| 962 const SkRect& ellipse, |
| 963 const SkStrokeRec& stroke) { |
| 964 return create_ellipse_batch(color, viewMatrix, ellipse, stroke); |
| 965 } |
| 966 |
914 ////////////////////////////////////////////////////////////////////////////////
///////////////// | 967 ////////////////////////////////////////////////////////////////////////////////
///////////////// |
915 | 968 |
916 class DIEllipseBatch : public GrVertexBatch { | 969 class DIEllipseBatch : public GrVertexBatch { |
917 public: | 970 public: |
918 DEFINE_BATCH_CLASS_ID | 971 DEFINE_BATCH_CLASS_ID |
919 | 972 |
920 static GrDrawBatch* Create(GrColor color, | 973 struct Geometry { |
921 const SkMatrix& viewMatrix, | 974 SkMatrix fViewMatrix; |
922 const SkRect& ellipse, | 975 SkRect fBounds; |
923 const SkStrokeRec& stroke) { | 976 SkScalar fXRadius; |
924 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); | 977 SkScalar fYRadius; |
925 SkScalar xRadius = SkScalarHalf(ellipse.width()); | 978 SkScalar fInnerXRadius; |
926 SkScalar yRadius = SkScalarHalf(ellipse.height()); | 979 SkScalar fInnerYRadius; |
| 980 SkScalar fGeoDx; |
| 981 SkScalar fGeoDy; |
| 982 GrColor fColor; |
| 983 DIEllipseStyle fStyle; |
| 984 }; |
927 | 985 |
928 SkStrokeRec::Style style = stroke.getStyle(); | 986 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) { |
929 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ? | 987 return new DIEllipseBatch(geometry, bounds); |
930 DIEllipseStyle::kStroke : | |
931 (SkStrokeRec::kHairline_Style == style) ? | |
932 DIEllipseStyle::kHairline : DIEllipseStyle::kF
ill; | |
933 | |
934 SkScalar innerXRadius = 0; | |
935 SkScalar innerYRadius = 0; | |
936 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style !=
style) { | |
937 SkScalar strokeWidth = stroke.getWidth(); | |
938 | |
939 if (SkScalarNearlyZero(strokeWidth)) { | |
940 strokeWidth = SK_ScalarHalf; | |
941 } else { | |
942 strokeWidth *= SK_ScalarHalf; | |
943 } | |
944 | |
945 // we only handle thick strokes for near-circular ellipses | |
946 if (strokeWidth > SK_ScalarHalf && | |
947 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRad
ius)) { | |
948 return nullptr; | |
949 } | |
950 | |
951 // we don't handle it if curvature of the stroke is less than curvat
ure of the ellipse | |
952 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadiu
s || | |
953 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadiu
s) { | |
954 return nullptr; | |
955 } | |
956 | |
957 // set inner radius (if needed) | |
958 if (SkStrokeRec::kStroke_Style == style) { | |
959 innerXRadius = xRadius - strokeWidth; | |
960 innerYRadius = yRadius - strokeWidth; | |
961 } | |
962 | |
963 xRadius += strokeWidth; | |
964 yRadius += strokeWidth; | |
965 } | |
966 if (DIEllipseStyle::kStroke == dieStyle) { | |
967 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle :
:kStroke : | |
968 DIEllipseStyle ::kFill; | |
969 } | |
970 | |
971 // This expands the outer rect so that after CTM we end up with a half-p
ixel border | |
972 SkScalar a = viewMatrix[SkMatrix::kMScaleX]; | |
973 SkScalar b = viewMatrix[SkMatrix::kMSkewX]; | |
974 SkScalar c = viewMatrix[SkMatrix::kMSkewY]; | |
975 SkScalar d = viewMatrix[SkMatrix::kMScaleY]; | |
976 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c); | |
977 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d); | |
978 | |
979 DIEllipseBatch* batch = new DIEllipseBatch(); | |
980 batch->fGeoData.emplace_back(Geometry { | |
981 viewMatrix, | |
982 color, | |
983 xRadius, | |
984 yRadius, | |
985 innerXRadius, | |
986 innerYRadius, | |
987 geoDx, | |
988 geoDy, | |
989 dieStyle, | |
990 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius -
geoDy, | |
991 center.fX + xRadius + geoDx, center.fY + yRadius +
geoDy) | |
992 }); | |
993 SkRect devBounds = batch->fGeoData.back().fBounds; | |
994 viewMatrix.mapRect(&devBounds); | |
995 batch->setBounds(devBounds); | |
996 return batch; | |
997 } | 988 } |
998 | 989 |
999 const char* name() const override { return "DIEllipseBatch"; } | 990 const char* name() const override { return "DIEllipseBatch"; } |
1000 | 991 |
1001 void computePipelineOptimizations(GrInitInvariantOutput* color, | 992 void computePipelineOptimizations(GrInitInvariantOutput* color, |
1002 GrInitInvariantOutput* coverage, | 993 GrInitInvariantOutput* coverage, |
1003 GrBatchToXPOverrides* overrides) const ove
rride { | 994 GrBatchToXPOverrides* overrides) const ove
rride { |
1004 // When this is called on a batch, there is only one geometry bundle | 995 // When this is called on a batch, there is only one geometry bundle |
1005 color->setKnownFourComponents(fGeoData[0].fColor); | 996 color->setKnownFourComponents(fGeoData[0].fColor); |
1006 coverage->setUnknownSingleComponent(); | 997 coverage->setUnknownSingleComponent(); |
1007 } | 998 } |
1008 | 999 |
1009 private: | 1000 private: |
1010 | 1001 |
1011 DIEllipseBatch() : INHERITED(ClassID()) {} | |
1012 | |
1013 void initBatchTracker(const GrXPOverridesForBatch& overrides) override { | 1002 void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
1014 // Handle any overrides that affect our GP. | 1003 // Handle any overrides that affect our GP. |
1015 overrides.getOverrideColorIfSet(&fGeoData[0].fColor); | 1004 overrides.getOverrideColorIfSet(&fGeoData[0].fColor); |
1016 fUsesLocalCoords = overrides.readsLocalCoords(); | 1005 fUsesLocalCoords = overrides.readsLocalCoords(); |
1017 } | 1006 } |
1018 | 1007 |
1019 void onPrepareDraws(Target* target) const override { | 1008 void onPrepareDraws(Target* target) const override { |
1020 // Setup geometry processor | 1009 // Setup geometry processor |
1021 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this
->viewMatrix(), | 1010 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this
->viewMatrix(), |
1022 this
->style())); | 1011 this
->style())); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1065 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); | 1054 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); |
1066 verts[3].fColor = color; | 1055 verts[3].fColor = color; |
1067 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offse
tDy); | 1056 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offse
tDy); |
1068 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -inner
RatioY - offsetDy); | 1057 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -inner
RatioY - offsetDy); |
1069 | 1058 |
1070 verts += kVerticesPerQuad; | 1059 verts += kVerticesPerQuad; |
1071 } | 1060 } |
1072 helper.recordDraw(target, gp); | 1061 helper.recordDraw(target, gp); |
1073 } | 1062 } |
1074 | 1063 |
| 1064 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(C
lassID()) { |
| 1065 fGeoData.push_back(geometry); |
| 1066 |
| 1067 this->setBounds(bounds); |
| 1068 } |
| 1069 |
1075 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override { | 1070 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override { |
1076 DIEllipseBatch* that = t->cast<DIEllipseBatch>(); | 1071 DIEllipseBatch* that = t->cast<DIEllipseBatch>(); |
1077 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pi
peline(), | 1072 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pi
peline(), |
1078 that->bounds(), caps)) { | 1073 that->bounds(), caps)) { |
1079 return false; | 1074 return false; |
1080 } | 1075 } |
1081 | 1076 |
1082 if (this->style() != that->style()) { | 1077 if (this->style() != that->style()) { |
1083 return false; | 1078 return false; |
1084 } | 1079 } |
1085 | 1080 |
1086 // TODO rewrite to allow positioning on CPU | 1081 // TODO rewrite to allow positioning on CPU |
1087 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { | 1082 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { |
1088 return false; | 1083 return false; |
1089 } | 1084 } |
1090 | 1085 |
1091 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); | 1086 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); |
1092 this->joinBounds(that->bounds()); | 1087 this->joinBounds(that->bounds()); |
1093 return true; | 1088 return true; |
1094 } | 1089 } |
1095 | 1090 |
1096 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } | 1091 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } |
1097 DIEllipseStyle style() const { return fGeoData[0].fStyle; } | 1092 DIEllipseStyle style() const { return fGeoData[0].fStyle; } |
1098 | 1093 |
1099 struct Geometry { | |
1100 SkMatrix fViewMatrix; | |
1101 GrColor fColor; | |
1102 SkScalar fXRadius; | |
1103 SkScalar fYRadius; | |
1104 SkScalar fInnerXRadius; | |
1105 SkScalar fInnerYRadius; | |
1106 SkScalar fGeoDx; | |
1107 SkScalar fGeoDy; | |
1108 DIEllipseStyle fStyle; | |
1109 SkRect fBounds; | |
1110 }; | |
1111 | |
1112 bool fUsesLocalCoords; | 1094 bool fUsesLocalCoords; |
1113 SkSTArray<1, Geometry, true> fGeoData; | 1095 SkSTArray<1, Geometry, true> fGeoData; |
1114 | 1096 |
1115 typedef GrVertexBatch INHERITED; | 1097 typedef GrVertexBatch INHERITED; |
1116 }; | 1098 }; |
1117 | 1099 |
| 1100 static GrDrawBatch* create_diellipse_batch(GrColor color, |
| 1101 const SkMatrix& viewMatrix, |
| 1102 const SkRect& ellipse, |
| 1103 const SkStrokeRec& stroke) { |
| 1104 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); |
| 1105 SkScalar xRadius = SkScalarHalf(ellipse.width()); |
| 1106 SkScalar yRadius = SkScalarHalf(ellipse.height()); |
| 1107 |
| 1108 SkStrokeRec::Style style = stroke.getStyle(); |
| 1109 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ? |
| 1110 DIEllipseStyle::kStroke : |
| 1111 (SkStrokeRec::kHairline_Style == style) ? |
| 1112 DIEllipseStyle::kHairline : DIEllipseSty
le::kFill; |
| 1113 |
| 1114 SkScalar innerXRadius = 0; |
| 1115 SkScalar innerYRadius = 0; |
| 1116 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != sty
le) { |
| 1117 SkScalar strokeWidth = stroke.getWidth(); |
| 1118 |
| 1119 if (SkScalarNearlyZero(strokeWidth)) { |
| 1120 strokeWidth = SK_ScalarHalf; |
| 1121 } else { |
| 1122 strokeWidth *= SK_ScalarHalf; |
| 1123 } |
| 1124 |
| 1125 // we only handle thick strokes for near-circular ellipses |
| 1126 if (strokeWidth > SK_ScalarHalf && |
| 1127 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)
) { |
| 1128 return nullptr; |
| 1129 } |
| 1130 |
| 1131 // we don't handle it if curvature of the stroke is less than curvature
of the ellipse |
| 1132 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius || |
| 1133 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) { |
| 1134 return nullptr; |
| 1135 } |
| 1136 |
| 1137 // set inner radius (if needed) |
| 1138 if (SkStrokeRec::kStroke_Style == style) { |
| 1139 innerXRadius = xRadius - strokeWidth; |
| 1140 innerYRadius = yRadius - strokeWidth; |
| 1141 } |
| 1142 |
| 1143 xRadius += strokeWidth; |
| 1144 yRadius += strokeWidth; |
| 1145 } |
| 1146 if (DIEllipseStyle::kStroke == dieStyle) { |
| 1147 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kSt
roke : |
| 1148 DIEllipseStyle ::kFi
ll; |
| 1149 } |
| 1150 |
| 1151 // This expands the outer rect so that after CTM we end up with a half-pixel
border |
| 1152 SkScalar a = viewMatrix[SkMatrix::kMScaleX]; |
| 1153 SkScalar b = viewMatrix[SkMatrix::kMSkewX]; |
| 1154 SkScalar c = viewMatrix[SkMatrix::kMSkewY]; |
| 1155 SkScalar d = viewMatrix[SkMatrix::kMScaleY]; |
| 1156 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c); |
| 1157 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d); |
| 1158 |
| 1159 DIEllipseBatch::Geometry geometry; |
| 1160 geometry.fViewMatrix = viewMatrix; |
| 1161 geometry.fColor = color; |
| 1162 geometry.fXRadius = xRadius; |
| 1163 geometry.fYRadius = yRadius; |
| 1164 geometry.fInnerXRadius = innerXRadius; |
| 1165 geometry.fInnerYRadius = innerYRadius; |
| 1166 geometry.fGeoDx = geoDx; |
| 1167 geometry.fGeoDy = geoDy; |
| 1168 geometry.fStyle = dieStyle; |
| 1169 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY -
yRadius - geoDy, |
| 1170 center.fX + xRadius + geoDx, center.fY +
yRadius + geoDy); |
| 1171 |
| 1172 SkRect devBounds = geometry.fBounds; |
| 1173 viewMatrix.mapRect(&devBounds); |
| 1174 return DIEllipseBatch::Create(geometry, devBounds); |
| 1175 } |
| 1176 |
| 1177 GrDrawBatch* GrOvalRenderer::CreateDIEllipseBatch(GrColor color, |
| 1178 const SkMatrix& viewMatrix, |
| 1179 const SkRect& ellipse, |
| 1180 const SkStrokeRec& stroke) { |
| 1181 return create_diellipse_batch(color, viewMatrix, ellipse, stroke); |
| 1182 } |
| 1183 |
1118 /////////////////////////////////////////////////////////////////////////////// | 1184 /////////////////////////////////////////////////////////////////////////////// |
1119 | 1185 |
1120 static const uint16_t gRRectIndices[] = { | 1186 static const uint16_t gRRectIndices[] = { |
1121 // corners | 1187 // corners |
1122 0, 1, 5, 0, 5, 4, | 1188 0, 1, 5, 0, 5, 4, |
1123 2, 3, 7, 2, 7, 6, | 1189 2, 3, 7, 2, 7, 6, |
1124 8, 9, 13, 8, 13, 12, | 1190 8, 9, 13, 8, 13, 12, |
1125 10, 11, 15, 10, 15, 14, | 1191 10, 11, 15, 10, 15, 14, |
1126 | 1192 |
1127 // edges | 1193 // edges |
(...skipping 29 matching lines...) Expand all Loading... |
1157 | 1223 |
1158 } | 1224 } |
1159 } | 1225 } |
1160 | 1226 |
1161 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 1227 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
1162 | 1228 |
1163 class RRectCircleRendererBatch : public GrVertexBatch { | 1229 class RRectCircleRendererBatch : public GrVertexBatch { |
1164 public: | 1230 public: |
1165 DEFINE_BATCH_CLASS_ID | 1231 DEFINE_BATCH_CLASS_ID |
1166 | 1232 |
1167 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then s
trokeOnly indicates | 1233 struct Geometry { |
1168 // whether the rrect is only stroked or stroked and filled. | 1234 SkRect fDevBounds; |
1169 RRectCircleRendererBatch(GrColor color, const SkMatrix& viewMatrix, const Sk
Rect& devRect, | 1235 SkScalar fInnerRadius; |
1170 float devRadius, float devStrokeWidth, bool strokeO
nly) | 1236 SkScalar fOuterRadius; |
1171 : INHERITED(ClassID()) | 1237 GrColor fColor; |
1172 , fViewMatrixIfUsingLocalCoords(viewMatrix) { | 1238 }; |
1173 SkRect bounds = devRect; | |
1174 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly)); | |
1175 SkScalar innerRadius = 0.0f; | |
1176 SkScalar outerRadius = devRadius; | |
1177 SkScalar halfWidth = 0; | |
1178 fStroked = false; | |
1179 if (devStrokeWidth > 0) { | |
1180 if (SkScalarNearlyZero(devStrokeWidth)) { | |
1181 halfWidth = SK_ScalarHalf; | |
1182 } else { | |
1183 halfWidth = SkScalarHalf(devStrokeWidth); | |
1184 } | |
1185 | 1239 |
1186 if (strokeOnly) { | 1240 RRectCircleRendererBatch(const Geometry& geometry, const SkMatrix& viewMatri
x, bool stroked) |
1187 innerRadius = devRadius - halfWidth; | 1241 : INHERITED(ClassID()) |
1188 fStroked = innerRadius >= 0; | 1242 , fStroked(stroked) |
1189 } | 1243 , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
1190 outerRadius += halfWidth; | 1244 fGeoData.push_back(geometry); |
1191 bounds.outset(halfWidth, halfWidth); | |
1192 } | |
1193 | 1245 |
1194 // The radii are outset for two reasons. First, it allows the shader to
simply perform | 1246 this->setBounds(geometry.fDevBounds); |
1195 // simpler computation because the computed alpha is zero, rather than 5
0%, at the radius. | |
1196 // Second, the outer radius is used to compute the verts of the bounding
box that is | |
1197 // rendered and the outset ensures the box will cover all partially cove
red by the rrect | |
1198 // corners. | |
1199 outerRadius += SK_ScalarHalf; | |
1200 innerRadius -= SK_ScalarHalf; | |
1201 | |
1202 // Expand the rect so all the pixels will be captured. | |
1203 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); | |
1204 | |
1205 fGeoData.emplace_back(Geometry { color, innerRadius, outerRadius, bounds
}); | |
1206 this->setBounds(bounds); | |
1207 } | 1247 } |
1208 | 1248 |
1209 const char* name() const override { return "RRectCircleBatch"; } | 1249 const char* name() const override { return "RRectCircleBatch"; } |
1210 | 1250 |
1211 void computePipelineOptimizations(GrInitInvariantOutput* color, | 1251 void computePipelineOptimizations(GrInitInvariantOutput* color, |
1212 GrInitInvariantOutput* coverage, | 1252 GrInitInvariantOutput* coverage, |
1213 GrBatchToXPOverrides* overrides) const ove
rride { | 1253 GrBatchToXPOverrides* overrides) const ove
rride { |
1214 // When this is called on a batch, there is only one geometry bundle | 1254 // When this is called on a batch, there is only one geometry bundle |
1215 color->setKnownFourComponents(fGeoData[0].fColor); | 1255 color->setKnownFourComponents(fGeoData[0].fColor); |
1216 coverage->setUnknownSingleComponent(); | 1256 coverage->setUnknownSingleComponent(); |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1318 | 1358 |
1319 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsing
LocalCoords)) { | 1359 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsing
LocalCoords)) { |
1320 return false; | 1360 return false; |
1321 } | 1361 } |
1322 | 1362 |
1323 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); | 1363 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); |
1324 this->joinBounds(that->bounds()); | 1364 this->joinBounds(that->bounds()); |
1325 return true; | 1365 return true; |
1326 } | 1366 } |
1327 | 1367 |
1328 struct Geometry { | |
1329 GrColor fColor; | |
1330 SkScalar fInnerRadius; | |
1331 SkScalar fOuterRadius; | |
1332 SkRect fDevBounds; | |
1333 }; | |
1334 | |
1335 bool fStroked; | 1368 bool fStroked; |
1336 SkMatrix fViewMatrixIfUsingLocalCoords; | 1369 SkMatrix fViewMatrixIfUsingLocalCoords; |
1337 SkSTArray<1, Geometry, true> fGeoData; | 1370 SkSTArray<1, Geometry, true> fGeoData; |
1338 | 1371 |
1339 typedef GrVertexBatch INHERITED; | 1372 typedef GrVertexBatch INHERITED; |
1340 }; | 1373 }; |
1341 | 1374 |
1342 class RRectEllipseRendererBatch : public GrVertexBatch { | 1375 class RRectEllipseRendererBatch : public GrVertexBatch { |
1343 public: | 1376 public: |
1344 DEFINE_BATCH_CLASS_ID | 1377 DEFINE_BATCH_CLASS_ID |
1345 | 1378 |
1346 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, s
trokeOnly indicates | 1379 struct Geometry { |
1347 // whether the rrect is only stroked or stroked and filled. | 1380 SkRect fDevBounds; |
1348 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const
SkRect& devRect, | 1381 SkScalar fXRadius; |
1349 float devXRadius, float devYRadius, SkVector devS
trokeWidths, | 1382 SkScalar fYRadius; |
1350 bool strokeOnly) { | 1383 SkScalar fInnerXRadius; |
1351 SkASSERT(devXRadius > 0.5); | 1384 SkScalar fInnerYRadius; |
1352 SkASSERT(devYRadius > 0.5); | 1385 GrColor fColor; |
1353 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0)); | 1386 }; |
1354 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0)); | |
1355 SkScalar innerXRadius = 0.0f; | |
1356 SkScalar innerYRadius = 0.0f; | |
1357 SkRect bounds = devRect; | |
1358 bool stroked = false; | |
1359 if (devStrokeWidths.fX > 0) { | |
1360 if (SkScalarNearlyZero(devStrokeWidths.length())) { | |
1361 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf); | |
1362 } else { | |
1363 devStrokeWidths.scale(SK_ScalarHalf); | |
1364 } | |
1365 | 1387 |
1366 // we only handle thick strokes for near-circular ellipses | 1388 RRectEllipseRendererBatch(const Geometry& geometry, const SkMatrix& viewMatr
ix, bool stroked) |
1367 if (devStrokeWidths.length() > SK_ScalarHalf && | 1389 : INHERITED(ClassID()) |
1368 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadi
us > devXRadius)) { | 1390 , fStroked(stroked) |
1369 return nullptr; | 1391 , fViewMatrixIfUsingLocalCoords(viewMatrix) { |
1370 } | 1392 fGeoData.push_back(geometry); |
1371 | 1393 this->setBounds(geometry.fDevBounds); |
1372 // we don't handle it if curvature of the stroke is less than curvat
ure of the ellipse | |
1373 if (devStrokeWidths.fX*(devYRadius*devYRadius) < | |
1374 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) { | |
1375 return nullptr; | |
1376 } | |
1377 if (devStrokeWidths.fY*(devXRadius*devXRadius) < | |
1378 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) { | |
1379 return nullptr; | |
1380 } | |
1381 | |
1382 // this is legit only if scale & translation (which should be the ca
se at the moment) | |
1383 if (strokeOnly) { | |
1384 innerXRadius = devXRadius - devStrokeWidths.fX; | |
1385 innerYRadius = devYRadius - devStrokeWidths.fY; | |
1386 stroked = (innerXRadius >= 0 && innerYRadius >= 0); | |
1387 } | |
1388 | |
1389 devXRadius += devStrokeWidths.fX; | |
1390 devYRadius += devStrokeWidths.fY; | |
1391 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY); | |
1392 } | |
1393 | |
1394 // Expand the rect so all the pixels will be captured. | |
1395 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); | |
1396 | |
1397 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch(); | |
1398 batch->fStroked = stroked; | |
1399 batch->fViewMatrixIfUsingLocalCoords = viewMatrix; | |
1400 batch->fGeoData.emplace_back( | |
1401 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius,
bounds}); | |
1402 batch->setBounds(bounds); | |
1403 return batch; | |
1404 } | 1394 } |
1405 | 1395 |
1406 const char* name() const override { return "RRectEllipseRendererBatch"; } | 1396 const char* name() const override { return "RRectEllipseRendererBatch"; } |
1407 | 1397 |
1408 void computePipelineOptimizations(GrInitInvariantOutput* color, | 1398 void computePipelineOptimizations(GrInitInvariantOutput* color, |
1409 GrInitInvariantOutput* coverage, | 1399 GrInitInvariantOutput* coverage, |
1410 GrBatchToXPOverrides* overrides) const ove
rride { | 1400 GrBatchToXPOverrides* overrides) const ove
rride { |
1411 // When this is called on a batch, there is only one geometry bundle | 1401 // When this is called on a batch, there is only one geometry bundle |
1412 color->setKnownFourComponents(fGeoData[0].fColor); | 1402 color->setKnownFourComponents(fGeoData[0].fColor); |
1413 coverage->setUnknownSingleComponent(); | 1403 coverage->setUnknownSingleComponent(); |
1414 } | 1404 } |
1415 | 1405 |
1416 private: | 1406 private: |
1417 RRectEllipseRendererBatch() : INHERITED(ClassID()) {} | |
1418 | |
1419 void initBatchTracker(const GrXPOverridesForBatch& overrides) override { | 1407 void initBatchTracker(const GrXPOverridesForBatch& overrides) override { |
1420 // Handle overrides that affect our GP. | 1408 // Handle overrides that affect our GP. |
1421 overrides.getOverrideColorIfSet(&fGeoData[0].fColor); | 1409 overrides.getOverrideColorIfSet(&fGeoData[0].fColor); |
1422 if (!overrides.readsLocalCoords()) { | 1410 if (!overrides.readsLocalCoords()) { |
1423 fViewMatrixIfUsingLocalCoords.reset(); | 1411 fViewMatrixIfUsingLocalCoords.reset(); |
1424 } | 1412 } |
1425 } | 1413 } |
1426 | 1414 |
1427 void onPrepareDraws(Target* target) const override { | 1415 void onPrepareDraws(Target* target) const override { |
1428 SkMatrix localMatrix; | 1416 SkMatrix localMatrix; |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1528 | 1516 |
1529 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsing
LocalCoords)) { | 1517 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsing
LocalCoords)) { |
1530 return false; | 1518 return false; |
1531 } | 1519 } |
1532 | 1520 |
1533 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); | 1521 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); |
1534 this->joinBounds(that->bounds()); | 1522 this->joinBounds(that->bounds()); |
1535 return true; | 1523 return true; |
1536 } | 1524 } |
1537 | 1525 |
1538 struct Geometry { | |
1539 GrColor fColor; | |
1540 SkScalar fXRadius; | |
1541 SkScalar fYRadius; | |
1542 SkScalar fInnerXRadius; | |
1543 SkScalar fInnerYRadius; | |
1544 SkRect fDevBounds; | |
1545 }; | |
1546 | |
1547 bool fStroked; | 1526 bool fStroked; |
1548 SkMatrix fViewMatrixIfUsingLocalCoords; | 1527 SkMatrix fViewMatrixIfUsingLocalCoords; |
1549 SkSTArray<1, Geometry, true> fGeoData; | 1528 SkSTArray<1, Geometry, true> fGeoData; |
1550 | 1529 |
1551 typedef GrVertexBatch INHERITED; | 1530 typedef GrVertexBatch INHERITED; |
1552 }; | 1531 }; |
1553 | 1532 |
1554 static GrDrawBatch* create_rrect_batch(GrColor color, | 1533 static GrDrawBatch* create_rrect_batch(GrColor color, |
1555 const SkMatrix& viewMatrix, | 1534 const SkMatrix& viewMatrix, |
1556 const SkRRect& rrect, | 1535 const SkRRect& rrect, |
1557 const SkStrokeRec& stroke) { | 1536 const SkStrokeRec& stroke) { |
1558 SkASSERT(viewMatrix.rectStaysRect()); | 1537 SkASSERT(viewMatrix.rectStaysRect()); |
1559 SkASSERT(rrect.isSimple()); | 1538 SkASSERT(rrect.isSimple()); |
1560 SkASSERT(!rrect.isOval()); | 1539 SkASSERT(!rrect.isOval()); |
1561 | 1540 |
1562 // RRect batchs only handle simple, but not too simple, rrects | 1541 // RRect batchs only handle simple, but not too simple, rrects |
1563 // do any matrix crunching before we reset the draw state for device coords | 1542 // do any matrix crunching before we reset the draw state for device coords |
1564 const SkRect& rrectBounds = rrect.getBounds(); | 1543 const SkRect& rrectBounds = rrect.getBounds(); |
1565 SkRect bounds; | 1544 SkRect bounds; |
1566 viewMatrix.mapRect(&bounds, rrectBounds); | 1545 viewMatrix.mapRect(&bounds, rrectBounds); |
1567 | 1546 |
1568 SkVector radii = rrect.getSimpleRadii(); | 1547 SkVector radii = rrect.getSimpleRadii(); |
1569 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX + | 1548 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX + |
1570 viewMatrix[SkMatrix::kMSkewY]*radii.fY); | 1549 viewMatrix[SkMatrix::kMSkewY]*radii.fY); |
1571 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX + | 1550 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX + |
1572 viewMatrix[SkMatrix::kMScaleY]*radii.fY); | 1551 viewMatrix[SkMatrix::kMScaleY]*radii.fY); |
1573 | 1552 |
1574 SkStrokeRec::Style style = stroke.getStyle(); | 1553 SkStrokeRec::Style style = stroke.getStyle(); |
1575 | 1554 |
1576 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-
only draws. | 1555 // do (potentially) anisotropic mapping of stroke |
1577 SkVector scaledStroke = {-1, -1}; | 1556 SkVector scaledStroke; |
1578 SkScalar strokeWidth = stroke.getWidth(); | 1557 SkScalar strokeWidth = stroke.getWidth(); |
1579 | 1558 |
1580 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || | 1559 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
1581 SkStrokeRec::kHairline_Style == style; | 1560 SkStrokeRec::kHairline_Style == style; |
1582 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; | 1561 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
1583 | 1562 |
1584 if (hasStroke) { | 1563 if (hasStroke) { |
1585 if (SkStrokeRec::kHairline_Style == style) { | 1564 if (SkStrokeRec::kHairline_Style == style) { |
1586 scaledStroke.set(1, 1); | 1565 scaledStroke.set(1, 1); |
1587 } else { | 1566 } else { |
(...skipping 13 matching lines...) Expand all Loading... |
1601 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner r
ect of the nine- | 1580 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner r
ect of the nine- |
1602 // patch will have fractional coverage. This only matters when the interior
is actually filled. | 1581 // patch will have fractional coverage. This only matters when the interior
is actually filled. |
1603 // We could consider falling back to rect rendering here, since a tiny radiu
s is | 1582 // We could consider falling back to rect rendering here, since a tiny radiu
s is |
1604 // indistinguishable from a square corner. | 1583 // indistinguishable from a square corner. |
1605 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { | 1584 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { |
1606 return nullptr; | 1585 return nullptr; |
1607 } | 1586 } |
1608 | 1587 |
1609 // if the corners are circles, use the circle renderer | 1588 // if the corners are circles, use the circle renderer |
1610 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius
) { | 1589 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius
) { |
1611 return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius,
scaledStroke.fX, | 1590 SkScalar innerRadius = 0.0f; |
1612 isStrokeOnly); | 1591 SkScalar outerRadius = xRadius; |
| 1592 SkScalar halfWidth = 0; |
| 1593 if (hasStroke) { |
| 1594 if (SkScalarNearlyZero(scaledStroke.fX)) { |
| 1595 halfWidth = SK_ScalarHalf; |
| 1596 } else { |
| 1597 halfWidth = SkScalarHalf(scaledStroke.fX); |
| 1598 } |
| 1599 |
| 1600 if (isStrokeOnly) { |
| 1601 innerRadius = xRadius - halfWidth; |
| 1602 } |
| 1603 outerRadius += halfWidth; |
| 1604 bounds.outset(halfWidth, halfWidth); |
| 1605 } |
| 1606 |
| 1607 isStrokeOnly = (isStrokeOnly && innerRadius >= 0); |
| 1608 |
| 1609 // The radii are outset for two reasons. First, it allows the shader to
simply perform |
| 1610 // simpler computation because the computed alpha is zero, rather than 5
0%, at the radius. |
| 1611 // Second, the outer radius is used to compute the verts of the bounding
box that is |
| 1612 // rendered and the outset ensures the box will cover all partially cove
red by the rrect |
| 1613 // corners. |
| 1614 outerRadius += SK_ScalarHalf; |
| 1615 innerRadius -= SK_ScalarHalf; |
| 1616 |
| 1617 // Expand the rect so all the pixels will be captured. |
| 1618 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
| 1619 |
| 1620 RRectCircleRendererBatch::Geometry geometry; |
| 1621 geometry.fColor = color; |
| 1622 geometry.fInnerRadius = innerRadius; |
| 1623 geometry.fOuterRadius = outerRadius; |
| 1624 geometry.fDevBounds = bounds; |
| 1625 |
| 1626 return new RRectCircleRendererBatch(geometry, viewMatrix, isStrokeOnly); |
1613 // otherwise we use the ellipse renderer | 1627 // otherwise we use the ellipse renderer |
1614 } else { | 1628 } else { |
1615 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRad
ius, yRadius, | 1629 SkScalar innerXRadius = 0.0f; |
1616 scaledStroke, isStrokeOnly); | 1630 SkScalar innerYRadius = 0.0f; |
| 1631 if (hasStroke) { |
| 1632 if (SkScalarNearlyZero(scaledStroke.length())) { |
| 1633 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
| 1634 } else { |
| 1635 scaledStroke.scale(SK_ScalarHalf); |
| 1636 } |
1617 | 1637 |
| 1638 // we only handle thick strokes for near-circular ellipses |
| 1639 if (scaledStroke.length() > SK_ScalarHalf && |
| 1640 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRad
ius)) { |
| 1641 return nullptr; |
| 1642 } |
| 1643 |
| 1644 // we don't handle it if curvature of the stroke is less than curvat
ure of the ellipse |
| 1645 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStrok
e.fY)*xRadius || |
| 1646 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStrok
e.fX)*yRadius) { |
| 1647 return nullptr; |
| 1648 } |
| 1649 |
| 1650 // this is legit only if scale & translation (which should be the ca
se at the moment) |
| 1651 if (isStrokeOnly) { |
| 1652 innerXRadius = xRadius - scaledStroke.fX; |
| 1653 innerYRadius = yRadius - scaledStroke.fY; |
| 1654 } |
| 1655 |
| 1656 xRadius += scaledStroke.fX; |
| 1657 yRadius += scaledStroke.fY; |
| 1658 bounds.outset(scaledStroke.fX, scaledStroke.fY); |
| 1659 } |
| 1660 |
| 1661 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0); |
| 1662 |
| 1663 // Expand the rect so all the pixels will be captured. |
| 1664 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); |
| 1665 |
| 1666 RRectEllipseRendererBatch::Geometry geometry; |
| 1667 geometry.fColor = color; |
| 1668 geometry.fXRadius = xRadius; |
| 1669 geometry.fYRadius = yRadius; |
| 1670 geometry.fInnerXRadius = innerXRadius; |
| 1671 geometry.fInnerYRadius = innerYRadius; |
| 1672 geometry.fDevBounds = bounds; |
| 1673 |
| 1674 return new RRectEllipseRendererBatch(geometry, viewMatrix, isStrokeOnly)
; |
1618 } | 1675 } |
1619 } | 1676 } |
1620 | 1677 |
1621 GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color, | 1678 GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color, |
1622 const SkMatrix& viewMatrix, | 1679 const SkMatrix& viewMatrix, |
1623 const SkRRect& rrect, | 1680 const SkRRect& rrect, |
1624 const SkStrokeRec& stroke, | 1681 const SkStrokeRec& stroke, |
1625 GrShaderCaps* shaderCaps) { | 1682 GrShaderCaps* shaderCaps) { |
1626 if (rrect.isOval()) { | 1683 if (rrect.isOval()) { |
1627 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, sha
derCaps); | 1684 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, sha
derCaps); |
1628 } | 1685 } |
1629 | 1686 |
1630 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) { | 1687 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) { |
1631 return nullptr; | 1688 return nullptr; |
1632 } | 1689 } |
1633 | 1690 |
1634 return create_rrect_batch(color, viewMatrix, rrect, stroke); | 1691 return create_rrect_batch(color, viewMatrix, rrect, stroke); |
1635 } | 1692 } |
1636 | 1693 |
1637 /////////////////////////////////////////////////////////////////////////////// | 1694 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
1638 | |
1639 GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color, | |
1640 const SkMatrix& viewMatrix, | |
1641 const SkRect& oval, | |
1642 const SkStrokeRec& stroke, | |
1643 GrShaderCaps* shaderCaps) { | |
1644 // we can draw circles | |
1645 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(
viewMatrix)) { | |
1646 return new CircleBatch(color, viewMatrix, oval, stroke); | |
1647 } | |
1648 | |
1649 // if we have shader derivative support, render as device-independent | |
1650 if (shaderCaps->shaderDerivativeSupport()) { | |
1651 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke); | |
1652 } | |
1653 | |
1654 // otherwise axis-aligned ellipses only | |
1655 if (viewMatrix.rectStaysRect()) { | |
1656 return EllipseBatch::Create(color, viewMatrix, oval, stroke); | |
1657 } | |
1658 | |
1659 return nullptr; | |
1660 } | |
1661 | |
1662 /////////////////////////////////////////////////////////////////////////////// | |
1663 | 1695 |
1664 #ifdef GR_TEST_UTILS | 1696 #ifdef GR_TEST_UTILS |
1665 | 1697 |
1666 DRAW_BATCH_TEST_DEFINE(CircleBatch) { | 1698 DRAW_BATCH_TEST_DEFINE(CircleBatch) { |
1667 SkMatrix viewMatrix = GrTest::TestMatrix(random); | 1699 SkMatrix viewMatrix = GrTest::TestMatrix(random); |
1668 GrColor color = GrRandomColor(random); | 1700 GrColor color = GrRandomColor(random); |
1669 SkRect circle = GrTest::TestSquare(random); | 1701 SkRect circle = GrTest::TestSquare(random); |
1670 return new CircleBatch(color, viewMatrix, circle, GrTest::TestStrokeRec(rand
om)); | 1702 return create_circle_batch(color, viewMatrix, circle, GrTest::TestStrokeRec(
random)); |
1671 } | 1703 } |
1672 | 1704 |
1673 DRAW_BATCH_TEST_DEFINE(EllipseBatch) { | 1705 DRAW_BATCH_TEST_DEFINE(EllipseBatch) { |
1674 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); | 1706 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); |
1675 GrColor color = GrRandomColor(random); | 1707 GrColor color = GrRandomColor(random); |
1676 SkRect ellipse = GrTest::TestSquare(random); | 1708 SkRect ellipse = GrTest::TestSquare(random); |
1677 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRe
c(random)); | 1709 return create_ellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRe
c(random)); |
1678 } | 1710 } |
1679 | 1711 |
1680 DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) { | 1712 DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) { |
1681 SkMatrix viewMatrix = GrTest::TestMatrix(random); | 1713 SkMatrix viewMatrix = GrTest::TestMatrix(random); |
1682 GrColor color = GrRandomColor(random); | 1714 GrColor color = GrRandomColor(random); |
1683 SkRect ellipse = GrTest::TestSquare(random); | 1715 SkRect ellipse = GrTest::TestSquare(random); |
1684 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStroke
Rec(random)); | 1716 return create_diellipse_batch(color, viewMatrix, ellipse, GrTest::TestStroke
Rec(random)); |
1685 } | 1717 } |
1686 | 1718 |
1687 DRAW_BATCH_TEST_DEFINE(RRectBatch) { | 1719 DRAW_BATCH_TEST_DEFINE(RRectBatch) { |
1688 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); | 1720 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); |
1689 GrColor color = GrRandomColor(random); | 1721 GrColor color = GrRandomColor(random); |
1690 const SkRRect& rrect = GrTest::TestRRectSimple(random); | 1722 const SkRRect& rrect = GrTest::TestRRectSimple(random); |
1691 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(ra
ndom)); | 1723 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(ra
ndom)); |
1692 } | 1724 } |
1693 | 1725 |
1694 #endif | 1726 #endif |
OLD | NEW |