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

Side by Side Diff: src/core/SkPath.cpp

Issue 1452203002: Parametric SkPath oval/rect/rrect starting point (Closed) Base URL: https://chromium.googlesource.com/skia.git@master
Patch Set: more comment tweaks Created 5 years, 1 month 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 | « include/core/SkPath.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 2006 The Android Open Source Project 2 * Copyright 2006 The Android Open Source Project
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 "SkBuffer.h" 8 #include "SkBuffer.h"
9 #include "SkErrorInternals.h" 9 #include "SkErrorInternals.h"
10 #include "SkGeometry.h" 10 #include "SkGeometry.h"
(...skipping 822 matching lines...) Expand 10 before | Expand all | Expand 10 after
833 if (fLastMoveToIndex >= 0) { 833 if (fLastMoveToIndex >= 0) {
834 fLastMoveToIndex = ~fLastMoveToIndex; 834 fLastMoveToIndex = ~fLastMoveToIndex;
835 } 835 }
836 #else 836 #else
837 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 837 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
838 #endif 838 #endif
839 } 839 }
840 840
841 /////////////////////////////////////////////////////////////////////////////// 841 ///////////////////////////////////////////////////////////////////////////////
842 842
843 namespace {
844
845 template <unsigned N>
846 class PointIterator {
847 public:
848 PointIterator(SkPath::Direction dir, unsigned startIndex)
849 : fCurrent(startIndex % N)
850 , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { }
851
852 const SkPoint& current() const {
853 SkASSERT(fCurrent < N);
854 return fPts[fCurrent];
855 }
856
857 const SkPoint& next() {
858 fCurrent = (fCurrent + fAdvance) % N;
859 return this->current();
860 }
861
862 protected:
863 SkPoint fPts[N];
864
865 private:
866 unsigned fCurrent;
867 unsigned fAdvance;
868 };
869
870 class RectPointIterator : public PointIterator<4> {
871 public:
872 RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startI ndex)
873 : PointIterator(dir, startIndex) {
874
875 fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
876 fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
877 fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
878 fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
879 }
880 };
881
882 class OvalPointIterator : public PointIterator<4> {
883 public:
884 OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startI ndex)
885 : PointIterator(dir, startIndex) {
886
887 const SkScalar cx = oval.centerX();
888 const SkScalar cy = oval.centerY();
889
890 fPts[0] = SkPoint::Make(cx, oval.fTop);
891 fPts[1] = SkPoint::Make(oval.fRight, cy);
892 fPts[2] = SkPoint::Make(cx, oval.fBottom);
893 fPts[3] = SkPoint::Make(oval.fLeft, cy);
894 }
895 };
896
897 class RRectPointIterator : public PointIterator<8> {
898 public:
899 RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned sta rtIndex)
900 : PointIterator(dir, startIndex) {
901
902 const SkRect& bounds = rrect.getBounds();
903 const SkScalar L = bounds.fLeft;
904 const SkScalar T = bounds.fTop;
905 const SkScalar R = bounds.fRight;
906 const SkScalar B = bounds.fBottom;
907
908 fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
909 fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
910 fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner). fY);
911 fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner). fY);
912 fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
913 fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
914 fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).f Y);
915 fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).f Y);
916 }
917 };
918
919 } // anonymous namespace
920
843 static void assert_known_direction(int dir) { 921 static void assert_known_direction(int dir) {
844 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir); 922 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
845 } 923 }
846 924
847 void SkPath::addRect(const SkRect& rect, Direction dir) { 925 void SkPath::addRect(const SkRect& rect, Direction dir) {
848 this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir); 926 this->addRect(rect, dir, 0);
849 } 927 }
850 928
851 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right, 929 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
852 SkScalar bottom, Direction dir) { 930 SkScalar bottom, Direction dir) {
931 this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0);
932 }
933
934 void SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) {
853 assert_known_direction(dir); 935 assert_known_direction(dir);
854 fFirstDirection = this->hasOnlyMoveTos() ? 936 fFirstDirection = this->hasOnlyMoveTos() ?
855 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_F irstDirection; 937 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
856 SkAutoDisableDirectionCheck addc(this); 938 SkAutoDisableDirectionCheck addc(this);
939 SkAutoPathBoundsUpdate apbu(this, rect);
857 940
858 SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom); 941 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
859 942
860 this->incReserve(5); 943 const int kVerbs = 5; // moveTo + 3x lineTo + close
944 this->incReserve(kVerbs);
861 945
862 this->moveTo(left, top); 946 RectPointIterator iter(rect, dir, startIndex);
863 if (dir == kCCW_Direction) { 947
864 this->lineTo(left, bottom); 948 this->moveTo(iter.current());
865 this->lineTo(right, bottom); 949 this->lineTo(iter.next());
866 this->lineTo(right, top); 950 this->lineTo(iter.next());
867 } else { 951 this->lineTo(iter.next());
868 this->lineTo(right, top);
869 this->lineTo(right, bottom);
870 this->lineTo(left, bottom);
871 }
872 this->close(); 952 this->close();
953
954 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
873 } 955 }
874 956
875 void SkPath::addPoly(const SkPoint pts[], int count, bool close) { 957 void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
876 SkDEBUGCODE(this->validate();) 958 SkDEBUGCODE(this->validate();)
877 if (count <= 0) { 959 if (count <= 0) {
878 return; 960 return;
879 } 961 }
880 962
881 fLastMoveToIndex = fPathRef->countPoints(); 963 fLastMoveToIndex = fPathRef->countPoints();
882 964
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
972 } 1054 }
973 1055
974 void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[], 1056 void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
975 Direction dir) { 1057 Direction dir) {
976 SkRRect rrect; 1058 SkRRect rrect;
977 rrect.setRectRadii(rect, (const SkVector*) radii); 1059 rrect.setRectRadii(rect, (const SkVector*) radii);
978 this->addRRect(rrect, dir); 1060 this->addRRect(rrect, dir);
979 } 1061 }
980 1062
981 void SkPath::addRRect(const SkRRect& rrect, Direction dir) { 1063 void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
982 assert_known_direction(dir); 1064 // legacy start indices: 6 (CW) and 7(CCW)
1065 this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
1066 }
983 1067
984 if (rrect.isEmpty()) { 1068 void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
985 return; 1069 assert_known_direction(dir);
986 }
987 1070
988 const SkRect& bounds = rrect.getBounds(); 1071 if (rrect.isEmpty()) {
1072 return;
1073 }
989 1074
990 if (rrect.isRect()) { 1075 const SkRect& bounds = rrect.getBounds();
991 this->addRect(bounds, dir);
992 } else if (rrect.isOval()) {
993 this->addOval(bounds, dir);
994 } else {
995 fFirstDirection = this->hasOnlyMoveTos() ?
996 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnkno wn_FirstDirection;
997 1076
998 SkAutoPathBoundsUpdate apbu(this, bounds); 1077 if (rrect.isRect()) {
999 SkAutoDisableDirectionCheck addc(this); 1078 // degenerate(rect) => radii points are collapsing
1079 this->addRect(bounds, dir, (startIndex + 1) / 2);
1080 } else if (rrect.isOval()) {
1081 // degenerate(oval) => line points are collapsing
1082 this->addOval(bounds, dir, startIndex / 2);
1083 } else {
1084 fFirstDirection = this->hasOnlyMoveTos() ?
1085 (SkPathPriv::FirstDirection)dir : SkPathPriv::kU nknown_FirstDirection;
1000 1086
1001 const SkScalar L = bounds.fLeft; 1087 SkAutoPathBoundsUpdate apbu(this, bounds);
1002 const SkScalar T = bounds.fTop; 1088 SkAutoDisableDirectionCheck addc(this);
1003 const SkScalar R = bounds.fRight;
1004 const SkScalar B = bounds.fBottom;
1005 const SkScalar W = SK_ScalarRoot2Over2;
1006 1089
1007 this->incReserve(13); 1090 // we start with a conic on odd indices when moving CW vs. even indi ces when moving CCW
1008 if (kCW_Direction == dir) { 1091 const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direct ion));
1009 this->moveTo(L, B - rrect.fRadii[SkRRect::kLowerLeft_Corner].fY); 1092 const SkScalar weight = SK_ScalarRoot2Over2;
1010 1093
1011 this->lineTo(L, T + rrect.fRadii[SkRRect::kUpperLeft_Corner].fY); 1094 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1012 this->conicTo(L, T, L + rrect.fRadii[SkRRect::kUpperLeft_Corner].fX, T, W); 1095 const int kVerbs = startsWithConic
1096 ? 9 // moveTo + 4x conicTo + 3x lineTo + close
1097 : 10; // moveTo + 4x lineTo + 4x conicTo + close
1098 this->incReserve(kVerbs);
1013 1099
1014 this->lineTo(R - rrect.fRadii[SkRRect::kUpperRight_Corner].fX, T); 1100 RRectPointIterator rrectIter(rrect, dir, startIndex);
1015 this->conicTo(R, T, R, T + rrect.fRadii[SkRRect::kUpperRight_Corner] .fY, W); 1101 // Corner iterator indices follow the collapsed radii model,
1102 // adjusted such that the start pt is "behind" the radii start pt.
1103 const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Directi on ? 0 : 1);
1104 RectPointIterator rectIter(bounds, dir, rectStartIndex);
1016 1105
1017 this->lineTo(R, B - rrect.fRadii[SkRRect::kLowerRight_Corner].fY); 1106 this->moveTo(rrectIter.current());
1018 this->conicTo(R, B, R - rrect.fRadii[SkRRect::kLowerRight_Corner].fX , B, W); 1107 if (startsWithConic) {
1108 for (unsigned i = 0; i < 3; ++i) {
1109 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1110 this->lineTo(rrectIter.next());
1111 }
1112 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1113 // final lineTo handled by close().
1114 } else {
1115 for (unsigned i = 0; i < 4; ++i) {
1116 this->lineTo(rrectIter.next());
1117 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1118 }
1119 }
1120 this->close();
1019 1121
1020 this->lineTo(L + rrect.fRadii[SkRRect::kLowerLeft_Corner].fX, B); 1122 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1021 this->conicTo(L, B, L, B - rrect.fRadii[SkRRect::kLowerLeft_Corner]. fY, W); 1123 }
1022 } else {
1023 this->moveTo(L, T + rrect.fRadii[SkRRect::kUpperLeft_Corner].fY);
1024 1124
1025 this->lineTo(L, B - rrect.fRadii[SkRRect::kLowerLeft_Corner].fY); 1125 SkDEBUGCODE(fPathRef->validate();)
1026 this->conicTo(L, B, L + rrect.fRadii[SkRRect::kLowerLeft_Corner].fX, B, W);
1027
1028 this->lineTo(R - rrect.fRadii[SkRRect::kLowerRight_Corner].fX, B);
1029 this->conicTo(R, B, R, B - rrect.fRadii[SkRRect::kLowerRight_Corner] .fY, W);
1030
1031 this->lineTo(R, T + rrect.fRadii[SkRRect::kUpperRight_Corner].fY);
1032 this->conicTo(R, T, R - rrect.fRadii[SkRRect::kUpperRight_Corner].fX , T, W);
1033
1034 this->lineTo(L + rrect.fRadii[SkRRect::kUpperLeft_Corner].fX, T);
1035 this->conicTo(L, T, L, T + rrect.fRadii[SkRRect::kUpperLeft_Corner]. fY, W);
1036 }
1037 this->close();
1038 }
1039 SkDEBUGCODE(fPathRef->validate();)
1040 } 1126 }
1041 1127
1042 bool SkPath::hasOnlyMoveTos() const { 1128 bool SkPath::hasOnlyMoveTos() const {
1043 int count = fPathRef->countVerbs(); 1129 int count = fPathRef->countVerbs();
1044 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMe mBegin(); 1130 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMe mBegin();
1045 for (int i = 0; i < count; ++i) { 1131 for (int i = 0; i < count; ++i) {
1046 if (*verbs == kLine_Verb || 1132 if (*verbs == kLine_Verb ||
1047 *verbs == kQuad_Verb || 1133 *verbs == kQuad_Verb ||
1048 *verbs == kConic_Verb || 1134 *verbs == kConic_Verb ||
1049 *verbs == kCubic_Verb) { 1135 *verbs == kCubic_Verb) {
(...skipping 15 matching lines...) Expand all
1065 SkScalarToDouble(rx), SkScalarToDouble(ry) ) ; 1151 SkScalarToDouble(rx), SkScalarToDouble(ry) ) ;
1066 return; 1152 return;
1067 } 1153 }
1068 1154
1069 SkRRect rrect; 1155 SkRRect rrect;
1070 rrect.setRectXY(rect, rx, ry); 1156 rrect.setRectXY(rect, rx, ry);
1071 this->addRRect(rrect, dir); 1157 this->addRRect(rrect, dir);
1072 } 1158 }
1073 1159
1074 void SkPath::addOval(const SkRect& oval, Direction dir) { 1160 void SkPath::addOval(const SkRect& oval, Direction dir) {
1161 // legacy start index: 1
1162 this->addOval(oval, dir, 1);
1163 }
1164
1165 void SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex ) {
1075 assert_known_direction(dir); 1166 assert_known_direction(dir);
1076 1167
1077 /* If addOval() is called after previous moveTo(), 1168 /* If addOval() is called after previous moveTo(),
1078 this path is still marked as an oval. This is used to 1169 this path is still marked as an oval. This is used to
1079 fit into WebKit's calling sequences. 1170 fit into WebKit's calling sequences.
1080 We can't simply check isEmpty() in this case, as additional 1171 We can't simply check isEmpty() in this case, as additional
1081 moveTo() would mark the path non empty. 1172 moveTo() would mark the path non empty.
1082 */ 1173 */
1083 bool isOval = hasOnlyMoveTos(); 1174 bool isOval = hasOnlyMoveTos();
1084 if (isOval) { 1175 if (isOval) {
1085 fFirstDirection = (SkPathPriv::FirstDirection)dir; 1176 fFirstDirection = (SkPathPriv::FirstDirection)dir;
1086 } else { 1177 } else {
1087 fFirstDirection = SkPathPriv::kUnknown_FirstDirection; 1178 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1088 } 1179 }
1089 1180
1090 SkAutoDisableDirectionCheck addc(this); 1181 SkAutoDisableDirectionCheck addc(this);
1091
1092 SkAutoPathBoundsUpdate apbu(this, oval); 1182 SkAutoPathBoundsUpdate apbu(this, oval);
1093 1183
1094 const SkScalar L = oval.fLeft; 1184 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1095 const SkScalar T = oval.fTop; 1185 const int kVerbs = 6; // moveTo + 4x conicTo + close
1096 const SkScalar R = oval.fRight; 1186 this->incReserve(kVerbs);
1097 const SkScalar B = oval.fBottom; 1187
1098 const SkScalar cx = oval.centerX(); 1188 OvalPointIterator ovalIter(oval, dir, startPointIndex);
1099 const SkScalar cy = oval.centerY(); 1189 // The corner iterator pts are tracking "behind" the oval/radii pts.
1190 RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Directio n ? 0 : 1));
1100 const SkScalar weight = SK_ScalarRoot2Over2; 1191 const SkScalar weight = SK_ScalarRoot2Over2;
1101 1192
1102 this->incReserve(9); // move + 4 conics 1193 this->moveTo(ovalIter.current());
1103 this->moveTo(R, cy); 1194 for (unsigned i = 0; i < 4; ++i) {
1104 if (dir == kCCW_Direction) { 1195 this->conicTo(rectIter.next(), ovalIter.next(), weight);
1105 this->conicTo(R, T, cx, T, weight);
1106 this->conicTo(L, T, L, cy, weight);
1107 this->conicTo(L, B, cx, B, weight);
1108 this->conicTo(R, B, R, cy, weight);
1109 } else {
1110 this->conicTo(R, B, cx, B, weight);
1111 this->conicTo(L, B, L, cy, weight);
1112 this->conicTo(L, T, cx, T, weight);
1113 this->conicTo(R, T, R, cy, weight);
1114 } 1196 }
1115 this->close(); 1197 this->close();
1116 1198
1199 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1200
1117 SkPathRef::Editor ed(&fPathRef); 1201 SkPathRef::Editor ed(&fPathRef);
1118 1202
1119 ed.setIsOval(isOval); 1203 ed.setIsOval(isOval);
1120 } 1204 }
1121 1205
1122 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { 1206 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
1123 if (r > 0) { 1207 if (r > 0) {
1124 SkRect rect; 1208 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
1125 rect.set(x - r, y - r, x + r, y + r);
1126 this->addOval(rect, dir);
1127 } 1209 }
1128 } 1210 }
1129 1211
1130 void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 1212 void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1131 bool forceMoveTo) { 1213 bool forceMoveTo) {
1132 if (oval.width() < 0 || oval.height() < 0) { 1214 if (oval.width() < 0 || oval.height() < 0) {
1133 return; 1215 return;
1134 } 1216 }
1135 1217
1136 if (fPathRef->countVerbs() == 0) { 1218 if (fPathRef->countVerbs() == 0) {
(...skipping 1676 matching lines...) Expand 10 before | Expand all | Expand 10 after
2813 switch (this->getFillType()) { 2895 switch (this->getFillType()) {
2814 case SkPath::kEvenOdd_FillType: 2896 case SkPath::kEvenOdd_FillType:
2815 case SkPath::kInverseEvenOdd_FillType: 2897 case SkPath::kInverseEvenOdd_FillType:
2816 w &= 1; 2898 w &= 1;
2817 break; 2899 break;
2818 default: 2900 default:
2819 break; 2901 break;
2820 } 2902 }
2821 return SkToBool(w); 2903 return SkToBool(w);
2822 } 2904 }
OLDNEW
« no previous file with comments | « include/core/SkPath.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698