1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/pathops/SkPathOpsCubic.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,523 @@ 1.4 +/* 1.5 + * Copyright 2012 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 +#include "SkLineParameters.h" 1.11 +#include "SkPathOpsCubic.h" 1.12 +#include "SkPathOpsLine.h" 1.13 +#include "SkPathOpsQuad.h" 1.14 +#include "SkPathOpsRect.h" 1.15 + 1.16 +const int SkDCubic::gPrecisionUnit = 256; // FIXME: test different values in test framework 1.17 + 1.18 +// FIXME: cache keep the bounds and/or precision with the caller? 1.19 +double SkDCubic::calcPrecision() const { 1.20 + SkDRect dRect; 1.21 + dRect.setBounds(*this); // OPTIMIZATION: just use setRawBounds ? 1.22 + double width = dRect.fRight - dRect.fLeft; 1.23 + double height = dRect.fBottom - dRect.fTop; 1.24 + return (width > height ? width : height) / gPrecisionUnit; 1.25 +} 1.26 + 1.27 +bool SkDCubic::clockwise() const { 1.28 + double sum = (fPts[0].fX - fPts[3].fX) * (fPts[0].fY + fPts[3].fY); 1.29 + for (int idx = 0; idx < 3; ++idx) { 1.30 + sum += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY); 1.31 + } 1.32 + return sum <= 0; 1.33 +} 1.34 + 1.35 +void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C, double* D) { 1.36 + *A = src[6]; // d 1.37 + *B = src[4] * 3; // 3*c 1.38 + *C = src[2] * 3; // 3*b 1.39 + *D = src[0]; // a 1.40 + *A -= *D - *C + *B; // A = -a + 3*b - 3*c + d 1.41 + *B += 3 * *D - 2 * *C; // B = 3*a - 6*b + 3*c 1.42 + *C -= 3 * *D; // C = -3*a + 3*b 1.43 +} 1.44 + 1.45 +bool SkDCubic::controlsContainedByEnds() const { 1.46 + SkDVector startTan = fPts[1] - fPts[0]; 1.47 + if (startTan.fX == 0 && startTan.fY == 0) { 1.48 + startTan = fPts[2] - fPts[0]; 1.49 + } 1.50 + SkDVector endTan = fPts[2] - fPts[3]; 1.51 + if (endTan.fX == 0 && endTan.fY == 0) { 1.52 + endTan = fPts[1] - fPts[3]; 1.53 + } 1.54 + if (startTan.dot(endTan) >= 0) { 1.55 + return false; 1.56 + } 1.57 + SkDLine startEdge = {{fPts[0], fPts[0]}}; 1.58 + startEdge[1].fX -= startTan.fY; 1.59 + startEdge[1].fY += startTan.fX; 1.60 + SkDLine endEdge = {{fPts[3], fPts[3]}}; 1.61 + endEdge[1].fX -= endTan.fY; 1.62 + endEdge[1].fY += endTan.fX; 1.63 + double leftStart1 = startEdge.isLeft(fPts[1]); 1.64 + if (leftStart1 * startEdge.isLeft(fPts[2]) < 0) { 1.65 + return false; 1.66 + } 1.67 + double leftEnd1 = endEdge.isLeft(fPts[1]); 1.68 + if (leftEnd1 * endEdge.isLeft(fPts[2]) < 0) { 1.69 + return false; 1.70 + } 1.71 + return leftStart1 * leftEnd1 >= 0; 1.72 +} 1.73 + 1.74 +bool SkDCubic::endsAreExtremaInXOrY() const { 1.75 + return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX) 1.76 + && between(fPts[0].fX, fPts[2].fX, fPts[3].fX)) 1.77 + || (between(fPts[0].fY, fPts[1].fY, fPts[3].fY) 1.78 + && between(fPts[0].fY, fPts[2].fY, fPts[3].fY)); 1.79 +} 1.80 + 1.81 +bool SkDCubic::isLinear(int startIndex, int endIndex) const { 1.82 + SkLineParameters lineParameters; 1.83 + lineParameters.cubicEndPoints(*this, startIndex, endIndex); 1.84 + // FIXME: maybe it's possible to avoid this and compare non-normalized 1.85 + lineParameters.normalize(); 1.86 + double distance = lineParameters.controlPtDistance(*this, 1); 1.87 + if (!approximately_zero(distance)) { 1.88 + return false; 1.89 + } 1.90 + distance = lineParameters.controlPtDistance(*this, 2); 1.91 + return approximately_zero(distance); 1.92 +} 1.93 + 1.94 +bool SkDCubic::monotonicInY() const { 1.95 + return between(fPts[0].fY, fPts[1].fY, fPts[3].fY) 1.96 + && between(fPts[0].fY, fPts[2].fY, fPts[3].fY); 1.97 +} 1.98 + 1.99 +bool SkDCubic::serpentine() const { 1.100 + if (!controlsContainedByEnds()) { 1.101 + return false; 1.102 + } 1.103 + double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY); 1.104 + for (int idx = 0; idx < 2; ++idx) { 1.105 + wiggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY); 1.106 + } 1.107 + double waggle = (fPts[1].fX - fPts[3].fX) * (fPts[1].fY + fPts[3].fY); 1.108 + for (int idx = 1; idx < 3; ++idx) { 1.109 + waggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY); 1.110 + } 1.111 + return wiggle * waggle < 0; 1.112 +} 1.113 + 1.114 +// cubic roots 1.115 + 1.116 +static const double PI = 3.141592653589793; 1.117 + 1.118 +// from SkGeometry.cpp (and Numeric Solutions, 5.6) 1.119 +int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { 1.120 + double s[3]; 1.121 + int realRoots = RootsReal(A, B, C, D, s); 1.122 + int foundRoots = SkDQuad::AddValidTs(s, realRoots, t); 1.123 + return foundRoots; 1.124 +} 1.125 + 1.126 +int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) { 1.127 +#ifdef SK_DEBUG 1.128 + // create a string mathematica understands 1.129 + // GDB set print repe 15 # if repeated digits is a bother 1.130 + // set print elements 400 # if line doesn't fit 1.131 + char str[1024]; 1.132 + sk_bzero(str, sizeof(str)); 1.133 + SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", 1.134 + A, B, C, D); 1.135 + SkPathOpsDebug::MathematicaIze(str, sizeof(str)); 1.136 +#if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA 1.137 + SkDebugf("%s\n", str); 1.138 +#endif 1.139 +#endif 1.140 + if (approximately_zero(A) 1.141 + && approximately_zero_when_compared_to(A, B) 1.142 + && approximately_zero_when_compared_to(A, C) 1.143 + && approximately_zero_when_compared_to(A, D)) { // we're just a quadratic 1.144 + return SkDQuad::RootsReal(B, C, D, s); 1.145 + } 1.146 + if (approximately_zero_when_compared_to(D, A) 1.147 + && approximately_zero_when_compared_to(D, B) 1.148 + && approximately_zero_when_compared_to(D, C)) { // 0 is one root 1.149 + int num = SkDQuad::RootsReal(A, B, C, s); 1.150 + for (int i = 0; i < num; ++i) { 1.151 + if (approximately_zero(s[i])) { 1.152 + return num; 1.153 + } 1.154 + } 1.155 + s[num++] = 0; 1.156 + return num; 1.157 + } 1.158 + if (approximately_zero(A + B + C + D)) { // 1 is one root 1.159 + int num = SkDQuad::RootsReal(A, A + B, -D, s); 1.160 + for (int i = 0; i < num; ++i) { 1.161 + if (AlmostDequalUlps(s[i], 1)) { 1.162 + return num; 1.163 + } 1.164 + } 1.165 + s[num++] = 1; 1.166 + return num; 1.167 + } 1.168 + double a, b, c; 1.169 + { 1.170 + double invA = 1 / A; 1.171 + a = B * invA; 1.172 + b = C * invA; 1.173 + c = D * invA; 1.174 + } 1.175 + double a2 = a * a; 1.176 + double Q = (a2 - b * 3) / 9; 1.177 + double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; 1.178 + double R2 = R * R; 1.179 + double Q3 = Q * Q * Q; 1.180 + double R2MinusQ3 = R2 - Q3; 1.181 + double adiv3 = a / 3; 1.182 + double r; 1.183 + double* roots = s; 1.184 + if (R2MinusQ3 < 0) { // we have 3 real roots 1.185 + double theta = acos(R / sqrt(Q3)); 1.186 + double neg2RootQ = -2 * sqrt(Q); 1.187 + 1.188 + r = neg2RootQ * cos(theta / 3) - adiv3; 1.189 + *roots++ = r; 1.190 + 1.191 + r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3; 1.192 + if (!AlmostDequalUlps(s[0], r)) { 1.193 + *roots++ = r; 1.194 + } 1.195 + r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3; 1.196 + if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) { 1.197 + *roots++ = r; 1.198 + } 1.199 + } else { // we have 1 real root 1.200 + double sqrtR2MinusQ3 = sqrt(R2MinusQ3); 1.201 + double A = fabs(R) + sqrtR2MinusQ3; 1.202 + A = SkDCubeRoot(A); 1.203 + if (R > 0) { 1.204 + A = -A; 1.205 + } 1.206 + if (A != 0) { 1.207 + A += Q / A; 1.208 + } 1.209 + r = A - adiv3; 1.210 + *roots++ = r; 1.211 + if (AlmostDequalUlps(R2, Q3)) { 1.212 + r = -A / 2 - adiv3; 1.213 + if (!AlmostDequalUlps(s[0], r)) { 1.214 + *roots++ = r; 1.215 + } 1.216 + } 1.217 + } 1.218 + return static_cast<int>(roots - s); 1.219 +} 1.220 + 1.221 +// from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf 1.222 +// c(t) = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3 1.223 +// c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2 1.224 +// = 3(b-a)(1-t)^2 + 6(c-b)t(1-t) + 3(d-c)t^2 1.225 +static double derivative_at_t(const double* src, double t) { 1.226 + double one_t = 1 - t; 1.227 + double a = src[0]; 1.228 + double b = src[2]; 1.229 + double c = src[4]; 1.230 + double d = src[6]; 1.231 + return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t); 1.232 +} 1.233 + 1.234 +// OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t? 1.235 +SkDVector SkDCubic::dxdyAtT(double t) const { 1.236 + SkDVector result = { derivative_at_t(&fPts[0].fX, t), derivative_at_t(&fPts[0].fY, t) }; 1.237 + return result; 1.238 +} 1.239 + 1.240 +// OPTIMIZE? share code with formulate_F1DotF2 1.241 +int SkDCubic::findInflections(double tValues[]) const { 1.242 + double Ax = fPts[1].fX - fPts[0].fX; 1.243 + double Ay = fPts[1].fY - fPts[0].fY; 1.244 + double Bx = fPts[2].fX - 2 * fPts[1].fX + fPts[0].fX; 1.245 + double By = fPts[2].fY - 2 * fPts[1].fY + fPts[0].fY; 1.246 + double Cx = fPts[3].fX + 3 * (fPts[1].fX - fPts[2].fX) - fPts[0].fX; 1.247 + double Cy = fPts[3].fY + 3 * (fPts[1].fY - fPts[2].fY) - fPts[0].fY; 1.248 + return SkDQuad::RootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues); 1.249 +} 1.250 + 1.251 +static void formulate_F1DotF2(const double src[], double coeff[4]) { 1.252 + double a = src[2] - src[0]; 1.253 + double b = src[4] - 2 * src[2] + src[0]; 1.254 + double c = src[6] + 3 * (src[2] - src[4]) - src[0]; 1.255 + coeff[0] = c * c; 1.256 + coeff[1] = 3 * b * c; 1.257 + coeff[2] = 2 * b * b + c * a; 1.258 + coeff[3] = a * b; 1.259 +} 1.260 + 1.261 +/** SkDCubic'(t) = At^2 + Bt + C, where 1.262 + A = 3(-a + 3(b - c) + d) 1.263 + B = 6(a - 2b + c) 1.264 + C = 3(b - a) 1.265 + Solve for t, keeping only those that fit between 0 < t < 1 1.266 +*/ 1.267 +int SkDCubic::FindExtrema(double a, double b, double c, double d, double tValues[2]) { 1.268 + // we divide A,B,C by 3 to simplify 1.269 + double A = d - a + 3*(b - c); 1.270 + double B = 2*(a - b - b + c); 1.271 + double C = b - a; 1.272 + 1.273 + return SkDQuad::RootsValidT(A, B, C, tValues); 1.274 +} 1.275 + 1.276 +/* from SkGeometry.cpp 1.277 + Looking for F' dot F'' == 0 1.278 + 1.279 + A = b - a 1.280 + B = c - 2b + a 1.281 + C = d - 3c + 3b - a 1.282 + 1.283 + F' = 3Ct^2 + 6Bt + 3A 1.284 + F'' = 6Ct + 6B 1.285 + 1.286 + F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB 1.287 +*/ 1.288 +int SkDCubic::findMaxCurvature(double tValues[]) const { 1.289 + double coeffX[4], coeffY[4]; 1.290 + int i; 1.291 + formulate_F1DotF2(&fPts[0].fX, coeffX); 1.292 + formulate_F1DotF2(&fPts[0].fY, coeffY); 1.293 + for (i = 0; i < 4; i++) { 1.294 + coeffX[i] = coeffX[i] + coeffY[i]; 1.295 + } 1.296 + return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues); 1.297 +} 1.298 + 1.299 +SkDPoint SkDCubic::top(double startT, double endT) const { 1.300 + SkDCubic sub = subDivide(startT, endT); 1.301 + SkDPoint topPt = sub[0]; 1.302 + if (topPt.fY > sub[3].fY || (topPt.fY == sub[3].fY && topPt.fX > sub[3].fX)) { 1.303 + topPt = sub[3]; 1.304 + } 1.305 + double extremeTs[2]; 1.306 + if (!sub.monotonicInY()) { 1.307 + int roots = FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, sub[3].fY, extremeTs); 1.308 + for (int index = 0; index < roots; ++index) { 1.309 + double t = startT + (endT - startT) * extremeTs[index]; 1.310 + SkDPoint mid = ptAtT(t); 1.311 + if (topPt.fY > mid.fY || (topPt.fY == mid.fY && topPt.fX > mid.fX)) { 1.312 + topPt = mid; 1.313 + } 1.314 + } 1.315 + } 1.316 + return topPt; 1.317 +} 1.318 + 1.319 +SkDPoint SkDCubic::ptAtT(double t) const { 1.320 + if (0 == t) { 1.321 + return fPts[0]; 1.322 + } 1.323 + if (1 == t) { 1.324 + return fPts[3]; 1.325 + } 1.326 + double one_t = 1 - t; 1.327 + double one_t2 = one_t * one_t; 1.328 + double a = one_t2 * one_t; 1.329 + double b = 3 * one_t2 * t; 1.330 + double t2 = t * t; 1.331 + double c = 3 * one_t * t2; 1.332 + double d = t2 * t; 1.333 + SkDPoint result = {a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX + d * fPts[3].fX, 1.334 + a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY + d * fPts[3].fY}; 1.335 + return result; 1.336 +} 1.337 + 1.338 +/* 1.339 + Given a cubic c, t1, and t2, find a small cubic segment. 1.340 + 1.341 + The new cubic is defined as points A, B, C, and D, where 1.342 + s1 = 1 - t1 1.343 + s2 = 1 - t2 1.344 + A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1 1.345 + D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2 1.346 + 1.347 + We don't have B or C. So We define two equations to isolate them. 1.348 + First, compute two reference T values 1/3 and 2/3 from t1 to t2: 1.349 + 1.350 + c(at (2*t1 + t2)/3) == E 1.351 + c(at (t1 + 2*t2)/3) == F 1.352 + 1.353 + Next, compute where those values must be if we know the values of B and C: 1.354 + 1.355 + _12 = A*2/3 + B*1/3 1.356 + 12_ = A*1/3 + B*2/3 1.357 + _23 = B*2/3 + C*1/3 1.358 + 23_ = B*1/3 + C*2/3 1.359 + _34 = C*2/3 + D*1/3 1.360 + 34_ = C*1/3 + D*2/3 1.361 + _123 = (A*2/3 + B*1/3)*2/3 + (B*2/3 + C*1/3)*1/3 = A*4/9 + B*4/9 + C*1/9 1.362 + 123_ = (A*1/3 + B*2/3)*1/3 + (B*1/3 + C*2/3)*2/3 = A*1/9 + B*4/9 + C*4/9 1.363 + _234 = (B*2/3 + C*1/3)*2/3 + (C*2/3 + D*1/3)*1/3 = B*4/9 + C*4/9 + D*1/9 1.364 + 234_ = (B*1/3 + C*2/3)*1/3 + (C*1/3 + D*2/3)*2/3 = B*1/9 + C*4/9 + D*4/9 1.365 + _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3 1.366 + = A*8/27 + B*12/27 + C*6/27 + D*1/27 1.367 + = E 1.368 + 1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3 1.369 + = A*1/27 + B*6/27 + C*12/27 + D*8/27 1.370 + = F 1.371 + E*27 = A*8 + B*12 + C*6 + D 1.372 + F*27 = A + B*6 + C*12 + D*8 1.373 + 1.374 +Group the known values on one side: 1.375 + 1.376 + M = E*27 - A*8 - D = B*12 + C* 6 1.377 + N = F*27 - A - D*8 = B* 6 + C*12 1.378 + M*2 - N = B*18 1.379 + N*2 - M = C*18 1.380 + B = (M*2 - N)/18 1.381 + C = (N*2 - M)/18 1.382 + */ 1.383 + 1.384 +static double interp_cubic_coords(const double* src, double t) { 1.385 + double ab = SkDInterp(src[0], src[2], t); 1.386 + double bc = SkDInterp(src[2], src[4], t); 1.387 + double cd = SkDInterp(src[4], src[6], t); 1.388 + double abc = SkDInterp(ab, bc, t); 1.389 + double bcd = SkDInterp(bc, cd, t); 1.390 + double abcd = SkDInterp(abc, bcd, t); 1.391 + return abcd; 1.392 +} 1.393 + 1.394 +SkDCubic SkDCubic::subDivide(double t1, double t2) const { 1.395 + if (t1 == 0 || t2 == 1) { 1.396 + if (t1 == 0 && t2 == 1) { 1.397 + return *this; 1.398 + } 1.399 + SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1); 1.400 + SkDCubic dst = t1 == 0 ? pair.first() : pair.second(); 1.401 + return dst; 1.402 + } 1.403 + SkDCubic dst; 1.404 + double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1); 1.405 + double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1); 1.406 + double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3); 1.407 + double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3); 1.408 + double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3); 1.409 + double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3); 1.410 + double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2); 1.411 + double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2); 1.412 + double mx = ex * 27 - ax * 8 - dx; 1.413 + double my = ey * 27 - ay * 8 - dy; 1.414 + double nx = fx * 27 - ax - dx * 8; 1.415 + double ny = fy * 27 - ay - dy * 8; 1.416 + /* bx = */ dst[1].fX = (mx * 2 - nx) / 18; 1.417 + /* by = */ dst[1].fY = (my * 2 - ny) / 18; 1.418 + /* cx = */ dst[2].fX = (nx * 2 - mx) / 18; 1.419 + /* cy = */ dst[2].fY = (ny * 2 - my) / 18; 1.420 + // FIXME: call align() ? 1.421 + return dst; 1.422 +} 1.423 + 1.424 +void SkDCubic::align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const { 1.425 + if (fPts[endIndex].fX == fPts[ctrlIndex].fX) { 1.426 + dstPt->fX = fPts[endIndex].fX; 1.427 + } 1.428 + if (fPts[endIndex].fY == fPts[ctrlIndex].fY) { 1.429 + dstPt->fY = fPts[endIndex].fY; 1.430 + } 1.431 +} 1.432 + 1.433 +void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d, 1.434 + double t1, double t2, SkDPoint dst[2]) const { 1.435 + SkASSERT(t1 != t2); 1.436 +#if 0 1.437 + double ex = interp_cubic_coords(&fPts[0].fX, (t1 * 2 + t2) / 3); 1.438 + double ey = interp_cubic_coords(&fPts[0].fY, (t1 * 2 + t2) / 3); 1.439 + double fx = interp_cubic_coords(&fPts[0].fX, (t1 + t2 * 2) / 3); 1.440 + double fy = interp_cubic_coords(&fPts[0].fY, (t1 + t2 * 2) / 3); 1.441 + double mx = ex * 27 - a.fX * 8 - d.fX; 1.442 + double my = ey * 27 - a.fY * 8 - d.fY; 1.443 + double nx = fx * 27 - a.fX - d.fX * 8; 1.444 + double ny = fy * 27 - a.fY - d.fY * 8; 1.445 + /* bx = */ dst[0].fX = (mx * 2 - nx) / 18; 1.446 + /* by = */ dst[0].fY = (my * 2 - ny) / 18; 1.447 + /* cx = */ dst[1].fX = (nx * 2 - mx) / 18; 1.448 + /* cy = */ dst[1].fY = (ny * 2 - my) / 18; 1.449 +#else 1.450 + // this approach assumes that the control points computed directly are accurate enough 1.451 + SkDCubic sub = subDivide(t1, t2); 1.452 + dst[0] = sub[1] + (a - sub[0]); 1.453 + dst[1] = sub[2] + (d - sub[3]); 1.454 +#endif 1.455 + if (t1 == 0 || t2 == 0) { 1.456 + align(0, 1, t1 == 0 ? &dst[0] : &dst[1]); 1.457 + } 1.458 + if (t1 == 1 || t2 == 1) { 1.459 + align(3, 2, t1 == 1 ? &dst[0] : &dst[1]); 1.460 + } 1.461 + if (precisely_subdivide_equal(dst[0].fX, a.fX)) { 1.462 + dst[0].fX = a.fX; 1.463 + } 1.464 + if (precisely_subdivide_equal(dst[0].fY, a.fY)) { 1.465 + dst[0].fY = a.fY; 1.466 + } 1.467 + if (precisely_subdivide_equal(dst[1].fX, d.fX)) { 1.468 + dst[1].fX = d.fX; 1.469 + } 1.470 + if (precisely_subdivide_equal(dst[1].fY, d.fY)) { 1.471 + dst[1].fY = d.fY; 1.472 + } 1.473 +} 1.474 + 1.475 +/* classic one t subdivision */ 1.476 +static void interp_cubic_coords(const double* src, double* dst, double t) { 1.477 + double ab = SkDInterp(src[0], src[2], t); 1.478 + double bc = SkDInterp(src[2], src[4], t); 1.479 + double cd = SkDInterp(src[4], src[6], t); 1.480 + double abc = SkDInterp(ab, bc, t); 1.481 + double bcd = SkDInterp(bc, cd, t); 1.482 + double abcd = SkDInterp(abc, bcd, t); 1.483 + 1.484 + dst[0] = src[0]; 1.485 + dst[2] = ab; 1.486 + dst[4] = abc; 1.487 + dst[6] = abcd; 1.488 + dst[8] = bcd; 1.489 + dst[10] = cd; 1.490 + dst[12] = src[6]; 1.491 +} 1.492 + 1.493 +SkDCubicPair SkDCubic::chopAt(double t) const { 1.494 + SkDCubicPair dst; 1.495 + if (t == 0.5) { 1.496 + dst.pts[0] = fPts[0]; 1.497 + dst.pts[1].fX = (fPts[0].fX + fPts[1].fX) / 2; 1.498 + dst.pts[1].fY = (fPts[0].fY + fPts[1].fY) / 2; 1.499 + dst.pts[2].fX = (fPts[0].fX + 2 * fPts[1].fX + fPts[2].fX) / 4; 1.500 + dst.pts[2].fY = (fPts[0].fY + 2 * fPts[1].fY + fPts[2].fY) / 4; 1.501 + dst.pts[3].fX = (fPts[0].fX + 3 * (fPts[1].fX + fPts[2].fX) + fPts[3].fX) / 8; 1.502 + dst.pts[3].fY = (fPts[0].fY + 3 * (fPts[1].fY + fPts[2].fY) + fPts[3].fY) / 8; 1.503 + dst.pts[4].fX = (fPts[1].fX + 2 * fPts[2].fX + fPts[3].fX) / 4; 1.504 + dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; 1.505 + dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; 1.506 + dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; 1.507 + dst.pts[6] = fPts[3]; 1.508 + return dst; 1.509 + } 1.510 + interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); 1.511 + interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); 1.512 + return dst; 1.513 +} 1.514 + 1.515 +#ifdef SK_DEBUG 1.516 +void SkDCubic::dump() { 1.517 + SkDebugf("{{"); 1.518 + int index = 0; 1.519 + do { 1.520 + fPts[index].dump(); 1.521 + SkDebugf(", "); 1.522 + } while (++index < 3); 1.523 + fPts[index].dump(); 1.524 + SkDebugf("}}\n"); 1.525 +} 1.526 +#endif