static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) { SkASSERT(ValidLine(line1)); SkASSERT(ValidLine(line2)); SkIntersections ts2; int pts2 = ts2.intersect(line1, line2); REPORTER_ASSERT(reporter, pts2 == 2); REPORTER_ASSERT(reporter, pts2 == ts2.used()); check_results(reporter, line1, line2, ts2); #if 0 SkIntersections ts; int pts = ts.intersect(line1, line2); REPORTER_ASSERT(reporter, pts == pts2); REPORTER_ASSERT(reporter, pts == 2); REPORTER_ASSERT(reporter, pts == ts.used()); check_results(reporter, line1, line2, ts); #endif }
static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) { SkASSERT(ValidLine(line1)); SkASSERT(ValidLine(line2)); SkIntersections i; int pts = i.intersect(line1, line2); REPORTER_ASSERT(reporter, pts); REPORTER_ASSERT(reporter, pts == i.used()); check_results(reporter, line1, line2, i); if (line1[0] == line1[1] || line2[0] == line2[1]) { return; } if (line1[0].fY == line1[1].fY) { double left = SkTMin(line1[0].fX, line1[1].fX); double right = SkTMax(line1[0].fX, line1[1].fX); SkIntersections ts; ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left); check_results(reporter, line2, line1, ts); } if (line2[0].fY == line2[1].fY) { double left = SkTMin(line2[0].fX, line2[1].fX); double right = SkTMax(line2[0].fX, line2[1].fX); SkIntersections ts; ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left); check_results(reporter, line1, line2, ts); } if (line1[0].fX == line1[1].fX) { double top = SkTMin(line1[0].fY, line1[1].fY); double bottom = SkTMax(line1[0].fY, line1[1].fY); SkIntersections ts; ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top); check_results(reporter, line2, line1, ts); } if (line2[0].fX == line2[1].fX) { double top = SkTMin(line2[0].fY, line2[1].fY); double bottom = SkTMax(line2[0].fY, line2[1].fY); SkIntersections ts; ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top); check_results(reporter, line1, line2, ts); } }
DEF_TEST(PathOpsConicLineIntersection, reporter) { for (size_t index = 0; index < lineConicTests_count; ++index) { int iIndex = static_cast<int>(index); const SkDConic& conic = lineConicTests[index].conic; SkASSERT(ValidConic(conic)); const SkDLine& line = lineConicTests[index].line; SkASSERT(ValidLine(line)); SkReduceOrder reducer; SkPoint pts[3] = { conic.fPts.fPts[0].asSkPoint(), conic.fPts.fPts[1].asSkPoint(), conic.fPts.fPts[2].asSkPoint() }; SkPoint reduced[3]; SkPath::Verb order1 = SkReduceOrder::Conic(pts, conic.fWeight, reduced); if (order1 != SkPath::kConic_Verb) { SkDebugf("%s [%d] conic verb=%d\n", __FUNCTION__, iIndex, order1); REPORTER_ASSERT(reporter, 0); } int order2 = reducer.reduce(line); if (order2 < 2) { SkDebugf("%s [%d] line order=%d\n", __FUNCTION__, iIndex, order2); REPORTER_ASSERT(reporter, 0); } SkIntersections intersections; bool flipped = false; int result = doIntersect(intersections, conic, line, flipped); REPORTER_ASSERT(reporter, result == lineConicTests[index].result); if (intersections.used() <= 0) { continue; } for (int pt = 0; pt < result; ++pt) { double tt1 = intersections[0][pt]; REPORTER_ASSERT(reporter, tt1 >= 0 && tt1 <= 1); SkDPoint t1 = conic.ptAtT(tt1); double tt2 = intersections[1][pt]; REPORTER_ASSERT(reporter, tt2 >= 0 && tt2 <= 1); SkDPoint t2 = line.ptAtT(tt2); if (!t1.approximatelyEqual(t2)) { SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__, iIndex, pt, tt1, t1.fX, t1.fY, tt2, t2.fX, t2.fY); REPORTER_ASSERT(reporter, 0); } if (!t1.approximatelyEqual(lineConicTests[index].expected[0]) && (lineConicTests[index].result == 1 || !t1.approximatelyEqual(lineConicTests[index].expected[1]))) { SkDebugf("%s t1=(%1.9g,%1.9g)\n", __FUNCTION__, t1.fX, t1.fY); REPORTER_ASSERT(reporter, 0); } } } }
static void testOneOffs(skiatest::Reporter* reporter) { bool flipped = false; for (size_t index = 0; index < oneOffs_count; ++index) { const SkDConic& conic = oneOffs[index].conic; SkASSERT(ValidConic(conic)); const SkDLine& line = oneOffs[index].line; SkASSERT(ValidLine(line)); SkIntersections intersections; int result = doIntersect(intersections, conic, line, flipped); for (int inner = 0; inner < result; ++inner) { double conicT = intersections[0][inner]; SkDPoint conicXY = conic.ptAtT(conicT); double lineT = intersections[1][inner]; SkDPoint lineXY = line.ptAtT(lineT); if (!conicXY.approximatelyEqual(lineXY)) { conicXY.approximatelyEqual(lineXY); SkDebugf(""); } REPORTER_ASSERT(reporter, conicXY.approximatelyEqual(lineXY)); } } }
static void PathOpsDRectTest(skiatest::Reporter* reporter) { size_t index; SkDRect rect, rect2; for (index = 0; index < lineTests_count; ++index) { const SkDLine& line = lineTests[index]; SkASSERT(ValidLine(line)); rect.setBounds(line); REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(line[0].fX, line[1].fX)); REPORTER_ASSERT(reporter, rect.fTop == SkTMin(line[0].fY, line[1].fY)); REPORTER_ASSERT(reporter, rect.fRight == SkTMax(line[0].fX, line[1].fX)); REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(line[0].fY, line[1].fY)); rect2.set(line[0]); rect2.add(line[1]); REPORTER_ASSERT(reporter, rect2.fLeft == SkTMin(line[0].fX, line[1].fX)); REPORTER_ASSERT(reporter, rect2.fTop == SkTMin(line[0].fY, line[1].fY)); REPORTER_ASSERT(reporter, rect2.fRight == SkTMax(line[0].fX, line[1].fX)); REPORTER_ASSERT(reporter, rect2.fBottom == SkTMax(line[0].fY, line[1].fY)); REPORTER_ASSERT(reporter, rect.contains(line[0])); REPORTER_ASSERT(reporter, rect.intersects(&rect2)); } for (index = 0; index < quadTests_count; ++index) { const SkDQuad& quad = quadTests[index]; SkASSERT(ValidQuad(quad)); rect.setRawBounds(quad); REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(quad[0].fX, SkTMin(quad[1].fX, quad[2].fX))); REPORTER_ASSERT(reporter, rect.fTop == SkTMin(quad[0].fY, SkTMin(quad[1].fY, quad[2].fY))); REPORTER_ASSERT(reporter, rect.fRight == SkTMax(quad[0].fX, SkTMax(quad[1].fX, quad[2].fX))); REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(quad[0].fY, SkTMax(quad[1].fY, quad[2].fY))); rect2.setBounds(quad); REPORTER_ASSERT(reporter, rect.intersects(&rect2)); // FIXME: add a recursive box subdivision method to verify that tight bounds is correct SkDPoint leftTop = {rect2.fLeft, rect2.fTop}; REPORTER_ASSERT(reporter, rect.contains(leftTop)); SkDPoint rightBottom = {rect2.fRight, rect2.fBottom}; REPORTER_ASSERT(reporter, rect.contains(rightBottom)); } for (index = 0; index < cubicTests_count; ++index) { const SkDCubic& cubic = cubicTests[index]; SkASSERT(ValidCubic(cubic)); rect.setRawBounds(cubic); REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(cubic[0].fX, SkTMin(cubic[1].fX, SkTMin(cubic[2].fX, cubic[3].fX)))); REPORTER_ASSERT(reporter, rect.fTop == SkTMin(cubic[0].fY, SkTMin(cubic[1].fY, SkTMin(cubic[2].fY, cubic[3].fY)))); REPORTER_ASSERT(reporter, rect.fRight == SkTMax(cubic[0].fX, SkTMax(cubic[1].fX, SkTMax(cubic[2].fX, cubic[3].fX)))); REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(cubic[0].fY, SkTMax(cubic[1].fY, SkTMax(cubic[2].fY, cubic[3].fY)))); rect2.setBounds(cubic); REPORTER_ASSERT(reporter, rect.intersects(&rect2)); // FIXME: add a recursive box subdivision method to verify that tight bounds is correct SkDPoint leftTop = {rect2.fLeft, rect2.fTop}; REPORTER_ASSERT(reporter, rect.contains(leftTop)); SkDPoint rightBottom = {rect2.fRight, rect2.fBottom}; REPORTER_ASSERT(reporter, rect.contains(rightBottom)); } }
// Creates new set of lines from the computed columns bool CubeLineSegmenter::AddLines(Pixa *lines) { // create an array that will hold the bounding boxes // of the concomps belonging to each line Boxaa *lines_con_comps = boxaaCreate(lines->n); if (lines_con_comps == NULL) { return false; } for (int line = 0; line < lines->n; line++) { // if the line is not valid if (ValidLine(lines->pix[line], lines->boxa->box[line]) == false) { // split it Pixa *split_lines = SplitLine(lines->pix[line], lines->boxa->box[line]); // remove the old line if (pixaRemovePix(lines, line) != 0) { return false; } line--; if (split_lines == NULL) { continue; } // add the split lines instead and move the pointer for (int s_line = 0; s_line < split_lines->n; s_line++) { Pix *sp_line = pixaGetPix(split_lines, s_line, L_CLONE); Box *sp_box = boxaGetBox(split_lines->boxa, s_line, L_CLONE); if (sp_line == NULL || sp_box == NULL) { return false; } // insert the new line if (pixaInsertPix(lines, ++line, sp_line, sp_box) != 0) { return false; } } // remove the split lines pixaDestroy(&split_lines); } } // compute the concomps bboxes of each line for (int line = 0; line < lines->n; line++) { Boxa *line_con_comps = ComputeLineConComps(lines->pix[line], lines->boxa->box[line], NULL); if (line_con_comps == NULL) { return false; } // insert it into the boxaa array if (boxaaAddBoxa(lines_con_comps, line_con_comps, L_INSERT) != 0) { return false; } } // post process the lines: // merge the contents of "small" lines info legitimate lines for (int line = 0; line < lines->n; line++) { // a small line detected if (SmallLine(lines->boxa->box[line]) == true) { // merge its components to one of the valid lines if (MergeLine(lines->pix[line], lines->boxa->box[line], lines, lines_con_comps) == true) { // remove the small line if (pixaRemovePix(lines, line) != 0) { return false; } if (boxaaRemoveBoxa(lines_con_comps, line) != 0) { return false; } line--; } } } boxaaDestroy(&lines_con_comps); // add the pix masks if (pixaaAddPixa(columns_, lines, L_INSERT) != 0) { return false; } return true; }
// split a line continously until valid or fail Pixa *CubeLineSegmenter::SplitLine(Pix *line_mask_pix, Box *line_box) { // clone the line mask Pix *line_pix = pixClone(line_mask_pix); if (line_pix == NULL) { return NULL; } // AND with the image to get the actual line pixRasterop(line_pix, 0, 0, line_pix->w, line_pix->h, PIX_SRC & PIX_DST, img_, line_box->x, line_box->y); // continue to do rasterop morphology on the line until // it splits to valid lines or we fail int morph_hgt = kLineSepMorphMinHgt - 1, best_threshold = kLineSepMorphMinHgt - 1, max_valid_portion = 0; Boxa *boxa; Pixa *pixac; do { pixac = VerticalClosing(line_pix, morph_hgt, &boxa); // add the box offset to all the lines // and check for the validity of each int line, valid_line_cnt = 0, valid_portion = 0; for (line = 0; line < pixac->n; line++) { boxa->box[line]->x += line_box->x; boxa->box[line]->y += line_box->y; if (ValidLine(pixac->pix[line], boxa->box[line]) == true) { // count valid lines valid_line_cnt++; // and the valid portions valid_portion += boxa->box[line]->h; } } // all the lines are valid if (valid_line_cnt == pixac->n) { boxaDestroy(&boxa); pixDestroy(&line_pix); return pixac; } // a larger valid portion if (valid_portion > max_valid_portion) { max_valid_portion = valid_portion; best_threshold = morph_hgt; } boxaDestroy(&boxa); pixaDestroy(&pixac); morph_hgt--; } while (morph_hgt > 0); // failed to break into valid lines // attempt to crack the line pixac = CrackLine(line_pix, line_box); if (pixac != NULL) { pixDestroy(&line_pix); return pixac; } // try to leverage any of the lines // did the best threshold yield a non zero valid portion if (max_valid_portion > 0) { // use this threshold to break lines pixac = VerticalClosing(line_pix, best_threshold, &boxa); // add the box offset to all the lines // and check for the validity of each for (int line = 0; line < pixac->n; line++) { boxa->box[line]->x += line_box->x; boxa->box[line]->y += line_box->y; // remove invalid lines from the pixa if (ValidLine(pixac->pix[line], boxa->box[line]) == false) { pixaRemovePix(pixac, line); line--; } } boxaDestroy(&boxa); pixDestroy(&line_pix); return pixac; } // last resort: attempt to crack the line pixDestroy(&line_pix); return NULL; }
// do a desperate attempt at cracking lines Pixa *CubeLineSegmenter::CrackLine(Pix *cracked_line_pix, Box *cracked_line_box, int line_cnt) { // create lines pixa array Pixa **lines_pixa = new Pixa*[line_cnt]; if (lines_pixa == NULL) { return NULL; } memset(lines_pixa, 0, line_cnt * sizeof(*lines_pixa)); // compute line conn comps Pixa *line_con_comps_pix; Boxa *line_con_comps = ComputeLineConComps(cracked_line_pix, cracked_line_box, &line_con_comps_pix); if (line_con_comps == NULL) { delete []lines_pixa; return NULL; } // assign each conn comp to the a line based on its centroid for (int con = 0; con < line_con_comps->n; con++) { Box *con_box = line_con_comps->box[con]; Pix *con_pix = line_con_comps_pix->pix[con]; int mid_y = (con_box->y - cracked_line_box->y) + (con_box->h / 2), line_idx = MIN(line_cnt - 1, (mid_y * line_cnt / cracked_line_box->h)); // create the line if it has not been created? if (lines_pixa[line_idx] == NULL) { lines_pixa[line_idx] = pixaCreate(line_con_comps->n); if (lines_pixa[line_idx] == NULL) { delete []lines_pixa; boxaDestroy(&line_con_comps); pixaDestroy(&line_con_comps_pix); return NULL; } } // add the concomp to the line if (pixaAddPix(lines_pixa[line_idx], con_pix, L_CLONE) != 0 || pixaAddBox(lines_pixa[line_idx], con_box, L_CLONE)) { delete []lines_pixa; boxaDestroy(&line_con_comps); pixaDestroy(&line_con_comps_pix); } } // create the lines pixa Pixa *lines = pixaCreate(line_cnt); bool success = true; // create and check the validity of the lines for (int line = 0; line < line_cnt; line++) { Pixa *line_pixa = lines_pixa[line]; // skip invalid lines if (line_pixa == NULL) { continue; } // merge the pix, check the validity of the line // and add it to the lines pixa Box *line_box; Pix *line_pix = Pixa2Pix(line_pixa, &line_box); if (line_pix == NULL || line_box == NULL || ValidLine(line_pix, line_box) == false || pixaAddPix(lines, line_pix, L_INSERT) != 0 || pixaAddBox(lines, line_box, L_INSERT) != 0) { if (line_pix != NULL) { pixDestroy(&line_pix); } if (line_box != NULL) { boxDestroy(&line_box); } success = false; break; } } // cleanup for (int line = 0; line < line_cnt; line++) { if (lines_pixa[line] != NULL) { pixaDestroy(&lines_pixa[line]); } } delete []lines_pixa; boxaDestroy(&line_con_comps); pixaDestroy(&line_con_comps_pix); if (success == false) { pixaDestroy(&lines); lines = NULL; } return lines; }
static void setup(const SortSet* set, const size_t idx, SkOpSegment* seg, int* ts, const SkPoint& startPt) { SkPoint start, end; const SkPoint* data = set[idx].ptData; bool useIntersectPt = startPt.fX != 0 || startPt.fY != 0; if (useIntersectPt) { start = startPt; end = set[idx].endPt; } switch(set[idx].ptCount) { case 2: { SkASSERT(ValidPoints(data, 2)); seg->addLine(data, false, false); SkDLine dLine; dLine.set(set[idx].ptData); SkASSERT(ValidLine(dLine)); if (useIntersectPt) { break; } start = dLine.ptAtT(set[idx].tStart).asSkPoint(); end = dLine.ptAtT(set[idx].tEnd).asSkPoint(); } break; case 3: { SkASSERT(ValidPoints(data, 3)); seg->addQuad(data, false, false); SkDQuad dQuad; dQuad.set(set[idx].ptData); SkASSERT(ValidQuad(dQuad)); if (useIntersectPt) { break; } start = dQuad.ptAtT(set[idx].tStart).asSkPoint(); end = dQuad.ptAtT(set[idx].tEnd).asSkPoint(); } break; case 4: { SkASSERT(ValidPoints(data, 4)); seg->addCubic(data, false, false); SkDCubic dCubic; dCubic.set(set[idx].ptData); SkASSERT(ValidCubic(dCubic)); if (useIntersectPt) { break; } start = dCubic.ptAtT(set[idx].tStart).asSkPoint(); end = dCubic.ptAtT(set[idx].tEnd).asSkPoint(); } break; } double tStart = set[idx].tStart; double tEnd = set[idx].tEnd; seg->addT(NULL, start, tStart); seg->addT(NULL, end, tEnd); if (tStart != 0 && tEnd != 0) { seg->addT(NULL, set[idx].ptData[0], 0); } if (tStart != 1 && tEnd != 1) { seg->addT(NULL, set[idx].ptData[set[idx].ptCount - 1], 1); } int tIndex = 0; ts[0] = 0; ts[1] = 1; do { if (seg->t(tIndex) == set[idx].tStart) { ts[0] = tIndex; } if (seg->t(tIndex) == set[idx].tEnd) { ts[1] = tIndex; } if (seg->t(tIndex) >= 1) { break; } } while (++tIndex); }