Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit ed0b191

Browse files
committed
Let GetRectsForRange provide more detailed/nuanced metrics through RectStyle enum.
1 parent 83ec05d commit ed0b191

File tree

8 files changed

+163
-27
lines changed

8 files changed

+163
-27
lines changed

lib/ui/text/paragraph.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ void Paragraph::paint(Canvas* canvas, double x, double y) {
8585
}
8686

8787
std::vector<TextBox> Paragraph::getRectsForRange(unsigned start, unsigned end) {
88-
return m_paragraphImpl->getRectsForRange(start, end);
88+
return m_paragraphImpl->getRectsForRange(start, end,
89+
txt::Paragraph::RectStyle::kTight);
8990
}
9091

9192
Dart_Handle Paragraph::getPositionForOffset(double dx, double dy) {

lib/ui/text/paragraph_impl.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "flutter/lib/ui/painting/canvas.h"
99
#include "flutter/lib/ui/text/text_box.h"
10+
#include "flutter/third_party/txt/src/txt/paragraph.h"
1011

1112
namespace blink {
1213

@@ -32,8 +33,10 @@ class ParagraphImpl {
3233

3334
virtual void paint(Canvas* canvas, double x, double y) = 0;
3435

35-
virtual std::vector<TextBox> getRectsForRange(unsigned start,
36-
unsigned end) = 0;
36+
virtual std::vector<TextBox> getRectsForRange(
37+
unsigned start,
38+
unsigned end,
39+
txt::Paragraph::RectStyle rect_style) = 0;
3740

3841
virtual Dart_Handle getPositionForOffset(double dx, double dy) = 0;
3942

lib/ui/text/paragraph_impl_txt.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@ void ParagraphImplTxt::paint(Canvas* canvas, double x, double y) {
6161
m_paragraph->Paint(sk_canvas, x, y);
6262
}
6363

64-
std::vector<TextBox> ParagraphImplTxt::getRectsForRange(unsigned start,
65-
unsigned end) {
64+
std::vector<TextBox> ParagraphImplTxt::getRectsForRange(
65+
unsigned start,
66+
unsigned end,
67+
txt::Paragraph::RectStyle rect_style) {
6668
std::vector<TextBox> result;
6769
std::vector<txt::Paragraph::TextBox> boxes =
68-
m_paragraph->GetRectsForRange(start, end);
70+
m_paragraph->GetRectsForRange(start, end, rect_style);
6971
for (const txt::Paragraph::TextBox& box : boxes) {
7072
result.emplace_back(box.rect,
7173
static_cast<blink::TextDirection>(box.direction));

lib/ui/text/paragraph_impl_txt.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
#include "flutter/lib/ui/painting/canvas.h"
99
#include "flutter/lib/ui/text/paragraph_impl.h"
1010
#include "flutter/lib/ui/text/text_box.h"
11-
#include "flutter/third_party/txt/src/txt/paragraph.h"
1211

1312
namespace blink {
1413

@@ -29,7 +28,10 @@ class ParagraphImplTxt : public ParagraphImpl {
2928
void layout(double width) override;
3029
void paint(Canvas* canvas, double x, double y) override;
3130

32-
std::vector<TextBox> getRectsForRange(unsigned start, unsigned end) override;
31+
std::vector<TextBox> getRectsForRange(
32+
unsigned start,
33+
unsigned end,
34+
txt::Paragraph::RectStyle rect_style) override;
3335
Dart_Handle getPositionForOffset(double dx, double dy) override;
3436
Dart_Handle getWordBoundary(unsigned offset) override;
3537

third_party/txt/src/txt/paragraph.cc

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -674,11 +674,10 @@ void Paragraph::Layout(double width, bool force) {
674674
word_start_position = std::numeric_limits<double>::quiet_NaN();
675675
}
676676
}
677-
}
677+
} // for each in glyph_blob
678678

679679
if (glyph_positions.empty())
680680
continue;
681-
682681
SkPaint::FontMetrics metrics;
683682
paint.getFontMetrics(&metrics);
684683
paint_records.emplace_back(run.style(), SkPoint::Make(run_x_offset, 0),
@@ -701,10 +700,10 @@ void Paragraph::Layout(double width, bool force) {
701700
Range<double>(glyph_positions.front().x_pos.start,
702701
glyph_positions.back().x_pos.end),
703702
line_number, metrics, run.direction());
704-
}
703+
} // for each in glyph_blobs
705704

706705
run_x_offset += layout.getAdvance();
707-
}
706+
} // for each in line_runs
708707

709708
// Adjust the glyph positions based on the alignment of the line.
710709
double line_x_offset = GetLineXOffset(run_x_offset);
@@ -772,7 +771,7 @@ void Paragraph::Layout(double width, bool force) {
772771
SkPoint::Make(paint_record.offset().x() + line_x_offset, y_offset));
773772
records_.emplace_back(std::move(paint_record));
774773
}
775-
}
774+
} // for each line_number
776775

