static void selfOneOff(skiatest::Reporter* reporter, int index) {
    const CubicPts& cubic = selfSet[index];
    SkPoint c[4];
    for (int i = 0; i < 4; ++i) {
        c[i] = cubic.fPts[i].asSkPoint();
    }
    SkScalar loopT;
    SkScalar d[3];
    SkCubicType cubicType = SkClassifyCubic(c, d);
    if (SkDCubic::ComplexBreak(c, &loopT) && cubicType == SkCubicType::kLoop_SkCubicType) {
        SkIntersections i;
        SkPoint twoCubics[7];
        SkChopCubicAt(c, twoCubics, loopT);
        SkDCubic chopped[2];
        chopped[0].set(&twoCubics[0]);
        chopped[1].set(&twoCubics[3]);
        int result = i.intersect(chopped[0], chopped[1]);
        REPORTER_ASSERT(reporter, result == 2);
        REPORTER_ASSERT(reporter, i.used() == 2);
        for (int index = 0; index < result; ++index) {
            SkDPoint pt1 = chopped[0].ptAtT(i[0][index]);
            SkDPoint pt2 = chopped[1].ptAtT(i[1][index]);
            REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
            reporter->bumpTestCount();
        }
    }
}
Пример #2
0
bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
    SkScalar d[3];
    SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
    if (cubicType == kLoop_SkCubicType) {
        // crib code from gpu path utils that finds t values where loop self-intersects
        // use it to find mid of t values which should be a friendly place to chop
        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];
        if (roughly_between(0, ls, lt) && roughly_between(0, ms, mt)) {
            ls = ls / lt;
            ms = ms / mt;
            SkASSERT(roughly_between(0, ls, 1) && roughly_between(0, ms, 1));
            *t = (ls + ms) / 2;
            SkASSERT(roughly_between(0, *t, 1));
            return *t > 0 && *t < 1;
        }
    } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubicType) {
        SkDCubic cubic;
        cubic.set(pointsPtr);
        double inflectionTs[2];
        int infTCount = cubic.findInflections(inflectionTs);
        if (infTCount == 2) {
            double maxCurvature[3];
            int roots = cubic.findMaxCurvature(maxCurvature);
#if DEBUG_CUBIC_SPLIT
            SkDebugf("%s\n", __FUNCTION__);
            cubic.dump();
            for (int index = 0; index < infTCount; ++index) {
                SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
                SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
                SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
                SkDLine perp = {{pt - dPt, pt + dPt}};
                perp.dump();
            }
            for (int index = 0; index < roots; ++index) {
                SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
                SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
                SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
                SkDLine perp = {{pt - dPt, pt + dPt}};
                perp.dump();
            }
#endif
            for (int index = 0; index < roots; ++index) {
                if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
                    *t = maxCurvature[index];
                    return *t > 0 && *t < 1;
                }
            }
        } else if (infTCount == 1) {
            *t = inflectionTs[0];
            return *t > 0 && *t < 1;
        }
    }
    return false;
}
    CubicKLMBench(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
                  SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3)  {
        fPoints[0].set(x0, y0);
        fPoints[1].set(x1, y1);
        fPoints[2].set(x2, y2);
        fPoints[3].set(x3, y3);

        fName = "cubic_klm_";
        switch (SkClassifyCubic(fPoints)) {
            case SkCubicType::kSerpentine:
                fName.append("serp");
                break;
            case SkCubicType::kLoop:
                fName.append("loop");
                break;
            default:
                SK_ABORT("Unexpected cubic type");
                break;
        }
    }
