SkEdgeClipper.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. /*
  2. * Copyright 2009 The Android Open Source Project
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "include/private/SkMacros.h"
  8. #include "src/core/SkEdgeClipper.h"
  9. #include "src/core/SkGeometry.h"
  10. #include "src/core/SkLineClipper.h"
  11. #include <utility>
  12. static bool quick_reject(const SkRect& bounds, const SkRect& clip) {
  13. return bounds.fTop >= clip.fBottom || bounds.fBottom <= clip.fTop;
  14. }
  15. static inline void clamp_le(SkScalar& value, SkScalar max) {
  16. if (value > max) {
  17. value = max;
  18. }
  19. }
  20. static inline void clamp_ge(SkScalar& value, SkScalar min) {
  21. if (value < min) {
  22. value = min;
  23. }
  24. }
  25. /* src[] must be monotonic in Y. This routine copies src into dst, and sorts
  26. it to be increasing in Y. If it had to reverse the order of the points,
  27. it returns true, otherwise it returns false
  28. */
  29. static bool sort_increasing_Y(SkPoint dst[], const SkPoint src[], int count) {
  30. // we need the data to be monotonically increasing in Y
  31. if (src[0].fY > src[count - 1].fY) {
  32. for (int i = 0; i < count; i++) {
  33. dst[i] = src[count - i - 1];
  34. }
  35. return true;
  36. } else {
  37. memcpy(dst, src, count * sizeof(SkPoint));
  38. return false;
  39. }
  40. }
  41. bool SkEdgeClipper::clipLine(SkPoint p0, SkPoint p1, const SkRect& clip) {
  42. fCurrPoint = fPoints;
  43. fCurrVerb = fVerbs;
  44. SkPoint lines[SkLineClipper::kMaxPoints];
  45. const SkPoint pts[] = { p0, p1 };
  46. int lineCount = SkLineClipper::ClipLine(pts, clip, lines, fCanCullToTheRight);
  47. for (int i = 0; i < lineCount; i++) {
  48. this->appendLine(lines[i], lines[i + 1]);
  49. }
  50. *fCurrVerb = SkPath::kDone_Verb;
  51. fCurrPoint = fPoints;
  52. fCurrVerb = fVerbs;
  53. return SkPath::kDone_Verb != fVerbs[0];
  54. }
  55. ///////////////////////////////////////////////////////////////////////////////
  56. static bool chopMonoQuadAt(SkScalar c0, SkScalar c1, SkScalar c2,
  57. SkScalar target, SkScalar* t) {
  58. /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2
  59. * We solve for t, using quadratic equation, hence we have to rearrange
  60. * our cooefficents to look like At^2 + Bt + C
  61. */
  62. SkScalar A = c0 - c1 - c1 + c2;
  63. SkScalar B = 2*(c1 - c0);
  64. SkScalar C = c0 - target;
  65. SkScalar roots[2]; // we only expect one, but make room for 2 for safety
  66. int count = SkFindUnitQuadRoots(A, B, C, roots);
  67. if (count) {
  68. *t = roots[0];
  69. return true;
  70. }
  71. return false;
  72. }
  73. static bool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) {
  74. return chopMonoQuadAt(pts[0].fY, pts[1].fY, pts[2].fY, y, t);
  75. }
  76. static bool chopMonoQuadAtX(SkPoint pts[3], SkScalar x, SkScalar* t) {
  77. return chopMonoQuadAt(pts[0].fX, pts[1].fX, pts[2].fX, x, t);
  78. }
  79. // Modify pts[] in place so that it is clipped in Y to the clip rect
  80. static void chop_quad_in_Y(SkPoint pts[3], const SkRect& clip) {
  81. SkScalar t;
  82. SkPoint tmp[5]; // for SkChopQuadAt
  83. // are we partially above
  84. if (pts[0].fY < clip.fTop) {
  85. if (chopMonoQuadAtY(pts, clip.fTop, &t)) {
  86. // take the 2nd chopped quad
  87. SkChopQuadAt(pts, tmp, t);
  88. // clamp to clean up imprecise numerics in the chop
  89. tmp[2].fY = clip.fTop;
  90. clamp_ge(tmp[3].fY, clip.fTop);
  91. pts[0] = tmp[2];
  92. pts[1] = tmp[3];
  93. } else {
  94. // if chopMonoQuadAtY failed, then we may have hit inexact numerics
  95. // so we just clamp against the top
  96. for (int i = 0; i < 3; i++) {
  97. if (pts[i].fY < clip.fTop) {
  98. pts[i].fY = clip.fTop;
  99. }
  100. }
  101. }
  102. }
  103. // are we partially below
  104. if (pts[2].fY > clip.fBottom) {
  105. if (chopMonoQuadAtY(pts, clip.fBottom, &t)) {
  106. SkChopQuadAt(pts, tmp, t);
  107. // clamp to clean up imprecise numerics in the chop
  108. clamp_le(tmp[1].fY, clip.fBottom);
  109. tmp[2].fY = clip.fBottom;
  110. pts[1] = tmp[1];
  111. pts[2] = tmp[2];
  112. } else {
  113. // if chopMonoQuadAtY failed, then we may have hit inexact numerics
  114. // so we just clamp against the bottom
  115. for (int i = 0; i < 3; i++) {
  116. if (pts[i].fY > clip.fBottom) {
  117. pts[i].fY = clip.fBottom;
  118. }
  119. }
  120. }
  121. }
  122. }
  123. // srcPts[] must be monotonic in X and Y
  124. void SkEdgeClipper::clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip) {
  125. SkPoint pts[3];
  126. bool reverse = sort_increasing_Y(pts, srcPts, 3);
  127. // are we completely above or below
  128. if (pts[2].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
  129. return;
  130. }
  131. // Now chop so that pts is contained within clip in Y
  132. chop_quad_in_Y(pts, clip);
  133. if (pts[0].fX > pts[2].fX) {
  134. using std::swap;
  135. swap(pts[0], pts[2]);
  136. reverse = !reverse;
  137. }
  138. SkASSERT(pts[0].fX <= pts[1].fX);
  139. SkASSERT(pts[1].fX <= pts[2].fX);
  140. // Now chop in X has needed, and record the segments
  141. if (pts[2].fX <= clip.fLeft) { // wholly to the left
  142. this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse);
  143. return;
  144. }
  145. if (pts[0].fX >= clip.fRight) { // wholly to the right
  146. if (!this->canCullToTheRight()) {
  147. this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse);
  148. }
  149. return;
  150. }
  151. SkScalar t;
  152. SkPoint tmp[5]; // for SkChopQuadAt
  153. // are we partially to the left
  154. if (pts[0].fX < clip.fLeft) {
  155. if (chopMonoQuadAtX(pts, clip.fLeft, &t)) {
  156. SkChopQuadAt(pts, tmp, t);
  157. this->appendVLine(clip.fLeft, tmp[0].fY, tmp[2].fY, reverse);
  158. // clamp to clean up imprecise numerics in the chop
  159. 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. // are we partially to the right
  171. if (pts[2].fX > clip.fRight) {
  172. if (chopMonoQuadAtX(pts, clip.fRight, &t)) {
  173. SkChopQuadAt(pts, tmp, t);
  174. // clamp to clean up imprecise numerics in the chop
  175. clamp_le(tmp[1].fX, clip.fRight);
  176. 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. bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) {
  189. fCurrPoint = fPoints;
  190. fCurrVerb = fVerbs;
  191. SkRect bounds;
  192. bounds.set(srcPts, 3);
  193. if (!quick_reject(bounds, clip)) {
  194. SkPoint monoY[5];
  195. int countY = SkChopQuadAtYExtrema(srcPts, monoY);
  196. for (int y = 0; y <= countY; y++) {
  197. SkPoint monoX[5];
  198. int countX = SkChopQuadAtXExtrema(&monoY[y * 2], monoX);
  199. for (int x = 0; x <= countX; x++) {
  200. this->clipMonoQuad(&monoX[x * 2], clip);
  201. SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
  202. SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
  203. }
  204. }
  205. }
  206. *fCurrVerb = SkPath::kDone_Verb;
  207. fCurrPoint = fPoints;
  208. fCurrVerb = fVerbs;
  209. return SkPath::kDone_Verb != fVerbs[0];
  210. }
  211. ///////////////////////////////////////////////////////////////////////////////
  212. static SkScalar mono_cubic_closestT(const SkScalar src[], SkScalar x) {
  213. SkScalar t = 0.5f;
  214. SkScalar lastT;
  215. SkScalar bestT SK_INIT_TO_AVOID_WARNING;
  216. SkScalar step = 0.25f;
  217. SkScalar D = src[0];
  218. SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
  219. SkScalar B = 3*(src[4] - src[2] - src[2] + D);
  220. SkScalar C = 3*(src[2] - D);
  221. x -= D;
  222. SkScalar closest = SK_ScalarMax;
  223. do {
  224. SkScalar loc = ((A * t + B) * t + C) * t;
  225. SkScalar dist = SkScalarAbs(loc - x);
  226. if (closest > dist) {
  227. closest = dist;
  228. bestT = t;
  229. }
  230. lastT = t;
  231. t += loc < x ? step : -step;
  232. step *= 0.5f;
  233. } while (closest > 0.25f && lastT != t);
  234. return bestT;
  235. }
  236. static void chop_mono_cubic_at_y(SkPoint src[4], SkScalar y, SkPoint dst[7]) {
  237. if (SkChopMonoCubicAtY(src, y, dst)) {
  238. return;
  239. }
  240. SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fY, y));
  241. }
  242. // Modify pts[] in place so that it is clipped in Y to the clip rect
  243. static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
  244. // are we partially above
  245. if (pts[0].fY < clip.fTop) {
  246. SkPoint tmp[7];
  247. chop_mono_cubic_at_y(pts, clip.fTop, tmp);
  248. /*
  249. * For a large range in the points, we can do a poor job of chopping, such that the t
  250. * we computed resulted in the lower cubic still being partly above the clip.
  251. *
  252. * If just the first or first 2 Y values are above the fTop, we can just smash them
  253. * down. If the first 3 Ys are above fTop, we can't smash all 3, as that can really
  254. * distort the cubic. In this case, we take the first output (tmp[3..6] and treat it as
  255. * a guess, and re-chop against fTop. Then we fall through to checking if we need to
  256. * smash the first 1 or 2 Y values.
  257. */
  258. if (tmp[3].fY < clip.fTop && tmp[4].fY < clip.fTop && tmp[5].fY < clip.fTop) {
  259. SkPoint tmp2[4];
  260. memcpy(tmp2, &tmp[3].fX, 4 * sizeof(SkPoint));
  261. chop_mono_cubic_at_y(tmp2, clip.fTop, tmp);
  262. }
  263. // tmp[3, 4].fY should all be to the below clip.fTop.
  264. // Since we can't trust the numerics of the chopper, we force those conditions now
  265. tmp[3].fY = clip.fTop;
  266. clamp_ge(tmp[4].fY, clip.fTop);
  267. pts[0] = tmp[3];
  268. pts[1] = tmp[4];
  269. pts[2] = tmp[5];
  270. }
  271. // are we partially below
  272. if (pts[3].fY > clip.fBottom) {
  273. SkPoint tmp[7];
  274. chop_mono_cubic_at_y(pts, clip.fBottom, tmp);
  275. tmp[3].fY = clip.fBottom;
  276. clamp_le(tmp[2].fY, clip.fBottom);
  277. pts[1] = tmp[1];
  278. pts[2] = tmp[2];
  279. pts[3] = tmp[3];
  280. }
  281. }
  282. static void chop_mono_cubic_at_x(SkPoint src[4], SkScalar x, SkPoint dst[7]) {
  283. if (SkChopMonoCubicAtX(src, x, dst)) {
  284. return;
  285. }
  286. SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fX, x));
  287. }
  288. // srcPts[] must be monotonic in X and Y
  289. void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) {
  290. SkPoint pts[4];
  291. bool reverse = sort_increasing_Y(pts, src, 4);
  292. // are we completely above or below
  293. if (pts[3].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
  294. return;
  295. }
  296. // Now chop so that pts is contained within clip in Y
  297. chop_cubic_in_Y(pts, clip);
  298. if (pts[0].fX > pts[3].fX) {
  299. using std::swap;
  300. swap(pts[0], pts[3]);
  301. swap(pts[1], pts[2]);
  302. reverse = !reverse;
  303. }
  304. // Now chop in X has needed, and record the segments
  305. if (pts[3].fX <= clip.fLeft) { // wholly to the left
  306. this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
  307. return;
  308. }
  309. if (pts[0].fX >= clip.fRight) { // wholly to the right
  310. if (!this->canCullToTheRight()) {
  311. this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
  312. }
  313. return;
  314. }
  315. // are we partially to the left
  316. if (pts[0].fX < clip.fLeft) {
  317. SkPoint tmp[7];
  318. chop_mono_cubic_at_x(pts, clip.fLeft, tmp);
  319. this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
  320. // tmp[3, 4].fX should all be to the right of clip.fLeft.
  321. // Since we can't trust the numerics of
  322. // the chopper, we force those conditions now
  323. tmp[3].fX = clip.fLeft;
  324. clamp_ge(tmp[4].fX, clip.fLeft);
  325. pts[0] = tmp[3];
  326. pts[1] = tmp[4];
  327. pts[2] = tmp[5];
  328. }
  329. // are we partially to the right
  330. if (pts[3].fX > clip.fRight) {
  331. SkPoint tmp[7];
  332. chop_mono_cubic_at_x(pts, clip.fRight, tmp);
  333. tmp[3].fX = clip.fRight;
  334. clamp_le(tmp[2].fX, clip.fRight);
  335. this->appendCubic(tmp, reverse);
  336. this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
  337. } else { // wholly inside the clip
  338. this->appendCubic(pts, reverse);
  339. }
  340. }
  341. static SkRect compute_cubic_bounds(const SkPoint pts[4]) {
  342. SkRect r;
  343. r.set(pts, 4);
  344. return r;
  345. }
  346. static bool too_big_for_reliable_float_math(const SkRect& r) {
  347. // limit set as the largest float value for which we can still reliably compute things like
  348. // - chopping at XY extrema
  349. // - chopping at Y or X values for clipping
  350. //
  351. // Current value chosen just by experiment. Larger (and still succeeds) is always better.
  352. //
  353. const SkScalar limit = 1 << 22;
  354. return r.fLeft < -limit || r.fTop < -limit || r.fRight > limit || r.fBottom > limit;
  355. }
  356. bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) {
  357. fCurrPoint = fPoints;
  358. fCurrVerb = fVerbs;
  359. const SkRect bounds = compute_cubic_bounds(srcPts);
  360. // check if we're clipped out vertically
  361. if (bounds.fBottom > clip.fTop && bounds.fTop < clip.fBottom) {
  362. if (too_big_for_reliable_float_math(bounds)) {
  363. // can't safely clip the cubic, so we give up and draw a line (which we can safely clip)
  364. //
  365. // If we rewrote chopcubicat*extrema and chopmonocubic using doubles, we could very
  366. // likely always handle the cubic safely, but (it seems) at a big loss in speed, so
  367. // we'd only want to take that alternate impl if needed. Perhaps a TODO to try it.
  368. //
  369. return this->clipLine(srcPts[0], srcPts[3], clip);
  370. } else {
  371. SkPoint monoY[10];
  372. int countY = SkChopCubicAtYExtrema(srcPts, monoY);
  373. for (int y = 0; y <= countY; y++) {
  374. SkPoint monoX[10];
  375. int countX = SkChopCubicAtXExtrema(&monoY[y * 3], monoX);
  376. for (int x = 0; x <= countX; x++) {
  377. this->clipMonoCubic(&monoX[x * 3], clip);
  378. SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
  379. SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
  380. }
  381. }
  382. }
  383. }
  384. *fCurrVerb = SkPath::kDone_Verb;
  385. fCurrPoint = fPoints;
  386. fCurrVerb = fVerbs;
  387. return SkPath::kDone_Verb != fVerbs[0];
  388. }
  389. ///////////////////////////////////////////////////////////////////////////////
  390. void SkEdgeClipper::appendLine(SkPoint p0, SkPoint p1) {
  391. *fCurrVerb++ = SkPath::kLine_Verb;
  392. fCurrPoint[0] = p0;
  393. fCurrPoint[1] = p1;
  394. fCurrPoint += 2;
  395. }
  396. void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1, bool reverse) {
  397. *fCurrVerb++ = SkPath::kLine_Verb;
  398. if (reverse) {
  399. using std::swap;
  400. swap(y0, y1);
  401. }
  402. fCurrPoint[0].set(x, y0);
  403. fCurrPoint[1].set(x, y1);
  404. fCurrPoint += 2;
  405. }
  406. void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) {
  407. *fCurrVerb++ = SkPath::kQuad_Verb;
  408. if (reverse) {
  409. fCurrPoint[0] = pts[2];
  410. fCurrPoint[2] = pts[0];
  411. } else {
  412. fCurrPoint[0] = pts[0];
  413. fCurrPoint[2] = pts[2];
  414. }
  415. fCurrPoint[1] = pts[1];
  416. fCurrPoint += 3;
  417. }
  418. void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) {
  419. *fCurrVerb++ = SkPath::kCubic_Verb;
  420. if (reverse) {
  421. for (int i = 0; i < 4; i++) {
  422. fCurrPoint[i] = pts[3 - i];
  423. }
  424. } else {
  425. memcpy(fCurrPoint, pts, 4 * sizeof(SkPoint));
  426. }
  427. fCurrPoint += 4;
  428. }
  429. SkPath::Verb SkEdgeClipper::next(SkPoint pts[]) {
  430. SkPath::Verb verb = *fCurrVerb;
  431. switch (verb) {
  432. case SkPath::kLine_Verb:
  433. memcpy(pts, fCurrPoint, 2 * sizeof(SkPoint));
  434. fCurrPoint += 2;
  435. fCurrVerb += 1;
  436. break;
  437. case SkPath::kQuad_Verb:
  438. memcpy(pts, fCurrPoint, 3 * sizeof(SkPoint));
  439. fCurrPoint += 3;
  440. fCurrVerb += 1;
  441. break;
  442. case SkPath::kCubic_Verb:
  443. memcpy(pts, fCurrPoint, 4 * sizeof(SkPoint));
  444. fCurrPoint += 4;
  445. fCurrVerb += 1;
  446. break;
  447. case SkPath::kDone_Verb:
  448. break;
  449. default:
  450. SkDEBUGFAIL("unexpected verb in quadclippper2 iter");
  451. break;
  452. }
  453. return verb;
  454. }
  455. ///////////////////////////////////////////////////////////////////////////////
  456. #ifdef SK_DEBUG
  457. static void assert_monotonic(const SkScalar coord[], int count) {
  458. if (coord[0] > coord[(count - 1) * 2]) {
  459. for (int i = 1; i < count; i++) {
  460. SkASSERT(coord[2 * (i - 1)] >= coord[i * 2]);
  461. }
  462. } else if (coord[0] < coord[(count - 1) * 2]) {
  463. for (int i = 1; i < count; i++) {
  464. SkASSERT(coord[2 * (i - 1)] <= coord[i * 2]);
  465. }
  466. } else {
  467. for (int i = 1; i < count; i++) {
  468. SkASSERT(coord[2 * (i - 1)] == coord[i * 2]);
  469. }
  470. }
  471. }
  472. void sk_assert_monotonic_y(const SkPoint pts[], int count) {
  473. if (count > 1) {
  474. assert_monotonic(&pts[0].fY, count);
  475. }
  476. }
  477. void sk_assert_monotonic_x(const SkPoint pts[], int count) {
  478. if (count > 1) {
  479. assert_monotonic(&pts[0].fX, count);
  480. }
  481. }
  482. #endif