777776
if (paragraph_style_.max_lines == 1 ||
778777
(paragraph_style_.unlimited_lines() && paragraph_style_.ellipsized())) {
@@ -1067,8 +1066,10 @@ void Paragraph::PaintBackground(SkCanvas* canvas,
10671066
canvas->drawRect(rect, record.style().background);
10681067
}
10691068

1070-
std::vector<Paragraph::TextBox> Paragraph::GetRectsForRange(size_t start,
1071-
size_t end) const {
1069+
std::vector<Paragraph::TextBox> Paragraph::GetRectsForRange(
1070+
size_t start,
1071+
size_t end,
1072+
RectStyle rect_style) const {
10721073
std::map<size_t, std::vector<Paragraph::TextBox>> line_boxes;
10731074

10741075
for (const CodeUnitRun& run : code_unit_runs_) {
@@ -1123,7 +1124,23 @@ std::vector<Paragraph::TextBox> Paragraph::GetRectsForRange(size_t start,
11231124

11241125
std::vector<Paragraph::TextBox> boxes;
11251126
for (const auto& kv : line_boxes) {
1126-
boxes.insert(boxes.end(), kv.second.begin(), kv.second.end());
1127+
if (rect_style & RectStyle::kTight) {
1128+
// Ignore line max height and width and generate tight bounds.
1129+
boxes.insert(boxes.end(), kv.second.begin(), kv.second.end());
1130+
} else {
1131+
// Set each box to the max height of each line to ensure continuity.
1132+
float min_top = DBL_MAX;
1133+
float max_bottom = -1;
1134+
for (const Paragraph::TextBox& box : kv.second) {
1135+
min_top = std::min(box.rect.fTop, min_top);
1136+
max_bottom = std::max(box.rect.fBottom, max_bottom);
1137+
}
1138+
for (const Paragraph::TextBox& box : kv.second) {
1139+
boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft, min_top,
1140+
box.rect.fRight, max_bottom),
1141+
box.direction);
1142+
}
1143+
}
11271144
}
11281145
return boxes;
11291146
}

third_party/txt/src/txt/paragraph.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,31 @@ class Paragraph {
5555

5656
enum Affinity { UPSTREAM, DOWNSTREAM };
5757

58+
// Options for various types of bounding boxes provided by
59+
// GetRectsForRange(...).
60+
// These options can be individually enabled, for example:
61+
//
62+
// (RectStyle::kTight | RectStyle::kExtendEndOfLine)
63+
//
64+
// provides tight bounding boxes and extends the last box per line to the end
65+
// of the layout area.
66+
enum RectStyle {
67+
kNone = 0x0, // kNone cannot be combined with |.
68+
69+
// Provide tight bounding boxes that fit heights per span. Otherwise, the
70+
// heights of spans are the max of the heights of the line the span belongs
71+
// in.
72+
kTight = 0x1,
73+
74+
// TODO(garyq): Implement line spacing inclusion/exclusion
75+
// Include the height of the extra line spacing in the rects.
76+
kIncludeLineSpacing = 0x2,
77+
78+
// TODO(garyq): Implement extension to end of line
79+
// Extend the last rect of each line to the end of the line (macOS style)
80+
kExtendEndOfLine = 0x4
81+
};
82+
5883
struct PositionWithAffinity {
5984
const size_t position;
6085
const Affinity affinity;
@@ -137,7 +162,9 @@ class Paragraph {
137162

138163
// Returns a vector of bounding boxes that enclose all text between start and
139164
// end glyph indexes, including start and excluding end.
140-
std::vector<TextBox> GetRectsForRange(size_t start, size_t end) const;
165+
std::vector<TextBox> GetRectsForRange(size_t start,
166+
size_t end,
167+
RectStyle rect_style) const;
141168

142169
// Returns the index of the glyph that corresponds to the provided coordinate,
143170
// with the top left corner as the origin, and +y direction as down.

third_party/txt/tests/paragraph_unittests.cc

Lines changed: 94 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -983,13 +983,13 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) {
983983
// are adjusted.
984984
paint.setColor(SK_ColorRED);
985985
std::vector<txt::Paragraph::TextBox> boxes =
986-
paragraph->GetRectsForRange(0, 0);
986+
paragraph->GetRectsForRange(0, 0, Paragraph::RectStyle::kNone);
987987
for (size_t i = 0; i < boxes.size(); ++i) {
988988
GetCanvas()->drawRect(boxes[i].rect, paint);
989989
}
990990
EXPECT_EQ(boxes.size(), 0ull);
991991

992-
boxes = paragraph->GetRectsForRange(0, 1);
992+
boxes = paragraph->GetRectsForRange(0, 1, Paragraph::RectStyle::kNone);
993993
for (size_t i = 0; i < boxes.size(); ++i) {
994994
GetCanvas()->drawRect(boxes[i].rect, paint);
995995
}
@@ -1000,7 +1000,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) {
10001000
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59);
10011001

10021002
paint.setColor(SK_ColorBLUE);
1003-
boxes = paragraph->GetRectsForRange(2, 8);
1003+
boxes = paragraph->GetRectsForRange(2, 8, Paragraph::RectStyle::kNone);
10041004
for (size_t i = 0; i < boxes.size(); ++i) {
10051005
GetCanvas()->drawRect(boxes[i].rect, paint);
10061006
}
@@ -1011,7 +1011,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) {
10111011
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59);
10121012

10131013
paint.setColor(SK_ColorGREEN);
1014-
boxes = paragraph->GetRectsForRange(8, 21);
1014+
boxes = paragraph->GetRectsForRange(8, 21, Paragraph::RectStyle::kNone);
10151015
for (size_t i = 0; i < boxes.size(); ++i) {
10161016
GetCanvas()->drawRect(boxes[i].rect, paint);
10171017
}
@@ -1022,7 +1022,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) {
10221022
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59);
10231023

10241024
paint.setColor(SK_ColorRED);
1025-
boxes = paragraph->GetRectsForRange(30, 100);
1025+
boxes = paragraph->GetRectsForRange(30, 100, Paragraph::RectStyle::kNone);
10261026
for (size_t i = 0; i < boxes.size(); ++i) {
10271027
GetCanvas()->drawRect(boxes[i].rect, paint);
10281028
}
@@ -1040,7 +1040,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) {
10401040
EXPECT_FLOAT_EQ(boxes[3].rect.bottom(), 295);
10411041

10421042
paint.setColor(SK_ColorBLUE);
1043-
boxes = paragraph->GetRectsForRange(19, 22);
1043+
boxes = paragraph->GetRectsForRange(19, 22, Paragraph::RectStyle::kNone);
10441044
for (size_t i = 0; i < boxes.size(); ++i) {
10451045
GetCanvas()->drawRect(boxes[i].rect, paint);
10461046
}
@@ -1051,7 +1051,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) {
10511051
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59);
10521052

10531053
paint.setColor(SK_ColorRED);
1054-
boxes = paragraph->GetRectsForRange(21, 21);
1054+
boxes = paragraph->GetRectsForRange(21, 21, Paragraph::RectStyle::kNone);
10551055
for (size_t i = 0; i < boxes.size(); ++i) {
10561056
GetCanvas()->drawRect(boxes[i].rect, paint);
10571057
}
@@ -1060,10 +1060,93 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) {
10601060
ASSERT_TRUE(Snapshot());
10611061
}
10621062

