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