Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/ui/text/paragraph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ void Paragraph::paint(Canvas* canvas, double x, double y) {
}

std::vector<TextBox> Paragraph::getRectsForRange(unsigned start, unsigned end) {
return m_paragraphImpl->getRectsForRange(start, end);
return m_paragraphImpl->getRectsForRange(start, end,
txt::Paragraph::RectStyle::kTight);
}

Dart_Handle Paragraph::getPositionForOffset(double dx, double dy) {
Expand Down
7 changes: 5 additions & 2 deletions lib/ui/text/paragraph_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "flutter/lib/ui/painting/canvas.h"
#include "flutter/lib/ui/text/text_box.h"
#include "flutter/third_party/txt/src/txt/paragraph.h"

namespace blink {

Expand All @@ -32,8 +33,10 @@ class ParagraphImpl {

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

virtual std::vector<TextBox> getRectsForRange(unsigned start,
unsigned end) = 0;
virtual std::vector<TextBox> getRectsForRange(
unsigned start,
unsigned end,
txt::Paragraph::RectStyle rect_style) = 0;

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

Expand Down
8 changes: 5 additions & 3 deletions lib/ui/text/paragraph_impl_txt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@ void ParagraphImplTxt::paint(Canvas* canvas, double x, double y) {
m_paragraph->Paint(sk_canvas, x, y);
}

std::vector<TextBox> ParagraphImplTxt::getRectsForRange(unsigned start,
unsigned end) {
std::vector<TextBox> ParagraphImplTxt::getRectsForRange(
unsigned start,
unsigned end,
txt::Paragraph::RectStyle rect_style) {
std::vector<TextBox> result;
std::vector<txt::Paragraph::TextBox> boxes =
m_paragraph->GetRectsForRange(start, end);
m_paragraph->GetRectsForRange(start, end, rect_style);
for (const txt::Paragraph::TextBox& box : boxes) {
result.emplace_back(box.rect,
static_cast<blink::TextDirection>(box.direction));
Expand Down
6 changes: 4 additions & 2 deletions lib/ui/text/paragraph_impl_txt.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include "flutter/lib/ui/painting/canvas.h"
#include "flutter/lib/ui/text/paragraph_impl.h"
#include "flutter/lib/ui/text/text_box.h"
#include "flutter/third_party/txt/src/txt/paragraph.h"

namespace blink {

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

std::vector<TextBox> getRectsForRange(unsigned start, unsigned end) override;
std::vector<TextBox> getRectsForRange(
unsigned start,
unsigned end,
txt::Paragraph::RectStyle rect_style) override;
Dart_Handle getPositionForOffset(double dx, double dy) override;
Dart_Handle getWordBoundary(unsigned offset) override;

Expand Down
33 changes: 25 additions & 8 deletions third_party/txt/src/txt/paragraph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -674,11 +674,10 @@ void Paragraph::Layout(double width, bool force) {
word_start_position = std::numeric_limits<double>::quiet_NaN();
}
}
}
} // for each in glyph_blob

if (glyph_positions.empty())
continue;

SkPaint::FontMetrics metrics;
paint.getFontMetrics(&metrics);
paint_records.emplace_back(run.style(), SkPoint::Make(run_x_offset, 0),
Expand All @@ -701,10 +700,10 @@ void Paragraph::Layout(double width, bool force) {
Range<double>(glyph_positions.front().x_pos.start,
glyph_positions.back().x_pos.end),
line_number, metrics, run.direction());
}
} // for each in glyph_blobs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A brief description of the for loops that this brace encloses. This method is long and this helps make sense of it.


run_x_offset += layout.getAdvance();
}
} // for each in line_runs

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

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

std::vector<Paragraph::TextBox> Paragraph::GetRectsForRange(size_t start,
size_t end) const {
std::vector<Paragraph::TextBox> Paragraph::GetRectsForRange(
size_t start,
size_t end,
RectStyle rect_style) const {
std::map<size_t, std::vector<Paragraph::TextBox>> line_boxes;

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

std::vector<Paragraph::TextBox> boxes;
for (const auto& kv : line_boxes) {
boxes.insert(boxes.end(), kv.second.begin(), kv.second.end());
if (rect_style & RectStyle::kTight) {
// Ignore line max height and width and generate tight bounds.
boxes.insert(boxes.end(), kv.second.begin(), kv.second.end());
} else {
// Set each box to the max height of each line to ensure continuity.
float min_top = DBL_MAX;
float max_bottom = 0;
for (const Paragraph::TextBox& box : kv.second) {
min_top = std::min(box.rect.fTop, min_top);
max_bottom = std::max(box.rect.fBottom, max_bottom);
}
for (const Paragraph::TextBox& box : kv.second) {
boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft, min_top,
box.rect.fRight, max_bottom),
box.direction);
}
}
}
return boxes;
}
Expand Down
23 changes: 22 additions & 1 deletion third_party/txt/src/txt/paragraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ class Paragraph {

enum Affinity { UPSTREAM, DOWNSTREAM };

// TODO(garyq): Implement kIncludeLineSpacing and kExtendEndOfLine

// Options for various types of bounding boxes provided by
// GetRectsForRange(...).
// These options can be individually enabled, for example:
//
// (RectStyle::kTight | RectStyle::kExtendEndOfLine)
//
// provides tight bounding boxes and extends the last box per line to the end
// of the layout area.
enum RectStyle {
kNone = 0x0, // kNone cannot be combined with |.

// Provide tight bounding boxes that fit heights per span. Otherwise, the
// heights of spans are the max of the heights of the line the span belongs
// in.
kTight = 0x1
};

struct PositionWithAffinity {
const size_t position;
const Affinity affinity;
Expand Down Expand Up @@ -137,7 +156,9 @@ class Paragraph {

// Returns a vector of bounding boxes that enclose all text between start and
// end glyph indexes, including start and excluding end.
std::vector<TextBox> GetRectsForRange(size_t start, size_t end) const;
std::vector<TextBox> GetRectsForRange(size_t start,
size_t end,
RectStyle rect_style) 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 Down
104 changes: 94 additions & 10 deletions third_party/txt/tests/paragraph_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -983,13 +983,13 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) {
// are adjusted.
paint.setColor(SK_ColorRED);
std::vector<txt::Paragraph::TextBox> boxes =
paragraph->GetRectsForRange(0, 0);
paragraph->GetRectsForRange(0, 0, Paragraph::RectStyle::kNone);
for (size_t i = 0; i < boxes.size(); ++i) {
GetCanvas()->drawRect(boxes[i].rect, paint);
}
EXPECT_EQ(boxes.size(), 0ull);

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

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

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

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

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

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

TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeTight)) {
const char* text =
"( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
" ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
auto icu_text = icu::UnicodeString::fromUTF8(text);
std::u16string u16_text(icu_text.getBuffer(),
icu_text.getBuffer() + icu_text.length());

txt::ParagraphStyle paragraph_style;
paragraph_style.max_lines = 10;
paragraph_style.text_align = TextAlign::left;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());

txt::TextStyle text_style;
text_style.font_family = "Noto Sans CJK JP";
text_style.font_size = 50;
text_style.letter_spacing = 0;
text_style.font_weight = FontWeight::w500;
text_style.word_spacing = 0;
text_style.color = SK_ColorBLACK;
text_style.height = 1;
builder.PushStyle(text_style);

builder.AddText(u16_text);

builder.Pop();

auto paragraph = builder.Build();
paragraph->Layout(550);

paragraph->Paint(GetCanvas(), 0, 0);

SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setAntiAlias(true);
paint.setStrokeWidth(1);

// Tests for GetRectsForRange()
// NOTE: The base truth values may still need adjustment as the specifics
// are adjusted.
paint.setColor(SK_ColorRED);
std::vector<txt::Paragraph::TextBox> boxes =
paragraph->GetRectsForRange(0, 0, Paragraph::RectStyle::kTight);
for (size_t i = 0; i < boxes.size(); ++i) {
GetCanvas()->drawRect(boxes[i].rect, paint);
}
EXPECT_EQ(boxes.size(), 0ull);

boxes = paragraph->GetRectsForRange(0, 1, Paragraph::RectStyle::kTight);
for (size_t i = 0; i < boxes.size(); ++i) {
GetCanvas()->drawRect(boxes[i].rect, paint);
}
EXPECT_EQ(boxes.size(), 1ull);
EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0);
EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0);
EXPECT_FLOAT_EQ(boxes[0].rect.right(), 16.898438);
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 74);

