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]);
}
SkCubicType SkClassifyCubic(const SkPoint src[4], SkScalar d[3]) {
    calc_cubic_inflection_func(src, d);
    return classify_cubic(src, d);
}
int GrPathUtils::chopCubicAtLoopIntersection(const SkPoint src[4], SkPoint dst[10], SkScalar klm[9],
                                             SkScalar klm_rev[3]) {
    // Variable to store the two parametric values at the loop double point
    SkScalar smallS = 0.f;
    SkScalar largeS = 0.f;

    SkScalar d[3];
    calc_cubic_inflection_func(src, d);

    CubicType cType = classify_cubic(src, 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) {
        // 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 (1 == 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(src, controlK, controlL, controlM, klm, &klm[3], &klm[6]);
    }
    return chop_count + 1;
}