blob: 9022ff30e6548a1435310bb29338f0c71bbfa675 [file] [log] [blame]
turk@google.com4896f9e2009-03-02 20:00:00 +00001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkCubicClipper.h"
18#include "SkGeometry.h"
19
20SkCubicClipper::SkCubicClipper() {}
21
22void SkCubicClipper::setClip(const SkIRect& clip) {
23 // conver to scalars, since that's where we'll see the points
24 fClip.set(clip);
25}
26
27
28static bool chopMonoCubicAtY(SkPoint pts[4], SkScalar y, SkScalar* t) {
29 SkScalar ycrv[4];
30 ycrv[0] = pts[0].fY - y;
31 ycrv[1] = pts[1].fY - y;
32 ycrv[2] = pts[2].fY - y;
33 ycrv[3] = pts[3].fY - y;
34
35#ifdef NEWTON_RAPHSON // Quadratic convergence, typically <= 3 iterations.
36 // Initial guess.
37 // TODO(turk): Check for zero denominator? Shouldn't happen unless the curve
38 // is not only monotonic but degenerate.
39#ifdef SK_SCALAR_IS_FLOAT
40 SkScalar t1 = ycrv[0] / (ycrv[0] - ycrv[3]);
41#else // !SK_SCALAR_IS_FLOAT
42 SkScalar t1 = SkDivBits(ycrv[0], ycrv[0] - ycrv[3], 16);
43#endif // !SK_SCALAR_IS_FLOAT
44
45 // Newton's iterations.
46 const SkScalar tol = SK_Scalar1 / 16384; // This leaves 2 fixed noise bits.
47 SkScalar t0;
48 const int maxiters = 5;
49 int iters = 0;
50 bool converged;
51 do {
52 t0 = t1;
53 SkScalar y01 = SkScalarInterp(ycrv[0], ycrv[1], t0);
54 SkScalar y12 = SkScalarInterp(ycrv[1], ycrv[2], t0);
55 SkScalar y23 = SkScalarInterp(ycrv[2], ycrv[3], t0);
56 SkScalar y012 = SkScalarInterp(y01, y12, t0);
57 SkScalar y123 = SkScalarInterp(y12, y23, t0);
58 SkScalar y0123 = SkScalarInterp(y012, y123, t0);
59 SkScalar yder = (y123 - y012) * 3;
60 // TODO(turk): check for yder==0: horizontal.
61#ifdef SK_SCALAR_IS_FLOAT
62 t1 -= y0123 / yder;
63#else // !SK_SCALAR_IS_FLOAT
64 t1 -= SkDivBits(y0123, yder, 16);
65#endif // !SK_SCALAR_IS_FLOAT
66 converged = SkScalarAbs(t1 - t0) <= tol; // NaN-safe
67 ++iters;
68 } while (!converged && (iters < maxiters));
69 *t = t1; // Return the result.
70
71 // The result might be valid, even if outside of the range [0, 1], but
72 // we never evaluate a Bezier outside this interval, so we return false.
73 if (t1 < 0 || t1 > SK_Scalar1)
74 return false; // This shouldn't happen, but check anyway.
75 return converged;
76
77#else // BISECTION // Linear convergence, typically 16 iterations.
78
79 // Check that the endpoints straddle zero.
80 SkScalar tNeg, tPos; // Parameter where the function is negative and positive, respectively.
81 if (ycrv[0] < 0) {
82 if (ycrv[3] < 0)
83 return false;
84 tNeg = 0;
85 tPos = SK_Scalar1;
86 } else if (ycrv[0] > 0) {
87 if (ycrv[3] > 0)
88 return false;
89 tNeg = SK_Scalar1;
90 tPos = 0;
91 } else {
92 *t = 0;
93 return true;
94 }
95
96 const SkScalar tol = SK_Scalar1 / 65536; // 1 for fixed, 1e-5 for float.
97 int iters = 0;
98 do {
99 SkScalar tMid = (tPos + tNeg) / 2;
100 SkScalar y01 = SkScalarInterp(ycrv[0], ycrv[1], tMid);
101 SkScalar y12 = SkScalarInterp(ycrv[1], ycrv[2], tMid);
102 SkScalar y23 = SkScalarInterp(ycrv[2], ycrv[3], tMid);
103 SkScalar y012 = SkScalarInterp(y01, y12, tMid);
104 SkScalar y123 = SkScalarInterp(y12, y23, tMid);
105 SkScalar y0123 = SkScalarInterp(y012, y123, tMid);
106 if (y0123 == 0) {
107 *t = tMid;
108 return true;
109 }
110 if (y0123 < 0) tNeg = tMid;
111 else tPos = tMid;
112 ++iters;
113 } while (!(SkScalarAbs(tPos - tNeg) <= tol)); // Nan-safe
114
115 *t = (tNeg + tPos) / 2;
116 return true;
117#endif // BISECTION
118}
119
120
121bool SkCubicClipper::clipCubic(const SkPoint srcPts[4], SkPoint dst[4]) {
122 bool reverse;
123
124 // we need the data to be monotonically descending in Y
125 if (srcPts[0].fY > srcPts[2].fY) {
126 dst[0] = srcPts[3];
127 dst[1] = srcPts[2];
128 dst[2] = srcPts[1];
129 dst[3] = srcPts[0];
130 reverse = true;
131 } else {
132 memcpy(dst, srcPts, 3 * sizeof(SkPoint));
133 reverse = false;
134 }
135
136 // are we completely above or below
137 const SkScalar ctop = fClip.fTop;
138 const SkScalar cbot = fClip.fBottom;
139 if (dst[2].fY <= ctop || dst[0].fY >= cbot) {
140 return false;
141 }
142
143 SkScalar t;
144 SkPoint tmp[5]; // for SkChopCubicAt
145
146 // are we partially above
147 if (dst[0].fY < ctop && chopMonoCubicAtY(dst, ctop, &t)) {
148 SkChopCubicAt(dst, tmp, t);
149 dst[0] = tmp[2];
150 dst[1] = tmp[3];
151 }
152
153 // are we partially below
154 if (dst[2].fY > cbot && chopMonoCubicAtY(dst, cbot, &t)) {
155 SkChopCubicAt(dst, tmp, t);
156 dst[1] = tmp[1];
157 dst[2] = tmp[2];
158 }
159
160 if (reverse) {
161 SkTSwap<SkPoint>(dst[0], dst[3]);
162 SkTSwap<SkPoint>(dst[1], dst[2]);
163 }
164 return true;
165}
166