paint.setColor(SK_ColorBLUE);
boxes = paragraph->GetRectsForRange(2, 8, Paragraph::RectStyle::kTight);
for (size_t i = 0; i < boxes.size(); ++i) {
GetCanvas()->drawRect(boxes[i].rect, paint);
}
EXPECT_EQ(boxes.size(), 1ull);
EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0);
EXPECT_FLOAT_EQ(boxes[0].rect.right(), 264.09375);
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 74);

paint.setColor(SK_ColorGREEN);
boxes = paragraph->GetRectsForRange(8, 21, Paragraph::RectStyle::kTight);
for (size_t i = 0; i < boxes.size(); ++i) {
GetCanvas()->drawRect(boxes[i].rect, paint);
}
EXPECT_EQ(boxes.size(), 2ull);
EXPECT_FLOAT_EQ(boxes[0].rect.left(), 264.09375);
EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0);
EXPECT_FLOAT_EQ(boxes[0].rect.right(), 595.08594);
EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 74);

ASSERT_TRUE(Snapshot());
}

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

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

auto rects1 = paragraph->GetRectsForRange(0, 12);
auto rects2 = paragraph2->GetRectsForRange(0, 12);
auto rects1 = paragraph->GetRectsForRange(0, 12, Paragraph::RectStyle::kNone);
auto rects2 =
paragraph2->GetRectsForRange(0, 12, Paragraph::RectStyle::kNone);

for (size_t i = 0; i < 12; ++i) {
auto r1 = GetCoordinatesForGlyphPosition(*paragraph, i);
Expand Down
Binary file not shown.