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

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

Issue 882643005: Add multiline support to RenderTextHarfBuzz. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix Created 5 years, 10 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 <map> 8 #include <map>
9 9
10 #include "base/i18n/bidi_line_iterator.h" 10 #include "base/i18n/bidi_line_iterator.h"
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 } 321 }
322 322
323 // Returns the boundary between a special and a regular character. Special 323 // Returns the boundary between a special and a regular character. Special
324 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. 324 // characters are brackets or characters that satisfy |IsUnusualBlockCode|.
325 size_t FindRunBreakingCharacter(const base::string16& text, 325 size_t FindRunBreakingCharacter(const base::string16& text,
326 size_t run_start, 326 size_t run_start,
327 size_t run_break) { 327 size_t run_break) {
328 const int32 run_length = static_cast<int32>(run_break - run_start); 328 const int32 run_length = static_cast<int32>(run_break - run_start);
329 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, run_length); 329 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, run_length);
330 const UChar32 first_char = iter.get(); 330 const UChar32 first_char = iter.get();
331 // The newline character should form a single run so that the line breaker
332 // can handle them easily.
333 if (first_char == '\n')
334 return run_start + 1;
335
331 const UBlockCode first_block = ublock_getCode(first_char); 336 const UBlockCode first_block = ublock_getCode(first_char);
332 const bool first_block_unusual = IsUnusualBlockCode(first_block); 337 const bool first_block_unusual = IsUnusualBlockCode(first_block);
333 const bool first_bracket = IsBracket(first_char); 338 const bool first_bracket = IsBracket(first_char);
334 339
335 while (iter.Advance() && iter.array_pos() < run_length) { 340 while (iter.Advance() && iter.array_pos() < run_length) {
336 const UChar32 current_char = iter.get(); 341 const UChar32 current_char = iter.get();
337 const UBlockCode current_block = ublock_getCode(current_char); 342 const UBlockCode current_block = ublock_getCode(current_char);
338 const bool block_break = current_block != first_block && 343 const bool block_break = current_block != first_block &&
339 (first_block_unusual || IsUnusualBlockCode(current_block)); 344 (first_block_unusual || IsUnusualBlockCode(current_block));
340 if (block_break || first_bracket != IsBracket(current_char)) 345 if (block_break || current_char == '\n' ||
346 first_bracket != IsBracket(current_char)) {
341 return run_start + iter.array_pos(); 347 return run_start + iter.array_pos();
348 }
342 } 349 }
343 return run_break; 350 return run_break;
344 } 351 }
345 352
346 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or 353 // 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 354 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns
348 // USCRIPT_INVALID_CODE. 355 // USCRIPT_INVALID_CODE.
349 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { 356 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) {
350 if (first == second || 357 if (first == second ||
351 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) { 358 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) {
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
457 reversed ? elements_end - element : element - elements_begin); 464 reversed ? elements_end - element : element - elements_begin);
458 if (reversed) 465 if (reversed)
459 *glyphs = Range(glyphs->end(), glyphs->start()); 466 *glyphs = Range(glyphs->end(), glyphs->start());
460 467
461 DCHECK(!chars->is_reversed()); 468 DCHECK(!chars->is_reversed());
462 DCHECK(!chars->is_empty()); 469 DCHECK(!chars->is_empty());
463 DCHECK(!glyphs->is_reversed()); 470 DCHECK(!glyphs->is_reversed());
464 DCHECK(!glyphs->is_empty()); 471 DCHECK(!glyphs->is_empty());
465 } 472 }
466 473
474 // Internal class to generate Line structures. If |multiline| is true, the text
475 // is broken into lines at |words| boundaries such that each line is no longer
476 // than |max_width|. If |multiline| is false, only outputs a single Line from
477 // the given runs. |min_baseline| and |min_height| are the minimum baseline and
478 // height for each line.
479 // TODO(ckocagil): Expose the interface of this class in the header and test
480 // this class directly.
481 class HarfBuzzLineBreaker {
482 public:
483 HarfBuzzLineBreaker(size_t max_width,
484 int min_baseline,
485 float min_height,
486 bool multiline,
487 const base::string16& text,
488 const BreakList<size_t>* words,
489 const ScopedVector<internal::TextRunHarfBuzz>& runs)
490 : max_width_((max_width == 0) ? std::numeric_limits<size_t>::max()
491 : max_width),
492 min_baseline_(min_baseline),
493 min_height_(min_height),
494 multiline_(multiline),
495 text_(text),
496 words_(words),
497 runs_(runs),
498 text_x_(0),
499 line_x_(0),
500 max_descent_(0),
501 max_ascent_(0) {
502 DCHECK_EQ(multiline_, (words_ != nullptr));
503 AdvanceLine();
504 }
505
506 // Breaks the run at given |run_index| into Line structs.
507 void AddRun(int run_index) {
508 const internal::TextRunHarfBuzz* run = runs_[run_index];
509 base::char16 first_char = text_[run->range.start()];
510 if (multiline_ && first_char == '\n')
511 AdvanceLine();
512 else if (multiline_ && (line_x_ + run->width) > max_width_)
513 BreakRun(run_index);
514 else
515 AddSegment(run_index, run->range, run->width);
516 }
517
518 // Finishes line breaking and outputs the results. Can be called at most once.
519 void Finalize(std::vector<internal::Line>* lines, SizeF* size) {
520 DCHECK(!lines_.empty());
521 // Add an empty line to finish the line size calculation and remove it.
522 AdvanceLine();
523 lines_.pop_back();
524 *size = total_size_;
525 lines->swap(lines_);
526 }
527
528 private:
529 // A (line index, segment index) pair that specifies a segment in |lines_|.
530 typedef std::pair<size_t, size_t> SegmentHandle;
531
532 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) {
533 return &lines_[handle.first].segments[handle.second];
534 }
535
536 // Breaks a run into segments that fit in the last line in |lines_| and adds
537 // them. Adds a new Line to the back of |lines_| whenever a new segment can't
538 // be added without the Line's width exceeding |max_width_|.
539 void BreakRun(int run_index) {
540 const internal::TextRunHarfBuzz& run = *runs_[run_index];
541 SkScalar width = 0;
542 size_t next_char = run.range.start();
543
544 // Break the run until it fits the current line.
545 while (next_char < run.range.end()) {
546 const size_t current_char = next_char;
547 const bool skip_line =
548 BreakRunAtWidth(run, current_char, &width, &next_char);
549 AddSegment(run_index, Range(current_char, next_char),
550 SkScalarToFloat(width));
551 if (skip_line)
552 AdvanceLine();
553 }
554 }
555
556 // Starting from |start_char|, finds a suitable line break position at or
557 // before available width using word break. If the current position is at the
558 // beginning of a line, this function will not roll back to |start_char| and
559 // |*next_char| will be greater than |start_char| (to avoid constructing empty
560 // lines).
561 // Returns whether to skip the line before |*next_char|.
562 // TODO(ckocagil): Check clusters to avoid breaking ligatures and diacritics.
563 // TODO(ckocagil): We might have to reshape after breaking at ligatures.
564 // See whether resolving the TODO above resolves this too.
565 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines.
566 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run,
567 size_t start_char,
568 SkScalar* width,
569 size_t* next_char) {
570 DCHECK(words_);
571 DCHECK(run.range.Contains(Range(start_char, start_char + 1)));
572 SkScalar available_width = SkIntToScalar(max_width_ - line_x_);
573 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char);
574 BreakList<size_t>::const_iterator next_word = word + 1;
575 // Width from |std::max(word->first, start_char)| to the current character.
576 SkScalar word_width = 0;
577 *width = 0;
578
579 for (size_t i = start_char; i < run.range.end(); ++i) {
580 // |word| holds the word boundary at or before |i|, and |next_word| holds
581 // the word boundary right after |i|. Advance both |word| and |next_word|
582 // when |i| reaches |next_word|.
583 if (next_word != words_->breaks().end() && i >= next_word->first) {
584 word = next_word++;
585 word_width = 0;
586 }
587
588 Range glyph_range = run.CharRangeToGlyphRange(Range(i, i + 1));
589 SkScalar char_width = ((glyph_range.end() >= run.glyph_count)
590 ? SkFloatToScalar(run.width)
591 : run.positions[glyph_range.end()].x()) -
592 run.positions[glyph_range.start()].x();
593
594 *width += char_width;
595 word_width += char_width;
596
597 if (*width > available_width) {
598 if (line_x_ != 0 || word_width < *width) {
599 // Roll back one word.
600 *width -= word_width;
601 *next_char = std::max(word->first, start_char);
602 } else if (char_width < *width) {
603 // Roll back one character.
604 *width -= char_width;
605 *next_char = i;
606 } else {
607 // Continue from the next character.
608 *next_char = i + 1;
609 }
610 return true;
611 }
612 }
613
614 *next_char = run.range.end();
615 return false;
616 }
617
618 // RTL runs are broken in logical order but displayed in visual order. To find
619 // the text-space coordinate (where it would fall in a single-line text)
620 // |x_range| of RTL segments, segment widths are applied in reverse order.
621 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}.
622 void UpdateRTLSegmentRanges() {
623 if (rtl_segments_.empty())
624 return;
625 int x = SegmentFromHandle(rtl_segments_[0])->x_range.start();
626 for (size_t i = rtl_segments_.size(); i > 0; --i) {
627 internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]);
628 const size_t segment_width = segment->x_range.length();
629 segment->x_range = Range(x, x + segment_width);
630 x += segment_width;
631 }
632 rtl_segments_.clear();
633 }
634
635 // Finishes the size calculations of the last Line in |lines_|. Adds a new
636 // Line to the back of |lines_|.
637 void AdvanceLine() {
638 if (!lines_.empty()) {
639 internal::Line* line = &lines_.back();
640 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_));
641 line->baseline =
642 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_));
643 line->preceding_heights = std::ceil(total_size_.height());
644 total_size_.set_height(total_size_.height() + line->size.height());
645 total_size_.set_width(std::max(total_size_.width(), line->size.width()));
646 }
647 max_descent_ = 0;
648 max_ascent_ = 0;
649 line_x_ = 0;
650 lines_.push_back(internal::Line());
651 }
652
653 // Adds a new segment with the given properties to |lines_.back()|.
654 void AddSegment(int run_index, Range char_range, float width) {
655 if (char_range.is_empty()) {
656 DCHECK_EQ(0, width);
657 return;
658 }
659 const internal::TextRunHarfBuzz& run = *runs_[run_index];
660
661 internal::LineSegment segment;
662 segment.run = run_index;
663 segment.char_range = char_range;
664 segment.x_range = Range(text_x_, text_x_ + width);
665
666 internal::Line* line = &lines_.back();
667 line->segments.push_back(segment);
668
669 SkPaint paint;
670 paint.setTypeface(run.skia_face.get());
671 paint.setTextSize(SkIntToScalar(run.font_size));
672 paint.setAntiAlias(run.render_params.antialiasing);
673 SkPaint::FontMetrics metrics;
674 paint.getFontMetrics(&metrics);
675
676 line->size.set_width(line->size.width() + width);
677 max_descent_ = std::max(max_descent_, metrics.fDescent);
678 // fAscent is always negative.
679 max_ascent_ = std::max(max_ascent_, -metrics.fAscent);
680
681 if (run.is_rtl) {
682 rtl_segments_.push_back(
683 SegmentHandle(lines_.size() - 1, line->segments.size() - 1));
684 // If this is the last segment of an RTL run, reprocess the text-space x
685 // ranges of all segments from the run.
686 if (char_range.end() == run.range.end())
687 UpdateRTLSegmentRanges();
688 }
689 text_x_ += width;
690 line_x_ += width;
691 }
692
693 const size_t max_width_;
694 const int min_baseline_;
695 const float min_height_;
696 const bool multiline_;
697 const base::string16& text_;
698 const BreakList<size_t>* const words_;
699 const ScopedVector<internal::TextRunHarfBuzz>& runs_;
700
701 // Stores the resulting lines.
702 std::vector<internal::Line> lines_;
703
704 // Text space and line space x coordinates of the next segment to be added.
705 int text_x_;
706 int line_x_;
707
708 float max_descent_;
709 float max_ascent_;
710
711 // Size of the multiline text, not including the currently processed line.
712 SizeF total_size_;
713
714 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|.
715 std::vector<SegmentHandle> rtl_segments_;
716
717 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker);
718 };
719
467 } // namespace 720 } // namespace
468 721
469 namespace internal { 722 namespace internal {
470 723
471 Range RoundRangeF(const RangeF& range_f) { 724 Range RoundRangeF(const RangeF& range_f) {
472 return Range(std::floor(range_f.first + 0.5f), 725 return Range(std::floor(range_f.first + 0.5f),
473 std::floor(range_f.second + 0.5f)); 726 std::floor(range_f.second + 0.5f));
474 } 727 }
475 728
476 TextRunHarfBuzz::TextRunHarfBuzz() 729 TextRunHarfBuzz::TextRunHarfBuzz()
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
579 } 832 }
580 833
581 return RangeF(preceding_run_widths + cluster_begin_x, 834 return RangeF(preceding_run_widths + cluster_begin_x,
582 preceding_run_widths + cluster_end_x); 835 preceding_run_widths + cluster_end_x);
583 } 836 }
584 837
585 } // namespace internal 838 } // namespace internal
586 839
587 RenderTextHarfBuzz::RenderTextHarfBuzz() 840 RenderTextHarfBuzz::RenderTextHarfBuzz()
588 : RenderText(), 841 : RenderText(),
589 needs_layout_(false) { 842 needs_layout_(false),
843 glyph_width_for_test_(0u) {
590 set_truncate_length(kMaxTextLength); 844 set_truncate_length(kMaxTextLength);
591 } 845 }
592 846
593 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} 847 RenderTextHarfBuzz::~RenderTextHarfBuzz() {}
594 848
595 scoped_ptr<RenderText> RenderTextHarfBuzz::CreateInstanceOfSameType() const { 849 scoped_ptr<RenderText> RenderTextHarfBuzz::CreateInstanceOfSameType() const {
596 return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz); 850 return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz);
597 } 851 }
598 852
599 Size RenderTextHarfBuzz::GetStringSize() { 853 Size RenderTextHarfBuzz::GetStringSize() {
600 const SizeF size_f = GetStringSizeF(); 854 const SizeF size_f = GetStringSizeF();
601 return Size(std::ceil(size_f.width()), size_f.height()); 855 return Size(std::ceil(size_f.width()), size_f.height());
602 } 856 }
603 857
604 SizeF RenderTextHarfBuzz::GetStringSizeF() { 858 SizeF RenderTextHarfBuzz::GetStringSizeF() {
605 EnsureLayout(); 859 EnsureLayout();
606 return lines()[0].size; 860 return total_size_;
607 } 861 }
608 862
609 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { 863 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) {
610 EnsureLayout(); 864 EnsureLayout();
611 865
612 int x = ToTextPoint(point).x(); 866 int x = ToTextPoint(point).x();
613 float offset = 0; 867 float offset = 0;
614 size_t run_index = GetRunContainingXCoord(x, &offset); 868 size_t run_index = GetRunContainingXCoord(x, &offset);
615 if (run_index >= runs_.size()) 869 if (run_index >= runs_.size())
616 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); 870 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
920 std::vector<internal::Line> empty_lines; 1174 std::vector<internal::Line> empty_lines;
921 set_lines(&empty_lines); 1175 set_lines(&empty_lines);
922 } 1176 }
923 1177
924 if (lines().empty()) { 1178 if (lines().empty()) {
925 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. 1179 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
926 tracked_objects::ScopedTracker tracking_profile2( 1180 tracked_objects::ScopedTracker tracking_profile2(
927 FROM_HERE_WITH_EXPLICIT_FUNCTION( 1181 FROM_HERE_WITH_EXPLICIT_FUNCTION(
928 "431326 RenderTextHarfBuzz::EnsureLayout2")); 1182 "431326 RenderTextHarfBuzz::EnsureLayout2"));
929 1183
930 std::vector<internal::Line> lines; 1184 HarfBuzzLineBreaker line_breaker(
931 lines.push_back(internal::Line()); 1185 display_rect().width(), font_list().GetBaseline(),
932 lines[0].baseline = font_list().GetBaseline(); 1186 font_list().GetHeight(), multiline(), GetLayoutText(),
933 lines[0].size.set_height(font_list().GetHeight()); 1187 multiline() ? &GetLineBreaks() : nullptr, runs_);
934
935 int current_x = 0;
936 SkPaint paint;
937 1188
938 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. 1189 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
939 tracked_objects::ScopedTracker tracking_profile3( 1190 tracked_objects::ScopedTracker tracking_profile3(
940 FROM_HERE_WITH_EXPLICIT_FUNCTION( 1191 FROM_HERE_WITH_EXPLICIT_FUNCTION(
941 "431326 RenderTextHarfBuzz::EnsureLayout3")); 1192 "431326 RenderTextHarfBuzz::EnsureLayout3"));
942 1193
943 for (size_t i = 0; i < runs_.size(); ++i) { 1194 for (size_t i = 0; i < runs_.size(); ++i)
944 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; 1195 line_breaker.AddRun(visual_to_logical_[i]);
945 internal::LineSegment segment; 1196 std::vector<internal::Line> lines;
946 segment.x_range = Range(current_x, current_x + run.width); 1197 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); 1198 set_lines(&lines);
965 } 1199 }
966 } 1200 }
967 1201
968 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { 1202 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) {
969 DCHECK(!needs_layout_); 1203 DCHECK(!needs_layout_);
1204 if (lines().empty())
1205 return;
1206
970 internal::SkiaTextRenderer renderer(canvas); 1207 internal::SkiaTextRenderer renderer(canvas);
971 ApplyFadeEffects(&renderer); 1208 ApplyFadeEffects(&renderer);
972 ApplyTextShadows(&renderer); 1209 ApplyTextShadows(&renderer);
973 ApplyCompositionAndSelectionStyles(); 1210 ApplyCompositionAndSelectionStyles();
974 1211
975 const Vector2d line_offset = GetLineOffset(0); 1212 for (size_t i = 0; i < lines().size(); ++i) {
976 for (size_t i = 0; i < runs_.size(); ++i) { 1213 const internal::Line& line = lines()[i];
977 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; 1214 const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline);
978 renderer.SetTypeface(run.skia_face.get()); 1215 SkScalar preceding_segment_widths = 0;
979 renderer.SetTextSize(SkIntToScalar(run.font_size)); 1216 for (const internal::LineSegment& segment : line.segments) {
980 renderer.SetFontRenderParams(run.render_params, 1217 const internal::TextRunHarfBuzz& run = *runs_[segment.run];
981 background_is_transparent()); 1218 renderer.SetTypeface(run.skia_face.get());
1219 renderer.SetTextSize(SkIntToScalar(run.font_size));
1220 renderer.SetFontRenderParams(run.render_params,
1221 background_is_transparent());
1222 Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range);
1223 scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]);
1224 SkScalar offset_x =
1225 preceding_segment_widths - run.positions[glyphs_range.start()].x();
1226 for (size_t j = 0; j < glyphs_range.length(); ++j) {
1227 positions[j] = run.positions[glyphs_range.start() +
1228 (glyphs_range.is_reversed() ? -j : j)];
1229 positions[j].offset(SkIntToScalar(origin.x()) + offset_x,
1230 SkIntToScalar(origin.y()));
1231 }
1232 for (BreakList<SkColor>::const_iterator it =
1233 colors().GetBreak(segment.char_range.start());
1234 it != colors().breaks().end() &&
1235 it->first < segment.char_range.end();
1236 ++it) {
1237 const Range intersection =
1238 colors().GetRange(it).Intersect(segment.char_range);
1239 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection);
1240 // The range may be empty if a portion of a multi-character grapheme is
1241 // selected, yielding two colors for a single glyph. For now, this just
1242 // paints the glyph with a single style, but it should paint it twice,
1243 // clipped according to selection bounds. See http://crbug.com/366786
1244 if (colored_glyphs.is_empty())
1245 continue;
982 1246
983 Vector2d origin = line_offset + Vector2d(0, lines()[0].baseline); 1247 renderer.SetForegroundColor(it->second);
984 scoped_ptr<SkPoint[]> positions(new SkPoint[run.glyph_count]); 1248 renderer.DrawPosText(
985 for (size_t j = 0; j < run.glyph_count; ++j) { 1249 &positions[colored_glyphs.start() - glyphs_range.start()],
986 positions[j] = run.positions[j]; 1250 &run.glyphs[colored_glyphs.start()], colored_glyphs.length());
987 positions[j].offset(SkIntToScalar(origin.x()) + run.preceding_run_widths, 1251 int start_x = SkScalarRoundToInt(
988 SkIntToScalar(origin.y())); 1252 positions[colored_glyphs.start() - glyphs_range.start()].x());
989 } 1253 int end_x = SkScalarRoundToInt(
990 1254 (colored_glyphs.end() == run.glyph_count)
991 for (BreakList<SkColor>::const_iterator it = 1255 ? (segment.x_range.length() + preceding_segment_widths +
992 colors().GetBreak(run.range.start()); 1256 SkIntToScalar(origin.x()))
993 it != colors().breaks().end() && it->first < run.range.end(); 1257 : positions[colored_glyphs.end() - glyphs_range.start()].x());
994 ++it) { 1258 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x,
995 const Range intersection = colors().GetRange(it).Intersect(run.range); 1259 run.underline, run.strike,
996 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); 1260 run.diagonal_strike);
997 // The range may be empty if a portion of a multi-character grapheme is 1261 }
998 // selected, yielding two colors for a single glyph. For now, this just 1262 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 } 1263 }
1015 } 1264 }
1016 1265
1017 renderer.EndDiagonalStrike(); 1266 renderer.EndDiagonalStrike();
1018 1267
1019 UndoCompositionAndSelectionStyles(); 1268 UndoCompositionAndSelectionStyles();
1020 } 1269 }
1021 1270
1022 size_t RenderTextHarfBuzz::GetRunContainingCaret( 1271 size_t RenderTextHarfBuzz::GetRunContainingCaret(
1023 const SelectionModel& caret) const { 1272 const SelectionModel& caret) const {
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after
1321 FROM_HERE_WITH_EXPLICIT_FUNCTION( 1570 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1322 "431326 RenderTextHarfBuzz::ShapeRunWithFont3")); 1571 "431326 RenderTextHarfBuzz::ShapeRunWithFont3"));
1323 1572
1324 for (size_t i = 0; i < run->glyph_count; ++i) { 1573 for (size_t i = 0; i < run->glyph_count; ++i) {
1325 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max()); 1574 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max());
1326 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); 1575 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint);
1327 run->glyph_to_char[i] = infos[i].cluster; 1576 run->glyph_to_char[i] = infos[i].cluster;
1328 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); 1577 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset);
1329 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); 1578 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset);
1330 run->positions[i].set(run->width + x_offset, -y_offset); 1579 run->positions[i].set(run->width + x_offset, -y_offset);
1331 run->width += SkFixedToScalar(hb_positions[i].x_advance); 1580 run->width += (glyph_width_for_test_ > 0)
1581 ? SkIntToScalar(glyph_width_for_test_)
1582 : SkFixedToScalar(hb_positions[i].x_advance);
1332 // Round run widths if subpixel positioning is off to match native behavior. 1583 // Round run widths if subpixel positioning is off to match native behavior.
1333 if (!run->render_params.subpixel_positioning) 1584 if (!run->render_params.subpixel_positioning)
1334 run->width = std::floor(run->width + 0.5f); 1585 run->width = std::floor(run->width + 0.5f);
1335 } 1586 }
1336 1587
1337 hb_buffer_destroy(buffer); 1588 hb_buffer_destroy(buffer);
1338 hb_font_destroy(harfbuzz_font); 1589 hb_font_destroy(harfbuzz_font);
1339 return true; 1590 return true;
1340 } 1591 }
1341 1592
1342 } // namespace gfx 1593 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698