Index: ui/gfx/render_text_win.cc |
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc |
index 0469c0f5b7a61a6770457d330cb22d6e1c09401b..f78dfd49aa3c606aa22d6bd85cd8bfc312680f04 100644 |
--- a/ui/gfx/render_text_win.cc |
+++ b/ui/gfx/render_text_win.cc |
@@ -22,14 +22,17 @@ namespace gfx { |
namespace { |
-// The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes. |
-// TODO(msw): Review memory use/failure? Max string length? Alternate approach? |
-const int kGuessItems = 100; |
-const int kMaxItems = 10000; |
+// The maximum length of text supported for Uniscribe layout and display. |
+// This empirically chosen value should prevent major performance degradations. |
+// TODO(msw): Support longer text, partial layout/painting, etc. |
+const size_t kMaxUniscribeTextLength = 10000; |
-// The maximum supported number of Uniscribe glyphs; a glyph is 1 word. |
-// TODO(msw): Review memory use/failure? Max string length? Alternate approach? |
-const int kMaxGlyphs = 100000; |
+// The maximum supported number of text runs; an arbitrarily chosen value. |
+// TODO(msw): Support more runs; see https://codereview.chromium.org/17745005 |
+const size_t kMaxRuns = 10000; |
+ |
+// The maximum number of glyphs per run; ScriptShape fails on larger values. |
+const size_t kMaxGlyphs = 65535; |
// Callback to |EnumEnhMetaFile()| to intercept font creation. |
int CALLBACK MetaFileEnumProc(HDC hdc, |
@@ -181,6 +184,8 @@ RenderTextWin::RenderTextWin() |
: RenderText(), |
common_baseline_(0), |
needs_layout_(false) { |
+ set_truncate_length(kMaxUniscribeTextLength); |
+ |
memset(&script_control_, 0, sizeof(script_control_)); |
memset(&script_state_, 0, sizeof(script_state_)); |
@@ -208,7 +213,7 @@ SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { |
// Find the run that contains the point and adjust the argument location. |
int x = ToTextPoint(point).x(); |
size_t run_index = GetRunContainingXCoord(x); |
- if (run_index == runs_.size()) |
+ if (run_index >= runs_.size()) |
return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); |
internal::TextRun* run = runs_[run_index]; |
@@ -345,7 +350,9 @@ void RenderTextWin::SetSelectionModel(const SelectionModel& model) { |
ui::Range RenderTextWin::GetGlyphBounds(size_t index) { |
const size_t run_index = |
GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); |
- DCHECK_LT(run_index, runs_.size()); |
+ // Return edge bounds if the index is invalid or beyond the layout text size. |
+ if (run_index >= runs_.size()) |
+ return ui::Range(string_size_.width()); |
internal::TextRun* run = runs_[run_index]; |
const size_t layout_index = TextIndexToLayoutIndex(index); |
return ui::Range(GetGlyphXBoundary(run, layout_index, false), |
@@ -386,14 +393,11 @@ std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { |
} |
size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { |
- if (!obscured()) |
- return index; |
- |
DCHECK_LE(index, text().length()); |
- const ptrdiff_t offset = ui::UTF16IndexToOffset(text(), 0, index); |
- DCHECK_GE(offset, 0); |
- DCHECK_LE(static_cast<size_t>(offset), GetLayoutText().length()); |
- return static_cast<size_t>(offset); |
+ ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index; |
+ CHECK_GE(i, 0); |
+ // Clamp layout indices to the length of the text actually used for layout. |
+ return std::min<size_t>(GetLayoutText().length(), i); |
} |
size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const { |
@@ -496,23 +500,23 @@ void RenderTextWin::ItemizeLogicalText() { |
HRESULT hr = E_OUTOFMEMORY; |
int script_items_count = 0; |
std::vector<SCRIPT_ITEM> script_items; |
- const size_t text_length = GetLayoutText().length(); |
- for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) { |
+ |
+ // Guess the run count based on string sizes; Uniscribe requires 3 at minimum. |
+ const size_t layout_text_length = GetLayoutText().length(); |
+ size_t run_guess = std::min(kMaxRuns, std::max(3U, layout_text_length / 10)); |
Alexei Svitkine (slow)
2013/06/28 15:37:02
The old code would start at 100 whereas your new c
msw
2013/06/28 16:31:23
Done.
|
+ while (hr == E_OUTOFMEMORY && run_guess <= kMaxRuns) { |
// Derive the array of Uniscribe script items from the logical text. |
- // ScriptItemize always adds a terminal array item so that the length of the |
- // last item can be derived from the terminal SCRIPT_ITEM::iCharPos. |
- script_items.resize(n); |
- hr = ScriptItemize(GetLayoutText().c_str(), |
- text_length, |
- n - 1, |
- &script_control_, |
- &script_state_, |
- &script_items[0], |
- &script_items_count); |
+ // ScriptItemize always adds a terminal array item so that the length of |
+ // the last item can be derived from the terminal SCRIPT_ITEM::iCharPos. |
+ script_items.resize(run_guess); |
+ hr = ScriptItemize(GetLayoutText().c_str(), layout_text_length, |
+ run_guess - 1, &script_control_, &script_state_, |
+ &script_items[0], &script_items_count); |
+ // Ensure that |kMaxRuns| is attempted and the loop terminates afterward. |
+ run_guess = std::max(run_guess + 1, std::min(run_guess * 2, kMaxRuns)); |
} |
DCHECK(SUCCEEDED(hr)); |
- |
- if (script_items_count <= 0) |
+ if (!SUCCEEDED(hr) || script_items_count <= 0) |
return; |
// Temporarily apply composition underlines and selection colors. |
@@ -522,7 +526,7 @@ void RenderTextWin::ItemizeLogicalText() { |
// TODO(msw): Only break for bold/italic, not color etc. See TextRun comment. |
internal::StyleIterator style(colors(), styles()); |
SCRIPT_ITEM* script_item = &script_items[0]; |
- const size_t layout_text_length = GetLayoutText().length(); |
+ const size_t max_run_length = kMaxGlyphs / 2; |
for (size_t run_break = 0; run_break < layout_text_length;) { |
internal::TextRun* run = new internal::TextRun(); |
run->range.set_start(run_break); |
@@ -541,6 +545,9 @@ void RenderTextWin::ItemizeLogicalText() { |
const size_t script_item_break = (script_item + 1)->iCharPos; |
run_break = std::min(script_item_break, |
TextIndexToLayoutIndex(style.GetRange().end())); |
+ // Clamp run lengths to avoid exceeding the maximum supported glyph count. |
+ if ((run_break - run->range.start()) > max_run_length) |
+ run_break = run->range.start() + max_run_length; |
style.UpdatePosition(LayoutIndexToTextIndex(run_break)); |
if (script_item_break == run_break) |
script_item++; |
@@ -740,23 +747,19 @@ HRESULT RenderTextWin::ShapeTextRunWithFont(internal::TextRun* run, |
HRESULT hr = E_OUTOFMEMORY; |
const size_t run_length = run->range.length(); |
const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); |
- // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx |
+ // Guess the expected number of glyphs from the length of the run. |
+ // MSDN suggests this at http://msdn.microsoft.com/en-us/library/dd368564.aspx |
size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); |
- while (hr == E_OUTOFMEMORY && max_glyphs < kMaxGlyphs) { |
+ while (hr == E_OUTOFMEMORY && max_glyphs <= kMaxGlyphs) { |
run->glyph_count = 0; |
run->glyphs.reset(new WORD[max_glyphs]); |
run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); |
- hr = ScriptShape(cached_hdc_, |
- &run->script_cache, |
- run_text, |
- run_length, |
- max_glyphs, |
- &run->script_analysis, |
- run->glyphs.get(), |
- run->logical_clusters.get(), |
- run->visible_attributes.get(), |
+ hr = ScriptShape(cached_hdc_, &run->script_cache, run_text, run_length, |
+ max_glyphs, &run->script_analysis, run->glyphs.get(), |
+ run->logical_clusters.get(), run->visible_attributes.get(), |
&run->glyph_count); |
- max_glyphs *= 2; |
+ // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward. |
+ max_glyphs = std::max(max_glyphs + 1, std::min(max_glyphs * 2, kMaxGlyphs)); |
Alexei Svitkine (slow)
2013/06/28 15:37:02
How about making a helper function for this constr
msw
2013/06/28 16:31:23
I'll pass; 3 uses or real complexity constitutes a
|
} |
return hr; |
} |