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