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