OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
| 7 #include "SkGeometry.h" |
7 #include "SkLineParameters.h" | 8 #include "SkLineParameters.h" |
8 #include "SkPathOpsCubic.h" | 9 #include "SkPathOpsCubic.h" |
9 #include "SkPathOpsLine.h" | 10 #include "SkPathOpsLine.h" |
10 #include "SkPathOpsQuad.h" | 11 #include "SkPathOpsQuad.h" |
11 #include "SkPathOpsRect.h" | 12 #include "SkPathOpsRect.h" |
12 #include "SkTSort.h" | 13 #include "SkTSort.h" |
13 | 14 |
14 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te
st framework | 15 const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in te
st framework |
15 | 16 |
16 // give up when changing t no longer moves point | 17 // give up when changing t no longer moves point |
17 // also, copy point rather than recompute it when it does change | 18 // also, copy point rather than recompute it when it does change |
18 double SkDCubic::binarySearch(double min, double max, double axisIntercept, | 19 double SkDCubic::binarySearch(double min, double max, double axisIntercept, |
19 SearchAxis xAxis) const { | 20 SearchAxis xAxis) const { |
20 double t = (min + max) / 2; | 21 double t = (min + max) / 2; |
21 double step = (t - min) / 2; | 22 double step = (t - min) / 2; |
22 SkDPoint cubicAtT = ptAtT(t); | 23 SkDPoint cubicAtT = ptAtT(t); |
23 double calcPos = (&cubicAtT.fX)[xAxis]; | 24 double calcPos = (&cubicAtT.fX)[xAxis]; |
24 double calcDist = calcPos - axisIntercept; | 25 double calcDist = calcPos - axisIntercept; |
25 do { | 26 do { |
26 double priorT = t - step; | 27 double priorT = t - step; |
27 SkASSERT(priorT >= min); | 28 SkASSERT(priorT >= min); |
28 SkDPoint lessPt = ptAtT(priorT); | 29 SkDPoint lessPt = ptAtT(priorT); |
29 if (approximately_equal(lessPt.fX, cubicAtT.fX) | 30 if (approximately_equal_half(lessPt.fX, cubicAtT.fX) |
30 && approximately_equal(lessPt.fY, cubicAtT.fY)) { | 31 && approximately_equal_half(lessPt.fY, cubicAtT.fY)) { |
31 return -1; // binary search found no point at this axis intercept | 32 return -1; // binary search found no point at this axis intercept |
32 } | 33 } |
33 double lessDist = (&lessPt.fX)[xAxis] - axisIntercept; | 34 double lessDist = (&lessPt.fX)[xAxis] - axisIntercept; |
34 #if DEBUG_CUBIC_BINARY_SEARCH | 35 #if DEBUG_CUBIC_BINARY_SEARCH |
35 SkDebugf("t=%1.9g calc=%1.9g dist=%1.9g step=%1.9g less=%1.9g\n", t, cal
cPos, calcDist, | 36 SkDebugf("t=%1.9g calc=%1.9g dist=%1.9g step=%1.9g less=%1.9g\n", t, cal
cPos, calcDist, |
36 step, lessDist); | 37 step, lessDist); |
37 #endif | 38 #endif |
38 double lastStep = step; | 39 double lastStep = step; |
39 step /= 2; | 40 step /= 2; |
40 if (calcDist > 0 ? calcDist > lessDist : calcDist < lessDist) { | 41 if (calcDist > 0 ? calcDist > lessDist : calcDist < lessDist) { |
41 t = priorT; | 42 t = priorT; |
42 } else { | 43 } else { |
43 double nextT = t + lastStep; | 44 double nextT = t + lastStep; |
44 SkASSERT(nextT <= max); | 45 if (nextT > max) { |
| 46 return -1; |
| 47 } |
45 SkDPoint morePt = ptAtT(nextT); | 48 SkDPoint morePt = ptAtT(nextT); |
46 if (approximately_equal(morePt.fX, cubicAtT.fX) | 49 if (approximately_equal_half(morePt.fX, cubicAtT.fX) |
47 && approximately_equal(morePt.fY, cubicAtT.fY)) { | 50 && approximately_equal_half(morePt.fY, cubicAtT.fY)) { |
48 return -1; // binary search found no point at this axis interce
pt | 51 return -1; // binary search found no point at this axis interce
pt |
49 } | 52 } |
50 double moreDist = (&morePt.fX)[xAxis] - axisIntercept; | 53 double moreDist = (&morePt.fX)[xAxis] - axisIntercept; |
51 if (calcDist > 0 ? calcDist <= moreDist : calcDist >= moreDist) { | 54 if (calcDist > 0 ? calcDist <= moreDist : calcDist >= moreDist) { |
52 continue; | 55 continue; |
53 } | 56 } |
54 t = nextT; | 57 t = nextT; |
55 } | 58 } |
56 SkDPoint testAtT = ptAtT(t); | 59 SkDPoint testAtT = ptAtT(t); |
57 cubicAtT = testAtT; | 60 cubicAtT = testAtT; |
(...skipping 23 matching lines...) Expand all Loading... |
81 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C,
double* D) { | 84 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C,
double* D) { |
82 *A = src[6]; // d | 85 *A = src[6]; // d |
83 *B = src[4] * 3; // 3*c | 86 *B = src[4] * 3; // 3*c |
84 *C = src[2] * 3; // 3*b | 87 *C = src[2] * 3; // 3*b |
85 *D = src[0]; // a | 88 *D = src[0]; // a |
86 *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d | 89 *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d |
87 *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c | 90 *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c |
88 *C -= 3 * *D; // C = -3*a + 3*b | 91 *C -= 3 * *D; // C = -3*a + 3*b |
89 } | 92 } |
90 | 93 |
91 bool SkDCubic::controlsContainedByEnds() const { | |
92 SkDVector startTan = fPts[1] - fPts[0]; | |
93 if (startTan.fX == 0 && startTan.fY == 0) { | |
94 startTan = fPts[2] - fPts[0]; | |
95 } | |
96 SkDVector endTan = fPts[2] - fPts[3]; | |
97 if (endTan.fX == 0 && endTan.fY == 0) { | |
98 endTan = fPts[1] - fPts[3]; | |
99 } | |
100 if (startTan.dot(endTan) >= 0) { | |
101 return false; | |
102 } | |
103 SkDLine startEdge = {{fPts[0], fPts[0]}}; | |
104 startEdge[1].fX -= startTan.fY; | |
105 startEdge[1].fY += startTan.fX; | |
106 SkDLine endEdge = {{fPts[3], fPts[3]}}; | |
107 endEdge[1].fX -= endTan.fY; | |
108 endEdge[1].fY += endTan.fX; | |
109 double leftStart1 = startEdge.isLeft(fPts[1]); | |
110 if (leftStart1 * startEdge.isLeft(fPts[2]) < 0) { | |
111 return false; | |
112 } | |
113 double leftEnd1 = endEdge.isLeft(fPts[1]); | |
114 if (leftEnd1 * endEdge.isLeft(fPts[2]) < 0) { | |
115 return false; | |
116 } | |
117 return leftStart1 * leftEnd1 >= 0; | |
118 } | |
119 | |
120 bool SkDCubic::endsAreExtremaInXOrY() const { | 94 bool SkDCubic::endsAreExtremaInXOrY() const { |
121 return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX) | 95 return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX) |
122 && between(fPts[0].fX, fPts[2].fX, fPts[3].fX)) | 96 && between(fPts[0].fX, fPts[2].fX, fPts[3].fX)) |
123 || (between(fPts[0].fY, fPts[1].fY, fPts[3].fY) | 97 || (between(fPts[0].fY, fPts[1].fY, fPts[3].fY) |
124 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY)); | 98 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY)); |
125 } | 99 } |
126 | 100 |
| 101 // Do a quick reject by rotating all points relative to a line formed by |
| 102 // a pair of one cubic's points. If the 2nd cubic's points |
| 103 // are on the line or on the opposite side from the 1st cubic's 'odd man', the |
| 104 // curves at most intersect at the endpoints. |
| 105 /* if returning true, check contains true if cubic's hull collapsed, making the
cubic linear |
| 106 if returning false, check contains true if the the cubic pair have only the e
nd point in common |
| 107 */ |
| 108 bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const { |
| 109 bool linear = true; |
| 110 char hullOrder[4]; |
| 111 int hullCount = convexHull(hullOrder); |
| 112 int end1 = hullOrder[0]; |
| 113 int hullIndex = 0; |
| 114 const SkDPoint* endPt[2]; |
| 115 endPt[0] = &fPts[end1]; |
| 116 do { |
| 117 hullIndex = (hullIndex + 1) % hullCount; |
| 118 int end2 = hullOrder[hullIndex]; |
| 119 endPt[1] = &fPts[end2]; |
| 120 double origX = endPt[0]->fX; |
| 121 double origY = endPt[0]->fY; |
| 122 double adj = endPt[1]->fX - origX; |
| 123 double opp = endPt[1]->fY - origY; |
| 124 int oddManMask = other_two(end1, end2); |
| 125 int oddMan = end1 ^ oddManMask; |
| 126 double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX
) * opp; |
| 127 int oddMan2 = end2 ^ oddManMask; |
| 128 double sign2 = (fPts[oddMan2].fY - origY) * adj - (fPts[oddMan2].fX - or
igX) * opp; |
| 129 if (sign * sign2 < 0) { |
| 130 continue; |
| 131 } |
| 132 if (approximately_zero(sign)) { |
| 133 sign = sign2; |
| 134 if (approximately_zero(sign)) { |
| 135 continue; |
| 136 } |
| 137 } |
| 138 linear = false; |
| 139 bool foundOutlier = false; |
| 140 for (int n = 0; n < kPointCount; ++n) { |
| 141 double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp; |
| 142 if (test * sign > 0 && !precisely_zero(test)) { |
| 143 foundOutlier = true; |
| 144 break; |
| 145 } |
| 146 } |
| 147 if (!foundOutlier) { |
| 148 return false; |
| 149 } |
| 150 endPt[0] = endPt[1]; |
| 151 end1 = end2; |
| 152 } while (hullIndex); |
| 153 *isLinear = linear; |
| 154 return true; |
| 155 } |
| 156 |
127 bool SkDCubic::isLinear(int startIndex, int endIndex) const { | 157 bool SkDCubic::isLinear(int startIndex, int endIndex) const { |
128 SkLineParameters lineParameters; | 158 SkLineParameters lineParameters; |
129 lineParameters.cubicEndPoints(*this, startIndex, endIndex); | 159 lineParameters.cubicEndPoints(*this, startIndex, endIndex); |
130 // FIXME: maybe it's possible to avoid this and compare non-normalized | 160 // FIXME: maybe it's possible to avoid this and compare non-normalized |
131 lineParameters.normalize(); | 161 lineParameters.normalize(); |
| 162 double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX
, fPts[0].fY), |
| 163 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt
s[3].fY); |
| 164 double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX
, fPts[0].fY), |
| 165 fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPt
s[3].fY); |
| 166 largest = SkTMax(largest, -tiniest); |
132 double distance = lineParameters.controlPtDistance(*this, 1); | 167 double distance = lineParameters.controlPtDistance(*this, 1); |
133 if (!approximately_zero(distance)) { | 168 if (!approximately_zero_when_compared_to(distance, largest)) { |
134 return false; | 169 return false; |
135 } | 170 } |
136 distance = lineParameters.controlPtDistance(*this, 2); | 171 distance = lineParameters.controlPtDistance(*this, 2); |
137 return approximately_zero(distance); | 172 return approximately_zero_when_compared_to(distance, largest); |
| 173 } |
| 174 |
| 175 bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) { |
| 176 SkScalar d[3]; |
| 177 SkCubicType cubicType = SkClassifyCubic(pointsPtr, d); |
| 178 if (cubicType == kLoop_SkCubicType) { |
| 179 // crib code from gpu path utils that finds t values where loop self-int
ersects |
| 180 // use it to find mid of t values which should be a friendly place to ch
op |
| 181 SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]); |
| 182 SkScalar ls = d[1] - tempSqrt; |
| 183 SkScalar lt = 2.f * d[0]; |
| 184 SkScalar ms = d[1] + tempSqrt; |
| 185 SkScalar mt = 2.f * d[0]; |
| 186 if (between(0, ls, lt) || between(0, ms, mt)) { |
| 187 ls = ls / lt; |
| 188 ms = ms / mt; |
| 189 SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms)); |
| 190 SkScalar larger = SkTMin(1.f, SkTMax(ls, ms)); |
| 191 *t = (smaller + larger) / 2; |
| 192 return *t > 0 && *t < 1; |
| 193 } |
| 194 } else if (cubicType == kSerpentine_SkCubicType) { |
| 195 SkDCubic cubic; |
| 196 cubic.set(pointsPtr); |
| 197 double inflectionTs[2]; |
| 198 int infTCount = cubic.findInflections(inflectionTs); |
| 199 if (infTCount == 2) { |
| 200 double maxCurvature[3]; |
| 201 int roots = cubic.findMaxCurvature(maxCurvature); |
| 202 for (int index = 0; index < roots; ++index) { |
| 203 if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1
])) { |
| 204 *t = maxCurvature[index]; |
| 205 return true; |
| 206 } |
| 207 } |
| 208 } else if (infTCount == 1) { |
| 209 *t = inflectionTs[0]; |
| 210 return *t > 0 && *t < 1; |
| 211 } |
| 212 return false; |
| 213 } |
| 214 return false; |
138 } | 215 } |
139 | 216 |
140 bool SkDCubic::monotonicInY() const { | 217 bool SkDCubic::monotonicInY() const { |
141 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) | 218 return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) |
142 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); | 219 && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); |
143 } | 220 } |
144 | 221 |
| 222 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const
{ |
| 223 int offset = (int) !SkToBool(index); |
| 224 o1Pts[0] = &fPts[offset]; |
| 225 o1Pts[1] = &fPts[++offset]; |
| 226 o1Pts[2] = &fPts[++offset]; |
| 227 } |
| 228 |
145 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept
, | 229 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept
, |
146 SearchAxis xAxis, double* validRoots) const { | 230 SearchAxis xAxis, double* validRoots) const { |
147 extrema += findInflections(&extremeTs[extrema]); | 231 extrema += findInflections(&extremeTs[extrema]); |
148 extremeTs[extrema++] = 0; | 232 extremeTs[extrema++] = 0; |
149 extremeTs[extrema] = 1; | 233 extremeTs[extrema] = 1; |
150 SkTQSort(extremeTs, extremeTs + extrema); | 234 SkTQSort(extremeTs, extremeTs + extrema); |
151 int validCount = 0; | 235 int validCount = 0; |
152 for (int index = 0; index < extrema; ) { | 236 for (int index = 0; index < extrema; ) { |
153 double min = extremeTs[index]; | 237 double min = extremeTs[index]; |
154 double max = extremeTs[++index]; | 238 double max = extremeTs[++index]; |
155 if (min == max) { | 239 if (min == max) { |
156 continue; | 240 continue; |
157 } | 241 } |
158 double newT = binarySearch(min, max, axisIntercept, xAxis); | 242 double newT = binarySearch(min, max, axisIntercept, xAxis); |
159 if (newT >= 0) { | 243 if (newT >= 0) { |
160 validRoots[validCount++] = newT; | 244 validRoots[validCount++] = newT; |
161 } | 245 } |
162 } | 246 } |
163 return validCount; | 247 return validCount; |
164 } | 248 } |
165 | 249 |
166 bool SkDCubic::serpentine() const { | |
167 #if 0 // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicO
p53d | |
168 double tValues[2]; | |
169 // OPTIMIZATION : another case where caching the present of cubic inflection
s would be useful | |
170 return findInflections(tValues) > 1; | |
171 #endif | |
172 if (!controlsContainedByEnds()) { | |
173 return false; | |
174 } | |
175 double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY); | |
176 for (int idx = 0; idx < 2; ++idx) { | |
177 wiggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[i
dx].fY); | |
178 } | |
179 double waggle = (fPts[1].fX - fPts[3].fX) * (fPts[1].fY + fPts[3].fY); | |
180 for (int idx = 1; idx < 3; ++idx) { | |
181 waggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[i
dx].fY); | |
182 } | |
183 return wiggle * waggle < 0; | |
184 } | |
185 | |
186 // cubic roots | 250 // cubic roots |
187 | 251 |
188 static const double PI = 3.141592653589793; | 252 static const double PI = 3.141592653589793; |
189 | 253 |
190 // from SkGeometry.cpp (and Numeric Solutions, 5.6) | 254 // from SkGeometry.cpp (and Numeric Solutions, 5.6) |
191 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { | 255 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { |
192 double s[3]; | 256 double s[3]; |
193 int realRoots = RootsReal(A, B, C, D, s); | 257 int realRoots = RootsReal(A, B, C, D, s); |
194 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t); | 258 int foundRoots = SkDQuad::AddValidTs(s, realRoots, t); |
195 return foundRoots; | 259 return foundRoots; |
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 dstPt->fX = fPts[endIndex].fX; | 562 dstPt->fX = fPts[endIndex].fX; |
499 } | 563 } |
500 if (fPts[endIndex].fY == fPts[ctrlIndex].fY) { | 564 if (fPts[endIndex].fY == fPts[ctrlIndex].fY) { |
501 dstPt->fY = fPts[endIndex].fY; | 565 dstPt->fY = fPts[endIndex].fY; |
502 } | 566 } |
503 } | 567 } |
504 | 568 |
505 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d, | 569 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d, |
506 double t1, double t2, SkDPoint dst[2]) const { | 570 double t1, double t2, SkDPoint dst[2]) const { |
507 SkASSERT(t1 != t2); | 571 SkASSERT(t1 != t2); |
508 #if 0 | |
509 double ex = interp_cubic_coords(&fPts[0].fX, (t1 * 2 + t2) / 3); | |
510 double ey = interp_cubic_coords(&fPts[0].fY, (t1 * 2 + t2) / 3); | |
511 double fx = interp_cubic_coords(&fPts[0].fX, (t1 + t2 * 2) / 3); | |
512 double fy = interp_cubic_coords(&fPts[0].fY, (t1 + t2 * 2) / 3); | |
513 double mx = ex * 27 - a.fX * 8 - d.fX; | |
514 double my = ey * 27 - a.fY * 8 - d.fY; | |
515 double nx = fx * 27 - a.fX - d.fX * 8; | |
516 double ny = fy * 27 - a.fY - d.fY * 8; | |
517 /* bx = */ dst[0].fX = (mx * 2 - nx) / 18; | |
518 /* by = */ dst[0].fY = (my * 2 - ny) / 18; | |
519 /* cx = */ dst[1].fX = (nx * 2 - mx) / 18; | |
520 /* cy = */ dst[1].fY = (ny * 2 - my) / 18; | |
521 #else | |
522 // this approach assumes that the control points computed directly are accur
ate enough | 572 // this approach assumes that the control points computed directly are accur
ate enough |
523 SkDCubic sub = subDivide(t1, t2); | 573 SkDCubic sub = subDivide(t1, t2); |
524 dst[0] = sub[1] + (a - sub[0]); | 574 dst[0] = sub[1] + (a - sub[0]); |
525 dst[1] = sub[2] + (d - sub[3]); | 575 dst[1] = sub[2] + (d - sub[3]); |
526 #endif | |
527 if (t1 == 0 || t2 == 0) { | 576 if (t1 == 0 || t2 == 0) { |
528 align(0, 1, t1 == 0 ? &dst[0] : &dst[1]); | 577 align(0, 1, t1 == 0 ? &dst[0] : &dst[1]); |
529 } | 578 } |
530 if (t1 == 1 || t2 == 1) { | 579 if (t1 == 1 || t2 == 1) { |
531 align(3, 2, t1 == 1 ? &dst[0] : &dst[1]); | 580 align(3, 2, t1 == 1 ? &dst[0] : &dst[1]); |
532 } | 581 } |
533 if (AlmostBequalUlps(dst[0].fX, a.fX)) { | 582 if (AlmostBequalUlps(dst[0].fX, a.fX)) { |
534 dst[0].fX = a.fX; | 583 dst[0].fX = a.fX; |
535 } | 584 } |
536 if (AlmostBequalUlps(dst[0].fY, a.fY)) { | 585 if (AlmostBequalUlps(dst[0].fY, a.fY)) { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
576 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; | 625 dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; |
577 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; | 626 dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; |
578 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; | 627 dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; |
579 dst.pts[6] = fPts[3]; | 628 dst.pts[6] = fPts[3]; |
580 return dst; | 629 return dst; |
581 } | 630 } |
582 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); | 631 interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); |
583 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); | 632 interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); |
584 return dst; | 633 return dst; |
585 } | 634 } |
OLD | NEW |