Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Side by Side Diff: src/gpu/GrOvalRenderer.cpp

Issue 2104423003: Better encapsulate oval/rrect batchs. (Closed) Base URL: https://chromium.googlesource.com/skia.git@master
Patch Set: cleanup Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/gpu/GrOvalRenderer.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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(&center, 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
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(&center, 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(&center, 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
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(&center, 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
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
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
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
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
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
OLDNEW
« no previous file with comments | « src/gpu/GrOvalRenderer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698