Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 27 matching lines...) Expand all Loading... | |
| 38 | 38 |
| 39 // Text length limit. Longer strings are slow and not fully tested. | 39 // Text length limit. Longer strings are slow and not fully tested. |
| 40 const size_t kMaxTextLength = 10000; | 40 const size_t kMaxTextLength = 10000; |
| 41 | 41 |
| 42 // The maximum number of scripts a Unicode character can belong to. This value | 42 // 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 | 43 // is arbitrarily chosen to be a good limit because it is unlikely for a single |
| 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, | |
| 49 // therefore it needs to be trigerred as fallbacks. See crbug.com/448909 | |
| 50 bool IsUnusualBlockCode(UBlockCode block_code) { | 48 bool IsUnusualBlockCode(UBlockCode block_code) { |
| 51 return block_code == UBLOCK_GEOMETRIC_SHAPES || | 49 return block_code == UBLOCK_GEOMETRIC_SHAPES || |
| 52 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS || | 50 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS; |
|
Jun Mukai
2015/05/11 23:21:40
ckocagil@ or msw@ -- is this function still necess
| |
| 53 block_code == UBLOCK_DINGBATS || | |
| 54 block_code == UBLOCK_EMOTICONS; | |
| 55 } | 51 } |
| 56 | 52 |
| 57 bool IsBracket(UChar32 character) { | 53 bool IsBracket(UChar32 character) { |
| 58 static const char kBrackets[] = { '(', ')', '{', '}', '<', '>', }; | 54 static const char kBrackets[] = { '(', ')', '{', '}', '<', '>', }; |
| 59 static const char* kBracketsEnd = kBrackets + arraysize(kBrackets); | 55 static const char* kBracketsEnd = kBrackets + arraysize(kBrackets); |
| 60 return std::find(kBrackets, kBracketsEnd, character) != kBracketsEnd; | 56 return std::find(kBrackets, kBracketsEnd, character) != kBracketsEnd; |
| 61 } | 57 } |
| 62 | 58 |
| 63 // Returns the boundary between a special and a regular character. Special | 59 // Returns the boundary between a special and a regular character. Special |
| 64 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. | 60 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 83 const bool block_break = current_block != first_block && | 79 const bool block_break = current_block != first_block && |
| 84 (first_block_unusual || IsUnusualBlockCode(current_block)); | 80 (first_block_unusual || IsUnusualBlockCode(current_block)); |
| 85 if (block_break || current_char == '\n' || | 81 if (block_break || current_char == '\n' || |
| 86 first_bracket != IsBracket(current_char)) { | 82 first_bracket != IsBracket(current_char)) { |
| 87 return run_start + iter.array_pos(); | 83 return run_start + iter.array_pos(); |
| 88 } | 84 } |
| 89 } | 85 } |
| 90 return run_break; | 86 return run_break; |
| 91 } | 87 } |
| 92 | 88 |
| 93 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or | 89 // If the given scripts match, returns the one that isn't USCRIPT_INHERITED, |
| 94 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns | 90 // i.e. the more specific one. Otherwise returns USCRIPT_INVALID_CODE. |
| 95 // USCRIPT_INVALID_CODE. | |
| 96 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { | 91 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { |
| 97 if (first == second || | 92 if (first == second || second == USCRIPT_INHERITED) |
| 98 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) { | |
| 99 return first; | 93 return first; |
| 100 } | 94 if (first == USCRIPT_INHERITED) |
| 101 if (first > USCRIPT_INVALID_CODE && first <= USCRIPT_INHERITED) | |
| 102 return second; | 95 return second; |
| 103 return USCRIPT_INVALID_CODE; | 96 return USCRIPT_INVALID_CODE; |
| 104 } | 97 } |
| 105 | 98 |
| 106 // Writes the script and the script extensions of the character with the | 99 // Writes the script and the script extensions of the character with the |
| 107 // Unicode |codepoint|. Returns the number of written scripts. | 100 // Unicode |codepoint|. Returns the number of written scripts. |
| 108 int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) { | 101 int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) { |
| 109 UErrorCode icu_error = U_ZERO_ERROR; | 102 UErrorCode icu_error = U_ZERO_ERROR; |
| 110 // ICU documentation incorrectly states that the result of | 103 // ICU documentation incorrectly states that the result of |
| 111 // |uscript_getScriptExtensions| will contain the regular script property. | 104 // |uscript_getScriptExtensions| will contain the regular script property. |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 159 UScriptCode* script) { | 152 UScriptCode* script) { |
| 160 DCHECK_GT(length, 0U); | 153 DCHECK_GT(length, 0U); |
| 161 | 154 |
| 162 UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE }; | 155 UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE }; |
| 163 | 156 |
| 164 base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length); | 157 base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length); |
| 165 size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts); | 158 size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts); |
| 166 *script = scripts[0]; | 159 *script = scripts[0]; |
| 167 | 160 |
| 168 while (char_iterator.Advance()) { | 161 while (char_iterator.Advance()) { |
| 162 // Special handling to merge white space into the previous run. | |
| 163 if (u_isUWhiteSpace(char_iterator.get())) | |
| 164 continue; | |
| 169 ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); | 165 ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); |
| 170 if (scripts_size == 0U) | 166 if (scripts_size == 0U) |
| 171 return char_iterator.array_pos(); | 167 return char_iterator.array_pos(); |
| 172 *script = scripts[0]; | 168 *script = scripts[0]; |
| 173 } | 169 } |
| 174 | 170 |
| 175 return length; | 171 return length; |
| 176 } | 172 } |
| 177 | 173 |
| 178 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without | 174 // 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 Loading... | |
| 232 min_baseline_(min_baseline), | 228 min_baseline_(min_baseline), |
| 233 min_height_(min_height), | 229 min_height_(min_height), |
| 234 multiline_(multiline), | 230 multiline_(multiline), |
| 235 word_wrap_behavior_(word_wrap_behavior), | 231 word_wrap_behavior_(word_wrap_behavior), |
| 236 text_(text), | 232 text_(text), |
| 237 words_(words), | 233 words_(words), |
| 238 run_list_(run_list), | 234 run_list_(run_list), |
| 239 text_x_(0), | 235 text_x_(0), |
| 240 line_x_(0), | 236 line_x_(0), |
| 241 max_descent_(0), | 237 max_descent_(0), |
| 242 max_ascent_(0) { | 238 max_ascent_(0), |
| 239 word_width_(0) { | |
| 243 DCHECK_EQ(multiline_, (words_ != nullptr)); | 240 DCHECK_EQ(multiline_, (words_ != nullptr)); |
| 244 AdvanceLine(); | 241 AdvanceLine(); |
| 245 } | 242 } |
| 246 | 243 |
| 247 // Breaks the run at given |run_index| into Line structs. | 244 // Breaks the run at given |run_index| into Line structs. |
| 248 void AddRun(int run_index) { | 245 void AddRun(int run_index) { |
| 249 const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index]; | 246 const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index]; |
| 250 base::char16 first_char = text_[run->range.start()]; | 247 base::char16 first_char = text_[run->range.start()]; |
| 251 if (multiline_ && first_char == '\n') { | 248 if (multiline_ && first_char == '\n') { |
| 252 AdvanceLine(); | 249 AdvanceLine(); |
| 253 } else if (multiline_ && (line_x_ + SkFloatToScalar(run->width)) > | 250 return; |
| 254 max_width_) { | 251 } |
| 252 | |
| 253 if (multiline_ && line_x_ != 0) { | |
|
Jun Mukai
2015/05/11 23:21:40
Why does this have to be here?
This break a run an
| |
| 254 // If the following word is not the first word in the current line and | |
| 255 // can't be fit into the current line, advance a new line. | |
| 256 // See kTestScenarios[2], kTestScenarios[7] and kTestScenarios[10] in | |
| 257 // RenderTextTest.Multiline_LineBreakerBehavior for example. | |
| 258 BreakList<size_t>::const_iterator word = | |
| 259 words_->GetBreak(run->range.start()); | |
| 260 if (run->range.start() == word->first && | |
| 261 line_x_ + GetWordWidth(word) > max_width_) { | |
| 262 AdvanceLine(); | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 if (multiline_ && (line_x_ + SkFloatToScalar(run->width)) > max_width_) { | |
| 255 BreakRun(run_index); | 267 BreakRun(run_index); |
| 256 } else { | 268 } else { |
| 257 AddSegment(run_index, run->range, run->width); | 269 AddSegment(run_index, run->range, run->width); |
| 258 } | 270 } |
| 259 } | 271 } |
| 260 | 272 |
| 261 // Finishes line breaking and outputs the results. Can be called at most once. | 273 // Finishes line breaking and outputs the results. Can be called at most once. |
| 262 void Finalize(std::vector<internal::Line>* lines, SizeF* size) { | 274 void Finalize(std::vector<internal::Line>* lines, SizeF* size) { |
| 263 DCHECK(!lines_.empty()); | 275 DCHECK(!lines_.empty()); |
| 264 // Add an empty line to finish the line size calculation and remove it. | 276 // Add an empty line to finish the line size calculation and remove it. |
| 265 AdvanceLine(); | 277 AdvanceLine(); |
| 266 lines_.pop_back(); | 278 lines_.pop_back(); |
| 267 *size = total_size_; | 279 *size = total_size_; |
| 268 lines->swap(lines_); | 280 lines->swap(lines_); |
| 269 } | 281 } |
| 270 | 282 |
| 271 private: | 283 private: |
| 272 // A (line index, segment index) pair that specifies a segment in |lines_|. | 284 // A (line index, segment index) pair that specifies a segment in |lines_|. |
| 273 typedef std::pair<size_t, size_t> SegmentHandle; | 285 typedef std::pair<size_t, size_t> SegmentHandle; |
| 274 | 286 |
| 275 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { | 287 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { |
| 276 return &lines_[handle.first].segments[handle.second]; | 288 return &lines_[handle.first].segments[handle.second]; |
| 277 } | 289 } |
| 278 | 290 |
| 291 // Gets the glyph width for |word|. | |
| 292 SkScalar GetWordWidth(BreakList<size_t>::const_iterator word) { | |
| 293 if (word == words_->breaks().end()) | |
| 294 return 0; | |
| 295 size_t word_st = word->first; | |
| 296 size_t word_en = | |
| 297 (word + 1 == words_->breaks().end()) ? text_.size() : (word + 1)->first; | |
| 298 internal::TextRunList::TextRunHarfBuzzIter run_st = | |
| 299 run_list_.GetRunAt(word_st); | |
| 300 internal::TextRunList::TextRunHarfBuzzIter run_en = | |
| 301 run_list_.GetRunAt(word_en); | |
| 302 SkScalar width = 0; | |
| 303 for (auto iter = run_st; iter != run_en; iter++) { | |
| 304 Range char_range = (*iter)->range.Intersect(Range(word_st, word_en)); | |
| 305 Range glyph_range = (*iter)->CharRangeToGlyphRange(char_range); | |
| 306 SkScalar char_width = ((glyph_range.end() >= (*iter)->glyph_count) | |
| 307 ? SkFloatToScalar((*iter)->width) | |
| 308 : (*iter)->positions[glyph_range.end()].x()) - | |
| 309 (*iter)->positions[glyph_range.start()].x(); | |
| 310 width += char_width; | |
| 311 } | |
| 312 return width; | |
| 313 } | |
| 314 | |
| 279 // Breaks a run into segments that fit in the last line in |lines_| and adds | 315 // 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 | 316 // 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_|. | 317 // be added without the Line's width exceeding |max_width_|. |
| 282 void BreakRun(int run_index) { | 318 void BreakRun(int run_index) { |
| 283 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 319 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); |
| 284 SkScalar width = 0; | 320 SkScalar width = 0; |
| 285 size_t next_char = run.range.start(); | 321 size_t next_char = run.range.start(); |
| 286 | 322 |
| 287 // Break the run until it fits the current line. | 323 // Break the run until it fits the current line. |
| 288 while (next_char < run.range.end()) { | 324 while (next_char < run.range.end()) { |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 310 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, | 346 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, |
| 311 size_t start_char, | 347 size_t start_char, |
| 312 SkScalar* width, | 348 SkScalar* width, |
| 313 size_t* end_char, | 349 size_t* end_char, |
| 314 size_t* next_char) { | 350 size_t* next_char) { |
| 315 DCHECK(words_); | 351 DCHECK(words_); |
| 316 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); | 352 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); |
| 317 SkScalar available_width = max_width_ - line_x_; | 353 SkScalar available_width = max_width_ - line_x_; |
| 318 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); | 354 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); |
| 319 BreakList<size_t>::const_iterator next_word = word + 1; | 355 BreakList<size_t>::const_iterator next_word = word + 1; |
| 320 // Width from |std::max(word->first, start_char)| to the current character. | 356 size_t word_end_pos = |
| 321 SkScalar word_width = 0; | 357 (next_word == words_->breaks().end()) ? text_.size() : next_word->first; |
| 358 word_width_ = (start_char == word->first) ? 0 : word_width_; | |
| 322 *width = 0; | 359 *width = 0; |
| 323 | 360 |
| 324 Range char_range; | 361 Range char_range; |
| 325 SkScalar truncated_width = 0; | 362 SkScalar truncated_width = 0; |
| 326 for (size_t i = start_char; i < run.range.end(); i += char_range.length()) { | 363 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 | 364 // |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| | 365 // the word boundary right after |i|. Advance both |word| and |next_word| |
| 329 // when |i| reaches |next_word|. | 366 // when |i| reaches |next_word|. |
| 330 if (next_word != words_->breaks().end() && i >= next_word->first) { | 367 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++; | 368 word = next_word++; |
| 341 word_width = 0; | 369 word_end_pos = (next_word == words_->breaks().end()) ? text_.size() |
| 370 : next_word->first; | |
| 371 word_width_ = 0; | |
| 342 } | 372 } |
| 343 | 373 |
| 344 Range glyph_range; | 374 Range glyph_range; |
| 345 run.GetClusterAt(i, &char_range, &glyph_range); | 375 run.GetClusterAt(i, &char_range, &glyph_range); |
| 346 DCHECK_LT(0U, char_range.length()); | 376 DCHECK_LT(0U, char_range.length()); |
| 347 | 377 |
| 348 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | 378 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) |
| 349 ? SkFloatToScalar(run.width) | 379 ? SkFloatToScalar(run.width) |
| 350 : run.positions[glyph_range.end()].x()) - | 380 : run.positions[glyph_range.end()].x()) - |
| 351 run.positions[glyph_range.start()].x(); | 381 run.positions[glyph_range.start()].x(); |
| 352 | 382 |
| 353 *width += char_width; | 383 *width += char_width; |
| 354 word_width += char_width; | 384 word_width_ += char_width; |
| 355 | 385 |
| 356 // TODO(mukai): implement ELIDE_LONG_WORDS. | 386 // TODO(mukai): implement ELIDE_LONG_WORDS. |
| 357 if (*width > available_width) { | 387 if (*width > available_width) { |
| 358 if (line_x_ != 0 || word_width < *width) { | 388 if ((line_x_ != 0 && word_width_ <= *width) || |
| 359 // Roll back one word. | 389 (line_x_ == 0 && word_width_ < *width)) { |
| 360 *width -= word_width; | 390 // If the run is not the first run in the current line (line_x_ != 0), |
| 391 // rool back one word and advance a new line if the current word can't | |
| 392 // be added into the current line without exceeding |available_width|. | |
| 393 // If the run is the first run in the current line (line_x_ == 0), | |
| 394 // don't roll back if the current word width is larger than the line | |
| 395 // width to avoid constructing empty lines. | |
| 396 // See kTestScenarios[0-1] in | |
| 397 // RenderTextTest.Multiline_LineBreakerBehavior. | |
| 398 *width -= word_width_; | |
| 361 *next_char = std::max(word->first, start_char); | 399 *next_char = std::max(word->first, start_char); |
| 362 *end_char = *next_char; | 400 *end_char = *next_char; |
| 401 word_width_ = 0; | |
| 363 return true; | 402 return true; |
| 364 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { | 403 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { |
| 365 if (char_width < *width) { | 404 if (char_width < *width || |
| 405 (word_width_ > *width && char_width <= *width)) { | |
| 366 // Roll back one character. | 406 // Roll back one character. |
| 407 // See kTestScenarios[8] for the first clause and kTestScenarios[9] | |
| 408 // for the second in RenderTextTest.Multiline_LineBreakerBehavior. | |
| 367 *width -= char_width; | 409 *width -= char_width; |
| 368 *next_char = i; | 410 *next_char = i; |
| 369 } else { | 411 } else { |
| 370 // Continue from the next character. | 412 // Continue from the next character. |
| 371 *next_char = i + char_range.length(); | 413 *next_char = i + char_range.length(); |
| 372 } | 414 } |
| 373 *end_char = *next_char; | 415 *end_char = *next_char; |
| 416 word_width_ = 0; | |
| 374 return true; | 417 return true; |
| 418 } else if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) { | |
| 419 *width = truncated_width; | |
| 420 *next_char = word_end_pos; | |
| 421 if (i < word_end_pos - 1) { | |
| 422 // Ignore all characters that belongs to the current word which has | |
| 423 // been truncated. Use kTestScenarios[5] as example. "that's good" | |
| 424 // has three runs: "that", "'", "s good". Suppose each line could | |
| 425 // fit into 4 characters, then the expected 2 lines should be: | |
| 426 // "that", "good", which means run "'" should be totally ignored and | |
| 427 // run "s good" should be partially ignored. | |
| 428 return false; | |
| 429 } else { | |
| 430 // Only advance a new line when the current word has been processed. | |
| 431 word_width_ = 0; | |
| 432 return true; | |
| 433 } | |
| 375 } | 434 } |
| 376 } else { | 435 } else { |
| 377 *end_char = char_range.end(); | 436 *end_char = char_range.end(); |
| 378 truncated_width = *width; | 437 truncated_width = *width; |
| 379 } | 438 } |
| 380 } | 439 } |
| 381 | 440 |
| 382 if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) | 441 if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) |
| 383 *width = truncated_width; | 442 *width = truncated_width; |
| 384 *end_char = *next_char = run.range.end(); | 443 *end_char = *next_char = run.range.end(); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 416 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); | 475 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); |
| 417 line->baseline = | 476 line->baseline = |
| 418 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); | 477 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); |
| 419 line->preceding_heights = std::ceil(total_size_.height()); | 478 line->preceding_heights = std::ceil(total_size_.height()); |
| 420 total_size_.set_height(total_size_.height() + line->size.height()); | 479 total_size_.set_height(total_size_.height() + line->size.height()); |
| 421 total_size_.set_width(std::max(total_size_.width(), line->size.width())); | 480 total_size_.set_width(std::max(total_size_.width(), line->size.width())); |
| 422 } | 481 } |
| 423 max_descent_ = 0; | 482 max_descent_ = 0; |
| 424 max_ascent_ = 0; | 483 max_ascent_ = 0; |
| 425 line_x_ = 0; | 484 line_x_ = 0; |
| 485 word_width_ = 0; | |
| 426 lines_.push_back(internal::Line()); | 486 lines_.push_back(internal::Line()); |
| 427 } | 487 } |
| 428 | 488 |
| 429 // Adds a new segment with the given properties to |lines_.back()|. | 489 // Adds a new segment with the given properties to |lines_.back()|. |
| 430 void AddSegment(int run_index, Range char_range, float width) { | 490 void AddSegment(int run_index, Range char_range, float width) { |
| 431 if (char_range.is_empty()) { | 491 if (char_range.is_empty()) { |
| 432 DCHECK_EQ(0, width); | 492 DCHECK_EQ(0, width); |
| 433 return; | 493 return; |
| 434 } | 494 } |
| 435 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 495 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 461 if (run.is_rtl) { | 521 if (run.is_rtl) { |
| 462 rtl_segments_.push_back( | 522 rtl_segments_.push_back( |
| 463 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | 523 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); |
| 464 // If this is the last segment of an RTL run, reprocess the text-space x | 524 // If this is the last segment of an RTL run, reprocess the text-space x |
| 465 // ranges of all segments from the run. | 525 // ranges of all segments from the run. |
| 466 if (char_range.end() == run.range.end()) | 526 if (char_range.end() == run.range.end()) |
| 467 UpdateRTLSegmentRanges(); | 527 UpdateRTLSegmentRanges(); |
| 468 } | 528 } |
| 469 text_x_ += SkFloatToScalar(width); | 529 text_x_ += SkFloatToScalar(width); |
| 470 line_x_ += SkFloatToScalar(width); | 530 line_x_ += SkFloatToScalar(width); |
| 531 | |
| 532 // Calculate |word_width_|. | |
|
Jun Mukai
2015/05/11 23:21:40
If you modify AddRun() to invoke always BreakRun()
| |
| 533 if (multiline_) { | |
| 534 BreakList<size_t>::const_iterator word_for_range_start = | |
| 535 words_->GetBreak(char_range.start()); | |
| 536 BreakList<size_t>::const_iterator word_for_range_end = | |
| 537 (char_range.end() == 0) ? words_->breaks().begin() | |
| 538 : words_->GetBreak(char_range.end() - 1); | |
| 539 | |
| 540 if (word_for_range_start == word_for_range_end) { | |
| 541 // Current word covers the entire char_range. | |
| 542 if (word_width_ == 0) { | |
| 543 Range glyph_range = run.CharRangeToGlyphRange(char_range); | |
| 544 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | |
| 545 ? SkFloatToScalar(run.width) | |
| 546 : run.positions[glyph_range.end()].x()) - | |
| 547 run.positions[glyph_range.start()].x(); | |
| 548 word_width_ += char_width; | |
| 549 } | |
| 550 } else { | |
| 551 // Current char_range intersects with more than one word. | |
| 552 word_width_ = char_range.end() - word_for_range_end->first; | |
| 553 } | |
| 554 } | |
| 471 } | 555 } |
| 472 | 556 |
| 473 const SkScalar max_width_; | 557 const SkScalar max_width_; |
| 474 const int min_baseline_; | 558 const int min_baseline_; |
| 475 const float min_height_; | 559 const float min_height_; |
| 476 const bool multiline_; | 560 const bool multiline_; |
| 477 const WordWrapBehavior word_wrap_behavior_; | 561 const WordWrapBehavior word_wrap_behavior_; |
| 478 const base::string16& text_; | 562 const base::string16& text_; |
| 479 const BreakList<size_t>* const words_; | 563 const BreakList<size_t>* const words_; |
| 480 const internal::TextRunList& run_list_; | 564 const internal::TextRunList& run_list_; |
| 481 | 565 |
| 482 // Stores the resulting lines. | 566 // Stores the resulting lines. |
| 483 std::vector<internal::Line> lines_; | 567 std::vector<internal::Line> lines_; |
| 484 | 568 |
| 485 // Text space and line space x coordinates of the next segment to be added. | 569 // Text space and line space x coordinates of the next segment to be added. |
| 486 SkScalar text_x_; | 570 SkScalar text_x_; |
| 487 SkScalar line_x_; | 571 SkScalar line_x_; |
| 488 | 572 |
| 489 float max_descent_; | 573 float max_descent_; |
| 490 float max_ascent_; | 574 float max_ascent_; |
| 491 | 575 |
| 576 // Stores the glyph width between the current position and the start position | |
| 577 // of the current word. If the current position is pointing to the first | |
| 578 // character of the current word, |word_width_| is set to 0. | |
| 579 SkScalar word_width_; | |
| 580 | |
| 492 // Size of the multiline text, not including the currently processed line. | 581 // Size of the multiline text, not including the currently processed line. |
| 493 SizeF total_size_; | 582 SizeF total_size_; |
| 494 | 583 |
| 495 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. | 584 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. |
| 496 std::vector<SegmentHandle> rtl_segments_; | 585 std::vector<SegmentHandle> rtl_segments_; |
| 497 | 586 |
| 498 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); | 587 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); |
| 499 }; | 588 }; |
| 500 | 589 |
| 501 // Function object for case insensitive string comparison. | 590 // Function object for case insensitive string comparison. |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 653 void TextRunList::ComputePrecedingRunWidths() { | 742 void TextRunList::ComputePrecedingRunWidths() { |
| 654 // Precalculate run width information. | 743 // Precalculate run width information. |
| 655 width_ = 0.0f; | 744 width_ = 0.0f; |
| 656 for (size_t i = 0; i < runs_.size(); ++i) { | 745 for (size_t i = 0; i < runs_.size(); ++i) { |
| 657 TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; | 746 TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; |
| 658 run->preceding_run_widths = width_; | 747 run->preceding_run_widths = width_; |
| 659 width_ += run->width; | 748 width_ += run->width; |
| 660 } | 749 } |
| 661 } | 750 } |
| 662 | 751 |
| 752 TextRunList::TextRunHarfBuzzIter TextRunList::GetRunAt(size_t position) const { | |
| 753 for (TextRunList::TextRunHarfBuzzIter iter = runs_.begin(); | |
| 754 iter != runs_.end(); iter++) { | |
| 755 if ((*iter)->range.start() <= position && (*iter)->range.end() > position) | |
| 756 return iter; | |
| 757 } | |
| 758 return runs_.end(); | |
| 759 } | |
| 760 | |
| 663 } // namespace internal | 761 } // namespace internal |
| 664 | 762 |
| 665 RenderTextHarfBuzz::RenderTextHarfBuzz() | 763 RenderTextHarfBuzz::RenderTextHarfBuzz() |
| 666 : RenderText(), | 764 : RenderText(), |
| 667 update_layout_run_list_(false), | 765 update_layout_run_list_(false), |
| 668 update_display_run_list_(false), | 766 update_display_run_list_(false), |
| 669 update_grapheme_iterator_(false), | 767 update_grapheme_iterator_(false), |
| 670 update_display_text_(false), | 768 update_display_text_(false), |
| 671 glyph_width_for_test_(0u) { | 769 glyph_width_for_test_(0u) { |
| 672 set_truncate_length(kMaxTextLength); | 770 set_truncate_length(kMaxTextLength); |
| (...skipping 806 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1479 DCHECK(!update_layout_run_list_); | 1577 DCHECK(!update_layout_run_list_); |
| 1480 DCHECK(!update_display_run_list_); | 1578 DCHECK(!update_display_run_list_); |
| 1481 return text_elided() ? display_run_list_.get() : &layout_run_list_; | 1579 return text_elided() ? display_run_list_.get() : &layout_run_list_; |
| 1482 } | 1580 } |
| 1483 | 1581 |
| 1484 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 1582 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { |
| 1485 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 1583 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); |
| 1486 } | 1584 } |
| 1487 | 1585 |
| 1488 } // namespace gfx | 1586 } // namespace gfx |
| OLD | NEW |