Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(249)

Unified Diff: ui/gfx/render_text.cc

Issue 916203002: Reduce the number of text reshaping in RenderText (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/gfx/render_text.h ('k') | ui/gfx/render_text_harfbuzz.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/gfx/render_text.cc
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 50d29e83fe14585dc6002bc9b2a502b49d5c0b31..89acc9401ab7f9b736fd34e7af0c6558c7defc82 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -13,6 +13,7 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
#include "third_party/icu/source/common/unicode/rbbi.h"
#include "third_party/icu/source/common/unicode/utf16.h"
#include "third_party/skia/include/core/SkTypeface.h"
@@ -444,7 +445,7 @@ void RenderText::SetText(const base::string16& text) {
text_direction_ = base::i18n::UNKNOWN_DIRECTION;
obscured_reveal_index_ = -1;
- UpdateLayoutText();
+ OnTextAttributeChanged();
}
void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) {
@@ -463,7 +464,7 @@ void RenderText::SetFontList(const FontList& font_list) {
SetStyle(UNDERLINE, (font_style & gfx::Font::UNDERLINE) != 0);
baseline_ = kInvalidBaseline;
cached_bounds_and_offset_valid_ = false;
- ResetLayout();
+ OnLayoutTextAttributeChanged(false);
}
void RenderText::SetCursorEnabled(bool cursor_enabled) {
@@ -481,7 +482,7 @@ void RenderText::SetObscured(bool obscured) {
obscured_ = obscured;
obscured_reveal_index_ = -1;
cached_bounds_and_offset_valid_ = false;
- UpdateLayoutText();
+ OnTextAttributeChanged();
}
}
@@ -491,13 +492,7 @@ void RenderText::SetObscuredRevealIndex(int index) {
obscured_reveal_index_ = index;
cached_bounds_and_offset_valid_ = false;
- UpdateLayoutText();
-}
-
-void RenderText::SetReplaceNewlineCharsWithSymbols(bool replace) {
- replace_newline_chars_with_symbols_ = replace;
- cached_bounds_and_offset_valid_ = false;
- UpdateLayoutText();
+ OnTextAttributeChanged();
}
void RenderText::SetMultiline(bool multiline) {
@@ -505,6 +500,7 @@ void RenderText::SetMultiline(bool multiline) {
multiline_ = multiline;
cached_bounds_and_offset_valid_ = false;
lines_.clear();
+ OnDisplayTextAttributeChanged();
}
}
@@ -514,13 +510,14 @@ void RenderText::SetMinLineHeight(int line_height) {
min_line_height_ = line_height;
cached_bounds_and_offset_valid_ = false;
lines_.clear();
+ OnDisplayTextAttributeChanged();
}
void RenderText::SetElideBehavior(ElideBehavior elide_behavior) {
// TODO(skanuj) : Add a test for triggering layout change.
if (elide_behavior_ != elide_behavior) {
elide_behavior_ = elide_behavior;
- UpdateLayoutText();
+ OnDisplayTextAttributeChanged();
}
}
@@ -530,8 +527,10 @@ void RenderText::SetDisplayRect(const Rect& r) {
baseline_ = kInvalidBaseline;
cached_bounds_and_offset_valid_ = false;
lines_.clear();
- if (elide_behavior_ != NO_ELIDE)
- UpdateLayoutText();
+ if (elide_behavior_ != NO_ELIDE &&
+ elide_behavior_ != FADE_TAIL) {
+ OnDisplayTextAttributeChanged();
+ }
}
}
@@ -657,7 +656,11 @@ void RenderText::SetCompositionRange(const Range& composition_range) {
Range(0, text_.length()).Contains(composition_range));
composition_range_.set_end(composition_range.end());
composition_range_.set_start(composition_range.start());
- ResetLayout();
+ // TODO(oshima|msw): Altering composition underlines shouldn't
+ // require layout changes. It's currently necessary because
+ // RenderTextHarfBuzz paints text decorations by run, and
+ // RenderTextMac applies all styles during layout.
+ OnLayoutTextAttributeChanged(false);
}
void RenderText::SetColor(SkColor value) {
@@ -672,7 +675,9 @@ void RenderText::SetStyle(TextStyle style, bool value) {
styles_[style].SetValue(value);
cached_bounds_and_offset_valid_ = false;
- ResetLayout();
+ // TODO(oshima|msw): Not all style change requires layout changes.
+ // Consider optimizing based on the type of change.
+ OnLayoutTextAttributeChanged(false);
}
void RenderText::ApplyStyle(TextStyle style, bool value, const Range& range) {
@@ -684,7 +689,9 @@ void RenderText::ApplyStyle(TextStyle style, bool value, const Range& range) {
styles_[style].ApplyValue(value, Range(start, end));
cached_bounds_and_offset_valid_ = false;
- ResetLayout();
+ // TODO(oshima|msw): Not all style change requires layout changes.
+ // Consider optimizing based on the type of change.
+ OnLayoutTextAttributeChanged(false);
}
bool RenderText::GetStyle(TextStyle style) const {
@@ -699,38 +706,15 @@ void RenderText::SetDirectionalityMode(DirectionalityMode mode) {
directionality_mode_ = mode;
text_direction_ = base::i18n::UNKNOWN_DIRECTION;
cached_bounds_and_offset_valid_ = false;
- ResetLayout();
+ OnLayoutTextAttributeChanged(false);
}
-base::i18n::TextDirection RenderText::GetTextDirection() {
- if (text_direction_ == base::i18n::UNKNOWN_DIRECTION) {
- switch (directionality_mode_) {
- case DIRECTIONALITY_FROM_TEXT:
- // Derive the direction from the display text, which differs from text()
- // in the case of obscured (password) textfields.
- text_direction_ =
- base::i18n::GetFirstStrongCharacterDirection(GetLayoutText());
- break;
- case DIRECTIONALITY_FROM_UI:
- text_direction_ = base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT :
- base::i18n::LEFT_TO_RIGHT;
- break;
- case DIRECTIONALITY_FORCE_LTR:
- text_direction_ = base::i18n::LEFT_TO_RIGHT;
- break;
- case DIRECTIONALITY_FORCE_RTL:
- text_direction_ = base::i18n::RIGHT_TO_LEFT;
- break;
- default:
- NOTREACHED();
- }
- }
-
- return text_direction_;
+base::i18n::TextDirection RenderText::GetDisplayTextDirection() {
+ return GetTextDirection(GetDisplayText());
}
VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
- return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ?
+ return GetDisplayTextDirection() == base::i18n::LEFT_TO_RIGHT ?
CURSOR_RIGHT : CURSOR_LEFT;
}
@@ -787,7 +771,7 @@ void RenderText::DrawCursor(Canvas* canvas, const SelectionModel& position) {
bool RenderText::IsValidLogicalIndex(size_t index) {
// Check that the index is at a valid code point (not mid-surrgate-pair) and
- // that it's not truncated from the layout text (its glyph may be shown).
+ // that it's not truncated from the display text (its glyph may be shown).
//
// Indices within truncated text are disallowed so users can easily interact
// with the underlying truncated text using the ellipsis as a proxy. This lets
@@ -818,8 +802,10 @@ Rect RenderText::GetCursorBounds(const SelectionModel& caret,
if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) {
// The caret is attached to the boundary. Always return a 1-dip width caret,
// since there is nothing to overtype.
- if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0))
+ if ((GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT)
+ == (caret_pos == 0)) {
x = size.width();
+ }
} else {
size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ?
caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD);
@@ -931,7 +917,7 @@ RenderText::RenderText()
obscured_reveal_index_(-1),
truncate_length_(0),
elide_behavior_(NO_ELIDE),
- replace_newline_chars_with_symbols_(true),
+ text_elided_(false),
min_line_height_(0),
multiline_(false),
background_is_transparent_(false),
@@ -967,15 +953,36 @@ void RenderText::SetSelectionModel(const SelectionModel& model) {
cached_bounds_and_offset_valid_ = false;
}
-const base::string16& RenderText::GetLayoutText() const {
- return layout_text_;
+void RenderText::UpdateDisplayText(float text_width) {
+ // TODO(oshima): Consider support eliding for multi-line text.
+ // This requires max_line support first.
+ if (multiline_ ||
+ elide_behavior() == NO_ELIDE ||
+ elide_behavior() == FADE_TAIL ||
+ text_width < display_rect_.width() ||
+ layout_text_.empty()) {
+ text_elided_ = false;
+ display_text_.clear();
+ return;
+ }
+
+ // This doesn't trim styles so ellipsis may get rendered as a different
+ // style than the preceding text. See crbug.com/327850.
+ display_text_.assign(Elide(layout_text_,
+ text_width,
+ static_cast<float>(display_rect_.width()),
+ elide_behavior_));
+
+ text_elided_ = display_text_ != layout_text_;
+ if (!text_elided_)
+ display_text_.clear();
}
const BreakList<size_t>& RenderText::GetLineBreaks() {
if (line_breaks_.max() != 0)
return line_breaks_;
- const base::string16& layout_text = GetLayoutText();
+ const base::string16& layout_text = GetDisplayText();
const size_t text_length = layout_text.length();
line_breaks_.SetValue(0);
line_breaks_.SetMax(text_length);
@@ -1081,8 +1088,8 @@ std::vector<Rect> RenderText::TextBoundsToViewBounds(const Range& x) {
HorizontalAlignment RenderText::GetCurrentHorizontalAlignment() {
if (horizontal_alignment_ != ALIGN_TO_HEAD)
return horizontal_alignment_;
- return GetTextDirection() == base::i18n::RIGHT_TO_LEFT ? ALIGN_RIGHT
- : ALIGN_LEFT;
+ return GetDisplayTextDirection() == base::i18n::RIGHT_TO_LEFT ?
+ ALIGN_RIGHT : ALIGN_LEFT;
}
Vector2d RenderText::GetAlignmentOffset(size_t line_number) {
@@ -1111,7 +1118,7 @@ Vector2d RenderText::GetAlignmentOffset(size_t line_number) {
lines_.back().size.height();
offset.set_y((display_rect_.height() - text_height) / 2);
} else {
- offset.set_y(GetBaseline() - GetLayoutTextBaseline());
+ offset.set_y(GetBaseline() - GetDisplayTextBaseline());
}
return offset;
@@ -1156,6 +1163,34 @@ void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) {
renderer->SetDrawLooper(looper.get());
}
+base::i18n::TextDirection RenderText::GetTextDirection(
+ const base::string16& text) {
+ if (text_direction_ == base::i18n::UNKNOWN_DIRECTION) {
+ switch (directionality_mode_) {
+ case DIRECTIONALITY_FROM_TEXT:
+ // Derive the direction from the display text, which differs from text()
+ // in the case of obscured (password) textfields.
+ text_direction_ =
+ base::i18n::GetFirstStrongCharacterDirection(text);
+ break;
+ case DIRECTIONALITY_FROM_UI:
+ text_direction_ = base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT :
+ base::i18n::LEFT_TO_RIGHT;
+ break;
+ case DIRECTIONALITY_FORCE_LTR:
+ text_direction_ = base::i18n::LEFT_TO_RIGHT;
+ break;
+ case DIRECTIONALITY_FORCE_RTL:
+ text_direction_ = base::i18n::RIGHT_TO_LEFT;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ return text_direction_;
+}
+
// static
bool RenderText::RangeContainsCaret(const Range& range,
size_t caret_pos,
@@ -1174,8 +1209,9 @@ void RenderText::MoveCursorTo(size_t position, bool select) {
(cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
}
-void RenderText::UpdateLayoutText() {
+void RenderText::OnTextAttributeChanged() {
layout_text_.clear();
+ display_text_.clear();
line_breaks_.SetMax(0);
if (obscured_) {
@@ -1223,34 +1259,26 @@ void RenderText::UpdateLayoutText() {
layout_text_.assign(text.substr(0, iter.getIndex()) + kEllipsisUTF16);
}
}
-
- if (elide_behavior_ != NO_ELIDE &&
- elide_behavior_ != FADE_TAIL &&
- !layout_text_.empty() &&
- GetContentWidth() > display_rect_.width()) {
- // This doesn't trim styles so ellipsis may get rendered as a different
- // style than the preceding text. See crbug.com/327850.
- layout_text_.assign(Elide(layout_text_,
- static_cast<float>(display_rect_.width()),
- elide_behavior_));
- }
-
- // Replace the newline character with a newline symbol in single line mode.
static const base::char16 kNewline[] = { '\n', 0 };
static const base::char16 kNewlineSymbol[] = { 0x2424, 0 };
- if (!multiline_ && replace_newline_chars_with_symbols_)
+ if (!multiline_)
base::ReplaceChars(layout_text_, kNewline, kNewlineSymbol, &layout_text_);
- ResetLayout();
+ OnLayoutTextAttributeChanged(true);
}
base::string16 RenderText::Elide(const base::string16& text,
+ float text_width,
float available_width,
ElideBehavior behavior) {
if (available_width <= 0 || text.empty())
return base::string16();
if (behavior == ELIDE_EMAIL)
return ElideEmail(text, available_width);
+ if (text_width > 0 && text_width < available_width)
+ return text;
+
+ TRACE_EVENT0("ui", "RenderText::Elide");
// Create a RenderText copy with attributes that affect the rendering width.
scoped_ptr<RenderText> render_text = CreateInstanceOfSameType();
@@ -1260,26 +1288,31 @@ base::string16 RenderText::Elide(const base::string16& text,
render_text->set_truncate_length(truncate_length_);
render_text->styles_ = styles_;
render_text->colors_ = colors_;
- render_text->SetText(text);
- if (render_text->GetContentWidthF() <= available_width)
+ if (text_width == 0) {
+ render_text->SetText(text);
+ text_width = render_text->GetContentWidthF();
+ }
+ if (text_width <= available_width)
return text;
const base::string16 ellipsis = base::string16(kEllipsisUTF16);
const bool insert_ellipsis = (behavior != TRUNCATE);
const bool elide_in_middle = (behavior == ELIDE_MIDDLE);
const bool elide_at_beginning = (behavior == ELIDE_HEAD);
- StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning);
- render_text->SetText(ellipsis);
- const float ellipsis_width = render_text->GetContentWidthF();
+ if (insert_ellipsis) {
+ render_text->SetText(ellipsis);
+ const float ellipsis_width = render_text->GetContentWidthF();
+ if (ellipsis_width > available_width)
+ return base::string16();
+ }
- if (insert_ellipsis && (ellipsis_width > available_width))
- return base::string16();
+ StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning);
// Use binary search to compute the elided text.
size_t lo = 0;
size_t hi = text.length() - 1;
- const base::i18n::TextDirection text_direction = GetTextDirection();
+ const base::i18n::TextDirection text_direction = GetTextDirection(text);
for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) {
// Restore colors. They will be truncated to size by SetText.
render_text->colors_ = colors_;
@@ -1384,7 +1417,7 @@ base::string16 RenderText::ElideEmail(const base::string16& email,
std::min<float>(available_domain_width,
std::max<float>(available_width - full_username_width,
available_width / 2));
- domain = Elide(domain, desired_domain_width, ELIDE_MIDDLE);
+ domain = Elide(domain, 0, desired_domain_width, ELIDE_MIDDLE);
// Failing to elide the domain such that at least one character remains
// (other than the ellipsis itself) remains: return a single ellipsis.
if (domain.length() <= 1U)
@@ -1395,7 +1428,7 @@ base::string16 RenderText::ElideEmail(const base::string16& email,
// is guaranteed to fit with at least one character remaining given all the
// precautions taken earlier).
available_width -= GetStringWidthF(domain, font_list());
- username = Elide(username, available_width, ELIDE_TAIL);
+ username = Elide(username, 0, available_width, ELIDE_TAIL);
return username + kAtSignUTF16 + domain;
}
@@ -1426,9 +1459,8 @@ void RenderText::UpdateCachedBoundsAndOffset() {
}
void RenderText::DrawSelection(Canvas* canvas) {
- const std::vector<Rect> sel = GetSubstringBounds(selection());
- for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
- canvas->FillRect(*i, selection_background_focused_color_);
+ for (const Rect& s : GetSubstringBounds(selection()))
+ canvas->FillRect(s, selection_background_focused_color_);
}
} // namespace gfx
« no previous file with comments | « ui/gfx/render_text.h ('k') | ui/gfx/render_text_harfbuzz.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698