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

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

michael@0 1 /*
michael@0 2 * Copyright 2008 The Android Open Source Project
michael@0 3 *
michael@0 4 * Use of this source code is governed by a BSD-style license that can be
michael@0 5 * found in the LICENSE file.
michael@0 6 */
michael@0 7
michael@0 8 #include "SkStrokerPriv.h"
michael@0 9 #include "SkGeometry.h"
michael@0 10 #include "SkPath.h"
michael@0 11
michael@0 12 #define kMaxQuadSubdivide 5
michael@0 13 #define kMaxCubicSubdivide 7
michael@0 14
michael@0 15 static inline bool degenerate_vector(const SkVector& v) {
michael@0 16 return !SkPoint::CanNormalize(v.fX, v.fY);
michael@0 17 }
michael@0 18
michael@0 19 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
michael@0 20 /* root2/2 is a 45-degree angle
michael@0 21 make this constant bigger for more subdivisions (but not >= 1)
michael@0 22 */
michael@0 23 static const SkScalar kFlatEnoughNormalDotProd =
michael@0 24 SK_ScalarSqrt2/2 + SK_Scalar1/10;
michael@0 25
michael@0 26 SkASSERT(kFlatEnoughNormalDotProd > 0 &&
michael@0 27 kFlatEnoughNormalDotProd < SK_Scalar1);
michael@0 28
michael@0 29 return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
michael@0 30 }
michael@0 31
michael@0 32 static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
michael@0 33 // if the dot-product is -1, then we are definitely too pinchy. We tweak
michael@0 34 // that by an epsilon to ensure we have significant bits in our test
michael@0 35 static const int kMinSigBitsForDot = 8;
michael@0 36 static const SkScalar kDotEpsilon = FLT_EPSILON * (1 << kMinSigBitsForDot);
michael@0 37 static const SkScalar kTooPinchyNormalDotProd = kDotEpsilon - 1;
michael@0 38
michael@0 39 // just some sanity asserts to help document the expected range
michael@0 40 SkASSERT(kTooPinchyNormalDotProd >= -1);
michael@0 41 SkASSERT(kTooPinchyNormalDotProd < SkDoubleToScalar(-0.999));
michael@0 42
michael@0 43 SkScalar dot = SkPoint::DotProduct(norm0, norm1);
michael@0 44 return dot <= kTooPinchyNormalDotProd;
michael@0 45 }
michael@0 46
michael@0 47 static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
michael@0 48 SkScalar radius,
michael@0 49 SkVector* normal, SkVector* unitNormal) {
michael@0 50 if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
michael@0 51 return false;
michael@0 52 }
michael@0 53 unitNormal->rotateCCW();
michael@0 54 unitNormal->scale(radius, normal);
michael@0 55 return true;
michael@0 56 }
michael@0 57
michael@0 58 static bool set_normal_unitnormal(const SkVector& vec,
michael@0 59 SkScalar radius,
michael@0 60 SkVector* normal, SkVector* unitNormal) {
michael@0 61 if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
michael@0 62 return false;
michael@0 63 }
michael@0 64 unitNormal->rotateCCW();
michael@0 65 unitNormal->scale(radius, normal);
michael@0 66 return true;
michael@0 67 }
michael@0 68
michael@0 69 ///////////////////////////////////////////////////////////////////////////////
michael@0 70
michael@0 71 class SkPathStroker {
michael@0 72 public:
michael@0 73 SkPathStroker(const SkPath& src,
michael@0 74 SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
michael@0 75 SkPaint::Join join);
michael@0 76
michael@0 77 void moveTo(const SkPoint&);
michael@0 78 void lineTo(const SkPoint&);
michael@0 79 void quadTo(const SkPoint&, const SkPoint&);
michael@0 80 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
michael@0 81 void close(bool isLine) { this->finishContour(true, isLine); }
michael@0 82
michael@0 83 void done(SkPath* dst, bool isLine) {
michael@0 84 this->finishContour(false, isLine);
michael@0 85 fOuter.addPath(fExtra);
michael@0 86 dst->swap(fOuter);
michael@0 87 }
michael@0 88
michael@0 89 private:
michael@0 90 SkScalar fRadius;
michael@0 91 SkScalar fInvMiterLimit;
michael@0 92
michael@0 93 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
michael@0 94 SkPoint fFirstPt, fPrevPt; // on original path
michael@0 95 SkPoint fFirstOuterPt;
michael@0 96 int fSegmentCount;
michael@0 97 bool fPrevIsLine;
michael@0 98
michael@0 99 SkStrokerPriv::CapProc fCapper;
michael@0 100 SkStrokerPriv::JoinProc fJoiner;
michael@0 101
michael@0 102 SkPath fInner, fOuter; // outer is our working answer, inner is temp
michael@0 103 SkPath fExtra; // added as extra complete contours
michael@0 104
michael@0 105 void finishContour(bool close, bool isLine);
michael@0 106 void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
michael@0 107 bool isLine);
michael@0 108 void postJoinTo(const SkPoint&, const SkVector& normal,
michael@0 109 const SkVector& unitNormal);
michael@0 110
michael@0 111 void line_to(const SkPoint& currPt, const SkVector& normal);
michael@0 112 void quad_to(const SkPoint pts[3],
michael@0 113 const SkVector& normalAB, const SkVector& unitNormalAB,
michael@0 114 SkVector* normalBC, SkVector* unitNormalBC,
michael@0 115 int subDivide);
michael@0 116 void cubic_to(const SkPoint pts[4],
michael@0 117 const SkVector& normalAB, const SkVector& unitNormalAB,
michael@0 118 SkVector* normalCD, SkVector* unitNormalCD,
michael@0 119 int subDivide);
michael@0 120 };
michael@0 121
michael@0 122 ///////////////////////////////////////////////////////////////////////////////
michael@0 123
michael@0 124 void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
michael@0 125 SkVector* unitNormal, bool currIsLine) {
michael@0 126 SkASSERT(fSegmentCount >= 0);
michael@0 127
michael@0 128 SkScalar prevX = fPrevPt.fX;
michael@0 129 SkScalar prevY = fPrevPt.fY;
michael@0 130
michael@0 131 SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal,
michael@0 132 unitNormal));
michael@0 133
michael@0 134 if (fSegmentCount == 0) {
michael@0 135 fFirstNormal = *normal;
michael@0 136 fFirstUnitNormal = *unitNormal;
michael@0 137 fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
michael@0 138
michael@0 139 fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
michael@0 140 fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
michael@0 141 } else { // we have a previous segment
michael@0 142 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
michael@0 143 fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
michael@0 144 }
michael@0 145 fPrevIsLine = currIsLine;
michael@0 146 }
michael@0 147
michael@0 148 void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
michael@0 149 const SkVector& unitNormal) {
michael@0 150 fPrevPt = currPt;
michael@0 151 fPrevUnitNormal = unitNormal;
michael@0 152 fPrevNormal = normal;
michael@0 153 fSegmentCount += 1;
michael@0 154 }
michael@0 155
michael@0 156 void SkPathStroker::finishContour(bool close, bool currIsLine) {
michael@0 157 if (fSegmentCount > 0) {
michael@0 158 SkPoint pt;
michael@0 159
michael@0 160 if (close) {
michael@0 161 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
michael@0 162 fFirstUnitNormal, fRadius, fInvMiterLimit,
michael@0 163 fPrevIsLine, currIsLine);
michael@0 164 fOuter.close();
michael@0 165 // now add fInner as its own contour
michael@0 166 fInner.getLastPt(&pt);
michael@0 167 fOuter.moveTo(pt.fX, pt.fY);
michael@0 168 fOuter.reversePathTo(fInner);
michael@0 169 fOuter.close();
michael@0 170 } else { // add caps to start and end
michael@0 171 // cap the end
michael@0 172 fInner.getLastPt(&pt);
michael@0 173 fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
michael@0 174 currIsLine ? &fInner : NULL);
michael@0 175 fOuter.reversePathTo(fInner);
michael@0 176 // cap the start
michael@0 177 fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
michael@0 178 fPrevIsLine ? &fInner : NULL);
michael@0 179 fOuter.close();
michael@0 180 }
michael@0 181 }
michael@0 182 // since we may re-use fInner, we rewind instead of reset, to save on
michael@0 183 // reallocating its internal storage.
michael@0 184 fInner.rewind();
michael@0 185 fSegmentCount = -1;
michael@0 186 }
michael@0 187
michael@0 188 ///////////////////////////////////////////////////////////////////////////////
michael@0 189
michael@0 190 SkPathStroker::SkPathStroker(const SkPath& src,
michael@0 191 SkScalar radius, SkScalar miterLimit,
michael@0 192 SkPaint::Cap cap, SkPaint::Join join)
michael@0 193 : fRadius(radius) {
michael@0 194
michael@0 195 /* This is only used when join is miter_join, but we initialize it here
michael@0 196 so that it is always defined, to fis valgrind warnings.
michael@0 197 */
michael@0 198 fInvMiterLimit = 0;
michael@0 199
michael@0 200 if (join == SkPaint::kMiter_Join) {
michael@0 201 if (miterLimit <= SK_Scalar1) {
michael@0 202 join = SkPaint::kBevel_Join;
michael@0 203 } else {
michael@0 204 fInvMiterLimit = SkScalarInvert(miterLimit);
michael@0 205 }
michael@0 206 }
michael@0 207 fCapper = SkStrokerPriv::CapFactory(cap);
michael@0 208 fJoiner = SkStrokerPriv::JoinFactory(join);
michael@0 209 fSegmentCount = -1;
michael@0 210 fPrevIsLine = false;
michael@0 211
michael@0 212 // Need some estimate of how large our final result (fOuter)
michael@0 213 // and our per-contour temp (fInner) will be, so we don't spend
michael@0 214 // extra time repeatedly growing these arrays.
michael@0 215 //
michael@0 216 // 3x for result == inner + outer + join (swag)
michael@0 217 // 1x for inner == 'wag' (worst contour length would be better guess)
michael@0 218 fOuter.incReserve(src.countPoints() * 3);
michael@0 219 fInner.incReserve(src.countPoints());
michael@0 220 }
michael@0 221
michael@0 222 void SkPathStroker::moveTo(const SkPoint& pt) {
michael@0 223 if (fSegmentCount > 0) {
michael@0 224 this->finishContour(false, false);
michael@0 225 }
michael@0 226 fSegmentCount = 0;
michael@0 227 fFirstPt = fPrevPt = pt;
michael@0 228 }
michael@0 229
michael@0 230 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
michael@0 231 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
michael@0 232 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
michael@0 233 }
michael@0 234
michael@0 235 void SkPathStroker::lineTo(const SkPoint& currPt) {
michael@0 236 if (SkPath::IsLineDegenerate(fPrevPt, currPt)) {
michael@0 237 return;
michael@0 238 }
michael@0 239 SkVector normal, unitNormal;
michael@0 240
michael@0 241 this->preJoinTo(currPt, &normal, &unitNormal, true);
michael@0 242 this->line_to(currPt, normal);
michael@0 243 this->postJoinTo(currPt, normal, unitNormal);
michael@0 244 }
michael@0 245
michael@0 246 void SkPathStroker::quad_to(const SkPoint pts[3],
michael@0 247 const SkVector& normalAB, const SkVector& unitNormalAB,
michael@0 248 SkVector* normalBC, SkVector* unitNormalBC,
michael@0 249 int subDivide) {
michael@0 250 if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
michael@0 251 normalBC, unitNormalBC)) {
michael@0 252 // pts[1] nearly equals pts[2], so just draw a line to pts[2]
michael@0 253 this->line_to(pts[2], normalAB);
michael@0 254 *normalBC = normalAB;
michael@0 255 *unitNormalBC = unitNormalAB;
michael@0 256 return;
michael@0 257 }
michael@0 258
michael@0 259 if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
michael@0 260 SkPoint tmp[5];
michael@0 261 SkVector norm, unit;
michael@0 262
michael@0 263 SkChopQuadAtHalf(pts, tmp);
michael@0 264 this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
michael@0 265 this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
michael@0 266 } else {
michael@0 267 SkVector normalB;
michael@0 268
michael@0 269 normalB = pts[2] - pts[0];
michael@0 270 normalB.rotateCCW();
michael@0 271 SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC);
michael@0 272 SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
michael@0 273 SkScalarSqrt((SK_Scalar1 + dot)/2))));
michael@0 274
michael@0 275 fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
michael@0 276 pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
michael@0 277 fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
michael@0 278 pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
michael@0 279 }
michael@0 280 }
michael@0 281
michael@0 282 void SkPathStroker::cubic_to(const SkPoint pts[4],
michael@0 283 const SkVector& normalAB, const SkVector& unitNormalAB,
michael@0 284 SkVector* normalCD, SkVector* unitNormalCD,
michael@0 285 int subDivide) {
michael@0 286 SkVector ab = pts[1] - pts[0];
michael@0 287 SkVector cd = pts[3] - pts[2];
michael@0 288 SkVector normalBC, unitNormalBC;
michael@0 289
michael@0 290 bool degenerateAB = degenerate_vector(ab);
michael@0 291 bool degenerateCD = degenerate_vector(cd);
michael@0 292
michael@0 293 if (degenerateAB && degenerateCD) {
michael@0 294 DRAW_LINE:
michael@0 295 this->line_to(pts[3], normalAB);
michael@0 296 *normalCD = normalAB;
michael@0 297 *unitNormalCD = unitNormalAB;
michael@0 298 return;
michael@0 299 }
michael@0 300
michael@0 301 if (degenerateAB) {
michael@0 302 ab = pts[2] - pts[0];
michael@0 303 degenerateAB = degenerate_vector(ab);
michael@0 304 }
michael@0 305 if (degenerateCD) {
michael@0 306 cd = pts[3] - pts[1];
michael@0 307 degenerateCD = degenerate_vector(cd);
michael@0 308 }
michael@0 309 if (degenerateAB || degenerateCD) {
michael@0 310 goto DRAW_LINE;
michael@0 311 }
michael@0 312 SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
michael@0 313 bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
michael@0 314 &normalBC, &unitNormalBC);
michael@0 315 #ifndef SK_IGNORE_CUBIC_STROKE_FIX
michael@0 316 if (--subDivide < 0) {
michael@0 317 goto DRAW_LINE;
michael@0 318 }
michael@0 319 #endif
michael@0 320 if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
michael@0 321 normals_too_curvy(unitNormalBC, *unitNormalCD)) {
michael@0 322 #ifdef SK_IGNORE_CUBIC_STROKE_FIX
michael@0 323 // subdivide if we can
michael@0 324 if (--subDivide < 0) {
michael@0 325 goto DRAW_LINE;
michael@0 326 }
michael@0 327 #endif
michael@0 328 SkPoint tmp[7];
michael@0 329 SkVector norm, unit, dummy, unitDummy;
michael@0 330
michael@0 331 SkChopCubicAtHalf(pts, tmp);
michael@0 332 this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
michael@0 333 subDivide);
michael@0 334 // we use dummys since we already have a valid (and more accurate)
michael@0 335 // normals for CD
michael@0 336 this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
michael@0 337 } else {
michael@0 338 SkVector normalB, normalC;
michael@0 339
michael@0 340 // need normals to inset/outset the off-curve pts B and C
michael@0 341
michael@0 342 SkVector unitBC = pts[2] - pts[1];
michael@0 343 unitBC.normalize();
michael@0 344 unitBC.rotateCCW();
michael@0 345
michael@0 346 normalB = unitNormalAB + unitBC;
michael@0 347 normalC = *unitNormalCD + unitBC;
michael@0 348
michael@0 349 SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
michael@0 350 SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
michael@0 351 SkScalarSqrt((SK_Scalar1 + dot)/2))));
michael@0 352 dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
michael@0 353 SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
michael@0 354 SkScalarSqrt((SK_Scalar1 + dot)/2))));
michael@0 355
michael@0 356 fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
michael@0 357 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
michael@0 358 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
michael@0 359
michael@0 360 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
michael@0 361 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
michael@0 362 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
michael@0 363 }
michael@0 364 }
michael@0 365
michael@0 366 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
michael@0 367 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
michael@0 368 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
michael@0 369
michael@0 370 if (degenerateAB | degenerateBC) {
michael@0 371 if (degenerateAB ^ degenerateBC) {
michael@0 372 this->lineTo(pt2);
michael@0 373 }
michael@0 374 return;
michael@0 375 }
michael@0 376
michael@0 377 SkVector normalAB, unitAB, normalBC, unitBC;
michael@0 378
michael@0 379 this->preJoinTo(pt1, &normalAB, &unitAB, false);
michael@0 380
michael@0 381 {
michael@0 382 SkPoint pts[3], tmp[5];
michael@0 383 pts[0] = fPrevPt;
michael@0 384 pts[1] = pt1;
michael@0 385 pts[2] = pt2;
michael@0 386
michael@0 387 if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
michael@0 388 unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
michael@0 389 unitBC.rotateCCW();
michael@0 390 if (normals_too_pinchy(unitAB, unitBC)) {
michael@0 391 normalBC = unitBC;
michael@0 392 normalBC.scale(fRadius);
michael@0 393
michael@0 394 fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
michael@0 395 fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
michael@0 396 fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
michael@0 397
michael@0 398 fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
michael@0 399 fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
michael@0 400 fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
michael@0 401
michael@0 402 fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
michael@0 403 SkPath::kCW_Direction);
michael@0 404 } else {
michael@0 405 this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
michael@0 406 kMaxQuadSubdivide);
michael@0 407 SkVector n = normalBC;
michael@0 408 SkVector u = unitBC;
michael@0 409 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
michael@0 410 kMaxQuadSubdivide);
michael@0 411 }
michael@0 412 } else {
michael@0 413 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
michael@0 414 kMaxQuadSubdivide);
michael@0 415 }
michael@0 416 }
michael@0 417
michael@0 418 this->postJoinTo(pt2, normalBC, unitBC);
michael@0 419 }
michael@0 420
michael@0 421 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
michael@0 422 const SkPoint& pt3) {
michael@0 423 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
michael@0 424 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
michael@0 425 bool degenerateCD = SkPath::IsLineDegenerate(pt2, pt3);
michael@0 426
michael@0 427 if (degenerateAB + degenerateBC + degenerateCD >= 2) {
michael@0 428 this->lineTo(pt3);
michael@0 429 return;
michael@0 430 }
michael@0 431
michael@0 432 SkVector normalAB, unitAB, normalCD, unitCD;
michael@0 433
michael@0 434 // find the first tangent (which might be pt1 or pt2
michael@0 435 {
michael@0 436 const SkPoint* nextPt = &pt1;
michael@0 437 if (degenerateAB)
michael@0 438 nextPt = &pt2;
michael@0 439 this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
michael@0 440 }
michael@0 441
michael@0 442 {
michael@0 443 SkPoint pts[4], tmp[13];
michael@0 444 int i, count;
michael@0 445 SkVector n, u;
michael@0 446 SkScalar tValues[3];
michael@0 447
michael@0 448 pts[0] = fPrevPt;
michael@0 449 pts[1] = pt1;
michael@0 450 pts[2] = pt2;
michael@0 451 pts[3] = pt3;
michael@0 452
michael@0 453 count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
michael@0 454 n = normalAB;
michael@0 455 u = unitAB;
michael@0 456 for (i = 0; i < count; i++) {
michael@0 457 this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
michael@0 458 kMaxCubicSubdivide);
michael@0 459 if (i == count - 1) {
michael@0 460 break;
michael@0 461 }
michael@0 462 n = normalCD;
michael@0 463 u = unitCD;
michael@0 464
michael@0 465 }
michael@0 466 }
michael@0 467
michael@0 468 this->postJoinTo(pt3, normalCD, unitCD);
michael@0 469 }
michael@0 470
michael@0 471 ///////////////////////////////////////////////////////////////////////////////
michael@0 472 ///////////////////////////////////////////////////////////////////////////////
michael@0 473
michael@0 474 #include "SkPaintDefaults.h"
michael@0 475
michael@0 476 SkStroke::SkStroke() {
michael@0 477 fWidth = SK_Scalar1;
michael@0 478 fMiterLimit = SkPaintDefaults_MiterLimit;
michael@0 479 fCap = SkPaint::kDefault_Cap;
michael@0 480 fJoin = SkPaint::kDefault_Join;
michael@0 481 fDoFill = false;
michael@0 482 }
michael@0 483
michael@0 484 SkStroke::SkStroke(const SkPaint& p) {
michael@0 485 fWidth = p.getStrokeWidth();
michael@0 486 fMiterLimit = p.getStrokeMiter();
michael@0 487 fCap = (uint8_t)p.getStrokeCap();
michael@0 488 fJoin = (uint8_t)p.getStrokeJoin();
michael@0 489 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
michael@0 490 }
michael@0 491
michael@0 492 SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
michael@0 493 fWidth = width;
michael@0 494 fMiterLimit = p.getStrokeMiter();
michael@0 495 fCap = (uint8_t)p.getStrokeCap();
michael@0 496 fJoin = (uint8_t)p.getStrokeJoin();
michael@0 497 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
michael@0 498 }
michael@0 499
michael@0 500 void SkStroke::setWidth(SkScalar width) {
michael@0 501 SkASSERT(width >= 0);
michael@0 502 fWidth = width;
michael@0 503 }
michael@0 504
michael@0 505 void SkStroke::setMiterLimit(SkScalar miterLimit) {
michael@0 506 SkASSERT(miterLimit >= 0);
michael@0 507 fMiterLimit = miterLimit;
michael@0 508 }
michael@0 509
michael@0 510 void SkStroke::setCap(SkPaint::Cap cap) {
michael@0 511 SkASSERT((unsigned)cap < SkPaint::kCapCount);
michael@0 512 fCap = SkToU8(cap);
michael@0 513 }
michael@0 514
michael@0 515 void SkStroke::setJoin(SkPaint::Join join) {
michael@0 516 SkASSERT((unsigned)join < SkPaint::kJoinCount);
michael@0 517 fJoin = SkToU8(join);
michael@0 518 }
michael@0 519
michael@0 520 ///////////////////////////////////////////////////////////////////////////////
michael@0 521
michael@0 522 // If src==dst, then we use a tmp path to record the stroke, and then swap
michael@0 523 // its contents with src when we're done.
michael@0 524 class AutoTmpPath {
michael@0 525 public:
michael@0 526 AutoTmpPath(const SkPath& src, SkPath** dst) : fSrc(src) {
michael@0 527 if (&src == *dst) {
michael@0 528 *dst = &fTmpDst;
michael@0 529 fSwapWithSrc = true;
michael@0 530 } else {
michael@0 531 (*dst)->reset();
michael@0 532 fSwapWithSrc = false;
michael@0 533 }
michael@0 534 }
michael@0 535
michael@0 536 ~AutoTmpPath() {
michael@0 537 if (fSwapWithSrc) {
michael@0 538 fTmpDst.swap(*const_cast<SkPath*>(&fSrc));
michael@0 539 }
michael@0 540 }
michael@0 541
michael@0 542 private:
michael@0 543 SkPath fTmpDst;
michael@0 544 const SkPath& fSrc;
michael@0 545 bool fSwapWithSrc;
michael@0 546 };
michael@0 547
michael@0 548 void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
michael@0 549 SkASSERT(&src != NULL && dst != NULL);
michael@0 550
michael@0 551 SkScalar radius = SkScalarHalf(fWidth);
michael@0 552
michael@0 553 AutoTmpPath tmp(src, &dst);
michael@0 554
michael@0 555 if (radius <= 0) {
michael@0 556 return;
michael@0 557 }
michael@0 558
michael@0 559 // If src is really a rect, call our specialty strokeRect() method
michael@0 560 {
michael@0 561 bool isClosed;
michael@0 562 SkPath::Direction dir;
michael@0 563 if (src.isRect(&isClosed, &dir) && isClosed) {
michael@0 564 this->strokeRect(src.getBounds(), dst, dir);
michael@0 565 // our answer should preserve the inverseness of the src
michael@0 566 if (src.isInverseFillType()) {
michael@0 567 SkASSERT(!dst->isInverseFillType());
michael@0 568 dst->toggleInverseFillType();
michael@0 569 }
michael@0 570 return;
michael@0 571 }
michael@0 572 }
michael@0 573
michael@0 574 SkAutoConicToQuads converter;
michael@0 575 const SkScalar conicTol = SK_Scalar1 / 4;
michael@0 576
michael@0 577 SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(),
michael@0 578 this->getJoin());
michael@0 579 SkPath::Iter iter(src, false);
michael@0 580 SkPath::Verb lastSegment = SkPath::kMove_Verb;
michael@0 581
michael@0 582 for (;;) {
michael@0 583 SkPoint pts[4];
michael@0 584 switch (iter.next(pts, false)) {
michael@0 585 case SkPath::kMove_Verb:
michael@0 586 stroker.moveTo(pts[0]);
michael@0 587 break;
michael@0 588 case SkPath::kLine_Verb:
michael@0 589 stroker.lineTo(pts[1]);
michael@0 590 lastSegment = SkPath::kLine_Verb;
michael@0 591 break;
michael@0 592 case SkPath::kQuad_Verb:
michael@0 593 stroker.quadTo(pts[1], pts[2]);
michael@0 594 lastSegment = SkPath::kQuad_Verb;
michael@0 595 break;
michael@0 596 case SkPath::kConic_Verb: {
michael@0 597 // todo: if we had maxcurvature for conics, perhaps we should
michael@0 598 // natively extrude the conic instead of converting to quads.
michael@0 599 const SkPoint* quadPts =
michael@0 600 converter.computeQuads(pts, iter.conicWeight(), conicTol);
michael@0 601 for (int i = 0; i < converter.countQuads(); ++i) {
michael@0 602 stroker.quadTo(quadPts[1], quadPts[2]);
michael@0 603 quadPts += 2;
michael@0 604 }
michael@0 605 lastSegment = SkPath::kQuad_Verb;
michael@0 606 } break;
michael@0 607 case SkPath::kCubic_Verb:
michael@0 608 stroker.cubicTo(pts[1], pts[2], pts[3]);
michael@0 609 lastSegment = SkPath::kCubic_Verb;
michael@0 610 break;
michael@0 611 case SkPath::kClose_Verb:
michael@0 612 stroker.close(lastSegment == SkPath::kLine_Verb);
michael@0 613 break;
michael@0 614 case SkPath::kDone_Verb:
michael@0 615 goto DONE;
michael@0 616 }
michael@0 617 }
michael@0 618 DONE:
michael@0 619 stroker.done(dst, lastSegment == SkPath::kLine_Verb);
michael@0 620
michael@0 621 if (fDoFill) {
michael@0 622 if (src.cheapIsDirection(SkPath::kCCW_Direction)) {
michael@0 623 dst->reverseAddPath(src);
michael@0 624 } else {
michael@0 625 dst->addPath(src);
michael@0 626 }
michael@0 627 } else {
michael@0 628 // Seems like we can assume that a 2-point src would always result in
michael@0 629 // a convex stroke, but testing has proved otherwise.
michael@0 630 // TODO: fix the stroker to make this assumption true (without making
michael@0 631 // it slower that the work that will be done in computeConvexity())
michael@0 632 #if 0
michael@0 633 // this test results in a non-convex stroke :(
michael@0 634 static void test(SkCanvas* canvas) {
michael@0 635 SkPoint pts[] = { 146.333328, 192.333328, 300.333344, 293.333344 };
michael@0 636 SkPaint paint;
michael@0 637 paint.setStrokeWidth(7);
michael@0 638 paint.setStrokeCap(SkPaint::kRound_Cap);
michael@0 639 canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
michael@0 640 }
michael@0 641 #endif
michael@0 642 #if 0
michael@0 643 if (2 == src.countPoints()) {
michael@0 644 dst->setIsConvex(true);
michael@0 645 }
michael@0 646 #endif
michael@0 647 }
michael@0 648
michael@0 649 // our answer should preserve the inverseness of the src
michael@0 650 if (src.isInverseFillType()) {
michael@0 651 SkASSERT(!dst->isInverseFillType());
michael@0 652 dst->toggleInverseFillType();
michael@0 653 }
michael@0 654 }
michael@0 655
michael@0 656 static SkPath::Direction reverse_direction(SkPath::Direction dir) {
michael@0 657 SkASSERT(SkPath::kUnknown_Direction != dir);
michael@0 658 return SkPath::kCW_Direction == dir ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
michael@0 659 }
michael@0 660
michael@0 661 static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPath::Direction dir) {
michael@0 662 SkPoint pts[8];
michael@0 663
michael@0 664 if (SkPath::kCW_Direction == dir) {
michael@0 665 pts[0].set(r.fLeft, outer.fTop);
michael@0 666 pts[1].set(r.fRight, outer.fTop);
michael@0 667 pts[2].set(outer.fRight, r.fTop);
michael@0 668 pts[3].set(outer.fRight, r.fBottom);
michael@0 669 pts[4].set(r.fRight, outer.fBottom);
michael@0 670 pts[5].set(r.fLeft, outer.fBottom);
michael@0 671 pts[6].set(outer.fLeft, r.fBottom);
michael@0 672 pts[7].set(outer.fLeft, r.fTop);
michael@0 673 } else {
michael@0 674 pts[7].set(r.fLeft, outer.fTop);
michael@0 675 pts[6].set(r.fRight, outer.fTop);
michael@0 676 pts[5].set(outer.fRight, r.fTop);
michael@0 677 pts[4].set(outer.fRight, r.fBottom);
michael@0 678 pts[3].set(r.fRight, outer.fBottom);
michael@0 679 pts[2].set(r.fLeft, outer.fBottom);
michael@0 680 pts[1].set(outer.fLeft, r.fBottom);
michael@0 681 pts[0].set(outer.fLeft, r.fTop);
michael@0 682 }
michael@0 683 path->addPoly(pts, 8, true);
michael@0 684 }
michael@0 685
michael@0 686 void SkStroke::strokeRect(const SkRect& origRect, SkPath* dst,
michael@0 687 SkPath::Direction dir) const {
michael@0 688 SkASSERT(dst != NULL);
michael@0 689 dst->reset();
michael@0 690
michael@0 691 SkScalar radius = SkScalarHalf(fWidth);
michael@0 692 if (radius <= 0) {
michael@0 693 return;
michael@0 694 }
michael@0 695
michael@0 696 SkScalar rw = origRect.width();
michael@0 697 SkScalar rh = origRect.height();
michael@0 698 if ((rw < 0) ^ (rh < 0)) {
michael@0 699 dir = reverse_direction(dir);
michael@0 700 }
michael@0 701 SkRect rect(origRect);
michael@0 702 rect.sort();
michael@0 703 // reassign these, now that we know they'll be >= 0
michael@0 704 rw = rect.width();
michael@0 705 rh = rect.height();
michael@0 706
michael@0 707 SkRect r(rect);
michael@0 708 r.outset(radius, radius);
michael@0 709
michael@0 710 SkPaint::Join join = (SkPaint::Join)fJoin;
michael@0 711 if (SkPaint::kMiter_Join == join && fMiterLimit < SK_ScalarSqrt2) {
michael@0 712 join = SkPaint::kBevel_Join;
michael@0 713 }
michael@0 714
michael@0 715 switch (join) {
michael@0 716 case SkPaint::kMiter_Join:
michael@0 717 dst->addRect(r, dir);
michael@0 718 break;
michael@0 719 case SkPaint::kBevel_Join:
michael@0 720 addBevel(dst, rect, r, dir);
michael@0 721 break;
michael@0 722 case SkPaint::kRound_Join:
michael@0 723 dst->addRoundRect(r, radius, radius, dir);
michael@0 724 break;
michael@0 725 default:
michael@0 726 break;
michael@0 727 }
michael@0 728
michael@0 729 if (fWidth < SkMinScalar(rw, rh) && !fDoFill) {
michael@0 730 r = rect;
michael@0 731 r.inset(radius, radius);
michael@0 732 dst->addRect(r, reverse_direction(dir));
michael@0 733 }
michael@0 734 }

mercurial