Skip to content

Commit

Permalink
Allow GetRectsForRange to provide more detailed/nuanced metrics throu…
Browse files Browse the repository at this point in the history
…gh RectStyle enum. (flutter#6335)

* Let GetRectsForRange provide more detailed/nuanced metrics through RectStyle enum.
  • Loading branch information
GaryQian authored Oct 2, 2018
1 parent b59c864 commit 3a9c22a
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 27 deletions.
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

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.

0 comments on commit 3a9c22a

Please sign in to comment.