Skip to content

Commit

Permalink
libtxt: implementation of GetRectsForRange that processes a line at a…
Browse files Browse the repository at this point in the history
… time (flutter#4155)
  • Loading branch information
jason-simmons authored Oct 2, 2017
1 parent e1aa867 commit e3404b8
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 93 deletions.
83 changes: 48 additions & 35 deletions third_party/txt/src/txt/paragraph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -773,49 +773,62 @@ void Paragraph::PaintDecorations(SkCanvas* canvas,
std::vector<SkRect> Paragraph::GetRectsForRange(size_t start,
size_t end) const {
std::vector<SkRect> rects;
end = fmax(start, end);
start = fmin(start, end);
FXL_DCHECK(end >= start && end >= 0 && start >= 0);
if (end == start)
end = start + 1;
end = fmin(end, text_.size());
while (start < end) {
SkIPoint word_bounds = GetWordBoundary(start);
word_bounds.fX = fmax(start, word_bounds.fX);
word_bounds.fY = fmin(end, word_bounds.fY);
start = fmax(word_bounds.fY, start + 1);
SkRect left_limits = GetCoordinatesForGlyphPosition(word_bounds.fX);
SkRect right_limits = GetCoordinatesForGlyphPosition(word_bounds.fY - 1);
if (left_limits.top() < right_limits.top()) {
rects.push_back(SkRect::MakeLTRB(
0, right_limits.top(), right_limits.right(), right_limits.bottom()));
} else {
rects.push_back(SkRect::MakeLTRB(left_limits.left(), left_limits.top(),
right_limits.right(),
right_limits.bottom()));
}
}
return rects;
}

SkRect Paragraph::GetCoordinatesForGlyphPosition(size_t pos) const {
size_t remainder = fmin(pos, text_.size() - 1);
if (end <= start || start == end)
return rects;

size_t pos = 0;
size_t line;
for (line = 0; line < glyph_position_x_.size(); ++line) {
if (remainder >= glyph_position_x_[line].size()) {
remainder -= glyph_position_x_[line].size();
} else {
if (start < pos + glyph_position_x_[line].size())
break;
pos += glyph_position_x_[line].size();
}
if (line == glyph_position_x_.size())
return rects;

if (end <= pos + glyph_position_x_[line].size()) {
rects.push_back(GetRectForLineRange(line, start - pos, end - pos));
return rects;
}

rects.push_back(
GetRectForLineRange(line, start - pos, glyph_position_x_[line].size()));

while (true) {
pos += glyph_position_x_[line].size();
line++;
if (line == glyph_position_x_.size())
break;

if (end <= pos + glyph_position_x_[line].size()) {
rects.push_back(GetRectForLineRange(line, 0, end - pos));
break;
} else {
rects.push_back(
GetRectForLineRange(line, 0, glyph_position_x_[line].size()));
}
}

return rects;
}

SkRect Paragraph::GetRectForLineRange(size_t line,
size_t start,
size_t end) const {
FXL_DCHECK(line < glyph_position_x_.size());
const std::vector<GlyphPosition>& line_glyph_position =
glyph_position_x_[line];
double glyph_end = (remainder < line_glyph_position.size() - 1)
? line_glyph_position[remainder + 1].start
: line_glyph_position[remainder].glyph_end();
return SkRect::MakeLTRB(line_glyph_position[remainder].start,
line > 0 ? line_heights_[line - 1] : 0, glyph_end,
line_heights_[line]);
if (line_glyph_position.empty())
return SkRect::MakeEmpty();

FXL_DCHECK(start < line_glyph_position.size());
SkScalar left = line_glyph_position[start].start;
end = std::min(end, line_glyph_position.size());
SkScalar right = line_glyph_position[end - 1].glyph_end();
SkScalar top = (line > 0) ? line_heights_[line - 1] : 0;
SkScalar bottom = line_heights_[line];
return SkRect::MakeLTRB(left, top, right, bottom);
}

Paragraph::PositionWithAffinity Paragraph::GetGlyphPositionAtCoordinate(
Expand Down
7 changes: 4 additions & 3 deletions third_party/txt/src/txt/paragraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ class Paragraph {
// end glyph indexes, including start and excluding end.
std::vector<SkRect> GetRectsForRange(size_t start, size_t end) const;

// Returns a bounding box that encloses the given starting and ending
// positions within a line.
SkRect GetRectForLineRange(size_t line, size_t start, size_t end) const;

// Returns the index of the glyph that corresponds to the provided coordinate,
// with the top left corner as the origin, and +y direction as down.
//
Expand All @@ -127,9 +131,6 @@ class Paragraph {
double dy,
bool using_glyph_center_as_boundary = false) const;

// Returns a bounding box that encloses the glyph at the index pos.
SkRect GetCoordinatesForGlyphPosition(size_t pos) const;

// Finds the first and last glyphs that define a word containing the glyph at
// index offset.
SkIPoint GetWordBoundary(size_t offset) const;
Expand Down
92 changes: 37 additions & 55 deletions third_party/txt/tests/paragraph_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -958,11 +958,7 @@ TEST_F(ParagraphTest, GetRectsForRangeParagraph) {
for (size_t i = 0; i < rects.size(); ++i) {
GetCanvas()->drawRect(rects[i], paint);
}
EXPECT_EQ(rects.size(), 1ull);
EXPECT_FLOAT_EQ(rects[0].left(), 0);
EXPECT_FLOAT_EQ(rects[0].top(), 0);
EXPECT_FLOAT_EQ(rects[0].right(), 28.417969);
EXPECT_FLOAT_EQ(rects[0].bottom(), 59);
EXPECT_EQ(rects.size(), 0ull);

rects = paragraph->GetRectsForRange(0, 1);
for (size_t i = 0; i < rects.size(); ++i) {
Expand All @@ -979,82 +975,68 @@ TEST_F(ParagraphTest, GetRectsForRangeParagraph) {
for (size_t i = 0; i < rects.size(); ++i) {
GetCanvas()->drawRect(rects[i], paint);
}
EXPECT_EQ(rects.size(), 3ull);
EXPECT_EQ(rects.size(), 1ull);
EXPECT_FLOAT_EQ(rects[0].left(), 56.835938);
EXPECT_FLOAT_EQ(rects[0].top(), 0);
EXPECT_FLOAT_EQ(rects[0].right(), 153.07422);
EXPECT_FLOAT_EQ(rects[0].right(), 177.97266);
EXPECT_FLOAT_EQ(rects[0].bottom(), 59);

EXPECT_FLOAT_EQ(rects[1].left(), 153.07422);
EXPECT_FLOAT_EQ(rects[1].top(), 0);
EXPECT_FLOAT_EQ(rects[1].right(), 165.52344);
EXPECT_FLOAT_EQ(rects[1].bottom(), 59);

EXPECT_FLOAT_EQ(rects[2].left(), 165.52344);
EXPECT_FLOAT_EQ(rects[2].top(), 0);
EXPECT_FLOAT_EQ(rects[2].right(), 177.97266);
EXPECT_FLOAT_EQ(rects[2].bottom(), 59);

paint.setColor(SK_ColorGREEN);
rects = paragraph->GetRectsForRange(8, 21);
for (size_t i = 0; i < rects.size(); ++i) {
GetCanvas()->drawRect(rects[i], paint);
}
EXPECT_EQ(rects.size(), 3ull);
EXPECT_EQ(rects.size(), 1ull);
EXPECT_FLOAT_EQ(rects[0].left(), 177.97266);
EXPECT_FLOAT_EQ(rects[0].top(), 0);
EXPECT_FLOAT_EQ(rects[0].right(), 352.48438);
EXPECT_FLOAT_EQ(rects[0].right(), 507.02344);
EXPECT_FLOAT_EQ(rects[0].bottom(), 59);

EXPECT_FLOAT_EQ(rects[1].left(), 352.48438);
EXPECT_FLOAT_EQ(rects[1].top(), 0);
EXPECT_FLOAT_EQ(rects[1].right(), 364.93359);
EXPECT_FLOAT_EQ(rects[1].bottom(), 59);

EXPECT_FLOAT_EQ(rects[2].left(), 364.93359);
EXPECT_FLOAT_EQ(rects[2].top(), 0);
EXPECT_FLOAT_EQ(rects[2].right(), 507.02344);
EXPECT_FLOAT_EQ(rects[2].bottom(), 59);

paint.setColor(SK_ColorRED);
rects = paragraph->GetRectsForRange(30, 100);
for (size_t i = 0; i < rects.size(); ++i) {
GetCanvas()->drawRect(rects[i], paint);
}
EXPECT_EQ(rects.size(), 17ull);
EXPECT_EQ(rects.size(), 4ull);
EXPECT_FLOAT_EQ(rects[0].left(), 211.375);
EXPECT_FLOAT_EQ(rects[0].top(), 59);
EXPECT_FLOAT_EQ(rects[0].right(), 296.62891);
EXPECT_FLOAT_EQ(rects[0].right(), 463.61719);
EXPECT_FLOAT_EQ(rects[0].bottom(), 118);

// TODO(garyq): The following set of vals are definetly wrong and
// end of paragraph handling needs to be fixed in a later patch.
EXPECT_FLOAT_EQ(rects[16].left(), 0);
EXPECT_FLOAT_EQ(rects[16].top(), 236);
EXPECT_FLOAT_EQ(rects[16].right(), 142.08984);
EXPECT_FLOAT_EQ(rects[16].bottom(), 295);
EXPECT_FLOAT_EQ(rects[3].left(), 0);
EXPECT_FLOAT_EQ(rects[3].top(), 236);
EXPECT_FLOAT_EQ(rects[3].right(), 142.08984);
EXPECT_FLOAT_EQ(rects[3].bottom(), 295);

paint.setColor(SK_ColorBLUE);
rects = paragraph->GetRectsForRange(19, 22);
for (size_t i = 0; i < rects.size(); ++i) {
GetCanvas()->drawRect(rects[i], paint);
}
EXPECT_EQ(rects.size(), 2ull);
EXPECT_FLOAT_EQ(rects[1].left(), 507.02344);
EXPECT_FLOAT_EQ(rects[1].top(), 0);
EXPECT_FLOAT_EQ(rects[1].right(), 519.47266);
EXPECT_FLOAT_EQ(rects[1].bottom(), 59);
EXPECT_EQ(rects.size(), 1ull);
EXPECT_FLOAT_EQ(rects[0].left(), 450.1875);
EXPECT_FLOAT_EQ(rects[0].top(), 0);
EXPECT_FLOAT_EQ(rects[0].right(), 519.47266);
EXPECT_FLOAT_EQ(rects[0].bottom(), 59);

paint.setColor(SK_ColorRED);
rects = paragraph->GetRectsForRange(21, 21);
for (size_t i = 0; i < rects.size(); ++i) {
GetCanvas()->drawRect(rects[i], paint);
}
EXPECT_EQ(rects.size(), 1ull);
EXPECT_EQ(rects.size(), 0ull);

ASSERT_TRUE(Snapshot());
}

SkRect GetCoordinatesForGlyphPosition(const txt::Paragraph& paragraph,
size_t pos) {
std::vector<SkRect> rects = paragraph.GetRectsForRange(pos, pos + 1);
return !rects.empty() ? rects.front() : SkRect::MakeEmpty();
}

TEST_F(ParagraphTest, GetWordBoundaryParagraph) {
const char* text =
"12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345 "
Expand Down Expand Up @@ -1091,63 +1073,63 @@ TEST_F(ParagraphTest, GetWordBoundaryParagraph) {
paint.setStrokeWidth(1);
paint.setColor(SK_ColorRED);

SkRect rect = paragraph->GetCoordinatesForGlyphPosition(0);
SkRect rect = GetCoordinatesForGlyphPosition(*paragraph, 0);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

EXPECT_EQ(paragraph->GetWordBoundary(0), SkIPoint::Make(0, 5));
EXPECT_EQ(paragraph->GetWordBoundary(1), SkIPoint::Make(0, 5));
EXPECT_EQ(paragraph->GetWordBoundary(2), SkIPoint::Make(0, 5));
EXPECT_EQ(paragraph->GetWordBoundary(3), SkIPoint::Make(0, 5));
EXPECT_EQ(paragraph->GetWordBoundary(4), SkIPoint::Make(0, 5));
rect = paragraph->GetCoordinatesForGlyphPosition(5);
rect = GetCoordinatesForGlyphPosition(*paragraph, 5);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

EXPECT_EQ(paragraph->GetWordBoundary(5), SkIPoint::Make(5, 6));
rect = paragraph->GetCoordinatesForGlyphPosition(6);
rect = GetCoordinatesForGlyphPosition(*paragraph, 6);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

EXPECT_EQ(paragraph->GetWordBoundary(6), SkIPoint::Make(6, 7));
rect = paragraph->GetCoordinatesForGlyphPosition(7);
rect = GetCoordinatesForGlyphPosition(*paragraph, 7);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

EXPECT_EQ(paragraph->GetWordBoundary(7), SkIPoint::Make(7, 12));
EXPECT_EQ(paragraph->GetWordBoundary(8), SkIPoint::Make(7, 12));
EXPECT_EQ(paragraph->GetWordBoundary(9), SkIPoint::Make(7, 12));
EXPECT_EQ(paragraph->GetWordBoundary(10), SkIPoint::Make(7, 12));
EXPECT_EQ(paragraph->GetWordBoundary(11), SkIPoint::Make(7, 12));
rect = paragraph->GetCoordinatesForGlyphPosition(12);
rect = GetCoordinatesForGlyphPosition(*paragraph, 12);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

EXPECT_EQ(paragraph->GetWordBoundary(12), SkIPoint::Make(12, 13));
rect = paragraph->GetCoordinatesForGlyphPosition(13);
rect = GetCoordinatesForGlyphPosition(*paragraph, 13);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

EXPECT_EQ(paragraph->GetWordBoundary(13), SkIPoint::Make(13, 18));
rect = paragraph->GetCoordinatesForGlyphPosition(18);
rect = GetCoordinatesForGlyphPosition(*paragraph, 18);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

rect = paragraph->GetCoordinatesForGlyphPosition(19);
rect = GetCoordinatesForGlyphPosition(*paragraph, 19);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

rect = paragraph->GetCoordinatesForGlyphPosition(24);
rect = GetCoordinatesForGlyphPosition(*paragraph, 24);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

rect = paragraph->GetCoordinatesForGlyphPosition(25);
rect = GetCoordinatesForGlyphPosition(*paragraph, 25);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

rect = paragraph->GetCoordinatesForGlyphPosition(30);
rect = GetCoordinatesForGlyphPosition(*paragraph, 30);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

EXPECT_EQ(paragraph->GetWordBoundary(30), SkIPoint::Make(30, 31));
rect = paragraph->GetCoordinatesForGlyphPosition(31);
rect = GetCoordinatesForGlyphPosition(*paragraph, 31);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

rect = paragraph->GetCoordinatesForGlyphPosition(icu_text.length() - 5);
rect = GetCoordinatesForGlyphPosition(*paragraph, icu_text.length() - 5);
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

EXPECT_EQ(paragraph->GetWordBoundary(icu_text.length() - 1),
SkIPoint::Make(icu_text.length() - 5, icu_text.length()));
rect = paragraph->GetCoordinatesForGlyphPosition(icu_text.length());
rect = GetCoordinatesForGlyphPosition(*paragraph, icu_text.length());
GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint);

ASSERT_TRUE(Snapshot());
Expand Down

0 comments on commit e3404b8

Please sign in to comment.