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 | 8 |
9 #include "base/i18n/bidi_line_iterator.h" | 9 #include "base/i18n/bidi_line_iterator.h" |
10 #include "base/i18n/break_iterator.h" | 10 #include "base/i18n/break_iterator.h" |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 // height for each line. | 213 // height for each line. |
214 // TODO(ckocagil): Expose the interface of this class in the header and test | 214 // TODO(ckocagil): Expose the interface of this class in the header and test |
215 // this class directly. | 215 // this class directly. |
216 class HarfBuzzLineBreaker { | 216 class HarfBuzzLineBreaker { |
217 public: | 217 public: |
218 HarfBuzzLineBreaker(size_t max_width, | 218 HarfBuzzLineBreaker(size_t max_width, |
219 int min_baseline, | 219 int min_baseline, |
220 float min_height, | 220 float min_height, |
221 bool multiline, | 221 bool multiline, |
222 const base::string16& text, | 222 const base::string16& text, |
| 223 const std::vector<int32_t>& logical_to_visual, |
223 const BreakList<size_t>* words, | 224 const BreakList<size_t>* words, |
224 const ScopedVector<internal::TextRunHarfBuzz>& runs) | 225 const ScopedVector<internal::TextRunHarfBuzz>& runs) |
225 : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)), | 226 : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)), |
226 min_baseline_(min_baseline), | 227 min_baseline_(min_baseline), |
227 min_height_(min_height), | 228 min_height_(min_height), |
228 multiline_(multiline), | 229 multiline_(multiline), |
229 text_(text), | 230 text_(text), |
| 231 logical_to_visual_(logical_to_visual), |
230 words_(words), | 232 words_(words), |
231 runs_(runs), | 233 runs_(runs), |
232 text_x_(0), | 234 text_x_(0), |
233 line_x_(0), | 235 line_x_(0), |
234 max_descent_(0), | 236 max_descent_(0), |
235 max_ascent_(0) { | 237 max_ascent_(0) { |
236 DCHECK_EQ(multiline_, (words_ != nullptr)); | 238 DCHECK_EQ(multiline_, (words_ != nullptr)); |
237 AdvanceLine(); | 239 AdvanceLine(); |
238 } | 240 } |
239 | 241 |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
366 x += segment_width; | 368 x += segment_width; |
367 } | 369 } |
368 rtl_segments_.clear(); | 370 rtl_segments_.clear(); |
369 } | 371 } |
370 | 372 |
371 // Finishes the size calculations of the last Line in |lines_|. Adds a new | 373 // Finishes the size calculations of the last Line in |lines_|. Adds a new |
372 // Line to the back of |lines_|. | 374 // Line to the back of |lines_|. |
373 void AdvanceLine() { | 375 void AdvanceLine() { |
374 if (!lines_.empty()) { | 376 if (!lines_.empty()) { |
375 internal::Line* line = &lines_.back(); | 377 internal::Line* line = &lines_.back(); |
| 378 std::sort(line->segments.begin(), line->segments.end(), |
| 379 [this](const internal::LineSegment& s1, |
| 380 const internal::LineSegment& s2) -> bool { |
| 381 return logical_to_visual_[s1.run] < logical_to_visual_[s2.run]; |
| 382 }); |
376 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); | 383 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); |
377 line->baseline = | 384 line->baseline = |
378 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); | 385 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); |
379 line->preceding_heights = std::ceil(total_size_.height()); | 386 line->preceding_heights = std::ceil(total_size_.height()); |
380 total_size_.set_height(total_size_.height() + line->size.height()); | 387 total_size_.set_height(total_size_.height() + line->size.height()); |
381 total_size_.set_width(std::max(total_size_.width(), line->size.width())); | 388 total_size_.set_width(std::max(total_size_.width(), line->size.width())); |
382 } | 389 } |
383 max_descent_ = 0; | 390 max_descent_ = 0; |
384 max_ascent_ = 0; | 391 max_ascent_ = 0; |
385 line_x_ = 0; | 392 line_x_ = 0; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
427 } | 434 } |
428 text_x_ += SkFloatToScalar(width); | 435 text_x_ += SkFloatToScalar(width); |
429 line_x_ += SkFloatToScalar(width); | 436 line_x_ += SkFloatToScalar(width); |
430 } | 437 } |
431 | 438 |
432 const SkScalar max_width_; | 439 const SkScalar max_width_; |
433 const int min_baseline_; | 440 const int min_baseline_; |
434 const float min_height_; | 441 const float min_height_; |
435 const bool multiline_; | 442 const bool multiline_; |
436 const base::string16& text_; | 443 const base::string16& text_; |
| 444 const std::vector<int32_t>& logical_to_visual_; |
437 const BreakList<size_t>* const words_; | 445 const BreakList<size_t>* const words_; |
438 const ScopedVector<internal::TextRunHarfBuzz>& runs_; | 446 const ScopedVector<internal::TextRunHarfBuzz>& runs_; |
439 | 447 |
440 // Stores the resulting lines. | 448 // Stores the resulting lines. |
441 std::vector<internal::Line> lines_; | 449 std::vector<internal::Line> lines_; |
442 | 450 |
443 // Text space and line space x coordinates of the next segment to be added. | 451 // Text space and line space x coordinates of the next segment to be added. |
444 SkScalar text_x_; | 452 SkScalar text_x_; |
445 SkScalar line_x_; | 453 SkScalar line_x_; |
446 | 454 |
(...skipping 469 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
916 | 924 |
917 if (lines().empty()) { | 925 if (lines().empty()) { |
918 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 926 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
919 tracked_objects::ScopedTracker tracking_profile2( | 927 tracked_objects::ScopedTracker tracking_profile2( |
920 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 928 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
921 "431326 RenderTextHarfBuzz::EnsureLayout2")); | 929 "431326 RenderTextHarfBuzz::EnsureLayout2")); |
922 | 930 |
923 HarfBuzzLineBreaker line_breaker( | 931 HarfBuzzLineBreaker line_breaker( |
924 display_rect().width(), font_list().GetBaseline(), | 932 display_rect().width(), font_list().GetBaseline(), |
925 std::max(font_list().GetHeight(), min_line_height()), multiline(), | 933 std::max(font_list().GetHeight(), min_line_height()), multiline(), |
926 GetLayoutText(), multiline() ? &GetLineBreaks() : nullptr, runs_); | 934 GetLayoutText(), logical_to_visual_, |
| 935 multiline() ? &GetLineBreaks() : nullptr, runs_); |
927 | 936 |
928 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 937 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
929 tracked_objects::ScopedTracker tracking_profile3( | 938 tracked_objects::ScopedTracker tracking_profile3( |
930 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 939 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
931 "431326 RenderTextHarfBuzz::EnsureLayout3")); | 940 "431326 RenderTextHarfBuzz::EnsureLayout3")); |
932 | 941 |
933 for (size_t i = 0; i < runs_.size(); ++i) | 942 for (size_t i = 0; i < runs_.size(); ++i) |
934 line_breaker.AddRun(visual_to_logical_[i]); | 943 line_breaker.AddRun(i); |
935 std::vector<internal::Line> lines; | 944 std::vector<internal::Line> lines; |
936 line_breaker.Finalize(&lines, &total_size_); | 945 line_breaker.Finalize(&lines, &total_size_); |
937 set_lines(&lines); | 946 set_lines(&lines); |
938 } | 947 } |
939 } | 948 } |
940 | 949 |
941 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { | 950 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { |
| 951 internal::SkiaTextRenderer renderer(canvas); |
| 952 DrawVisualTextInternal(&renderer); |
| 953 } |
| 954 |
| 955 void RenderTextHarfBuzz::DrawVisualTextInternal( |
| 956 internal::SkiaTextRenderer* renderer) { |
942 DCHECK(!needs_layout_); | 957 DCHECK(!needs_layout_); |
943 if (lines().empty()) | 958 if (lines().empty()) |
944 return; | 959 return; |
945 | 960 |
946 internal::SkiaTextRenderer renderer(canvas); | 961 ApplyFadeEffects(renderer); |
947 ApplyFadeEffects(&renderer); | 962 ApplyTextShadows(renderer); |
948 ApplyTextShadows(&renderer); | |
949 ApplyCompositionAndSelectionStyles(); | 963 ApplyCompositionAndSelectionStyles(); |
950 | 964 |
951 for (size_t i = 0; i < lines().size(); ++i) { | 965 for (size_t i = 0; i < lines().size(); ++i) { |
952 const internal::Line& line = lines()[i]; | 966 const internal::Line& line = lines()[i]; |
953 const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline); | 967 const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline); |
954 SkScalar preceding_segment_widths = 0; | 968 SkScalar preceding_segment_widths = 0; |
955 for (const internal::LineSegment& segment : line.segments) { | 969 for (const internal::LineSegment& segment : line.segments) { |
956 const internal::TextRunHarfBuzz& run = *runs_[segment.run]; | 970 const internal::TextRunHarfBuzz& run = *runs_[segment.run]; |
957 renderer.SetTypeface(run.skia_face.get()); | 971 renderer->SetTypeface(run.skia_face.get()); |
958 renderer.SetTextSize(SkIntToScalar(run.font_size)); | 972 renderer->SetTextSize(SkIntToScalar(run.font_size)); |
959 renderer.SetFontRenderParams(run.render_params, | 973 renderer->SetFontRenderParams(run.render_params, |
960 background_is_transparent()); | 974 background_is_transparent()); |
961 Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range); | 975 Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range); |
962 scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]); | 976 scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]); |
963 SkScalar offset_x = | 977 SkScalar offset_x = |
964 preceding_segment_widths - run.positions[glyphs_range.start()].x(); | 978 preceding_segment_widths - run.positions[glyphs_range.start()].x(); |
965 for (size_t j = 0; j < glyphs_range.length(); ++j) { | 979 for (size_t j = 0; j < glyphs_range.length(); ++j) { |
966 positions[j] = run.positions[(glyphs_range.is_reversed()) ? | 980 positions[j] = run.positions[(glyphs_range.is_reversed()) ? |
967 (glyphs_range.start() - j) : | 981 (glyphs_range.start() - j) : |
968 (glyphs_range.start() + j)]; | 982 (glyphs_range.start() + j)]; |
969 positions[j].offset(SkIntToScalar(origin.x()) + offset_x, | 983 positions[j].offset(SkIntToScalar(origin.x()) + offset_x, |
970 SkIntToScalar(origin.y())); | 984 SkIntToScalar(origin.y())); |
971 } | 985 } |
972 for (BreakList<SkColor>::const_iterator it = | 986 for (BreakList<SkColor>::const_iterator it = |
973 colors().GetBreak(segment.char_range.start()); | 987 colors().GetBreak(segment.char_range.start()); |
974 it != colors().breaks().end() && | 988 it != colors().breaks().end() && |
975 it->first < segment.char_range.end(); | 989 it->first < segment.char_range.end(); |
976 ++it) { | 990 ++it) { |
977 const Range intersection = | 991 const Range intersection = |
978 colors().GetRange(it).Intersect(segment.char_range); | 992 colors().GetRange(it).Intersect(segment.char_range); |
979 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | 993 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); |
980 // The range may be empty if a portion of a multi-character grapheme is | 994 // The range may be empty if a portion of a multi-character grapheme is |
981 // selected, yielding two colors for a single glyph. For now, this just | 995 // selected, yielding two colors for a single glyph. For now, this just |
982 // paints the glyph with a single style, but it should paint it twice, | 996 // paints the glyph with a single style, but it should paint it twice, |
983 // clipped according to selection bounds. See http://crbug.com/366786 | 997 // clipped according to selection bounds. See http://crbug.com/366786 |
984 if (colored_glyphs.is_empty()) | 998 if (colored_glyphs.is_empty()) |
985 continue; | 999 continue; |
986 | 1000 |
987 renderer.SetForegroundColor(it->second); | 1001 renderer->SetForegroundColor(it->second); |
988 renderer.DrawPosText( | 1002 renderer->DrawPosText( |
989 &positions[colored_glyphs.start() - glyphs_range.start()], | 1003 &positions[colored_glyphs.start() - glyphs_range.start()], |
990 &run.glyphs[colored_glyphs.start()], colored_glyphs.length()); | 1004 &run.glyphs[colored_glyphs.start()], colored_glyphs.length()); |
991 int start_x = SkScalarRoundToInt( | 1005 int start_x = SkScalarRoundToInt( |
992 positions[colored_glyphs.start() - glyphs_range.start()].x()); | 1006 positions[colored_glyphs.start() - glyphs_range.start()].x()); |
993 int end_x = SkScalarRoundToInt( | 1007 int end_x = SkScalarRoundToInt( |
994 (colored_glyphs.end() == glyphs_range.end()) | 1008 (colored_glyphs.end() == glyphs_range.end()) |
995 ? (SkFloatToScalar(segment.width) + preceding_segment_widths + | 1009 ? (SkFloatToScalar(segment.width) + preceding_segment_widths + |
996 SkIntToScalar(origin.x())) | 1010 SkIntToScalar(origin.x())) |
997 : positions[colored_glyphs.end() - glyphs_range.start()].x()); | 1011 : positions[colored_glyphs.end() - glyphs_range.start()].x()); |
998 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x, | 1012 renderer->DrawDecorations(start_x, origin.y(), end_x - start_x, |
999 run.underline, run.strike, | 1013 run.underline, run.strike, |
1000 run.diagonal_strike); | 1014 run.diagonal_strike); |
1001 } | 1015 } |
1002 preceding_segment_widths += SkFloatToScalar(segment.width); | 1016 preceding_segment_widths += SkFloatToScalar(segment.width); |
1003 } | 1017 } |
1004 } | 1018 } |
1005 | 1019 |
1006 renderer.EndDiagonalStrike(); | 1020 renderer->EndDiagonalStrike(); |
1007 | 1021 |
1008 UndoCompositionAndSelectionStyles(); | 1022 UndoCompositionAndSelectionStyles(); |
1009 } | 1023 } |
1010 | 1024 |
1011 size_t RenderTextHarfBuzz::GetRunContainingCaret( | 1025 size_t RenderTextHarfBuzz::GetRunContainingCaret( |
1012 const SelectionModel& caret) const { | 1026 const SelectionModel& caret) const { |
1013 DCHECK(!needs_layout_); | 1027 DCHECK(!needs_layout_); |
1014 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); | 1028 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); |
1015 LogicalCursorDirection affinity = caret.caret_affinity(); | 1029 LogicalCursorDirection affinity = caret.caret_affinity(); |
1016 for (size_t run = 0; run < runs_.size(); ++run) { | 1030 for (size_t run = 0; run < runs_.size(); ++run) { |
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1311 "431326 RenderTextHarfBuzz::ShapeRunWithFont3")); | 1325 "431326 RenderTextHarfBuzz::ShapeRunWithFont3")); |
1312 | 1326 |
1313 for (size_t i = 0; i < run->glyph_count; ++i) { | 1327 for (size_t i = 0; i < run->glyph_count; ++i) { |
1314 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max()); | 1328 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max()); |
1315 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); | 1329 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); |
1316 run->glyph_to_char[i] = infos[i].cluster; | 1330 run->glyph_to_char[i] = infos[i].cluster; |
1317 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); | 1331 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); |
1318 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); | 1332 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); |
1319 run->positions[i].set(run->width + x_offset, -y_offset); | 1333 run->positions[i].set(run->width + x_offset, -y_offset); |
1320 run->width += (glyph_width_for_test_ > 0) | 1334 run->width += (glyph_width_for_test_ > 0) |
1321 ? SkIntToScalar(glyph_width_for_test_) | 1335 ? glyph_width_for_test_ |
1322 : SkFixedToScalar(hb_positions[i].x_advance); | 1336 : SkFixedToFloat(hb_positions[i].x_advance); |
1323 // Round run widths if subpixel positioning is off to match native behavior. | 1337 // Round run widths if subpixel positioning is off to match native behavior. |
1324 if (!run->render_params.subpixel_positioning) | 1338 if (!run->render_params.subpixel_positioning) |
1325 run->width = std::floor(run->width + 0.5f); | 1339 run->width = std::floor(run->width + 0.5f); |
1326 } | 1340 } |
1327 | 1341 |
1328 hb_buffer_destroy(buffer); | 1342 hb_buffer_destroy(buffer); |
1329 hb_font_destroy(harfbuzz_font); | 1343 hb_font_destroy(harfbuzz_font); |
1330 return true; | 1344 return true; |
1331 } | 1345 } |
1332 | 1346 |
1333 } // namespace gfx | 1347 } // namespace gfx |
OLD | NEW |