Пример #4
0
void GrPathUtils::getCubicKLM(const SkPoint p[4], SkScalar klm[9]) {
    SkScalar d[3];
    SkCubicType cType = SkClassifyCubic(p, d);

    SkScalar controlK[4];
    SkScalar controlL[4];
    SkScalar controlM[4];

    if (kSerpentine_SkCubicType == cType || (kCusp_SkCubicType == cType && 0.f != d[0])) {
        set_serp_klm(d, controlK, controlL, controlM);
    } else if (kLoop_SkCubicType == cType) {
        set_loop_klm(d, controlK, controlL, controlM);
    } else if (kCusp_SkCubicType == cType) {
        SkASSERT(0.f == d[0]);
        set_cusp_klm(d, controlK, controlL, controlM);
    } else if (kQuadratic_SkCubicType == cType) {
        set_quadratic_klm(d, controlK, controlL, controlM);
    }

    calc_cubic_klm(p, controlK, controlL, controlM, klm, &klm[3], &klm[6]);
}
Пример #5
0
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];
    SkCubicType cType = SkClassifyCubic(src, d);

    int chop_count = 0;
    if (kLoop_SkCubicType == 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_SkCubicType == cType || (kCusp_SkCubicType == cType && 0.f != d[0])) {
            set_serp_klm(d, controlK, controlL, controlM);
        } else if (kLoop_SkCubicType == cType) {
            set_loop_klm(d, controlK, controlL, controlM);
        } else if (kCusp_SkCubicType == cType) {
            SkASSERT(0.f == d[0]);
            set_cusp_klm(d, controlK, controlL, controlM);
        } else if (kQuadratic_SkCubicType == cType) {
            set_quadratic_klm(d, controlK, controlL, controlM);
        }

        calc_cubic_klm(src, controlK, controlL, controlM, klm, &klm[3], &klm[6]);
    }
    return chop_count + 1;
}
Пример #6
0
int SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
    SkDCubic cubic;
    cubic.set(pointsPtr);
    if (cubic.monotonicInX() && cubic.monotonicInY()) {
        return 0;
    }
    SkScalar d[3];
    SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
    switch (cubicType) {
        case kLoop_SkCubicType: {
            // crib code from gpu path utils that finds t values where loop self-intersects
            // use it to find mid of t values which should be a friendly place to chop
            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];
            if (roughly_between(0, ls, lt) && roughly_between(0, ms, mt)) {
                ls = ls / lt;
                ms = ms / mt;
                SkASSERT(roughly_between(0, ls, 1) && roughly_between(0, ms, 1));
                t[0] = (ls + ms) / 2;
                SkASSERT(roughly_between(0, *t, 1));
                return (int) (t[0] > 0 && t[0] < 1);
            }
        }
        // fall through if no t value found
        case kSerpentine_SkCubicType:
        case kCusp_SkCubicType: {
            double inflectionTs[2];
            int infTCount = cubic.findInflections(inflectionTs);
            double maxCurvature[3];
            int roots = cubic.findMaxCurvature(maxCurvature);
    #if DEBUG_CUBIC_SPLIT
            SkDebugf("%s\n", __FUNCTION__);
            cubic.dump();
            for (int index = 0; index < infTCount; ++index) {
                SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
                SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
                SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
                SkDLine perp = {{pt - dPt, pt + dPt}};
                perp.dump();
            }
            for (int index = 0; index < roots; ++index) {
                SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
                SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
                SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
                SkDLine perp = {{pt - dPt, pt + dPt}};
                perp.dump();
            }
    #endif
            if (infTCount == 2) {
                for (int index = 0; index < roots; ++index) {
                    if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
                        t[0] = maxCurvature[index];
                        return (int) (t[0] > 0 && t[0] < 1);
                    }
                }
            } else {
                int resultCount = 0;
                // FIXME: constant found through experimentation -- maybe there's a better way....
                double precision = cubic.calcPrecision() * 2;
                for (int index = 0; index < roots; ++index) {
                    double testT = maxCurvature[index];
                    if (0 >= testT || testT >= 1) {
                        continue;
                    }
                    // don't call dxdyAtT since we want (0,0) results
                    SkDVector dPt = { derivative_at_t(&cubic.fPts[0].fX, testT),
                            derivative_at_t(&cubic.fPts[0].fY, testT) };
                    double dPtLen = dPt.length();
                    if (dPtLen < precision) {
                        t[resultCount++] = testT;
                    }
                }
                if (!resultCount && infTCount == 1) {
                    t[0] = inflectionTs[0];
                    resultCount = (int) (t[0] > 0 && t[0] < 1);
                }
                return resultCount;
            }
        }
        default:
            ;
    }
    return 0;
}
Пример #7
0
void GrCCFillGeometry::cubicTo(const SkPoint P[4], float inflectPad, float loopIntersectPad) {
    SkASSERT(fBuildingContour);
    SkASSERT(P[0] == fPoints.back());

    // Don't crunch on the curve or inflate geometry if it is nearly flat (or just very small).
    // Flat curves can break the math below.
    if (are_collinear(P)) {
        Sk2f p0 = Sk2f::Load(P);
        Sk2f p3 = Sk2f::Load(P+3);
        this->appendLine(p0, p3);
        return;
    }

    Sk2f p0 = Sk2f::Load(P);
    Sk2f p1 = Sk2f::Load(P+1);
    Sk2f p2 = Sk2f::Load(P+2);
    Sk2f p3 = Sk2f::Load(P+3);

    // Also detect near-quadratics ahead of time.
    Sk2f tan0, tan1, c;
    get_cubic_tangents(p0, p1, p2, p3, &tan0, &tan1);
    if (is_cubic_nearly_quadratic(p0, p1, p2, p3, tan0, tan1, &c)) {
        this->appendQuadratics(p0, c, p3);
        return;
    }

    double tt[2], ss[2], D[4];
    fCurrCubicType = SkClassifyCubic(P, tt, ss, D);
    SkASSERT(!SkCubicIsDegenerate(fCurrCubicType));
    Sk2f t = Sk2f(static_cast<float>(tt[0]), static_cast<float>(tt[1]));
    Sk2f s = Sk2f(static_cast<float>(ss[0]), static_cast<float>(ss[1]));

    ExcludedTerm skipTerm = (std::abs(D[2]) > std::abs(D[1]))
                                    ? ExcludedTerm::kQuadraticTerm
                                    : ExcludedTerm::kLinearTerm;
    Sk2f C0 = SkNx_fma(Sk2f(3), p1 - p2, p3 - p0);
    Sk2f C1 = (ExcludedTerm::kLinearTerm == skipTerm
                       ? SkNx_fma(Sk2f(-2), p1, p0 + p2)
                       : p1 - p0) * 3;
    Sk2f C0x1 = C0 * SkNx_shuffle<1,0>(C1);
    float Cdet = C0x1[0] - C0x1[1];

    SkSTArray<4, float> chops;
    if (SkCubicType::kLoop != fCurrCubicType) {
        find_chops_around_inflection_points(inflectPad, t, s, C0, C1, skipTerm, Cdet, &chops);
    } else {
        find_chops_around_loop_intersection(loopIntersectPad, t, s, C0, C1, skipTerm, Cdet, &chops);
    }
    if (4 == chops.count() && chops[1] >= chops[2]) {
        // This just the means the KLM roots are so close that their paddings overlap. We will
        // approximate the entire middle section, but still have it chopped midway. For loops this
        // chop guarantees the append code only sees convex segments. Otherwise, it means we are (at
        // least almost) a cusp and the chop makes sure we get a sharp point.
        Sk2f ts = t * SkNx_shuffle<1,0>(s);
        chops[1] = chops[2] = (ts[0] + ts[1]) / (2*s[0]*s[1]);
    }

#ifdef SK_DEBUG
    for (int i = 1; i < chops.count(); ++i) {
        SkASSERT(chops[i] >= chops[i - 1]);
    }
#endif
    this->appendCubics(AppendCubicMode::kLiteral, p0, p1, p2, p3, chops.begin(), chops.count());
}