| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2006-2008 The Android Open Source Project | |
| 3 * | |
| 4 * Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 * you may not use this file except in compliance with the License. | |
| 6 * You may obtain a copy of the License at | |
| 7 * | |
| 8 * http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 * | |
| 10 * Unless required by applicable law or agreed to in writing, software | |
| 11 * distributed under the License is distributed on an "AS IS" BASIS, | |
| 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 13 * See the License for the specific language governing permissions and | |
| 14 * limitations under the License. | |
| 15 */ | |
| 16 | |
| 17 #include "SkStrokerPriv.h" | |
| 18 #include "SkGeometry.h" | |
| 19 #include "SkPath.h" | |
| 20 | |
| 21 #define kMaxQuadSubdivide 5 | |
| 22 #define kMaxCubicSubdivide 4 | |
| 23 | |
| 24 static inline bool degenerate_vector(const SkVector& v) { | |
| 25 return SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY); | |
| 26 } | |
| 27 | |
| 28 static inline bool degenerate_line(const SkPoint& a, const SkPoint& b, | |
| 29 SkScalar tolerance = SK_ScalarNearlyZero) { | |
| 30 return SkScalarNearlyZero(a.fX - b.fX, tolerance) && | |
| 31 SkScalarNearlyZero(a.fY - b.fY, tolerance); | |
| 32 } | |
| 33 | |
| 34 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) { | |
| 35 /* root2/2 is a 45-degree angle | |
| 36 make this constant bigger for more subdivisions (but not >= 1) | |
| 37 */ | |
| 38 static const SkScalar kFlatEnoughNormalDotProd = | |
| 39 SK_ScalarSqrt2/2 + SK_Scalar1/10; | |
| 40 | |
| 41 SkASSERT(kFlatEnoughNormalDotProd > 0 && | |
| 42 kFlatEnoughNormalDotProd < SK_Scalar1); | |
| 43 | |
| 44 return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd; | |
| 45 } | |
| 46 | |
| 47 static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) { | |
| 48 static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000; | |
| 49 | |
| 50 return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd; | |
| 51 } | |
| 52 | |
| 53 static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, | |
| 54 SkScalar radius, | |
| 55 SkVector* normal, SkVector* unitNormal) { | |
| 56 if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) { | |
| 57 return false; | |
| 58 } | |
| 59 unitNormal->rotateCCW(); | |
| 60 unitNormal->scale(radius, normal); | |
| 61 return true; | |
| 62 } | |
| 63 | |
| 64 static bool set_normal_unitnormal(const SkVector& vec, | |
| 65 SkScalar radius, | |
| 66 SkVector* normal, SkVector* unitNormal) { | |
| 67 if (!unitNormal->setNormalize(vec.fX, vec.fY)) { | |
| 68 return false; | |
| 69 } | |
| 70 unitNormal->rotateCCW(); | |
| 71 unitNormal->scale(radius, normal); | |
| 72 return true; | |
| 73 } | |
| 74 | |
| 75 /////////////////////////////////////////////////////////////////////////////// | |
| 76 | |
| 77 class SkPathStroker { | |
| 78 public: | |
| 79 SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap, | |
| 80 SkPaint::Join join); | |
| 81 | |
| 82 void moveTo(const SkPoint&); | |
| 83 void lineTo(const SkPoint&); | |
| 84 void quadTo(const SkPoint&, const SkPoint&); | |
| 85 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); | |
| 86 void close(bool isLine) { this->finishContour(true, isLine); } | |
| 87 | |
| 88 void done(SkPath* dst, bool isLine) { | |
| 89 this->finishContour(false, isLine); | |
| 90 fOuter.addPath(fExtra); | |
| 91 dst->swap(fOuter); | |
| 92 } | |
| 93 | |
| 94 private: | |
| 95 SkScalar fRadius; | |
| 96 SkScalar fInvMiterLimit; | |
| 97 | |
| 98 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; | |
| 99 SkPoint fFirstPt, fPrevPt; // on original path | |
| 100 SkPoint fFirstOuterPt; | |
| 101 int fSegmentCount; | |
| 102 bool fPrevIsLine; | |
| 103 | |
| 104 SkStrokerPriv::CapProc fCapper; | |
| 105 SkStrokerPriv::JoinProc fJoiner; | |
| 106 | |
| 107 SkPath fInner, fOuter; // outer is our working answer, inner is temp | |
| 108 SkPath fExtra; // added as extra complete contours | |
| 109 | |
| 110 void finishContour(bool close, bool isLine); | |
| 111 void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, | |
| 112 bool isLine); | |
| 113 void postJoinTo(const SkPoint&, const SkVector& normal, | |
| 114 const SkVector& unitNormal); | |
| 115 | |
| 116 void line_to(const SkPoint& currPt, const SkVector& normal); | |
| 117 void quad_to(const SkPoint pts[3], | |
| 118 const SkVector& normalAB, const SkVector& unitNormalAB, | |
| 119 SkVector* normalBC, SkVector* unitNormalBC, | |
| 120 int subDivide); | |
| 121 void cubic_to(const SkPoint pts[4], | |
| 122 const SkVector& normalAB, const SkVector& unitNormalAB, | |
| 123 SkVector* normalCD, SkVector* unitNormalCD, | |
| 124 int subDivide); | |
| 125 }; | |
| 126 | |
| 127 /////////////////////////////////////////////////////////////////////////////// | |
| 128 | |
| 129 void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, | |
| 130 SkVector* unitNormal, bool currIsLine) { | |
| 131 SkASSERT(fSegmentCount >= 0); | |
| 132 | |
| 133 SkScalar prevX = fPrevPt.fX; | |
| 134 SkScalar prevY = fPrevPt.fY; | |
| 135 | |
| 136 SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal, | |
| 137 unitNormal)); | |
| 138 | |
| 139 if (fSegmentCount == 0) { | |
| 140 fFirstNormal = *normal; | |
| 141 fFirstUnitNormal = *unitNormal; | |
| 142 fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY); | |
| 143 | |
| 144 fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY); | |
| 145 fInner.moveTo(prevX - normal->fX, prevY - normal->fY); | |
| 146 } else { // we have a previous segment | |
| 147 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal, | |
| 148 fRadius, fInvMiterLimit, fPrevIsLine, currIsLine); | |
| 149 } | |
| 150 fPrevIsLine = currIsLine; | |
| 151 } | |
| 152 | |
| 153 void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal, | |
| 154 const SkVector& unitNormal) { | |
| 155 fPrevPt = currPt; | |
| 156 fPrevUnitNormal = unitNormal; | |
| 157 fPrevNormal = normal; | |
| 158 fSegmentCount += 1; | |
| 159 } | |
| 160 | |
| 161 void SkPathStroker::finishContour(bool close, bool currIsLine) { | |
| 162 if (fSegmentCount > 0) { | |
| 163 SkPoint pt; | |
| 164 | |
| 165 if (close) { | |
| 166 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, | |
| 167 fFirstUnitNormal, fRadius, fInvMiterLimit, | |
| 168 fPrevIsLine, currIsLine); | |
| 169 fOuter.close(); | |
| 170 // now add fInner as its own contour | |
| 171 fInner.getLastPt(&pt); | |
| 172 fOuter.moveTo(pt.fX, pt.fY); | |
| 173 fOuter.reversePathTo(fInner); | |
| 174 fOuter.close(); | |
| 175 } else { // add caps to start and end | |
| 176 // cap the end | |
| 177 fInner.getLastPt(&pt); | |
| 178 fCapper(&fOuter, fPrevPt, fPrevNormal, pt, | |
| 179 currIsLine ? &fInner : NULL); | |
| 180 fOuter.reversePathTo(fInner); | |
| 181 // cap the start | |
| 182 fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, | |
| 183 fPrevIsLine ? &fInner : NULL); | |
| 184 fOuter.close(); | |
| 185 } | |
| 186 } | |
| 187 fInner.reset(); | |
| 188 fSegmentCount = -1; | |
| 189 } | |
| 190 | |
| 191 /////////////////////////////////////////////////////////////////////////////// | |
| 192 | |
| 193 SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit, | |
| 194 SkPaint::Cap cap, SkPaint::Join join) | |
| 195 : fRadius(radius) { | |
| 196 | |
| 197 /* This is only used when join is miter_join, but we initialize it here | |
| 198 so that it is always defined, to fis valgrind warnings. | |
| 199 */ | |
| 200 fInvMiterLimit = 0; | |
| 201 | |
| 202 if (join == SkPaint::kMiter_Join) { | |
| 203 if (miterLimit <= SK_Scalar1) { | |
| 204 join = SkPaint::kBevel_Join; | |
| 205 } else { | |
| 206 fInvMiterLimit = SkScalarInvert(miterLimit); | |
| 207 } | |
| 208 } | |
| 209 fCapper = SkStrokerPriv::CapFactory(cap); | |
| 210 fJoiner = SkStrokerPriv::JoinFactory(join); | |
| 211 fSegmentCount = -1; | |
| 212 fPrevIsLine = false; | |
| 213 } | |
| 214 | |
| 215 void SkPathStroker::moveTo(const SkPoint& pt) { | |
| 216 if (fSegmentCount > 0) { | |
| 217 this->finishContour(false, false); | |
| 218 } | |
| 219 fSegmentCount = 0; | |
| 220 fFirstPt = fPrevPt = pt; | |
| 221 } | |
| 222 | |
| 223 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { | |
| 224 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); | |
| 225 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); | |
| 226 } | |
| 227 | |
| 228 void SkPathStroker::lineTo(const SkPoint& currPt) { | |
| 229 if (degenerate_line(fPrevPt, currPt)) { | |
| 230 return; | |
| 231 } | |
| 232 SkVector normal, unitNormal; | |
| 233 | |
| 234 this->preJoinTo(currPt, &normal, &unitNormal, true); | |
| 235 this->line_to(currPt, normal); | |
| 236 this->postJoinTo(currPt, normal, unitNormal); | |
| 237 } | |
| 238 | |
| 239 void SkPathStroker::quad_to(const SkPoint pts[3], | |
| 240 const SkVector& normalAB, const SkVector& unitNormalAB, | |
| 241 SkVector* normalBC, SkVector* unitNormalBC, | |
| 242 int subDivide) { | |
| 243 if (!set_normal_unitnormal(pts[1], pts[2], fRadius, | |
| 244 normalBC, unitNormalBC)) { | |
| 245 // pts[1] nearly equals pts[2], so just draw a line to pts[2] | |
| 246 this->line_to(pts[2], normalAB); | |
| 247 *normalBC = normalAB; | |
| 248 *unitNormalBC = unitNormalAB; | |
| 249 return; | |
| 250 } | |
| 251 | |
| 252 if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) { | |
| 253 SkPoint tmp[5]; | |
| 254 SkVector norm, unit; | |
| 255 | |
| 256 SkChopQuadAtHalf(pts, tmp); | |
| 257 this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide); | |
| 258 this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide); | |
| 259 } else { | |
| 260 SkVector normalB, unitB; | |
| 261 SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius, | |
| 262 &normalB, &unitB)); | |
| 263 | |
| 264 fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, | |
| 265 pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); | |
| 266 fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, | |
| 267 pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 void SkPathStroker::cubic_to(const SkPoint pts[4], | |
| 272 const SkVector& normalAB, const SkVector& unitNormalAB, | |
| 273 SkVector* normalCD, SkVector* unitNormalCD, | |
| 274 int subDivide) { | |
| 275 SkVector ab = pts[1] - pts[0]; | |
| 276 SkVector cd = pts[3] - pts[2]; | |
| 277 SkVector normalBC, unitNormalBC; | |
| 278 | |
| 279 bool degenerateAB = degenerate_vector(ab); | |
| 280 bool degenerateCD = degenerate_vector(cd); | |
| 281 | |
| 282 if (degenerateAB && degenerateCD) { | |
| 283 DRAW_LINE: | |
| 284 this->line_to(pts[3], normalAB); | |
| 285 *normalCD = normalAB; | |
| 286 *unitNormalCD = unitNormalAB; | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 if (degenerateAB) { | |
| 291 ab = pts[2] - pts[0]; | |
| 292 degenerateAB = degenerate_vector(ab); | |
| 293 } | |
| 294 if (degenerateCD) { | |
| 295 cd = pts[3] - pts[1]; | |
| 296 degenerateCD = degenerate_vector(cd); | |
| 297 } | |
| 298 if (degenerateAB || degenerateCD) { | |
| 299 goto DRAW_LINE; | |
| 300 } | |
| 301 SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD)); | |
| 302 bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius, | |
| 303 &normalBC, &unitNormalBC); | |
| 304 | |
| 305 if (--subDivide >= 0 && | |
| 306 (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) || | |
| 307 normals_too_curvy(unitNormalBC, *unitNormalCD))) { | |
| 308 SkPoint tmp[7]; | |
| 309 SkVector norm, unit, dummy, unitDummy; | |
| 310 | |
| 311 SkChopCubicAtHalf(pts, tmp); | |
| 312 this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, | |
| 313 subDivide); | |
| 314 // we use dummys since we already have a valid (and more accurate) | |
| 315 // normals for CD | |
| 316 this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide); | |
| 317 } else { | |
| 318 SkVector normalB, normalC; | |
| 319 | |
| 320 // need normals to inset/outset the off-curve pts B and C | |
| 321 | |
| 322 if (0) { // this is normal to the line between our adjacent pts | |
| 323 normalB = pts[2] - pts[0]; | |
| 324 normalB.rotateCCW(); | |
| 325 SkAssertResult(normalB.setLength(fRadius)); | |
| 326 | |
| 327 normalC = pts[3] - pts[1]; | |
| 328 normalC.rotateCCW(); | |
| 329 SkAssertResult(normalC.setLength(fRadius)); | |
| 330 } else { // miter-join | |
| 331 SkVector unitBC = pts[2] - pts[1]; | |
| 332 unitBC.normalize(); | |
| 333 unitBC.rotateCCW(); | |
| 334 | |
| 335 normalB = unitNormalAB + unitBC; | |
| 336 normalC = *unitNormalCD + unitBC; | |
| 337 | |
| 338 SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC); | |
| 339 SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, | |
| 340 SkScalarSqrt((SK_Scalar1 + dot)/2)))); | |
| 341 dot = SkPoint::DotProduct(*unitNormalCD, unitBC); | |
| 342 SkAssertResult(normalC.setLength(SkScalarDiv(fRadius, | |
| 343 SkScalarSqrt((SK_Scalar1 + dot)/2)))); | |
| 344 } | |
| 345 | |
| 346 fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, | |
| 347 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, | |
| 348 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); | |
| 349 | |
| 350 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, | |
| 351 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, | |
| 352 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) { | |
| 357 bool degenerateAB = degenerate_line(fPrevPt, pt1); | |
| 358 bool degenerateBC = degenerate_line(pt1, pt2); | |
| 359 | |
| 360 if (degenerateAB | degenerateBC) { | |
| 361 if (degenerateAB ^ degenerateBC) { | |
| 362 this->lineTo(pt2); | |
| 363 } | |
| 364 return; | |
| 365 } | |
| 366 | |
| 367 SkVector normalAB, unitAB, normalBC, unitBC; | |
| 368 | |
| 369 this->preJoinTo(pt1, &normalAB, &unitAB, false); | |
| 370 | |
| 371 { | |
| 372 SkPoint pts[3], tmp[5]; | |
| 373 pts[0] = fPrevPt; | |
| 374 pts[1] = pt1; | |
| 375 pts[2] = pt2; | |
| 376 | |
| 377 if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) { | |
| 378 unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY); | |
| 379 unitBC.rotateCCW(); | |
| 380 if (normals_too_pinchy(unitAB, unitBC)) { | |
| 381 normalBC = unitBC; | |
| 382 normalBC.scale(fRadius); | |
| 383 | |
| 384 fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY); | |
| 385 fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY); | |
| 386 fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY); | |
| 387 | |
| 388 fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY); | |
| 389 fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY); | |
| 390 fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY); | |
| 391 | |
| 392 fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius, | |
| 393 SkPath::kCW_Direction); | |
| 394 } else { | |
| 395 this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC, | |
| 396 kMaxQuadSubdivide); | |
| 397 SkVector n = normalBC; | |
| 398 SkVector u = unitBC; | |
| 399 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC, | |
| 400 kMaxQuadSubdivide); | |
| 401 } | |
| 402 } else { | |
| 403 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC, | |
| 404 kMaxQuadSubdivide); | |
| 405 } | |
| 406 } | |
| 407 | |
| 408 this->postJoinTo(pt2, normalBC, unitBC); | |
| 409 } | |
| 410 | |
| 411 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, | |
| 412 const SkPoint& pt3) { | |
| 413 bool degenerateAB = degenerate_line(fPrevPt, pt1); | |
| 414 bool degenerateBC = degenerate_line(pt1, pt2); | |
| 415 bool degenerateCD = degenerate_line(pt2, pt3); | |
| 416 | |
| 417 if (degenerateAB + degenerateBC + degenerateCD >= 2) { | |
| 418 this->lineTo(pt3); | |
| 419 return; | |
| 420 } | |
| 421 | |
| 422 SkVector normalAB, unitAB, normalCD, unitCD; | |
| 423 | |
| 424 // find the first tangent (which might be pt1 or pt2 | |
| 425 { | |
| 426 const SkPoint* nextPt = &pt1; | |
| 427 if (degenerateAB) | |
| 428 nextPt = &pt2; | |
| 429 this->preJoinTo(*nextPt, &normalAB, &unitAB, false); | |
| 430 } | |
| 431 | |
| 432 { | |
| 433 SkPoint pts[4], tmp[13]; | |
| 434 int i, count; | |
| 435 SkVector n, u; | |
| 436 SkScalar tValues[3]; | |
| 437 | |
| 438 pts[0] = fPrevPt; | |
| 439 pts[1] = pt1; | |
| 440 pts[2] = pt2; | |
| 441 pts[3] = pt3; | |
| 442 | |
| 443 #if 1 | |
| 444 count = SkChopCubicAtMaxCurvature(pts, tmp, tValues); | |
| 445 #else | |
| 446 count = 1; | |
| 447 memcpy(tmp, pts, 4 * sizeof(SkPoint)); | |
| 448 #endif | |
| 449 n = normalAB; | |
| 450 u = unitAB; | |
| 451 for (i = 0; i < count; i++) { | |
| 452 this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD, | |
| 453 kMaxCubicSubdivide); | |
| 454 if (i == count - 1) { | |
| 455 break; | |
| 456 } | |
| 457 n = normalCD; | |
| 458 u = unitCD; | |
| 459 | |
| 460 } | |
| 461 | |
| 462 // check for too pinchy | |
| 463 for (i = 1; i < count; i++) { | |
| 464 SkPoint p; | |
| 465 SkVector v, c; | |
| 466 | |
| 467 SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c); | |
| 468 | |
| 469 SkScalar dot = SkPoint::DotProduct(c, c); | |
| 470 v.scale(SkScalarInvert(dot)); | |
| 471 | |
| 472 if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) { | |
| 473 fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction); | |
| 474 } | |
| 475 } | |
| 476 | |
| 477 } | |
| 478 | |
| 479 this->postJoinTo(pt3, normalCD, unitCD); | |
| 480 } | |
| 481 | |
| 482 /////////////////////////////////////////////////////////////////////////////// | |
| 483 /////////////////////////////////////////////////////////////////////////////// | |
| 484 | |
| 485 #include "SkPaint.h" | |
| 486 | |
| 487 SkStroke::SkStroke() { | |
| 488 fWidth = SK_DefaultStrokeWidth; | |
| 489 fMiterLimit = SK_DefaultMiterLimit; | |
| 490 fCap = SkPaint::kDefault_Cap; | |
| 491 fJoin = SkPaint::kDefault_Join; | |
| 492 fDoFill = false; | |
| 493 } | |
| 494 | |
| 495 SkStroke::SkStroke(const SkPaint& p) { | |
| 496 fWidth = p.getStrokeWidth(); | |
| 497 fMiterLimit = p.getStrokeMiter(); | |
| 498 fCap = (uint8_t)p.getStrokeCap(); | |
| 499 fJoin = (uint8_t)p.getStrokeJoin(); | |
| 500 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); | |
| 501 } | |
| 502 | |
| 503 SkStroke::SkStroke(const SkPaint& p, SkScalar width) { | |
| 504 fWidth = width; | |
| 505 fMiterLimit = p.getStrokeMiter(); | |
| 506 fCap = (uint8_t)p.getStrokeCap(); | |
| 507 fJoin = (uint8_t)p.getStrokeJoin(); | |
| 508 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); | |
| 509 } | |
| 510 | |
| 511 void SkStroke::setWidth(SkScalar width) { | |
| 512 SkASSERT(width >= 0); | |
| 513 fWidth = width; | |
| 514 } | |
| 515 | |
| 516 void SkStroke::setMiterLimit(SkScalar miterLimit) { | |
| 517 SkASSERT(miterLimit >= 0); | |
| 518 fMiterLimit = miterLimit; | |
| 519 } | |
| 520 | |
| 521 void SkStroke::setCap(SkPaint::Cap cap) { | |
| 522 SkASSERT((unsigned)cap < SkPaint::kCapCount); | |
| 523 fCap = SkToU8(cap); | |
| 524 } | |
| 525 | |
| 526 void SkStroke::setJoin(SkPaint::Join join) { | |
| 527 SkASSERT((unsigned)join < SkPaint::kJoinCount); | |
| 528 fJoin = SkToU8(join); | |
| 529 } | |
| 530 | |
| 531 /////////////////////////////////////////////////////////////////////////////// | |
| 532 | |
| 533 #ifdef SK_SCALAR_IS_FIXED | |
| 534 /* return non-zero if the path is too big, and should be shrunk to avoid | |
| 535 overflows during intermediate calculations. Note that we compute the | |
| 536 bounds for this. If we had a custom callback/walker for paths, we could | |
| 537 perhaps go faster by using that, and just perform the abs | in that | |
| 538 routine | |
| 539 */ | |
| 540 static int needs_to_shrink(const SkPath& path) { | |
| 541 SkRect r; | |
| 542 path.computeBounds(&r, SkPath::kFast_BoundsType); | |
| 543 SkFixed mask = SkAbs32(r.fLeft); | |
| 544 mask |= SkAbs32(r.fTop); | |
| 545 mask |= SkAbs32(r.fRight); | |
| 546 mask |= SkAbs32(r.fBottom); | |
| 547 // we need the top 3 bits clear (after abs) to avoid overflow | |
| 548 return mask >> 29; | |
| 549 } | |
| 550 | |
| 551 static void identity_proc(SkPoint pts[], int count) {} | |
| 552 static void shift_down_2_proc(SkPoint pts[], int count) { | |
| 553 for (int i = 0; i < count; i++) { | |
| 554 pts->fX >>= 2; | |
| 555 pts->fY >>= 2; | |
| 556 pts += 1; | |
| 557 } | |
| 558 } | |
| 559 #define APPLY_PROC(proc, pts, count) proc(pts, count) | |
| 560 #else // float does need any of this | |
| 561 #define APPLY_PROC(proc, pts, count) | |
| 562 #endif | |
| 563 | |
| 564 void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { | |
| 565 SkASSERT(&src != NULL && dst != NULL); | |
| 566 | |
| 567 SkScalar radius = SkScalarHalf(fWidth); | |
| 568 | |
| 569 dst->reset(); | |
| 570 if (radius <= 0) { | |
| 571 return; | |
| 572 } | |
| 573 | |
| 574 #ifdef SK_SCALAR_IS_FIXED | |
| 575 void (*proc)(SkPoint pts[], int count) = identity_proc; | |
| 576 if (needs_to_shrink(src)) { | |
| 577 proc = shift_down_2_proc; | |
| 578 radius >>= 2; | |
| 579 if (radius == 0) { | |
| 580 return; | |
| 581 } | |
| 582 } | |
| 583 #endif | |
| 584 | |
| 585 SkPathStroker stroker(radius, fMiterLimit, this->getCap(), | |
| 586 this->getJoin()); | |
| 587 | |
| 588 SkPath::Iter iter(src, false); | |
| 589 SkPoint pts[4]; | |
| 590 SkPath::Verb verb, lastSegment = SkPath::kMove_Verb; | |
| 591 | |
| 592 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { | |
| 593 switch (verb) { | |
| 594 case SkPath::kMove_Verb: | |
| 595 APPLY_PROC(proc, &pts[0], 1); | |
| 596 stroker.moveTo(pts[0]); | |
| 597 break; | |
| 598 case SkPath::kLine_Verb: | |
| 599 APPLY_PROC(proc, &pts[1], 1); | |
| 600 stroker.lineTo(pts[1]); | |
| 601 lastSegment = verb; | |
| 602 break; | |
| 603 case SkPath::kQuad_Verb: | |
| 604 APPLY_PROC(proc, &pts[1], 2); | |
| 605 stroker.quadTo(pts[1], pts[2]); | |
| 606 lastSegment = verb; | |
| 607 break; | |
| 608 case SkPath::kCubic_Verb: | |
| 609 APPLY_PROC(proc, &pts[1], 3); | |
| 610 stroker.cubicTo(pts[1], pts[2], pts[3]); | |
| 611 lastSegment = verb; | |
| 612 break; | |
| 613 case SkPath::kClose_Verb: | |
| 614 stroker.close(lastSegment == SkPath::kLine_Verb); | |
| 615 break; | |
| 616 default: | |
| 617 break; | |
| 618 } | |
| 619 } | |
| 620 stroker.done(dst, lastSegment == SkPath::kLine_Verb); | |
| 621 | |
| 622 #ifdef SK_SCALAR_IS_FIXED | |
| 623 // undo our previous down_shift | |
| 624 if (shift_down_2_proc == proc) { | |
| 625 // need a real shift methid on path. antialias paths could use this too | |
| 626 SkMatrix matrix; | |
| 627 matrix.setScale(SkIntToScalar(4), SkIntToScalar(4)); | |
| 628 dst->transform(matrix); | |
| 629 } | |
| 630 #endif | |
| 631 | |
| 632 if (fDoFill) { | |
| 633 dst->addPath(src); | |
| 634 } | |
| 635 } | |
| 636 | |
| 637 void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1, | |
| 638 SkPath* dst) const { | |
| 639 SkPath tmp; | |
| 640 | |
| 641 tmp.moveTo(p0); | |
| 642 tmp.lineTo(p1); | |
| 643 this->strokePath(tmp, dst); | |
| 644 } | |
| 645 | |
| OLD | NEW |