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

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

Issue 26372006: Replace cubic round rect corners with quads (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: base files missing retry Created 7 years, 2 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 | Annotate | Revision Log
« no previous file with comments | « expectations/gm/ignored-tests.txt ('k') | src/core/SkPathRef.cpp » ('j') | 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 /* 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
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
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
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
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 }
OLDNEW
« no previous file with comments | « expectations/gm/ignored-tests.txt ('k') | src/core/SkPathRef.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698