Chromium Code Reviews| 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; |
| } |