gfx/skia/trunk/src/core/SkPath.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2006 The Android Open Source Project
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9
michael@0 10 #include "SkBuffer.h"
michael@0 11 #include "SkErrorInternals.h"
michael@0 12 #include "SkMath.h"
michael@0 13 #include "SkPath.h"
michael@0 14 #include "SkPathRef.h"
michael@0 15 #include "SkRRect.h"
michael@0 16 #include "SkThread.h"
michael@0 17
michael@0 18 ////////////////////////////////////////////////////////////////////////////
michael@0 19
michael@0 20 /**
michael@0 21 * Path.bounds is defined to be the bounds of all the control points.
michael@0 22 * If we called bounds.join(r) we would skip r if r was empty, which breaks
michael@0 23 * our promise. Hence we have a custom joiner that doesn't look at emptiness
michael@0 24 */
michael@0 25 static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
michael@0 26 dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
michael@0 27 dst->fTop = SkMinScalar(dst->fTop, src.fTop);
michael@0 28 dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
michael@0 29 dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
michael@0 30 }
michael@0 31
michael@0 32 static bool is_degenerate(const SkPath& path) {
michael@0 33 SkPath::Iter iter(path, false);
michael@0 34 SkPoint pts[4];
michael@0 35 return SkPath::kDone_Verb == iter.next(pts);
michael@0 36 }
michael@0 37
michael@0 38 class SkAutoDisableDirectionCheck {
michael@0 39 public:
michael@0 40 SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
michael@0 41 fSaved = static_cast<SkPath::Direction>(fPath->fDirection);
michael@0 42 }
michael@0 43
michael@0 44 ~SkAutoDisableDirectionCheck() {
michael@0 45 fPath->fDirection = fSaved;
michael@0 46 }
michael@0 47
michael@0 48 private:
michael@0 49 SkPath* fPath;
michael@0 50 SkPath::Direction fSaved;
michael@0 51 };
michael@0 52 #define SkAutoDisableDirectionCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableDirectionCheck)
michael@0 53
michael@0 54 /* This guy's constructor/destructor bracket a path editing operation. It is
michael@0 55 used when we know the bounds of the amount we are going to add to the path
michael@0 56 (usually a new contour, but not required).
michael@0 57
michael@0 58 It captures some state about the path up front (i.e. if it already has a
michael@0 59 cached bounds), and then if it can, it updates the cache bounds explicitly,
michael@0 60 avoiding the need to revisit all of the points in getBounds().
michael@0 61
michael@0 62 It also notes if the path was originally degenerate, and if so, sets
michael@0 63 isConvex to true. Thus it can only be used if the contour being added is
michael@0 64 convex.
michael@0 65 */
michael@0 66 class SkAutoPathBoundsUpdate {
michael@0 67 public:
michael@0 68 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
michael@0 69 this->init(path);
michael@0 70 }
michael@0 71
michael@0 72 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
michael@0 73 SkScalar right, SkScalar bottom) {
michael@0 74 fRect.set(left, top, right, bottom);
michael@0 75 this->init(path);
michael@0 76 }
michael@0 77
michael@0 78 ~SkAutoPathBoundsUpdate() {
michael@0 79 fPath->setConvexity(fDegenerate ? SkPath::kConvex_Convexity
michael@0 80 : SkPath::kUnknown_Convexity);
michael@0 81 if (fEmpty || fHasValidBounds) {
michael@0 82 fPath->setBounds(fRect);
michael@0 83 }
michael@0 84 }
michael@0 85
michael@0 86 private:
michael@0 87 SkPath* fPath;
michael@0 88 SkRect fRect;
michael@0 89 bool fHasValidBounds;
michael@0 90 bool fDegenerate;
michael@0 91 bool fEmpty;
michael@0 92
michael@0 93 void init(SkPath* path) {
michael@0 94 // Cannot use fRect for our bounds unless we know it is sorted
michael@0 95 fRect.sort();
michael@0 96 fPath = path;
michael@0 97 // Mark the path's bounds as dirty if (1) they are, or (2) the path
michael@0 98 // is non-finite, and therefore its bounds are not meaningful
michael@0 99 fHasValidBounds = path->hasComputedBounds() && path->isFinite();
michael@0 100 fEmpty = path->isEmpty();
michael@0 101 if (fHasValidBounds && !fEmpty) {
michael@0 102 joinNoEmptyChecks(&fRect, fPath->getBounds());
michael@0 103 }
michael@0 104 fDegenerate = is_degenerate(*path);
michael@0 105 }
michael@0 106 };
michael@0 107 #define SkAutoPathBoundsUpdate(...) SK_REQUIRE_LOCAL_VAR(SkAutoPathBoundsUpdate)
michael@0 108
michael@0 109 ////////////////////////////////////////////////////////////////////////////
michael@0 110
michael@0 111 /*
michael@0 112 Stores the verbs and points as they are given to us, with exceptions:
michael@0 113 - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
michael@0 114 - we insert a Move(0,0) if Line | Quad | Cubic is our first command
michael@0 115
michael@0 116 The iterator does more cleanup, especially if forceClose == true
michael@0 117 1. If we encounter degenerate segments, remove them
michael@0 118 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
michael@0 119 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
michael@0 120 4. if we encounter Line | Quad | Cubic after Close, cons up a Move
michael@0 121 */
michael@0 122
michael@0 123 ////////////////////////////////////////////////////////////////////////////
michael@0 124
michael@0 125 // flag to require a moveTo if we begin with something else, like lineTo etc.
michael@0 126 #define INITIAL_LASTMOVETOINDEX_VALUE ~0
michael@0 127
michael@0 128 SkPath::SkPath()
michael@0 129 : fPathRef(SkPathRef::CreateEmpty())
michael@0 130 #ifdef SK_BUILD_FOR_ANDROID
michael@0 131 , fSourcePath(NULL)
michael@0 132 #endif
michael@0 133 {
michael@0 134 this->resetFields();
michael@0 135 }
michael@0 136
michael@0 137 void SkPath::resetFields() {
michael@0 138 //fPathRef is assumed to have been emptied by the caller.
michael@0 139 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
michael@0 140 fFillType = kWinding_FillType;
michael@0 141 fConvexity = kUnknown_Convexity;
michael@0 142 fDirection = kUnknown_Direction;
michael@0 143
michael@0 144 // We don't touch Android's fSourcePath. It's used to track texture garbage collection, so we
michael@0 145 // don't want to muck with it if it's been set to something non-NULL.
michael@0 146 }
michael@0 147
michael@0 148 SkPath::SkPath(const SkPath& that)
michael@0 149 : fPathRef(SkRef(that.fPathRef.get())) {
michael@0 150 this->copyFields(that);
michael@0 151 #ifdef SK_BUILD_FOR_ANDROID
michael@0 152 fSourcePath = that.fSourcePath;
michael@0 153 #endif
michael@0 154 SkDEBUGCODE(that.validate();)
michael@0 155 }
michael@0 156
michael@0 157 SkPath::~SkPath() {
michael@0 158 SkDEBUGCODE(this->validate();)
michael@0 159 }
michael@0 160
michael@0 161 SkPath& SkPath::operator=(const SkPath& that) {
michael@0 162 SkDEBUGCODE(that.validate();)
michael@0 163
michael@0 164 if (this != &that) {
michael@0 165 fPathRef.reset(SkRef(that.fPathRef.get()));
michael@0 166 this->copyFields(that);
michael@0 167 #ifdef SK_BUILD_FOR_ANDROID
michael@0 168 fSourcePath = that.fSourcePath;
michael@0 169 #endif
michael@0 170 }
michael@0 171 SkDEBUGCODE(this->validate();)
michael@0 172 return *this;
michael@0 173 }
michael@0 174
michael@0 175 void SkPath::copyFields(const SkPath& that) {
michael@0 176 //fPathRef is assumed to have been set by the caller.
michael@0 177 fLastMoveToIndex = that.fLastMoveToIndex;
michael@0 178 fFillType = that.fFillType;
michael@0 179 fConvexity = that.fConvexity;
michael@0 180 fDirection = that.fDirection;
michael@0 181 }
michael@0 182
michael@0 183 bool operator==(const SkPath& a, const SkPath& b) {
michael@0 184 // note: don't need to look at isConvex or bounds, since just comparing the
michael@0 185 // raw data is sufficient.
michael@0 186 return &a == &b ||
michael@0 187 (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get());
michael@0 188 }
michael@0 189
michael@0 190 void SkPath::swap(SkPath& that) {
michael@0 191 SkASSERT(&that != NULL);
michael@0 192
michael@0 193 if (this != &that) {
michael@0 194 fPathRef.swap(&that.fPathRef);
michael@0 195 SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex);
michael@0 196 SkTSwap<uint8_t>(fFillType, that.fFillType);
michael@0 197 SkTSwap<uint8_t>(fConvexity, that.fConvexity);
michael@0 198 SkTSwap<uint8_t>(fDirection, that.fDirection);
michael@0 199 #ifdef SK_BUILD_FOR_ANDROID
michael@0 200 SkTSwap<const SkPath*>(fSourcePath, that.fSourcePath);
michael@0 201 #endif
michael@0 202 }
michael@0 203 }
michael@0 204
michael@0 205 static inline bool check_edge_against_rect(const SkPoint& p0,
michael@0 206 const SkPoint& p1,
michael@0 207 const SkRect& rect,
michael@0 208 SkPath::Direction dir) {
michael@0 209 const SkPoint* edgeBegin;
michael@0 210 SkVector v;
michael@0 211 if (SkPath::kCW_Direction == dir) {
michael@0 212 v = p1 - p0;
michael@0 213 edgeBegin = &p0;
michael@0 214 } else {
michael@0 215 v = p0 - p1;
michael@0 216 edgeBegin = &p1;
michael@0 217 }
michael@0 218 if (v.fX || v.fY) {
michael@0 219 // check the cross product of v with the vec from edgeBegin to each rect corner
michael@0 220 SkScalar yL = SkScalarMul(v.fY, rect.fLeft - edgeBegin->fX);
michael@0 221 SkScalar xT = SkScalarMul(v.fX, rect.fTop - edgeBegin->fY);
michael@0 222 SkScalar yR = SkScalarMul(v.fY, rect.fRight - edgeBegin->fX);
michael@0 223 SkScalar xB = SkScalarMul(v.fX, rect.fBottom - edgeBegin->fY);
michael@0 224 if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
michael@0 225 return false;
michael@0 226 }
michael@0 227 }
michael@0 228 return true;
michael@0 229 }
michael@0 230
michael@0 231 bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
michael@0 232 // This only handles non-degenerate convex paths currently.
michael@0 233 if (kConvex_Convexity != this->getConvexity()) {
michael@0 234 return false;
michael@0 235 }
michael@0 236
michael@0 237 Direction direction;
michael@0 238 if (!this->cheapComputeDirection(&direction)) {
michael@0 239 return false;
michael@0 240 }
michael@0 241
michael@0 242 SkPoint firstPt;
michael@0 243 SkPoint prevPt;
michael@0 244 RawIter iter(*this);
michael@0 245 SkPath::Verb verb;
michael@0 246 SkPoint pts[4];
michael@0 247 SkDEBUGCODE(int moveCnt = 0;)
michael@0 248 SkDEBUGCODE(int segmentCount = 0;)
michael@0 249 SkDEBUGCODE(int closeCount = 0;)
michael@0 250
michael@0 251 while ((verb = iter.next(pts)) != kDone_Verb) {
michael@0 252 int nextPt = -1;
michael@0 253 switch (verb) {
michael@0 254 case kMove_Verb:
michael@0 255 SkASSERT(!segmentCount && !closeCount);
michael@0 256 SkDEBUGCODE(++moveCnt);
michael@0 257 firstPt = prevPt = pts[0];
michael@0 258 break;
michael@0 259 case kLine_Verb:
michael@0 260 nextPt = 1;
michael@0 261 SkASSERT(moveCnt && !closeCount);
michael@0 262 SkDEBUGCODE(++segmentCount);
michael@0 263 break;
michael@0 264 case kQuad_Verb:
michael@0 265 case kConic_Verb:
michael@0 266 SkASSERT(moveCnt && !closeCount);
michael@0 267 SkDEBUGCODE(++segmentCount);
michael@0 268 nextPt = 2;
michael@0 269 break;
michael@0 270 case kCubic_Verb:
michael@0 271 SkASSERT(moveCnt && !closeCount);
michael@0 272 SkDEBUGCODE(++segmentCount);
michael@0 273 nextPt = 3;
michael@0 274 break;
michael@0 275 case kClose_Verb:
michael@0 276 SkDEBUGCODE(++closeCount;)
michael@0 277 break;
michael@0 278 default:
michael@0 279 SkDEBUGFAIL("unknown verb");
michael@0 280 }
michael@0 281 if (-1 != nextPt) {
michael@0 282 if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) {
michael@0 283 return false;
michael@0 284 }
michael@0 285 prevPt = pts[nextPt];
michael@0 286 }
michael@0 287 }
michael@0 288
michael@0 289 return check_edge_against_rect(prevPt, firstPt, rect, direction);
michael@0 290 }
michael@0 291
michael@0 292 uint32_t SkPath::getGenerationID() const {
michael@0 293 uint32_t genID = fPathRef->genID();
michael@0 294 #ifdef SK_BUILD_FOR_ANDROID
michael@0 295 SkASSERT((unsigned)fFillType < (1 << (32 - kPathRefGenIDBitCnt)));
michael@0 296 genID |= static_cast<uint32_t>(fFillType) << kPathRefGenIDBitCnt;
michael@0 297 #endif
michael@0 298 return genID;
michael@0 299 }
michael@0 300
michael@0 301 #ifdef SK_BUILD_FOR_ANDROID
michael@0 302 const SkPath* SkPath::getSourcePath() const {
michael@0 303 return fSourcePath;
michael@0 304 }
michael@0 305
michael@0 306 void SkPath::setSourcePath(const SkPath* path) {
michael@0 307 fSourcePath = path;
michael@0 308 }
michael@0 309 #endif
michael@0 310
michael@0 311 void SkPath::reset() {
michael@0 312 SkDEBUGCODE(this->validate();)
michael@0 313
michael@0 314 fPathRef.reset(SkPathRef::CreateEmpty());
michael@0 315 this->resetFields();
michael@0 316 }
michael@0 317
michael@0 318 void SkPath::rewind() {
michael@0 319 SkDEBUGCODE(this->validate();)
michael@0 320
michael@0 321 SkPathRef::Rewind(&fPathRef);
michael@0 322 this->resetFields();
michael@0 323 }
michael@0 324
michael@0 325 bool SkPath::isLine(SkPoint line[2]) const {
michael@0 326 int verbCount = fPathRef->countVerbs();
michael@0 327
michael@0 328 if (2 == verbCount) {
michael@0 329 SkASSERT(kMove_Verb == fPathRef->atVerb(0));
michael@0 330 if (kLine_Verb == fPathRef->atVerb(1)) {
michael@0 331 SkASSERT(2 == fPathRef->countPoints());
michael@0 332 if (line) {
michael@0 333 const SkPoint* pts = fPathRef->points();
michael@0 334 line[0] = pts[0];
michael@0 335 line[1] = pts[1];
michael@0 336 }
michael@0 337 return true;
michael@0 338 }
michael@0 339 }
michael@0 340 return false;
michael@0 341 }
michael@0 342
michael@0 343 /*
michael@0 344 Determines if path is a rect by keeping track of changes in direction
michael@0 345 and looking for a loop either clockwise or counterclockwise.
michael@0 346
michael@0 347 The direction is computed such that:
michael@0 348 0: vertical up
michael@0 349 1: horizontal left
michael@0 350 2: vertical down
michael@0 351 3: horizontal right
michael@0 352
michael@0 353 A rectangle cycles up/right/down/left or up/left/down/right.
michael@0 354
michael@0 355 The test fails if:
michael@0 356 The path is closed, and followed by a line.
michael@0 357 A second move creates a new endpoint.
michael@0 358 A diagonal line is parsed.
michael@0 359 There's more than four changes of direction.
michael@0 360 There's a discontinuity on the line (e.g., a move in the middle)
michael@0 361 The line reverses direction.
michael@0 362 The path contains a quadratic or cubic.
michael@0 363 The path contains fewer than four points.
michael@0 364 *The rectangle doesn't complete a cycle.
michael@0 365 *The final point isn't equal to the first point.
michael@0 366
michael@0 367 *These last two conditions we relax if we have a 3-edge path that would
michael@0 368 form a rectangle if it were closed (as we do when we fill a path)
michael@0 369
michael@0 370 It's OK if the path has:
michael@0 371 Several colinear line segments composing a rectangle side.
michael@0 372 Single points on the rectangle side.
michael@0 373
michael@0 374 The direction takes advantage of the corners found since opposite sides
michael@0 375 must travel in opposite directions.
michael@0 376
michael@0 377 FIXME: Allow colinear quads and cubics to be treated like lines.
michael@0 378 FIXME: If the API passes fill-only, return true if the filled stroke
michael@0 379 is a rectangle, though the caller failed to close the path.
michael@0 380
michael@0 381 first,last,next direction state-machine:
michael@0 382 0x1 is set if the segment is horizontal
michael@0 383 0x2 is set if the segment is moving to the right or down
michael@0 384 thus:
michael@0 385 two directions are opposites iff (dirA ^ dirB) == 0x2
michael@0 386 two directions are perpendicular iff (dirA ^ dirB) == 0x1
michael@0 387
michael@0 388 */
michael@0 389 static int rect_make_dir(SkScalar dx, SkScalar dy) {
michael@0 390 return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1);
michael@0 391 }
michael@0 392 bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr,
michael@0 393 bool* isClosed, Direction* direction) const {
michael@0 394 int corners = 0;
michael@0 395 SkPoint first, last;
michael@0 396 const SkPoint* pts = *ptsPtr;
michael@0 397 const SkPoint* savePts = NULL;
michael@0 398 first.set(0, 0);
michael@0 399 last.set(0, 0);
michael@0 400 int firstDirection = 0;
michael@0 401 int lastDirection = 0;
michael@0 402 int nextDirection = 0;
michael@0 403 bool closedOrMoved = false;
michael@0 404 bool autoClose = false;
michael@0 405 int verbCnt = fPathRef->countVerbs();
michael@0 406 while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
michael@0 407 switch (fPathRef->atVerb(*currVerb)) {
michael@0 408 case kClose_Verb:
michael@0 409 savePts = pts;
michael@0 410 pts = *ptsPtr;
michael@0 411 autoClose = true;
michael@0 412 case kLine_Verb: {
michael@0 413 SkScalar left = last.fX;
michael@0 414 SkScalar top = last.fY;
michael@0 415 SkScalar right = pts->fX;
michael@0 416 SkScalar bottom = pts->fY;
michael@0 417 ++pts;
michael@0 418 if (left != right && top != bottom) {
michael@0 419 return false; // diagonal
michael@0 420 }
michael@0 421 if (left == right && top == bottom) {
michael@0 422 break; // single point on side OK
michael@0 423 }
michael@0 424 nextDirection = rect_make_dir(right - left, bottom - top);
michael@0 425 if (0 == corners) {
michael@0 426 firstDirection = nextDirection;
michael@0 427 first = last;
michael@0 428 last = pts[-1];
michael@0 429 corners = 1;
michael@0 430 closedOrMoved = false;
michael@0 431 break;
michael@0 432 }
michael@0 433 if (closedOrMoved) {
michael@0 434 return false; // closed followed by a line
michael@0 435 }
michael@0 436 if (autoClose && nextDirection == firstDirection) {
michael@0 437 break; // colinear with first
michael@0 438 }
michael@0 439 closedOrMoved = autoClose;
michael@0 440 if (lastDirection != nextDirection) {
michael@0 441 if (++corners > 4) {
michael@0 442 return false; // too many direction changes
michael@0 443 }
michael@0 444 }
michael@0 445 last = pts[-1];
michael@0 446 if (lastDirection == nextDirection) {
michael@0 447 break; // colinear segment
michael@0 448 }
michael@0 449 // Possible values for corners are 2, 3, and 4.
michael@0 450 // When corners == 3, nextDirection opposes firstDirection.
michael@0 451 // Otherwise, nextDirection at corner 2 opposes corner 4.
michael@0 452 int turn = firstDirection ^ (corners - 1);
michael@0 453 int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
michael@0 454 if ((directionCycle ^ turn) != nextDirection) {
michael@0 455 return false; // direction didn't follow cycle
michael@0 456 }
michael@0 457 break;
michael@0 458 }
michael@0 459 case kQuad_Verb:
michael@0 460 case kConic_Verb:
michael@0 461 case kCubic_Verb:
michael@0 462 return false; // quadratic, cubic not allowed
michael@0 463 case kMove_Verb:
michael@0 464 last = *pts++;
michael@0 465 closedOrMoved = true;
michael@0 466 break;
michael@0 467 default:
michael@0 468 SkDEBUGFAIL("unexpected verb");
michael@0 469 break;
michael@0 470 }
michael@0 471 *currVerb += 1;
michael@0 472 lastDirection = nextDirection;
michael@0 473 }
michael@0 474 // Success if 4 corners and first point equals last
michael@0 475 bool result = 4 == corners && (first == last || autoClose);
michael@0 476 if (!result) {
michael@0 477 // check if we are just an incomplete rectangle, in which case we can
michael@0 478 // return true, but not claim to be closed.
michael@0 479 // e.g.
michael@0 480 // 3 sided rectangle
michael@0 481 // 4 sided but the last edge is not long enough to reach the start
michael@0 482 //
michael@0 483 SkScalar closeX = first.x() - last.x();
michael@0 484 SkScalar closeY = first.y() - last.y();
michael@0 485 if (closeX && closeY) {
michael@0 486 return false; // we're diagonal, abort (can we ever reach this?)
michael@0 487 }
michael@0 488 int closeDirection = rect_make_dir(closeX, closeY);
michael@0 489 // make sure the close-segment doesn't double-back on itself
michael@0 490 if (3 == corners || (4 == corners && closeDirection == lastDirection)) {
michael@0 491 result = true;
michael@0 492 autoClose = false; // we are not closed
michael@0 493 }
michael@0 494 }
michael@0 495 if (savePts) {
michael@0 496 *ptsPtr = savePts;
michael@0 497 }
michael@0 498 if (result && isClosed) {
michael@0 499 *isClosed = autoClose;
michael@0 500 }
michael@0 501 if (result && direction) {
michael@0 502 *direction = firstDirection == ((lastDirection + 1) & 3) ? kCCW_Direction : kCW_Direction;
michael@0 503 }
michael@0 504 return result;
michael@0 505 }
michael@0 506
michael@0 507 SkPath::PathAsRect SkPath::asRect(Direction* direction) const {
michael@0 508 SK_COMPILE_ASSERT(0 == kNone_PathAsRect, path_as_rect_mismatch);
michael@0 509 SK_COMPILE_ASSERT(1 == kFill_PathAsRect, path_as_rect_mismatch);
michael@0 510 SK_COMPILE_ASSERT(2 == kStroke_PathAsRect, path_as_rect_mismatch);
michael@0 511 bool isClosed = false;
michael@0 512 return (PathAsRect) (isRect(&isClosed, direction) + isClosed);
michael@0 513 }
michael@0 514
michael@0 515 bool SkPath::isRect(SkRect* rect) const {
michael@0 516 SkDEBUGCODE(this->validate();)
michael@0 517 int currVerb = 0;
michael@0 518 const SkPoint* pts = fPathRef->points();
michael@0 519 bool result = isRectContour(false, &currVerb, &pts, NULL, NULL);
michael@0 520 if (result && rect) {
michael@0 521 *rect = getBounds();
michael@0 522 }
michael@0 523 return result;
michael@0 524 }
michael@0 525
michael@0 526 bool SkPath::isRect(bool* isClosed, Direction* direction) const {
michael@0 527 SkDEBUGCODE(this->validate();)
michael@0 528 int currVerb = 0;
michael@0 529 const SkPoint* pts = fPathRef->points();
michael@0 530 return isRectContour(false, &currVerb, &pts, isClosed, direction);
michael@0 531 }
michael@0 532
michael@0 533 bool SkPath::isNestedRects(SkRect rects[2], Direction dirs[2]) const {
michael@0 534 SkDEBUGCODE(this->validate();)
michael@0 535 int currVerb = 0;
michael@0 536 const SkPoint* pts = fPathRef->points();
michael@0 537 const SkPoint* first = pts;
michael@0 538 Direction testDirs[2];
michael@0 539 if (!isRectContour(true, &currVerb, &pts, NULL, &testDirs[0])) {
michael@0 540 return false;
michael@0 541 }
michael@0 542 const SkPoint* last = pts;
michael@0 543 SkRect testRects[2];
michael@0 544 if (isRectContour(false, &currVerb, &pts, NULL, &testDirs[1])) {
michael@0 545 testRects[0].set(first, SkToS32(last - first));
michael@0 546 testRects[1].set(last, SkToS32(pts - last));
michael@0 547 if (testRects[0].contains(testRects[1])) {
michael@0 548 if (rects) {
michael@0 549 rects[0] = testRects[0];
michael@0 550 rects[1] = testRects[1];
michael@0 551 }
michael@0 552 if (dirs) {
michael@0 553 dirs[0] = testDirs[0];
michael@0 554 dirs[1] = testDirs[1];
michael@0 555 }
michael@0 556 return true;
michael@0 557 }
michael@0 558 if (testRects[1].contains(testRects[0])) {
michael@0 559 if (rects) {
michael@0 560 rects[0] = testRects[1];
michael@0 561 rects[1] = testRects[0];
michael@0 562 }
michael@0 563 if (dirs) {
michael@0 564 dirs[0] = testDirs[1];
michael@0 565 dirs[1] = testDirs[0];
michael@0 566 }
michael@0 567 return true;
michael@0 568 }
michael@0 569 }
michael@0 570 return false;
michael@0 571 }
michael@0 572
michael@0 573 int SkPath::countPoints() const {
michael@0 574 return fPathRef->countPoints();
michael@0 575 }
michael@0 576
michael@0 577 int SkPath::getPoints(SkPoint dst[], int max) const {
michael@0 578 SkDEBUGCODE(this->validate();)
michael@0 579
michael@0 580 SkASSERT(max >= 0);
michael@0 581 SkASSERT(!max || dst);
michael@0 582 int count = SkMin32(max, fPathRef->countPoints());
michael@0 583 memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
michael@0 584 return fPathRef->countPoints();
michael@0 585 }
michael@0 586
michael@0 587 SkPoint SkPath::getPoint(int index) const {
michael@0 588 if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
michael@0 589 return fPathRef->atPoint(index);
michael@0 590 }
michael@0 591 return SkPoint::Make(0, 0);
michael@0 592 }
michael@0 593
michael@0 594 int SkPath::countVerbs() const {
michael@0 595 return fPathRef->countVerbs();
michael@0 596 }
michael@0 597
michael@0 598 static inline void copy_verbs_reverse(uint8_t* inorderDst,
michael@0 599 const uint8_t* reversedSrc,
michael@0 600 int count) {
michael@0 601 for (int i = 0; i < count; ++i) {
michael@0 602 inorderDst[i] = reversedSrc[~i];
michael@0 603 }
michael@0 604 }
michael@0 605
michael@0 606 int SkPath::getVerbs(uint8_t dst[], int max) const {
michael@0 607 SkDEBUGCODE(this->validate();)
michael@0 608
michael@0 609 SkASSERT(max >= 0);
michael@0 610 SkASSERT(!max || dst);
michael@0 611 int count = SkMin32(max, fPathRef->countVerbs());
michael@0 612 copy_verbs_reverse(dst, fPathRef->verbs(), count);
michael@0 613 return fPathRef->countVerbs();
michael@0 614 }
michael@0 615
michael@0 616 bool SkPath::getLastPt(SkPoint* lastPt) const {
michael@0 617 SkDEBUGCODE(this->validate();)
michael@0 618
michael@0 619 int count = fPathRef->countPoints();
michael@0 620 if (count > 0) {
michael@0 621 if (lastPt) {
michael@0 622 *lastPt = fPathRef->atPoint(count - 1);
michael@0 623 }
michael@0 624 return true;
michael@0 625 }
michael@0 626 if (lastPt) {
michael@0 627 lastPt->set(0, 0);
michael@0 628 }
michael@0 629 return false;
michael@0 630 }
michael@0 631
michael@0 632 void SkPath::setLastPt(SkScalar x, SkScalar y) {
michael@0 633 SkDEBUGCODE(this->validate();)
michael@0 634
michael@0 635 int count = fPathRef->countPoints();
michael@0 636 if (count == 0) {
michael@0 637 this->moveTo(x, y);
michael@0 638 } else {
michael@0 639 SkPathRef::Editor ed(&fPathRef);
michael@0 640 ed.atPoint(count-1)->set(x, y);
michael@0 641 }
michael@0 642 }
michael@0 643
michael@0 644 void SkPath::setConvexity(Convexity c) {
michael@0 645 if (fConvexity != c) {
michael@0 646 fConvexity = c;
michael@0 647 }
michael@0 648 }
michael@0 649
michael@0 650 //////////////////////////////////////////////////////////////////////////////
michael@0 651 // Construction methods
michael@0 652
michael@0 653 #define DIRTY_AFTER_EDIT \
michael@0 654 do { \
michael@0 655 fConvexity = kUnknown_Convexity; \
michael@0 656 fDirection = kUnknown_Direction; \
michael@0 657 } while (0)
michael@0 658
michael@0 659 void SkPath::incReserve(U16CPU inc) {
michael@0 660 SkDEBUGCODE(this->validate();)
michael@0 661 SkPathRef::Editor(&fPathRef, inc, inc);
michael@0 662 SkDEBUGCODE(this->validate();)
michael@0 663 }
michael@0 664
michael@0 665 void SkPath::moveTo(SkScalar x, SkScalar y) {
michael@0 666 SkDEBUGCODE(this->validate();)
michael@0 667
michael@0 668 SkPathRef::Editor ed(&fPathRef);
michael@0 669
michael@0 670 // remember our index
michael@0 671 fLastMoveToIndex = fPathRef->countPoints();
michael@0 672
michael@0 673 ed.growForVerb(kMove_Verb)->set(x, y);
michael@0 674 }
michael@0 675
michael@0 676 void SkPath::rMoveTo(SkScalar x, SkScalar y) {
michael@0 677 SkPoint pt;
michael@0 678 this->getLastPt(&pt);
michael@0 679 this->moveTo(pt.fX + x, pt.fY + y);
michael@0 680 }
michael@0 681
michael@0 682 void SkPath::injectMoveToIfNeeded() {
michael@0 683 if (fLastMoveToIndex < 0) {
michael@0 684 SkScalar x, y;
michael@0 685 if (fPathRef->countVerbs() == 0) {
michael@0 686 x = y = 0;
michael@0 687 } else {
michael@0 688 const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
michael@0 689 x = pt.fX;
michael@0 690 y = pt.fY;
michael@0 691 }
michael@0 692 this->moveTo(x, y);
michael@0 693 }
michael@0 694 }
michael@0 695
michael@0 696 void SkPath::lineTo(SkScalar x, SkScalar y) {
michael@0 697 SkDEBUGCODE(this->validate();)
michael@0 698
michael@0 699 this->injectMoveToIfNeeded();
michael@0 700
michael@0 701 SkPathRef::Editor ed(&fPathRef);
michael@0 702 ed.growForVerb(kLine_Verb)->set(x, y);
michael@0 703
michael@0 704 DIRTY_AFTER_EDIT;
michael@0 705 }
michael@0 706
michael@0 707 void SkPath::rLineTo(SkScalar x, SkScalar y) {
michael@0 708 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
michael@0 709 SkPoint pt;
michael@0 710 this->getLastPt(&pt);
michael@0 711 this->lineTo(pt.fX + x, pt.fY + y);
michael@0 712 }
michael@0 713
michael@0 714 void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
michael@0 715 SkDEBUGCODE(this->validate();)
michael@0 716
michael@0 717 this->injectMoveToIfNeeded();
michael@0 718
michael@0 719 SkPathRef::Editor ed(&fPathRef);
michael@0 720 SkPoint* pts = ed.growForVerb(kQuad_Verb);
michael@0 721 pts[0].set(x1, y1);
michael@0 722 pts[1].set(x2, y2);
michael@0 723
michael@0 724 DIRTY_AFTER_EDIT;
michael@0 725 }
michael@0 726
michael@0 727 void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
michael@0 728 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
michael@0 729 SkPoint pt;
michael@0 730 this->getLastPt(&pt);
michael@0 731 this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
michael@0 732 }
michael@0 733
michael@0 734 void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
michael@0 735 SkScalar w) {
michael@0 736 // check for <= 0 or NaN with this test
michael@0 737 if (!(w > 0)) {
michael@0 738 this->lineTo(x2, y2);
michael@0 739 } else if (!SkScalarIsFinite(w)) {
michael@0 740 this->lineTo(x1, y1);
michael@0 741 this->lineTo(x2, y2);
michael@0 742 } else if (SK_Scalar1 == w) {
michael@0 743 this->quadTo(x1, y1, x2, y2);
michael@0 744 } else {
michael@0 745 SkDEBUGCODE(this->validate();)
michael@0 746
michael@0 747 this->injectMoveToIfNeeded();
michael@0 748
michael@0 749 SkPathRef::Editor ed(&fPathRef);
michael@0 750 SkPoint* pts = ed.growForVerb(kConic_Verb, w);
michael@0 751 pts[0].set(x1, y1);
michael@0 752 pts[1].set(x2, y2);
michael@0 753
michael@0 754 DIRTY_AFTER_EDIT;
michael@0 755 }
michael@0 756 }
michael@0 757
michael@0 758 void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
michael@0 759 SkScalar w) {
michael@0 760 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
michael@0 761 SkPoint pt;
michael@0 762 this->getLastPt(&pt);
michael@0 763 this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
michael@0 764 }
michael@0 765
michael@0 766 void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
michael@0 767 SkScalar x3, SkScalar y3) {
michael@0 768 SkDEBUGCODE(this->validate();)
michael@0 769
michael@0 770 this->injectMoveToIfNeeded();
michael@0 771
michael@0 772 SkPathRef::Editor ed(&fPathRef);
michael@0 773 SkPoint* pts = ed.growForVerb(kCubic_Verb);
michael@0 774 pts[0].set(x1, y1);
michael@0 775 pts[1].set(x2, y2);
michael@0 776 pts[2].set(x3, y3);
michael@0 777
michael@0 778 DIRTY_AFTER_EDIT;
michael@0 779 }
michael@0 780
michael@0 781 void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
michael@0 782 SkScalar x3, SkScalar y3) {
michael@0 783 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
michael@0 784 SkPoint pt;
michael@0 785 this->getLastPt(&pt);
michael@0 786 this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
michael@0 787 pt.fX + x3, pt.fY + y3);
michael@0 788 }
michael@0 789
michael@0 790 void SkPath::close() {
michael@0 791 SkDEBUGCODE(this->validate();)
michael@0 792
michael@0 793 int count = fPathRef->countVerbs();
michael@0 794 if (count > 0) {
michael@0 795 switch (fPathRef->atVerb(count - 1)) {
michael@0 796 case kLine_Verb:
michael@0 797 case kQuad_Verb:
michael@0 798 case kConic_Verb:
michael@0 799 case kCubic_Verb:
michael@0 800 case kMove_Verb: {
michael@0 801 SkPathRef::Editor ed(&fPathRef);
michael@0 802 ed.growForVerb(kClose_Verb);
michael@0 803 break;
michael@0 804 }
michael@0 805 case kClose_Verb:
michael@0 806 // don't add a close if it's the first verb or a repeat
michael@0 807 break;
michael@0 808 default:
michael@0 809 SkDEBUGFAIL("unexpected verb");
michael@0 810 break;
michael@0 811 }
michael@0 812 }
michael@0 813
michael@0 814 // signal that we need a moveTo to follow us (unless we're done)
michael@0 815 #if 0
michael@0 816 if (fLastMoveToIndex >= 0) {
michael@0 817 fLastMoveToIndex = ~fLastMoveToIndex;
michael@0 818 }
michael@0 819 #else
michael@0 820 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
michael@0 821 #endif
michael@0 822 }
michael@0 823
michael@0 824 ///////////////////////////////////////////////////////////////////////////////
michael@0 825
michael@0 826 static void assert_known_direction(int dir) {
michael@0 827 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
michael@0 828 }
michael@0 829
michael@0 830 void SkPath::addRect(const SkRect& rect, Direction dir) {
michael@0 831 this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir);
michael@0 832 }
michael@0 833
michael@0 834 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
michael@0 835 SkScalar bottom, Direction dir) {
michael@0 836 assert_known_direction(dir);
michael@0 837 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
michael@0 838 SkAutoDisableDirectionCheck addc(this);
michael@0 839
michael@0 840 SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
michael@0 841
michael@0 842 this->incReserve(5);
michael@0 843
michael@0 844 this->moveTo(left, top);
michael@0 845 if (dir == kCCW_Direction) {
michael@0 846 this->lineTo(left, bottom);
michael@0 847 this->lineTo(right, bottom);
michael@0 848 this->lineTo(right, top);
michael@0 849 } else {
michael@0 850 this->lineTo(right, top);
michael@0 851 this->lineTo(right, bottom);
michael@0 852 this->lineTo(left, bottom);
michael@0 853 }
michael@0 854 this->close();
michael@0 855 }
michael@0 856
michael@0 857 void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
michael@0 858 SkDEBUGCODE(this->validate();)
michael@0 859 if (count <= 0) {
michael@0 860 return;
michael@0 861 }
michael@0 862
michael@0 863 fLastMoveToIndex = fPathRef->countPoints();
michael@0 864
michael@0 865 // +close makes room for the extra kClose_Verb
michael@0 866 SkPathRef::Editor ed(&fPathRef, count+close, count);
michael@0 867
michael@0 868 ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
michael@0 869 if (count > 1) {
michael@0 870 SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1);
michael@0 871 memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
michael@0 872 }
michael@0 873
michael@0 874 if (close) {
michael@0 875 ed.growForVerb(kClose_Verb);
michael@0 876 }
michael@0 877
michael@0 878 DIRTY_AFTER_EDIT;
michael@0 879 SkDEBUGCODE(this->validate();)
michael@0 880 }
michael@0 881
michael@0 882 #include "SkGeometry.h"
michael@0 883
michael@0 884 static int build_arc_points(const SkRect& oval, SkScalar startAngle,
michael@0 885 SkScalar sweepAngle,
michael@0 886 SkPoint pts[kSkBuildQuadArcStorage]) {
michael@0 887
michael@0 888 if (0 == sweepAngle &&
michael@0 889 (0 == startAngle || SkIntToScalar(360) == startAngle)) {
michael@0 890 // Chrome uses this path to move into and out of ovals. If not
michael@0 891 // treated as a special case the moves can distort the oval's
michael@0 892 // bounding box (and break the circle special case).
michael@0 893 pts[0].set(oval.fRight, oval.centerY());
michael@0 894 return 1;
michael@0 895 } else if (0 == oval.width() && 0 == oval.height()) {
michael@0 896 // Chrome will sometimes create 0 radius round rects. Having degenerate
michael@0 897 // quad segments in the path prevents the path from being recognized as
michael@0 898 // a rect.
michael@0 899 // TODO: optimizing the case where only one of width or height is zero
michael@0 900 // should also be considered. This case, however, doesn't seem to be
michael@0 901 // as common as the single point case.
michael@0 902 pts[0].set(oval.fRight, oval.fTop);
michael@0 903 return 1;
michael@0 904 }
michael@0 905
michael@0 906 SkVector start, stop;
michael@0 907
michael@0 908 start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
michael@0 909 stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle),
michael@0 910 &stop.fX);
michael@0 911
michael@0 912 /* If the sweep angle is nearly (but less than) 360, then due to precision
michael@0 913 loss in radians-conversion and/or sin/cos, we may end up with coincident
michael@0 914 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
michael@0 915 of drawing a nearly complete circle (good).
michael@0 916 e.g. canvas.drawArc(0, 359.99, ...)
michael@0 917 -vs- canvas.drawArc(0, 359.9, ...)
michael@0 918 We try to detect this edge case, and tweak the stop vector
michael@0 919 */
michael@0 920 if (start == stop) {
michael@0 921 SkScalar sw = SkScalarAbs(sweepAngle);
michael@0 922 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
michael@0 923 SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
michael@0 924 // make a guess at a tiny angle (in radians) to tweak by
michael@0 925 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
michael@0 926 // not sure how much will be enough, so we use a loop
michael@0 927 do {
michael@0 928 stopRad -= deltaRad;
michael@0 929 stop.fY = SkScalarSinCos(stopRad, &stop.fX);
michael@0 930 } while (start == stop);
michael@0 931 }
michael@0 932 }
michael@0 933
michael@0 934 SkMatrix matrix;
michael@0 935
michael@0 936 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
michael@0 937 matrix.postTranslate(oval.centerX(), oval.centerY());
michael@0 938
michael@0 939 return SkBuildQuadArc(start, stop,
michael@0 940 sweepAngle > 0 ? kCW_SkRotationDirection :
michael@0 941 kCCW_SkRotationDirection,
michael@0 942 &matrix, pts);
michael@0 943 }
michael@0 944
michael@0 945 void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
michael@0 946 Direction dir) {
michael@0 947 SkRRect rrect;
michael@0 948 rrect.setRectRadii(rect, (const SkVector*) radii);
michael@0 949 this->addRRect(rrect, dir);
michael@0 950 }
michael@0 951
michael@0 952 /* The inline clockwise and counterclockwise round rect quad approximations
michael@0 953 make it easier to see the symmetry patterns used by add corner quads.
michael@0 954 Clockwise corner value
michael@0 955 path->lineTo(rect.fLeft, rect.fTop + ry); 0 upper left
michael@0 956 path->quadTo(rect.fLeft, rect.fTop + offPtY,
michael@0 957 rect.fLeft + midPtX, rect.fTop + midPtY);
michael@0 958 path->quadTo(rect.fLeft + offPtX, rect.fTop,
michael@0 959 rect.fLeft + rx, rect.fTop);
michael@0 960
michael@0 961 path->lineTo(rect.fRight - rx, rect.fTop); 1 upper right
michael@0 962 path->quadTo(rect.fRight - offPtX, rect.fTop,
michael@0 963 rect.fRight - midPtX, rect.fTop + midPtY);
michael@0 964 path->quadTo(rect.fRight, rect.fTop + offPtY,
michael@0 965 rect.fRight, rect.fTop + ry);
michael@0 966
michael@0 967 path->lineTo(rect.fRight, rect.fBottom - ry); 2 lower right
michael@0 968 path->quadTo(rect.fRight, rect.fBottom - offPtY,
michael@0 969 rect.fRight - midPtX, rect.fBottom - midPtY);
michael@0 970 path->quadTo(rect.fRight - offPtX, rect.fBottom,
michael@0 971 rect.fRight - rx, rect.fBottom);
michael@0 972
michael@0 973 path->lineTo(rect.fLeft + rx, rect.fBottom); 3 lower left
michael@0 974 path->quadTo(rect.fLeft + offPtX, rect.fBottom,
michael@0 975 rect.fLeft + midPtX, rect.fBottom - midPtY);
michael@0 976 path->quadTo(rect.fLeft, rect.fBottom - offPtY,
michael@0 977 rect.fLeft, rect.fBottom - ry);
michael@0 978
michael@0 979 Counterclockwise
michael@0 980 path->lineTo(rect.fLeft, rect.fBottom - ry); 3 lower left
michael@0 981 path->quadTo(rect.fLeft, rect.fBottom - offPtY,
michael@0 982 rect.fLeft + midPtX, rect.fBottom - midPtY);
michael@0 983 path->quadTo(rect.fLeft + offPtX, rect.fBottom,
michael@0 984 rect.fLeft + rx, rect.fBottom);
michael@0 985
michael@0 986 path->lineTo(rect.fRight - rx, rect.fBottom); 2 lower right
michael@0 987 path->quadTo(rect.fRight - offPtX, rect.fBottom,
michael@0 988 rect.fRight - midPtX, rect.fBottom - midPtY);
michael@0 989 path->quadTo(rect.fRight, rect.fBottom - offPtY,
michael@0 990 rect.fRight, rect.fBottom - ry);
michael@0 991
michael@0 992 path->lineTo(rect.fRight, rect.fTop + ry); 1 upper right
michael@0 993 path->quadTo(rect.fRight, rect.fTop + offPtY,
michael@0 994 rect.fRight - midPtX, rect.fTop + midPtY);
michael@0 995 path->quadTo(rect.fRight - offPtX, rect.fTop,
michael@0 996 rect.fRight - rx, rect.fTop);
michael@0 997
michael@0 998 path->lineTo(rect.fLeft + rx, rect.fTop); 0 upper left
michael@0 999 path->quadTo(rect.fLeft + offPtX, rect.fTop,
michael@0 1000 rect.fLeft + midPtX, rect.fTop + midPtY);
michael@0 1001 path->quadTo(rect.fLeft, rect.fTop + offPtY,
michael@0 1002 rect.fLeft, rect.fTop + ry);
michael@0 1003 */
michael@0 1004 static void add_corner_quads(SkPath* path, const SkRRect& rrect,
michael@0 1005 SkRRect::Corner corner, SkPath::Direction dir) {
michael@0 1006 const SkRect& rect = rrect.rect();
michael@0 1007 const SkVector& radii = rrect.radii(corner);
michael@0 1008 SkScalar rx = radii.fX;
michael@0 1009 SkScalar ry = radii.fY;
michael@0 1010 // The mid point of the quadratic arc approximation is half way between the two
michael@0 1011 // control points.
michael@0 1012 const SkScalar mid = 1 - (SK_Scalar1 + SK_ScalarTanPIOver8) / 2;
michael@0 1013 SkScalar midPtX = rx * mid;
michael@0 1014 SkScalar midPtY = ry * mid;
michael@0 1015 const SkScalar control = 1 - SK_ScalarTanPIOver8;
michael@0 1016 SkScalar offPtX = rx * control;
michael@0 1017 SkScalar offPtY = ry * control;
michael@0 1018 static const int kCornerPts = 5;
michael@0 1019 SkScalar xOff[kCornerPts];
michael@0 1020 SkScalar yOff[kCornerPts];
michael@0 1021
michael@0 1022 if ((corner & 1) == (dir == SkPath::kCCW_Direction)) { // corners always alternate direction
michael@0 1023 SkASSERT(dir == SkPath::kCCW_Direction
michael@0 1024 ? corner == SkRRect::kLowerLeft_Corner || corner == SkRRect::kUpperRight_Corner
michael@0 1025 : corner == SkRRect::kUpperLeft_Corner || corner == SkRRect::kLowerRight_Corner);
michael@0 1026 xOff[0] = xOff[1] = 0;
michael@0 1027 xOff[2] = midPtX;
michael@0 1028 xOff[3] = offPtX;
michael@0 1029 xOff[4] = rx;
michael@0 1030 yOff[0] = ry;
michael@0 1031 yOff[1] = offPtY;
michael@0 1032 yOff[2] = midPtY;
michael@0 1033 yOff[3] = yOff[4] = 0;
michael@0 1034 } else {
michael@0 1035 xOff[0] = rx;
michael@0 1036 xOff[1] = offPtX;
michael@0 1037 xOff[2] = midPtX;
michael@0 1038 xOff[3] = xOff[4] = 0;
michael@0 1039 yOff[0] = yOff[1] = 0;
michael@0 1040 yOff[2] = midPtY;
michael@0 1041 yOff[3] = offPtY;
michael@0 1042 yOff[4] = ry;
michael@0 1043 }
michael@0 1044 if ((corner - 1) & 2) {
michael@0 1045 SkASSERT(corner == SkRRect::kLowerLeft_Corner || corner == SkRRect::kUpperLeft_Corner);
michael@0 1046 for (int i = 0; i < kCornerPts; ++i) {
michael@0 1047 xOff[i] = rect.fLeft + xOff[i];
michael@0 1048 }
michael@0 1049 } else {
michael@0 1050 SkASSERT(corner == SkRRect::kLowerRight_Corner || corner == SkRRect::kUpperRight_Corner);
michael@0 1051 for (int i = 0; i < kCornerPts; ++i) {
michael@0 1052 xOff[i] = rect.fRight - xOff[i];
michael@0 1053 }
michael@0 1054 }
michael@0 1055 if (corner < SkRRect::kLowerRight_Corner) {
michael@0 1056 for (int i = 0; i < kCornerPts; ++i) {
michael@0 1057 yOff[i] = rect.fTop + yOff[i];
michael@0 1058 }
michael@0 1059 } else {
michael@0 1060 for (int i = 0; i < kCornerPts; ++i) {
michael@0 1061 yOff[i] = rect.fBottom - yOff[i];
michael@0 1062 }
michael@0 1063 }
michael@0 1064
michael@0 1065 SkPoint lastPt;
michael@0 1066 SkAssertResult(path->getLastPt(&lastPt));
michael@0 1067 if (lastPt.fX != xOff[0] || lastPt.fY != yOff[0]) {
michael@0 1068 path->lineTo(xOff[0], yOff[0]);
michael@0 1069 }
michael@0 1070 if (rx || ry) {
michael@0 1071 path->quadTo(xOff[1], yOff[1], xOff[2], yOff[2]);
michael@0 1072 path->quadTo(xOff[3], yOff[3], xOff[4], yOff[4]);
michael@0 1073 } else {
michael@0 1074 path->lineTo(xOff[2], yOff[2]);
michael@0 1075 path->lineTo(xOff[4], yOff[4]);
michael@0 1076 }
michael@0 1077 }
michael@0 1078
michael@0 1079 void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
michael@0 1080 assert_known_direction(dir);
michael@0 1081
michael@0 1082 if (rrect.isEmpty()) {
michael@0 1083 return;
michael@0 1084 }
michael@0 1085
michael@0 1086 const SkRect& bounds = rrect.getBounds();
michael@0 1087
michael@0 1088 if (rrect.isRect()) {
michael@0 1089 this->addRect(bounds, dir);
michael@0 1090 } else if (rrect.isOval()) {
michael@0 1091 this->addOval(bounds, dir);
michael@0 1092 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT
michael@0 1093 } else if (rrect.isSimple()) {
michael@0 1094 const SkVector& rad = rrect.getSimpleRadii();
michael@0 1095 this->addRoundRect(bounds, rad.x(), rad.y(), dir);
michael@0 1096 #endif
michael@0 1097 } else {
michael@0 1098 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
michael@0 1099
michael@0 1100 SkAutoPathBoundsUpdate apbu(this, bounds);
michael@0 1101 SkAutoDisableDirectionCheck addc(this);
michael@0 1102
michael@0 1103 this->incReserve(21);
michael@0 1104 if (kCW_Direction == dir) {
michael@0 1105 this->moveTo(bounds.fLeft,
michael@0 1106 bounds.fBottom - rrect.fRadii[SkRRect::kLowerLeft_Corner].fY);
michael@0 1107 add_corner_quads(this, rrect, SkRRect::kUpperLeft_Corner, dir);
michael@0 1108 add_corner_quads(this, rrect, SkRRect::kUpperRight_Corner, dir);
michael@0 1109 add_corner_quads(this, rrect, SkRRect::kLowerRight_Corner, dir);
michael@0 1110 add_corner_quads(this, rrect, SkRRect::kLowerLeft_Corner, dir);
michael@0 1111 } else {
michael@0 1112 this->moveTo(bounds.fLeft,
michael@0 1113 bounds.fTop + rrect.fRadii[SkRRect::kUpperLeft_Corner].fY);
michael@0 1114 add_corner_quads(this, rrect, SkRRect::kLowerLeft_Corner, dir);
michael@0 1115 add_corner_quads(this, rrect, SkRRect::kLowerRight_Corner, dir);
michael@0 1116 add_corner_quads(this, rrect, SkRRect::kUpperRight_Corner, dir);
michael@0 1117 add_corner_quads(this, rrect, SkRRect::kUpperLeft_Corner, dir);
michael@0 1118 }
michael@0 1119 this->close();
michael@0 1120 }
michael@0 1121 }
michael@0 1122
michael@0 1123 bool SkPath::hasOnlyMoveTos() const {
michael@0 1124 int count = fPathRef->countVerbs();
michael@0 1125 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
michael@0 1126 for (int i = 0; i < count; ++i) {
michael@0 1127 if (*verbs == kLine_Verb ||
michael@0 1128 *verbs == kQuad_Verb ||
michael@0 1129 *verbs == kConic_Verb ||
michael@0 1130 *verbs == kCubic_Verb) {
michael@0 1131 return false;
michael@0 1132 }
michael@0 1133 ++verbs;
michael@0 1134 }
michael@0 1135 return true;
michael@0 1136 }
michael@0 1137
michael@0 1138 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT
michael@0 1139 #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
michael@0 1140 #endif
michael@0 1141
michael@0 1142 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
michael@0 1143 Direction dir) {
michael@0 1144 assert_known_direction(dir);
michael@0 1145
michael@0 1146 if (rx < 0 || ry < 0) {
michael@0 1147 SkErrorInternals::SetError( kInvalidArgument_SkError,
michael@0 1148 "I got %f and %f as radii to SkPath::AddRoundRect, "
michael@0 1149 "but negative radii are not allowed.",
michael@0 1150 SkScalarToDouble(rx), SkScalarToDouble(ry) );
michael@0 1151 return;
michael@0 1152 }
michael@0 1153
michael@0 1154 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT
michael@0 1155 SkScalar w = rect.width();
michael@0 1156 SkScalar halfW = SkScalarHalf(w);
michael@0 1157 SkScalar h = rect.height();
michael@0 1158 SkScalar halfH = SkScalarHalf(h);
michael@0 1159
michael@0 1160 if (halfW <= 0 || halfH <= 0) {
michael@0 1161 return;
michael@0 1162 }
michael@0 1163
michael@0 1164 bool skip_hori = rx >= halfW;
michael@0 1165 bool skip_vert = ry >= halfH;
michael@0 1166
michael@0 1167 if (skip_hori && skip_vert) {
michael@0 1168 this->addOval(rect, dir);
michael@0 1169 return;
michael@0 1170 }
michael@0 1171
michael@0 1172 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
michael@0 1173
michael@0 1174 SkAutoPathBoundsUpdate apbu(this, rect);
michael@0 1175 SkAutoDisableDirectionCheck addc(this);
michael@0 1176
michael@0 1177 if (skip_hori) {
michael@0 1178 rx = halfW;
michael@0 1179 } else if (skip_vert) {
michael@0 1180 ry = halfH;
michael@0 1181 }
michael@0 1182 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
michael@0 1183 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
michael@0 1184
michael@0 1185 this->incReserve(17);
michael@0 1186 this->moveTo(rect.fRight - rx, rect.fTop); // top-right
michael@0 1187 if (dir == kCCW_Direction) {
michael@0 1188 if (!skip_hori) {
michael@0 1189 this->lineTo(rect.fLeft + rx, rect.fTop); // top
michael@0 1190 }
michael@0 1191 this->cubicTo(rect.fLeft + rx - sx, rect.fTop,
michael@0 1192 rect.fLeft, rect.fTop + ry - sy,
michael@0 1193 rect.fLeft, rect.fTop + ry); // top-left
michael@0 1194 if (!skip_vert) {
michael@0 1195 this->lineTo(rect.fLeft, rect.fBottom - ry); // left
michael@0 1196 }
michael@0 1197 this->cubicTo(rect.fLeft, rect.fBottom - ry + sy,
michael@0 1198 rect.fLeft + rx - sx, rect.fBottom,
michael@0 1199 rect.fLeft + rx, rect.fBottom); // bot-left
michael@0 1200 if (!skip_hori) {
michael@0 1201 this->lineTo(rect.fRight - rx, rect.fBottom); // bottom
michael@0 1202 }
michael@0 1203 this->cubicTo(rect.fRight - rx + sx, rect.fBottom,
michael@0 1204 rect.fRight, rect.fBottom - ry + sy,
michael@0 1205 rect.fRight, rect.fBottom - ry); // bot-right
michael@0 1206 if (!skip_vert) {
michael@0 1207 this->lineTo(rect.fRight, rect.fTop + ry); // right
michael@0 1208 }
michael@0 1209 this->cubicTo(rect.fRight, rect.fTop + ry - sy,
michael@0 1210 rect.fRight - rx + sx, rect.fTop,
michael@0 1211 rect.fRight - rx, rect.fTop); // top-right
michael@0 1212 } else {
michael@0 1213 this->cubicTo(rect.fRight - rx + sx, rect.fTop,
michael@0 1214 rect.fRight, rect.fTop + ry - sy,
michael@0 1215 rect.fRight, rect.fTop + ry); // top-right
michael@0 1216 if (!skip_vert) {
michael@0 1217 this->lineTo(rect.fRight, rect.fBottom - ry); // right
michael@0 1218 }
michael@0 1219 this->cubicTo(rect.fRight, rect.fBottom - ry + sy,
michael@0 1220 rect.fRight - rx + sx, rect.fBottom,
michael@0 1221 rect.fRight - rx, rect.fBottom); // bot-right
michael@0 1222 if (!skip_hori) {
michael@0 1223 this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom
michael@0 1224 }
michael@0 1225 this->cubicTo(rect.fLeft + rx - sx, rect.fBottom,
michael@0 1226 rect.fLeft, rect.fBottom - ry + sy,
michael@0 1227 rect.fLeft, rect.fBottom - ry); // bot-left
michael@0 1228 if (!skip_vert) {
michael@0 1229 this->lineTo(rect.fLeft, rect.fTop + ry); // left
michael@0 1230 }
michael@0 1231 this->cubicTo(rect.fLeft, rect.fTop + ry - sy,
michael@0 1232 rect.fLeft + rx - sx, rect.fTop,
michael@0 1233 rect.fLeft + rx, rect.fTop); // top-left
michael@0 1234 if (!skip_hori) {
michael@0 1235 this->lineTo(rect.fRight - rx, rect.fTop); // top
michael@0 1236 }
michael@0 1237 }
michael@0 1238 this->close();
michael@0 1239 #else
michael@0 1240 SkRRect rrect;
michael@0 1241 rrect.setRectXY(rect, rx, ry);
michael@0 1242 this->addRRect(rrect, dir);
michael@0 1243 #endif
michael@0 1244 }
michael@0 1245
michael@0 1246 void SkPath::addOval(const SkRect& oval, Direction dir) {
michael@0 1247 assert_known_direction(dir);
michael@0 1248
michael@0 1249 /* If addOval() is called after previous moveTo(),
michael@0 1250 this path is still marked as an oval. This is used to
michael@0 1251 fit into WebKit's calling sequences.
michael@0 1252 We can't simply check isEmpty() in this case, as additional
michael@0 1253 moveTo() would mark the path non empty.
michael@0 1254 */
michael@0 1255 bool isOval = hasOnlyMoveTos();
michael@0 1256 if (isOval) {
michael@0 1257 fDirection = dir;
michael@0 1258 } else {
michael@0 1259 fDirection = kUnknown_Direction;
michael@0 1260 }
michael@0 1261
michael@0 1262 SkAutoDisableDirectionCheck addc(this);
michael@0 1263
michael@0 1264 SkAutoPathBoundsUpdate apbu(this, oval);
michael@0 1265
michael@0 1266 SkScalar cx = oval.centerX();
michael@0 1267 SkScalar cy = oval.centerY();
michael@0 1268 SkScalar rx = SkScalarHalf(oval.width());
michael@0 1269 SkScalar ry = SkScalarHalf(oval.height());
michael@0 1270
michael@0 1271 SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8);
michael@0 1272 SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8);
michael@0 1273 SkScalar mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
michael@0 1274 SkScalar my = SkScalarMul(ry, SK_ScalarRoot2Over2);
michael@0 1275
michael@0 1276 /*
michael@0 1277 To handle imprecision in computing the center and radii, we revert to
michael@0 1278 the provided bounds when we can (i.e. use oval.fLeft instead of cx-rx)
michael@0 1279 to ensure that we don't exceed the oval's bounds *ever*, since we want
michael@0 1280 to use oval for our fast-bounds, rather than have to recompute it.
michael@0 1281 */
michael@0 1282 const SkScalar L = oval.fLeft; // cx - rx
michael@0 1283 const SkScalar T = oval.fTop; // cy - ry
michael@0 1284 const SkScalar R = oval.fRight; // cx + rx
michael@0 1285 const SkScalar B = oval.fBottom; // cy + ry
michael@0 1286
michael@0 1287 this->incReserve(17); // 8 quads + close
michael@0 1288 this->moveTo(R, cy);
michael@0 1289 if (dir == kCCW_Direction) {
michael@0 1290 this->quadTo( R, cy - sy, cx + mx, cy - my);
michael@0 1291 this->quadTo(cx + sx, T, cx , T);
michael@0 1292 this->quadTo(cx - sx, T, cx - mx, cy - my);
michael@0 1293 this->quadTo( L, cy - sy, L, cy );
michael@0 1294 this->quadTo( L, cy + sy, cx - mx, cy + my);
michael@0 1295 this->quadTo(cx - sx, B, cx , B);
michael@0 1296 this->quadTo(cx + sx, B, cx + mx, cy + my);
michael@0 1297 this->quadTo( R, cy + sy, R, cy );
michael@0 1298 } else {
michael@0 1299 this->quadTo( R, cy + sy, cx + mx, cy + my);
michael@0 1300 this->quadTo(cx + sx, B, cx , B);
michael@0 1301 this->quadTo(cx - sx, B, cx - mx, cy + my);
michael@0 1302 this->quadTo( L, cy + sy, L, cy );
michael@0 1303 this->quadTo( L, cy - sy, cx - mx, cy - my);
michael@0 1304 this->quadTo(cx - sx, T, cx , T);
michael@0 1305 this->quadTo(cx + sx, T, cx + mx, cy - my);
michael@0 1306 this->quadTo( R, cy - sy, R, cy );
michael@0 1307 }
michael@0 1308 this->close();
michael@0 1309
michael@0 1310 SkPathRef::Editor ed(&fPathRef);
michael@0 1311
michael@0 1312 ed.setIsOval(isOval);
michael@0 1313 }
michael@0 1314
michael@0 1315 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
michael@0 1316 if (r > 0) {
michael@0 1317 SkRect rect;
michael@0 1318 rect.set(x - r, y - r, x + r, y + r);
michael@0 1319 this->addOval(rect, dir);
michael@0 1320 }
michael@0 1321 }
michael@0 1322
michael@0 1323 void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
michael@0 1324 bool forceMoveTo) {
michael@0 1325 if (oval.width() < 0 || oval.height() < 0) {
michael@0 1326 return;
michael@0 1327 }
michael@0 1328
michael@0 1329 SkPoint pts[kSkBuildQuadArcStorage];
michael@0 1330 int count = build_arc_points(oval, startAngle, sweepAngle, pts);
michael@0 1331 SkASSERT((count & 1) == 1);
michael@0 1332
michael@0 1333 if (fPathRef->countVerbs() == 0) {
michael@0 1334 forceMoveTo = true;
michael@0 1335 }
michael@0 1336 this->incReserve(count);
michael@0 1337 forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
michael@0 1338 for (int i = 1; i < count; i += 2) {
michael@0 1339 this->quadTo(pts[i], pts[i+1]);
michael@0 1340 }
michael@0 1341 }
michael@0 1342
michael@0 1343 void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
michael@0 1344 if (oval.isEmpty() || 0 == sweepAngle) {
michael@0 1345 return;
michael@0 1346 }
michael@0 1347
michael@0 1348 const SkScalar kFullCircleAngle = SkIntToScalar(360);
michael@0 1349
michael@0 1350 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
michael@0 1351 this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
michael@0 1352 return;
michael@0 1353 }
michael@0 1354
michael@0 1355 SkPoint pts[kSkBuildQuadArcStorage];
michael@0 1356 int count = build_arc_points(oval, startAngle, sweepAngle, pts);
michael@0 1357
michael@0 1358 SkDEBUGCODE(this->validate();)
michael@0 1359 SkASSERT(count & 1);
michael@0 1360
michael@0 1361 fLastMoveToIndex = fPathRef->countPoints();
michael@0 1362
michael@0 1363 SkPathRef::Editor ed(&fPathRef, 1+(count-1)/2, count);
michael@0 1364
michael@0 1365 ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
michael@0 1366 if (count > 1) {
michael@0 1367 SkPoint* p = ed.growForRepeatedVerb(kQuad_Verb, (count-1)/2);
michael@0 1368 memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
michael@0 1369 }
michael@0 1370
michael@0 1371 DIRTY_AFTER_EDIT;
michael@0 1372 SkDEBUGCODE(this->validate();)
michael@0 1373 }
michael@0 1374
michael@0 1375 /*
michael@0 1376 Need to handle the case when the angle is sharp, and our computed end-points
michael@0 1377 for the arc go behind pt1 and/or p2...
michael@0 1378 */
michael@0 1379 void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
michael@0 1380 SkScalar radius) {
michael@0 1381 SkVector before, after;
michael@0 1382
michael@0 1383 // need to know our prev pt so we can construct tangent vectors
michael@0 1384 {
michael@0 1385 SkPoint start;
michael@0 1386 this->getLastPt(&start);
michael@0 1387 // Handle degenerate cases by adding a line to the first point and
michael@0 1388 // bailing out.
michael@0 1389 if ((x1 == start.fX && y1 == start.fY) ||
michael@0 1390 (x1 == x2 && y1 == y2) ||
michael@0 1391 radius == 0) {
michael@0 1392 this->lineTo(x1, y1);
michael@0 1393 return;
michael@0 1394 }
michael@0 1395 before.setNormalize(x1 - start.fX, y1 - start.fY);
michael@0 1396 after.setNormalize(x2 - x1, y2 - y1);
michael@0 1397 }
michael@0 1398
michael@0 1399 SkScalar cosh = SkPoint::DotProduct(before, after);
michael@0 1400 SkScalar sinh = SkPoint::CrossProduct(before, after);
michael@0 1401
michael@0 1402 if (SkScalarNearlyZero(sinh)) { // angle is too tight
michael@0 1403 this->lineTo(x1, y1);
michael@0 1404 return;
michael@0 1405 }
michael@0 1406
michael@0 1407 SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
michael@0 1408 if (dist < 0) {
michael@0 1409 dist = -dist;
michael@0 1410 }
michael@0 1411
michael@0 1412 SkScalar xx = x1 - SkScalarMul(dist, before.fX);
michael@0 1413 SkScalar yy = y1 - SkScalarMul(dist, before.fY);
michael@0 1414 SkRotationDirection arcDir;
michael@0 1415
michael@0 1416 // now turn before/after into normals
michael@0 1417 if (sinh > 0) {
michael@0 1418 before.rotateCCW();
michael@0 1419 after.rotateCCW();
michael@0 1420 arcDir = kCW_SkRotationDirection;
michael@0 1421 } else {
michael@0 1422 before.rotateCW();
michael@0 1423 after.rotateCW();
michael@0 1424 arcDir = kCCW_SkRotationDirection;
michael@0 1425 }
michael@0 1426
michael@0 1427 SkMatrix matrix;
michael@0 1428 SkPoint pts[kSkBuildQuadArcStorage];
michael@0 1429
michael@0 1430 matrix.setScale(radius, radius);
michael@0 1431 matrix.postTranslate(xx - SkScalarMul(radius, before.fX),
michael@0 1432 yy - SkScalarMul(radius, before.fY));
michael@0 1433
michael@0 1434 int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts);
michael@0 1435
michael@0 1436 this->incReserve(count);
michael@0 1437 // [xx,yy] == pts[0]
michael@0 1438 this->lineTo(xx, yy);
michael@0 1439 for (int i = 1; i < count; i += 2) {
michael@0 1440 this->quadTo(pts[i], pts[i+1]);
michael@0 1441 }
michael@0 1442 }
michael@0 1443
michael@0 1444 ///////////////////////////////////////////////////////////////////////////////
michael@0 1445
michael@0 1446 void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) {
michael@0 1447 SkMatrix matrix;
michael@0 1448
michael@0 1449 matrix.setTranslate(dx, dy);
michael@0 1450 this->addPath(path, matrix, mode);
michael@0 1451 }
michael@0 1452
michael@0 1453 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix, AddPathMode mode) {
michael@0 1454 SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints());
michael@0 1455
michael@0 1456 RawIter iter(path);
michael@0 1457 SkPoint pts[4];
michael@0 1458 Verb verb;
michael@0 1459
michael@0 1460 SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
michael@0 1461 bool firstVerb = true;
michael@0 1462 while ((verb = iter.next(pts)) != kDone_Verb) {
michael@0 1463 switch (verb) {
michael@0 1464 case kMove_Verb:
michael@0 1465 proc(matrix, &pts[0], &pts[0], 1);
michael@0 1466 if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
michael@0 1467 injectMoveToIfNeeded(); // In case last contour is closed
michael@0 1468 this->lineTo(pts[0]);
michael@0 1469 } else {
michael@0 1470 this->moveTo(pts[0]);
michael@0 1471 }
michael@0 1472 break;
michael@0 1473 case kLine_Verb:
michael@0 1474 proc(matrix, &pts[1], &pts[1], 1);
michael@0 1475 this->lineTo(pts[1]);
michael@0 1476 break;
michael@0 1477 case kQuad_Verb:
michael@0 1478 proc(matrix, &pts[1], &pts[1], 2);
michael@0 1479 this->quadTo(pts[1], pts[2]);
michael@0 1480 break;
michael@0 1481 case kConic_Verb:
michael@0 1482 proc(matrix, &pts[1], &pts[1], 2);
michael@0 1483 this->conicTo(pts[1], pts[2], iter.conicWeight());
michael@0 1484 break;
michael@0 1485 case kCubic_Verb:
michael@0 1486 proc(matrix, &pts[1], &pts[1], 3);
michael@0 1487 this->cubicTo(pts[1], pts[2], pts[3]);
michael@0 1488 break;
michael@0 1489 case kClose_Verb:
michael@0 1490 this->close();
michael@0 1491 break;
michael@0 1492 default:
michael@0 1493 SkDEBUGFAIL("unknown verb");
michael@0 1494 }
michael@0 1495 firstVerb = false;
michael@0 1496 }
michael@0 1497 }
michael@0 1498
michael@0 1499 ///////////////////////////////////////////////////////////////////////////////
michael@0 1500
michael@0 1501 static int pts_in_verb(unsigned verb) {
michael@0 1502 static const uint8_t gPtsInVerb[] = {
michael@0 1503 1, // kMove
michael@0 1504 1, // kLine
michael@0 1505 2, // kQuad
michael@0 1506 2, // kConic
michael@0 1507 3, // kCubic
michael@0 1508 0, // kClose
michael@0 1509 0 // kDone
michael@0 1510 };
michael@0 1511
michael@0 1512 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
michael@0 1513 return gPtsInVerb[verb];
michael@0 1514 }
michael@0 1515
michael@0 1516 // ignore the last point of the 1st contour
michael@0 1517 void SkPath::reversePathTo(const SkPath& path) {
michael@0 1518 int i, vcount = path.fPathRef->countVerbs();
michael@0 1519 // exit early if the path is empty, or just has a moveTo.
michael@0 1520 if (vcount < 2) {
michael@0 1521 return;
michael@0 1522 }
michael@0 1523
michael@0 1524 SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
michael@0 1525
michael@0 1526 const uint8_t* verbs = path.fPathRef->verbs();
michael@0 1527 const SkPoint* pts = path.fPathRef->points();
michael@0 1528 const SkScalar* conicWeights = path.fPathRef->conicWeights();
michael@0 1529
michael@0 1530 SkASSERT(verbs[~0] == kMove_Verb);
michael@0 1531 for (i = 1; i < vcount; ++i) {
michael@0 1532 unsigned v = verbs[~i];
michael@0 1533 int n = pts_in_verb(v);
michael@0 1534 if (n == 0) {
michael@0 1535 break;
michael@0 1536 }
michael@0 1537 pts += n;
michael@0 1538 conicWeights += (SkPath::kConic_Verb == v);
michael@0 1539 }
michael@0 1540
michael@0 1541 while (--i > 0) {
michael@0 1542 switch (verbs[~i]) {
michael@0 1543 case kLine_Verb:
michael@0 1544 this->lineTo(pts[-1].fX, pts[-1].fY);
michael@0 1545 break;
michael@0 1546 case kQuad_Verb:
michael@0 1547 this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
michael@0 1548 break;
michael@0 1549 case kConic_Verb:
michael@0 1550 this->conicTo(pts[-1], pts[-2], *--conicWeights);
michael@0 1551 break;
michael@0 1552 case kCubic_Verb:
michael@0 1553 this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
michael@0 1554 pts[-3].fX, pts[-3].fY);
michael@0 1555 break;
michael@0 1556 default:
michael@0 1557 SkDEBUGFAIL("bad verb");
michael@0 1558 break;
michael@0 1559 }
michael@0 1560 pts -= pts_in_verb(verbs[~i]);
michael@0 1561 }
michael@0 1562 }
michael@0 1563
michael@0 1564 void SkPath::reverseAddPath(const SkPath& src) {
michael@0 1565 SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
michael@0 1566
michael@0 1567 const SkPoint* pts = src.fPathRef->pointsEnd();
michael@0 1568 // we will iterator through src's verbs backwards
michael@0 1569 const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb
michael@0 1570 const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
michael@0 1571 const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
michael@0 1572
michael@0 1573 bool needMove = true;
michael@0 1574 bool needClose = false;
michael@0 1575 while (verbs < verbsEnd) {
michael@0 1576 uint8_t v = *(verbs++);
michael@0 1577 int n = pts_in_verb(v);
michael@0 1578
michael@0 1579 if (needMove) {
michael@0 1580 --pts;
michael@0 1581 this->moveTo(pts->fX, pts->fY);
michael@0 1582 needMove = false;
michael@0 1583 }
michael@0 1584 pts -= n;
michael@0 1585 switch (v) {
michael@0 1586 case kMove_Verb:
michael@0 1587 if (needClose) {
michael@0 1588 this->close();
michael@0 1589 needClose = false;
michael@0 1590 }
michael@0 1591 needMove = true;
michael@0 1592 pts += 1; // so we see the point in "if (needMove)" above
michael@0 1593 break;
michael@0 1594 case kLine_Verb:
michael@0 1595 this->lineTo(pts[0]);
michael@0 1596 break;
michael@0 1597 case kQuad_Verb:
michael@0 1598 this->quadTo(pts[1], pts[0]);
michael@0 1599 break;
michael@0 1600 case kConic_Verb:
michael@0 1601 this->conicTo(pts[1], pts[0], *--conicWeights);
michael@0 1602 break;
michael@0 1603 case kCubic_Verb:
michael@0 1604 this->cubicTo(pts[2], pts[1], pts[0]);
michael@0 1605 break;
michael@0 1606 case kClose_Verb:
michael@0 1607 needClose = true;
michael@0 1608 break;
michael@0 1609 default:
michael@0 1610 SkDEBUGFAIL("unexpected verb");
michael@0 1611 }
michael@0 1612 }
michael@0 1613 }
michael@0 1614
michael@0 1615 ///////////////////////////////////////////////////////////////////////////////
michael@0 1616
michael@0 1617 void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
michael@0 1618 SkMatrix matrix;
michael@0 1619
michael@0 1620 matrix.setTranslate(dx, dy);
michael@0 1621 this->transform(matrix, dst);
michael@0 1622 }
michael@0 1623
michael@0 1624 #include "SkGeometry.h"
michael@0 1625
michael@0 1626 static void subdivide_quad_to(SkPath* path, const SkPoint pts[3],
michael@0 1627 int level = 2) {
michael@0 1628 if (--level >= 0) {
michael@0 1629 SkPoint tmp[5];
michael@0 1630
michael@0 1631 SkChopQuadAtHalf(pts, tmp);
michael@0 1632 subdivide_quad_to(path, &tmp[0], level);
michael@0 1633 subdivide_quad_to(path, &tmp[2], level);
michael@0 1634 } else {
michael@0 1635 path->quadTo(pts[1], pts[2]);
michael@0 1636 }
michael@0 1637 }
michael@0 1638
michael@0 1639 static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
michael@0 1640 int level = 2) {
michael@0 1641 if (--level >= 0) {
michael@0 1642 SkPoint tmp[7];
michael@0 1643
michael@0 1644 SkChopCubicAtHalf(pts, tmp);
michael@0 1645 subdivide_cubic_to(path, &tmp[0], level);
michael@0 1646 subdivide_cubic_to(path, &tmp[3], level);
michael@0 1647 } else {
michael@0 1648 path->cubicTo(pts[1], pts[2], pts[3]);
michael@0 1649 }
michael@0 1650 }
michael@0 1651
michael@0 1652 void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
michael@0 1653 SkDEBUGCODE(this->validate();)
michael@0 1654 if (dst == NULL) {
michael@0 1655 dst = (SkPath*)this;
michael@0 1656 }
michael@0 1657
michael@0 1658 if (matrix.hasPerspective()) {
michael@0 1659 SkPath tmp;
michael@0 1660 tmp.fFillType = fFillType;
michael@0 1661
michael@0 1662 SkPath::Iter iter(*this, false);
michael@0 1663 SkPoint pts[4];
michael@0 1664 SkPath::Verb verb;
michael@0 1665
michael@0 1666 while ((verb = iter.next(pts, false)) != kDone_Verb) {
michael@0 1667 switch (verb) {
michael@0 1668 case kMove_Verb:
michael@0 1669 tmp.moveTo(pts[0]);
michael@0 1670 break;
michael@0 1671 case kLine_Verb:
michael@0 1672 tmp.lineTo(pts[1]);
michael@0 1673 break;
michael@0 1674 case kQuad_Verb:
michael@0 1675 subdivide_quad_to(&tmp, pts);
michael@0 1676 break;
michael@0 1677 case kConic_Verb:
michael@0 1678 SkDEBUGFAIL("TODO: compute new weight");
michael@0 1679 tmp.conicTo(pts[1], pts[2], iter.conicWeight());
michael@0 1680 break;
michael@0 1681 case kCubic_Verb:
michael@0 1682 subdivide_cubic_to(&tmp, pts);
michael@0 1683 break;
michael@0 1684 case kClose_Verb:
michael@0 1685 tmp.close();
michael@0 1686 break;
michael@0 1687 default:
michael@0 1688 SkDEBUGFAIL("unknown verb");
michael@0 1689 break;
michael@0 1690 }
michael@0 1691 }
michael@0 1692
michael@0 1693 dst->swap(tmp);
michael@0 1694 SkPathRef::Editor ed(&dst->fPathRef);
michael@0 1695 matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
michael@0 1696 dst->fDirection = kUnknown_Direction;
michael@0 1697 } else {
michael@0 1698 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
michael@0 1699
michael@0 1700 if (this != dst) {
michael@0 1701 dst->fFillType = fFillType;
michael@0 1702 dst->fConvexity = fConvexity;
michael@0 1703 }
michael@0 1704
michael@0 1705 if (kUnknown_Direction == fDirection) {
michael@0 1706 dst->fDirection = kUnknown_Direction;
michael@0 1707 } else {
michael@0 1708 SkScalar det2x2 =
michael@0 1709 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix::kMScaleY)) -
michael@0 1710 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::kMSkewY));
michael@0 1711 if (det2x2 < 0) {
michael@0 1712 dst->fDirection = SkPath::OppositeDirection(static_cast<Direction>(fDirection));
michael@0 1713 } else if (det2x2 > 0) {
michael@0 1714 dst->fDirection = fDirection;
michael@0 1715 } else {
michael@0 1716 dst->fConvexity = kUnknown_Convexity;
michael@0 1717 dst->fDirection = kUnknown_Direction;
michael@0 1718 }
michael@0 1719 }
michael@0 1720
michael@0 1721 SkDEBUGCODE(dst->validate();)
michael@0 1722 }
michael@0 1723 }
michael@0 1724
michael@0 1725 ///////////////////////////////////////////////////////////////////////////////
michael@0 1726 ///////////////////////////////////////////////////////////////////////////////
michael@0 1727
michael@0 1728 enum SegmentState {
michael@0 1729 kEmptyContour_SegmentState, // The current contour is empty. We may be
michael@0 1730 // starting processing or we may have just
michael@0 1731 // closed a contour.
michael@0 1732 kAfterMove_SegmentState, // We have seen a move, but nothing else.
michael@0 1733 kAfterPrimitive_SegmentState // We have seen a primitive but not yet
michael@0 1734 // closed the path. Also the initial state.
michael@0 1735 };
michael@0 1736
michael@0 1737 SkPath::Iter::Iter() {
michael@0 1738 #ifdef SK_DEBUG
michael@0 1739 fPts = NULL;
michael@0 1740 fConicWeights = NULL;
michael@0 1741 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
michael@0 1742 fForceClose = fCloseLine = false;
michael@0 1743 fSegmentState = kEmptyContour_SegmentState;
michael@0 1744 #endif
michael@0 1745 // need to init enough to make next() harmlessly return kDone_Verb
michael@0 1746 fVerbs = NULL;
michael@0 1747 fVerbStop = NULL;
michael@0 1748 fNeedClose = false;
michael@0 1749 }
michael@0 1750
michael@0 1751 SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
michael@0 1752 this->setPath(path, forceClose);
michael@0 1753 }
michael@0 1754
michael@0 1755 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
michael@0 1756 fPts = path.fPathRef->points();
michael@0 1757 fVerbs = path.fPathRef->verbs();
michael@0 1758 fVerbStop = path.fPathRef->verbsMemBegin();
michael@0 1759 fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
michael@0 1760 fLastPt.fX = fLastPt.fY = 0;
michael@0 1761 fMoveTo.fX = fMoveTo.fY = 0;
michael@0 1762 fForceClose = SkToU8(forceClose);
michael@0 1763 fNeedClose = false;
michael@0 1764 fSegmentState = kEmptyContour_SegmentState;
michael@0 1765 }
michael@0 1766
michael@0 1767 bool SkPath::Iter::isClosedContour() const {
michael@0 1768 if (fVerbs == NULL || fVerbs == fVerbStop) {
michael@0 1769 return false;
michael@0 1770 }
michael@0 1771 if (fForceClose) {
michael@0 1772 return true;
michael@0 1773 }
michael@0 1774
michael@0 1775 const uint8_t* verbs = fVerbs;
michael@0 1776 const uint8_t* stop = fVerbStop;
michael@0 1777
michael@0 1778 if (kMove_Verb == *(verbs - 1)) {
michael@0 1779 verbs -= 1; // skip the initial moveto
michael@0 1780 }
michael@0 1781
michael@0 1782 while (verbs > stop) {
michael@0 1783 // verbs points one beyond the current verb, decrement first.
michael@0 1784 unsigned v = *(--verbs);
michael@0 1785 if (kMove_Verb == v) {
michael@0 1786 break;
michael@0 1787 }
michael@0 1788 if (kClose_Verb == v) {
michael@0 1789 return true;
michael@0 1790 }
michael@0 1791 }
michael@0 1792 return false;
michael@0 1793 }
michael@0 1794
michael@0 1795 SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
michael@0 1796 SkASSERT(pts);
michael@0 1797 if (fLastPt != fMoveTo) {
michael@0 1798 // A special case: if both points are NaN, SkPoint::operation== returns
michael@0 1799 // false, but the iterator expects that they are treated as the same.
michael@0 1800 // (consider SkPoint is a 2-dimension float point).
michael@0 1801 if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
michael@0 1802 SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
michael@0 1803 return kClose_Verb;
michael@0 1804 }
michael@0 1805
michael@0 1806 pts[0] = fLastPt;
michael@0 1807 pts[1] = fMoveTo;
michael@0 1808 fLastPt = fMoveTo;
michael@0 1809 fCloseLine = true;
michael@0 1810 return kLine_Verb;
michael@0 1811 } else {
michael@0 1812 pts[0] = fMoveTo;
michael@0 1813 return kClose_Verb;
michael@0 1814 }
michael@0 1815 }
michael@0 1816
michael@0 1817 const SkPoint& SkPath::Iter::cons_moveTo() {
michael@0 1818 if (fSegmentState == kAfterMove_SegmentState) {
michael@0 1819 // Set the first return pt to the move pt
michael@0 1820 fSegmentState = kAfterPrimitive_SegmentState;
michael@0 1821 return fMoveTo;
michael@0 1822 } else {
michael@0 1823 SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
michael@0 1824 // Set the first return pt to the last pt of the previous primitive.
michael@0 1825 return fPts[-1];
michael@0 1826 }
michael@0 1827 }
michael@0 1828
michael@0 1829 void SkPath::Iter::consumeDegenerateSegments() {
michael@0 1830 // We need to step over anything that will not move the current draw point
michael@0 1831 // forward before the next move is seen
michael@0 1832 const uint8_t* lastMoveVerb = 0;
michael@0 1833 const SkPoint* lastMovePt = 0;
michael@0 1834 SkPoint lastPt = fLastPt;
michael@0 1835 while (fVerbs != fVerbStop) {
michael@0 1836 unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb
michael@0 1837 switch (verb) {
michael@0 1838 case kMove_Verb:
michael@0 1839 // Keep a record of this most recent move
michael@0 1840 lastMoveVerb = fVerbs;
michael@0 1841 lastMovePt = fPts;
michael@0 1842 lastPt = fPts[0];
michael@0 1843 fVerbs--;
michael@0 1844 fPts++;
michael@0 1845 break;
michael@0 1846
michael@0 1847 case kClose_Verb:
michael@0 1848 // A close when we are in a segment is always valid except when it
michael@0 1849 // follows a move which follows a segment.
michael@0 1850 if (fSegmentState == kAfterPrimitive_SegmentState && !lastMoveVerb) {
michael@0 1851 return;
michael@0 1852 }
michael@0 1853 // A close at any other time must be ignored
michael@0 1854 fVerbs--;
michael@0 1855 break;
michael@0 1856
michael@0 1857 case kLine_Verb:
michael@0 1858 if (!IsLineDegenerate(lastPt, fPts[0])) {
michael@0 1859 if (lastMoveVerb) {
michael@0 1860 fVerbs = lastMoveVerb;
michael@0 1861 fPts = lastMovePt;
michael@0 1862 return;
michael@0 1863 }
michael@0 1864 return;
michael@0 1865 }
michael@0 1866 // Ignore this line and continue
michael@0 1867 fVerbs--;
michael@0 1868 fPts++;
michael@0 1869 break;
michael@0 1870
michael@0 1871 case kConic_Verb:
michael@0 1872 case kQuad_Verb:
michael@0 1873 if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1])) {
michael@0 1874 if (lastMoveVerb) {
michael@0 1875 fVerbs = lastMoveVerb;
michael@0 1876 fPts = lastMovePt;
michael@0 1877 return;
michael@0 1878 }
michael@0 1879 return;
michael@0 1880 }
michael@0 1881 // Ignore this line and continue
michael@0 1882 fVerbs--;
michael@0 1883 fPts += 2;
michael@0 1884 fConicWeights += (kConic_Verb == verb);
michael@0 1885 break;
michael@0 1886
michael@0 1887 case kCubic_Verb:
michael@0 1888 if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2])) {
michael@0 1889 if (lastMoveVerb) {
michael@0 1890 fVerbs = lastMoveVerb;
michael@0 1891 fPts = lastMovePt;
michael@0 1892 return;
michael@0 1893 }
michael@0 1894 return;
michael@0 1895 }
michael@0 1896 // Ignore this line and continue
michael@0 1897 fVerbs--;
michael@0 1898 fPts += 3;
michael@0 1899 break;
michael@0 1900
michael@0 1901 default:
michael@0 1902 SkDEBUGFAIL("Should never see kDone_Verb");
michael@0 1903 }
michael@0 1904 }
michael@0 1905 }
michael@0 1906
michael@0 1907 SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
michael@0 1908 SkASSERT(ptsParam);
michael@0 1909
michael@0 1910 if (fVerbs == fVerbStop) {
michael@0 1911 // Close the curve if requested and if there is some curve to close
michael@0 1912 if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
michael@0 1913 if (kLine_Verb == this->autoClose(ptsParam)) {
michael@0 1914 return kLine_Verb;
michael@0 1915 }
michael@0 1916 fNeedClose = false;
michael@0 1917 return kClose_Verb;
michael@0 1918 }
michael@0 1919 return kDone_Verb;
michael@0 1920 }
michael@0 1921
michael@0 1922 // fVerbs is one beyond the current verb, decrement first
michael@0 1923 unsigned verb = *(--fVerbs);
michael@0 1924 const SkPoint* SK_RESTRICT srcPts = fPts;
michael@0 1925 SkPoint* SK_RESTRICT pts = ptsParam;
michael@0 1926
michael@0 1927 switch (verb) {
michael@0 1928 case kMove_Verb:
michael@0 1929 if (fNeedClose) {
michael@0 1930 fVerbs++; // move back one verb
michael@0 1931 verb = this->autoClose(pts);
michael@0 1932 if (verb == kClose_Verb) {
michael@0 1933 fNeedClose = false;
michael@0 1934 }
michael@0 1935 return (Verb)verb;
michael@0 1936 }
michael@0 1937 if (fVerbs == fVerbStop) { // might be a trailing moveto
michael@0 1938 return kDone_Verb;
michael@0 1939 }
michael@0 1940 fMoveTo = *srcPts;
michael@0 1941 pts[0] = *srcPts;
michael@0 1942 srcPts += 1;
michael@0 1943 fSegmentState = kAfterMove_SegmentState;
michael@0 1944 fLastPt = fMoveTo;
michael@0 1945 fNeedClose = fForceClose;
michael@0 1946 break;
michael@0 1947 case kLine_Verb:
michael@0 1948 pts[0] = this->cons_moveTo();
michael@0 1949 pts[1] = srcPts[0];
michael@0 1950 fLastPt = srcPts[0];
michael@0 1951 fCloseLine = false;
michael@0 1952 srcPts += 1;
michael@0 1953 break;
michael@0 1954 case kConic_Verb:
michael@0 1955 fConicWeights += 1;
michael@0 1956 // fall-through
michael@0 1957 case kQuad_Verb:
michael@0 1958 pts[0] = this->cons_moveTo();
michael@0 1959 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
michael@0 1960 fLastPt = srcPts[1];
michael@0 1961 srcPts += 2;
michael@0 1962 break;
michael@0 1963 case kCubic_Verb:
michael@0 1964 pts[0] = this->cons_moveTo();
michael@0 1965 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
michael@0 1966 fLastPt = srcPts[2];
michael@0 1967 srcPts += 3;
michael@0 1968 break;
michael@0 1969 case kClose_Verb:
michael@0 1970 verb = this->autoClose(pts);
michael@0 1971 if (verb == kLine_Verb) {
michael@0 1972 fVerbs++; // move back one verb
michael@0 1973 } else {
michael@0 1974 fNeedClose = false;
michael@0 1975 fSegmentState = kEmptyContour_SegmentState;
michael@0 1976 }
michael@0 1977 fLastPt = fMoveTo;
michael@0 1978 break;
michael@0 1979 }
michael@0 1980 fPts = srcPts;
michael@0 1981 return (Verb)verb;
michael@0 1982 }
michael@0 1983
michael@0 1984 ///////////////////////////////////////////////////////////////////////////////
michael@0 1985
michael@0 1986 SkPath::RawIter::RawIter() {
michael@0 1987 #ifdef SK_DEBUG
michael@0 1988 fPts = NULL;
michael@0 1989 fConicWeights = NULL;
michael@0 1990 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
michael@0 1991 #endif
michael@0 1992 // need to init enough to make next() harmlessly return kDone_Verb
michael@0 1993 fVerbs = NULL;
michael@0 1994 fVerbStop = NULL;
michael@0 1995 }
michael@0 1996
michael@0 1997 SkPath::RawIter::RawIter(const SkPath& path) {
michael@0 1998 this->setPath(path);
michael@0 1999 }
michael@0 2000
michael@0 2001 void SkPath::RawIter::setPath(const SkPath& path) {
michael@0 2002 fPts = path.fPathRef->points();
michael@0 2003 fVerbs = path.fPathRef->verbs();
michael@0 2004 fVerbStop = path.fPathRef->verbsMemBegin();
michael@0 2005 fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
michael@0 2006 fMoveTo.fX = fMoveTo.fY = 0;
michael@0 2007 fLastPt.fX = fLastPt.fY = 0;
michael@0 2008 }
michael@0 2009
michael@0 2010 SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
michael@0 2011 SkASSERT(NULL != pts);
michael@0 2012 if (fVerbs == fVerbStop) {
michael@0 2013 return kDone_Verb;
michael@0 2014 }
michael@0 2015
michael@0 2016 // fVerbs points one beyond next verb so decrement first.
michael@0 2017 unsigned verb = *(--fVerbs);
michael@0 2018 const SkPoint* srcPts = fPts;
michael@0 2019
michael@0 2020 switch (verb) {
michael@0 2021 case kMove_Verb:
michael@0 2022 pts[0] = *srcPts;
michael@0 2023 fMoveTo = srcPts[0];
michael@0 2024 fLastPt = fMoveTo;
michael@0 2025 srcPts += 1;
michael@0 2026 break;
michael@0 2027 case kLine_Verb:
michael@0 2028 pts[0] = fLastPt;
michael@0 2029 pts[1] = srcPts[0];
michael@0 2030 fLastPt = srcPts[0];
michael@0 2031 srcPts += 1;
michael@0 2032 break;
michael@0 2033 case kConic_Verb:
michael@0 2034 fConicWeights += 1;
michael@0 2035 // fall-through
michael@0 2036 case kQuad_Verb:
michael@0 2037 pts[0] = fLastPt;
michael@0 2038 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
michael@0 2039 fLastPt = srcPts[1];
michael@0 2040 srcPts += 2;
michael@0 2041 break;
michael@0 2042 case kCubic_Verb:
michael@0 2043 pts[0] = fLastPt;
michael@0 2044 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
michael@0 2045 fLastPt = srcPts[2];
michael@0 2046 srcPts += 3;
michael@0 2047 break;
michael@0 2048 case kClose_Verb:
michael@0 2049 fLastPt = fMoveTo;
michael@0 2050 pts[0] = fMoveTo;
michael@0 2051 break;
michael@0 2052 }
michael@0 2053 fPts = srcPts;
michael@0 2054 return (Verb)verb;
michael@0 2055 }
michael@0 2056
michael@0 2057 ///////////////////////////////////////////////////////////////////////////////
michael@0 2058
michael@0 2059 /*
michael@0 2060 Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
michael@0 2061 */
michael@0 2062
michael@0 2063 size_t SkPath::writeToMemory(void* storage) const {
michael@0 2064 SkDEBUGCODE(this->validate();)
michael@0 2065
michael@0 2066 if (NULL == storage) {
michael@0 2067 const int byteCount = sizeof(int32_t) + fPathRef->writeSize();
michael@0 2068 return SkAlign4(byteCount);
michael@0 2069 }
michael@0 2070
michael@0 2071 SkWBuffer buffer(storage);
michael@0 2072
michael@0 2073 int32_t packed = (fConvexity << kConvexity_SerializationShift) |
michael@0 2074 (fFillType << kFillType_SerializationShift) |
michael@0 2075 (fDirection << kDirection_SerializationShift);
michael@0 2076
michael@0 2077 buffer.write32(packed);
michael@0 2078
michael@0 2079 fPathRef->writeToBuffer(&buffer);
michael@0 2080
michael@0 2081 buffer.padToAlign4();
michael@0 2082 return buffer.pos();
michael@0 2083 }
michael@0 2084
michael@0 2085 size_t SkPath::readFromMemory(const void* storage, size_t length) {
michael@0 2086 SkRBufferWithSizeCheck buffer(storage, length);
michael@0 2087
michael@0 2088 int32_t packed;
michael@0 2089 if (!buffer.readS32(&packed)) {
michael@0 2090 return 0;
michael@0 2091 }
michael@0 2092
michael@0 2093 fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
michael@0 2094 fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
michael@0 2095 fDirection = (packed >> kDirection_SerializationShift) & 0x3;
michael@0 2096 SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer);
michael@0 2097
michael@0 2098 size_t sizeRead = 0;
michael@0 2099 if (buffer.isValid()) {
michael@0 2100 fPathRef.reset(pathRef);
michael@0 2101 SkDEBUGCODE(this->validate();)
michael@0 2102 buffer.skipToAlign4();
michael@0 2103 sizeRead = buffer.pos();
michael@0 2104 } else if (NULL != pathRef) {
michael@0 2105 // If the buffer is not valid, pathRef should be NULL
michael@0 2106 sk_throw();
michael@0 2107 }
michael@0 2108 return sizeRead;
michael@0 2109 }
michael@0 2110
michael@0 2111 ///////////////////////////////////////////////////////////////////////////////
michael@0 2112
michael@0 2113 #include "SkString.h"
michael@0 2114
michael@0 2115 static void append_scalar(SkString* str, SkScalar value) {
michael@0 2116 SkString tmp;
michael@0 2117 tmp.printf("%g", value);
michael@0 2118 if (tmp.contains('.')) {
michael@0 2119 tmp.appendUnichar('f');
michael@0 2120 }
michael@0 2121 str->append(tmp);
michael@0 2122 }
michael@0 2123
michael@0 2124 static void append_params(SkString* str, const char label[], const SkPoint pts[],
michael@0 2125 int count, SkScalar conicWeight = -1) {
michael@0 2126 str->append(label);
michael@0 2127 str->append("(");
michael@0 2128
michael@0 2129 const SkScalar* values = &pts[0].fX;
michael@0 2130 count *= 2;
michael@0 2131
michael@0 2132 for (int i = 0; i < count; ++i) {
michael@0 2133 append_scalar(str, values[i]);
michael@0 2134 if (i < count - 1) {
michael@0 2135 str->append(", ");
michael@0 2136 }
michael@0 2137 }
michael@0 2138 if (conicWeight >= 0) {
michael@0 2139 str->append(", ");
michael@0 2140 append_scalar(str, conicWeight);
michael@0 2141 }
michael@0 2142 str->append(");\n");
michael@0 2143 }
michael@0 2144
michael@0 2145 void SkPath::dump(bool forceClose, const char title[]) const {
michael@0 2146 Iter iter(*this, forceClose);
michael@0 2147 SkPoint pts[4];
michael@0 2148 Verb verb;
michael@0 2149
michael@0 2150 SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
michael@0 2151 title ? title : "");
michael@0 2152
michael@0 2153 SkString builder;
michael@0 2154
michael@0 2155 while ((verb = iter.next(pts, false)) != kDone_Verb) {
michael@0 2156 switch (verb) {
michael@0 2157 case kMove_Verb:
michael@0 2158 append_params(&builder, "path.moveTo", &pts[0], 1);
michael@0 2159 break;
michael@0 2160 case kLine_Verb:
michael@0 2161 append_params(&builder, "path.lineTo", &pts[1], 1);
michael@0 2162 break;
michael@0 2163 case kQuad_Verb:
michael@0 2164 append_params(&builder, "path.quadTo", &pts[1], 2);
michael@0 2165 break;
michael@0 2166 case kConic_Verb:
michael@0 2167 append_params(&builder, "path.conicTo", &pts[1], 2, iter.conicWeight());
michael@0 2168 break;
michael@0 2169 case kCubic_Verb:
michael@0 2170 append_params(&builder, "path.cubicTo", &pts[1], 3);
michael@0 2171 break;
michael@0 2172 case kClose_Verb:
michael@0 2173 builder.append("path.close();\n");
michael@0 2174 break;
michael@0 2175 default:
michael@0 2176 SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb);
michael@0 2177 verb = kDone_Verb; // stop the loop
michael@0 2178 break;
michael@0 2179 }
michael@0 2180 }
michael@0 2181 SkDebugf("%s\n", builder.c_str());
michael@0 2182 }
michael@0 2183
michael@0 2184 void SkPath::dump() const {
michael@0 2185 this->dump(false);
michael@0 2186 }
michael@0 2187
michael@0 2188 #ifdef SK_DEBUG
michael@0 2189 void SkPath::validate() const {
michael@0 2190 SkASSERT(this != NULL);
michael@0 2191 SkASSERT((fFillType & ~3) == 0);
michael@0 2192
michael@0 2193 #ifdef SK_DEBUG_PATH
michael@0 2194 if (!fBoundsIsDirty) {
michael@0 2195 SkRect bounds;
michael@0 2196
michael@0 2197 bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
michael@0 2198 SkASSERT(SkToBool(fIsFinite) == isFinite);
michael@0 2199
michael@0 2200 if (fPathRef->countPoints() <= 1) {
michael@0 2201 // if we're empty, fBounds may be empty but translated, so we can't
michael@0 2202 // necessarily compare to bounds directly
michael@0 2203 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
michael@0 2204 // be [2, 2, 2, 2]
michael@0 2205 SkASSERT(bounds.isEmpty());
michael@0 2206 SkASSERT(fBounds.isEmpty());
michael@0 2207 } else {
michael@0 2208 if (bounds.isEmpty()) {
michael@0 2209 SkASSERT(fBounds.isEmpty());
michael@0 2210 } else {
michael@0 2211 if (!fBounds.isEmpty()) {
michael@0 2212 SkASSERT(fBounds.contains(bounds));
michael@0 2213 }
michael@0 2214 }
michael@0 2215 }
michael@0 2216 }
michael@0 2217 #endif // SK_DEBUG_PATH
michael@0 2218 }
michael@0 2219 #endif // SK_DEBUG
michael@0 2220
michael@0 2221 ///////////////////////////////////////////////////////////////////////////////
michael@0 2222
michael@0 2223 static int sign(SkScalar x) { return x < 0; }
michael@0 2224 #define kValueNeverReturnedBySign 2
michael@0 2225
michael@0 2226 static bool AlmostEqual(SkScalar compA, SkScalar compB) {
michael@0 2227 // The error epsilon was empirically derived; worse case round rects
michael@0 2228 // with a mid point outset by 2x float epsilon in tests had an error
michael@0 2229 // of 12.
michael@0 2230 const int epsilon = 16;
michael@0 2231 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
michael@0 2232 return false;
michael@0 2233 }
michael@0 2234 // no need to check for small numbers because SkPath::Iter has removed degenerate values
michael@0 2235 int aBits = SkFloatAs2sCompliment(compA);
michael@0 2236 int bBits = SkFloatAs2sCompliment(compB);
michael@0 2237 return aBits < bBits + epsilon && bBits < aBits + epsilon;
michael@0 2238 }
michael@0 2239
michael@0 2240 // only valid for a single contour
michael@0 2241 struct Convexicator {
michael@0 2242 Convexicator()
michael@0 2243 : fPtCount(0)
michael@0 2244 , fConvexity(SkPath::kConvex_Convexity)
michael@0 2245 , fDirection(SkPath::kUnknown_Direction) {
michael@0 2246 fSign = 0;
michael@0 2247 // warnings
michael@0 2248 fLastPt.set(0, 0);
michael@0 2249 fCurrPt.set(0, 0);
michael@0 2250 fVec0.set(0, 0);
michael@0 2251 fVec1.set(0, 0);
michael@0 2252 fFirstVec.set(0, 0);
michael@0 2253
michael@0 2254 fDx = fDy = 0;
michael@0 2255 fSx = fSy = kValueNeverReturnedBySign;
michael@0 2256 }
michael@0 2257
michael@0 2258 SkPath::Convexity getConvexity() const { return fConvexity; }
michael@0 2259
michael@0 2260 /** The direction returned is only valid if the path is determined convex */
michael@0 2261 SkPath::Direction getDirection() const { return fDirection; }
michael@0 2262
michael@0 2263 void addPt(const SkPoint& pt) {
michael@0 2264 if (SkPath::kConcave_Convexity == fConvexity) {
michael@0 2265 return;
michael@0 2266 }
michael@0 2267
michael@0 2268 if (0 == fPtCount) {
michael@0 2269 fCurrPt = pt;
michael@0 2270 ++fPtCount;
michael@0 2271 } else {
michael@0 2272 SkVector vec = pt - fCurrPt;
michael@0 2273 if (vec.fX || vec.fY) {
michael@0 2274 fLastPt = fCurrPt;
michael@0 2275 fCurrPt = pt;
michael@0 2276 if (++fPtCount == 2) {
michael@0 2277 fFirstVec = fVec1 = vec;
michael@0 2278 } else {
michael@0 2279 SkASSERT(fPtCount > 2);
michael@0 2280 this->addVec(vec);
michael@0 2281 }
michael@0 2282
michael@0 2283 int sx = sign(vec.fX);
michael@0 2284 int sy = sign(vec.fY);
michael@0 2285 fDx += (sx != fSx);
michael@0 2286 fDy += (sy != fSy);
michael@0 2287 fSx = sx;
michael@0 2288 fSy = sy;
michael@0 2289
michael@0 2290 if (fDx > 3 || fDy > 3) {
michael@0 2291 fConvexity = SkPath::kConcave_Convexity;
michael@0 2292 }
michael@0 2293 }
michael@0 2294 }
michael@0 2295 }
michael@0 2296
michael@0 2297 void close() {
michael@0 2298 if (fPtCount > 2) {
michael@0 2299 this->addVec(fFirstVec);
michael@0 2300 }
michael@0 2301 }
michael@0 2302
michael@0 2303 private:
michael@0 2304 void addVec(const SkVector& vec) {
michael@0 2305 SkASSERT(vec.fX || vec.fY);
michael@0 2306 fVec0 = fVec1;
michael@0 2307 fVec1 = vec;
michael@0 2308 SkScalar cross = SkPoint::CrossProduct(fVec0, fVec1);
michael@0 2309 SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
michael@0 2310 SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
michael@0 2311 largest = SkTMax(largest, -smallest);
michael@0 2312 int sign = AlmostEqual(largest, largest + cross) ? 0 : SkScalarSignAsInt(cross);
michael@0 2313 if (0 == fSign) {
michael@0 2314 fSign = sign;
michael@0 2315 if (1 == sign) {
michael@0 2316 fDirection = SkPath::kCW_Direction;
michael@0 2317 } else if (-1 == sign) {
michael@0 2318 fDirection = SkPath::kCCW_Direction;
michael@0 2319 }
michael@0 2320 } else if (sign) {
michael@0 2321 if (fSign != sign) {
michael@0 2322 fConvexity = SkPath::kConcave_Convexity;
michael@0 2323 fDirection = SkPath::kUnknown_Direction;
michael@0 2324 }
michael@0 2325 }
michael@0 2326 }
michael@0 2327
michael@0 2328 SkPoint fLastPt;
michael@0 2329 SkPoint fCurrPt;
michael@0 2330 SkVector fVec0, fVec1, fFirstVec;
michael@0 2331 int fPtCount; // non-degenerate points
michael@0 2332 int fSign;
michael@0 2333 SkPath::Convexity fConvexity;
michael@0 2334 SkPath::Direction fDirection;
michael@0 2335 int fDx, fDy, fSx, fSy;
michael@0 2336 };
michael@0 2337
michael@0 2338 SkPath::Convexity SkPath::internalGetConvexity() const {
michael@0 2339 SkASSERT(kUnknown_Convexity == fConvexity);
michael@0 2340 SkPoint pts[4];
michael@0 2341 SkPath::Verb verb;
michael@0 2342 SkPath::Iter iter(*this, true);
michael@0 2343
michael@0 2344 int contourCount = 0;
michael@0 2345 int count;
michael@0 2346 Convexicator state;
michael@0 2347
michael@0 2348 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
michael@0 2349 switch (verb) {
michael@0 2350 case kMove_Verb:
michael@0 2351 if (++contourCount > 1) {
michael@0 2352 fConvexity = kConcave_Convexity;
michael@0 2353 return kConcave_Convexity;
michael@0 2354 }
michael@0 2355 pts[1] = pts[0];
michael@0 2356 count = 1;
michael@0 2357 break;
michael@0 2358 case kLine_Verb: count = 1; break;
michael@0 2359 case kQuad_Verb: count = 2; break;
michael@0 2360 case kConic_Verb: count = 2; break;
michael@0 2361 case kCubic_Verb: count = 3; break;
michael@0 2362 case kClose_Verb:
michael@0 2363 state.close();
michael@0 2364 count = 0;
michael@0 2365 break;
michael@0 2366 default:
michael@0 2367 SkDEBUGFAIL("bad verb");
michael@0 2368 fConvexity = kConcave_Convexity;
michael@0 2369 return kConcave_Convexity;
michael@0 2370 }
michael@0 2371
michael@0 2372 for (int i = 1; i <= count; i++) {
michael@0 2373 state.addPt(pts[i]);
michael@0 2374 }
michael@0 2375 // early exit
michael@0 2376 if (kConcave_Convexity == state.getConvexity()) {
michael@0 2377 fConvexity = kConcave_Convexity;
michael@0 2378 return kConcave_Convexity;
michael@0 2379 }
michael@0 2380 }
michael@0 2381 fConvexity = state.getConvexity();
michael@0 2382 if (kConvex_Convexity == fConvexity && kUnknown_Direction == fDirection) {
michael@0 2383 fDirection = state.getDirection();
michael@0 2384 }
michael@0 2385 return static_cast<Convexity>(fConvexity);
michael@0 2386 }
michael@0 2387
michael@0 2388 ///////////////////////////////////////////////////////////////////////////////
michael@0 2389
michael@0 2390 class ContourIter {
michael@0 2391 public:
michael@0 2392 ContourIter(const SkPathRef& pathRef);
michael@0 2393
michael@0 2394 bool done() const { return fDone; }
michael@0 2395 // if !done() then these may be called
michael@0 2396 int count() const { return fCurrPtCount; }
michael@0 2397 const SkPoint* pts() const { return fCurrPt; }
michael@0 2398 void next();
michael@0 2399
michael@0 2400 private:
michael@0 2401 int fCurrPtCount;
michael@0 2402 const SkPoint* fCurrPt;
michael@0 2403 const uint8_t* fCurrVerb;
michael@0 2404 const uint8_t* fStopVerbs;
michael@0 2405 const SkScalar* fCurrConicWeight;
michael@0 2406 bool fDone;
michael@0 2407 SkDEBUGCODE(int fContourCounter;)
michael@0 2408 };
michael@0 2409
michael@0 2410 ContourIter::ContourIter(const SkPathRef& pathRef) {
michael@0 2411 fStopVerbs = pathRef.verbsMemBegin();
michael@0 2412 fDone = false;
michael@0 2413 fCurrPt = pathRef.points();
michael@0 2414 fCurrVerb = pathRef.verbs();
michael@0 2415 fCurrConicWeight = pathRef.conicWeights();
michael@0 2416 fCurrPtCount = 0;
michael@0 2417 SkDEBUGCODE(fContourCounter = 0;)
michael@0 2418 this->next();
michael@0 2419 }
michael@0 2420
michael@0 2421 void ContourIter::next() {
michael@0 2422 if (fCurrVerb <= fStopVerbs) {
michael@0 2423 fDone = true;
michael@0 2424 }
michael@0 2425 if (fDone) {
michael@0 2426 return;
michael@0 2427 }
michael@0 2428
michael@0 2429 // skip pts of prev contour
michael@0 2430 fCurrPt += fCurrPtCount;
michael@0 2431
michael@0 2432 SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
michael@0 2433 int ptCount = 1; // moveTo
michael@0 2434 const uint8_t* verbs = fCurrVerb;
michael@0 2435
michael@0 2436 for (--verbs; verbs > fStopVerbs; --verbs) {
michael@0 2437 switch (verbs[~0]) {
michael@0 2438 case SkPath::kMove_Verb:
michael@0 2439 goto CONTOUR_END;
michael@0 2440 case SkPath::kLine_Verb:
michael@0 2441 ptCount += 1;
michael@0 2442 break;
michael@0 2443 case SkPath::kConic_Verb:
michael@0 2444 fCurrConicWeight += 1;
michael@0 2445 // fall-through
michael@0 2446 case SkPath::kQuad_Verb:
michael@0 2447 ptCount += 2;
michael@0 2448 break;
michael@0 2449 case SkPath::kCubic_Verb:
michael@0 2450 ptCount += 3;
michael@0 2451 break;
michael@0 2452 case SkPath::kClose_Verb:
michael@0 2453 break;
michael@0 2454 default:
michael@0 2455 SkDEBUGFAIL("unexpected verb");
michael@0 2456 break;
michael@0 2457 }
michael@0 2458 }
michael@0 2459 CONTOUR_END:
michael@0 2460 fCurrPtCount = ptCount;
michael@0 2461 fCurrVerb = verbs;
michael@0 2462 SkDEBUGCODE(++fContourCounter;)
michael@0 2463 }
michael@0 2464
michael@0 2465 // returns cross product of (p1 - p0) and (p2 - p0)
michael@0 2466 static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
michael@0 2467 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
michael@0 2468 // We may get 0 when the above subtracts underflow. We expect this to be
michael@0 2469 // very rare and lazily promote to double.
michael@0 2470 if (0 == cross) {
michael@0 2471 double p0x = SkScalarToDouble(p0.fX);
michael@0 2472 double p0y = SkScalarToDouble(p0.fY);
michael@0 2473
michael@0 2474 double p1x = SkScalarToDouble(p1.fX);
michael@0 2475 double p1y = SkScalarToDouble(p1.fY);
michael@0 2476
michael@0 2477 double p2x = SkScalarToDouble(p2.fX);
michael@0 2478 double p2y = SkScalarToDouble(p2.fY);
michael@0 2479
michael@0 2480 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
michael@0 2481 (p1y - p0y) * (p2x - p0x));
michael@0 2482
michael@0 2483 }
michael@0 2484 return cross;
michael@0 2485 }
michael@0 2486
michael@0 2487 // Returns the first pt with the maximum Y coordinate
michael@0 2488 static int find_max_y(const SkPoint pts[], int count) {
michael@0 2489 SkASSERT(count > 0);
michael@0 2490 SkScalar max = pts[0].fY;
michael@0 2491 int firstIndex = 0;
michael@0 2492 for (int i = 1; i < count; ++i) {
michael@0 2493 SkScalar y = pts[i].fY;
michael@0 2494 if (y > max) {
michael@0 2495 max = y;
michael@0 2496 firstIndex = i;
michael@0 2497 }
michael@0 2498 }
michael@0 2499 return firstIndex;
michael@0 2500 }
michael@0 2501
michael@0 2502 static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
michael@0 2503 int i = index;
michael@0 2504 for (;;) {
michael@0 2505 i = (i + inc) % n;
michael@0 2506 if (i == index) { // we wrapped around, so abort
michael@0 2507 break;
michael@0 2508 }
michael@0 2509 if (pts[index] != pts[i]) { // found a different point, success!
michael@0 2510 break;
michael@0 2511 }
michael@0 2512 }
michael@0 2513 return i;
michael@0 2514 }
michael@0 2515
michael@0 2516 /**
michael@0 2517 * Starting at index, and moving forward (incrementing), find the xmin and
michael@0 2518 * xmax of the contiguous points that have the same Y.
michael@0 2519 */
michael@0 2520 static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
michael@0 2521 int* maxIndexPtr) {
michael@0 2522 const SkScalar y = pts[index].fY;
michael@0 2523 SkScalar min = pts[index].fX;
michael@0 2524 SkScalar max = min;
michael@0 2525 int minIndex = index;
michael@0 2526 int maxIndex = index;
michael@0 2527 for (int i = index + 1; i < n; ++i) {
michael@0 2528 if (pts[i].fY != y) {
michael@0 2529 break;
michael@0 2530 }
michael@0 2531 SkScalar x = pts[i].fX;
michael@0 2532 if (x < min) {
michael@0 2533 min = x;
michael@0 2534 minIndex = i;
michael@0 2535 } else if (x > max) {
michael@0 2536 max = x;
michael@0 2537 maxIndex = i;
michael@0 2538 }
michael@0 2539 }
michael@0 2540 *maxIndexPtr = maxIndex;
michael@0 2541 return minIndex;
michael@0 2542 }
michael@0 2543
michael@0 2544 static void crossToDir(SkScalar cross, SkPath::Direction* dir) {
michael@0 2545 *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
michael@0 2546 }
michael@0 2547
michael@0 2548 /*
michael@0 2549 * We loop through all contours, and keep the computed cross-product of the
michael@0 2550 * contour that contained the global y-max. If we just look at the first
michael@0 2551 * contour, we may find one that is wound the opposite way (correctly) since
michael@0 2552 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour
michael@0 2553 * that is outer most (or at least has the global y-max) before we can consider
michael@0 2554 * its cross product.
michael@0 2555 */
michael@0 2556 bool SkPath::cheapComputeDirection(Direction* dir) const {
michael@0 2557 if (kUnknown_Direction != fDirection) {
michael@0 2558 *dir = static_cast<Direction>(fDirection);
michael@0 2559 return true;
michael@0 2560 }
michael@0 2561
michael@0 2562 // don't want to pay the cost for computing this if it
michael@0 2563 // is unknown, so we don't call isConvex()
michael@0 2564 if (kConvex_Convexity == this->getConvexityOrUnknown()) {
michael@0 2565 SkASSERT(kUnknown_Direction == fDirection);
michael@0 2566 *dir = static_cast<Direction>(fDirection);
michael@0 2567 return false;
michael@0 2568 }
michael@0 2569
michael@0 2570 ContourIter iter(*fPathRef.get());
michael@0 2571
michael@0 2572 // initialize with our logical y-min
michael@0 2573 SkScalar ymax = this->getBounds().fTop;
michael@0 2574 SkScalar ymaxCross = 0;
michael@0 2575
michael@0 2576 for (; !iter.done(); iter.next()) {
michael@0 2577 int n = iter.count();
michael@0 2578 if (n < 3) {
michael@0 2579 continue;
michael@0 2580 }
michael@0 2581
michael@0 2582 const SkPoint* pts = iter.pts();
michael@0 2583 SkScalar cross = 0;
michael@0 2584 int index = find_max_y(pts, n);
michael@0 2585 if (pts[index].fY < ymax) {
michael@0 2586 continue;
michael@0 2587 }
michael@0 2588
michael@0 2589 // If there is more than 1 distinct point at the y-max, we take the
michael@0 2590 // x-min and x-max of them and just subtract to compute the dir.
michael@0 2591 if (pts[(index + 1) % n].fY == pts[index].fY) {
michael@0 2592 int maxIndex;
michael@0 2593 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
michael@0 2594 if (minIndex == maxIndex) {
michael@0 2595 goto TRY_CROSSPROD;
michael@0 2596 }
michael@0 2597 SkASSERT(pts[minIndex].fY == pts[index].fY);
michael@0 2598 SkASSERT(pts[maxIndex].fY == pts[index].fY);
michael@0 2599 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
michael@0 2600 // we just subtract the indices, and let that auto-convert to
michael@0 2601 // SkScalar, since we just want - or + to signal the direction.
michael@0 2602 cross = minIndex - maxIndex;
michael@0 2603 } else {
michael@0 2604 TRY_CROSSPROD:
michael@0 2605 // Find a next and prev index to use for the cross-product test,
michael@0 2606 // but we try to find pts that form non-zero vectors from pts[index]
michael@0 2607 //
michael@0 2608 // Its possible that we can't find two non-degenerate vectors, so
michael@0 2609 // we have to guard our search (e.g. all the pts could be in the
michael@0 2610 // same place).
michael@0 2611
michael@0 2612 // we pass n - 1 instead of -1 so we don't foul up % operator by
michael@0 2613 // passing it a negative LH argument.
michael@0 2614 int prev = find_diff_pt(pts, index, n, n - 1);
michael@0 2615 if (prev == index) {
michael@0 2616 // completely degenerate, skip to next contour
michael@0 2617 continue;
michael@0 2618 }
michael@0 2619 int next = find_diff_pt(pts, index, n, 1);
michael@0 2620 SkASSERT(next != index);
michael@0 2621 cross = cross_prod(pts[prev], pts[index], pts[next]);
michael@0 2622 // if we get a zero and the points are horizontal, then we look at the spread in
michael@0 2623 // x-direction. We really should continue to walk away from the degeneracy until
michael@0 2624 // there is a divergence.
michael@0 2625 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
michael@0 2626 // construct the subtract so we get the correct Direction below
michael@0 2627 cross = pts[index].fX - pts[next].fX;
michael@0 2628 }
michael@0 2629 }
michael@0 2630
michael@0 2631 if (cross) {
michael@0 2632 // record our best guess so far
michael@0 2633 ymax = pts[index].fY;
michael@0 2634 ymaxCross = cross;
michael@0 2635 }
michael@0 2636 }
michael@0 2637 if (ymaxCross) {
michael@0 2638 crossToDir(ymaxCross, dir);
michael@0 2639 fDirection = *dir;
michael@0 2640 return true;
michael@0 2641 } else {
michael@0 2642 return false;
michael@0 2643 }
michael@0 2644 }
michael@0 2645
michael@0 2646 ///////////////////////////////////////////////////////////////////////////////
michael@0 2647
michael@0 2648 static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C,
michael@0 2649 SkScalar D, SkScalar t) {
michael@0 2650 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
michael@0 2651 }
michael@0 2652
michael@0 2653 static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
michael@0 2654 SkScalar t) {
michael@0 2655 SkScalar A = c3 + 3*(c1 - c2) - c0;
michael@0 2656 SkScalar B = 3*(c2 - c1 - c1 + c0);
michael@0 2657 SkScalar C = 3*(c1 - c0);
michael@0 2658 SkScalar D = c0;
michael@0 2659 return eval_cubic_coeff(A, B, C, D, t);
michael@0 2660 }
michael@0 2661
michael@0 2662 /* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the
michael@0 2663 t value such that cubic(t) = target
michael@0 2664 */
michael@0 2665 static void chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
michael@0 2666 SkScalar target, SkScalar* t) {
michael@0 2667 // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3);
michael@0 2668 SkASSERT(c0 < target && target < c3);
michael@0 2669
michael@0 2670 SkScalar D = c0 - target;
michael@0 2671 SkScalar A = c3 + 3*(c1 - c2) - c0;
michael@0 2672 SkScalar B = 3*(c2 - c1 - c1 + c0);
michael@0 2673 SkScalar C = 3*(c1 - c0);
michael@0 2674
michael@0 2675 const SkScalar TOLERANCE = SK_Scalar1 / 4096;
michael@0 2676 SkScalar minT = 0;
michael@0 2677 SkScalar maxT = SK_Scalar1;
michael@0 2678 SkScalar mid;
michael@0 2679 int i;
michael@0 2680 for (i = 0; i < 16; i++) {
michael@0 2681 mid = SkScalarAve(minT, maxT);
michael@0 2682 SkScalar delta = eval_cubic_coeff(A, B, C, D, mid);
michael@0 2683 if (delta < 0) {
michael@0 2684 minT = mid;
michael@0 2685 delta = -delta;
michael@0 2686 } else {
michael@0 2687 maxT = mid;
michael@0 2688 }
michael@0 2689 if (delta < TOLERANCE) {
michael@0 2690 break;
michael@0 2691 }
michael@0 2692 }
michael@0 2693 *t = mid;
michael@0 2694 }
michael@0 2695
michael@0 2696 template <size_t N> static void find_minmax(const SkPoint pts[],
michael@0 2697 SkScalar* minPtr, SkScalar* maxPtr) {
michael@0 2698 SkScalar min, max;
michael@0 2699 min = max = pts[0].fX;
michael@0 2700 for (size_t i = 1; i < N; ++i) {
michael@0 2701 min = SkMinScalar(min, pts[i].fX);
michael@0 2702 max = SkMaxScalar(max, pts[i].fX);
michael@0 2703 }
michael@0 2704 *minPtr = min;
michael@0 2705 *maxPtr = max;
michael@0 2706 }
michael@0 2707
michael@0 2708 static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y) {
michael@0 2709 SkPoint storage[4];
michael@0 2710
michael@0 2711 int dir = 1;
michael@0 2712 if (pts[0].fY > pts[3].fY) {
michael@0 2713 storage[0] = pts[3];
michael@0 2714 storage[1] = pts[2];
michael@0 2715 storage[2] = pts[1];
michael@0 2716 storage[3] = pts[0];
michael@0 2717 pts = storage;
michael@0 2718 dir = -1;
michael@0 2719 }
michael@0 2720 if (y < pts[0].fY || y >= pts[3].fY) {
michael@0 2721 return 0;
michael@0 2722 }
michael@0 2723
michael@0 2724 // quickreject or quickaccept
michael@0 2725 SkScalar min, max;
michael@0 2726 find_minmax<4>(pts, &min, &max);
michael@0 2727 if (x < min) {
michael@0 2728 return 0;
michael@0 2729 }
michael@0 2730 if (x > max) {
michael@0 2731 return dir;
michael@0 2732 }
michael@0 2733
michael@0 2734 // compute the actual x(t) value
michael@0 2735 SkScalar t;
michael@0 2736 chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, &t);
michael@0 2737 SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
michael@0 2738 return xt < x ? dir : 0;
michael@0 2739 }
michael@0 2740
michael@0 2741 static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y) {
michael@0 2742 SkPoint dst[10];
michael@0 2743 int n = SkChopCubicAtYExtrema(pts, dst);
michael@0 2744 int w = 0;
michael@0 2745 for (int i = 0; i <= n; ++i) {
michael@0 2746 w += winding_mono_cubic(&dst[i * 3], x, y);
michael@0 2747 }
michael@0 2748 return w;
michael@0 2749 }
michael@0 2750
michael@0 2751 static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) {
michael@0 2752 SkScalar y0 = pts[0].fY;
michael@0 2753 SkScalar y2 = pts[2].fY;
michael@0 2754
michael@0 2755 int dir = 1;
michael@0 2756 if (y0 > y2) {
michael@0 2757 SkTSwap(y0, y2);
michael@0 2758 dir = -1;
michael@0 2759 }
michael@0 2760 if (y < y0 || y >= y2) {
michael@0 2761 return 0;
michael@0 2762 }
michael@0 2763
michael@0 2764 // bounds check on X (not required. is it faster?)
michael@0 2765 #if 0
michael@0 2766 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
michael@0 2767 return 0;
michael@0 2768 }
michael@0 2769 #endif
michael@0 2770
michael@0 2771 SkScalar roots[2];
michael@0 2772 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
michael@0 2773 2 * (pts[1].fY - pts[0].fY),
michael@0 2774 pts[0].fY - y,
michael@0 2775 roots);
michael@0 2776 SkASSERT(n <= 1);
michael@0 2777 SkScalar xt;
michael@0 2778 if (0 == n) {
michael@0 2779 SkScalar mid = SkScalarAve(y0, y2);
michael@0 2780 // Need [0] and [2] if dir == 1
michael@0 2781 // and [2] and [0] if dir == -1
michael@0 2782 xt = y < mid ? pts[1 - dir].fX : pts[dir - 1].fX;
michael@0 2783 } else {
michael@0 2784 SkScalar t = roots[0];
michael@0 2785 SkScalar C = pts[0].fX;
michael@0 2786 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
michael@0 2787 SkScalar B = 2 * (pts[1].fX - C);
michael@0 2788 xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
michael@0 2789 }
michael@0 2790 return xt < x ? dir : 0;
michael@0 2791 }
michael@0 2792
michael@0 2793 static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
michael@0 2794 // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
michael@0 2795 if (y0 == y1) {
michael@0 2796 return true;
michael@0 2797 }
michael@0 2798 if (y0 < y1) {
michael@0 2799 return y1 <= y2;
michael@0 2800 } else {
michael@0 2801 return y1 >= y2;
michael@0 2802 }
michael@0 2803 }
michael@0 2804
michael@0 2805 static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y) {
michael@0 2806 SkPoint dst[5];
michael@0 2807 int n = 0;
michael@0 2808
michael@0 2809 if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
michael@0 2810 n = SkChopQuadAtYExtrema(pts, dst);
michael@0 2811 pts = dst;
michael@0 2812 }
michael@0 2813 int w = winding_mono_quad(pts, x, y);
michael@0 2814 if (n > 0) {
michael@0 2815 w += winding_mono_quad(&pts[2], x, y);
michael@0 2816 }
michael@0 2817 return w;
michael@0 2818 }
michael@0 2819
michael@0 2820 static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y) {
michael@0 2821 SkScalar x0 = pts[0].fX;
michael@0 2822 SkScalar y0 = pts[0].fY;
michael@0 2823 SkScalar x1 = pts[1].fX;
michael@0 2824 SkScalar y1 = pts[1].fY;
michael@0 2825
michael@0 2826 SkScalar dy = y1 - y0;
michael@0 2827
michael@0 2828 int dir = 1;
michael@0 2829 if (y0 > y1) {
michael@0 2830 SkTSwap(y0, y1);
michael@0 2831 dir = -1;
michael@0 2832 }
michael@0 2833 if (y < y0 || y >= y1) {
michael@0 2834 return 0;
michael@0 2835 }
michael@0 2836
michael@0 2837 SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) -
michael@0 2838 SkScalarMul(dy, x - pts[0].fX);
michael@0 2839
michael@0 2840 if (SkScalarSignAsInt(cross) == dir) {
michael@0 2841 dir = 0;
michael@0 2842 }
michael@0 2843 return dir;
michael@0 2844 }
michael@0 2845
michael@0 2846 static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
michael@0 2847 return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
michael@0 2848 }
michael@0 2849
michael@0 2850 bool SkPath::contains(SkScalar x, SkScalar y) const {
michael@0 2851 bool isInverse = this->isInverseFillType();
michael@0 2852 if (this->isEmpty()) {
michael@0 2853 return isInverse;
michael@0 2854 }
michael@0 2855
michael@0 2856 if (!contains_inclusive(this->getBounds(), x, y)) {
michael@0 2857 return isInverse;
michael@0 2858 }
michael@0 2859
michael@0 2860 SkPath::Iter iter(*this, true);
michael@0 2861 bool done = false;
michael@0 2862 int w = 0;
michael@0 2863 do {
michael@0 2864 SkPoint pts[4];
michael@0 2865 switch (iter.next(pts, false)) {
michael@0 2866 case SkPath::kMove_Verb:
michael@0 2867 case SkPath::kClose_Verb:
michael@0 2868 break;
michael@0 2869 case SkPath::kLine_Verb:
michael@0 2870 w += winding_line(pts, x, y);
michael@0 2871 break;
michael@0 2872 case SkPath::kQuad_Verb:
michael@0 2873 w += winding_quad(pts, x, y);
michael@0 2874 break;
michael@0 2875 case SkPath::kConic_Verb:
michael@0 2876 SkASSERT(0);
michael@0 2877 break;
michael@0 2878 case SkPath::kCubic_Verb:
michael@0 2879 w += winding_cubic(pts, x, y);
michael@0 2880 break;
michael@0 2881 case SkPath::kDone_Verb:
michael@0 2882 done = true;
michael@0 2883 break;
michael@0 2884 }
michael@0 2885 } while (!done);
michael@0 2886
michael@0 2887 switch (this->getFillType()) {
michael@0 2888 case SkPath::kEvenOdd_FillType:
michael@0 2889 case SkPath::kInverseEvenOdd_FillType:
michael@0 2890 w &= 1;
michael@0 2891 break;
michael@0 2892 default:
michael@0 2893 break;
michael@0 2894 }
michael@0 2895 return SkToBool(w);
michael@0 2896 }

mercurial