Chromium Code Reviews| Index: src/gpu/GrPathUtils.cpp |
| diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp |
| index f169455b4ad019b38b4f8349a79c8cd6bd2ce88c..719fb1d3e75a7ec30b1dbfda6e03297187983aa1 100644 |
| --- a/src/gpu/GrPathUtils.cpp |
| +++ b/src/gpu/GrPathUtils.cpp |
| @@ -476,3 +476,333 @@ void GrPathUtils::convertCubicToQuads(const GrPoint p[4], |
| } |
| } |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| +enum CubicType { |
| + kSerpentine_CubicType, |
| + kCusp_CubicType, |
| + kLoop_CubicType, |
| + kQuadratic_CubicType, |
| + kLine_CubicType, |
| + kPoint_CubicType |
| +}; |
| + |
| +// discr(I) = d0^2 * (3*d1^2 - 4*d0*d2) |
| +// Classification: |
| +// discr(I) > 0 Serpentine |
| +// discr(I) = 0 Cusp |
| +// discr(I) < 0 Loop |
| +// d0 = d1 = 0 Quadratic |
| +// d0 = d1 = d2 = 0 Line |
| +// p0 = p1 = p2 = p3 Point |
| +static CubicType classify_cubic(const SkPoint p[4], const SkScalar d[3]) { |
| + if (p[0] == p[1] && p[0] == p[2] && p[0] == p[3]) { |
| + return kPoint_CubicType; |
| + } |
| + const SkScalar discr = d[0] * d[0] * (3.f * d[1] * d[1] - 4.f * d[0] * d[2]); |
|
jvanverth1
2013/08/19 15:12:06
Might want to make these checks against SK_ScalarN
|
| + if (discr > 0.f) { |
| + return kSerpentine_CubicType; |
| + } else if (discr < 0.f) { |
| + return kLoop_CubicType; |
| + } else { |
| + if (0.f == d[0] && 0.f == d[1]) { |
| + return (0.f == d[2] ? kLine_CubicType : kQuadratic_CubicType); |
| + } else { |
| + return kCusp_CubicType; |
| + } |
| + } |
| +} |
| + |
| +// Assumes the third component of points is 1. |
| +// Calcs p0 . (p1 x p2) |
| +static SkScalar calc_dot_cross_cubic(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) { |
| + const SkScalar xComp = p0.fX * (p1.fY - p2.fY); |
| + const SkScalar yComp = p0.fY * (p2.fX - p1.fX); |
| + const SkScalar wComp = p1.fX * p2.fY - p1.fY * p2.fX; |
| + return (xComp + yComp + wComp); |
| +} |
| + |
| +// Solves linear system to extract klm |
| +// P.K = k (similarly for l, m) |
| +// Where P is matrix of control points |
| +// K is coefficients for the line K |
| +// k is vector of values of K evaluated at the control points |
| +// Solving for K, thus K = P^(-1) . k |
| +static void calc_cubic_klm(const SkPoint p[4], const SkScalar controlK[4], |
| + const SkScalar controlL[4], const SkScalar controlM[4], |
| + SkScalar k[3], SkScalar l[3], SkScalar m[3]) { |
| + SkMatrix matrix; |
| + matrix.setAll(p[0].fX, p[0].fY, 1.f, |
| + p[1].fX, p[1].fY, 1.f, |
| + p[2].fX, p[2].fY, 1.f); |
| + SkMatrix inverse; |
| + if (matrix.invert(&inverse)) { |
| + inverse.mapHomogeneousPoints(k, controlK, 1); |
| + inverse.mapHomogeneousPoints(l, controlL, 1); |
| + inverse.mapHomogeneousPoints(m, controlM, 1); |
| + } |
| + |
| +} |
| + |
| +static void set_serp_klm(const SkScalar d[3], SkScalar k[4], SkScalar l[4], SkScalar m[4]) { |
| + SkScalar tempSqrt = SkScalarSqrt(9.f * d[1] * d[1] - 12.f * d[0] * d[2]); |
| + SkScalar ls = 3.f * d[1] - tempSqrt; |
| + SkScalar lt = 6.f * d[0]; |
| + SkScalar ms = 3.f * d[1] + tempSqrt; |
| + SkScalar mt = 6.f * d[0]; |
| + |
| + k[0] = ls * ms; |
| + k[1] = (3.f * ls * ms - ls * mt - lt * ms) / 3.f; |
| + k[2] = (lt * (mt - 2.f * ms) + ls * (3.f * ms - 2.f * mt)) / 3.f; |
| + k[3] = (lt - ls) * (mt - ms); |
| + |
| + l[0] = ls * ls * ls; |
| + const SkScalar lt_ls = lt - ls; |
| + l[1] = ls * ls * lt_ls * -1.f; |
| + l[2] = lt_ls * lt_ls * ls; |
| + l[3] = -1.f * lt_ls * lt_ls * lt_ls; |
| + |
| + m[0] = ms * ms * ms; |
| + const SkScalar mt_ms = mt - ms; |
| + m[1] = ms * ms * mt_ms * -1.f; |
| + m[2] = mt_ms * mt_ms * ms; |
| + m[3] = -1.f * mt_ms * mt_ms * mt_ms; |
| + |
| + // If d0 < 0 we need to flip the orientation of our curve |
| + // This is done by negating the k and l values |
| + // We want negative distance values to be on the inside |
| + if ( d[0] > 0) { |
| + for (int i = 0; i < 4; ++i) { |
| + k[i] = -k[i]; |
| + l[i] = -l[i]; |
| + } |
| + } |
| +} |
| + |
| +static void set_loop_klm(const SkScalar d[3], SkScalar k[4], SkScalar l[4], SkScalar m[4]) { |
| + SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]); |
| + SkScalar ls = d[1] - tempSqrt; |
| + SkScalar lt = 2.f * d[0]; |
| + SkScalar ms = d[1] + tempSqrt; |
| + SkScalar mt = 2.f * d[0]; |
| + |
| + k[0] = ls * ms; |
| + k[1] = (3.f * ls*ms - ls * mt - lt * ms) / 3.f; |
| + k[2] = (lt * (mt - 2.f * ms) + ls * (3.f * ms - 2.f * mt)) / 3.f; |
| + k[3] = (lt - ls) * (mt - ms); |
| + |
| + l[0] = ls * ls * ms; |
| + l[1] = (ls * (ls * (mt - 3.f * ms) + 2.f * lt * ms))/-3.f; |
| + l[2] = ((lt - ls) * (ls * (2.f * mt - 3.f * ms) + lt * ms))/3.f; |
| + l[3] = -1.f * (lt - ls) * (lt - ls) * (mt - ms); |
| + |
| + m[0] = ls * ms * ms; |
| + m[1] = (ms * (ls * (2.f * mt - 3.f * ms) + lt * ms))/-3.f; |
| + m[2] = ((mt - ms) * (ls * (mt - 3.f * ms) + 2.f * lt * ms))/3.f; |
| + m[3] = -1.f * (lt - ls) * (mt - ms) * (mt - ms); |
| + |
| + |
| + // If (d0 < 0 && sign(k1) > 0) || (d0 > 0 && sign(k1) < 0), |
| + // we need to flip the orientation of our curve. |
| + // This is done by negating the k and l values |
| + if ( (d[0] < 0 && k[1] < 0) || (d[0] > 0 && k[1] > 0)) { |
| + for (int i = 0; i < 4; ++i) { |
| + k[i] = -k[i]; |
| + l[i] = -l[i]; |
| + } |
| + } |
| +} |
| + |
| +static void set_cusp_klm(const SkScalar d[3], SkScalar k[4], SkScalar l[4], SkScalar m[4]) { |
| + const SkScalar ls = d[2]; |
| + const SkScalar lt = 3.f * d[1]; |
| + |
| + k[0] = ls; |
| + k[1] = ls - lt / 3.f; |
| + k[2] = ls - 2.f * lt / 3.f; |
| + k[3] = ls - lt; |
| + |
| + l[0] = ls * ls * ls; |
| + const SkScalar ls_lt = ls - lt; |
| + l[1] = ls * ls * ls_lt; |
| + l[2] = ls_lt * ls_lt * ls; |
| + l[3] = ls_lt * ls_lt * ls_lt; |
| + |
| + m[0] = 1.f; |
| + m[1] = 1.f; |
| + m[2] = 1.f; |
| + m[3] = 1.f; |
| +} |
| + |
| +// For the case when a cubic is actually a quadratic |
| +// M = |
| +// 0 0 0 |
| +// 1/3 0 1/3 |
| +// 2/3 1/3 2/3 |
| +// 1 1 1 |
| +static void set_quadratic_klm(const SkScalar d[3], SkScalar k[4], SkScalar l[4], SkScalar m[4]) { |
| + k[0] = 0.f; |
| + k[1] = 1.f/3.f; |
| + k[2] = 2.f/3.f; |
| + k[3] = 1.f; |
| + |
| + l[0] = 0.f; |
| + l[1] = 0.f; |
| + l[2] = 1.f/3.f; |
| + l[3] = 1.f; |
| + |
| + m[0] = 0.f; |
| + m[1] = 1.f/3.f; |
| + m[2] = 2.f/3.f; |
| + m[3] = 1.f; |
| + |
| + // If d2 < 0 we need to flip the orientation of our curve |
| + // This is done by negating the k and l values |
| + if ( d[2] > 0) { |
| + for (int i = 0; i < 4; ++i) { |
| + k[i] = -k[i]; |
| + l[i] = -l[i]; |
| + } |
| + } |
| +} |
| + |
| +// Calc coefficients of I(s,t) where roots of I are inflection points of curve |
| +// I(s,t) = t*(3*d0*s^2 - 3*d1*s*t + d2*t^2) |
| +// d0 = a1 - 2*a2+3*a3 |
| +// d1 = -a2 + 3*a3 |
| +// d2 = 3*a3 |
| +// a1 = p0 . (p3 x p2) |
| +// a2 = p1 . (p0 x p3) |
| +// a3 = p2 . (p1 x p0) |
| +// Places the values of d1, d2, d3 in array d passed in |
| +static void calc_cubic_inflection_func(const SkPoint p[4], SkScalar d[3]) { |
| + SkScalar a1 = calc_dot_cross_cubic(p[0], p[3], p[2]); |
| + SkScalar a2 = calc_dot_cross_cubic(p[1], p[0], p[3]); |
| + SkScalar a3 = calc_dot_cross_cubic(p[2], p[1], p[0]); |
| + |
| + // need to scale a's or values in later calculations will grow to high |
| + SkScalar max = SkScalarAbs(a1); |
| + max = SkMaxScalar(max, SkScalarAbs(a2)); |
| + max = SkMaxScalar(max, SkScalarAbs(a3)); |
| + max = 1.f/max; |
| + a1 = a1 * max; |
| + a2 = a2 * max; |
| + a3 = a3 * max; |
| + |
| + d[2] = 3.f * a3; |
| + d[1] = d[2] - a2; |
| + d[0] = d[1] - a2 + a1; |
| +} |
| + |
| +int GrPathUtils::chopCubicAtLoopIntersection(const SkPoint src[4], SkPoint dst[10], SkScalar klm[9], |
| + SkScalar klm_rev[3], const SkPoint devPts[4]) { |
| + // Variable to store the two parametric values at the loop double point |
| + SkScalar smallS = 0.f; |
| + SkScalar largeS = 0.f; |
| + |
| + SkScalar d[3]; |
| + const SkPoint* cPts = devPts ? devPts : src; |
| + calc_cubic_inflection_func(cPts, d); |
| + |
| + CubicType cType = classify_cubic(cPts, d); |
| + |
| + int chop_count = 0; |
| + if (kLoop_CubicType == cType) { |
| + SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]); |
| + SkScalar ls = d[1] - tempSqrt; |
| + SkScalar lt = 2.f * d[0]; |
| + SkScalar ms = d[1] + tempSqrt; |
| + SkScalar mt = 2.f * d[0]; |
| + ls = ls / lt; |
| + ms = ms / mt; |
| + // need to have t values sorted since this is what is expected by SkChopCubicAt |
| + if (ls <= ms) { |
| + smallS = ls; |
| + largeS = ms; |
| + } else { |
| + smallS = ms; |
| + largeS = ls; |
| + } |
| + |
| + SkScalar chop_ts[2]; |
| + if (smallS > 0.f && smallS < 1.f) { |
| + chop_ts[chop_count++] = smallS; |
| + } |
| + if (largeS > 0.f && largeS < 1.f) { |
| + chop_ts[chop_count++] = largeS; |
| + } |
| + if(dst) { |
| + SkChopCubicAt(src, dst, chop_ts, chop_count); |
| + } |
| + } else { |
| + if (dst) { |
| + memcpy(dst, src, sizeof(SkPoint) * 4); |
| + } |
| + } |
| + |
| + if (klm && klm_rev && devPts) { |
| + // Set klm_rev to to match the sub_section of cubic that needs to have its orientation |
| + // flipped. This will always be the section that is the "loop" |
| + if (2 == chop_count) { |
| + klm_rev[0] = 1.f; |
| + klm_rev[1] = -1.f; |
| + klm_rev[2] = 1.f; |
| + } else if (2 == chop_count) { |
| + if (smallS < 0.f) { |
| + klm_rev[0] = -1.f; |
| + klm_rev[1] = 1.f; |
| + } else { |
| + klm_rev[0] = 1.f; |
| + klm_rev[1] = -1.f; |
| + } |
| + } else { |
| + if (smallS < 0.f && largeS > 1.f) { |
| + klm_rev[0] = -1.f; |
| + } else { |
| + klm_rev[0] = 1.f; |
| + } |
| + } |
| + SkScalar controlK[4]; |
| + SkScalar controlL[4]; |
| + SkScalar controlM[4]; |
| + |
| + if (kSerpentine_CubicType == cType || (kCusp_CubicType == cType && 0.f != d[0])) { |
| + set_serp_klm(d, controlK, controlL, controlM); |
| + } else if (kLoop_CubicType == cType) { |
| + set_loop_klm(d, controlK, controlL, controlM); |
| + } else if (kCusp_CubicType == cType) { |
| + SkASSERT(0.f == d[0]); |
| + set_cusp_klm(d, controlK, controlL, controlM); |
| + } else if (kQuadratic_CubicType == cType) { |
| + set_quadratic_klm(d, controlK, controlL, controlM); |
| + } |
| + |
| + calc_cubic_klm(devPts, controlK, controlL, controlM, klm, &klm[3], &klm[6]); |
| + } |
| + return chop_count + 1; |
| +} |
| + |
| +void GrPathUtils::getCubicKLM(const SkPoint p[4], SkScalar klm[9]) { |
| + SkScalar d[3]; |
| + calc_cubic_inflection_func(p, d); |
| + |
| + CubicType cType = classify_cubic(p, d); |
| + |
| + SkScalar controlK[4]; |
| + SkScalar controlL[4]; |
| + SkScalar controlM[4]; |
| + |
| + if (kSerpentine_CubicType == cType || (kCusp_CubicType == cType && 0.f != d[0])) { |
| + set_serp_klm(d, controlK, controlL, controlM); |
| + } else if (kLoop_CubicType == cType) { |
| + set_loop_klm(d, controlK, controlL, controlM); |
| + } else if (kCusp_CubicType == cType) { |
| + SkASSERT(0.f == d[0]); |
| + set_cusp_klm(d, controlK, controlL, controlM); |
| + } else if (kQuadratic_CubicType == cType) { |
| + set_quadratic_klm(d, controlK, controlL, controlM); |
| + } |
| + |
| + calc_cubic_klm(p, controlK, controlL, controlM, klm, &klm[3], &klm[6]); |
| +} |