OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2006 The Android Open Source Project | 3 * Copyright 2006 The Android Open Source Project |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 | 9 |
10 #include "SkBuffer.h" | 10 #include "SkBuffer.h" |
(...skipping 881 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
892 } | 892 } |
893 if (close) { | 893 if (close) { |
894 vb[~count] = kClose_Verb; | 894 vb[~count] = kClose_Verb; |
895 } | 895 } |
896 | 896 |
897 GEN_ID_INC; | 897 GEN_ID_INC; |
898 DIRTY_AFTER_EDIT; | 898 DIRTY_AFTER_EDIT; |
899 SkDEBUGCODE(this->validate();) | 899 SkDEBUGCODE(this->validate();) |
900 } | 900 } |
901 | 901 |
| 902 #include "SkGeometry.h" |
| 903 |
| 904 static int build_arc_points(const SkRect& oval, SkScalar startAngle, |
| 905 SkScalar sweepAngle, |
| 906 SkPoint pts[kSkBuildQuadArcStorage]) { |
| 907 |
| 908 if (0 == sweepAngle && |
| 909 (0 == startAngle || SkIntToScalar(360) == startAngle)) { |
| 910 // Chrome uses this path to move into and out of ovals. If not |
| 911 // treated as a special case the moves can distort the oval's |
| 912 // bounding box (and break the circle special case). |
| 913 pts[0].set(oval.fRight, oval.centerY()); |
| 914 return 1; |
| 915 } else if (0 == oval.width() && 0 == oval.height()) { |
| 916 // Chrome will sometimes create 0 radius round rects. Having degenerate |
| 917 // quad segments in the path prevents the path from being recognized as |
| 918 // a rect. |
| 919 // TODO: optimizing the case where only one of width or height is zero |
| 920 // should also be considered. This case, however, doesn't seem to be |
| 921 // as common as the single point case. |
| 922 pts[0].set(oval.fRight, oval.fTop); |
| 923 return 1; |
| 924 } |
| 925 |
| 926 SkVector start, stop; |
| 927 |
| 928 start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX); |
| 929 stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), |
| 930 &stop.fX); |
| 931 |
| 932 /* If the sweep angle is nearly (but less than) 360, then due to precision |
| 933 loss in radians-conversion and/or sin/cos, we may end up with coincident |
| 934 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead |
| 935 of drawing a nearly complete circle (good). |
| 936 e.g. canvas.drawArc(0, 359.99, ...) |
| 937 -vs- canvas.drawArc(0, 359.9, ...) |
| 938 We try to detect this edge case, and tweak the stop vector |
| 939 */ |
| 940 if (start == stop) { |
| 941 SkScalar sw = SkScalarAbs(sweepAngle); |
| 942 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) { |
| 943 SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle); |
| 944 // make a guess at a tiny angle (in radians) to tweak by |
| 945 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle); |
| 946 // not sure how much will be enough, so we use a loop |
| 947 do { |
| 948 stopRad -= deltaRad; |
| 949 stop.fY = SkScalarSinCos(stopRad, &stop.fX); |
| 950 } while (start == stop); |
| 951 } |
| 952 } |
| 953 |
| 954 SkMatrix matrix; |
| 955 |
| 956 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); |
| 957 matrix.postTranslate(oval.centerX(), oval.centerY()); |
| 958 |
| 959 return SkBuildQuadArc(start, stop, |
| 960 sweepAngle > 0 ? kCW_SkRotationDirection : |
| 961 kCCW_SkRotationDirection, |
| 962 &matrix, pts); |
| 963 } |
| 964 |
902 static void add_corner_arc(SkPath* path, const SkRect& rect, | 965 static void add_corner_arc(SkPath* path, const SkRect& rect, |
903 SkScalar rx, SkScalar ry, int startAngle, | 966 SkScalar rx, SkScalar ry, int startAngle, |
904 SkPath::Direction dir, bool forceMoveTo) { | 967 SkPath::Direction dir, bool addArcTo, |
| 968 bool forceMoveTo = false) { |
905 // These two asserts are not sufficient, since really we want to know | 969 // These two asserts are not sufficient, since really we want to know |
906 // that the pair of radii (e.g. left and right, or top and bottom) sum | 970 // that the pair of radii (e.g. left and right, or top and bottom) sum |
907 // to <= dimension, but we don't have that data here, so we just have | 971 // to <= dimension, but we don't have that data here, so we just have |
908 // these conservative asserts. | 972 // these conservative asserts. |
909 SkASSERT(0 <= rx && rx <= rect.width()); | 973 SkASSERT(0 <= rx && rx <= rect.width()); |
910 SkASSERT(0 <= ry && ry <= rect.height()); | 974 SkASSERT(0 <= ry && ry <= rect.height()); |
911 | 975 |
912 SkRect r; | 976 SkRect r; |
913 r.set(-rx, -ry, rx, ry); | 977 r.set(-rx, -ry, rx, ry); |
914 | 978 |
915 switch (startAngle) { | 979 switch (startAngle) { |
916 case 0: | 980 case 0: |
917 r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom); | 981 r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom); |
918 break; | 982 break; |
919 case 90: | 983 case 90: |
920 r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom); | 984 r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom); |
921 break; | 985 break; |
922 case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break; | 986 case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break; |
923 case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break; | 987 case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break; |
924 default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc"); | 988 default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc"); |
925 } | 989 } |
926 | 990 |
927 SkScalar start = SkIntToScalar(startAngle); | 991 SkScalar start = SkIntToScalar(startAngle); |
928 SkScalar sweep = SkIntToScalar(90); | 992 SkScalar sweep = SkIntToScalar(90); |
929 if (SkPath::kCCW_Direction == dir) { | 993 if (SkPath::kCCW_Direction == dir) { |
930 start += sweep; | 994 start += sweep; |
931 sweep = -sweep; | 995 sweep = -sweep; |
932 } | 996 } |
933 | 997 |
934 path->arcTo(r, start, sweep, forceMoveTo); | 998 if (addArcTo) { |
| 999 path->arcTo(r, start, sweep, forceMoveTo); |
| 1000 } else { |
| 1001 SkPoint pts[kSkBuildQuadArcStorage]; |
| 1002 int count = build_arc_points(r, start, sweep, pts); |
| 1003 for (int i = 1; i < count; i += 2) { |
| 1004 path->quadTo(pts[i], pts[i+1]); |
| 1005 } |
| 1006 } |
935 } | 1007 } |
936 | 1008 |
937 void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[], | 1009 void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[], |
938 Direction dir) { | 1010 Direction dir) { |
939 SkRRect rrect; | 1011 SkRRect rrect; |
940 rrect.setRectRadii(rect, (const SkVector*) radii); | 1012 rrect.setRectRadii(rect, (const SkVector*) radii); |
941 this->addRRect(rrect, dir); | 1013 this->addRRect(rrect, dir); |
942 } | 1014 } |
943 | 1015 |
944 void SkPath::addRRect(const SkRRect& rrect, Direction dir) { | 1016 void SkPath::addRRect(const SkRRect& rrect, Direction dir) { |
945 assert_known_direction(dir); | 1017 assert_known_direction(dir); |
946 | 1018 |
947 if (rrect.isEmpty()) { | 1019 if (rrect.isEmpty()) { |
948 return; | 1020 return; |
949 } | 1021 } |
950 | 1022 |
951 const SkRect& bounds = rrect.getBounds(); | 1023 const SkRect& bounds = rrect.getBounds(); |
952 | 1024 |
953 if (rrect.isRect()) { | 1025 if (rrect.isRect()) { |
954 this->addRect(bounds, dir); | 1026 this->addRect(bounds, dir); |
955 } else if (rrect.isOval()) { | 1027 } else if (rrect.isOval()) { |
956 this->addOval(bounds, dir); | 1028 this->addOval(bounds, dir); |
957 } else if (rrect.isSimple()) { | 1029 } else if (rrect.isSimple()) { |
958 const SkVector& rad = rrect.getSimpleRadii(); | 1030 const SkVector& rad = rrect.getSimpleRadii(); |
959 this->addRoundRect(bounds, rad.x(), rad.y(), dir); | 1031 this->addRoundRect(bounds, rad.x(), rad.y(), dir); |
960 } else { | 1032 } else { |
961 SkAutoPathBoundsUpdate apbu(this, bounds); | 1033 SkAutoPathBoundsUpdate apbu(this, bounds); |
962 | 1034 |
963 if (kCW_Direction == dir) { | 1035 if (kCW_Direction == dir) { |
964 add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY,
180, dir, true); | 1036 add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY,
180, dir, true, true); |
965 add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY,
270, dir, false); | 1037 add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY,
270, dir, true); |
966 add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,
0, dir, false); | 1038 add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,
0, dir, true); |
967 add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,
90, dir, false); | 1039 add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,
90, dir, true); |
968 } else { | 1040 } else { |
969 add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY,
180, dir, true); | 1041 add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY,
180, dir, true, true); |
970 add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,
90, dir, false); | 1042 add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,
90, dir, true); |
971 add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,
0, dir, false); | 1043 add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,
0, dir, true); |
972 add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY,
270, dir, false); | 1044 add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY,
270, dir, true); |
973 } | 1045 } |
974 this->close(); | 1046 this->close(); |
975 } | 1047 } |
976 } | 1048 } |
977 | 1049 |
978 bool SkPath::hasOnlyMoveTos() const { | 1050 bool SkPath::hasOnlyMoveTos() const { |
979 int count = fPathRef->countVerbs(); | 1051 int count = fPathRef->countVerbs(); |
980 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMe
mBegin(); | 1052 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMe
mBegin(); |
981 for (int i = 0; i < count; ++i) { | 1053 for (int i = 0; i < count; ++i) { |
982 if (*verbs == kLine_Verb || | 1054 if (*verbs == kLine_Verb || |
983 *verbs == kQuad_Verb || | 1055 *verbs == kQuad_Verb || |
984 *verbs == kCubic_Verb) { | 1056 *verbs == kCubic_Verb) { |
985 return false; | 1057 return false; |
986 } | 1058 } |
987 ++verbs; | 1059 ++verbs; |
988 } | 1060 } |
989 return true; | 1061 return true; |
990 } | 1062 } |
991 | 1063 |
| 1064 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
992 #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) | 1065 #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) |
| 1066 #endif |
993 | 1067 |
994 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, | 1068 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, |
995 Direction dir) { | 1069 Direction dir) { |
996 assert_known_direction(dir); | 1070 assert_known_direction(dir); |
997 | 1071 |
998 if (rx < 0 || ry < 0) { | 1072 if (rx < 0 || ry < 0) { |
999 SkErrorInternals::SetError( kInvalidArgument_SkError, | 1073 SkErrorInternals::SetError( kInvalidArgument_SkError, |
1000 "I got %f and %f as radii to SkPath::AddRoun
dRect, " | 1074 "I got %f and %f as radii to SkPath::AddRoun
dRect, " |
1001 "but negative radii are not allowed.", | 1075 "but negative radii are not allowed.", |
1002 SkScalarToDouble(rx), SkScalarToDouble(ry) )
; | 1076 SkScalarToDouble(rx), SkScalarToDouble(ry) )
; |
(...skipping 21 matching lines...) Expand all Loading... |
1024 | 1098 |
1025 SkAutoPathBoundsUpdate apbu(this, rect); | 1099 SkAutoPathBoundsUpdate apbu(this, rect); |
1026 SkAutoDisableDirectionCheck(this); | 1100 SkAutoDisableDirectionCheck(this); |
1027 | 1101 |
1028 if (skip_hori) { | 1102 if (skip_hori) { |
1029 rx = halfW; | 1103 rx = halfW; |
1030 } else if (skip_vert) { | 1104 } else if (skip_vert) { |
1031 ry = halfH; | 1105 ry = halfH; |
1032 } | 1106 } |
1033 | 1107 |
| 1108 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1034 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); | 1109 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); |
1035 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); | 1110 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); |
1036 | 1111 |
1037 this->incReserve(17); | 1112 this->incReserve(17); |
| 1113 #else |
| 1114 this->incReserve(13); |
| 1115 #endif |
1038 this->moveTo(rect.fRight - rx, rect.fTop); | 1116 this->moveTo(rect.fRight - rx, rect.fTop); |
1039 if (dir == kCCW_Direction) { | 1117 if (dir == kCCW_Direction) { |
1040 if (!skip_hori) { | 1118 if (!skip_hori) { |
1041 this->lineTo(rect.fLeft + rx, rect.fTop); // top | 1119 this->lineTo(rect.fLeft + rx, rect.fTop); // top |
1042 } | 1120 } |
| 1121 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1043 this->cubicTo(rect.fLeft + rx - sx, rect.fTop, | 1122 this->cubicTo(rect.fLeft + rx - sx, rect.fTop, |
1044 rect.fLeft, rect.fTop + ry - sy, | 1123 rect.fLeft, rect.fTop + ry - sy, |
1045 rect.fLeft, rect.fTop + ry); // top-left | 1124 rect.fLeft, rect.fTop + ry); // top-left |
| 1125 #else |
| 1126 add_corner_arc(this, rect, rx, ry, 180, dir, false); // top-left |
| 1127 #endif |
1046 if (!skip_vert) { | 1128 if (!skip_vert) { |
1047 this->lineTo(rect.fLeft, rect.fBottom - ry); // left | 1129 this->lineTo(rect.fLeft, rect.fBottom - ry); // left |
1048 } | 1130 } |
| 1131 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1049 this->cubicTo(rect.fLeft, rect.fBottom - ry + sy, | 1132 this->cubicTo(rect.fLeft, rect.fBottom - ry + sy, |
1050 rect.fLeft + rx - sx, rect.fBottom, | 1133 rect.fLeft + rx - sx, rect.fBottom, |
1051 rect.fLeft + rx, rect.fBottom); // bot-left | 1134 rect.fLeft + rx, rect.fBottom); // bot-left |
| 1135 #else |
| 1136 add_corner_arc(this, rect, rx, ry, 90, dir, false); // bot-left |
| 1137 #endif |
1052 if (!skip_hori) { | 1138 if (!skip_hori) { |
1053 this->lineTo(rect.fRight - rx, rect.fBottom); // bottom | 1139 this->lineTo(rect.fRight - rx, rect.fBottom); // bottom |
1054 } | 1140 } |
| 1141 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1055 this->cubicTo(rect.fRight - rx + sx, rect.fBottom, | 1142 this->cubicTo(rect.fRight - rx + sx, rect.fBottom, |
1056 rect.fRight, rect.fBottom - ry + sy, | 1143 rect.fRight, rect.fBottom - ry + sy, |
1057 rect.fRight, rect.fBottom - ry); // bot-right | 1144 rect.fRight, rect.fBottom - ry); // bot-right |
| 1145 #else |
| 1146 add_corner_arc(this, rect, rx, ry, 0, dir, false); // bot-right |
| 1147 #endif |
1058 if (!skip_vert) { | 1148 if (!skip_vert) { |
1059 this->lineTo(rect.fRight, rect.fTop + ry); | 1149 this->lineTo(rect.fRight, rect.fTop + ry); // right |
1060 } | 1150 } |
| 1151 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1061 this->cubicTo(rect.fRight, rect.fTop + ry - sy, | 1152 this->cubicTo(rect.fRight, rect.fTop + ry - sy, |
1062 rect.fRight - rx + sx, rect.fTop, | 1153 rect.fRight - rx + sx, rect.fTop, |
1063 rect.fRight - rx, rect.fTop); // top-right | 1154 rect.fRight - rx, rect.fTop); // top-right |
| 1155 #else |
| 1156 add_corner_arc(this, rect, rx, ry, 270, dir, false); // top-right |
| 1157 #endif |
1064 } else { | 1158 } else { |
| 1159 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1065 this->cubicTo(rect.fRight - rx + sx, rect.fTop, | 1160 this->cubicTo(rect.fRight - rx + sx, rect.fTop, |
1066 rect.fRight, rect.fTop + ry - sy, | 1161 rect.fRight, rect.fTop + ry - sy, |
1067 rect.fRight, rect.fTop + ry); // top-right | 1162 rect.fRight, rect.fTop + ry); // top-right |
| 1163 #else |
| 1164 add_corner_arc(this, rect, rx, ry, 270, dir, false); // top-right |
| 1165 #endif |
1068 if (!skip_vert) { | 1166 if (!skip_vert) { |
1069 this->lineTo(rect.fRight, rect.fBottom - ry); | 1167 this->lineTo(rect.fRight, rect.fBottom - ry); // right |
1070 } | 1168 } |
| 1169 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1071 this->cubicTo(rect.fRight, rect.fBottom - ry + sy, | 1170 this->cubicTo(rect.fRight, rect.fBottom - ry + sy, |
1072 rect.fRight - rx + sx, rect.fBottom, | 1171 rect.fRight - rx + sx, rect.fBottom, |
1073 rect.fRight - rx, rect.fBottom); // bot-right | 1172 rect.fRight - rx, rect.fBottom); // bot-right |
| 1173 #else |
| 1174 add_corner_arc(this, rect, rx, ry, 0, dir, false); // bot-right |
| 1175 #endif |
1074 if (!skip_hori) { | 1176 if (!skip_hori) { |
1075 this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom | 1177 this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom |
1076 } | 1178 } |
| 1179 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1077 this->cubicTo(rect.fLeft + rx - sx, rect.fBottom, | 1180 this->cubicTo(rect.fLeft + rx - sx, rect.fBottom, |
1078 rect.fLeft, rect.fBottom - ry + sy, | 1181 rect.fLeft, rect.fBottom - ry + sy, |
1079 rect.fLeft, rect.fBottom - ry); // bot-left | 1182 rect.fLeft, rect.fBottom - ry); // bot-left |
| 1183 #else |
| 1184 add_corner_arc(this, rect, rx, ry, 90, dir, false); // bot-left |
| 1185 #endif |
1080 if (!skip_vert) { | 1186 if (!skip_vert) { |
1081 this->lineTo(rect.fLeft, rect.fTop + ry); // left | 1187 this->lineTo(rect.fLeft, rect.fTop + ry); // left |
1082 } | 1188 } |
| 1189 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1083 this->cubicTo(rect.fLeft, rect.fTop + ry - sy, | 1190 this->cubicTo(rect.fLeft, rect.fTop + ry - sy, |
1084 rect.fLeft + rx - sx, rect.fTop, | 1191 rect.fLeft + rx - sx, rect.fTop, |
1085 rect.fLeft + rx, rect.fTop); // top-left | 1192 rect.fLeft + rx, rect.fTop); // top-left |
| 1193 #else |
| 1194 add_corner_arc(this, rect, rx, ry, 180, dir, false); // top-left |
| 1195 #endif |
1086 if (!skip_hori) { | 1196 if (!skip_hori) { |
1087 this->lineTo(rect.fRight - rx, rect.fTop); // top | 1197 this->lineTo(rect.fRight - rx, rect.fTop); // top |
1088 } | 1198 } |
1089 } | 1199 } |
1090 this->close(); | 1200 this->close(); |
1091 } | 1201 } |
1092 | 1202 |
1093 void SkPath::addOval(const SkRect& oval, Direction dir) { | 1203 void SkPath::addOval(const SkRect& oval, Direction dir) { |
1094 assert_known_direction(dir); | 1204 assert_known_direction(dir); |
1095 | 1205 |
1096 /* If addOval() is called after previous moveTo(), | 1206 /* If addOval() is called after previous moveTo(), |
1097 this path is still marked as an oval. This is used to | 1207 this path is still marked as an oval. This is used to |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1165 } | 1275 } |
1166 | 1276 |
1167 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { | 1277 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { |
1168 if (r > 0) { | 1278 if (r > 0) { |
1169 SkRect rect; | 1279 SkRect rect; |
1170 rect.set(x - r, y - r, x + r, y + r); | 1280 rect.set(x - r, y - r, x + r, y + r); |
1171 this->addOval(rect, dir); | 1281 this->addOval(rect, dir); |
1172 } | 1282 } |
1173 } | 1283 } |
1174 | 1284 |
1175 #include "SkGeometry.h" | |
1176 | |
1177 static int build_arc_points(const SkRect& oval, SkScalar startAngle, | |
1178 SkScalar sweepAngle, | |
1179 SkPoint pts[kSkBuildQuadArcStorage]) { | |
1180 | |
1181 if (0 == sweepAngle && | |
1182 (0 == startAngle || SkIntToScalar(360) == startAngle)) { | |
1183 // Chrome uses this path to move into and out of ovals. If not | |
1184 // treated as a special case the moves can distort the oval's | |
1185 // bounding box (and break the circle special case). | |
1186 pts[0].set(oval.fRight, oval.centerY()); | |
1187 return 1; | |
1188 } else if (0 == oval.width() && 0 == oval.height()) { | |
1189 // Chrome will sometimes create 0 radius round rects. Having degenerate | |
1190 // quad segments in the path prevents the path from being recognized as | |
1191 // a rect. | |
1192 // TODO: optimizing the case where only one of width or height is zero | |
1193 // should also be considered. This case, however, doesn't seem to be | |
1194 // as common as the single point case. | |
1195 pts[0].set(oval.fRight, oval.fTop); | |
1196 return 1; | |
1197 } | |
1198 | |
1199 SkVector start, stop; | |
1200 | |
1201 start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX); | |
1202 stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), | |
1203 &stop.fX); | |
1204 | |
1205 /* If the sweep angle is nearly (but less than) 360, then due to precision | |
1206 loss in radians-conversion and/or sin/cos, we may end up with coincident | |
1207 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead | |
1208 of drawing a nearly complete circle (good). | |
1209 e.g. canvas.drawArc(0, 359.99, ...) | |
1210 -vs- canvas.drawArc(0, 359.9, ...) | |
1211 We try to detect this edge case, and tweak the stop vector | |
1212 */ | |
1213 if (start == stop) { | |
1214 SkScalar sw = SkScalarAbs(sweepAngle); | |
1215 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) { | |
1216 SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle); | |
1217 // make a guess at a tiny angle (in radians) to tweak by | |
1218 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle); | |
1219 // not sure how much will be enough, so we use a loop | |
1220 do { | |
1221 stopRad -= deltaRad; | |
1222 stop.fY = SkScalarSinCos(stopRad, &stop.fX); | |
1223 } while (start == stop); | |
1224 } | |
1225 } | |
1226 | |
1227 SkMatrix matrix; | |
1228 | |
1229 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); | |
1230 matrix.postTranslate(oval.centerX(), oval.centerY()); | |
1231 | |
1232 return SkBuildQuadArc(start, stop, | |
1233 sweepAngle > 0 ? kCW_SkRotationDirection : | |
1234 kCCW_SkRotationDirection, | |
1235 &matrix, pts); | |
1236 } | |
1237 | |
1238 void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, | 1285 void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, |
1239 bool forceMoveTo) { | 1286 bool forceMoveTo) { |
1240 if (oval.width() < 0 || oval.height() < 0) { | 1287 if (oval.width() < 0 || oval.height() < 0) { |
1241 return; | 1288 return; |
1242 } | 1289 } |
1243 | 1290 |
1244 SkPoint pts[kSkBuildQuadArcStorage]; | 1291 SkPoint pts[kSkBuildQuadArcStorage]; |
1245 int count = build_arc_points(oval, startAngle, sweepAngle, pts); | 1292 int count = build_arc_points(oval, startAngle, sweepAngle, pts); |
1246 SkASSERT((count & 1) == 1); | 1293 SkASSERT((count & 1) == 1); |
1247 | 1294 |
1248 if (fPathRef->countVerbs() == 0) { | 1295 if (fPathRef->countVerbs() == 0) { |
1249 forceMoveTo = true; | 1296 forceMoveTo = true; |
1250 } | 1297 } |
1251 this->incReserve(count); | 1298 this->incReserve(count); |
1252 forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]); | 1299 forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]); |
1253 for (int i = 1; i < count; i += 2) { | 1300 for (int i = 1; i < count; i += 2) { |
1254 this->quadTo(pts[i], pts[i+1]); | 1301 this->quadTo(pts[i], pts[i+1]); |
1255 } | 1302 } |
1256 } | 1303 } |
1257 | 1304 |
1258 void SkPath::addArc(const SkRect& oval, SkScalar startAngle, | 1305 void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle
) { |
1259 SkScalar sweepAngle) { | |
1260 if (oval.isEmpty() || 0 == sweepAngle) { | 1306 if (oval.isEmpty() || 0 == sweepAngle) { |
1261 return; | 1307 return; |
1262 } | 1308 } |
1263 | 1309 |
1264 const SkScalar kFullCircleAngle = SkIntToScalar(360); | 1310 const SkScalar kFullCircleAngle = SkIntToScalar(360); |
1265 | 1311 |
1266 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) { | 1312 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) { |
1267 this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction); | 1313 this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction); |
1268 return; | 1314 return; |
1269 } | 1315 } |
(...skipping 1660 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2930 switch (this->getFillType()) { | 2976 switch (this->getFillType()) { |
2931 case SkPath::kEvenOdd_FillType: | 2977 case SkPath::kEvenOdd_FillType: |
2932 case SkPath::kInverseEvenOdd_FillType: | 2978 case SkPath::kInverseEvenOdd_FillType: |
2933 w &= 1; | 2979 w &= 1; |
2934 break; | 2980 break; |
2935 default: | 2981 default: |
2936 break; | 2982 break; |
2937 } | 2983 } |
2938 return SkToBool(w); | 2984 return SkToBool(w); |
2939 } | 2985 } |
OLD | NEW |