Chromium Code Reviews| 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" |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 69 // |paint|'s font. | 69 // |paint|'s font. |
| 70 void GetGlyphWidthAndExtents(SkPaint* paint, | 70 void GetGlyphWidthAndExtents(SkPaint* paint, |
| 71 hb_codepoint_t codepoint, | 71 hb_codepoint_t codepoint, |
| 72 hb_position_t* width, | 72 hb_position_t* width, |
| 73 hb_glyph_extents_t* extents) { | 73 hb_glyph_extents_t* extents) { |
| 74 DCHECK_LE(codepoint, 0xFFFFU); | 74 DCHECK_LE(codepoint, 0xFFFFU); |
| 75 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 75 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); |
| 76 | 76 |
| 77 SkScalar sk_width; | 77 SkScalar sk_width; |
| 78 SkRect sk_bounds; | 78 SkRect sk_bounds; |
| 79 uint16_t glyph = codepoint; | 79 uint16_t glyph = static_cast<uint16_t>(codepoint); |
|
msw
2014/10/17 22:13:23
ditto nit q: DCHECK against uint32_t -> uint16_t o
Peter Kasting
2014/10/21 01:20:45
This was checked above (line 74).
| |
| 80 | 80 |
| 81 paint->getTextWidths(&glyph, sizeof(glyph), &sk_width, &sk_bounds); | 81 paint->getTextWidths(&glyph, sizeof(glyph), &sk_width, &sk_bounds); |
| 82 if (width) | 82 if (width) |
| 83 *width = SkiaScalarToHarfBuzzPosition(sk_width); | 83 *width = SkiaScalarToHarfBuzzPosition(sk_width); |
| 84 if (extents) { | 84 if (extents) { |
| 85 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be | 85 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be |
| 86 // y-grows-up. | 86 // y-grows-up. |
| 87 extents->x_bearing = SkiaScalarToHarfBuzzPosition(sk_bounds.fLeft); | 87 extents->x_bearing = SkiaScalarToHarfBuzzPosition(sk_bounds.fLeft); |
| 88 extents->y_bearing = SkiaScalarToHarfBuzzPosition(-sk_bounds.fTop); | 88 extents->y_bearing = SkiaScalarToHarfBuzzPosition(-sk_bounds.fTop); |
| 89 extents->width = SkiaScalarToHarfBuzzPosition(sk_bounds.width()); | 89 extents->width = SkiaScalarToHarfBuzzPosition(sk_bounds.width()); |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 266 hb_face_t* get() { | 266 hb_face_t* get() { |
| 267 return face_; | 267 return face_; |
| 268 } | 268 } |
| 269 | 269 |
| 270 private: | 270 private: |
| 271 hb_face_t* face_; | 271 hb_face_t* face_; |
| 272 }; | 272 }; |
| 273 | 273 |
| 274 // Creates a HarfBuzz font from the given Skia face and text size. | 274 // Creates a HarfBuzz font from the given Skia face and text size. |
| 275 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, | 275 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, |
| 276 int text_size, | 276 SkScalar text_size, |
| 277 const FontRenderParams& params, | 277 const FontRenderParams& params, |
| 278 bool background_is_transparent) { | 278 bool background_is_transparent) { |
| 279 typedef std::pair<HarfBuzzFace, GlyphCache> FaceCache; | 279 typedef std::pair<HarfBuzzFace, GlyphCache> FaceCache; |
| 280 | 280 |
| 281 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache? | 281 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache? |
| 282 static std::map<SkFontID, FaceCache> face_caches; | 282 static std::map<SkFontID, FaceCache> face_caches; |
| 283 | 283 |
| 284 FaceCache* face_cache = &face_caches[skia_face->uniqueID()]; | 284 FaceCache* face_cache = &face_caches[skia_face->uniqueID()]; |
| 285 if (face_cache->first.get() == NULL) | 285 if (face_cache->first.get() == NULL) |
| 286 face_cache->first.Init(skia_face); | 286 face_cache->first.Init(skia_face); |
| (...skipping 588 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 875 | 875 |
| 876 for (size_t i = 0; i < runs_.size(); ++i) { | 876 for (size_t i = 0; i < runs_.size(); ++i) { |
| 877 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; | 877 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; |
| 878 internal::LineSegment segment; | 878 internal::LineSegment segment; |
| 879 segment.x_range = Range(current_x, current_x + run.width); | 879 segment.x_range = Range(current_x, current_x + run.width); |
| 880 segment.char_range = run.range; | 880 segment.char_range = run.range; |
| 881 segment.run = i; | 881 segment.run = i; |
| 882 lines[0].segments.push_back(segment); | 882 lines[0].segments.push_back(segment); |
| 883 | 883 |
| 884 paint.setTypeface(run.skia_face.get()); | 884 paint.setTypeface(run.skia_face.get()); |
| 885 paint.setTextSize(run.font_size); | 885 paint.setTextSize(SkIntToScalar(run.font_size)); |
| 886 SkPaint::FontMetrics metrics; | 886 SkPaint::FontMetrics metrics; |
| 887 paint.getFontMetrics(&metrics); | 887 paint.getFontMetrics(&metrics); |
| 888 | 888 |
| 889 lines[0].size.set_width(lines[0].size.width() + run.width); | 889 lines[0].size.set_width(lines[0].size.width() + run.width); |
| 890 lines[0].size.set_height(std::max(lines[0].size.height(), | 890 lines[0].size.set_height(std::max(lines[0].size.height(), |
| 891 metrics.fDescent - metrics.fAscent)); | 891 metrics.fDescent - metrics.fAscent)); |
| 892 lines[0].baseline = std::max(lines[0].baseline, | 892 lines[0].baseline = std::max(lines[0].baseline, |
| 893 SkScalarRoundToInt(-metrics.fAscent)); | 893 SkScalarRoundToInt(-metrics.fAscent)); |
| 894 } | 894 } |
| 895 | 895 |
| 896 set_lines(&lines); | 896 set_lines(&lines); |
| 897 } | 897 } |
| 898 } | 898 } |
| 899 | 899 |
| 900 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { | 900 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { |
| 901 DCHECK(!needs_layout_); | 901 DCHECK(!needs_layout_); |
| 902 internal::SkiaTextRenderer renderer(canvas); | 902 internal::SkiaTextRenderer renderer(canvas); |
| 903 ApplyFadeEffects(&renderer); | 903 ApplyFadeEffects(&renderer); |
| 904 ApplyTextShadows(&renderer); | 904 ApplyTextShadows(&renderer); |
| 905 ApplyCompositionAndSelectionStyles(); | 905 ApplyCompositionAndSelectionStyles(); |
| 906 | 906 |
| 907 int current_x = 0; | 907 int current_x = 0; |
| 908 const Vector2d line_offset = GetLineOffset(0); | 908 const Vector2d line_offset = GetLineOffset(0); |
| 909 for (size_t i = 0; i < runs_.size(); ++i) { | 909 for (size_t i = 0; i < runs_.size(); ++i) { |
| 910 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; | 910 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; |
| 911 renderer.SetTypeface(run.skia_face.get()); | 911 renderer.SetTypeface(run.skia_face.get()); |
| 912 renderer.SetTextSize(run.font_size); | 912 renderer.SetTextSize(SkIntToScalar(run.font_size)); |
| 913 renderer.SetFontRenderParams(run.render_params, | 913 renderer.SetFontRenderParams(run.render_params, |
| 914 background_is_transparent()); | 914 background_is_transparent()); |
| 915 | 915 |
| 916 Vector2d origin = line_offset + Vector2d(current_x, lines()[0].baseline); | 916 Vector2d origin = line_offset + Vector2d(current_x, lines()[0].baseline); |
| 917 scoped_ptr<SkPoint[]> positions(new SkPoint[run.glyph_count]); | 917 scoped_ptr<SkPoint[]> positions(new SkPoint[run.glyph_count]); |
| 918 for (size_t j = 0; j < run.glyph_count; ++j) { | 918 for (size_t j = 0; j < run.glyph_count; ++j) { |
| 919 positions[j] = run.positions[j]; | 919 positions[j] = run.positions[j]; |
| 920 positions[j].offset(SkIntToScalar(origin.x()), SkIntToScalar(origin.y())); | 920 positions[j].offset(SkIntToScalar(origin.x()), SkIntToScalar(origin.y())); |
| 921 } | 921 } |
| 922 | 922 |
| 923 for (BreakList<SkColor>::const_iterator it = | 923 for (BreakList<SkColor>::const_iterator it = |
| 924 colors().GetBreak(run.range.start()); | 924 colors().GetBreak(run.range.start()); |
| 925 it != colors().breaks().end() && it->first < run.range.end(); | 925 it != colors().breaks().end() && it->first < run.range.end(); |
| 926 ++it) { | 926 ++it) { |
| 927 const Range intersection = colors().GetRange(it).Intersect(run.range); | 927 const Range intersection = colors().GetRange(it).Intersect(run.range); |
| 928 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | 928 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); |
| 929 // The range may be empty if a portion of a multi-character grapheme is | 929 // The range may be empty if a portion of a multi-character grapheme is |
| 930 // selected, yielding two colors for a single glyph. For now, this just | 930 // selected, yielding two colors for a single glyph. For now, this just |
| 931 // paints the glyph with a single style, but it should paint it twice, | 931 // paints the glyph with a single style, but it should paint it twice, |
| 932 // clipped according to selection bounds. See http://crbug.com/366786 | 932 // clipped according to selection bounds. See http://crbug.com/366786 |
| 933 if (colored_glyphs.is_empty()) | 933 if (colored_glyphs.is_empty()) |
| 934 continue; | 934 continue; |
| 935 | 935 |
| 936 renderer.SetForegroundColor(it->second); | 936 renderer.SetForegroundColor(it->second); |
| 937 renderer.DrawPosText(&positions[colored_glyphs.start()], | 937 renderer.DrawPosText(&positions[colored_glyphs.start()], |
| 938 &run.glyphs[colored_glyphs.start()], | 938 &run.glyphs[colored_glyphs.start()], |
| 939 colored_glyphs.length()); | 939 colored_glyphs.length()); |
| 940 int width = (colored_glyphs.end() == run.glyph_count ? run.width : | 940 // !!! Should this be SkScalarRoundToInt, or SkScalarCeilToInt? |
|
msw
2014/10/17 22:13:23
Again, Dan or Cem might have more informed advice,
Daniel Erat
2014/10/17 23:20:05
i'm not sure about this either, but rounding seems
Peter Kasting
2014/10/21 01:20:45
After in-person discussion, I fixed bugs in this c
| |
| 941 run.positions[colored_glyphs.end()].x()) - | 941 int width = SkScalarFloorToInt( |
| 942 run.positions[colored_glyphs.start()].x(); | 942 (colored_glyphs.end() == run.glyph_count ? |
| 943 run.width : run.positions[colored_glyphs.end()].x()) - | |
| 944 run.positions[colored_glyphs.start()].x()); | |
| 943 renderer.DrawDecorations(origin.x(), origin.y(), width, run.underline, | 945 renderer.DrawDecorations(origin.x(), origin.y(), width, run.underline, |
| 944 run.strike, run.diagonal_strike); | 946 run.strike, run.diagonal_strike); |
| 945 } | 947 } |
| 946 | 948 |
| 947 current_x += run.width; | 949 current_x += run.width; |
| 948 } | 950 } |
| 949 | 951 |
| 950 renderer.EndDiagonalStrike(); | 952 renderer.EndDiagonalStrike(); |
| 951 | 953 |
| 952 UndoCompositionAndSelectionStyles(); | 954 UndoCompositionAndSelectionStyles(); |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1138 skia::RefPtr<SkTypeface> skia_face = | 1140 skia::RefPtr<SkTypeface> skia_face = |
| 1139 internal::CreateSkiaTypeface(font_family, run->font_style); | 1141 internal::CreateSkiaTypeface(font_family, run->font_style); |
| 1140 if (skia_face == NULL) | 1142 if (skia_face == NULL) |
| 1141 return false; | 1143 return false; |
| 1142 run->skia_face = skia_face; | 1144 run->skia_face = skia_face; |
| 1143 FontRenderParamsQuery query(false); | 1145 FontRenderParamsQuery query(false); |
| 1144 query.families.push_back(font_family); | 1146 query.families.push_back(font_family); |
| 1145 query.pixel_size = run->font_size; | 1147 query.pixel_size = run->font_size; |
| 1146 query.style = run->font_style; | 1148 query.style = run->font_style; |
| 1147 run->render_params = GetFontRenderParams(query, NULL); | 1149 run->render_params = GetFontRenderParams(query, NULL); |
| 1148 hb_font_t* harfbuzz_font = CreateHarfBuzzFont(run->skia_face.get(), | 1150 hb_font_t* harfbuzz_font = CreateHarfBuzzFont( |
| 1149 run->font_size, run->render_params, background_is_transparent()); | 1151 run->skia_face.get(), SkIntToScalar(run->font_size), run->render_params, |
| 1152 background_is_transparent()); | |
| 1150 | 1153 |
| 1151 // Create a HarfBuzz buffer and add the string to be shaped. The HarfBuzz | 1154 // Create a HarfBuzz buffer and add the string to be shaped. The HarfBuzz |
| 1152 // buffer holds our text, run information to be used by the shaping engine, | 1155 // buffer holds our text, run information to be used by the shaping engine, |
| 1153 // and the resulting glyph data. | 1156 // and the resulting glyph data. |
| 1154 hb_buffer_t* buffer = hb_buffer_create(); | 1157 hb_buffer_t* buffer = hb_buffer_create(); |
| 1155 hb_buffer_add_utf16(buffer, reinterpret_cast<const uint16*>(text.c_str()), | 1158 hb_buffer_add_utf16(buffer, reinterpret_cast<const uint16*>(text.c_str()), |
| 1156 text.length(), run->range.start(), run->range.length()); | 1159 text.length(), run->range.start(), run->range.length()); |
| 1157 hb_buffer_set_script(buffer, ICUScriptToHBScript(run->script)); | 1160 hb_buffer_set_script(buffer, ICUScriptToHBScript(run->script)); |
| 1158 hb_buffer_set_direction(buffer, | 1161 hb_buffer_set_direction(buffer, |
| 1159 run->is_rtl ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); | 1162 run->is_rtl ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); |
| 1160 // TODO(ckocagil): Should we determine the actual language? | 1163 // TODO(ckocagil): Should we determine the actual language? |
| 1161 hb_buffer_set_language(buffer, hb_language_get_default()); | 1164 hb_buffer_set_language(buffer, hb_language_get_default()); |
| 1162 | 1165 |
| 1163 // Shape the text. | 1166 // Shape the text. |
| 1164 hb_shape(harfbuzz_font, buffer, NULL, 0); | 1167 hb_shape(harfbuzz_font, buffer, NULL, 0); |
| 1165 | 1168 |
| 1166 // Populate the run fields with the resulting glyph data in the buffer. | 1169 // Populate the run fields with the resulting glyph data in the buffer. |
| 1167 unsigned int glyph_count = 0; | 1170 unsigned int glyph_count = 0; |
| 1168 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count); | 1171 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count); |
| 1169 run->glyph_count = glyph_count; | 1172 run->glyph_count = glyph_count; |
| 1170 hb_glyph_position_t* hb_positions = | 1173 hb_glyph_position_t* hb_positions = |
| 1171 hb_buffer_get_glyph_positions(buffer, NULL); | 1174 hb_buffer_get_glyph_positions(buffer, NULL); |
| 1172 run->glyphs.reset(new uint16[run->glyph_count]); | 1175 run->glyphs.reset(new uint16[run->glyph_count]); |
| 1173 run->glyph_to_char.resize(run->glyph_count); | 1176 run->glyph_to_char.resize(run->glyph_count); |
| 1174 run->positions.reset(new SkPoint[run->glyph_count]); | 1177 run->positions.reset(new SkPoint[run->glyph_count]); |
| 1175 run->width = 0.0f; | 1178 run->width = 0.0f; |
| 1176 for (size_t i = 0; i < run->glyph_count; ++i) { | 1179 for (size_t i = 0; i < run->glyph_count; ++i) { |
| 1177 run->glyphs[i] = infos[i].codepoint; | 1180 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); |
|
msw
2014/10/17 22:13:23
ditto nit q: DCHECK against uint32_t -> uint16_t o
Peter Kasting
2014/10/21 01:20:45
Added a DCHECK. Used numeric_limits::max here for
| |
| 1178 run->glyph_to_char[i] = infos[i].cluster; | 1181 run->glyph_to_char[i] = infos[i].cluster; |
| 1179 const int x_offset = SkFixedToScalar(hb_positions[i].x_offset); | 1182 const int x_offset = |
| 1180 const int y_offset = SkFixedToScalar(hb_positions[i].y_offset); | 1183 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_offset)); |
|
msw
2014/10/17 22:13:23
Hmm, this looks like it undoes my <https://coderev
Peter Kasting
2014/10/21 01:20:46
Yeah, this is a remnant of how the code looked bef
| |
| 1181 run->positions[i].set(run->width + x_offset, -y_offset); | 1184 const int y_offset = |
| 1182 run->width += SkFixedToScalar(hb_positions[i].x_advance); | 1185 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].y_offset)); |
| 1186 run->positions[i].iset(run->width + x_offset, -y_offset); | |
| 1187 run->width += | |
| 1188 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); | |
| 1183 #if defined(OS_LINUX) | 1189 #if defined(OS_LINUX) |
| 1184 // Match Pango's glyph rounding logic on Linux. | 1190 // Match Pango's glyph rounding logic on Linux. |
| 1185 if (!run->render_params.subpixel_positioning) | 1191 if (!run->render_params.subpixel_positioning) |
| 1186 run->width = std::floor(run->width + 0.5f); | 1192 run->width = std::floor(run->width + 0.5f); |
| 1187 #endif | 1193 #endif |
| 1188 } | 1194 } |
| 1189 | 1195 |
| 1190 hb_buffer_destroy(buffer); | 1196 hb_buffer_destroy(buffer); |
| 1191 hb_font_destroy(harfbuzz_font); | 1197 hb_font_destroy(harfbuzz_font); |
| 1192 return true; | 1198 return true; |
| 1193 } | 1199 } |
| 1194 | 1200 |
| 1195 } // namespace gfx | 1201 } // namespace gfx |
| OLD | NEW |