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 18 matching lines...) Expand all Loading... | |
258 AdvanceLine(); | 260 AdvanceLine(); |
259 lines_.pop_back(); | 261 lines_.pop_back(); |
260 *size = total_size_; | 262 *size = total_size_; |
261 lines->swap(lines_); | 263 lines->swap(lines_); |
262 } | 264 } |
263 | 265 |
264 private: | 266 private: |
265 // A (line index, segment index) pair that specifies a segment in |lines_|. | 267 // A (line index, segment index) pair that specifies a segment in |lines_|. |
266 typedef std::pair<size_t, size_t> SegmentHandle; | 268 typedef std::pair<size_t, size_t> SegmentHandle; |
267 | 269 |
270 struct SortByVisualPosition { | |
ckocagil
2015/02/13 19:05:25
Make this comparator a lambda expression and defin
Jun Mukai
2015/02/13 21:57:58
Done.
| |
271 explicit SortByVisualPosition(const std::vector<int32_t>& logical_to_visual) | |
272 : logical_to_visual(logical_to_visual) {} | |
273 | |
274 bool operator()(const internal::LineSegment& segment1, | |
275 const internal::LineSegment& segment2) { | |
276 return logical_to_visual[segment1.run] < logical_to_visual[segment2.run]; | |
277 } | |
278 | |
279 const std::vector<int32_t>& logical_to_visual; | |
280 }; | |
281 | |
268 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { | 282 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { |
269 return &lines_[handle.first].segments[handle.second]; | 283 return &lines_[handle.first].segments[handle.second]; |
270 } | 284 } |
271 | 285 |
272 // Breaks a run into segments that fit in the last line in |lines_| and adds | 286 // Breaks a run into segments that fit in the last line in |lines_| and adds |
273 // them. Adds a new Line to the back of |lines_| whenever a new segment can't | 287 // them. Adds a new Line to the back of |lines_| whenever a new segment can't |
274 // be added without the Line's width exceeding |max_width_|. | 288 // be added without the Line's width exceeding |max_width_|. |
275 void BreakRun(int run_index) { | 289 void BreakRun(int run_index) { |
276 const internal::TextRunHarfBuzz& run = *runs_[run_index]; | 290 const internal::TextRunHarfBuzz& run = *runs_[run_index]; |
277 SkScalar width = 0; | 291 SkScalar width = 0; |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
366 x += segment_width; | 380 x += segment_width; |
367 } | 381 } |
368 rtl_segments_.clear(); | 382 rtl_segments_.clear(); |
369 } | 383 } |
370 | 384 |
371 // Finishes the size calculations of the last Line in |lines_|. Adds a new | 385 // Finishes the size calculations of the last Line in |lines_|. Adds a new |
372 // Line to the back of |lines_|. | 386 // Line to the back of |lines_|. |
373 void AdvanceLine() { | 387 void AdvanceLine() { |
374 if (!lines_.empty()) { | 388 if (!lines_.empty()) { |
375 internal::Line* line = &lines_.back(); | 389 internal::Line* line = &lines_.back(); |
390 std::sort(line->segments.begin(), line->segments.end(), | |
391 SortByVisualPosition(logical_to_visual_)); | |
376 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); | 392 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); |
377 line->baseline = | 393 line->baseline = |
378 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); | 394 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); |
379 line->preceding_heights = std::ceil(total_size_.height()); | 395 line->preceding_heights = std::ceil(total_size_.height()); |
380 total_size_.set_height(total_size_.height() + line->size.height()); | 396 total_size_.set_height(total_size_.height() + line->size.height()); |
381 total_size_.set_width(std::max(total_size_.width(), line->size.width())); | 397 total_size_.set_width(std::max(total_size_.width(), line->size.width())); |
382 } | 398 } |
383 max_descent_ = 0; | 399 max_descent_ = 0; |
384 max_ascent_ = 0; | 400 max_ascent_ = 0; |
385 line_x_ = 0; | 401 line_x_ = 0; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
427 } | 443 } |
428 text_x_ += SkFloatToScalar(width); | 444 text_x_ += SkFloatToScalar(width); |
429 line_x_ += SkFloatToScalar(width); | 445 line_x_ += SkFloatToScalar(width); |
430 } | 446 } |
431 | 447 |
432 const SkScalar max_width_; | 448 const SkScalar max_width_; |
433 const int min_baseline_; | 449 const int min_baseline_; |
434 const float min_height_; | 450 const float min_height_; |
435 const bool multiline_; | 451 const bool multiline_; |
436 const base::string16& text_; | 452 const base::string16& text_; |
453 const std::vector<int32_t>& logical_to_visual_; | |
437 const BreakList<size_t>* const words_; | 454 const BreakList<size_t>* const words_; |
438 const ScopedVector<internal::TextRunHarfBuzz>& runs_; | 455 const ScopedVector<internal::TextRunHarfBuzz>& runs_; |
439 | 456 |
440 // Stores the resulting lines. | 457 // Stores the resulting lines. |
441 std::vector<internal::Line> lines_; | 458 std::vector<internal::Line> lines_; |
442 | 459 |
443 // Text space and line space x coordinates of the next segment to be added. | 460 // Text space and line space x coordinates of the next segment to be added. |
444 SkScalar text_x_; | 461 SkScalar text_x_; |
445 SkScalar line_x_; | 462 SkScalar line_x_; |
446 | 463 |
(...skipping 469 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
916 | 933 |
917 if (lines().empty()) { | 934 if (lines().empty()) { |
918 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 935 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
919 tracked_objects::ScopedTracker tracking_profile2( | 936 tracked_objects::ScopedTracker tracking_profile2( |
920 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 937 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
921 "431326 RenderTextHarfBuzz::EnsureLayout2")); | 938 "431326 RenderTextHarfBuzz::EnsureLayout2")); |
922 | 939 |
923 HarfBuzzLineBreaker line_breaker( | 940 HarfBuzzLineBreaker line_breaker( |
924 display_rect().width(), font_list().GetBaseline(), | 941 display_rect().width(), font_list().GetBaseline(), |
925 std::max(font_list().GetHeight(), min_line_height()), multiline(), | 942 std::max(font_list().GetHeight(), min_line_height()), multiline(), |
926 GetLayoutText(), multiline() ? &GetLineBreaks() : nullptr, runs_); | 943 GetLayoutText(), logical_to_visual_, |
944 multiline() ? &GetLineBreaks() : nullptr, runs_); | |
927 | 945 |
928 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 946 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
929 tracked_objects::ScopedTracker tracking_profile3( | 947 tracked_objects::ScopedTracker tracking_profile3( |
930 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 948 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
931 "431326 RenderTextHarfBuzz::EnsureLayout3")); | 949 "431326 RenderTextHarfBuzz::EnsureLayout3")); |
932 | 950 |
933 for (size_t i = 0; i < runs_.size(); ++i) | 951 for (size_t i = 0; i < runs_.size(); ++i) |
934 line_breaker.AddRun(visual_to_logical_[i]); | 952 line_breaker.AddRun(i); |
935 std::vector<internal::Line> lines; | 953 std::vector<internal::Line> lines; |
936 line_breaker.Finalize(&lines, &total_size_); | 954 line_breaker.Finalize(&lines, &total_size_); |
937 set_lines(&lines); | 955 set_lines(&lines); |
938 } | 956 } |
939 } | 957 } |
940 | 958 |
941 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { | 959 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { |
960 internal::SkiaTextRenderer renderer(canvas); | |
961 DrawVisualTextInternal(&renderer); | |
962 } | |
963 | |
964 void RenderTextHarfBuzz::DrawVisualTextInternal( | |
965 internal::SkiaTextRenderer* renderer) { | |
942 DCHECK(!needs_layout_); | 966 DCHECK(!needs_layout_); |
943 if (lines().empty()) | 967 if (lines().empty()) |
944 return; | 968 return; |
945 | 969 |
946 internal::SkiaTextRenderer renderer(canvas); | 970 ApplyFadeEffects(renderer); |
947 ApplyFadeEffects(&renderer); | 971 ApplyTextShadows(renderer); |
948 ApplyTextShadows(&renderer); | |
949 ApplyCompositionAndSelectionStyles(); | 972 ApplyCompositionAndSelectionStyles(); |
950 | 973 |
951 for (size_t i = 0; i < lines().size(); ++i) { | 974 for (size_t i = 0; i < lines().size(); ++i) { |
952 const internal::Line& line = lines()[i]; | 975 const internal::Line& line = lines()[i]; |
953 const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline); | 976 const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline); |
954 SkScalar preceding_segment_widths = 0; | 977 SkScalar preceding_segment_widths = 0; |
955 for (const internal::LineSegment& segment : line.segments) { | 978 for (const internal::LineSegment& segment : line.segments) { |
956 const internal::TextRunHarfBuzz& run = *runs_[segment.run]; | 979 const internal::TextRunHarfBuzz& run = *runs_[segment.run]; |
957 renderer.SetTypeface(run.skia_face.get()); | 980 renderer->SetTypeface(run.skia_face.get()); |
958 renderer.SetTextSize(SkIntToScalar(run.font_size)); | 981 renderer->SetTextSize(SkIntToScalar(run.font_size)); |
959 renderer.SetFontRenderParams(run.render_params, | 982 renderer->SetFontRenderParams(run.render_params, |
960 background_is_transparent()); | 983 background_is_transparent()); |
961 Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range); | 984 Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range); |
962 scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]); | 985 scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]); |
963 SkScalar offset_x = | 986 SkScalar offset_x = |
964 preceding_segment_widths - run.positions[glyphs_range.start()].x(); | 987 preceding_segment_widths - run.positions[glyphs_range.start()].x(); |
965 for (size_t j = 0; j < glyphs_range.length(); ++j) { | 988 for (size_t j = 0; j < glyphs_range.length(); ++j) { |
966 positions[j] = run.positions[(glyphs_range.is_reversed()) ? | 989 positions[j] = run.positions[(glyphs_range.is_reversed()) ? |
967 (glyphs_range.start() - j) : | 990 (glyphs_range.start() - j) : |
968 (glyphs_range.start() + j)]; | 991 (glyphs_range.start() + j)]; |
969 positions[j].offset(SkIntToScalar(origin.x()) + offset_x, | 992 positions[j].offset(SkIntToScalar(origin.x()) + offset_x, |
970 SkIntToScalar(origin.y())); | 993 SkIntToScalar(origin.y())); |
971 } | 994 } |
972 for (BreakList<SkColor>::const_iterator it = | 995 for (BreakList<SkColor>::const_iterator it = |
973 colors().GetBreak(segment.char_range.start()); | 996 colors().GetBreak(segment.char_range.start()); |
974 it != colors().breaks().end() && | 997 it != colors().breaks().end() && |
975 it->first < segment.char_range.end(); | 998 it->first < segment.char_range.end(); |
976 ++it) { | 999 ++it) { |
977 const Range intersection = | 1000 const Range intersection = |
978 colors().GetRange(it).Intersect(segment.char_range); | 1001 colors().GetRange(it).Intersect(segment.char_range); |
979 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | 1002 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); |
980 // The range may be empty if a portion of a multi-character grapheme is | 1003 // 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 | 1004 // 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, | 1005 // paints the glyph with a single style, but it should paint it twice, |
983 // clipped according to selection bounds. See http://crbug.com/366786 | 1006 // clipped according to selection bounds. See http://crbug.com/366786 |
984 if (colored_glyphs.is_empty()) | 1007 if (colored_glyphs.is_empty()) |
985 continue; | 1008 continue; |
986 | 1009 |
987 renderer.SetForegroundColor(it->second); | 1010 renderer->SetForegroundColor(it->second); |
988 renderer.DrawPosText( | 1011 renderer->DrawPosText( |
989 &positions[colored_glyphs.start() - glyphs_range.start()], | 1012 &positions[colored_glyphs.start() - glyphs_range.start()], |
990 &run.glyphs[colored_glyphs.start()], colored_glyphs.length()); | 1013 &run.glyphs[colored_glyphs.start()], colored_glyphs.length()); |
991 int start_x = SkScalarRoundToInt( | 1014 int start_x = SkScalarRoundToInt( |
992 positions[colored_glyphs.start() - glyphs_range.start()].x()); | 1015 positions[colored_glyphs.start() - glyphs_range.start()].x()); |
993 int end_x = SkScalarRoundToInt( | 1016 int end_x = SkScalarRoundToInt( |
994 (colored_glyphs.end() == glyphs_range.end()) | 1017 (colored_glyphs.end() == glyphs_range.end()) |
995 ? (SkFloatToScalar(segment.width) + preceding_segment_widths + | 1018 ? (SkFloatToScalar(segment.width) + preceding_segment_widths + |
996 SkIntToScalar(origin.x())) | 1019 SkIntToScalar(origin.x())) |
997 : positions[colored_glyphs.end() - glyphs_range.start()].x()); | 1020 : positions[colored_glyphs.end() - glyphs_range.start()].x()); |
998 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x, | 1021 renderer->DrawDecorations(start_x, origin.y(), end_x - start_x, |
999 run.underline, run.strike, | 1022 run.underline, run.strike, |
1000 run.diagonal_strike); | 1023 run.diagonal_strike); |
1001 } | 1024 } |
1002 preceding_segment_widths += SkFloatToScalar(segment.width); | 1025 preceding_segment_widths += SkFloatToScalar(segment.width); |
1003 } | 1026 } |
1004 } | 1027 } |
1005 | 1028 |
1006 renderer.EndDiagonalStrike(); | 1029 renderer->EndDiagonalStrike(); |
1007 | 1030 |
1008 UndoCompositionAndSelectionStyles(); | 1031 UndoCompositionAndSelectionStyles(); |
1009 } | 1032 } |
1010 | 1033 |
1011 size_t RenderTextHarfBuzz::GetRunContainingCaret( | 1034 size_t RenderTextHarfBuzz::GetRunContainingCaret( |
1012 const SelectionModel& caret) const { | 1035 const SelectionModel& caret) const { |
1013 DCHECK(!needs_layout_); | 1036 DCHECK(!needs_layout_); |
1014 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); | 1037 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); |
1015 LogicalCursorDirection affinity = caret.caret_affinity(); | 1038 LogicalCursorDirection affinity = caret.caret_affinity(); |
1016 for (size_t run = 0; run < runs_.size(); ++run) { | 1039 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")); | 1334 "431326 RenderTextHarfBuzz::ShapeRunWithFont3")); |
1312 | 1335 |
1313 for (size_t i = 0; i < run->glyph_count; ++i) { | 1336 for (size_t i = 0; i < run->glyph_count; ++i) { |
1314 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max()); | 1337 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max()); |
1315 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); | 1338 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); |
1316 run->glyph_to_char[i] = infos[i].cluster; | 1339 run->glyph_to_char[i] = infos[i].cluster; |
1317 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); | 1340 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); |
1318 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); | 1341 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); |
1319 run->positions[i].set(run->width + x_offset, -y_offset); | 1342 run->positions[i].set(run->width + x_offset, -y_offset); |
1320 run->width += (glyph_width_for_test_ > 0) | 1343 run->width += (glyph_width_for_test_ > 0) |
1321 ? SkIntToScalar(glyph_width_for_test_) | 1344 ? glyph_width_for_test_ |
1322 : SkFixedToScalar(hb_positions[i].x_advance); | 1345 : SkFixedToFloat(hb_positions[i].x_advance); |
1323 // Round run widths if subpixel positioning is off to match native behavior. | 1346 // Round run widths if subpixel positioning is off to match native behavior. |
1324 if (!run->render_params.subpixel_positioning) | 1347 if (!run->render_params.subpixel_positioning) |
1325 run->width = std::floor(run->width + 0.5f); | 1348 run->width = std::floor(run->width + 0.5f); |
1326 } | 1349 } |
1327 | 1350 |
1328 hb_buffer_destroy(buffer); | 1351 hb_buffer_destroy(buffer); |
1329 hb_font_destroy(harfbuzz_font); | 1352 hb_font_destroy(harfbuzz_font); |
1330 return true; | 1353 return true; |
1331 } | 1354 } |
1332 | 1355 |
1333 } // namespace gfx | 1356 } // namespace gfx |
OLD | NEW |