| |
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 to clean up imprecise numerics in the chop |
| |
86 tmp[2].fY = clip.fTop; |
| |
87 clamp_ge(tmp[3].fY, clip.fTop); |
| |
88 |
| |
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 } |
| |
101 |
| |
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; |
| |
109 |
| |
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 |
| |
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); |
| |
128 |
| |
129 // are we completely above or below |
| |
130 if (pts[2].fY <= clip.fTop || pts[0].fY >= clip.fBottom) { |
| |
131 return; |
| |
132 } |
| |
133 |
| |
134 // Now chop so that pts is contained within clip in Y |
| |
135 chop_quad_in_Y(pts, clip); |
| |
136 |
| |
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); |
| |
143 |
| |
144 // Now chop in X has needed, and record the segments |
| |
145 |
| |
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 } |
| |
154 |
| |
155 SkScalar t; |
| |
156 SkPoint tmp[5]; // for SkChopQuadAt |
| |
157 |
| |
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); |
| |
166 |
| |
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 } |
| |
176 |
| |
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; |
| |
184 |
| |
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 } |
| |
196 |
| |
197 bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) { |
| |
198 fCurrPoint = fPoints; |
| |
199 fCurrVerb = fVerbs; |
| |
200 |
| |
201 SkRect bounds; |
| |
202 bounds.set(srcPts, 3); |
| |
203 |
| |
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 } |
| |
217 |
| |
218 *fCurrVerb = SkPath::kDone_Verb; |
| |
219 fCurrPoint = fPoints; |
| |
220 fCurrVerb = fVerbs; |
| |
221 return SkPath::kDone_Verb != fVerbs[0]; |
| |
222 } |
| |
223 |
| |
224 /////////////////////////////////////////////////////////////////////////////// |
| |
225 |
| |
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 } |
| |
230 |
| |
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); |
| |
238 |
| |
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); |
| |
243 |
| |
244 const SkScalar TOLERANCE = SK_Scalar1 / 4096; |
| |
245 SkScalar minT = 0; |
| |
246 SkScalar maxT = SK_Scalar1; |
| |
247 SkScalar mid; |
| |
248 |
| |
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 } |
| |
267 |
| |
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 } |
| |
271 |
| |
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 } |
| |
275 |
| |
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) { |
| |
278 |
| |
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); |
| |
285 |
| |
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); |
| |
292 |
| |
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 } |
| |
304 |
| |
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); |
| |
313 |
| |
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 } |
| |
326 |
| |
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); |
| |
331 |
| |
332 // are we completely above or below |
| |
333 if (pts[3].fY <= clip.fTop || pts[0].fY >= clip.fBottom) { |
| |
334 return; |
| |
335 } |
| |
336 |
| |
337 // Now chop so that pts is contained within clip in Y |
| |
338 chop_cubic_in_Y(pts, clip); |
| |
339 |
| |
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 } |
| |
345 |
| |
346 // Now chop in X has needed, and record the segments |
| |
347 |
| |
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 } |
| |
356 |
| |
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); |
| |
364 |
| |
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); |
| |
371 |
| |
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 } |
| |
382 |
| |
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); |
| |
392 |
| |
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 } |
| |
404 |
| |
405 bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) { |
| |
406 fCurrPoint = fPoints; |
| |
407 fCurrVerb = fVerbs; |
| |
408 |
| |
409 SkRect bounds; |
| |
410 bounds.set(srcPts, 4); |
| |
411 |
| |
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 } |
| |
425 |
| |
426 *fCurrVerb = SkPath::kDone_Verb; |
| |
427 fCurrPoint = fPoints; |
| |
428 fCurrVerb = fVerbs; |
| |
429 return SkPath::kDone_Verb != fVerbs[0]; |
| |
430 } |
| |
431 |
| |
432 /////////////////////////////////////////////////////////////////////////////// |
| |
433 |
| |
434 void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1, |
| |
435 bool reverse) { |
| |
436 *fCurrVerb++ = SkPath::kLine_Verb; |
| |
437 |
| |
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 } |
| |
445 |
| |
446 void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) { |
| |
447 *fCurrVerb++ = SkPath::kQuad_Verb; |
| |
448 |
| |
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 } |
| |
459 |
| |
460 void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) { |
| |
461 *fCurrVerb++ = SkPath::kCubic_Verb; |
| |
462 |
| |
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 } |
| |
472 |
| |
473 SkPath::Verb SkEdgeClipper::next(SkPoint pts[]) { |
| |
474 SkPath::Verb verb = *fCurrVerb; |
| |
475 |
| |
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 } |
| |
500 |
| |
501 /////////////////////////////////////////////////////////////////////////////// |
| |
502 |
| |
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 } |
| |
519 |
| |
520 void sk_assert_monotonic_y(const SkPoint pts[], int count) { |
| |
521 if (count > 1) { |
| |
522 assert_monotonic(&pts[0].fY, count); |
| |
523 } |
| |
524 } |
| |
525 |
| |
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 |