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 "SkPathMeasure.h" 18 #include "SkGeometry.h" 19 #include "SkPath.h" 20 #include "SkTSearch.h" 21 22 // these must be 0,1,2 since they are in our 2-bit field 23 enum { 24 kLine_SegType, 25 kCloseLine_SegType, 26 kQuad_SegType, 27 kCubic_SegType 28 }; 29 30 #define kMaxTValue 32767 31 32 static inline SkScalar tValue2Scalar(int t) { 33 SkASSERT((unsigned)t <= kMaxTValue); 34 35 #ifdef SK_SCALAR_IS_FLOAT 36 return t * 3.05185e-5f; // t / 32767 37 #else 38 return (t + (t >> 14)) << 1; 39 #endif 40 } 41 42 SkScalar SkPathMeasure::Segment::getScalarT() const { 43 return tValue2Scalar(fTValue); 44 } 45 46 const SkPathMeasure::Segment* SkPathMeasure::NextSegment(const Segment* seg) { 47 unsigned ptIndex = seg->fPtIndex; 48 49 do { 50 ++seg; 51 } while (seg->fPtIndex == ptIndex); 52 return seg; 53 } 54 55 /////////////////////////////////////////////////////////////////////////////// 56 57 static inline int tspan_big_enough(int tspan) { 58 SkASSERT((unsigned)tspan <= kMaxTValue); 59 return tspan >> 10; 60 } 61 62 #if 0 63 static inline bool tangents_too_curvy(const SkVector& tan0, SkVector& tan1) { 64 static const SkScalar kFlatEnoughTangentDotProd = SK_Scalar1 * 99 / 100; 65 66 SkASSERT(kFlatEnoughTangentDotProd > 0 && 67 kFlatEnoughTangentDotProd < SK_Scalar1); 68 69 return SkPoint::DotProduct(tan0, tan1) < kFlatEnoughTangentDotProd; 70 } 71 #endif 72 73 // can't use tangents, since we need [0..1..................2] to be seen 74 // as definitely not a line (it is when drawn, but not parametrically) 75 // so we compare midpoints 76 #define CHEAP_DIST_LIMIT (SK_Scalar1/2) // just made this value up 77 78 static bool quad_too_curvy(const SkPoint pts[3]) { 79 // diff = (a/4 + b/2 + c/4) - (a/2 + c/2) 80 // diff = -a/4 + b/2 - c/4 81 SkScalar dx = SkScalarHalf(pts[1].fX) - 82 SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX)); 83 SkScalar dy = SkScalarHalf(pts[1].fY) - 84 SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY)); 85 86 SkScalar dist = SkMaxScalar(SkScalarAbs(dx), SkScalarAbs(dy)); 87 return dist > CHEAP_DIST_LIMIT; 88 } 89 90 static bool cheap_dist_exceeds_limit(const SkPoint& pt, 91 SkScalar x, SkScalar y) { 92 SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY)); 93 // just made up the 1/2 94 return dist > CHEAP_DIST_LIMIT; 95 } 96 97 static bool cubic_too_curvy(const SkPoint pts[4]) { 98 return cheap_dist_exceeds_limit(pts[1], 99 SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3), 100 SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3)) 101 || 102 cheap_dist_exceeds_limit(pts[2], 103 SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3), 104 SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3)); 105 } 106 107 SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3], 108 SkScalar distance, int mint, int maxt, int ptIndex) { 109 if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) { 110 SkPoint tmp[5]; 111 int halft = (mint + maxt) >> 1; 112 113 SkChopQuadAtHalf(pts, tmp); 114 distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex); 115 distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex); 116 } else { 117 SkScalar d = SkPoint::Distance(pts[0], pts[2]); 118 SkASSERT(d >= 0); 119 if (!SkScalarNearlyZero(d)) { 120 distance += d; 121 Segment* seg = fSegments.append(); 122 seg->fDistance = distance; 123 seg->fPtIndex = ptIndex; 124 seg->fType = kQuad_SegType; 125 seg->fTValue = maxt; 126 } 127 } 128 return distance; 129 } 130 131 SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4], 132 SkScalar distance, int mint, int maxt, int ptIndex) { 133 if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts)) { 134 SkPoint tmp[7]; 135 int halft = (mint + maxt) >> 1; 136 137 SkChopCubicAtHalf(pts, tmp); 138 distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex); 139 distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex); 140 } else { 141 SkScalar d = SkPoint::Distance(pts[0], pts[3]); 142 SkASSERT(d >= 0); 143 if (!SkScalarNearlyZero(d)) { 144 distance += d; 145 Segment* seg = fSegments.append(); 146 seg->fDistance = distance; 147 seg->fPtIndex = ptIndex; 148 seg->fType = kCubic_SegType; 149 seg->fTValue = maxt; 150 } 151 } 152 return distance; 153 } 154 155 void SkPathMeasure::buildSegments() { 156 SkPoint pts[4]; 157 int ptIndex = fFirstPtIndex; 158 SkScalar d, distance = 0; 159 bool isClosed = fForceClosed; 160 bool firstMoveTo = ptIndex < 0; 161 Segment* seg; 162 163 fSegments.reset(); 164 for (;;) { 165 switch (fIter.next(pts)) { 166 case SkPath::kMove_Verb: 167 if (!firstMoveTo) { 168 goto DONE; 169 } 170 ptIndex += 1; 171 firstMoveTo = false; 172 break; 173 174 case SkPath::kLine_Verb: 175 d = SkPoint::Distance(pts[0], pts[1]); 176 SkASSERT(d >= 0); 177 if (!SkScalarNearlyZero(d)) { 178 distance += d; 179 seg = fSegments.append(); 180 seg->fDistance = distance; 181 seg->fPtIndex = ptIndex; 182 seg->fType = fIter.isCloseLine() ? 183 kCloseLine_SegType : kLine_SegType; 184 seg->fTValue = kMaxTValue; 185 } 186 ptIndex += !fIter.isCloseLine(); 187 break; 188 189 case SkPath::kQuad_Verb: 190 distance = this->compute_quad_segs(pts, distance, 0, 191 kMaxTValue, ptIndex); 192 ptIndex += 2; 193 break; 194 195 case SkPath::kCubic_Verb: 196 distance = this->compute_cubic_segs(pts, distance, 0, 197 kMaxTValue, ptIndex); 198 ptIndex += 3; 199 break; 200 201 case SkPath::kClose_Verb: 202 isClosed = true; 203 break; 204 205 case SkPath::kDone_Verb: 206 goto DONE; 207 } 208 } 209 DONE: 210 fLength = distance; 211 fIsClosed = isClosed; 212 fFirstPtIndex = ptIndex + 1; 213 214 #ifdef SK_DEBUG 215 { 216 const Segment* seg = fSegments.begin(); 217 const Segment* stop = fSegments.end(); 218 unsigned ptIndex = 0; 219 SkScalar distance = 0; 220 221 while (seg < stop) { 222 SkASSERT(seg->fDistance > distance); 223 SkASSERT(seg->fPtIndex >= ptIndex); 224 SkASSERT(seg->fTValue > 0); 225 226 const Segment* s = seg; 227 while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex) { 228 SkASSERT(s[0].fType == s[1].fType); 229 SkASSERT(s[0].fTValue < s[1].fTValue); 230 s += 1; 231 } 232 233 distance = seg->fDistance; 234 ptIndex = seg->fPtIndex; 235 seg += 1; 236 } 237 // SkDebugf("\n"); 238 } 239 #endif 240 } 241 242 // marked as a friend in SkPath.h 243 const SkPoint* sk_get_path_points(const SkPath& path, int index) { 244 return &path.fPts[index]; 245 } 246 247 static void compute_pos_tan(const SkPath& path, int firstPtIndex, int ptIndex, 248 int segType, SkScalar t, SkPoint* pos, SkVector* tangent) { 249 const SkPoint* pts = sk_get_path_points(path, ptIndex); 250 251 switch (segType) { 252 case kLine_SegType: 253 case kCloseLine_SegType: { 254 const SkPoint* endp = (segType == kLine_SegType) ? 255 &pts[1] : 256 sk_get_path_points(path, firstPtIndex); 257 258 if (pos) { 259 pos->set(SkScalarInterp(pts[0].fX, endp->fX, t), 260 SkScalarInterp(pts[0].fY, endp->fY, t)); 261 } 262 if (tangent) { 263 tangent->setNormalize(endp->fX - pts[0].fX, endp->fY - pts[0].fY); 264 } 265 break; 266 } 267 case kQuad_SegType: 268 SkEvalQuadAt(pts, t, pos, tangent); 269 if (tangent) { 270 tangent->normalize(); 271 } 272 break; 273 case kCubic_SegType: 274 SkEvalCubicAt(pts, t, pos, tangent, NULL); 275 if (tangent) { 276 tangent->normalize(); 277 } 278 break; 279 default: 280 SkASSERT(!"unknown segType"); 281 } 282 } 283 284 static void seg_to(const SkPath& src, int firstPtIndex, int ptIndex, 285 int segType, SkScalar startT, SkScalar stopT, SkPath* dst) { 286 SkASSERT(startT >= 0 && startT <= SK_Scalar1); 287 SkASSERT(stopT >= 0 && stopT <= SK_Scalar1); 288 SkASSERT(startT <= stopT); 289 290 if (SkScalarNearlyZero(stopT - startT)) { 291 return; 292 } 293 294 const SkPoint* pts = sk_get_path_points(src, ptIndex); 295 SkPoint tmp0[7], tmp1[7]; 296 297 switch (segType) { 298 case kLine_SegType: 299 case kCloseLine_SegType: { 300 const SkPoint* endp = (segType == kLine_SegType) ? 301 &pts[1] : 302 sk_get_path_points(src, firstPtIndex); 303 304 if (stopT == kMaxTValue) { 305 dst->lineTo(*endp); 306 } else { 307 dst->lineTo(SkScalarInterp(pts[0].fX, endp->fX, stopT), 308 SkScalarInterp(pts[0].fY, endp->fY, stopT)); 309 } 310 break; 311 } 312 case kQuad_SegType: 313 if (startT == 0) { 314 if (stopT == SK_Scalar1) { 315 dst->quadTo(pts[1], pts[2]); 316 } else { 317 SkChopQuadAt(pts, tmp0, stopT); 318 dst->quadTo(tmp0[1], tmp0[2]); 319 } 320 } else { 321 SkChopQuadAt(pts, tmp0, startT); 322 if (stopT == SK_Scalar1) { 323 dst->quadTo(tmp0[3], tmp0[4]); 324 } else { 325 SkChopQuadAt(&tmp0[2], tmp1, SkScalarDiv(stopT - startT, 326 SK_Scalar1 - startT)); 327 dst->quadTo(tmp1[1], tmp1[2]); 328 } 329 } 330 break; 331 case kCubic_SegType: 332 if (startT == 0) { 333 if (stopT == SK_Scalar1) { 334 dst->cubicTo(pts[1], pts[2], pts[3]); 335 } else { 336 SkChopCubicAt(pts, tmp0, stopT); 337 dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]); 338 } 339 } else { 340 SkChopCubicAt(pts, tmp0, startT); 341 if (stopT == SK_Scalar1) { 342 dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]); 343 } else { 344 SkChopCubicAt(&tmp0[3], tmp1, SkScalarDiv(stopT - startT, 345 SK_Scalar1 - startT)); 346 dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]); 347 } 348 } 349 break; 350 default: 351 SkASSERT(!"unknown segType"); 352 sk_throw(); 353 } 354 } 355 356 //////////////////////////////////////////////////////////////////////////////// 357 //////////////////////////////////////////////////////////////////////////////// 358 359 SkPathMeasure::SkPathMeasure() { 360 fPath = NULL; 361 fLength = -1; // signal we need to compute it 362 fForceClosed = false; 363 fFirstPtIndex = -1; 364 } 365 366 SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) { 367 fPath = &path; 368 fLength = -1; // signal we need to compute it 369 fForceClosed = forceClosed; 370 fFirstPtIndex = -1; 371 372 fIter.setPath(path, forceClosed); 373 } 374 375 SkPathMeasure::~SkPathMeasure() {} 376 377 /** Assign a new path, or null to have none. 378 */ 379 void SkPathMeasure::setPath(const SkPath* path, bool forceClosed) { 380 fPath = path; 381 fLength = -1; // signal we need to compute it 382 fForceClosed = forceClosed; 383 fFirstPtIndex = -1; 384 385 if (path) { 386 fIter.setPath(*path, forceClosed); 387 } 388 fSegments.reset(); 389 } 390 391 SkScalar SkPathMeasure::getLength() { 392 if (fPath == NULL) { 393 return 0; 394 } 395 if (fLength < 0) { 396 this->buildSegments(); 397 } 398 SkASSERT(fLength >= 0); 399 return fLength; 400 } 401 402 const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment( 403 SkScalar distance, SkScalar* t) { 404 SkDEBUGCODE(SkScalar length = ) this->getLength(); 405 SkASSERT(distance >= 0 && distance <= length); 406 407 const Segment* seg = fSegments.begin(); 408 int count = fSegments.count(); 409 410 int index = SkTSearch<SkScalar>(&seg->fDistance, count, distance, 411 sizeof(Segment)); 412 // don't care if we hit an exact match or not, so we xor index if it is negative 413 index ^= (index >> 31); 414 seg = &seg[index]; 415 416 // now interpolate t-values with the prev segment (if possible) 417 SkScalar startT = 0, startD = 0; 418 // check if the prev segment is legal, and references the same set of points 419 if (index > 0) { 420 startD = seg[-1].fDistance; 421 if (seg[-1].fPtIndex == seg->fPtIndex) { 422 SkASSERT(seg[-1].fType == seg->fType); 423 startT = seg[-1].getScalarT(); 424 } 425 } 426 427 SkASSERT(seg->getScalarT() > startT); 428 SkASSERT(distance >= startD); 429 SkASSERT(seg->fDistance > startD); 430 431 *t = startT + SkScalarMulDiv(seg->getScalarT() - startT, 432 distance - startD, 433 seg->fDistance - startD); 434 return seg; 435 } 436 437 bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos, 438 SkVector* tangent) { 439 SkASSERT(fPath); 440 if (fPath == NULL) { 441 EMPTY: 442 return false; 443 } 444 445 SkScalar length = this->getLength(); // call this to force computing it 446 int count = fSegments.count(); 447 448 if (count == 0 || length == 0) { 449 goto EMPTY; 450 } 451 452 // pin the distance to a legal range 453 if (distance < 0) { 454 distance = 0; 455 } else if (distance > length) { 456 distance = length; 457 } 458 459 SkScalar t; 460 const Segment* seg = this->distanceToSegment(distance, &t); 461 462 compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, 463 t, pos, tangent); 464 return true; 465 } 466 467 bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix, 468 MatrixFlags flags) { 469 SkPoint position; 470 SkVector tangent; 471 472 if (this->getPosTan(distance, &position, &tangent)) { 473 if (matrix) { 474 if (flags & kGetTangent_MatrixFlag) { 475 matrix->setSinCos(tangent.fY, tangent.fX, 0, 0); 476 } else { 477 matrix->reset(); 478 } 479 if (flags & kGetPosition_MatrixFlag) { 480 matrix->postTranslate(position.fX, position.fY); 481 } 482 } 483 return true; 484 } 485 return false; 486 } 487 488 bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, 489 bool startWithMoveTo) { 490 SkASSERT(dst); 491 492 SkScalar length = this->getLength(); // ensure we have built our segments 493 494 if (startD < 0) { 495 startD = 0; 496 } 497 if (stopD > length) { 498 stopD = length; 499 } 500 if (startD >= stopD) { 501 return false; 502 } 503 504 SkPoint p; 505 SkScalar startT, stopT; 506 const Segment* seg = this->distanceToSegment(startD, &startT); 507 const Segment* stopSeg = this->distanceToSegment(stopD, &stopT); 508 SkASSERT(seg <= stopSeg); 509 510 if (startWithMoveTo) { 511 compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, 512 seg->fType, startT, &p, NULL); 513 dst->moveTo(p); 514 } 515 516 if (seg->fPtIndex == stopSeg->fPtIndex) { 517 seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, 518 startT, stopT, dst); 519 } else { 520 do { 521 seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, 522 startT, SK_Scalar1, dst); 523 seg = SkPathMeasure::NextSegment(seg); 524 startT = 0; 525 } while (seg->fPtIndex < stopSeg->fPtIndex); 526 seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, 527 0, stopT, dst); 528 } 529 return true; 530 } 531 532 bool SkPathMeasure::isClosed() { 533 (void)this->getLength(); 534 return fIsClosed; 535 } 536 537 /** Move to the next contour in the path. Return true if one exists, or false if 538 we're done with the path. 539 */ 540 bool SkPathMeasure::nextContour() { 541 fLength = -1; 542 return this->getLength() > 0; 543 } 544 545 /////////////////////////////////////////////////////////////////////////////// 546 /////////////////////////////////////////////////////////////////////////////// 547 548 #ifdef SK_DEBUG 549 550 void SkPathMeasure::dump() { 551 SkDebugf("pathmeas: length=%g, segs=%d\n", fLength, fSegments.count()); 552 553 for (int i = 0; i < fSegments.count(); i++) { 554 const Segment* seg = &fSegments[i]; 555 SkDebugf("pathmeas: seg[%d] distance=%g, point=%d, t=%g, type=%d\n", 556 i, seg->fDistance, seg->fPtIndex, seg->getScalarT(), 557 seg->fType); 558 } 559 } 560 561 #endif 562