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

Side by Side Diff: ui/gfx/render_text_harfbuzz.cc

Issue 1070223004: Stop combining text runs which are connected by 'COMMON' blocks. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address msw@'s comments. Created 5 years, 6 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 unified diff | Download patch
« no previous file with comments | « ui/gfx/render_text_harfbuzz.h ('k') | ui/gfx/render_text_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/render_text_harfbuzz.h" 5 #include "ui/gfx/render_text_harfbuzz.h"
6 6
7 #include <limits> 7 #include <limits>
8 #include <set> 8 #include <set>
9 9
10 #include "base/i18n/bidi_line_iterator.h" 10 #include "base/i18n/bidi_line_iterator.h"
11 #include "base/i18n/break_iterator.h" 11 #include "base/i18n/break_iterator.h"
12 #include "base/i18n/char_iterator.h" 12 #include "base/i18n/char_iterator.h"
13 #include "base/profiler/scoped_tracker.h" 13 #include "base/profiler/scoped_tracker.h"
14 #include "base/strings/string_util.h" 14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
16 #include "base/trace_event/trace_event.h" 16 #include "base/trace_event/trace_event.h"
17 #include "third_party/harfbuzz-ng/src/hb.h" 17 #include "third_party/harfbuzz-ng/src/hb.h"
18 #include "third_party/icu/source/common/unicode/ubidi.h" 18 #include "third_party/icu/source/common/unicode/ubidi.h"
19 #include "third_party/icu/source/common/unicode/utf16.h"
19 #include "third_party/skia/include/core/SkColor.h" 20 #include "third_party/skia/include/core/SkColor.h"
20 #include "third_party/skia/include/core/SkTypeface.h" 21 #include "third_party/skia/include/core/SkTypeface.h"
21 #include "ui/gfx/canvas.h" 22 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/font_fallback.h" 23 #include "ui/gfx/font_fallback.h"
23 #include "ui/gfx/font_render_params.h" 24 #include "ui/gfx/font_render_params.h"
24 #include "ui/gfx/geometry/safe_integer_conversions.h" 25 #include "ui/gfx/geometry/safe_integer_conversions.h"
25 #include "ui/gfx/harfbuzz_font_skia.h" 26 #include "ui/gfx/harfbuzz_font_skia.h"
26 #include "ui/gfx/range/range_f.h" 27 #include "ui/gfx/range/range_f.h"
27 #include "ui/gfx/utf16_indexing.h" 28 #include "ui/gfx/utf16_indexing.h"
28 29
(...skipping 13 matching lines...) Expand all
42 // The maximum number of scripts a Unicode character can belong to. This value 43 // The maximum number of scripts a Unicode character can belong to. This value
43 // is arbitrarily chosen to be a good limit because it is unlikely for a single 44 // is arbitrarily chosen to be a good limit because it is unlikely for a single
44 // character to belong to more scripts. 45 // character to belong to more scripts.
45 const size_t kMaxScripts = 5; 46 const size_t kMaxScripts = 5;
46 47
47 // Returns true if characters of |block_code| may trigger font fallback. 48 // Returns true if characters of |block_code| may trigger font fallback.
48 // Dingbats and emoticons can be rendered through the color emoji font file, 49 // Dingbats and emoticons can be rendered through the color emoji font file,
49 // therefore it needs to be trigerred as fallbacks. See crbug.com/448909 50 // therefore it needs to be trigerred as fallbacks. See crbug.com/448909
50 bool IsUnusualBlockCode(UBlockCode block_code) { 51 bool IsUnusualBlockCode(UBlockCode block_code) {
51 return block_code == UBLOCK_GEOMETRIC_SHAPES || 52 return block_code == UBLOCK_GEOMETRIC_SHAPES ||
52 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS || 53 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS;
53 block_code == UBLOCK_DINGBATS ||
54 block_code == UBLOCK_EMOTICONS;
55 } 54 }
56 55
57 bool IsBracket(UChar32 character) { 56 bool IsBracket(UChar32 character) {
58 static const char kBrackets[] = { '(', ')', '{', '}', '<', '>', }; 57 static const char kBrackets[] = { '(', ')', '{', '}', '<', '>', };
59 static const char* kBracketsEnd = kBrackets + arraysize(kBrackets); 58 static const char* kBracketsEnd = kBrackets + arraysize(kBrackets);
60 return std::find(kBrackets, kBracketsEnd, character) != kBracketsEnd; 59 return std::find(kBrackets, kBracketsEnd, character) != kBracketsEnd;
61 } 60 }
62 61
63 // Returns the boundary between a special and a regular character. Special 62 // Returns the boundary between a special and a regular character. Special
64 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. 63 // characters are brackets or characters that satisfy |IsUnusualBlockCode|.
(...skipping 18 matching lines...) Expand all
83 const bool block_break = current_block != first_block && 82 const bool block_break = current_block != first_block &&
84 (first_block_unusual || IsUnusualBlockCode(current_block)); 83 (first_block_unusual || IsUnusualBlockCode(current_block));
85 if (block_break || current_char == '\n' || 84 if (block_break || current_char == '\n' ||
86 first_bracket != IsBracket(current_char)) { 85 first_bracket != IsBracket(current_char)) {
87 return run_start + iter.array_pos(); 86 return run_start + iter.array_pos();
88 } 87 }
89 } 88 }
90 return run_break; 89 return run_break;
91 } 90 }
92 91
93 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or 92 // If the given scripts match, returns the one that isn't USCRIPT_INHERITED,
94 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns 93 // i.e. the more specific one. Otherwise returns USCRIPT_INVALID_CODE.
95 // USCRIPT_INVALID_CODE.
96 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { 94 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) {
97 if (first == second || 95 if (first == second || second == USCRIPT_INHERITED)
98 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) {
99 return first; 96 return first;
100 } 97 if (first == USCRIPT_INHERITED)
101 if (first > USCRIPT_INVALID_CODE && first <= USCRIPT_INHERITED)
102 return second; 98 return second;
103 return USCRIPT_INVALID_CODE; 99 return USCRIPT_INVALID_CODE;
104 } 100 }
105 101
106 // Writes the script and the script extensions of the character with the 102 // Writes the script and the script extensions of the character with the
107 // Unicode |codepoint|. Returns the number of written scripts. 103 // Unicode |codepoint|. Returns the number of written scripts.
108 int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) { 104 int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) {
109 UErrorCode icu_error = U_ZERO_ERROR; 105 UErrorCode icu_error = U_ZERO_ERROR;
110 // ICU documentation incorrectly states that the result of 106 // ICU documentation incorrectly states that the result of
111 // |uscript_getScriptExtensions| will contain the regular script property. 107 // |uscript_getScriptExtensions| will contain the regular script property.
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 UScriptCode* script) { 155 UScriptCode* script) {
160 DCHECK_GT(length, 0U); 156 DCHECK_GT(length, 0U);
161 157
162 UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE }; 158 UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE };
163 159
164 base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length); 160 base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length);
165 size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts); 161 size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts);
166 *script = scripts[0]; 162 *script = scripts[0];
167 163
168 while (char_iterator.Advance()) { 164 while (char_iterator.Advance()) {
165 // Special handling to merge white space into the previous run.
166 if (u_isUWhiteSpace(char_iterator.get()))
167 continue;
169 ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); 168 ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size);
170 if (scripts_size == 0U) 169 if (scripts_size == 0U)
171 return char_iterator.array_pos(); 170 return char_iterator.array_pos();
172 *script = scripts[0]; 171 *script = scripts[0];
173 } 172 }
174 173
175 return length; 174 return length;
176 } 175 }
177 176
178 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without 177 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without
(...skipping 25 matching lines...) Expand all
204 reversed ? elements_end - element : element - elements_begin); 203 reversed ? elements_end - element : element - elements_begin);
205 if (reversed) 204 if (reversed)
206 *glyphs = Range(glyphs->end(), glyphs->start()); 205 *glyphs = Range(glyphs->end(), glyphs->start());
207 206
208 DCHECK(!chars->is_reversed()); 207 DCHECK(!chars->is_reversed());
209 DCHECK(!chars->is_empty()); 208 DCHECK(!chars->is_empty());
210 DCHECK(!glyphs->is_reversed()); 209 DCHECK(!glyphs->is_reversed());
211 DCHECK(!glyphs->is_empty()); 210 DCHECK(!glyphs->is_empty());
212 } 211 }
213 212
213 // Returns true if the code point |c| is a combining mark character in Unicode.
214 bool CharIsMark(UChar32 c) {
215 int8_t char_type = u_charType(c);
216 return char_type == U_NON_SPACING_MARK || char_type == U_ENCLOSING_MARK ||
217 char_type == U_COMBINING_SPACING_MARK;
218 }
219
220 // Gets the code point of |str| at the given code unit position |index|. If
221 // |index| is a surrogate code unit, returns the whole code point (unless the
222 // code unit is unpaired, in which case it just returns the surrogate value).
223 UChar32 GetCodePointAt(const base::string16& str, size_t index) {
224 UChar32 c;
225 U16_GET(str.data(), 0, index, str.size(), c);
226 return c;
227 }
228
214 // Internal class to generate Line structures. If |multiline| is true, the text 229 // Internal class to generate Line structures. If |multiline| is true, the text
215 // is broken into lines at |words| boundaries such that each line is no longer 230 // is broken into lines at |words| boundaries such that each line is no longer
216 // than |max_width|. If |multiline| is false, only outputs a single Line from 231 // than |max_width|. If |multiline| is false, only outputs a single Line from
217 // the given runs. |min_baseline| and |min_height| are the minimum baseline and 232 // the given runs. |min_baseline| and |min_height| are the minimum baseline and
218 // height for each line. 233 // height for each line.
219 // TODO(ckocagil): Expose the interface of this class in the header and test 234 // TODO(ckocagil): Expose the interface of this class in the header and test
220 // this class directly. 235 // this class directly.
221 class HarfBuzzLineBreaker { 236 class HarfBuzzLineBreaker {
222 public: 237 public:
223 HarfBuzzLineBreaker(size_t max_width, 238 HarfBuzzLineBreaker(size_t max_width,
224 int min_baseline, 239 int min_baseline,
225 float min_height, 240 float min_height,
226 bool multiline,
227 WordWrapBehavior word_wrap_behavior, 241 WordWrapBehavior word_wrap_behavior,
228 const base::string16& text, 242 const base::string16& text,
229 const BreakList<size_t>* words, 243 const BreakList<size_t>* words,
230 const internal::TextRunList& run_list) 244 const internal::TextRunList& run_list)
231 : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)), 245 : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)),
232 min_baseline_(min_baseline), 246 min_baseline_(min_baseline),
233 min_height_(min_height), 247 min_height_(min_height),
234 multiline_(multiline),
235 word_wrap_behavior_(word_wrap_behavior), 248 word_wrap_behavior_(word_wrap_behavior),
236 text_(text), 249 text_(text),
237 words_(words), 250 words_(words),
238 run_list_(run_list), 251 run_list_(run_list),
252 max_descent_(0),
253 max_ascent_(0),
239 text_x_(0), 254 text_x_(0),
240 line_x_(0), 255 available_width_(max_width_) {
241 max_descent_(0),
242 max_ascent_(0) {
243 DCHECK_EQ(multiline_, (words_ != nullptr));
244 AdvanceLine(); 256 AdvanceLine();
245 } 257 }
246 258
247 // Breaks the run at given |run_index| into Line structs. 259 // Constructs a single line for |text_| using |run_list_|.
248 void AddRun(int run_index) { 260 void ConstructSingleLine() {
249 const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index]; 261 for (size_t i = 0; i < run_list_.size(); i++) {
250 base::char16 first_char = text_[run->range.start()]; 262 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[i]);
251 if (multiline_ && first_char == '\n') { 263 internal::LineSegment segment;
252 AdvanceLine(); 264 segment.run = i;
253 } else if (multiline_ && (line_x_ + SkFloatToScalar(run->width)) > 265 segment.char_range = run.range;
254 max_width_) { 266 segment.x_range = Range(SkScalarCeilToInt(text_x_),
255 BreakRun(run_index); 267 SkScalarCeilToInt(text_x_ + run.width));
256 } else { 268 segment.width = run.width;
257 AddSegment(run_index, run->range, run->width); 269 AddLineSegment(segment);
270 }
271 }
272
273 // Constructs multiple lines for |text_| based on words iteration approach.
274 void ConstructMultiLines() {
275 DCHECK(words_);
276 for (auto iter = words_->breaks().begin(); iter != words_->breaks().end();
277 iter++) {
278 const Range word_range = words_->GetRange(iter);
279 std::vector<internal::LineSegment> word_segments;
280 SkScalar word_width = GetWordWidth(word_range, &word_segments);
281
282 // If the last word is '\n', we should advance a new line after adding
283 // the word to the current line.
284 bool new_line = false;
285
286 if (!word_segments.empty() &&
287 text_[word_segments.back().char_range.start()] == '\n') {
288 new_line = true;
289 word_width -= word_segments.back().width;
290 word_segments.pop_back();
291 }
292
293 // If the word is not the first word in the line and it can't fit into
294 // the current line, advance a new line.
295 if (word_width > available_width_ && available_width_ != max_width_)
296 AdvanceLine();
297 AddWordToLine(word_segments);
298 if (new_line)
299 AdvanceLine();
258 } 300 }
259 } 301 }
260 302
261 // Finishes line breaking and outputs the results. Can be called at most once. 303 // Finishes line breaking and outputs the results. Can be called at most once.
262 void Finalize(std::vector<internal::Line>* lines, SizeF* size) { 304 void FinalizeLines(std::vector<internal::Line>* lines, SizeF* size) {
263 DCHECK(!lines_.empty()); 305 DCHECK(!lines_.empty());
264 // Add an empty line to finish the line size calculation and remove it. 306 // Add an empty line to finish the line size calculation and remove it.
265 AdvanceLine(); 307 AdvanceLine();
266 lines_.pop_back(); 308 lines_.pop_back();
267 *size = total_size_; 309 *size = total_size_;
268 lines->swap(lines_); 310 lines->swap(lines_);
269 } 311 }
270 312
271 private: 313 private:
272 // A (line index, segment index) pair that specifies a segment in |lines_|. 314 // A (line index, segment index) pair that specifies a segment in |lines_|.
273 typedef std::pair<size_t, size_t> SegmentHandle; 315 typedef std::pair<size_t, size_t> SegmentHandle;
274 316
275 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { 317 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) {
276 return &lines_[handle.first].segments[handle.second]; 318 return &lines_[handle.first].segments[handle.second];
277 } 319 }
278 320
279 // Breaks a run into segments that fit in the last line in |lines_| and adds
280 // them. Adds a new Line to the back of |lines_| whenever a new segment can't
281 // be added without the Line's width exceeding |max_width_|.
282 void BreakRun(int run_index) {
283 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]);
284 SkScalar width = 0;
285 size_t next_char = run.range.start();
286
287 // Break the run until it fits the current line.
288 while (next_char < run.range.end()) {
289 const size_t current_char = next_char;
290 size_t end_char = next_char;
291 const bool skip_line =
292 BreakRunAtWidth(run, current_char, &width, &end_char, &next_char);
293 AddSegment(run_index, Range(current_char, end_char),
294 SkScalarToFloat(width));
295 if (skip_line)
296 AdvanceLine();
297 }
298 }
299
300 // Starting from |start_char|, finds a suitable line break position at or
301 // before available width using word break. If the current position is at the
302 // beginning of a line, this function will not roll back to |start_char| and
303 // |*next_char| will be greater than |start_char| (to avoid constructing empty
304 // lines). It stores the end of the segment range to |end_char|, which can be
305 // smaller than |*next_char| for certain word wrapping behavior.
306 // Returns whether to skip the line before |*next_char|.
307 // TODO(ckocagil): We might have to reshape after breaking at ligatures.
308 // See whether resolving the TODO above resolves this too.
309 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines.
310 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run,
311 size_t start_char,
312 SkScalar* width,
313 size_t* end_char,
314 size_t* next_char) {
315 DCHECK(words_);
316 DCHECK(run.range.Contains(Range(start_char, start_char + 1)));
317 SkScalar available_width = max_width_ - line_x_;
318 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char);
319 BreakList<size_t>::const_iterator next_word = word + 1;
320 // Width from |std::max(word->first, start_char)| to the current character.
321 SkScalar word_width = 0;
322 *width = 0;
323
324 Range char_range;
325 SkScalar truncated_width = 0;
326 for (size_t i = start_char; i < run.range.end(); i += char_range.length()) {
327 // |word| holds the word boundary at or before |i|, and |next_word| holds
328 // the word boundary right after |i|. Advance both |word| and |next_word|
329 // when |i| reaches |next_word|.
330 if (next_word != words_->breaks().end() && i >= next_word->first) {
331 if (*width > available_width) {
332 DCHECK_NE(WRAP_LONG_WORDS, word_wrap_behavior_);
333 *next_char = i;
334 if (word_wrap_behavior_ != TRUNCATE_LONG_WORDS)
335 *end_char = *next_char;
336 else
337 *width = truncated_width;
338 return true;
339 }
340 word = next_word++;
341 word_width = 0;
342 }
343
344 Range glyph_range;
345 run.GetClusterAt(i, &char_range, &glyph_range);
346 DCHECK_LT(0U, char_range.length());
347
348 SkScalar char_width = ((glyph_range.end() >= run.glyph_count)
349 ? SkFloatToScalar(run.width)
350 : run.positions[glyph_range.end()].x()) -
351 run.positions[glyph_range.start()].x();
352
353 *width += char_width;
354 word_width += char_width;
355
356 // TODO(mukai): implement ELIDE_LONG_WORDS.
357 if (*width > available_width) {
358 if (line_x_ != 0 || word_width < *width) {
359 // Roll back one word.
360 *width -= word_width;
361 *next_char = std::max(word->first, start_char);
362 *end_char = *next_char;
363 return true;
364 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) {
365 if (char_width < *width) {
366 // Roll back one character.
367 *width -= char_width;
368 *next_char = i;
369 } else {
370 // Continue from the next character.
371 *next_char = i + char_range.length();
372 }
373 *end_char = *next_char;
374 return true;
375 }
376 } else {
377 *end_char = char_range.end();
378 truncated_width = *width;
379 }
380 }
381
382 if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS)
383 *width = truncated_width;
384 *end_char = *next_char = run.range.end();
385 return false;
386 }
387
388 // RTL runs are broken in logical order but displayed in visual order. To find
389 // the text-space coordinate (where it would fall in a single-line text)
390 // |x_range| of RTL segments, segment widths are applied in reverse order.
391 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}.
392 void UpdateRTLSegmentRanges() {
393 if (rtl_segments_.empty())
394 return;
395 float x = SegmentFromHandle(rtl_segments_[0])->x_range.start();
396 for (size_t i = rtl_segments_.size(); i > 0; --i) {
397 internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]);
398 const float segment_width = segment->width;
399 segment->x_range = Range(x, x + segment_width);
400 x += segment_width;
401 }
402 rtl_segments_.clear();
403 }
404
405 // Finishes the size calculations of the last Line in |lines_|. Adds a new 321 // Finishes the size calculations of the last Line in |lines_|. Adds a new
406 // Line to the back of |lines_|. 322 // Line to the back of |lines_|.
407 void AdvanceLine() { 323 void AdvanceLine() {
408 if (!lines_.empty()) { 324 if (!lines_.empty()) {
409 internal::Line* line = &lines_.back(); 325 internal::Line* line = &lines_.back();
410 std::sort(line->segments.begin(), line->segments.end(), 326 std::sort(line->segments.begin(), line->segments.end(),
411 [this](const internal::LineSegment& s1, 327 [this](const internal::LineSegment& s1,
412 const internal::LineSegment& s2) -> bool { 328 const internal::LineSegment& s2) -> bool {
413 return run_list_.logical_to_visual(s1.run) < 329 return run_list_.logical_to_visual(s1.run) <
414 run_list_.logical_to_visual(s2.run); 330 run_list_.logical_to_visual(s2.run);
415 }); 331 });
416 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); 332 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_));
417 line->baseline = 333 line->baseline = std::max(min_baseline_, SkScalarRoundToInt(max_ascent_));
418 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_));
419 line->preceding_heights = std::ceil(total_size_.height()); 334 line->preceding_heights = std::ceil(total_size_.height());
420 total_size_.set_height(total_size_.height() + line->size.height()); 335 total_size_.set_height(total_size_.height() + line->size.height());
421 total_size_.set_width(std::max(total_size_.width(), line->size.width())); 336 total_size_.set_width(std::max(total_size_.width(), line->size.width()));
422 } 337 }
423 max_descent_ = 0; 338 max_descent_ = 0;
424 max_ascent_ = 0; 339 max_ascent_ = 0;
425 line_x_ = 0; 340 available_width_ = max_width_;
426 lines_.push_back(internal::Line()); 341 lines_.push_back(internal::Line());
427 } 342 }
428 343
429 // Adds a new segment with the given properties to |lines_.back()|. 344 // Adds word to the current line. A word may contain multiple segments. If the
430 void AddSegment(int run_index, Range char_range, float width) { 345 // word is the first word in line and its width exceeds |available_width_|,
431 if (char_range.is_empty()) { 346 // ignore/truncate/wrap it according to |word_wrap_behavior_|.
432 DCHECK_EQ(0, width); 347 void AddWordToLine(const std::vector<internal::LineSegment>& word_segments) {
348 DCHECK(!lines_.empty());
349 if (word_segments.empty())
433 return; 350 return;
351
352 bool has_truncated = false;
353 for (const internal::LineSegment& segment : word_segments) {
354 if (has_truncated)
355 break;
356 if (segment.width <= available_width_ ||
357 word_wrap_behavior_ == IGNORE_LONG_WORDS) {
358 AddLineSegment(segment);
359 } else {
360 DCHECK(word_wrap_behavior_ == TRUNCATE_LONG_WORDS ||
361 word_wrap_behavior_ == WRAP_LONG_WORDS);
362 has_truncated = (word_wrap_behavior_ == TRUNCATE_LONG_WORDS);
363
364 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]);
365 internal::LineSegment remaining_segment = segment;
366 while (!remaining_segment.char_range.is_empty()) {
367 size_t cutoff_pos = GetCutoffPos(remaining_segment);
368 // Corrects the boundary position if necessary. The surrogate pair and
369 // combined characters should not be separated.
370 FindValidBoundaryBefore(remaining_segment, &cutoff_pos);
msw 2015/06/01 23:27:18 Can this be part of GetCutoffPos?
xdai1 2015/06/02 03:49:56 Done.
371 SkScalar width =
372 remaining_segment.char_range.start() == cutoff_pos
msw 2015/06/01 23:27:18 nit: put this ==/0 logic in GetGlyphWidthForCharRa
xdai1 2015/06/02 03:49:56 Done.
373 ? 0
374 : run.GetGlyphWidthForCharRange(Range(
375 remaining_segment.char_range.start(), cutoff_pos));
376
377 // |max_width_| might be smaller than a single character. In this case
msw 2015/06/01 23:27:18 Ditto, can this be part of GetCutoffPos?
xdai1 2015/06/02 03:49:56 Done.
378 // we need to put at least one character in the line. Note that, we
379 // should not separate surrogate pair or combined characters.
380 // See RenderTextTest.Multiline_MinWidth for an example.
381 if (width == 0 && available_width_ == max_width_) {
382 // Skip all zero width characters.
383 for (cutoff_pos = remaining_segment.char_range.start();
384 cutoff_pos < remaining_segment.char_range.end();
385 cutoff_pos++) {
386 SkScalar char_width = run.GetGlyphWidthForCharRange(
387 Range(cutoff_pos, cutoff_pos + 1));
388 if (char_width > 0)
389 break;
390 }
391 // Don't separate surrogate pair or combined characters.
392 cutoff_pos = FindValidBoundaryAfter(remaining_segment, cutoff_pos);
393 width = run.GetGlyphWidthForCharRange(
394 Range(remaining_segment.char_range.start(), cutoff_pos));
395 }
396
397 if (width > 0) {
398 internal::LineSegment cut_segment;
399 cut_segment.run = remaining_segment.run;
400 cut_segment.char_range =
401 Range(remaining_segment.char_range.start(), cutoff_pos);
402 cut_segment.width = width;
403 cut_segment.x_range = Range(remaining_segment.x_range.start(),
404 SkScalarCeilToInt(text_x_ + width));
405 AddLineSegment(cut_segment);
406 // Updates old segment range.
407 remaining_segment.char_range.set_start(cutoff_pos);
408 remaining_segment.x_range.set_start(SkScalarCeilToInt(text_x_));
409 remaining_segment.width -= width;
410 }
411 if (has_truncated)
412 break;
413 if (!remaining_segment.char_range.is_empty())
414 AdvanceLine();
415 }
416 }
434 } 417 }
435 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); 418 }
436 419
437 internal::LineSegment segment; 420 // Add a line segment to the current line. Note that, in order to keep the
438 segment.run = run_index; 421 // visual order correct for ltr and rtl language, we need to merge segments
439 segment.char_range = char_range; 422 // that belong to the same run.
440 segment.x_range = Range( 423 void AddLineSegment(const internal::LineSegment& segment) {
441 SkScalarCeilToInt(text_x_), 424 DCHECK(!lines_.empty());
442 SkScalarCeilToInt(text_x_ + SkFloatToScalar(width)));
443 segment.width = width;
444
445 internal::Line* line = &lines_.back(); 425 internal::Line* line = &lines_.back();
426 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]);
427 if (!line->segments.empty()) {
428 internal::LineSegment& last_segment = line->segments.back();
429 // Merge segments that belong to the same run.
430 if (last_segment.run == segment.run) {
431 DCHECK(last_segment.char_range.end() == segment.char_range.start());
msw 2015/06/01 23:27:18 nit: DCHECK_EQ here and below.
xdai1 2015/06/02 03:49:56 Done.
432 DCHECK(last_segment.x_range.end() == segment.x_range.start());
433 last_segment.char_range.set_end(segment.char_range.end());
434 last_segment.width += segment.width;
435 last_segment.x_range.set_end(
436 SkScalarCeilToInt(text_x_ + segment.width));
437 if (run.is_rtl && last_segment.char_range.end() == run.range.end())
438 UpdateRTLSegmentRanges();
439 line->size.set_width(line->size.width() + segment.width);
440 text_x_ += segment.width;
441 available_width_ -= segment.width;
442 return;
443 }
444 }
446 line->segments.push_back(segment); 445 line->segments.push_back(segment);
447 446
448 SkPaint paint; 447 SkPaint paint;
449 paint.setTypeface(run.skia_face.get()); 448 paint.setTypeface(run.skia_face.get());
450 paint.setTextSize(SkIntToScalar(run.font_size)); 449 paint.setTextSize(SkIntToScalar(run.font_size));
451 paint.setAntiAlias(run.render_params.antialiasing); 450 paint.setAntiAlias(run.render_params.antialiasing);
452 SkPaint::FontMetrics metrics; 451 SkPaint::FontMetrics metrics;
453 paint.getFontMetrics(&metrics); 452 paint.getFontMetrics(&metrics);
454 453
455 line->size.set_width(line->size.width() + width); 454 line->size.set_width(line->size.width() + segment.width);
456 // TODO(dschuyler): Account for stylized baselines in string sizing. 455 // TODO(dschuyler): Account for stylized baselines in string sizing.
457 max_descent_ = std::max(max_descent_, metrics.fDescent); 456 max_descent_ = std::max(max_descent_, metrics.fDescent);
458 // fAscent is always negative. 457 // fAscent is always negative.
459 max_ascent_ = std::max(max_ascent_, -metrics.fAscent); 458 max_ascent_ = std::max(max_ascent_, -metrics.fAscent);
460 459
461 if (run.is_rtl) { 460 if (run.is_rtl) {
462 rtl_segments_.push_back( 461 rtl_segments_.push_back(
463 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); 462 SegmentHandle(lines_.size() - 1, line->segments.size() - 1));
464 // If this is the last segment of an RTL run, reprocess the text-space x 463 // If this is the last segment of an RTL run, reprocess the text-space x
465 // ranges of all segments from the run. 464 // ranges of all segments from the run.
466 if (char_range.end() == run.range.end()) 465 if (segment.char_range.end() == run.range.end())
467 UpdateRTLSegmentRanges(); 466 UpdateRTLSegmentRanges();
468 } 467 }
469 text_x_ += SkFloatToScalar(width); 468 text_x_ += segment.width;
470 line_x_ += SkFloatToScalar(width); 469 available_width_ -= segment.width;
470 }
471
472 // Finds the end position |end_pos| in |segment| where the preceding width is
473 // no larger than |available_width_|.
474 size_t GetCutoffPos(const internal::LineSegment& segment) const {
475 DCHECK(!segment.char_range.is_empty());
476 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]);
477 size_t end_pos = segment.char_range.start();
478 SkScalar width = 0;
479 while (end_pos < segment.char_range.end()) {
480 const SkScalar char_width =
481 run.GetGlyphWidthForCharRange(Range(end_pos, end_pos + 1));
482 if (width + char_width > available_width_)
483 break;
484 width += char_width;
485 end_pos++;
486 }
487 return end_pos;
488 }
489
490 // Finds the first valid position after |start_pos| for |segment|. The
491 // surrogate pair and combined characters should not be separated. If there
492 // is no surrogate pair or combined characters, return |start_pos| + 1.
493 size_t FindValidBoundaryAfter(const internal::LineSegment& segment,
msw 2015/06/01 23:27:18 Can we avoid copying modified versions of text_eli
xdai1 2015/06/02 03:49:56 Put these functions in text_util.h and text_util.c
494 const size_t start_pos) {
495 DCHECK_GE(start_pos, segment.char_range.start());
496 DCHECK_LT(start_pos, segment.char_range.end());
497
498 size_t index = start_pos;
499 // Check if it is a surrogate pair.
500 if (index <= text_.size() - 2 && U16_IS_LEAD(text_[index]) &&
501 U16_IS_TRAIL(text_[index + 1])) {
502 return index + 2;
503 }
504
505 // Check if it is a combined character.
506 while (index < segment.char_range.end() &&
507 CharIsMark(GetCodePointAt(text_, index))) {
508 index++;
509 }
510 // If |index| straddles a surrogate pair, go forward.
511 U16_SET_CP_LIMIT(text_.data(), 0, index, text_.size());
512
513 return index == start_pos ? start_pos + 1 : index;
514 }
515
516 // Corrects the boundary position |end_pos| if necessary. The surrogate
517 // pair and combined characters should not be separated.
518 void FindValidBoundaryBefore(const internal::LineSegment& segment,
519 size_t* end_pos) {
520 DCHECK_GE(*end_pos, segment.char_range.start());
521 DCHECK_LE(*end_pos, segment.char_range.end());
522 if (*end_pos == segment.char_range.start())
523 return;
524
525 size_t index = *end_pos;
526 // Check the surrogate pair was separated, go back.
527 if (index > 0 && index < text_.size() - 1 &&
528 U16_IS_LEAD(text_[index - 1]) && U16_IS_TRAIL(text_[index])) {
529 *end_pos = index - 1;
530 return;
531 }
532
533 // Check if it is a combined character.
534 while (index > segment.char_range.start() &&
535 CharIsMark(GetCodePointAt(text_, index - 1))) {
536 index--;
537 }
538 // If |index| straddles a surrogate pair, go back.
539 U16_SET_CP_START(text_.data(), segment.char_range.start(), index);
540
541 *end_pos = index;
542 }
543
544 // Gets the glyph width for |word_range|, and splits the |word| into different
545 // segments based on its runs.
546 SkScalar GetWordWidth(const Range& word_range,
547 std::vector<internal::LineSegment>* segments) const {
548 DCHECK(words_);
549 if (word_range.is_empty() || segments == nullptr)
550 return 0;
551 size_t run_start_index = run_list_.GetRunIndexAt(word_range.start());
552 size_t run_end_index = run_list_.GetRunIndexAt(word_range.end() - 1);
553 SkScalar width = 0;
554 for (size_t i = run_start_index; i <= run_end_index; i++) {
555 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[i]);
556 const Range char_range = run.range.Intersect(word_range);
557 DCHECK(!char_range.is_empty());
558 const SkScalar char_width = run.GetGlyphWidthForCharRange(char_range);
559 width += char_width;
560
561 internal::LineSegment segment;
562 segment.run = i;
563 segment.char_range = char_range;
564 segment.width = char_width;
565 segment.x_range = Range(SkScalarCeilToInt(text_x_ + width - char_width),
566 SkScalarCeilToInt(text_x_ + width));
567 segments->push_back(segment);
568 }
569 return width;
570 }
571
572 // RTL runs are broken in logical order but displayed in visual order. To find
573 // the text-space coordinate (where it would fall in a single-line text)
574 // |x_range| of RTL segments, segment widths are applied in reverse order.
575 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}.
576 void UpdateRTLSegmentRanges() {
577 if (rtl_segments_.empty())
578 return;
579 float x = SegmentFromHandle(rtl_segments_[0])->x_range.start();
580 for (size_t i = rtl_segments_.size(); i > 0; --i) {
581 internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]);
582 const float segment_width = segment->width;
583 segment->x_range = Range(x, x + segment_width);
584 x += segment_width;
585 }
586 rtl_segments_.clear();
471 } 587 }
472 588
473 const SkScalar max_width_; 589 const SkScalar max_width_;
474 const int min_baseline_; 590 const int min_baseline_;
475 const float min_height_; 591 const float min_height_;
476 const bool multiline_;
477 const WordWrapBehavior word_wrap_behavior_; 592 const WordWrapBehavior word_wrap_behavior_;
478 const base::string16& text_; 593 const base::string16& text_;
479 const BreakList<size_t>* const words_; 594 const BreakList<size_t>* const words_;
480 const internal::TextRunList& run_list_; 595 const internal::TextRunList& run_list_;
481 596
482 // Stores the resulting lines. 597 // Stores the resulting lines.
483 std::vector<internal::Line> lines_; 598 std::vector<internal::Line> lines_;
484 599
485 // Text space and line space x coordinates of the next segment to be added.
486 SkScalar text_x_;
487 SkScalar line_x_;
488
489 float max_descent_; 600 float max_descent_;
490 float max_ascent_; 601 float max_ascent_;
491 602
603 // Text space x coordinates of the next segment to be added.
604 SkScalar text_x_;
605 // Stores available width in the current line.
606 SkScalar available_width_;
607
492 // Size of the multiline text, not including the currently processed line. 608 // Size of the multiline text, not including the currently processed line.
493 SizeF total_size_; 609 SizeF total_size_;
494 610
495 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. 611 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|.
496 std::vector<SegmentHandle> rtl_segments_; 612 std::vector<SegmentHandle> rtl_segments_;
497 613
498 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); 614 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker);
499 }; 615 };
500 616
501 // Function object for case insensitive string comparison. 617 // Function object for case insensitive string comparison.
(...skipping 23 matching lines...) Expand all
525 baseline_offset(0), 641 baseline_offset(0),
526 baseline_type(0), 642 baseline_type(0),
527 font_style(0), 643 font_style(0),
528 strike(false), 644 strike(false),
529 diagonal_strike(false), 645 diagonal_strike(false),
530 underline(false) { 646 underline(false) {
531 } 647 }
532 648
533 TextRunHarfBuzz::~TextRunHarfBuzz() {} 649 TextRunHarfBuzz::~TextRunHarfBuzz() {}
534 650
535 void TextRunHarfBuzz::GetClusterAt(size_t pos,
536 Range* chars,
537 Range* glyphs) const {
538 DCHECK(range.Contains(Range(pos, pos + 1)));
539 DCHECK(chars);
540 DCHECK(glyphs);
541
542 if (glyph_count == 0) {
543 *chars = range;
544 *glyphs = Range();
545 return;
546 }
547
548 if (is_rtl) {
549 GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(),
550 true, chars, glyphs);
551 return;
552 }
553
554 GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(),
555 false, chars, glyphs);
556 }
557
558 Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const { 651 Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const {
559 DCHECK(range.Contains(char_range)); 652 DCHECK(range.Contains(char_range));
560 DCHECK(!char_range.is_reversed()); 653 DCHECK(!char_range.is_reversed());
561 DCHECK(!char_range.is_empty()); 654 DCHECK(!char_range.is_empty());
562 655
563 Range start_glyphs; 656 Range start_glyphs;
564 Range end_glyphs; 657 Range end_glyphs;
565 Range temp_range; 658 Range temp_range;
566 GetClusterAt(char_range.start(), &temp_range, &start_glyphs); 659 GetClusterAt(char_range.start(), &temp_range, &start_glyphs);
567 GetClusterAt(char_range.end() - 1, &temp_range, &end_glyphs); 660 GetClusterAt(char_range.end() - 1, &temp_range, &end_glyphs);
568 661
569 return is_rtl ? Range(end_glyphs.start(), start_glyphs.end()) : 662 return is_rtl ? Range(end_glyphs.start(), start_glyphs.end()) :
570 Range(start_glyphs.start(), end_glyphs.end()); 663 Range(start_glyphs.start(), end_glyphs.end());
571 } 664 }
572 665
573 size_t TextRunHarfBuzz::CountMissingGlyphs() const { 666 size_t TextRunHarfBuzz::CountMissingGlyphs() const {
574 static const int kMissingGlyphId = 0; 667 static const int kMissingGlyphId = 0;
575 size_t missing = 0; 668 size_t missing = 0;
576 for (size_t i = 0; i < glyph_count; ++i) 669 for (size_t i = 0; i < glyph_count; ++i)
577 missing += (glyphs[i] == kMissingGlyphId) ? 1 : 0; 670 missing += (glyphs[i] == kMissingGlyphId) ? 1 : 0;
578 return missing; 671 return missing;
579 } 672 }
580 673
674 void TextRunHarfBuzz::GetClusterAt(size_t pos,
675 Range* chars,
676 Range* glyphs) const {
677 DCHECK(range.Contains(Range(pos, pos + 1)));
678 DCHECK(chars);
679 DCHECK(glyphs);
680
681 if (glyph_count == 0) {
682 *chars = range;
683 *glyphs = Range();
684 return;
685 }
686
687 if (is_rtl) {
688 GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(),
689 true, chars, glyphs);
690 return;
691 }
692
693 GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(),
694 false, chars, glyphs);
695 }
696
581 RangeF TextRunHarfBuzz::GetGraphemeBounds( 697 RangeF TextRunHarfBuzz::GetGraphemeBounds(
582 base::i18n::BreakIterator* grapheme_iterator, 698 base::i18n::BreakIterator* grapheme_iterator,
583 size_t text_index) { 699 size_t text_index) {
584 DCHECK_LT(text_index, range.end()); 700 DCHECK_LT(text_index, range.end());
585 if (glyph_count == 0) 701 if (glyph_count == 0)
586 return RangeF(preceding_run_widths, preceding_run_widths + width); 702 return RangeF(preceding_run_widths, preceding_run_widths + width);
587 703
588 Range chars; 704 Range chars;
589 Range glyphs; 705 Range glyphs;
590 GetClusterAt(text_index, &chars, &glyphs); 706 GetClusterAt(text_index, &chars, &glyphs);
(...skipping 28 matching lines...) Expand all
619 cluster_width * (before + 1) / static_cast<float>(total)); 735 cluster_width * (before + 1) / static_cast<float>(total));
620 return RangeF(preceding_run_widths + grapheme_begin_x, 736 return RangeF(preceding_run_widths + grapheme_begin_x,
621 preceding_run_widths + grapheme_end_x); 737 preceding_run_widths + grapheme_end_x);
622 } 738 }
623 } 739 }
624 740
625 return RangeF(preceding_run_widths + cluster_begin_x, 741 return RangeF(preceding_run_widths + cluster_begin_x,
626 preceding_run_widths + cluster_end_x); 742 preceding_run_widths + cluster_end_x);
627 } 743 }
628 744
745 SkScalar TextRunHarfBuzz::GetGlyphWidthForCharRange(
746 const Range& char_range) const {
747 DCHECK(range.Contains(char_range));
748 Range glyph_range = CharRangeToGlyphRange(char_range);
749 return ((glyph_range.end() == glyph_count)
750 ? SkFloatToScalar(width)
751 : positions[glyph_range.end()].x()) -
752 positions[glyph_range.start()].x();
753 }
754
629 TextRunList::TextRunList() : width_(0.0f) {} 755 TextRunList::TextRunList() : width_(0.0f) {}
630 756
631 TextRunList::~TextRunList() {} 757 TextRunList::~TextRunList() {}
632 758
633 void TextRunList::Reset() { 759 void TextRunList::Reset() {
634 runs_.clear(); 760 runs_.clear();
635 width_ = 0.0f; 761 width_ = 0.0f;
636 } 762 }
637 763
638 void TextRunList::InitIndexMap() { 764 void TextRunList::InitIndexMap() {
(...skipping 14 matching lines...) Expand all
653 void TextRunList::ComputePrecedingRunWidths() { 779 void TextRunList::ComputePrecedingRunWidths() {
654 // Precalculate run width information. 780 // Precalculate run width information.
655 width_ = 0.0f; 781 width_ = 0.0f;
656 for (size_t i = 0; i < runs_.size(); ++i) { 782 for (size_t i = 0; i < runs_.size(); ++i) {
657 TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; 783 TextRunHarfBuzz* run = runs_[visual_to_logical_[i]];
658 run->preceding_run_widths = width_; 784 run->preceding_run_widths = width_;
659 width_ += run->width; 785 width_ += run->width;
660 } 786 }
661 } 787 }
662 788
789 size_t TextRunList::GetRunIndexAt(size_t position) const {
790 for (size_t i = 0; i < runs_.size(); ++i) {
791 if (runs_[i]->range.start() <= position && runs_[i]->range.end() > position)
792 return i;
793 }
794 return runs_.size();
795 }
796
663 } // namespace internal 797 } // namespace internal
664 798
665 RenderTextHarfBuzz::RenderTextHarfBuzz() 799 RenderTextHarfBuzz::RenderTextHarfBuzz()
666 : RenderText(), 800 : RenderText(),
667 update_layout_run_list_(false), 801 update_layout_run_list_(false),
668 update_display_run_list_(false), 802 update_display_run_list_(false),
669 update_grapheme_iterator_(false), 803 update_grapheme_iterator_(false),
670 update_display_text_(false), 804 update_display_text_(false),
671 glyph_width_for_test_(0u) { 805 glyph_width_for_test_(0u) {
672 set_truncate_length(kMaxTextLength); 806 set_truncate_length(kMaxTextLength);
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after
1001 if (lines().empty()) { 1135 if (lines().empty()) {
1002 // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is 1136 // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is
1003 // fixed. 1137 // fixed.
1004 scoped_ptr<tracked_objects::ScopedTracker> tracking_profile( 1138 scoped_ptr<tracked_objects::ScopedTracker> tracking_profile(
1005 new tracked_objects::ScopedTracker( 1139 new tracked_objects::ScopedTracker(
1006 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 HarfBuzzLineBreaker"))); 1140 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 HarfBuzzLineBreaker")));
1007 1141
1008 internal::TextRunList* run_list = GetRunList(); 1142 internal::TextRunList* run_list = GetRunList();
1009 HarfBuzzLineBreaker line_breaker( 1143 HarfBuzzLineBreaker line_breaker(
1010 display_rect().width(), font_list().GetBaseline(), 1144 display_rect().width(), font_list().GetBaseline(),
1011 std::max(font_list().GetHeight(), min_line_height()), multiline(), 1145 std::max(font_list().GetHeight(), min_line_height()),
1012 word_wrap_behavior(), GetDisplayText(), 1146 word_wrap_behavior(), GetDisplayText(),
1013 multiline() ? &GetLineBreaks() : nullptr, *run_list); 1147 multiline() ? &GetLineBreaks() : nullptr, *run_list);
1014 1148
1015 tracking_profile.reset(); 1149 tracking_profile.reset();
1016 1150
1017 for (size_t i = 0; i < run_list->size(); ++i) 1151 if (multiline())
1018 line_breaker.AddRun(i); 1152 line_breaker.ConstructMultiLines();
1153 else
1154 line_breaker.ConstructSingleLine();
1019 std::vector<internal::Line> lines; 1155 std::vector<internal::Line> lines;
1020 line_breaker.Finalize(&lines, &total_size_); 1156 line_breaker.FinalizeLines(&lines, &total_size_);
1021 set_lines(&lines); 1157 set_lines(&lines);
1022 } 1158 }
1023 } 1159 }
1024 1160
1025 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { 1161 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) {
1026 internal::SkiaTextRenderer renderer(canvas); 1162 internal::SkiaTextRenderer renderer(canvas);
1027 DrawVisualTextInternal(&renderer); 1163 DrawVisualTextInternal(&renderer);
1028 } 1164 }
1029 1165
1030 void RenderTextHarfBuzz::DrawVisualTextInternal( 1166 void RenderTextHarfBuzz::DrawVisualTextInternal(
(...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after
1479 DCHECK(!update_layout_run_list_); 1615 DCHECK(!update_layout_run_list_);
1480 DCHECK(!update_display_run_list_); 1616 DCHECK(!update_display_run_list_);
1481 return text_elided() ? display_run_list_.get() : &layout_run_list_; 1617 return text_elided() ? display_run_list_.get() : &layout_run_list_;
1482 } 1618 }
1483 1619
1484 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { 1620 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const {
1485 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); 1621 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList();
1486 } 1622 }
1487 1623
1488 } // namespace gfx 1624 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text_harfbuzz.h ('k') | ui/gfx/render_text_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698