1 2 /* 3 * Copyright 2009 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10 #include "SkEdgeClipper.h" 11 #include "SkGeometry.h" 12 13 static bool quick_reject(const SkRect& bounds, const SkRect& clip) { 14 return bounds.fTop >= clip.fBottom || bounds.fBottom <= clip.fTop; 15 } 16 17 static inline void clamp_le(SkScalar& value, SkScalar max) { 18 if (value > max) { 19 value = max; 20 } 21 } 22 23 static inline void clamp_ge(SkScalar& value, SkScalar min) { 24 if (value < min) { 25 value = min; 26 } 27 } 28 29 /* src[] must be monotonic in Y. This routine copies src into dst, and sorts 30 it to be increasing in Y. If it had to reverse the order of the points, 31 it returns true, otherwise it returns false 32 */ 33 static bool sort_increasing_Y(SkPoint dst[], const SkPoint src[], int count) { 34 // we need the data to be monotonically increasing in Y 35 if (src[0].fY > src[count - 1].fY) { 36 for (int i = 0; i < count; i++) { 37 dst[i] = src[count - i - 1]; 38 } 39 return true; 40 } else { 41 memcpy(dst, src, count * sizeof(SkPoint)); 42 return false; 43 } 44 } 45 46 /////////////////////////////////////////////////////////////////////////////// 47 48 static bool chopMonoQuadAt(SkScalar c0, SkScalar c1, SkScalar c2, 49 SkScalar target, SkScalar* t) { 50 /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2 51 * We solve for t, using quadratic equation, hence we have to rearrange 52 * our cooefficents to look like At^2 + Bt + C 53 */ 54 SkScalar A = c0 - c1 - c1 + c2; 55 SkScalar B = 2*(c1 - c0); 56 SkScalar C = c0 - target; 57 58 SkScalar roots[2]; // we only expect one, but make room for 2 for safety 59 int count = SkFindUnitQuadRoots(A, B, C, roots); 60 if (count) { 61 *t = roots[0]; 62 return true; 63 } 64 return false; 65 } 66 67 static bool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) { 68 return chopMonoQuadAt(pts[0].fY, pts[1].fY, pts[2].fY, y, t); 69 } 70 71 static bool chopMonoQuadAtX(SkPoint pts[3], SkScalar x, SkScalar* t) { 72 return chopMonoQuadAt(pts[0].fX, pts[1].fX, pts[2].fX, x, t); 73 } 74 75 // Modify pts[] in place so that it is clipped in Y to the clip rect 76 static void chop_quad_in_Y(SkPoint pts[3], const SkRect& clip) { 77 SkScalar t; 78 SkPoint tmp[5]; // for SkChopQuadAt 79 80 // are we partially above 81 if (pts[0].fY < clip.fTop) { 82 if (chopMonoQuadAtY(pts, clip.fTop, &t)) { 83 // take the 2nd chopped quad 84 SkChopQuadAt(pts, tmp, t); 85 clamp_ge(tmp[2].fY, clip.fTop); 86 clamp_ge(tmp[3].fY, clip.fTop); 87 pts[0] = tmp[2]; 88 pts[1] = tmp[3]; 89 } else { 90 // if chopMonoQuadAtY failed, then we may have hit inexact numerics 91 // so we just clamp against the top 92 for (int i = 0; i < 3; i++) { 93 if (pts[i].fY < clip.fTop) { 94 pts[i].fY = clip.fTop; 95 } 96 } 97 } 98 } 99 100 // are we partially below 101 if (pts[2].fY > clip.fBottom) { 102 if (chopMonoQuadAtY(pts, clip.fBottom, &t)) { 103 SkChopQuadAt(pts, tmp, t); 104 clamp_le(tmp[1].fY, clip.fBottom); 105 clamp_le(tmp[2].fY, clip.fBottom); 106 pts[1] = tmp[1]; 107 pts[2] = tmp[2]; 108 } else { 109 // if chopMonoQuadAtY failed, then we may have hit inexact numerics 110 // so we just clamp against the bottom 111 for (int i = 0; i < 3; i++) { 112 if (pts[i].fY > clip.fBottom) { 113 pts[i].fY = clip.fBottom; 114 } 115 } 116 } 117 } 118 } 119 120 // srcPts[] must be monotonic in X and Y 121 void SkEdgeClipper::clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip) { 122 SkPoint pts[3]; 123 bool reverse = sort_increasing_Y(pts, srcPts, 3); 124 125 // are we completely above or below 126 if (pts[2].fY <= clip.fTop || pts[0].fY >= clip.fBottom) { 127 return; 128 } 129 130 // Now chop so that pts is contained within clip in Y 131 chop_quad_in_Y(pts, clip); 132 133 if (pts[0].fX > pts[2].fX) { 134 SkTSwap<SkPoint>(pts[0], pts[2]); 135 reverse = !reverse; 136 } 137 SkASSERT(pts[0].fX <= pts[1].fX); 138 SkASSERT(pts[1].fX <= pts[2].fX); 139 140 // Now chop in X has needed, and record the segments 141 142 if (pts[2].fX <= clip.fLeft) { // wholly to the left 143 this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse); 144 return; 145 } 146 if (pts[0].fX >= clip.fRight) { // wholly to the right 147 this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse); 148 return; 149 } 150 151 SkScalar t; 152 SkPoint tmp[5]; // for SkChopQuadAt 153 154 // are we partially to the left 155 if (pts[0].fX < clip.fLeft) { 156 if (chopMonoQuadAtX(pts, clip.fLeft, &t)) { 157 SkChopQuadAt(pts, tmp, t); 158 this->appendVLine(clip.fLeft, tmp[0].fY, tmp[2].fY, reverse); 159 clamp_ge(tmp[2].fX, clip.fLeft); 160 clamp_ge(tmp[3].fX, clip.fLeft); 161 pts[0] = tmp[2]; 162 pts[1] = tmp[3]; 163 } else { 164 // if chopMonoQuadAtY failed, then we may have hit inexact numerics 165 // so we just clamp against the left 166 this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse); 167 return; 168 } 169 } 170 171 // are we partially to the right 172 if (pts[2].fX > clip.fRight) { 173 if (chopMonoQuadAtX(pts, clip.fRight, &t)) { 174 SkChopQuadAt(pts, tmp, t); 175 clamp_le(tmp[1].fX, clip.fRight); 176 clamp_le(tmp[2].fX, clip.fRight); 177 this->appendQuad(tmp, reverse); 178 this->appendVLine(clip.fRight, tmp[2].fY, tmp[4].fY, reverse); 179 } else { 180 // if chopMonoQuadAtY failed, then we may have hit inexact numerics 181 // so we just clamp against the right 182 this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse); 183 } 184 } else { // wholly inside the clip 185 this->appendQuad(pts, reverse); 186 } 187 } 188 189 bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) { 190 fCurrPoint = fPoints; 191 fCurrVerb = fVerbs; 192 193 SkRect bounds; 194 bounds.set(srcPts, 3); 195 196 if (!quick_reject(bounds, clip)) { 197 SkPoint monoY[5]; 198 int countY = SkChopQuadAtYExtrema(srcPts, monoY); 199 for (int y = 0; y <= countY; y++) { 200 SkPoint monoX[5]; 201 int countX = SkChopQuadAtXExtrema(&monoY[y * 2], monoX); 202 for (int x = 0; x <= countX; x++) { 203 this->clipMonoQuad(&monoX[x * 2], clip); 204 SkASSERT(fCurrVerb - fVerbs < kMaxVerbs); 205 SkASSERT(fCurrPoint - fPoints <= kMaxPoints); 206 } 207 } 208 } 209 210 *fCurrVerb = SkPath::kDone_Verb; 211 fCurrPoint = fPoints; 212 fCurrVerb = fVerbs; 213 return SkPath::kDone_Verb != fVerbs[0]; 214 } 215 216 /////////////////////////////////////////////////////////////////////////////// 217 218 static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C, 219 SkScalar D, SkScalar t) { 220 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D); 221 } 222 223 /* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the 224 t value such that cubic(t) = target 225 */ 226 static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, 227 SkScalar target, SkScalar* t) { 228 // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3); 229 SkASSERT(c0 < target && target < c3); 230 231 SkScalar D = c0 - target; 232 SkScalar A = c3 + 3*(c1 - c2) - c0; 233 SkScalar B = 3*(c2 - c1 - c1 + c0); 234 SkScalar C = 3*(c1 - c0); 235 236 const SkScalar TOLERANCE = SK_Scalar1 / 4096; 237 SkScalar minT = 0; 238 SkScalar maxT = SK_Scalar1; 239 SkScalar mid; 240 int i; 241 for (i = 0; i < 16; i++) { 242 mid = SkScalarAve(minT, maxT); 243 SkScalar delta = eval_cubic_coeff(A, B, C, D, mid); 244 if (delta < 0) { 245 minT = mid; 246 delta = -delta; 247 } else { 248 maxT = mid; 249 } 250 if (delta < TOLERANCE) { 251 break; 252 } 253 } 254 *t = mid; 255 // SkDebugf("-- evalCubicAt %d delta %g\n", i, eval_cubic_coeff(A, B, C, D, *t)); 256 return true; 257 } 258 259 static bool chopMonoCubicAtY(SkPoint pts[4], SkScalar y, SkScalar* t) { 260 return chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, t); 261 } 262 263 static bool chopMonoCubicAtX(SkPoint pts[4], SkScalar x, SkScalar* t) { 264 return chopMonoCubicAt(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, x, t); 265 } 266 267 // Modify pts[] in place so that it is clipped in Y to the clip rect 268 static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) { 269 270 // are we partially above 271 if (pts[0].fY < clip.fTop) { 272 SkScalar t; 273 if (chopMonoCubicAtY(pts, clip.fTop, &t)) { 274 SkPoint tmp[7]; 275 SkChopCubicAt(pts, tmp, t); 276 277 // tmp[3, 4, 5].fY should all be to the below clip.fTop, and 278 // still be monotonic in Y. Since we can't trust the numerics of 279 // the chopper, we force those conditions now 280 tmp[3].fY = clip.fTop; 281 tmp[4].fY = SkMaxScalar(tmp[4].fY, clip.fTop); 282 tmp[5].fY = SkMaxScalar(tmp[5].fY, tmp[4].fY); 283 284 pts[0] = tmp[3]; 285 pts[1] = tmp[4]; 286 pts[2] = tmp[5]; 287 } else { 288 // if chopMonoCubicAtY failed, then we may have hit inexact numerics 289 // so we just clamp against the top 290 for (int i = 0; i < 4; i++) { 291 clamp_ge(pts[i].fY, clip.fTop); 292 } 293 } 294 } 295 296 // are we partially below 297 if (pts[3].fY > clip.fBottom) { 298 SkScalar t; 299 if (chopMonoCubicAtY(pts, clip.fBottom, &t)) { 300 SkPoint tmp[7]; 301 SkChopCubicAt(pts, tmp, t); 302 clamp_le(tmp[1].fY, clip.fBottom); 303 clamp_le(tmp[2].fY, clip.fBottom); 304 clamp_le(tmp[3].fY, clip.fBottom); 305 pts[1] = tmp[1]; 306 pts[2] = tmp[2]; 307 pts[3] = tmp[3]; 308 } else { 309 // if chopMonoCubicAtY failed, then we may have hit inexact numerics 310 // so we just clamp against the bottom 311 for (int i = 0; i < 4; i++) { 312 clamp_le(pts[i].fY, clip.fBottom); 313 } 314 } 315 } 316 } 317 318 // srcPts[] must be monotonic in X and Y 319 void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) { 320 SkPoint pts[4]; 321 bool reverse = sort_increasing_Y(pts, src, 4); 322 323 // are we completely above or below 324 if (pts[3].fY <= clip.fTop || pts[0].fY >= clip.fBottom) { 325 return; 326 } 327 328 // Now chop so that pts is contained within clip in Y 329 chop_cubic_in_Y(pts, clip); 330 331 if (pts[0].fX > pts[3].fX) { 332 SkTSwap<SkPoint>(pts[0], pts[3]); 333 SkTSwap<SkPoint>(pts[1], pts[2]); 334 reverse = !reverse; 335 } 336 337 // Now chop in X has needed, and record the segments 338 339 if (pts[3].fX <= clip.fLeft) { // wholly to the left 340 this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse); 341 return; 342 } 343 if (pts[0].fX >= clip.fRight) { // wholly to the right 344 this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse); 345 return; 346 } 347 348 // are we partially to the left 349 if (pts[0].fX < clip.fLeft) { 350 SkScalar t; 351 if (chopMonoCubicAtX(pts, clip.fLeft, &t)) { 352 SkPoint tmp[7]; 353 SkChopCubicAt(pts, tmp, t); 354 this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse); 355 356 // tmp[3, 4, 5].fX should all be to the right of clip.fLeft, and 357 // still be monotonic in X. Since we can't trust the numerics of 358 // the chopper, we force those conditions now 359 tmp[3].fX = clip.fLeft; 360 tmp[4].fX = SkMaxScalar(tmp[4].fX, clip.fLeft); 361 tmp[5].fX = SkMaxScalar(tmp[5].fX, tmp[4].fX); 362 363 pts[0] = tmp[3]; 364 pts[1] = tmp[4]; 365 pts[2] = tmp[5]; 366 } else { 367 // if chopMonocubicAtY failed, then we may have hit inexact numerics 368 // so we just clamp against the left 369 this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse); 370 return; 371 } 372 } 373 374 // are we partially to the right 375 if (pts[3].fX > clip.fRight) { 376 SkScalar t; 377 if (chopMonoCubicAtX(pts, clip.fRight, &t)) { 378 SkPoint tmp[7]; 379 SkChopCubicAt(pts, tmp, t); 380 clamp_le(tmp[1].fX, clip.fRight); 381 clamp_le(tmp[2].fX, clip.fRight); 382 clamp_le(tmp[3].fX, clip.fRight); 383 this->appendCubic(tmp, reverse); 384 this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse); 385 } else { 386 // if chopMonoCubicAtX failed, then we may have hit inexact numerics 387 // so we just clamp against the right 388 this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse); 389 } 390 } else { // wholly inside the clip 391 this->appendCubic(pts, reverse); 392 } 393 } 394 395 bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) { 396 fCurrPoint = fPoints; 397 fCurrVerb = fVerbs; 398 399 SkRect bounds; 400 bounds.set(srcPts, 4); 401 402 if (!quick_reject(bounds, clip)) { 403 SkPoint monoY[10]; 404 int countY = SkChopCubicAtYExtrema(srcPts, monoY); 405 for (int y = 0; y <= countY; y++) { 406 // sk_assert_monotonic_y(&monoY[y * 3], 4); 407 SkPoint monoX[10]; 408 int countX = SkChopCubicAtXExtrema(&monoY[y * 3], monoX); 409 for (int x = 0; x <= countX; x++) { 410 // sk_assert_monotonic_y(&monoX[x * 3], 4); 411 // sk_assert_monotonic_x(&monoX[x * 3], 4); 412 this->clipMonoCubic(&monoX[x * 3], clip); 413 SkASSERT(fCurrVerb - fVerbs < kMaxVerbs); 414 SkASSERT(fCurrPoint - fPoints <= kMaxPoints); 415 } 416 } 417 } 418 419 *fCurrVerb = SkPath::kDone_Verb; 420 fCurrPoint = fPoints; 421 fCurrVerb = fVerbs; 422 return SkPath::kDone_Verb != fVerbs[0]; 423 } 424 425 /////////////////////////////////////////////////////////////////////////////// 426 427 void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1, 428 bool reverse) { 429 *fCurrVerb++ = SkPath::kLine_Verb; 430 431 if (reverse) { 432 SkTSwap<SkScalar>(y0, y1); 433 } 434 fCurrPoint[0].set(x, y0); 435 fCurrPoint[1].set(x, y1); 436 fCurrPoint += 2; 437 } 438 439 void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) { 440 *fCurrVerb++ = SkPath::kQuad_Verb; 441 442 if (reverse) { 443 fCurrPoint[0] = pts[2]; 444 fCurrPoint[2] = pts[0]; 445 } else { 446 fCurrPoint[0] = pts[0]; 447 fCurrPoint[2] = pts[2]; 448 } 449 fCurrPoint[1] = pts[1]; 450 fCurrPoint += 3; 451 } 452 453 void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) { 454 *fCurrVerb++ = SkPath::kCubic_Verb; 455 456 if (reverse) { 457 for (int i = 0; i < 4; i++) { 458 fCurrPoint[i] = pts[3 - i]; 459 } 460 } else { 461 memcpy(fCurrPoint, pts, 4 * sizeof(SkPoint)); 462 } 463 fCurrPoint += 4; 464 } 465 466 SkPath::Verb SkEdgeClipper::next(SkPoint pts[]) { 467 SkPath::Verb verb = *fCurrVerb; 468 469 switch (verb) { 470 case SkPath::kLine_Verb: 471 memcpy(pts, fCurrPoint, 2 * sizeof(SkPoint)); 472 fCurrPoint += 2; 473 fCurrVerb += 1; 474 break; 475 case SkPath::kQuad_Verb: 476 memcpy(pts, fCurrPoint, 3 * sizeof(SkPoint)); 477 fCurrPoint += 3; 478 fCurrVerb += 1; 479 break; 480 case SkPath::kCubic_Verb: 481 memcpy(pts, fCurrPoint, 4 * sizeof(SkPoint)); 482 fCurrPoint += 4; 483 fCurrVerb += 1; 484 break; 485 case SkPath::kDone_Verb: 486 break; 487 default: 488 SkDEBUGFAIL("unexpected verb in quadclippper2 iter"); 489 break; 490 } 491 return verb; 492 } 493 494 /////////////////////////////////////////////////////////////////////////////// 495 496 #ifdef SK_DEBUG 497 static void assert_monotonic(const SkScalar coord[], int count) { 498 if (coord[0] > coord[(count - 1) * 2]) { 499 for (int i = 1; i < count; i++) { 500 SkASSERT(coord[2 * (i - 1)] >= coord[i * 2]); 501 } 502 } else if (coord[0] < coord[(count - 1) * 2]) { 503 for (int i = 1; i < count; i++) { 504 SkASSERT(coord[2 * (i - 1)] <= coord[i * 2]); 505 } 506 } else { 507 for (int i = 1; i < count; i++) { 508 SkASSERT(coord[2 * (i - 1)] == coord[i * 2]); 509 } 510 } 511 } 512 513 void sk_assert_monotonic_y(const SkPoint pts[], int count) { 514 if (count > 1) { 515 assert_monotonic(&pts[0].fY, count); 516 } 517 } 518 519 void sk_assert_monotonic_x(const SkPoint pts[], int count) { 520 if (count > 1) { 521 assert_monotonic(&pts[0].fX, count); 522 } 523 } 524 #endif 525