Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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 */
10 #include "SkEdgeClipper.h"
11 #include "SkGeometry.h"
13 static bool quick_reject(const SkRect& bounds, const SkRect& clip) {
14 return bounds.fTop >= clip.fBottom || bounds.fBottom <= clip.fTop;
15 }
17 static inline void clamp_le(SkScalar& value, SkScalar max) {
18 if (value > max) {
19 value = max;
20 }
21 }
23 static inline void clamp_ge(SkScalar& value, SkScalar min) {
24 if (value < min) {
25 value = min;
26 }
27 }
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 }
46 ///////////////////////////////////////////////////////////////////////////////
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;
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 }
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 }
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 }
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
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 to clean up imprecise numerics in the chop
86 tmp[2].fY = clip.fTop;
87 clamp_ge(tmp[3].fY, clip.fTop);
89 pts[0] = tmp[2];
90 pts[1] = tmp[3];
91 } else {
92 // if chopMonoQuadAtY failed, then we may have hit inexact numerics
93 // so we just clamp against the top
94 for (int i = 0; i < 3; i++) {
95 if (pts[i].fY < clip.fTop) {
96 pts[i].fY = clip.fTop;
97 }
98 }
99 }
100 }
102 // are we partially below
103 if (pts[2].fY > clip.fBottom) {
104 if (chopMonoQuadAtY(pts, clip.fBottom, &t)) {
105 SkChopQuadAt(pts, tmp, t);
106 // clamp to clean up imprecise numerics in the chop
107 clamp_le(tmp[1].fY, clip.fBottom);
108 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 }
124 // srcPts[] must be monotonic in X and Y
125 void SkEdgeClipper::clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip) {
126 SkPoint pts[3];
127 bool reverse = sort_increasing_Y(pts, srcPts, 3);
129 // are we completely above or below
130 if (pts[2].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
131 return;
132 }
134 // Now chop so that pts is contained within clip in Y
135 chop_quad_in_Y(pts, clip);
137 if (pts[0].fX > pts[2].fX) {
138 SkTSwap<SkPoint>(pts[0], pts[2]);
139 reverse = !reverse;
140 }
141 SkASSERT(pts[0].fX <= pts[1].fX);
142 SkASSERT(pts[1].fX <= pts[2].fX);
144 // Now chop in X has needed, and record the segments
146 if (pts[2].fX <= clip.fLeft) { // wholly to the left
147 this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse);
148 return;
149 }
150 if (pts[0].fX >= clip.fRight) { // wholly to the right
151 this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse);
152 return;
153 }
155 SkScalar t;
156 SkPoint tmp[5]; // for SkChopQuadAt
158 // are we partially to the left
159 if (pts[0].fX < clip.fLeft) {
160 if (chopMonoQuadAtX(pts, clip.fLeft, &t)) {
161 SkChopQuadAt(pts, tmp, t);
162 this->appendVLine(clip.fLeft, tmp[0].fY, tmp[2].fY, reverse);
163 // clamp to clean up imprecise numerics in the chop
164 tmp[2].fX = clip.fLeft;
165 clamp_ge(tmp[3].fX, clip.fLeft);
167 pts[0] = tmp[2];
168 pts[1] = tmp[3];
169 } else {
170 // if chopMonoQuadAtY failed, then we may have hit inexact numerics
171 // so we just clamp against the left
172 this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse);
173 return;
174 }
175 }
177 // are we partially to the right
178 if (pts[2].fX > clip.fRight) {
179 if (chopMonoQuadAtX(pts, clip.fRight, &t)) {
180 SkChopQuadAt(pts, tmp, t);
181 // clamp to clean up imprecise numerics in the chop
182 clamp_le(tmp[1].fX, clip.fRight);
183 tmp[2].fX = clip.fRight;
185 this->appendQuad(tmp, reverse);
186 this->appendVLine(clip.fRight, tmp[2].fY, tmp[4].fY, reverse);
187 } else {
188 // if chopMonoQuadAtY failed, then we may have hit inexact numerics
189 // so we just clamp against the right
190 this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse);
191 }
192 } else { // wholly inside the clip
193 this->appendQuad(pts, reverse);
194 }
195 }
197 bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) {
198 fCurrPoint = fPoints;
199 fCurrVerb = fVerbs;
201 SkRect bounds;
202 bounds.set(srcPts, 3);
204 if (!quick_reject(bounds, clip)) {
205 SkPoint monoY[5];
206 int countY = SkChopQuadAtYExtrema(srcPts, monoY);
207 for (int y = 0; y <= countY; y++) {
208 SkPoint monoX[5];
209 int countX = SkChopQuadAtXExtrema(&monoY[y * 2], monoX);
210 for (int x = 0; x <= countX; x++) {
211 this->clipMonoQuad(&monoX[x * 2], clip);
212 SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
213 SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
214 }
215 }
216 }
218 *fCurrVerb = SkPath::kDone_Verb;
219 fCurrPoint = fPoints;
220 fCurrVerb = fVerbs;
221 return SkPath::kDone_Verb != fVerbs[0];
222 }
224 ///////////////////////////////////////////////////////////////////////////////
226 static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C,
227 SkScalar D, SkScalar t) {
228 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
229 }
231 /* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the
232 t value such that cubic(t) = target
233 */
234 static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
235 SkScalar target, SkScalar* t) {
236 // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3);
237 SkASSERT(c0 < target && target < c3);
239 SkScalar D = c0 - target;
240 SkScalar A = c3 + 3*(c1 - c2) - c0;
241 SkScalar B = 3*(c2 - c1 - c1 + c0);
242 SkScalar C = 3*(c1 - c0);
244 const SkScalar TOLERANCE = SK_Scalar1 / 4096;
245 SkScalar minT = 0;
246 SkScalar maxT = SK_Scalar1;
247 SkScalar mid;
249 // This is a lot of iterations. Is there a faster way?
250 for (int i = 0; i < 24; i++) {
251 mid = SkScalarAve(minT, maxT);
252 SkScalar delta = eval_cubic_coeff(A, B, C, D, mid);
253 if (delta < 0) {
254 minT = mid;
255 delta = -delta;
256 } else {
257 maxT = mid;
258 }
259 if (delta < TOLERANCE) {
260 break;
261 }
262 }
263 *t = mid;
264 // SkDebugf("-- evalCubicAt %d delta %g\n", i, eval_cubic_coeff(A, B, C, D, *t));
265 return true;
266 }
268 static bool chopMonoCubicAtY(SkPoint pts[4], SkScalar y, SkScalar* t) {
269 return chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, t);
270 }
272 static bool chopMonoCubicAtX(SkPoint pts[4], SkScalar x, SkScalar* t) {
273 return chopMonoCubicAt(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, x, t);
274 }
276 // Modify pts[] in place so that it is clipped in Y to the clip rect
277 static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
279 // are we partially above
280 if (pts[0].fY < clip.fTop) {
281 SkScalar t;
282 if (chopMonoCubicAtY(pts, clip.fTop, &t)) {
283 SkPoint tmp[7];
284 SkChopCubicAt(pts, tmp, t);
286 // tmp[3, 4, 5].fY should all be to the below clip.fTop.
287 // Since we can't trust the numerics of
288 // the chopper, we force those conditions now
289 tmp[3].fY = clip.fTop;
290 clamp_ge(tmp[4].fY, clip.fTop);
291 clamp_ge(tmp[5].fY, clip.fTop);
293 pts[0] = tmp[3];
294 pts[1] = tmp[4];
295 pts[2] = tmp[5];
296 } else {
297 // if chopMonoCubicAtY failed, then we may have hit inexact numerics
298 // so we just clamp against the top
299 for (int i = 0; i < 4; i++) {
300 clamp_ge(pts[i].fY, clip.fTop);
301 }
302 }
303 }
305 // are we partially below
306 if (pts[3].fY > clip.fBottom) {
307 SkScalar t;
308 if (chopMonoCubicAtY(pts, clip.fBottom, &t)) {
309 SkPoint tmp[7];
310 SkChopCubicAt(pts, tmp, t);
311 tmp[3].fY = clip.fBottom;
312 clamp_le(tmp[2].fY, clip.fBottom);
314 pts[1] = tmp[1];
315 pts[2] = tmp[2];
316 pts[3] = tmp[3];
317 } else {
318 // if chopMonoCubicAtY failed, then we may have hit inexact numerics
319 // so we just clamp against the bottom
320 for (int i = 0; i < 4; i++) {
321 clamp_le(pts[i].fY, clip.fBottom);
322 }
323 }
324 }
325 }
327 // srcPts[] must be monotonic in X and Y
328 void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) {
329 SkPoint pts[4];
330 bool reverse = sort_increasing_Y(pts, src, 4);
332 // are we completely above or below
333 if (pts[3].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
334 return;
335 }
337 // Now chop so that pts is contained within clip in Y
338 chop_cubic_in_Y(pts, clip);
340 if (pts[0].fX > pts[3].fX) {
341 SkTSwap<SkPoint>(pts[0], pts[3]);
342 SkTSwap<SkPoint>(pts[1], pts[2]);
343 reverse = !reverse;
344 }
346 // Now chop in X has needed, and record the segments
348 if (pts[3].fX <= clip.fLeft) { // wholly to the left
349 this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
350 return;
351 }
352 if (pts[0].fX >= clip.fRight) { // wholly to the right
353 this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
354 return;
355 }
357 // are we partially to the left
358 if (pts[0].fX < clip.fLeft) {
359 SkScalar t;
360 if (chopMonoCubicAtX(pts, clip.fLeft, &t)) {
361 SkPoint tmp[7];
362 SkChopCubicAt(pts, tmp, t);
363 this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
365 // tmp[3, 4, 5].fX should all be to the right of clip.fLeft.
366 // Since we can't trust the numerics of
367 // the chopper, we force those conditions now
368 tmp[3].fX = clip.fLeft;
369 clamp_ge(tmp[4].fX, clip.fLeft);
370 clamp_ge(tmp[5].fX, clip.fLeft);
372 pts[0] = tmp[3];
373 pts[1] = tmp[4];
374 pts[2] = tmp[5];
375 } else {
376 // if chopMonocubicAtY failed, then we may have hit inexact numerics
377 // so we just clamp against the left
378 this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
379 return;
380 }
381 }
383 // are we partially to the right
384 if (pts[3].fX > clip.fRight) {
385 SkScalar t;
386 if (chopMonoCubicAtX(pts, clip.fRight, &t)) {
387 SkPoint tmp[7];
388 SkChopCubicAt(pts, tmp, t);
389 tmp[3].fX = clip.fRight;
390 clamp_le(tmp[2].fX, clip.fRight);
391 clamp_le(tmp[1].fX, clip.fRight);
393 this->appendCubic(tmp, reverse);
394 this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
395 } else {
396 // if chopMonoCubicAtX failed, then we may have hit inexact numerics
397 // so we just clamp against the right
398 this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
399 }
400 } else { // wholly inside the clip
401 this->appendCubic(pts, reverse);
402 }
403 }
405 bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) {
406 fCurrPoint = fPoints;
407 fCurrVerb = fVerbs;
409 SkRect bounds;
410 bounds.set(srcPts, 4);
412 if (!quick_reject(bounds, clip)) {
413 SkPoint monoY[10];
414 int countY = SkChopCubicAtYExtrema(srcPts, monoY);
415 for (int y = 0; y <= countY; y++) {
416 SkPoint monoX[10];
417 int countX = SkChopCubicAtXExtrema(&monoY[y * 3], monoX);
418 for (int x = 0; x <= countX; x++) {
419 this->clipMonoCubic(&monoX[x * 3], clip);
420 SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
421 SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
422 }
423 }
424 }
426 *fCurrVerb = SkPath::kDone_Verb;
427 fCurrPoint = fPoints;
428 fCurrVerb = fVerbs;
429 return SkPath::kDone_Verb != fVerbs[0];
430 }
432 ///////////////////////////////////////////////////////////////////////////////
434 void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1,
435 bool reverse) {
436 *fCurrVerb++ = SkPath::kLine_Verb;
438 if (reverse) {
439 SkTSwap<SkScalar>(y0, y1);
440 }
441 fCurrPoint[0].set(x, y0);
442 fCurrPoint[1].set(x, y1);
443 fCurrPoint += 2;
444 }
446 void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) {
447 *fCurrVerb++ = SkPath::kQuad_Verb;
449 if (reverse) {
450 fCurrPoint[0] = pts[2];
451 fCurrPoint[2] = pts[0];
452 } else {
453 fCurrPoint[0] = pts[0];
454 fCurrPoint[2] = pts[2];
455 }
456 fCurrPoint[1] = pts[1];
457 fCurrPoint += 3;
458 }
460 void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) {
461 *fCurrVerb++ = SkPath::kCubic_Verb;
463 if (reverse) {
464 for (int i = 0; i < 4; i++) {
465 fCurrPoint[i] = pts[3 - i];
466 }
467 } else {
468 memcpy(fCurrPoint, pts, 4 * sizeof(SkPoint));
469 }
470 fCurrPoint += 4;
471 }
473 SkPath::Verb SkEdgeClipper::next(SkPoint pts[]) {
474 SkPath::Verb verb = *fCurrVerb;
476 switch (verb) {
477 case SkPath::kLine_Verb:
478 memcpy(pts, fCurrPoint, 2 * sizeof(SkPoint));
479 fCurrPoint += 2;
480 fCurrVerb += 1;
481 break;
482 case SkPath::kQuad_Verb:
483 memcpy(pts, fCurrPoint, 3 * sizeof(SkPoint));
484 fCurrPoint += 3;
485 fCurrVerb += 1;
486 break;
487 case SkPath::kCubic_Verb:
488 memcpy(pts, fCurrPoint, 4 * sizeof(SkPoint));
489 fCurrPoint += 4;
490 fCurrVerb += 1;
491 break;
492 case SkPath::kDone_Verb:
493 break;
494 default:
495 SkDEBUGFAIL("unexpected verb in quadclippper2 iter");
496 break;
497 }
498 return verb;
499 }
501 ///////////////////////////////////////////////////////////////////////////////
503 #ifdef SK_DEBUG
504 static void assert_monotonic(const SkScalar coord[], int count) {
505 if (coord[0] > coord[(count - 1) * 2]) {
506 for (int i = 1; i < count; i++) {
507 SkASSERT(coord[2 * (i - 1)] >= coord[i * 2]);
508 }
509 } else if (coord[0] < coord[(count - 1) * 2]) {
510 for (int i = 1; i < count; i++) {
511 SkASSERT(coord[2 * (i - 1)] <= coord[i * 2]);
512 }
513 } else {
514 for (int i = 1; i < count; i++) {
515 SkASSERT(coord[2 * (i - 1)] == coord[i * 2]);
516 }
517 }
518 }
520 void sk_assert_monotonic_y(const SkPoint pts[], int count) {
521 if (count > 1) {
522 assert_monotonic(&pts[0].fY, count);
523 }
524 }
526 void sk_assert_monotonic_x(const SkPoint pts[], int count) {
527 if (count > 1) {
528 assert_monotonic(&pts[0].fX, count);
529 }
530 }
531 #endif