/// Used inside SkCurveMeasure::getTime's Newton's iteration static inline SkPoint evaluate(const SkPoint pts[4], SkSegType segType, SkScalar t) { SkPoint pos; switch (segType) { case kQuad_SegType: pos = SkEvalQuadAt(pts, t); break; case kLine_SegType: pos = SkPoint::Make(SkScalarInterp(pts[0].x(), pts[1].x(), t), SkScalarInterp(pts[0].y(), pts[1].y(), t)); break; case kCubic_SegType: SkEvalCubicAt(pts, t, &pos, nullptr, nullptr); break; case kConic_SegType: { SkConic conic(pts, pts[3].x()); conic.evalAt(t, &pos); } break; default: UNIMPLEMENTED; } return pos; }
static void test_conic_tangents(skiatest::Reporter* reporter) { SkPoint pts[] = { { 10, 20}, {10, 20}, {20, 30}, { 10, 20}, {15, 25}, {20, 30}, { 10, 20}, {20, 30}, {20, 30} }; int count = (int) SK_ARRAY_COUNT(pts) / 3; for (int index = 0; index < count; ++index) { SkConic conic(&pts[index * 3], 0.707f); SkVector start = conic.evalTangentAt(0); SkVector mid = conic.evalTangentAt(.5f); SkVector end = conic.evalTangentAt(1); REPORTER_ASSERT(reporter, start.fX && start.fY); REPORTER_ASSERT(reporter, mid.fX && mid.fY); REPORTER_ASSERT(reporter, end.fX && end.fY); REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid))); REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end))); } }
static void test_cubic_tangents(skiatest::Reporter* reporter) { SkPoint pts[] = { { 10, 20}, {10, 20}, {20, 30}, {30, 40}, { 10, 20}, {15, 25}, {20, 30}, {30, 40}, { 10, 20}, {20, 30}, {30, 40}, {30, 40}, }; int count = (int) SK_ARRAY_COUNT(pts) / 4; for (int index = 0; index < count; ++index) { SkConic conic(&pts[index * 3], 0.707f); SkVector start, mid, end; SkEvalCubicAt(&pts[index * 4], 0, nullptr, &start, nullptr); SkEvalCubicAt(&pts[index * 4], .5f, nullptr, &mid, nullptr); SkEvalCubicAt(&pts[index * 4], 1, nullptr, &end, nullptr); REPORTER_ASSERT(reporter, start.fX && start.fY); REPORTER_ASSERT(reporter, mid.fX && mid.fY); REPORTER_ASSERT(reporter, end.fX && end.fY); REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid))); REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end))); } }
static void test_conic(skiatest::Reporter* reporter) { SkRandom rand; for (int i = 0; i < 1000; ++i) { SkPoint pts[3]; for (int j = 0; j < 3; ++j) { pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100); } for (int k = 0; k < 10; ++k) { SkScalar w = rand.nextUScalar1() * 2; SkConic conic(pts, w); const SkScalar dt = SK_Scalar1 / 128; SkScalar t = dt; for (int j = 1; j < 128; ++j) { test_conic_eval_pos(reporter, conic, t); test_conic_eval_tan(reporter, conic, t); t += dt; } } } }
/// Used inside SkCurveMeasure::getTime's Newton's iteration static inline SkVector evaluateDerivative(const SkPoint pts[4], SkSegType segType, SkScalar t) { SkVector tan; switch (segType) { case kQuad_SegType: tan = SkEvalQuadTangentAt(pts, t); break; case kLine_SegType: tan = pts[1] - pts[0]; break; case kCubic_SegType: SkEvalCubicAt(pts, t, nullptr, &tan, nullptr); break; case kConic_SegType: { SkConic conic(pts, pts[3].x()); conic.evalAt(t, nullptr, &tan); } break; default: UNIMPLEMENTED; } return tan; }
bool SkOpEdgeBuilder::walk() { uint8_t* verbPtr = fPathVerbs.begin(); uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf]; SkPoint* pointsPtr = fPathPts.begin() - 1; SkScalar* weightPtr = fWeights.begin(); SkPath::Verb verb; while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) { if (verbPtr == endOfFirstHalf) { fOperand = true; } verbPtr++; switch (verb) { case SkPath::kMove_Verb: if (fCurrentContour && fCurrentContour->count()) { if (fAllowOpenContours) { complete(); } else if (!close()) { return false; } } if (!fCurrentContour) { fCurrentContour = fContoursHead->appendContour(); } fCurrentContour->init(fGlobalState, fOperand, fXorMask[fOperand] == kEvenOdd_PathOpsMask); pointsPtr += 1; continue; case SkPath::kLine_Verb: fCurrentContour->addLine(pointsPtr); break; case SkPath::kQuad_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; if (v1.dot(v2) < 0) { SkPoint pair[5]; if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) { goto addOneQuad; } if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) { return false; } SkPoint cStorage[2][2]; SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? &pair[0] : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? &pair[2] : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fCurrentContour->addCurve(v1, curve1); fCurrentContour->addCurve(v2, curve2); break; } } } addOneQuad: fCurrentContour->addQuad(pointsPtr); break; case SkPath::kConic_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; SkScalar weight = *weightPtr++; if (v1.dot(v2) < 0) { // FIXME: max curvature for conics hasn't been implemented; use placeholder SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr); if (maxCurvature > 0) { SkConic conic(pointsPtr, weight); SkConic pair[2]; if (!conic.chopAt(maxCurvature, pair)) { // if result can't be computed, use original fCurrentContour->addConic(pointsPtr, weight); break; } SkPoint cStorage[2][3]; SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? pair[0].fPts : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? pair[1].fPts : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fCurrentContour->addCurve(v1, curve1, pair[0].fW); fCurrentContour->addCurve(v2, curve2, pair[1].fW); break; } } } fCurrentContour->addConic(pointsPtr, weight); } break; case SkPath::kCubic_Verb: { // Split complex cubics (such as self-intersecting curves or // ones with difficult curvature) in two before proceeding. // This can be required for intersection to succeed. SkScalar splitT; if (SkDCubic::ComplexBreak(pointsPtr, &splitT)) { SkPoint pair[7]; SkChopCubicAt(pointsPtr, pair, splitT); if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) { return false; } SkPoint cStorage[2][4]; SkPath::Verb v1 = SkReduceOrder::Cubic(&pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Cubic(&pair[3], cStorage[1]); SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &pair[0] : cStorage[0]; SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &pair[3] : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fCurrentContour->addCurve(v1, curve1); fCurrentContour->addCurve(v2, curve2); break; } } } fCurrentContour->addCubic(pointsPtr); break; case SkPath::kClose_Verb: SkASSERT(fCurrentContour); if (!close()) { return false; } continue; default: SkDEBUGFAIL("bad verb"); return false; } SkASSERT(fCurrentContour); fCurrentContour->debugValidate(); pointsPtr += SkPathOpsVerbToPoints(verb); } if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) { return false; } return true; }
Json::Value SkJSONCanvas::makePath(const SkPath& path) { Json::Value result(Json::objectValue); switch (path.getFillType()) { case SkPath::kWinding_FillType: result[SKJSONCANVAS_ATTRIBUTE_FILLTYPE] = SKJSONCANVAS_FILLTYPE_WINDING; break; case SkPath::kEvenOdd_FillType: result[SKJSONCANVAS_ATTRIBUTE_FILLTYPE] = SKJSONCANVAS_FILLTYPE_EVENODD; break; case SkPath::kInverseWinding_FillType: result[SKJSONCANVAS_ATTRIBUTE_FILLTYPE] = SKJSONCANVAS_FILLTYPE_INVERSEWINDING; break; case SkPath::kInverseEvenOdd_FillType: result[SKJSONCANVAS_ATTRIBUTE_FILLTYPE] = SKJSONCANVAS_FILLTYPE_INVERSEEVENODD; break; } Json::Value verbs(Json::arrayValue); SkPath::Iter iter(path, false); SkPoint pts[4]; SkPath::Verb verb; while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kLine_Verb: { Json::Value line(Json::objectValue); line[SKJSONCANVAS_VERB_LINE] = this->makePoint(pts[1]); verbs.append(line); break; } case SkPath::kQuad_Verb: { Json::Value quad(Json::objectValue); Json::Value coords(Json::arrayValue); coords.append(this->makePoint(pts[1])); coords.append(this->makePoint(pts[2])); quad[SKJSONCANVAS_VERB_QUAD] = coords; verbs.append(quad); break; } case SkPath::kCubic_Verb: { Json::Value cubic(Json::objectValue); Json::Value coords(Json::arrayValue); coords.append(this->makePoint(pts[1])); coords.append(this->makePoint(pts[2])); coords.append(this->makePoint(pts[3])); cubic[SKJSONCANVAS_VERB_CUBIC] = coords; verbs.append(cubic); break; } case SkPath::kConic_Verb: { Json::Value conic(Json::objectValue); Json::Value coords(Json::arrayValue); coords.append(this->makePoint(pts[1])); coords.append(this->makePoint(pts[2])); coords.append(Json::Value(iter.conicWeight())); conic[SKJSONCANVAS_VERB_CONIC] = coords; verbs.append(conic); break; } case SkPath::kMove_Verb: { Json::Value move(Json::objectValue); move[SKJSONCANVAS_VERB_MOVE] = this->makePoint(pts[0]); verbs.append(move); break; } case SkPath::kClose_Verb: verbs.append(Json::Value(SKJSONCANVAS_VERB_CLOSE)); break; case SkPath::kDone_Verb: break; } } result[SKJSONCANVAS_ATTRIBUTE_VERBS] = verbs; return result; }
int main(int argc, char* argv[]) { #if 1 QApplication a(argc, argv); MainWindow w; w.show(); // look for image file //std::string img_file("data/floor.jpg"); //QFile file; //for (int i = 0; i < 5; ++i) // if (!file.exists(img_file.c_str())) // img_file.insert(0, "../"); //w.getGLWidget()->setImage(img_file.c_str()); GLLines* gllines = &w.getGLWidget()->glLines(); //gllines->setVertexLine(0, 0, QVector3D(52.3467f, 125.102f, 1.0f)); //gllines->setVertexLine(0, 1, QVector3D(340.253f, 130.147f, 1.0f)); //gllines->setVertexLine(1, 0, QVector3D(193.28f, 126.111f, 1.0f)); //gllines->setVertexLine(1, 1, QVector3D(225.493f, 360.173f, 1.0f)); //gllines->setVertexLine(2, 0, QVector3D(42.28f, 263.32f, 1.0f)); //gllines->setVertexLine(2, 1, QVector3D(296.967f, 397.502f, 1.0f)); //gllines->setVertexLine(3, 0, QVector3D(212.407f, 269.373f, 1.0f)); //gllines->setVertexLine(3, 1, QVector3D(34.2267f, 391.449f, 1.0f)); //gllines->setVertexLine(4, 0, QVector3D(294.953f, 318.809f, 1.0f)); //gllines->setVertexLine(4, 1, QVector3D(456.02f, 322.844f, 1.0f)); //gllines->setVertexLine(5, 0, QVector3D(492.26f, 208.84f, 1.0f)); //gllines->setVertexLine(5, 1, QVector3D(429.847f, 400.529f, 1.0f)); //gllines->setVertexLine(6, 0, QVector3D(299.987f, 31.2756f, 1.0f)); //gllines->setVertexLine(6, 1, QVector3D(555.68f, 273.409f, 1.0f)); //gllines->setVertexLine(7, 0, QVector3D(545.613f, 39.3467f, 1.0f)); //gllines->setVertexLine(7, 1, QVector3D(236.567f, 250.204f, 1.0f)); //gllines->setVertexLine(8, 0, QVector3D(95.6333f, 264.329f, 1.0f)); //gllines->setVertexLine(8, 1, QVector3D(501.32f, 273.409f, 1.0f)); //gllines->setVertexLine(9, 0, QVector3D(302.00f, 29.2578f, 1.0f)); //gllines->setVertexLine(9, 1, QVector3D(297.973f, 398.511f, 1.0f)); gllines->computeCanonicalVertices(w.getGLWidget()->width(), w.getGLWidget()->height()); gllines->onEnable(true); return a.exec(); #else std::vector<Eigen::Vector3f> vertices; vertices.push_back(Eigen::Vector3f(52.3467f, 125.102f, 1.0f)); vertices.push_back(Eigen::Vector3f(340.253f, 130.147f, 1.0f)); vertices.push_back(Eigen::Vector3f(193.28f, 126.111f, 1.0f)); vertices.push_back(Eigen::Vector3f(225.493f, 360.173f, 1.0f)); vertices.push_back(Eigen::Vector3f(42.28f, 263.32f, 1.0f)); vertices.push_back(Eigen::Vector3f(296.967f, 397.502f, 1.0f)); vertices.push_back(Eigen::Vector3f(212.407f, 269.373f, 1.0f)); vertices.push_back(Eigen::Vector3f(34.2267f, 391.449f, 1.0f)); vertices.push_back(Eigen::Vector3f(294.953f, 318.809f, 1.0f)); vertices.push_back(Eigen::Vector3f(456.02f, 322.844f, 1.0f)); vertices.push_back(Eigen::Vector3f(492.26f, 208.84f, 1.0f)); vertices.push_back(Eigen::Vector3f(429.847f, 400.529f, 1.0f)); vertices.push_back(Eigen::Vector3f(299.987f, 31.2756f, 1.0f)); vertices.push_back(Eigen::Vector3f(555.68f, 273.409f, 1.0f)); vertices.push_back(Eigen::Vector3f(545.613f, 39.3467f, 1.0f)); vertices.push_back(Eigen::Vector3f(236.567f, 250.204f, 1.0f)); vertices.push_back(Eigen::Vector3f(95.6333f, 264.329f, 1.0f)); vertices.push_back(Eigen::Vector3f(501.32f, 273.409f, 1.0f)); vertices.push_back(Eigen::Vector3f(302.00f, 29.2578f, 1.0f)); vertices.push_back(Eigen::Vector3f(297.973f, 398.511f, 1.0f)); std::cout << vertices.size() << std::endl; std::vector<Eigen::Vector3f> lines; for (int i = 0; i < vertices.size() - 1; i += 2) lines.push_back(lineNormalized(vertices[i], vertices[i + 1])); for (int i = 0; i < lines.size(); ++i) std::cout << "l" << i << " : " << lines[i].x() << ", " << lines[i].y() << ", " << lines[i].z() << std::endl; std::cout << std::endl << std::endl; //lines[0] = Eigen::Vector3f(-0.000141084, 0.00805224, -0.999968); //lines[1] = Eigen::Vector3f(-0.00568419, 0.000782299, 0.999984); //lines[2] = Eigen::Vector3f(-0.00218568, 0.00414856, -0.999989); //lines[3] = Eigen::Vector3f(-0.0016513, -0.00241022, 0.999996); //lines[4] = Eigen::Vector3f(-8.04546e-05, 0.00321109, -0.999995); //lines[5] = Eigen::Vector3f(-0.00178489, -0.000581155, 0.999998); //lines[6] = Eigen::Vector3f(-0.00374583, 0.0039556, 0.999985); //lines[7] = Eigen::Vector3f(-0.00165759, -0.00242947, 0.999996); //lines[8] = Eigen::Vector3f(-8.53647e-05, 0.00381402, -0.999993); //lines[9] = Eigen::Vector3f(-0.00330775, -3.60706e-05, 0.999995); Eigen::MatrixXf A(5, 5); Eigen::Vector3f l, m; Eigen::VectorXf b(5); for (int i = 0; i < 5; ++i) { l = lines[i * 2 + 0]; m = lines[i * 2 + 1]; A(i, 0) = l[0] * m[0]; A(i, 1) = (l[0] * m[1] + l[1] * m[0]) / 2.0f; A(i, 2) = l[1] * m[1]; A(i, 3) = (l[0] * m[2] + l[2] * m[0]) / 2.0f; A(i, 4) = (l[1] * m[2] + l[2] * m[1]) / 2.0f; A(i, 5) = l[2] * m[2]; b[i] = -l[2] * m[2]; } std::cout << "A: \n" << A << std::endl << std::endl; std::cout << "b: \n" << b << std::endl << std::endl; Eigen::MatrixXf x = A.colPivHouseholderQr().solve(b); std::cout << std::fixed << "x: \n" << x << std::endl << std::endl; Eigen::MatrixXf conic(3, 3); conic(0, 0) = x(0); conic(0, 1) = x(1) / 2.0f; conic(0, 2) = x(3) / 2.0f; conic(1, 0) = x(1) / 2.0f; conic(1, 1) = x(2); conic(1, 2) = x(4) / 2.0f; conic(2, 0) = x(3) / 2.0f; conic(2, 1) = x(4) / 2.0f; conic(2, 2) = 1.0f; std::cout << "Conic : " << std::endl << conic << std::endl << std::endl; Eigen::JacobiSVD<Eigen::MatrixXf> svd(conic, Eigen::ComputeFullU); Eigen::MatrixXf H = svd.matrixU(); std::cout << "H matrix: " << std::endl << H << std::endl << std::endl << "Singular values: " << svd.singularValues() << std::endl << std::endl; std::cout << "Rectification transformation: " << std::endl << H.inverse() << std::endl << std::endl; QImage input("floor-persp.jpg"); Eigen::Vector3f img(input.width(), input.height(), 1.0f); float xmin = 0; float xmax = 0; float ymin = 0; float ymax = 0; computImageSize(H.inverse(), 0, 0, input.width(), input.height(), xmin, xmax, ymin, ymax); float aspect = (xmax - xmin) / (ymax - ymin); QImage output(input.width(), input.width() / aspect, input.format()); output.fill(qRgb(0, 0, 0)); std::cout << "Output size: " << output.width() << ", " << output.height() << std::endl; float dx = (xmax - xmin) / float(output.width()); float dy = (ymax - ymin) / float(output.height()); std::cout << std::fixed << "dx, dy: " << dx << ", " << dy << std::endl; for (int x = 0; x < output.width(); ++x) { for (int y = 0; y < output.height(); ++y) { Eigen::Vector3f px(x, y, 1); float tx = 0.0f; float ty = 0.0f; Eigen::Vector2f t = multiplyPointMatrix(H, xmin + x * dx, ymin + y * dy); if (t.x() > -1 && t.y() > -1 && t.x() < input.width() && t.y() < input.height()) { //if (interpolate) //{ // QRgb rgb = bilinearInterpol(input, t.x(), t.y(), dx / 2.0, dy / 2.0); // output.setPixel(x, y, rgb); //} //else { output.setPixel(x, y, input.pixel(t.x(), t.y())); } } } } output.save("output_5_floor.jpg"); return EXIT_SUCCESS; #endif }
Function qpsol_nlp(const std::string& name, const std::string& solver, const std::map<std::string, M>& qp, const Dict& opts) { // We have: minimize f(x) = 1/2 * x' H x + c'x // subject to lbx <= x <= ubx // lbg <= g(x) = A x + b <= ubg M x, p, f, g; for (auto&& i : qp) { if (i.first=="x") { x = i.second; } else if (i.first=="p") { p = i.second; } else if (i.first=="f") { f = i.second; } else if (i.first=="g") { g = i.second; } else { casadi_error("No such field: " + i.first); } } if (g.is_empty(true)) g = M(0, 1); // workaround // Gradient of the objective: gf == Hx + g M gf = M::gradient(f, x); // Identify the linear term in the objective M c = substitute(gf, x, M::zeros(x.sparsity())); // Identify the quadratic term in the objective M H = M::jacobian(gf, x, true); // Identify the constant term in the constraints M b = substitute(g, x, M::zeros(x.sparsity())); // Identify the linear term in the constraints M A = M::jacobian(g, x); // Create a function for calculating the required matrices vectors Function prob(name + "_qp", {x, p}, {H, c, A, b}); // Create the QP solver Function conic_f = conic(name + "_qpsol", solver, {{"h", H.sparsity()}, {"a", A.sparsity()}}, opts); // Create an MXFunction with the right signature vector<MX> ret_in(NLPSOL_NUM_IN); ret_in[NLPSOL_X0] = MX::sym("x0", x.sparsity()); ret_in[NLPSOL_P] = MX::sym("p", p.sparsity()); ret_in[NLPSOL_LBX] = MX::sym("lbx", x.sparsity()); ret_in[NLPSOL_UBX] = MX::sym("ubx", x.sparsity()); ret_in[NLPSOL_LBG] = MX::sym("lbg", g.sparsity()); ret_in[NLPSOL_UBG] = MX::sym("ubg", g.sparsity()); ret_in[NLPSOL_LAM_X0] = MX::sym("lam_x0", x.sparsity()); ret_in[NLPSOL_LAM_G0] = MX::sym("lam_g0", g.sparsity()); vector<MX> ret_out(NLPSOL_NUM_OUT); // Get expressions for the QP matrices and vectors vector<MX> v(NL_NUM_IN); v[NL_X] = ret_in[NLPSOL_X0]; v[NL_P] = ret_in[NLPSOL_P]; v = prob(v); // Call the QP solver vector<MX> w(CONIC_NUM_IN); w[CONIC_H] = v.at(0); w[CONIC_G] = v.at(1); w[CONIC_A] = v.at(2); w[CONIC_LBX] = ret_in[NLPSOL_LBX]; w[CONIC_UBX] = ret_in[NLPSOL_UBX]; w[CONIC_LBA] = ret_in[NLPSOL_LBG] - v.at(3); w[CONIC_UBA] = ret_in[NLPSOL_UBG] - v.at(3); w[CONIC_X0] = ret_in[NLPSOL_X0]; w[CONIC_LAM_X0] = ret_in[NLPSOL_LAM_X0]; w[CONIC_LAM_A0] = ret_in[NLPSOL_LAM_G0]; w = conic_f(w); // Get expressions for the solution ret_out[NLPSOL_X] = w[CONIC_X]; ret_out[NLPSOL_F] = w[CONIC_COST]; ret_out[NLPSOL_G] = mtimes(v.at(2), w[CONIC_X]) + v.at(3); ret_out[NLPSOL_LAM_X] = w[CONIC_LAM_X]; ret_out[NLPSOL_LAM_G] = w[CONIC_LAM_A]; ret_out[NLPSOL_LAM_P] = MX::nan(p.sparsity()); return Function(name, ret_in, ret_out, nlpsol_in(), nlpsol_out(), {{"default_in", nlpsol_default_in()}}); }
bool SkOpEdgeBuilder::walk() { uint8_t* verbPtr = fPathVerbs.begin(); uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf]; SkPoint* pointsPtr = fPathPts.begin() - 1; SkScalar* weightPtr = fWeights.begin(); SkPath::Verb verb; SkOpContour* contour = fContourBuilder.contour(); while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) { if (verbPtr == endOfFirstHalf) { fOperand = true; } verbPtr++; switch (verb) { case SkPath::kMove_Verb: if (contour && contour->count()) { if (fAllowOpenContours) { complete(); } else if (!close()) { return false; } } if (!contour) { fContourBuilder.setContour(contour = fContoursHead->appendContour()); } contour->init(fGlobalState, fOperand, fXorMask[fOperand] == kEvenOdd_PathOpsMask); pointsPtr += 1; continue; case SkPath::kLine_Verb: fContourBuilder.addLine(pointsPtr); break; case SkPath::kQuad_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; if (v1.dot(v2) < 0) { SkPoint pair[5]; if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) { goto addOneQuad; } if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) { return false; } for (unsigned index = 0; index < SK_ARRAY_COUNT(pair); ++index) { force_small_to_zero(&pair[index]); } SkPoint cStorage[2][2]; SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? &pair[0] : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? &pair[2] : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fContourBuilder.addCurve(v1, curve1); fContourBuilder.addCurve(v2, curve2); break; } } } addOneQuad: fContourBuilder.addQuad(pointsPtr); break; case SkPath::kConic_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; SkScalar weight = *weightPtr++; if (v1.dot(v2) < 0) { // FIXME: max curvature for conics hasn't been implemented; use placeholder SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr); if (maxCurvature > 0) { SkConic conic(pointsPtr, weight); SkConic pair[2]; if (!conic.chopAt(maxCurvature, pair)) { // if result can't be computed, use original fContourBuilder.addConic(pointsPtr, weight); break; } SkPoint cStorage[2][3]; SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? pair[0].fPts : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? pair[1].fPts : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fContourBuilder.addCurve(v1, curve1, pair[0].fW); fContourBuilder.addCurve(v2, curve2, pair[1].fW); break; } } } fContourBuilder.addConic(pointsPtr, weight); } break; case SkPath::kCubic_Verb: { // Split complex cubics (such as self-intersecting curves or // ones with difficult curvature) in two before proceeding. // This can be required for intersection to succeed. SkScalar splitT[3]; int breaks = SkDCubic::ComplexBreak(pointsPtr, splitT); if (!breaks) { fContourBuilder.addCubic(pointsPtr); break; } SkASSERT(breaks <= (int) SK_ARRAY_COUNT(splitT)); struct Splitsville { double fT[2]; SkPoint fPts[4]; SkPoint fReduced[4]; SkPath::Verb fVerb; bool fCanAdd; } splits[4]; SkASSERT(SK_ARRAY_COUNT(splits) == SK_ARRAY_COUNT(splitT) + 1); SkTQSort(splitT, &splitT[breaks - 1]); for (int index = 0; index <= breaks; ++index) { Splitsville* split = &splits[index]; split->fT[0] = index ? splitT[index - 1] : 0; split->fT[1] = index < breaks ? splitT[index] : 1; SkDCubic part = SkDCubic::SubDivide(pointsPtr, split->fT[0], split->fT[1]); if (!part.toFloatPoints(split->fPts)) { return false; } split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced); SkPoint* curve = SkPath::kCubic_Verb == verb ? split->fPts : split->fReduced; split->fCanAdd = can_add_curve(split->fVerb, curve); } for (int index = 0; index <= breaks; ++index) { Splitsville* split = &splits[index]; if (!split->fCanAdd) { continue; } int prior = index; while (prior > 0 && !splits[prior - 1].fCanAdd) { --prior; } if (prior < index) { split->fT[0] = splits[prior].fT[0]; split->fPts[0] = splits[prior].fPts[0]; } int next = index; int breakLimit = SkTMin(breaks, (int) SK_ARRAY_COUNT(splits) - 1); while (next < breakLimit && !splits[next + 1].fCanAdd) { ++next; } if (next > index) { split->fT[1] = splits[next].fT[1]; split->fPts[3] = splits[next].fPts[3]; } if (prior < index || next > index) { split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced); } SkPoint* curve = SkPath::kCubic_Verb == split->fVerb ? split->fPts : split->fReduced; if (!can_add_curve(split->fVerb, curve)) { return false; } fContourBuilder.addCurve(split->fVerb, curve); } } break; case SkPath::kClose_Verb: SkASSERT(contour); if (!close()) { return false; } contour = nullptr; continue; default: SkDEBUGFAIL("bad verb"); return false; } SkASSERT(contour); if (contour->count()) { contour->debugValidate(); } pointsPtr += SkPathOpsVerbToPoints(verb); } fContourBuilder.flush(); if (contour && contour->count() &&!fAllowOpenContours && !close()) { return false; } return true; }