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 "SkCubicClipper.h"
11 : #include "SkGeometry.h"
12 :
13 0 : SkCubicClipper::SkCubicClipper() {}
14 :
15 0 : void SkCubicClipper::setClip(const SkIRect& clip) {
16 : // conver to scalars, since that's where we'll see the points
17 0 : fClip.set(clip);
18 0 : }
19 :
20 :
21 0 : static bool chopMonoCubicAtY(SkPoint pts[4], SkScalar y, SkScalar* t) {
22 : SkScalar ycrv[4];
23 0 : ycrv[0] = pts[0].fY - y;
24 0 : ycrv[1] = pts[1].fY - y;
25 0 : ycrv[2] = pts[2].fY - y;
26 0 : ycrv[3] = pts[3].fY - y;
27 :
28 : #ifdef NEWTON_RAPHSON // Quadratic convergence, typically <= 3 iterations.
29 : // Initial guess.
30 : // TODO(turk): Check for zero denominator? Shouldn't happen unless the curve
31 : // is not only monotonic but degenerate.
32 : #ifdef SK_SCALAR_IS_FLOAT
33 : SkScalar t1 = ycrv[0] / (ycrv[0] - ycrv[3]);
34 : #else // !SK_SCALAR_IS_FLOAT
35 : SkScalar t1 = SkDivBits(ycrv[0], ycrv[0] - ycrv[3], 16);
36 : #endif // !SK_SCALAR_IS_FLOAT
37 :
38 : // Newton's iterations.
39 : const SkScalar tol = SK_Scalar1 / 16384; // This leaves 2 fixed noise bits.
40 : SkScalar t0;
41 : const int maxiters = 5;
42 : int iters = 0;
43 : bool converged;
44 : do {
45 : t0 = t1;
46 : SkScalar y01 = SkScalarInterp(ycrv[0], ycrv[1], t0);
47 : SkScalar y12 = SkScalarInterp(ycrv[1], ycrv[2], t0);
48 : SkScalar y23 = SkScalarInterp(ycrv[2], ycrv[3], t0);
49 : SkScalar y012 = SkScalarInterp(y01, y12, t0);
50 : SkScalar y123 = SkScalarInterp(y12, y23, t0);
51 : SkScalar y0123 = SkScalarInterp(y012, y123, t0);
52 : SkScalar yder = (y123 - y012) * 3;
53 : // TODO(turk): check for yder==0: horizontal.
54 : #ifdef SK_SCALAR_IS_FLOAT
55 : t1 -= y0123 / yder;
56 : #else // !SK_SCALAR_IS_FLOAT
57 : t1 -= SkDivBits(y0123, yder, 16);
58 : #endif // !SK_SCALAR_IS_FLOAT
59 : converged = SkScalarAbs(t1 - t0) <= tol; // NaN-safe
60 : ++iters;
61 : } while (!converged && (iters < maxiters));
62 : *t = t1; // Return the result.
63 :
64 : // The result might be valid, even if outside of the range [0, 1], but
65 : // we never evaluate a Bezier outside this interval, so we return false.
66 : if (t1 < 0 || t1 > SK_Scalar1)
67 : return false; // This shouldn't happen, but check anyway.
68 : return converged;
69 :
70 : #else // BISECTION // Linear convergence, typically 16 iterations.
71 :
72 : // Check that the endpoints straddle zero.
73 : SkScalar tNeg, tPos; // Negative and positive function parameters.
74 0 : if (ycrv[0] < 0) {
75 0 : if (ycrv[3] < 0)
76 0 : return false;
77 0 : tNeg = 0;
78 0 : tPos = SK_Scalar1;
79 0 : } else if (ycrv[0] > 0) {
80 0 : if (ycrv[3] > 0)
81 0 : return false;
82 0 : tNeg = SK_Scalar1;
83 0 : tPos = 0;
84 : } else {
85 0 : *t = 0;
86 0 : return true;
87 : }
88 :
89 0 : const SkScalar tol = SK_Scalar1 / 65536; // 1 for fixed, 1e-5 for float.
90 0 : int iters = 0;
91 0 : do {
92 0 : SkScalar tMid = (tPos + tNeg) / 2;
93 0 : SkScalar y01 = SkScalarInterp(ycrv[0], ycrv[1], tMid);
94 0 : SkScalar y12 = SkScalarInterp(ycrv[1], ycrv[2], tMid);
95 0 : SkScalar y23 = SkScalarInterp(ycrv[2], ycrv[3], tMid);
96 0 : SkScalar y012 = SkScalarInterp(y01, y12, tMid);
97 0 : SkScalar y123 = SkScalarInterp(y12, y23, tMid);
98 0 : SkScalar y0123 = SkScalarInterp(y012, y123, tMid);
99 0 : if (y0123 == 0) {
100 0 : *t = tMid;
101 0 : return true;
102 : }
103 0 : if (y0123 < 0) tNeg = tMid;
104 0 : else tPos = tMid;
105 0 : ++iters;
106 0 : } while (!(SkScalarAbs(tPos - tNeg) <= tol)); // Nan-safe
107 :
108 0 : *t = (tNeg + tPos) / 2;
109 0 : return true;
110 : #endif // BISECTION
111 : }
112 :
113 :
114 0 : bool SkCubicClipper::clipCubic(const SkPoint srcPts[4], SkPoint dst[4]) {
115 : bool reverse;
116 :
117 : // we need the data to be monotonically descending in Y
118 0 : if (srcPts[0].fY > srcPts[3].fY) {
119 0 : dst[0] = srcPts[3];
120 0 : dst[1] = srcPts[2];
121 0 : dst[2] = srcPts[1];
122 0 : dst[3] = srcPts[0];
123 0 : reverse = true;
124 : } else {
125 0 : memcpy(dst, srcPts, 4 * sizeof(SkPoint));
126 0 : reverse = false;
127 : }
128 :
129 : // are we completely above or below
130 0 : const SkScalar ctop = fClip.fTop;
131 0 : const SkScalar cbot = fClip.fBottom;
132 0 : if (dst[3].fY <= ctop || dst[0].fY >= cbot) {
133 0 : return false;
134 : }
135 :
136 : SkScalar t;
137 : SkPoint tmp[7]; // for SkChopCubicAt
138 :
139 : // are we partially above
140 0 : if (dst[0].fY < ctop && chopMonoCubicAtY(dst, ctop, &t)) {
141 0 : SkChopCubicAt(dst, tmp, t);
142 0 : dst[0] = tmp[3];
143 0 : dst[1] = tmp[4];
144 0 : dst[2] = tmp[5];
145 : }
146 :
147 : // are we partially below
148 0 : if (dst[3].fY > cbot && chopMonoCubicAtY(dst, cbot, &t)) {
149 0 : SkChopCubicAt(dst, tmp, t);
150 0 : dst[1] = tmp[1];
151 0 : dst[2] = tmp[2];
152 0 : dst[3] = tmp[3];
153 : }
154 :
155 0 : if (reverse) {
156 0 : SkTSwap<SkPoint>(dst[0], dst[3]);
157 0 : SkTSwap<SkPoint>(dst[1], dst[2]);
158 : }
159 0 : return true;
160 : }
161 :
|