Take letter spacing into account when linebreaking and initial implementation of GetRectsForRange().
Change-Id: Id7fa1b5b60c988cb73ba182241b3d8b3c998e07c
This commit is contained in:
@@ -166,7 +166,7 @@ class LineBreaker {
|
||||
// is having some kind of callback (or virtual class, or maybe even template), which could
|
||||
// easily be instantiated with Minikin's Layout. Future work for when needed.
|
||||
float addStyleRun(MinikinPaint* paint, const std::shared_ptr<FontCollection>& typeface,
|
||||
FontStyle style, size_t start, size_t end, bool isRtl);
|
||||
FontStyle style, size_t start, size_t end, bool isRtl, double letterSpacing = 0);
|
||||
|
||||
void addReplacement(size_t start, size_t end, float width);
|
||||
|
||||
@@ -230,6 +230,7 @@ class LineBreaker {
|
||||
icu::Locale mLocale;
|
||||
std::vector<uint16_t>mTextBuf;
|
||||
std::vector<float>mCharWidths;
|
||||
std::vector<float>mCharSpacing;
|
||||
|
||||
Hyphenator* mHyphenator;
|
||||
std::vector<HyphenationType> mHyphBuf;
|
||||
|
||||
@@ -113,7 +113,7 @@ static bool isLineEndSpace(uint16_t c) {
|
||||
// This method finds the candidate word breaks (using the ICU break iterator) and sends them
|
||||
// to addCandidate.
|
||||
float LineBreaker::addStyleRun(MinikinPaint* paint, const std::shared_ptr<FontCollection>& typeface,
|
||||
FontStyle style, size_t start, size_t end, bool isRtl) {
|
||||
FontStyle style, size_t start, size_t end, bool isRtl, double letterSpacing) {
|
||||
float width = 0.0f;
|
||||
int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
|
||||
|
||||
@@ -138,6 +138,10 @@ float LineBreaker::addStyleRun(MinikinPaint* paint, const std::shared_ptr<FontCo
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = start; i < end; ++i) {
|
||||
mCharSpacing.push_back(letterSpacing);
|
||||
}
|
||||
|
||||
size_t current = (size_t)mWordBreaker.current();
|
||||
size_t afterWord = start;
|
||||
size_t lastBreak = start;
|
||||
@@ -155,7 +159,7 @@ float LineBreaker::addStyleRun(MinikinPaint* paint, const std::shared_ptr<FontCo
|
||||
mStrategy = kBreakStrategy_Greedy;
|
||||
} else {
|
||||
if (isWordSpace(c)) mSpaceCount += 1;
|
||||
mWidth += mCharWidths[i];
|
||||
mWidth += mCharWidths[i] + mCharSpacing[i];
|
||||
if (!isLineEndSpace(c)) {
|
||||
postBreak = mWidth;
|
||||
postSpaceCount = mSpaceCount;
|
||||
@@ -341,7 +345,7 @@ void LineBreaker::pushBreak(int offset, float width, uint8_t hyphenEdit) {
|
||||
void LineBreaker::addReplacement(size_t start, size_t end, float width) {
|
||||
mCharWidths[start] = width;
|
||||
std::fill(&mCharWidths[start + 1], &mCharWidths[end], 0.0f);
|
||||
addStyleRun(nullptr, nullptr, FontStyle(), start, end, false);
|
||||
addStyleRun(nullptr, nullptr, FontStyle(), start, end, false, 0);
|
||||
}
|
||||
|
||||
// Get the width of a space. May return 0 if there are no spaces.
|
||||
|
||||
@@ -135,7 +135,8 @@ void Paragraph::AddRunsToLineBreaker(
|
||||
breaker_.addStyleRun(&paint,
|
||||
font_collection_->GetMinikinFontCollectionForFamily(
|
||||
run.style.font_family),
|
||||
font, run.start, run.end, false);
|
||||
font, run.start, run.end, false,
|
||||
run.style.letter_spacing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +172,8 @@ void Paragraph::Layout(double width, bool force) {
|
||||
lines_ = 0;
|
||||
line_widths_ = std::vector<double>();
|
||||
line_heights_ = std::vector<double>();
|
||||
|
||||
// Set padding elements to have a minimum point.
|
||||
line_heights_.push_back(0);
|
||||
glyph_position_x_ = std::vector<std::vector<double>>();
|
||||
glyph_position_x_.push_back(std::vector<double>());
|
||||
@@ -283,6 +286,7 @@ void Paragraph::Layout(double width, bool force) {
|
||||
// Check if we should remove trailing whitespace of blobs.
|
||||
size_t trailing_length = 0;
|
||||
while (
|
||||
paragraph_style_.text_align == TextAlign::justify &&
|
||||
minikin::isWordSpace(
|
||||
text_[character_index + blob_length - trailing_length - 1]) &&
|
||||
layout_end == next_break) {
|
||||
@@ -613,7 +617,43 @@ void Paragraph::PaintWavyDecoration(SkCanvas* canvas,
|
||||
|
||||
std::vector<SkRect> Paragraph::GetRectsForRange(size_t start,
|
||||
size_t end) const {
|
||||
return std::vector<SkRect>();
|
||||
std::vector<SkRect> rects;
|
||||
end = fmin(end, text_.size() - 1);
|
||||
while (start <= end) {
|
||||
SkIPoint word_bounds = GetWordBoundary(start);
|
||||
word_bounds.fY = fmin(end + 1, word_bounds.fY);
|
||||
word_bounds.fX = fmax(start, word_bounds.fX);
|
||||
start = word_bounds.fY;
|
||||
SkRect left_limits = GetCoordinatesForGlyphPosition(word_bounds.fX + 1);
|
||||
SkRect right_limits = GetCoordinatesForGlyphPosition(word_bounds.fY);
|
||||
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());
|
||||
size_t line = 1;
|
||||
for (line = 1; line < line_heights_.size() - 1; ++line) {
|
||||
if (remainder > glyph_position_x_[line].size() - 2) {
|
||||
remainder -= glyph_position_x_[line].size() - 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SkRect::MakeLTRB(glyph_position_x_[line][remainder],
|
||||
line_heights_[line - 1],
|
||||
remainder < glyph_position_x_[line].size() - 2
|
||||
? glyph_position_x_[line][remainder + 1]
|
||||
: line_widths_[line - 1],
|
||||
line_heights_[line]);
|
||||
}
|
||||
|
||||
size_t Paragraph::GetGlyphPositionAtCoordinate(double dx, double dy) const {
|
||||
@@ -643,7 +683,8 @@ size_t Paragraph::GetGlyphPositionAtCoordinate(double dx, double dy) const {
|
||||
}
|
||||
|
||||
SkIPoint Paragraph::GetWordBoundary(size_t offset) const {
|
||||
return SkIPoint::Make(0, 0);
|
||||
// TODO(garyq): Implement.
|
||||
return SkIPoint::Make(0, offset + 1);
|
||||
}
|
||||
|
||||
int Paragraph::GetLineCount() const {
|
||||
|
||||
@@ -61,6 +61,8 @@ class Paragraph {
|
||||
|
||||
size_t GetGlyphPositionAtCoordinate(double dx, double dy) const;
|
||||
|
||||
SkRect GetCoordinatesForGlyphPosition(size_t pos) const;
|
||||
|
||||
SkIPoint GetWordBoundary(size_t offset) const;
|
||||
|
||||
int GetLineCount() const;
|
||||
|
||||
@@ -303,6 +303,9 @@ TEST_F(RenderTest, LeftAlignParagraph) {
|
||||
paragraph->Layout(GetTestCanvasWidth() - 100);
|
||||
|
||||
paragraph->Paint(GetCanvas(), 0, 0);
|
||||
|
||||
ASSERT_TRUE(Snapshot());
|
||||
|
||||
ASSERT_EQ(paragraph->text_.size(), std::string{text}.length());
|
||||
for (size_t i = 0; i < u16_text.length(); i++) {
|
||||
ASSERT_EQ(paragraph->text_[i], u16_text[i]);
|
||||
@@ -343,11 +346,9 @@ TEST_F(RenderTest, LeftAlignParagraph) {
|
||||
// Tests for GetGlyphPositionAtCoordinate()
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(0, 0), 0ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 1), 0ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 30), 74ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 60), 142ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(2000, 30), 141ull);
|
||||
|
||||
ASSERT_TRUE(Snapshot());
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 30), 68ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 60), 134ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(2000, 30), 133ull);
|
||||
}
|
||||
|
||||
TEST_F(RenderTest, RightAlignParagraph) {
|
||||
@@ -856,7 +857,7 @@ TEST_F(RenderTest, DISABLED_ArabicParagraph) {
|
||||
TEST_F(RenderTest, GetGlyphPositionAtCoordinateParagraph) {
|
||||
const char* text =
|
||||
"12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345 "
|
||||
"67890";
|
||||
"67890 12345";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
@@ -880,27 +881,32 @@ TEST_F(RenderTest, GetGlyphPositionAtCoordinateParagraph) {
|
||||
builder.Pop();
|
||||
|
||||
auto paragraph = builder.Build();
|
||||
paragraph->Layout(GetTestCanvasWidth() - 500);
|
||||
paragraph->Layout(550);
|
||||
|
||||
paragraph->Paint(GetCanvas(), 0, 0);
|
||||
|
||||
ASSERT_TRUE(Snapshot());
|
||||
|
||||
// Tests for GetGlyphPositionAtCoordinate()
|
||||
// NOTE: resulting values can be a few off from their respective positions in
|
||||
// the original text because the final trailing whitespaces are sometimes not
|
||||
// drawn and therefore are not active glyphs.
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(-10000, -10000), 0ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(-1, -1), 0ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(0, 0), 0ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(3, 3), 0ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(35, 1), 1ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(100000, 20), 16ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(100000, 80), 33ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 80), 17ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 160), 34ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(10000, 160), 50ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(70, 160), 36ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 270), 51ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(35, 80), 18ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(10000, 10000), 68ull);
|
||||
ASSERT_TRUE(Snapshot());
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(100000, 20), 17ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(100000, 80), 35ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(-100000, 80), 18ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(20, -80), 0ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 80), 18ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 160), 36ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(10000, 160), 53ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(70, 160), 38ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 270), 54ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(35, 80), 19ull);
|
||||
ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(10000, 10000), 77ull);
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
|
||||
Reference in New Issue
Block a user