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

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: Rebase. Created 5 years, 7 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
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"
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
44 // character to belong to more scripts. 44 // character to belong to more scripts.
45 const size_t kMaxScripts = 5; 45 const size_t kMaxScripts = 5;
46 46
47 // Returns true if characters of |block_code| may trigger font fallback. 47 // Returns true if characters of |block_code| may trigger font fallback.
48 // Dingbats and emoticons can be rendered through the color emoji font file, 48 // 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 49 // therefore it needs to be trigerred as fallbacks. See crbug.com/448909
50 bool IsUnusualBlockCode(UBlockCode block_code) { 50 bool IsUnusualBlockCode(UBlockCode block_code) {
51 return block_code == UBLOCK_GEOMETRIC_SHAPES || 51 return block_code == UBLOCK_GEOMETRIC_SHAPES ||
52 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS || 52 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS ||
53 block_code == UBLOCK_DINGBATS || 53 block_code == UBLOCK_DINGBATS ||
54 block_code == UBLOCK_EMOTICONS; 54 block_code == UBLOCK_EMOTICONS;
Jun Mukai 2015/05/11 05:14:38 Remove these special cases of DINGBATS and EMOTICI
xdai1 2015/05/11 21:55:22 Done.
55 } 55 }
56 56
57 bool IsBracket(UChar32 character) { 57 bool IsBracket(UChar32 character) {
58 static const char kBrackets[] = { '(', ')', '{', '}', '<', '>', }; 58 static const char kBrackets[] = { '(', ')', '{', '}', '<', '>', };
59 static const char* kBracketsEnd = kBrackets + arraysize(kBrackets); 59 static const char* kBracketsEnd = kBrackets + arraysize(kBrackets);
60 return std::find(kBrackets, kBracketsEnd, character) != kBracketsEnd; 60 return std::find(kBrackets, kBracketsEnd, character) != kBracketsEnd;
61 } 61 }
62 62
63 // Returns the boundary between a special and a regular character. Special 63 // Returns the boundary between a special and a regular character. Special
64 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. 64 // characters are brackets or characters that satisfy |IsUnusualBlockCode|.
(...skipping 18 matching lines...) Expand all
83 const bool block_break = current_block != first_block && 83 const bool block_break = current_block != first_block &&
84 (first_block_unusual || IsUnusualBlockCode(current_block)); 84 (first_block_unusual || IsUnusualBlockCode(current_block));
85 if (block_break || current_char == '\n' || 85 if (block_break || current_char == '\n' ||
86 first_bracket != IsBracket(current_char)) { 86 first_bracket != IsBracket(current_char)) {
87 return run_start + iter.array_pos(); 87 return run_start + iter.array_pos();
88 } 88 }
89 } 89 }
90 return run_break; 90 return run_break;
91 } 91 }
92 92
93 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or 93 // If the given scripts match, returns the one that isn't USCRIPT_INHERITED,
94 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns 94 // i.e. the more specific one. Otherwise returns USCRIPT_INVALID_CODE.
95 // USCRIPT_INVALID_CODE.
96 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { 95 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) {
97 if (first == second || 96 if (first == second || second == USCRIPT_INHERITED)
98 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) {
99 return first; 97 return first;
100 } 98 if (first == USCRIPT_INHERITED)
101 if (first > USCRIPT_INVALID_CODE && first <= USCRIPT_INHERITED)
102 return second; 99 return second;
103 return USCRIPT_INVALID_CODE; 100 return USCRIPT_INVALID_CODE;
104 } 101 }
105 102
106 // Writes the script and the script extensions of the character with the 103 // Writes the script and the script extensions of the character with the
107 // Unicode |codepoint|. Returns the number of written scripts. 104 // Unicode |codepoint|. Returns the number of written scripts.
108 int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) { 105 int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) {
109 UErrorCode icu_error = U_ZERO_ERROR; 106 UErrorCode icu_error = U_ZERO_ERROR;
110 // ICU documentation incorrectly states that the result of 107 // ICU documentation incorrectly states that the result of
111 // |uscript_getScriptExtensions| will contain the regular script property. 108 // |uscript_getScriptExtensions| will contain the regular script property.
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 UScriptCode* script) { 156 UScriptCode* script) {
160 DCHECK_GT(length, 0U); 157 DCHECK_GT(length, 0U);
161 158
162 UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE }; 159 UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE };
163 160
164 base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length); 161 base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length);
165 size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts); 162 size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts);
166 *script = scripts[0]; 163 *script = scripts[0];
167 164
168 while (char_iterator.Advance()) { 165 while (char_iterator.Advance()) {
166 // Special handling to merge white space into the previous run.
167 if (u_isUWhiteSpace(char_iterator.get()))
168 continue;
169 ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); 169 ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size);
170 if (scripts_size == 0U) 170 if (scripts_size == 0U)
171 return char_iterator.array_pos(); 171 return char_iterator.array_pos();
172 *script = scripts[0]; 172 *script = scripts[0];
173 } 173 }
174 174
175 return length; 175 return length;
176 } 176 }
177 177
178 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without 178 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 min_baseline_(min_baseline), 232 min_baseline_(min_baseline),
233 min_height_(min_height), 233 min_height_(min_height),
234 multiline_(multiline), 234 multiline_(multiline),
235 word_wrap_behavior_(word_wrap_behavior), 235 word_wrap_behavior_(word_wrap_behavior),
236 text_(text), 236 text_(text),
237 words_(words), 237 words_(words),
238 run_list_(run_list), 238 run_list_(run_list),
239 text_x_(0), 239 text_x_(0),
240 line_x_(0), 240 line_x_(0),
241 max_descent_(0), 241 max_descent_(0),
242 max_ascent_(0) { 242 max_ascent_(0),
243 next_char_pos_(0),
244 word_width_(0) {
243 DCHECK_EQ(multiline_, (words_ != nullptr)); 245 DCHECK_EQ(multiline_, (words_ != nullptr));
244 AdvanceLine(); 246 AdvanceLine();
245 } 247 }
246 248
247 // Breaks the run at given |run_index| into Line structs. 249 // Breaks the run at given |run_index| into Line structs.
248 void AddRun(int run_index) { 250 void AddRun(int run_index) {
249 const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index]; 251 const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index];
250 base::char16 first_char = text_[run->range.start()]; 252 base::char16 first_char = text_[run->range.start()];
251 if (multiline_ && first_char == '\n') { 253 if (multiline_ && first_char == '\n') {
252 AdvanceLine(); 254 AdvanceLine();
253 } else if (multiline_ && (line_x_ + SkFloatToScalar(run->width)) > 255 return;
254 max_width_) { 256 }
255 BreakRun(run_index); 257
258 int ignored_char_offset = 0;
259 SkScalar ignored_width = 0;
260 if (multiline_ && word_wrap_behavior_ == TRUNCATE_LONG_WORDS ) {
Jun Mukai 2015/05/11 05:14:38 This logic looks fairly complicated. I don't get
xdai1 2015/05/11 21:55:22 Done.
261 // Check if the run needs to be fully or partially ignored.
262 if (run->range.start() < next_char_pos_) {
263 BreakList<size_t>::const_iterator first_word_in_run =
264 words_->GetBreak(run->range.start());
265 BreakList<size_t>::const_iterator last_word_in_run =
266 words_->GetBreak(run->range.end() - 1);
267 if (first_word_in_run == last_word_in_run) {
268 // The run's range is fully covered by the current word.
269 return;
270 } else {
271 // The run contains multiple words, calculate |ignored_char_offset|
272 // and |ignored_width|.
273 BreakList<size_t>::const_iterator next_word = first_word_in_run + 1;
274 ignored_char_offset = next_word->first - run->range.start();
275 Range ignored_char_range(run->range.start(), next_word->first);
276 Range ignored_glyph_range =
277 run->CharRangeToGlyphRange(ignored_char_range);
278 ignored_width =
279 ((ignored_glyph_range.end() >= run->glyph_count)
280 ? SkFloatToScalar(run->width)
281 : run->positions[ignored_glyph_range.end()].x()) -
282 run->positions[ignored_glyph_range.start()].x();
283 }
284 }
285 }
286
287 if (multiline_ && line_x_ != 0) {
288 // If the following word is not the first word in the current line and
289 // can't be fit into the current line, advance a new line.
290 BreakList<size_t>::const_iterator word =
291 words_->GetBreak(run->range.start());
292 if (run->range.start() == word->first &&
293 line_x_ + GetWordWidth(word) - ignored_width > max_width_) {
294 AdvanceLine();
295 }
296 }
297
298 if (multiline_ &&
299 (line_x_ + SkFloatToScalar(run->width) - ignored_width) > max_width_) {
300 BreakRun(run_index, ignored_char_offset);
256 } else { 301 } else {
257 AddSegment(run_index, run->range, run->width); 302 Range char_range(run->range.start() + ignored_char_offset,
303 run->range.end());
304 AddSegment(run_index, char_range, run->width - ignored_width);
258 } 305 }
259 } 306 }
260 307
261 // Finishes line breaking and outputs the results. Can be called at most once. 308 // Finishes line breaking and outputs the results. Can be called at most once.
262 void Finalize(std::vector<internal::Line>* lines, SizeF* size) { 309 void Finalize(std::vector<internal::Line>* lines, SizeF* size) {
263 DCHECK(!lines_.empty()); 310 DCHECK(!lines_.empty());
264 // Add an empty line to finish the line size calculation and remove it. 311 // Add an empty line to finish the line size calculation and remove it.
265 AdvanceLine(); 312 AdvanceLine();
266 lines_.pop_back(); 313 lines_.pop_back();
267 *size = total_size_; 314 *size = total_size_;
268 lines->swap(lines_); 315 lines->swap(lines_);
269 } 316 }
270 317
271 private: 318 private:
272 // A (line index, segment index) pair that specifies a segment in |lines_|. 319 // A (line index, segment index) pair that specifies a segment in |lines_|.
273 typedef std::pair<size_t, size_t> SegmentHandle; 320 typedef std::pair<size_t, size_t> SegmentHandle;
274 321
275 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { 322 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) {
276 return &lines_[handle.first].segments[handle.second]; 323 return &lines_[handle.first].segments[handle.second];
277 } 324 }
278 325
326 SkScalar GetWordWidth(BreakList<size_t>::const_iterator word) {
327 if (word == words_->breaks().end())
328 return 0;
329 size_t word_st = word->first;
330 size_t word_en = (word + 1 == words_->breaks().end())
331 ? text_.size()
332 : (word + 1)->first;
333 internal::TextRunList::TextRunHarfBuzzIter run_st =
334 run_list_.GetRunAt(word_st);
335 internal::TextRunList::TextRunHarfBuzzIter run_en =
336 run_list_.GetRunAt(word_en);
337 SkScalar width = 0;
338 for (auto iter = run_st; iter != run_en; iter++) {
339 Range char_range = (*iter)->range.Intersect(Range(word_st, word_en));
340 Range glyph_range = (*iter)->CharRangeToGlyphRange(char_range);
341 SkScalar char_width = ((glyph_range.end() >= (*iter)->glyph_count)
342 ? SkFloatToScalar((*iter)->width)
343 : (*iter)->positions[glyph_range.end()].x()) -
344 (*iter)->positions[glyph_range.start()].x();
345 width += char_width;
346 }
347 return width;
348 }
349
279 // Breaks a run into segments that fit in the last line in |lines_| and adds 350 // 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 351 // 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_|. 352 // be added without the Line's width exceeding |max_width_|.
282 void BreakRun(int run_index) { 353 void BreakRun(int run_index, int ignored_offset) {
283 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); 354 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]);
284 SkScalar width = 0; 355 SkScalar width = 0;
285 size_t next_char = run.range.start(); 356 next_char_pos_ = run.range.start() + ignored_offset;
286 357
287 // Break the run until it fits the current line. 358 // Break the run until it fits the current line.
288 while (next_char < run.range.end()) { 359 while (next_char_pos_ < run.range.end()) {
289 const size_t current_char = next_char; 360 const size_t current_char = next_char_pos_;
290 size_t end_char = next_char; 361 size_t end_char = next_char_pos_;
291 const bool skip_line = 362 const bool skip_line =
292 BreakRunAtWidth(run, current_char, &width, &end_char, &next_char); 363 BreakRunAtWidth(run, current_char, &width, &end_char);
293 AddSegment(run_index, Range(current_char, end_char), 364 AddSegment(run_index, Range(current_char, end_char),
294 SkScalarToFloat(width)); 365 SkScalarToFloat(width));
295 if (skip_line) 366 if (skip_line)
296 AdvanceLine(); 367 AdvanceLine();
297 } 368 }
298 } 369 }
299 370
300 // Starting from |start_char|, finds a suitable line break position at or 371 // 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 372 // 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 373 // 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 374 // |*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 375 // 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. 376 // smaller than |*next_char| for certain word wrapping behavior.
306 // Returns whether to skip the line before |*next_char|. 377 // Returns whether to skip the line before |*next_char|.
307 // TODO(ckocagil): We might have to reshape after breaking at ligatures. 378 // TODO(ckocagil): We might have to reshape after breaking at ligatures.
308 // See whether resolving the TODO above resolves this too. 379 // See whether resolving the TODO above resolves this too.
309 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. 380 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines.
310 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, 381 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run,
311 size_t start_char, 382 size_t start_char,
312 SkScalar* width, 383 SkScalar* width,
313 size_t* end_char, 384 size_t* end_char) {
314 size_t* next_char) {
315 DCHECK(words_); 385 DCHECK(words_);
316 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); 386 DCHECK(run.range.Contains(Range(start_char, start_char + 1)));
317 SkScalar available_width = max_width_ - line_x_; 387 SkScalar available_width = max_width_ - line_x_;
318 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); 388 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char);
319 BreakList<size_t>::const_iterator next_word = word + 1; 389 BreakList<size_t>::const_iterator next_word = word + 1;
320 // Width from |std::max(word->first, start_char)| to the current character. 390 size_t word_end_pos =
321 SkScalar word_width = 0; 391 (next_word == words_->breaks().end()) ? text_.size() : next_word->first;
392 word_width_ = (start_char == word->first) ? 0 : word_width_;
322 *width = 0; 393 *width = 0;
323 394
324 Range char_range; 395 Range char_range;
325 SkScalar truncated_width = 0; 396 SkScalar truncated_width = 0;
326 for (size_t i = start_char; i < run.range.end(); i += char_range.length()) { 397 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 398 // |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| 399 // the word boundary right after |i|. Advance both |word| and |next_word|
329 // when |i| reaches |next_word|. 400 // when |i| reaches |next_word|.
330 if (next_word != words_->breaks().end() && i >= next_word->first) { 401 if (next_word != words_->breaks().end() && i >= next_word->first) {
331 if (*width > available_width) {
Jun Mukai 2015/05/11 05:14:38 And then, you will have to keep this logic right?
xdai1 2015/05/11 21:55:22 As we discussed offline, "aaaaa'bb ccc" has two wo
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++; 402 word = next_word++;
341 word_width = 0; 403 word_end_pos = (next_word == words_->breaks().end())
404 ? text_.size()
405 : next_word->first;
406 word_width_ = 0;
342 } 407 }
343 408
344 Range glyph_range; 409 Range glyph_range;
345 run.GetClusterAt(i, &char_range, &glyph_range); 410 run.GetClusterAt(i, &char_range, &glyph_range);
346 DCHECK_LT(0U, char_range.length()); 411 DCHECK_LT(0U, char_range.length());
347 412
348 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) 413 SkScalar char_width = ((glyph_range.end() >= run.glyph_count)
349 ? SkFloatToScalar(run.width) 414 ? SkFloatToScalar(run.width)
350 : run.positions[glyph_range.end()].x()) - 415 : run.positions[glyph_range.end()].x()) -
351 run.positions[glyph_range.start()].x(); 416 run.positions[glyph_range.start()].x();
352 417
353 *width += char_width; 418 *width += char_width;
354 word_width += char_width; 419 word_width_ += char_width;
355 420
356 // TODO(mukai): implement ELIDE_LONG_WORDS. 421 // TODO(mukai): implement ELIDE_LONG_WORDS.
357 if (*width > available_width) { 422 if (*width > available_width) {
358 if (line_x_ != 0 || word_width < *width) { 423 if ((line_x_ != 0 && word_width_ <= *width) ||
424 (line_x_ == 0 && word_width_ < *width)) {
359 // Roll back one word. 425 // Roll back one word.
360 *width -= word_width; 426 *width -= word_width_;
361 *next_char = std::max(word->first, start_char); 427 next_char_pos_ = std::max(word->first, start_char);;
362 *end_char = *next_char; 428 *end_char = next_char_pos_;
429 word_width_ = 0;
363 return true; 430 return true;
364 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { 431 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) {
365 if (char_width < *width) { 432 if (char_width < *width ||
433 (word_width_ > *width && char_width <= *width)) {
366 // Roll back one character. 434 // Roll back one character.
367 *width -= char_width; 435 *width -= char_width;
368 *next_char = i; 436 next_char_pos_ = i;
369 } else { 437 } else {
370 // Continue from the next character. 438 // Continue from the next character.
371 *next_char = i + char_range.length(); 439 next_char_pos_ = i + char_range.length();
372 } 440 }
373 *end_char = *next_char; 441 *end_char = next_char_pos_;
442 word_width_ = 0;
443 return true;
444 } else if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) {
445 *width = truncated_width;
446 next_char_pos_ = word_end_pos;
447 word_width_ = 0;
374 return true; 448 return true;
375 } 449 }
376 } else { 450 } else {
377 *end_char = char_range.end(); 451 *end_char = char_range.end();
378 truncated_width = *width; 452 truncated_width = *width;
379 } 453 }
380 } 454 }
381 455
382 if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) 456 if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS)
383 *width = truncated_width; 457 *width = truncated_width;
384 *end_char = *next_char = run.range.end(); 458 *end_char = next_char_pos_ = run.range.end();
385 return false; 459 return false;
386 } 460 }
387 461
388 // RTL runs are broken in logical order but displayed in visual order. To find 462 // 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) 463 // 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. 464 // |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]}. 465 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}.
392 void UpdateRTLSegmentRanges() { 466 void UpdateRTLSegmentRanges() {
393 if (rtl_segments_.empty()) 467 if (rtl_segments_.empty())
394 return; 468 return;
(...skipping 21 matching lines...) Expand all
416 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); 490 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_));
417 line->baseline = 491 line->baseline =
418 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); 492 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_));
419 line->preceding_heights = std::ceil(total_size_.height()); 493 line->preceding_heights = std::ceil(total_size_.height());
420 total_size_.set_height(total_size_.height() + line->size.height()); 494 total_size_.set_height(total_size_.height() + line->size.height());
421 total_size_.set_width(std::max(total_size_.width(), line->size.width())); 495 total_size_.set_width(std::max(total_size_.width(), line->size.width()));
422 } 496 }
423 max_descent_ = 0; 497 max_descent_ = 0;
424 max_ascent_ = 0; 498 max_ascent_ = 0;
425 line_x_ = 0; 499 line_x_ = 0;
500 word_width_ = 0;
426 lines_.push_back(internal::Line()); 501 lines_.push_back(internal::Line());
427 } 502 }
428 503
429 // Adds a new segment with the given properties to |lines_.back()|. 504 // Adds a new segment with the given properties to |lines_.back()|.
430 void AddSegment(int run_index, Range char_range, float width) { 505 void AddSegment(int run_index, Range char_range, float width) {
Jun Mukai 2015/05/11 05:14:38 I think all of your problem is that treating the '
431 if (char_range.is_empty()) { 506 if (char_range.is_empty()) {
432 DCHECK_EQ(0, width); 507 DCHECK_EQ(0, width);
433 return; 508 return;
434 } 509 }
435 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); 510 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]);
436 511
437 internal::LineSegment segment; 512 internal::LineSegment segment;
438 segment.run = run_index; 513 segment.run = run_index;
439 segment.char_range = char_range; 514 segment.char_range = char_range;
440 segment.x_range = Range( 515 segment.x_range = Range(
(...skipping 20 matching lines...) Expand all
461 if (run.is_rtl) { 536 if (run.is_rtl) {
462 rtl_segments_.push_back( 537 rtl_segments_.push_back(
463 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); 538 SegmentHandle(lines_.size() - 1, line->segments.size() - 1));
464 // If this is the last segment of an RTL run, reprocess the text-space x 539 // If this is the last segment of an RTL run, reprocess the text-space x
465 // ranges of all segments from the run. 540 // ranges of all segments from the run.
466 if (char_range.end() == run.range.end()) 541 if (char_range.end() == run.range.end())
467 UpdateRTLSegmentRanges(); 542 UpdateRTLSegmentRanges();
468 } 543 }
469 text_x_ += SkFloatToScalar(width); 544 text_x_ += SkFloatToScalar(width);
470 line_x_ += SkFloatToScalar(width); 545 line_x_ += SkFloatToScalar(width);
546
547 // Calculate |word_width_|.
548 if (words_) {
549 BreakList<size_t>::const_iterator word_for_range_start =
550 words_->GetBreak(char_range.start());
551 BreakList<size_t>::const_iterator word_for_range_end =
552 (char_range.end() == 0)
553 ? words_->breaks().begin()
554 : words_->GetBreak(char_range.end() - 1);
555
556 if (word_for_range_start == word_for_range_end) {
557 // Current word covers the entire char_range.
558 if (word_width_ == 0) {
559 Range glyph_range = run.CharRangeToGlyphRange(char_range);
560 SkScalar char_width = ((glyph_range.end() >= run.glyph_count)
561 ? SkFloatToScalar(run.width)
562 : run.positions[glyph_range.end()].x()) -
563 run.positions[glyph_range.start()].x();
564 word_width_ += char_width;
565 }
566 } else {
567 // Current char_range intersects with more than one word.
568 word_width_ = char_range.end() - word_for_range_end->first;
569 }
570 }
471 } 571 }
472 572
473 const SkScalar max_width_; 573 const SkScalar max_width_;
474 const int min_baseline_; 574 const int min_baseline_;
475 const float min_height_; 575 const float min_height_;
476 const bool multiline_; 576 const bool multiline_;
477 const WordWrapBehavior word_wrap_behavior_; 577 const WordWrapBehavior word_wrap_behavior_;
478 const base::string16& text_; 578 const base::string16& text_;
479 const BreakList<size_t>* const words_; 579 const BreakList<size_t>* const words_;
480 const internal::TextRunList& run_list_; 580 const internal::TextRunList& run_list_;
481 581
482 // Stores the resulting lines. 582 // Stores the resulting lines.
483 std::vector<internal::Line> lines_; 583 std::vector<internal::Line> lines_;
484 584
485 // Text space and line space x coordinates of the next segment to be added. 585 // Text space and line space x coordinates of the next segment to be added.
486 SkScalar text_x_; 586 SkScalar text_x_;
487 SkScalar line_x_; 587 SkScalar line_x_;
488 588
489 float max_descent_; 589 float max_descent_;
490 float max_ascent_; 590 float max_ascent_;
491 591
592 // Stores the position of the character that need to be processed when
593 // breaking runs into multiple lines.
594 size_t next_char_pos_;
595 // Stores the glyph width between the current position and the start position
596 // of the current word. If the current position is pointing to the first
597 // character of the current word, |word_width_| is set to 0.
598 SkScalar word_width_;
599
492 // Size of the multiline text, not including the currently processed line. 600 // Size of the multiline text, not including the currently processed line.
493 SizeF total_size_; 601 SizeF total_size_;
494 602
495 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. 603 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|.
496 std::vector<SegmentHandle> rtl_segments_; 604 std::vector<SegmentHandle> rtl_segments_;
497 605
498 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); 606 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker);
499 }; 607 };
500 608
501 // Function object for case insensitive string comparison. 609 // Function object for case insensitive string comparison.
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
653 void TextRunList::ComputePrecedingRunWidths() { 761 void TextRunList::ComputePrecedingRunWidths() {
654 // Precalculate run width information. 762 // Precalculate run width information.
655 width_ = 0.0f; 763 width_ = 0.0f;
656 for (size_t i = 0; i < runs_.size(); ++i) { 764 for (size_t i = 0; i < runs_.size(); ++i) {
657 TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; 765 TextRunHarfBuzz* run = runs_[visual_to_logical_[i]];
658 run->preceding_run_widths = width_; 766 run->preceding_run_widths = width_;
659 width_ += run->width; 767 width_ += run->width;
660 } 768 }
661 } 769 }
662 770
771 TextRunList::TextRunHarfBuzzIter TextRunList::GetRunAt(size_t position) const {
772 for (TextRunList::TextRunHarfBuzzIter iter = runs_.begin();
773 iter != runs_.end(); iter++) {
774 if ((*iter)->range.start() <= position && (*iter)->range.end() > position)
775 return iter;
776 }
777 return runs_.end();
778 }
779
663 } // namespace internal 780 } // namespace internal
664 781
665 RenderTextHarfBuzz::RenderTextHarfBuzz() 782 RenderTextHarfBuzz::RenderTextHarfBuzz()
666 : RenderText(), 783 : RenderText(),
667 update_layout_run_list_(false), 784 update_layout_run_list_(false),
668 update_display_run_list_(false), 785 update_display_run_list_(false),
669 update_grapheme_iterator_(false), 786 update_grapheme_iterator_(false),
670 update_display_text_(false), 787 update_display_text_(false),
671 glyph_width_for_test_(0u) { 788 glyph_width_for_test_(0u) {
672 set_truncate_length(kMaxTextLength); 789 set_truncate_length(kMaxTextLength);
(...skipping 806 matching lines...) Expand 10 before | Expand all | Expand 10 after
1479 DCHECK(!update_layout_run_list_); 1596 DCHECK(!update_layout_run_list_);
1480 DCHECK(!update_display_run_list_); 1597 DCHECK(!update_display_run_list_);
1481 return text_elided() ? display_run_list_.get() : &layout_run_list_; 1598 return text_elided() ? display_run_list_.get() : &layout_run_list_;
1482 } 1599 }
1483 1600
1484 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { 1601 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const {
1485 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); 1602 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList();
1486 } 1603 }
1487 1604
1488 } // namespace gfx 1605 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698