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" |
11 #include "base/i18n/char_iterator.h" | 11 #include "base/i18n/char_iterator.h" |
12 #include "base/profiler/scoped_tracker.h" | 12 #include "base/profiler/scoped_tracker.h" |
13 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
14 #include "base/trace_event/trace_event.h" | 14 #include "base/trace_event/trace_event.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/safe_integer_conversions.h" | |
23 #include "ui/gfx/harfbuzz_font_skia.h" | 22 #include "ui/gfx/harfbuzz_font_skia.h" |
24 #include "ui/gfx/range/range_f.h" | 23 #include "ui/gfx/range/range_f.h" |
25 #include "ui/gfx/utf16_indexing.h" | 24 #include "ui/gfx/utf16_indexing.h" |
26 | 25 |
27 #if defined(OS_WIN) | 26 #if defined(OS_WIN) |
28 #include "ui/gfx/font_fallback_win.h" | 27 #include "ui/gfx/font_fallback_win.h" |
29 #endif | 28 #endif |
30 | 29 |
31 using gfx::internal::RoundRangeF; | 30 using gfx::internal::RoundRangeF; |
32 | 31 |
(...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
415 line->segments.push_back(segment); | 414 line->segments.push_back(segment); |
416 | 415 |
417 SkPaint paint; | 416 SkPaint paint; |
418 paint.setTypeface(run.skia_face.get()); | 417 paint.setTypeface(run.skia_face.get()); |
419 paint.setTextSize(SkIntToScalar(run.font_size)); | 418 paint.setTextSize(SkIntToScalar(run.font_size)); |
420 paint.setAntiAlias(run.render_params.antialiasing); | 419 paint.setAntiAlias(run.render_params.antialiasing); |
421 SkPaint::FontMetrics metrics; | 420 SkPaint::FontMetrics metrics; |
422 paint.getFontMetrics(&metrics); | 421 paint.getFontMetrics(&metrics); |
423 | 422 |
424 line->size.set_width(line->size.width() + width); | 423 line->size.set_width(line->size.width() + width); |
425 // TODO(dschuyler): Account for stylized baselines in string sizing. | |
426 max_descent_ = std::max(max_descent_, metrics.fDescent); | 424 max_descent_ = std::max(max_descent_, metrics.fDescent); |
427 // fAscent is always negative. | 425 // fAscent is always negative. |
428 max_ascent_ = std::max(max_ascent_, -metrics.fAscent); | 426 max_ascent_ = std::max(max_ascent_, -metrics.fAscent); |
429 | 427 |
430 if (run.is_rtl) { | 428 if (run.is_rtl) { |
431 rtl_segments_.push_back( | 429 rtl_segments_.push_back( |
432 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | 430 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); |
433 // If this is the last segment of an RTL run, reprocess the text-space x | 431 // If this is the last segment of an RTL run, reprocess the text-space x |
434 // ranges of all segments from the run. | 432 // ranges of all segments from the run. |
435 if (char_range.end() == run.range.end()) | 433 if (char_range.end() == run.range.end()) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
476 } | 474 } |
477 | 475 |
478 TextRunHarfBuzz::TextRunHarfBuzz() | 476 TextRunHarfBuzz::TextRunHarfBuzz() |
479 : width(0.0f), | 477 : width(0.0f), |
480 preceding_run_widths(0.0f), | 478 preceding_run_widths(0.0f), |
481 is_rtl(false), | 479 is_rtl(false), |
482 level(0), | 480 level(0), |
483 script(USCRIPT_INVALID_CODE), | 481 script(USCRIPT_INVALID_CODE), |
484 glyph_count(static_cast<size_t>(-1)), | 482 glyph_count(static_cast<size_t>(-1)), |
485 font_size(0), | 483 font_size(0), |
486 baseline_offset(0), | |
487 baseline_type(0), | |
488 font_style(0), | 484 font_style(0), |
489 strike(false), | 485 strike(false), |
490 diagonal_strike(false), | 486 diagonal_strike(false), |
491 underline(false) { | 487 underline(false) {} |
492 } | |
493 | 488 |
494 TextRunHarfBuzz::~TextRunHarfBuzz() {} | 489 TextRunHarfBuzz::~TextRunHarfBuzz() {} |
495 | 490 |
496 void TextRunHarfBuzz::GetClusterAt(size_t pos, | 491 void TextRunHarfBuzz::GetClusterAt(size_t pos, |
497 Range* chars, | 492 Range* chars, |
498 Range* glyphs) const { | 493 Range* glyphs) const { |
499 DCHECK(range.Contains(Range(pos, pos + 1))); | 494 DCHECK(range.Contains(Range(pos, pos + 1))); |
500 DCHECK(chars); | 495 DCHECK(chars); |
501 DCHECK(glyphs); | 496 DCHECK(glyphs); |
502 | 497 |
(...skipping 525 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1028 scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]); | 1023 scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]); |
1029 SkScalar offset_x = preceding_segment_widths - | 1024 SkScalar offset_x = preceding_segment_widths - |
1030 ((glyphs_range.GetMin() != 0) | 1025 ((glyphs_range.GetMin() != 0) |
1031 ? run.positions[glyphs_range.GetMin()].x() | 1026 ? run.positions[glyphs_range.GetMin()].x() |
1032 : 0); | 1027 : 0); |
1033 for (size_t j = 0; j < glyphs_range.length(); ++j) { | 1028 for (size_t j = 0; j < glyphs_range.length(); ++j) { |
1034 positions[j] = run.positions[(glyphs_range.is_reversed()) ? | 1029 positions[j] = run.positions[(glyphs_range.is_reversed()) ? |
1035 (glyphs_range.start() - j) : | 1030 (glyphs_range.start() - j) : |
1036 (glyphs_range.start() + j)]; | 1031 (glyphs_range.start() + j)]; |
1037 positions[j].offset(SkIntToScalar(origin.x()) + offset_x, | 1032 positions[j].offset(SkIntToScalar(origin.x()) + offset_x, |
1038 SkIntToScalar(origin.y() + run.baseline_offset)); | 1033 SkIntToScalar(origin.y())); |
1039 } | 1034 } |
1040 for (BreakList<SkColor>::const_iterator it = | 1035 for (BreakList<SkColor>::const_iterator it = |
1041 colors().GetBreak(segment.char_range.start()); | 1036 colors().GetBreak(segment.char_range.start()); |
1042 it != colors().breaks().end() && | 1037 it != colors().breaks().end() && |
1043 it->first < segment.char_range.end(); | 1038 it->first < segment.char_range.end(); |
1044 ++it) { | 1039 ++it) { |
1045 const Range intersection = | 1040 const Range intersection = |
1046 colors().GetRange(it).Intersect(segment.char_range); | 1041 colors().GetRange(it).Intersect(segment.char_range); |
1047 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | 1042 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); |
1048 // The range may be empty if a portion of a multi-character grapheme is | 1043 // The range may be empty if a portion of a multi-character grapheme is |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1137 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; | 1132 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; |
1138 run->range = Range(0, text.length()); | 1133 run->range = Range(0, text.length()); |
1139 run_list_out->add(run); | 1134 run_list_out->add(run); |
1140 run_list_out->InitIndexMap(); | 1135 run_list_out->InitIndexMap(); |
1141 return; | 1136 return; |
1142 } | 1137 } |
1143 | 1138 |
1144 // Temporarily apply composition underlines and selection colors. | 1139 // Temporarily apply composition underlines and selection colors. |
1145 ApplyCompositionAndSelectionStyles(); | 1140 ApplyCompositionAndSelectionStyles(); |
1146 | 1141 |
1147 // Build the run list from the script items and ranged styles and baselines. | 1142 // Build the list of runs from the script items and ranged styles. Use an |
1148 // Use an empty color BreakList to avoid breaking runs at color boundaries. | 1143 // empty color BreakList to avoid breaking runs at color boundaries. |
1149 BreakList<SkColor> empty_colors; | 1144 BreakList<SkColor> empty_colors; |
1150 empty_colors.SetMax(text.length()); | 1145 empty_colors.SetMax(text.length()); |
1151 internal::StyleIterator style(empty_colors, baselines(), styles()); | 1146 internal::StyleIterator style(empty_colors, styles()); |
1152 | 1147 |
1153 for (size_t run_break = 0; run_break < text.length();) { | 1148 for (size_t run_break = 0; run_break < text.length();) { |
1154 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; | 1149 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; |
1155 run->range.set_start(run_break); | 1150 run->range.set_start(run_break); |
1156 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | | 1151 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | |
1157 (style.style(ITALIC) ? Font::ITALIC : 0); | 1152 (style.style(ITALIC) ? Font::ITALIC : 0); |
1158 run->baseline_type = style.baseline(); | |
1159 run->strike = style.style(STRIKE); | 1153 run->strike = style.style(STRIKE); |
1160 run->diagonal_strike = style.style(DIAGONAL_STRIKE); | 1154 run->diagonal_strike = style.style(DIAGONAL_STRIKE); |
1161 run->underline = style.style(UNDERLINE); | 1155 run->underline = style.style(UNDERLINE); |
1162 int32 script_item_break = 0; | 1156 int32 script_item_break = 0; |
1163 bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level); | 1157 bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level); |
1164 // Odd BiDi embedding levels correspond to RTL runs. | 1158 // Odd BiDi embedding levels correspond to RTL runs. |
1165 run->is_rtl = (run->level % 2) == 1; | 1159 run->is_rtl = (run->level % 2) == 1; |
1166 // Find the length and script of this script run. | 1160 // Find the length and script of this script run. |
1167 script_item_break = ScriptInterval(text, run_break, | 1161 script_item_break = ScriptInterval(text, run_break, |
1168 script_item_break - run_break, &run->script) + run_break; | 1162 script_item_break - run_break, &run->script) + run_break; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1217 for (auto* run : run_list->runs()) | 1211 for (auto* run : run_list->runs()) |
1218 ShapeRun(text, run); | 1212 ShapeRun(text, run); |
1219 run_list->ComputePrecedingRunWidths(); | 1213 run_list->ComputePrecedingRunWidths(); |
1220 } | 1214 } |
1221 | 1215 |
1222 void RenderTextHarfBuzz::ShapeRun(const base::string16& text, | 1216 void RenderTextHarfBuzz::ShapeRun(const base::string16& text, |
1223 internal::TextRunHarfBuzz* run) { | 1217 internal::TextRunHarfBuzz* run) { |
1224 const Font& primary_font = font_list().GetPrimaryFont(); | 1218 const Font& primary_font = font_list().GetPrimaryFont(); |
1225 const std::string primary_family = primary_font.GetFontName(); | 1219 const std::string primary_family = primary_font.GetFontName(); |
1226 run->font_size = primary_font.GetFontSize(); | 1220 run->font_size = primary_font.GetFontSize(); |
1227 run->baseline_offset = 0; | |
1228 if (run->baseline_type != NORMAL_BASELINE) { | |
1229 // Calculate a slightly smaller font. The ratio here is somewhat arbitrary. | |
1230 // Proportions from 5/9 to 5/7 all look pretty good. | |
1231 const float ratio = 5.0f / 9.0f; | |
1232 run->font_size = gfx::ToRoundedInt(primary_font.GetFontSize() * ratio); | |
1233 switch (run->baseline_type) { | |
1234 case SUPERSCRIPT: | |
1235 run->baseline_offset = | |
1236 primary_font.GetCapHeight() - primary_font.GetHeight(); | |
1237 break; | |
1238 case SUPERIOR: | |
1239 run->baseline_offset = | |
1240 gfx::ToRoundedInt(primary_font.GetCapHeight() * ratio) - | |
1241 primary_font.GetCapHeight(); | |
1242 break; | |
1243 case SUBSCRIPT: | |
1244 run->baseline_offset = | |
1245 primary_font.GetHeight() - primary_font.GetBaseline(); | |
1246 break; | |
1247 case INFERIOR: // Fall through. | |
1248 default: | |
1249 break; | |
1250 } | |
1251 } | |
1252 | 1221 |
1253 std::string best_family; | 1222 std::string best_family; |
1254 FontRenderParams best_render_params; | 1223 FontRenderParams best_render_params; |
1255 size_t best_missing_glyphs = std::numeric_limits<size_t>::max(); | 1224 size_t best_missing_glyphs = std::numeric_limits<size_t>::max(); |
1256 | 1225 |
1257 for (const Font& font : font_list().GetFonts()) { | 1226 for (const Font& font : font_list().GetFonts()) { |
1258 if (CompareFamily(text, font.GetFontName(), font.GetFontRenderParams(), | 1227 if (CompareFamily(text, font.GetFontName(), font.GetFontRenderParams(), |
1259 run, &best_family, &best_render_params, | 1228 run, &best_family, &best_render_params, |
1260 &best_missing_glyphs)) | 1229 &best_missing_glyphs)) |
1261 return; | 1230 return; |
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1499 DCHECK(!update_layout_run_list_); | 1468 DCHECK(!update_layout_run_list_); |
1500 DCHECK(!update_display_run_list_); | 1469 DCHECK(!update_display_run_list_); |
1501 return text_elided() ? display_run_list_.get() : &layout_run_list_; | 1470 return text_elided() ? display_run_list_.get() : &layout_run_list_; |
1502 } | 1471 } |
1503 | 1472 |
1504 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 1473 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { |
1505 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 1474 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); |
1506 } | 1475 } |
1507 | 1476 |
1508 } // namespace gfx | 1477 } // namespace gfx |
OLD | NEW |