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 <map> | 8 #include <map> |
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/lazy_instance.h" | 13 #include "base/lazy_instance.h" |
14 #include "base/profiler/scoped_tracker.h" | 14 #include "base/profiler/scoped_tracker.h" |
15 #include "third_party/harfbuzz-ng/src/hb.h" | 15 #include "third_party/harfbuzz-ng/src/hb.h" |
16 #include "third_party/icu/source/common/unicode/ubidi.h" | 16 #include "third_party/icu/source/common/unicode/ubidi.h" |
17 #include "third_party/skia/include/core/SkColor.h" | 17 #include "third_party/skia/include/core/SkColor.h" |
18 #include "third_party/skia/include/core/SkTypeface.h" | 18 #include "third_party/skia/include/core/SkTypeface.h" |
19 #include "ui/gfx/canvas.h" | 19 #include "ui/gfx/canvas.h" |
20 #include "ui/gfx/font_fallback.h" | 20 #include "ui/gfx/font_fallback.h" |
21 #include "ui/gfx/font_render_params.h" | 21 #include "ui/gfx/font_render_params.h" |
22 #include "ui/gfx/geometry/size_conversions.h" | |
msw
2015/02/05 01:09:15
This is no longer needed if you no longer use gfx:
Jun Mukai
2015/02/05 19:48:31
Done.
| |
22 #include "ui/gfx/utf16_indexing.h" | 23 #include "ui/gfx/utf16_indexing.h" |
23 | 24 |
24 #if defined(OS_WIN) | 25 #if defined(OS_WIN) |
25 #include "ui/gfx/font_fallback_win.h" | 26 #include "ui/gfx/font_fallback_win.h" |
26 #endif | 27 #endif |
27 | 28 |
28 using gfx::internal::RangeF; | 29 using gfx::internal::RangeF; |
29 using gfx::internal::RoundRangeF; | 30 using gfx::internal::RoundRangeF; |
30 | 31 |
31 namespace gfx { | 32 namespace gfx { |
32 | 33 |
33 namespace { | 34 namespace { |
34 | 35 |
35 // Text length limit. Longer strings are slow and not fully tested. | 36 // Text length limit. Longer strings are slow and not fully tested. |
36 const size_t kMaxTextLength = 10000; | 37 const size_t kMaxTextLength = 10000; |
37 | 38 |
38 // The maximum number of scripts a Unicode character can belong to. This value | 39 // The maximum number of scripts a Unicode character can belong to. This value |
39 // is arbitrarily chosen to be a good limit because it is unlikely for a single | 40 // is arbitrarily chosen to be a good limit because it is unlikely for a single |
40 // character to belong to more scripts. | 41 // character to belong to more scripts. |
41 const size_t kMaxScripts = 5; | 42 const size_t kMaxScripts = 5; |
42 | 43 |
44 // The fixed width of charcters. This must not be set on the real environment. | |
msw
2015/02/05 01:09:15
nit: "glyphs" not the misspelled "charcters".
also
Jun Mukai
2015/02/05 19:48:31
Done.
| |
45 int g_glyph_width_for_test = 0; | |
46 | |
43 // Maps from code points to glyph indices in a font. | 47 // Maps from code points to glyph indices in a font. |
44 typedef std::map<uint32_t, uint16_t> GlyphCache; | 48 typedef std::map<uint32_t, uint16_t> GlyphCache; |
45 | 49 |
46 // Font data provider for HarfBuzz using Skia. Copied from Blink. | 50 // Font data provider for HarfBuzz using Skia. Copied from Blink. |
47 // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375 | 51 // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375 |
48 struct FontData { | 52 struct FontData { |
49 FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {} | 53 FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {} |
50 | 54 |
51 SkPaint paint_; | 55 SkPaint paint_; |
52 GlyphCache* glyph_cache_; | 56 GlyphCache* glyph_cache_; |
(...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
321 } | 325 } |
322 | 326 |
323 // Returns the boundary between a special and a regular character. Special | 327 // Returns the boundary between a special and a regular character. Special |
324 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. | 328 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. |
325 size_t FindRunBreakingCharacter(const base::string16& text, | 329 size_t FindRunBreakingCharacter(const base::string16& text, |
326 size_t run_start, | 330 size_t run_start, |
327 size_t run_break) { | 331 size_t run_break) { |
328 const int32 run_length = static_cast<int32>(run_break - run_start); | 332 const int32 run_length = static_cast<int32>(run_break - run_start); |
329 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, run_length); | 333 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, run_length); |
330 const UChar32 first_char = iter.get(); | 334 const UChar32 first_char = iter.get(); |
335 // The newline character should form a single run so that the line breaker | |
336 // can handle them easily. | |
337 if (first_char == '\n') | |
338 return run_start + 1; | |
339 | |
331 const UBlockCode first_block = ublock_getCode(first_char); | 340 const UBlockCode first_block = ublock_getCode(first_char); |
332 const bool first_block_unusual = IsUnusualBlockCode(first_block); | 341 const bool first_block_unusual = IsUnusualBlockCode(first_block); |
333 const bool first_bracket = IsBracket(first_char); | 342 const bool first_bracket = IsBracket(first_char); |
334 | 343 |
335 while (iter.Advance() && iter.array_pos() < run_length) { | 344 while (iter.Advance() && iter.array_pos() < run_length) { |
336 const UChar32 current_char = iter.get(); | 345 const UChar32 current_char = iter.get(); |
337 const UBlockCode current_block = ublock_getCode(current_char); | 346 const UBlockCode current_block = ublock_getCode(current_char); |
338 const bool block_break = current_block != first_block && | 347 const bool block_break = current_block != first_block && |
339 (first_block_unusual || IsUnusualBlockCode(current_block)); | 348 (first_block_unusual || IsUnusualBlockCode(current_block)); |
340 if (block_break || first_bracket != IsBracket(current_char)) | 349 if (block_break || current_char == '\n' || |
350 first_bracket != IsBracket(current_char)) { | |
341 return run_start + iter.array_pos(); | 351 return run_start + iter.array_pos(); |
352 } | |
342 } | 353 } |
343 return run_break; | 354 return run_break; |
344 } | 355 } |
345 | 356 |
346 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or | 357 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or |
347 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns | 358 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns |
348 // USCRIPT_INVALID_CODE. | 359 // USCRIPT_INVALID_CODE. |
349 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { | 360 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { |
350 if (first == second || | 361 if (first == second || |
351 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) { | 362 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
457 reversed ? elements_end - element : element - elements_begin); | 468 reversed ? elements_end - element : element - elements_begin); |
458 if (reversed) | 469 if (reversed) |
459 *glyphs = Range(glyphs->end(), glyphs->start()); | 470 *glyphs = Range(glyphs->end(), glyphs->start()); |
460 | 471 |
461 DCHECK(!chars->is_reversed()); | 472 DCHECK(!chars->is_reversed()); |
462 DCHECK(!chars->is_empty()); | 473 DCHECK(!chars->is_empty()); |
463 DCHECK(!glyphs->is_reversed()); | 474 DCHECK(!glyphs->is_reversed()); |
464 DCHECK(!glyphs->is_empty()); | 475 DCHECK(!glyphs->is_empty()); |
465 } | 476 } |
466 | 477 |
478 // Internal class to generate Line structures. If |multiline| is true, the text | |
479 // is broken into lines at |words| boundaries such that each line is no longer | |
480 // than |max_width|. If |multiline| is false, only outputs a single Line from | |
481 // the given runs. |min_baseline| and |min_height| are the minimum baseline and | |
482 // height for each line. | |
483 // TODO(ckocagil): Expose the interface of this class in the header and test | |
484 // this class directly. | |
485 class HarfBuzzLineBreaker { | |
486 public: | |
487 HarfBuzzLineBreaker(size_t max_width, | |
488 int min_baseline, | |
489 float min_height, | |
490 bool multiline, | |
491 const base::string16& text, | |
492 const BreakList<size_t>* words, | |
493 const ScopedVector<internal::TextRunHarfBuzz>& runs) | |
494 : max_width_((max_width == 0) ? std::numeric_limits<size_t>::max() | |
495 : max_width), | |
496 min_baseline_(min_baseline), | |
497 min_height_(min_height), | |
498 multiline_(multiline), | |
499 text_(text), | |
500 words_(words), | |
501 runs_(runs), | |
502 text_x_(0), | |
503 line_x_(0), | |
504 max_descent_(0), | |
505 min_ascent_(0) { | |
506 DCHECK_EQ(multiline_, (words_ != nullptr)); | |
507 AdvanceLine(); | |
508 } | |
509 | |
510 // Breaks the run at given |run_index| into Line structs. | |
511 void AddRun(int run_index) { | |
512 const internal::TextRunHarfBuzz* run = runs_[run_index]; | |
513 base::char16 first_char = text_[run->range.start()]; | |
514 if (multiline_ && U16_IS_SINGLE(first_char) && first_char == '\n') | |
msw
2015/02/05 01:09:15
nit q: Do we actually need to check U16_IS_SINGLE(
Jun Mukai
2015/02/05 19:48:31
removed.
| |
515 AdvanceLine(); | |
516 else if (multiline_ && (line_x_ + run->width > max_width_)) | |
517 BreakRun(run_index); | |
518 else | |
519 AddSegment(run_index, run->range, run->width); | |
520 } | |
521 | |
522 // Finishes line breaking and outputs the results. Can be called at most once. | |
523 void Finalize(std::vector<internal::Line>* lines, SizeF* size) { | |
524 DCHECK(!lines_.empty()); | |
525 // Add an empty line to finish the line size calculation and remove it. | |
526 AdvanceLine(); | |
527 lines_.pop_back(); | |
528 *size = total_size_; | |
529 lines->swap(lines_); | |
530 } | |
531 | |
532 private: | |
533 // A (line index, segment index) pair that specifies a segment in |lines_|. | |
534 typedef std::pair<size_t, size_t> SegmentHandle; | |
535 | |
536 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { | |
537 return &lines_[handle.first].segments[handle.second]; | |
538 } | |
539 | |
540 // Breaks a run into segments that fit in the last line in |lines_| and adds | |
541 // them. Adds a new Line to the back of |lines_| whenever a new segment can't | |
542 // be added without the Line's width exceeding |max_width_|. | |
543 void BreakRun(int run_index) { | |
544 const internal::TextRunHarfBuzz& run = *runs_[run_index]; | |
545 SkScalar width = 0; | |
546 size_t next_char = run.range.start(); | |
547 | |
548 // Break the run until it fits the current line. | |
549 while (next_char < run.range.end()) { | |
550 const size_t current_char = next_char; | |
551 const bool skip_line = | |
552 BreakRunAtWidth(run, current_char, &width, &next_char); | |
553 AddSegment(run_index, Range(current_char, next_char), | |
554 SkScalarToFloat(width)); | |
555 if (skip_line) | |
556 AdvanceLine(); | |
557 } | |
558 } | |
559 | |
560 // Starting from |start_char|, finds a suitable line break position at or | |
561 // before available width using word break. If the current position is at the | |
562 // beginning of a line, this function will not roll back to |start_char| and | |
563 // |*next_char| will be greater than |start_char| (to avoid constructing empty | |
564 // lines). | |
565 // Returns whether to skip the line before |*next_char|. | |
566 // TODO(ckocagil): Check clusters to avoid breaking ligatures and diacritics. | |
567 // TODO(ckocagil): We might have to reshape after breaking at ligatures. | |
568 // See whether resolving the TODO above resolves this too. | |
569 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. | |
570 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, | |
571 size_t start_char, | |
572 SkScalar* width, | |
573 size_t* next_char) { | |
574 DCHECK(words_); | |
575 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); | |
576 SkScalar available_width = SkIntToScalar(max_width_ - line_x_); | |
577 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); | |
578 BreakList<size_t>::const_iterator next_word = word + 1; | |
579 // Width from |std::max(word->first, start_char)| to the current character. | |
580 SkScalar word_width = 0; | |
581 *width = 0; | |
582 | |
583 for (size_t i = start_char; i < run.range.end(); ++i) { | |
584 // |word| holds the word boundary at or before |i|, and |next_word| holds | |
585 // the word boundary right after |i|. Advance both |word| and |next_word| | |
586 // when |i| reaches |next_word|. | |
587 if (next_word != words_->breaks().end() && i >= next_word->first) { | |
588 word = next_word++; | |
589 word_width = 0; | |
590 } | |
591 | |
592 Range glyph_range = run.CharRangeToGlyphRange(Range(i, i + 1)); | |
593 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | |
594 ? SkFloatToScalar(run.width) | |
595 : run.positions[glyph_range.end()].x()) - | |
596 run.positions[glyph_range.start()].x(); | |
597 | |
598 *width += char_width; | |
599 word_width += char_width; | |
600 | |
601 if (*width > available_width) { | |
602 if (line_x_ != 0 || word_width < *width) { | |
603 // Roll back one word. | |
604 *width -= word_width; | |
605 *next_char = std::max(word->first, start_char); | |
606 } else if (char_width < *width) { | |
607 // Roll back one character. | |
608 *width -= char_width; | |
609 *next_char = i; | |
610 } else { | |
611 // Continue from the next character. | |
612 *next_char = i + 1; | |
613 } | |
614 return true; | |
615 } | |
616 } | |
617 | |
618 *next_char = run.range.end(); | |
619 return false; | |
620 } | |
621 | |
622 // RTL runs are broken in logical order but displayed in visual order. To find | |
623 // the text-space coordinate (where it would fall in a single-line text) | |
624 // |x_range| of RTL segments, segment widths are applied in reverse order. | |
625 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. | |
626 void UpdateRTLSegmentRanges() { | |
627 if (rtl_segments_.empty()) | |
628 return; | |
629 int x = SegmentFromHandle(rtl_segments_[0])->x_range.start(); | |
630 for (size_t i = rtl_segments_.size(); i > 0; --i) { | |
631 internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]); | |
632 const size_t segment_width = segment->x_range.length(); | |
633 segment->x_range = Range(x, x + segment_width); | |
634 x += segment_width; | |
635 } | |
636 rtl_segments_.clear(); | |
637 } | |
638 | |
639 // Finishes the size calculations of the last Line in |lines_|. Adds a new | |
640 // Line to the back of |lines_|. | |
641 void AdvanceLine() { | |
642 if (!lines_.empty()) { | |
643 internal::Line* line = &lines_.back(); | |
644 line->size.set_height(std::max(min_height_, max_descent_ - min_ascent_)); | |
645 line->baseline = | |
646 std::max(min_baseline_, SkScalarRoundToInt(-min_ascent_)); | |
647 line->preceding_heights = std::ceil(total_size_.height()); | |
648 total_size_.set_height(total_size_.height() + line->size.height()); | |
649 total_size_.set_width(std::max(total_size_.width(), line->size.width())); | |
650 } | |
651 max_descent_ = 0; | |
652 min_ascent_ = 0; | |
653 line_x_ = 0; | |
654 lines_.push_back(internal::Line()); | |
655 } | |
656 | |
657 // Adds a new segment with the given properties to |lines_.back()|. | |
658 void AddSegment(int run_index, Range char_range, float width) { | |
659 if (char_range.is_empty()) { | |
660 DCHECK_EQ(width, 0); | |
661 return; | |
662 } | |
663 const internal::TextRunHarfBuzz& run = *runs_[run_index]; | |
664 | |
665 internal::LineSegment segment; | |
666 segment.run = run_index; | |
667 segment.char_range = char_range; | |
668 segment.x_range = Range(text_x_, text_x_ + width); | |
669 | |
670 internal::Line* line = &lines_.back(); | |
671 line->segments.push_back(segment); | |
672 | |
673 SkPaint paint; | |
674 paint.setTypeface(run.skia_face.get()); | |
675 paint.setTextSize(SkIntToScalar(run.font_size)); | |
676 paint.setAntiAlias(run.render_params.antialiasing); | |
677 SkPaint::FontMetrics metrics; | |
678 paint.getFontMetrics(&metrics); | |
679 | |
680 line->size.set_width(line->size.width() + width); | |
681 max_descent_ = std::max(max_descent_, metrics.fDescent); | |
682 min_ascent_ = std::min(min_ascent_, metrics.fAscent); | |
ckocagil
2015/02/05 04:21:33
Can you change |min_ascent_| to |max_ascent_|, mak
Jun Mukai
2015/02/05 19:48:32
Done.
| |
683 | |
684 if (run.is_rtl) { | |
685 rtl_segments_.push_back( | |
686 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | |
687 // If this is the last segment of an RTL run, reprocess the text-space x | |
688 // ranges of all segments from the run. | |
689 if (char_range.end() == run.range.end()) | |
690 UpdateRTLSegmentRanges(); | |
691 } | |
692 text_x_ += width; | |
693 line_x_ += width; | |
694 } | |
695 | |
696 const size_t max_width_; | |
697 const int min_baseline_; | |
698 const float min_height_; | |
699 const bool multiline_; | |
700 const base::string16& text_; | |
701 const BreakList<size_t>* const words_; | |
702 const ScopedVector<internal::TextRunHarfBuzz>& runs_; | |
703 | |
704 // Stores the resulting lines. | |
705 std::vector<internal::Line> lines_; | |
706 | |
707 // Text space and line space x coordinates of the next segment to be added. | |
708 int text_x_; | |
709 int line_x_; | |
710 | |
711 float max_descent_; | |
712 float min_ascent_; | |
713 | |
714 // Size of the multiline text, not including the currently processed line. | |
715 SizeF total_size_; | |
716 | |
717 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. | |
718 std::vector<SegmentHandle> rtl_segments_; | |
719 | |
720 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); | |
721 }; | |
722 | |
467 } // namespace | 723 } // namespace |
468 | 724 |
469 namespace internal { | 725 namespace internal { |
470 | 726 |
471 Range RoundRangeF(const RangeF& range_f) { | 727 Range RoundRangeF(const RangeF& range_f) { |
472 return Range(std::floor(range_f.first + 0.5f), | 728 return Range(std::floor(range_f.first + 0.5f), |
473 std::floor(range_f.second + 0.5f)); | 729 std::floor(range_f.second + 0.5f)); |
474 } | 730 } |
475 | 731 |
476 TextRunHarfBuzz::TextRunHarfBuzz() | 732 TextRunHarfBuzz::TextRunHarfBuzz() |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
596 return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz); | 852 return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz); |
597 } | 853 } |
598 | 854 |
599 Size RenderTextHarfBuzz::GetStringSize() { | 855 Size RenderTextHarfBuzz::GetStringSize() { |
600 const SizeF size_f = GetStringSizeF(); | 856 const SizeF size_f = GetStringSizeF(); |
601 return Size(std::ceil(size_f.width()), size_f.height()); | 857 return Size(std::ceil(size_f.width()), size_f.height()); |
602 } | 858 } |
603 | 859 |
604 SizeF RenderTextHarfBuzz::GetStringSizeF() { | 860 SizeF RenderTextHarfBuzz::GetStringSizeF() { |
605 EnsureLayout(); | 861 EnsureLayout(); |
606 return lines()[0].size; | 862 return total_size_; |
607 } | 863 } |
608 | 864 |
609 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { | 865 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { |
610 EnsureLayout(); | 866 EnsureLayout(); |
611 | 867 |
612 int x = ToTextPoint(point).x(); | 868 int x = ToTextPoint(point).x(); |
613 float offset = 0; | 869 float offset = 0; |
614 size_t run_index = GetRunContainingXCoord(x, &offset); | 870 size_t run_index = GetRunContainingXCoord(x, &offset); |
615 if (run_index >= runs_.size()) | 871 if (run_index >= runs_.size()) |
616 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); | 872 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); |
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
920 std::vector<internal::Line> empty_lines; | 1176 std::vector<internal::Line> empty_lines; |
921 set_lines(&empty_lines); | 1177 set_lines(&empty_lines); |
922 } | 1178 } |
923 | 1179 |
924 if (lines().empty()) { | 1180 if (lines().empty()) { |
925 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1181 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
926 tracked_objects::ScopedTracker tracking_profile2( | 1182 tracked_objects::ScopedTracker tracking_profile2( |
927 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1183 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
928 "431326 RenderTextHarfBuzz::EnsureLayout2")); | 1184 "431326 RenderTextHarfBuzz::EnsureLayout2")); |
929 | 1185 |
930 std::vector<internal::Line> lines; | 1186 HarfBuzzLineBreaker line_breaker( |
931 lines.push_back(internal::Line()); | 1187 display_rect().width(), font_list().GetBaseline(), |
932 lines[0].baseline = font_list().GetBaseline(); | 1188 font_list().GetHeight(), multiline(), GetLayoutText(), |
933 lines[0].size.set_height(font_list().GetHeight()); | 1189 multiline() ? &GetLineBreaks() : nullptr, runs_); |
934 | |
935 int current_x = 0; | |
936 SkPaint paint; | |
937 | 1190 |
938 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1191 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
939 tracked_objects::ScopedTracker tracking_profile3( | 1192 tracked_objects::ScopedTracker tracking_profile3( |
940 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1193 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
941 "431326 RenderTextHarfBuzz::EnsureLayout3")); | 1194 "431326 RenderTextHarfBuzz::EnsureLayout3")); |
942 | 1195 |
943 for (size_t i = 0; i < runs_.size(); ++i) { | 1196 for (size_t i = 0; i < runs_.size(); ++i) |
944 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; | 1197 line_breaker.AddRun(visual_to_logical_[i]); |
945 internal::LineSegment segment; | 1198 std::vector<internal::Line> lines; |
946 segment.x_range = Range(current_x, current_x + run.width); | 1199 line_breaker.Finalize(&lines, &total_size_); |
947 segment.char_range = run.range; | |
948 segment.run = i; | |
949 lines[0].segments.push_back(segment); | |
950 | |
951 paint.setTypeface(run.skia_face.get()); | |
952 paint.setTextSize(SkIntToScalar(run.font_size)); | |
953 paint.setAntiAlias(run.render_params.antialiasing); | |
954 SkPaint::FontMetrics metrics; | |
955 paint.getFontMetrics(&metrics); | |
956 | |
957 lines[0].size.set_width(lines[0].size.width() + run.width); | |
958 lines[0].size.set_height(std::max(lines[0].size.height(), | |
959 metrics.fDescent - metrics.fAscent)); | |
960 lines[0].baseline = std::max(lines[0].baseline, | |
961 SkScalarRoundToInt(-metrics.fAscent)); | |
962 } | |
963 | |
964 set_lines(&lines); | 1200 set_lines(&lines); |
965 } | 1201 } |
966 } | 1202 } |
967 | 1203 |
968 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { | 1204 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { |
969 DCHECK(!needs_layout_); | 1205 DCHECK(!needs_layout_); |
1206 if (lines().empty()) | |
1207 return; | |
1208 | |
970 internal::SkiaTextRenderer renderer(canvas); | 1209 internal::SkiaTextRenderer renderer(canvas); |
971 ApplyFadeEffects(&renderer); | 1210 ApplyFadeEffects(&renderer); |
972 ApplyTextShadows(&renderer); | 1211 ApplyTextShadows(&renderer); |
973 ApplyCompositionAndSelectionStyles(); | 1212 ApplyCompositionAndSelectionStyles(); |
974 | 1213 |
975 const Vector2d line_offset = GetLineOffset(0); | 1214 for (size_t i = 0; i < lines().size(); ++i) { |
976 for (size_t i = 0; i < runs_.size(); ++i) { | 1215 const internal::Line& line = lines()[i]; |
977 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; | 1216 const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline); |
978 renderer.SetTypeface(run.skia_face.get()); | 1217 SkScalar preceding_segment_widths = 0; |
979 renderer.SetTextSize(SkIntToScalar(run.font_size)); | 1218 for (const internal::LineSegment& segment : line.segments) { |
980 renderer.SetFontRenderParams(run.render_params, | 1219 const internal::TextRunHarfBuzz& run = *runs_[segment.run]; |
981 background_is_transparent()); | 1220 renderer.SetTypeface(run.skia_face.get()); |
1221 renderer.SetTextSize(SkIntToScalar(run.font_size)); | |
1222 renderer.SetFontRenderParams(run.render_params, | |
1223 background_is_transparent()); | |
1224 Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range); | |
1225 scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]); | |
1226 SkScalar segment_base_x = run.positions[glyphs_range.start()].x(); | |
1227 for (size_t j = 0; j < glyphs_range.length(); ++j) { | |
1228 positions[j] = run.positions[glyphs_range.start() + | |
1229 (glyphs_range.is_reversed() ? -j : j)]; | |
1230 positions[j].offset(SkIntToScalar(origin.x()) - segment_base_x + | |
msw
2015/02/05 01:09:15
optionaly nit: pre-calculate these constant offset
Jun Mukai
2015/02/05 19:48:31
Done.
| |
1231 preceding_segment_widths, | |
1232 SkIntToScalar(origin.y())); | |
1233 } | |
1234 for (BreakList<SkColor>::const_iterator it = | |
1235 colors().GetBreak(segment.char_range.start()); | |
1236 it != colors().breaks().end() && | |
1237 it->first < segment.char_range.end(); | |
1238 ++it) { | |
1239 const Range intersection = | |
1240 colors().GetRange(it).Intersect(segment.char_range); | |
1241 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | |
1242 // The range may be empty if a portion of a multi-character grapheme is | |
1243 // selected, yielding two colors for a single glyph. For now, this just | |
1244 // paints the glyph with a single style, but it should paint it twice, | |
1245 // clipped according to selection bounds. See http://crbug.com/366786 | |
1246 if (colored_glyphs.is_empty()) | |
1247 continue; | |
982 | 1248 |
983 Vector2d origin = line_offset + Vector2d(0, lines()[0].baseline); | 1249 renderer.SetForegroundColor(it->second); |
984 scoped_ptr<SkPoint[]> positions(new SkPoint[run.glyph_count]); | 1250 renderer.DrawPosText( |
985 for (size_t j = 0; j < run.glyph_count; ++j) { | 1251 &positions[colored_glyphs.start() - glyphs_range.start()], |
986 positions[j] = run.positions[j]; | 1252 &run.glyphs[colored_glyphs.start()], colored_glyphs.length()); |
987 positions[j].offset(SkIntToScalar(origin.x()) + run.preceding_run_widths, | 1253 int start_x = SkScalarRoundToInt( |
988 SkIntToScalar(origin.y())); | 1254 positions[colored_glyphs.start() - glyphs_range.start()].x()); |
989 } | 1255 int end_x = SkScalarRoundToInt( |
990 | 1256 (colored_glyphs.end() == run.glyph_count) |
991 for (BreakList<SkColor>::const_iterator it = | 1257 ? (segment.x_range.length() + preceding_segment_widths + |
992 colors().GetBreak(run.range.start()); | 1258 SkIntToScalar(origin.x())) |
993 it != colors().breaks().end() && it->first < run.range.end(); | 1259 : positions[colored_glyphs.end() - glyphs_range.start()].x()); |
994 ++it) { | 1260 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x, |
995 const Range intersection = colors().GetRange(it).Intersect(run.range); | 1261 run.underline, run.strike, |
996 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | 1262 run.diagonal_strike); |
997 // The range may be empty if a portion of a multi-character grapheme is | 1263 } |
998 // selected, yielding two colors for a single glyph. For now, this just | 1264 preceding_segment_widths += SkIntToScalar(segment.x_range.length()); |
999 // paints the glyph with a single style, but it should paint it twice, | |
1000 // clipped according to selection bounds. See http://crbug.com/366786 | |
1001 if (colored_glyphs.is_empty()) | |
1002 continue; | |
1003 | |
1004 renderer.SetForegroundColor(it->second); | |
1005 renderer.DrawPosText(&positions[colored_glyphs.start()], | |
1006 &run.glyphs[colored_glyphs.start()], | |
1007 colored_glyphs.length()); | |
1008 int start_x = SkScalarRoundToInt(positions[colored_glyphs.start()].x()); | |
1009 int end_x = SkScalarRoundToInt((colored_glyphs.end() == run.glyph_count) ? | |
1010 (run.width + run.preceding_run_widths + SkIntToScalar(origin.x())) : | |
1011 positions[colored_glyphs.end()].x()); | |
1012 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x, | |
1013 run.underline, run.strike, run.diagonal_strike); | |
1014 } | 1265 } |
1015 } | 1266 } |
1016 | 1267 |
1017 renderer.EndDiagonalStrike(); | 1268 renderer.EndDiagonalStrike(); |
1018 | 1269 |
1019 UndoCompositionAndSelectionStyles(); | 1270 UndoCompositionAndSelectionStyles(); |
1020 } | 1271 } |
1021 | 1272 |
1273 // static | |
1274 void RenderTextHarfBuzz::SetGlyphWidthForTest(int width) { | |
1275 g_glyph_width_for_test = width; | |
1276 } | |
1277 | |
1022 size_t RenderTextHarfBuzz::GetRunContainingCaret( | 1278 size_t RenderTextHarfBuzz::GetRunContainingCaret( |
1023 const SelectionModel& caret) const { | 1279 const SelectionModel& caret) const { |
1024 DCHECK(!needs_layout_); | 1280 DCHECK(!needs_layout_); |
1025 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); | 1281 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); |
1026 LogicalCursorDirection affinity = caret.caret_affinity(); | 1282 LogicalCursorDirection affinity = caret.caret_affinity(); |
1027 for (size_t run = 0; run < runs_.size(); ++run) { | 1283 for (size_t run = 0; run < runs_.size(); ++run) { |
1028 if (RangeContainsCaret(runs_[run]->range, layout_position, affinity)) | 1284 if (RangeContainsCaret(runs_[run]->range, layout_position, affinity)) |
1029 return run; | 1285 return run; |
1030 } | 1286 } |
1031 return runs_.size(); | 1287 return runs_.size(); |
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1321 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1577 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
1322 "431326 RenderTextHarfBuzz::ShapeRunWithFont3")); | 1578 "431326 RenderTextHarfBuzz::ShapeRunWithFont3")); |
1323 | 1579 |
1324 for (size_t i = 0; i < run->glyph_count; ++i) { | 1580 for (size_t i = 0; i < run->glyph_count; ++i) { |
1325 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max()); | 1581 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max()); |
1326 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); | 1582 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); |
1327 run->glyph_to_char[i] = infos[i].cluster; | 1583 run->glyph_to_char[i] = infos[i].cluster; |
1328 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); | 1584 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); |
1329 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); | 1585 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); |
1330 run->positions[i].set(run->width + x_offset, -y_offset); | 1586 run->positions[i].set(run->width + x_offset, -y_offset); |
1331 run->width += SkFixedToScalar(hb_positions[i].x_advance); | 1587 run->width += (g_glyph_width_for_test > 0) |
1588 ? SkIntToScalar(g_glyph_width_for_test) | |
1589 : SkFixedToScalar(hb_positions[i].x_advance); | |
1332 // Round run widths if subpixel positioning is off to match native behavior. | 1590 // Round run widths if subpixel positioning is off to match native behavior. |
1333 if (!run->render_params.subpixel_positioning) | 1591 if (!run->render_params.subpixel_positioning) |
1334 run->width = std::floor(run->width + 0.5f); | 1592 run->width = std::floor(run->width + 0.5f); |
1335 } | 1593 } |
1336 | 1594 |
1337 hb_buffer_destroy(buffer); | 1595 hb_buffer_destroy(buffer); |
1338 hb_font_destroy(harfbuzz_font); | 1596 hb_font_destroy(harfbuzz_font); |
1339 return true; | 1597 return true; |
1340 } | 1598 } |
1341 | 1599 |
1342 } // namespace gfx | 1600 } // namespace gfx |
OLD | NEW |