1063+
TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeTight)) {
1064+
const char* text =
1065+
"( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
1066+
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
1067+
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
1068+
auto icu_text = icu::UnicodeString::fromUTF8(text);
1069+
std::u16string u16_text(icu_text.getBuffer(),
1070+
icu_text.getBuffer() + icu_text.length());
1071+
1072+
txt::ParagraphStyle paragraph_style;
1073+
paragraph_style.max_lines = 10;
1074+
paragraph_style.text_align = TextAlign::left;
1075+
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
1076+
1077+
txt::TextStyle text_style;
1078+
text_style.font_family = "Noto Sans CJK JP";
1079+
text_style.font_size = 50;
1080+
text_style.letter_spacing = 0;
1081+
text_style.font_weight = FontWeight::w500;
1082+
text_style.word_spacing = 0;
1083+
text_style.color = SK_ColorBLACK;
1084+
text_style.height = 1;
1085+
builder.PushStyle(text_style);
1086+
1087+
builder.AddText(u16_text);
1088+
1089+
builder.Pop();
1090+
1091+
auto paragraph = builder.Build();
1092+
paragraph->Layout(550);
1093+
1094+
paragraph->Paint(GetCanvas(), 0, 0);
1095+
1096+
SkPaint paint;
1097+
paint.setStyle(SkPaint::kStroke_Style);
1098+
paint.setAntiAlias(true);
1099+
paint.setStrokeWidth(1);
1100+
1101+
// Tests for GetRectsForRange()
1102+
// NOTE: The base truth values may still need adjustment as the specifics
1103+
// are adjusted.
1104+
paint.setColor(SK_ColorRED);
1105+
std::vector<txt::Paragraph::TextBox> boxes =
1106+
paragraph->GetRectsForRange(0, 0, Paragraph::RectStyle::kTight);
1107+
for (size_t i = 0; i < boxes.size(); ++i) {
1108+
GetCanvas()->drawRect(boxes[i].rect, paint);
1109+
}
1110+
EXPECT_EQ(boxes.size(), 0ull);
1111+
1112+
boxes = paragraph->GetRectsForRange(0, 1, Paragraph::RectStyle::kTight);
1113+
for (size_t i = 0; i < boxes.size(); ++i) {
1114+
GetCanvas()->drawRect(boxes[i].rect, paint);
1115+
}
1116+
EXPECT_EQ(boxes.size(), 1ull);
1117+
EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0);
1118+
EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0);
1119+
EXPECT_FLOAT_EQ(boxes[0].rect.right(), 16.898438);
1120+
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 74);
1121+
1122+
paint.setColor(SK_ColorBLUE);
1123+
boxes = paragraph->GetRectsForRange(2, 8, Paragraph::RectStyle::kTight);
1124+
for (size_t i = 0; i < boxes.size(); ++i) {
1125+
GetCanvas()->drawRect(boxes[i].rect, paint);
1126+
}
1127+
EXPECT_EQ(boxes.size(), 1ull);
1128+
EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0);
1129+
EXPECT_FLOAT_EQ(boxes[0].rect.right(), 264.09375);
1130+
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 74);
1131+
1132+
paint.setColor(SK_ColorGREEN);
1133+
boxes = paragraph->GetRectsForRange(8, 21, Paragraph::RectStyle::kTight);
1134+
for (size_t i = 0; i < boxes.size(); ++i) {
1135+
GetCanvas()->drawRect(boxes[i].rect, paint);
1136+
}
1137+
EXPECT_EQ(boxes.size(), 2ull);
1138+
EXPECT_FLOAT_EQ(boxes[0].rect.left(), 264.09375);
1139+
EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0);
1140+
EXPECT_FLOAT_EQ(boxes[0].rect.right(), 595.08594);
1141+
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 74);
1142+
1143+
ASSERT_TRUE(Snapshot());
1144+
}
1145+
10631146
SkRect GetCoordinatesForGlyphPosition(const txt::Paragraph& paragraph,
10641147
size_t pos) {
10651148
std::vector<txt::Paragraph::TextBox> boxes =
1066-
paragraph.GetRectsForRange(pos, pos + 1);
1149+
paragraph.GetRectsForRange(pos, pos + 1, Paragraph::RectStyle::kNone);
10671150
return !boxes.empty() ? boxes.front().rect : SkRect::MakeEmpty();
10681151
}
10691152

@@ -1635,8 +1718,9 @@ TEST_F(ParagraphTest, UnderlineShiftParagraph) {
16351718
paragraph->records_[1].GetRunWidth(),
16361719
paragraph2->records_[0].GetRunWidth());
16371720

1638-
auto rects1 = paragraph->GetRectsForRange(0, 12);
1639-
auto rects2 = paragraph2->GetRectsForRange(0, 12);
1721+
auto rects1 = paragraph->GetRectsForRange(0, 12, Paragraph::RectStyle::kNone);
1722+
auto rects2 =
1723+
paragraph2->GetRectsForRange(0, 12, Paragraph::RectStyle::kNone);
16401724

16411725
for (size_t i = 0; i < 12; ++i) {
16421726
auto r1 = GetCoordinatesForGlyphPosition(*paragraph, i);
17.8 MB
Binary file not shown.

0 commit comments

Comments
 (0)