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

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

Issue 152473008: More or less implement RenderTextHarfBuzz (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 9 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 | Annotate | Revision Log
« no previous file with comments | « ui/gfx/render_text_harfbuzz.h ('k') | ui/gfx/render_text_mac.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/gfx/render_text_harfbuzz.h"
6
7 #include <stdlib.h>
8 #include <iterator>
9 #include <map>
10
11 #include "base/i18n/break_iterator.h"
12 #include "third_party/harfbuzz-ng/src/hb.h"
13 #include "third_party/icu/source/common/unicode/ubidi.h"
14 #include "third_party/skia/include/core/SkColor.h"
15 #include "third_party/skia/include/core/SkTypeface.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/render_text.h"
18 #include "ui/gfx/utf16_indexing.h"
19
20 namespace gfx {
21
22 namespace {
23
24 // Font data provider for HarfBuzz using Skia. Copied from Blink.
25 struct HarfBuzzFontData {
26 HarfBuzzFontData(std::map<uint32_t, uint16_t>* glyph_cache)
27 : glyph_cache_(glyph_cache) {}
28
29 SkPaint paint_;
30 std::map<uint32_t, uint16_t>* glyph_cache_;
31 };
32
33 hb_position_t SkiaScalarToHarfBuzzPosition(SkScalar value) {
34 return SkScalarToFixed(value);
35 }
36
37 void SkiaGetGlyphWidthAndExtents(SkPaint* paint,
38 hb_codepoint_t codepoint,
39 hb_position_t* width,
40 hb_glyph_extents_t* extents) {
41 CHECK(codepoint <= 0xFFFF);
42 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
43
44 SkScalar skWidth;
45 SkRect skBounds;
46 uint16_t glyph = codepoint;
47
48 paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds);
49 if (width)
50 *width = SkiaScalarToHarfBuzzPosition(skWidth);
51 if (extents) {
52 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be
53 // y-grows-up.
54 extents->x_bearing = SkiaScalarToHarfBuzzPosition(skBounds.fLeft);
55 extents->y_bearing = SkiaScalarToHarfBuzzPosition(-skBounds.fTop);
56 extents->width = SkiaScalarToHarfBuzzPosition(skBounds.width());
57 extents->height = SkiaScalarToHarfBuzzPosition(-skBounds.height());
58 }
59 }
60
61 hb_bool_t HarfBuzzGetGlyph(hb_font_t* font,
62 void* font_data,
63 hb_codepoint_t unicode,
64 hb_codepoint_t variation_selector,
65 hb_codepoint_t* glyph,
66 void* user_data) {
67 HarfBuzzFontData* hb_font_data =
68 reinterpret_cast<HarfBuzzFontData*>(font_data);
69 std::map<uint32_t, uint16_t>& cache = *hb_font_data->glyph_cache_;
70
71 bool exists = cache.count(unicode) != 0;
72 if (!exists) {
73 SkPaint* paint = &hb_font_data->paint_;
74 paint->setTextEncoding(SkPaint::kUTF32_TextEncoding);
75 uint16_t glyph16;
76 paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &glyph16);
77 cache[unicode] = glyph16;
78 *glyph = glyph16;
79 }
80 *glyph = cache[unicode];
81 return !!*glyph;
82 }
83
84 hb_position_t HarfBuzzGetGlyphHorizontalAdvance(hb_font_t* font,
85 void* font_data,
86 hb_codepoint_t glyph,
87 void* user_data) {
88 HarfBuzzFontData* hb_font_data =
89 reinterpret_cast<HarfBuzzFontData*>(font_data);
90 hb_position_t advance = 0;
91
92 SkiaGetGlyphWidthAndExtents(&hb_font_data->paint_, glyph, &advance, 0);
93 return advance;
94 }
95
96 hb_bool_t HarfBuzzGetGlyphHorizontalOrigin(hb_font_t* font,
97 void* font_data,
98 hb_codepoint_t glyph,
99 hb_position_t* x,
100 hb_position_t* y,
101 void* user_data) {
102 // Just return true, following the way that HarfBuzz-FreeType
103 // implementation does.
104 return true;
105 }
106
107 hb_bool_t HarfBuzzGetGlyphExtents(hb_font_t* font,
108 void* font_data,
109 hb_codepoint_t glyph,
110 hb_glyph_extents_t* extents,
111 void* user_data) {
112 HarfBuzzFontData* hb_font_data =
113 reinterpret_cast<HarfBuzzFontData*>(font_data);
114
115 SkiaGetGlyphWidthAndExtents(&hb_font_data->paint_, glyph, 0, extents);
116 return true;
117 }
118
119 hb_font_funcs_t* HarfBuzzSkiaGetFontFuncs() {
120 static hb_font_funcs_t* harfbuzz_skia_font_funcs = 0;
121
122 // We don't set callback functions which we can't support.
123 // HarfBuzz will use the fallback implementation if they aren't set.
124 if (!harfbuzz_skia_font_funcs) {
125 harfbuzz_skia_font_funcs = hb_font_funcs_create();
126 hb_font_funcs_set_glyph_func(
127 harfbuzz_skia_font_funcs, HarfBuzzGetGlyph, 0, 0);
128 hb_font_funcs_set_glyph_h_advance_func(
129 harfbuzz_skia_font_funcs, HarfBuzzGetGlyphHorizontalAdvance, 0, 0);
130 hb_font_funcs_set_glyph_h_origin_func(
131 harfbuzz_skia_font_funcs, HarfBuzzGetGlyphHorizontalOrigin, 0, 0);
132 hb_font_funcs_set_glyph_extents_func(
133 harfbuzz_skia_font_funcs, HarfBuzzGetGlyphExtents, 0, 0);
134 hb_font_funcs_make_immutable(harfbuzz_skia_font_funcs);
135 }
136 return harfbuzz_skia_font_funcs;
137 }
138
139 hb_blob_t* HarfBuzzSkiaGetTable(hb_face_t* face,
140 hb_tag_t tag,
141 void* user_data) {
142 SkTypeface* typeface = reinterpret_cast<SkTypeface*>(user_data);
143
144 const size_t table_size = typeface->getTableSize(tag);
145 if (!table_size)
146 return 0;
147
148 char* buffer = reinterpret_cast<char*>(malloc(table_size));
149 if (!buffer)
150 return 0;
151 size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer);
152 if (table_size != actual_size) {
153 free(buffer);
154 return 0;
155 }
156
157 return hb_blob_create(const_cast<char*>(buffer), table_size,
158 HB_MEMORY_MODE_WRITABLE, buffer, free);
159 }
160
161 template<typename Type>
162 void DeleteByType(void* data) {
163 Type* typed_data = reinterpret_cast<Type*>(data);
164 delete typed_data;
165 }
166
167 // TODO(ckocagil): Implement a cache for HarfBuzz faces. SkTypeface provides
168 // unique face IDs, which we can use as keys. Also see base::MRUCache.
169 hb_face_t* CreateHarfBuzzFace(SkTypeface* skia_face) {
170 hb_face_t* face = hb_face_create_for_tables(HarfBuzzSkiaGetTable, skia_face,
171 0);
172 CHECK(face);
173 return face;
174 }
175
176 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, int text_size) {
177 typedef std::map<uint32_t, uint16_t> GlyphCache;
178 // TODO: This shouldn't grow indefinitely. See the comment above
179 // CreateHarfBuzzFace;
180 static std::map<SkFontID, GlyphCache> glyph_caches;
181
182 SkFontID font_id = skia_face->uniqueID();
183 if (glyph_caches.count(font_id) == 0)
184 glyph_caches[font_id] = GlyphCache();
185
186 hb_face_t* harfbuzz_face = CreateHarfBuzzFace(skia_face);
187 hb_font_t* harfbuzz_font = hb_font_create(harfbuzz_face);
188 unsigned int upem = hb_face_get_upem(harfbuzz_face);
189 hb_face_destroy(harfbuzz_face);
190 hb_font_set_scale(harfbuzz_font, upem, upem);
191 HarfBuzzFontData* hb_font_data = new HarfBuzzFontData(&glyph_caches[font_id]);
192 hb_font_data->paint_.setTypeface(skia_face);
193 hb_font_data->paint_.setTextSize(text_size);
194 hb_font_set_funcs(harfbuzz_font, HarfBuzzSkiaGetFontFuncs(), hb_font_data,
195 DeleteByType<HarfBuzzFontData>);
196 hb_font_make_immutable(harfbuzz_font);
197 return harfbuzz_font;
198 }
199
200 size_t CharToGlyph(const internal::TextRunHarfBuzz& run, size_t pos) {
201 DCHECK(run.range.start() <= pos && pos < run.range.end());
202
203 if (run.direction == UBIDI_LTR) {
204 for (size_t i = 0; i < run.glyph_count - 1; ++i) {
205 if (pos < run.glyph_to_char[i + 1])
206 return i;
207 }
208 return run.glyph_count - 1;
209 }
210
211 for (size_t i = 0; i < run.glyph_count; ++i) {
212 if (pos >= run.glyph_to_char[i])
213 return i;
214 }
215 DCHECK(false);
216 return -1;
217 }
218
219 Range CharRangeToGlyphRange(const internal::TextRunHarfBuzz& run,
220 const Range& range) {
221 DCHECK(run.range.Contains(range));
222 DCHECK(!range.is_reversed());
223 DCHECK(!range.is_empty());
224
225 size_t first = CharToGlyph(run, range.start());
226 size_t last = CharToGlyph(run, range.end() - 1);
227 return Range(std::min(first, last), std::max(first, last) + 1);
228 }
229
230 } // namespace
231
232 namespace internal {
233
234 TextRunHarfBuzz::TextRunHarfBuzz()
235 : direction(UBIDI_LTR),
236 glyph_count(-1),
237 width(0) {}
238
239 TextRunHarfBuzz::~TextRunHarfBuzz() {
240 SkSafeUnref(skia_face);
241 }
242
243 } // namespace internal
244
245 RenderTextHarfBuzz::RenderTextHarfBuzz()
246 : RenderText(),
247 num_runs_(0),
248 needs_layout_(true) {}
249
250 RenderTextHarfBuzz::~RenderTextHarfBuzz() {}
251
252 Size RenderTextHarfBuzz::GetStringSize() {
253 EnsureLayout();
254 int width = 0;
255 for (size_t i = 0; i < num_runs_; ++i)
256 width += runs_[i].width;
257 return Size(width, font_list().GetHeight());
258 }
259
260 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) {
261 EnsureLayout();
262
263 int x = ToTextPoint(point).x();
264 int offset = 0;
265 size_t run_index = GetRunContainingXCoord(x, &offset);
266 if (run_index >= num_runs_)
267 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
268 internal::TextRunHarfBuzz& run = runs_[run_index];
269
270 for (size_t i = 0; i < run.glyph_count; ++i) {
271 const SkScalar end =
272 i + 1 == run.glyph_count ? run.width : run.positions[i + 1].x();
273 const SkScalar middle = (end + run.positions[i].x()) / 2;
274 if (offset < middle)
275 return SelectionModel(LayoutIndexToTextIndex(
276 run.glyph_to_char[i] + (run.direction == UBIDI_RTL)),
277 CURSOR_BACKWARD);
278 if (offset < end)
279 return SelectionModel(LayoutIndexToTextIndex(
280 run.glyph_to_char[i] + (run.direction == UBIDI_LTR)),
281 CURSOR_FORWARD);
282 }
283 return EdgeSelectionModel(CURSOR_RIGHT);
284 }
285
286 bool RenderTextHarfBuzz::IsCursorablePosition(size_t position) {
287 if (position == 0 || position == text().length())
288 return true;
289 EnsureLayout();
290
291 // TODO: Can this be simplified by using |TextRunHarfBuzz::glyph_to_char|?
292 // Check that the index is at a valid code point (not mid-surrgate-pair),
293 // that it is not truncated from layout text (its glyph is shown on screen),
294 // and that its glyph has distinct bounds (not mid-multi-character-grapheme).
295 // An example of a multi-character-grapheme that is not a surrogate-pair is:
296 // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts.
297 return gfx::IsValidCodePointIndex(text(), position) &&
298 position < LayoutIndexToTextIndex(GetLayoutText().length()) &&
299 GetGlyphBounds(position) != GetGlyphBounds(position - 1);
300 }
301
302 std::vector<RenderText::FontSpan> RenderTextHarfBuzz::GetFontSpansForTesting() {
303 NOTIMPLEMENTED();
304 return std::vector<RenderText::FontSpan>();
305 }
306
307 int RenderTextHarfBuzz::GetLayoutTextBaseline() {
308 EnsureLayout();
309 return lines()[0].baseline;
310 }
311
312 SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel(
313 const SelectionModel& selection,
314 VisualCursorDirection direction) {
315 DCHECK(!needs_layout_);
316 internal::TextRunHarfBuzz* run;
317 size_t run_index = GetRunContainingCaret(selection);
318 if (run_index >= num_runs_) {
319 // The cursor is not in any run: we're at the visual and logical edge.
320 SelectionModel edge = EdgeSelectionModel(direction);
321 if (edge.caret_pos() == selection.caret_pos())
322 return edge;
323 run_index = (direction == CURSOR_RIGHT) ? 0 : num_runs_ - 1;
324 run = &runs_[run_index];
325 } else {
326 // If the cursor is moving within the current run, just move it by one
327 // grapheme in the appropriate direction.
328 run = &runs_[run_index];
329 size_t caret = selection.caret_pos();
330 bool forward_motion =
331 (run->direction == UBIDI_RTL) == (direction == CURSOR_LEFT);
332 if (forward_motion) {
333 if (caret < LayoutIndexToTextIndex(run->range.end())) {
334 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
335 return SelectionModel(caret, CURSOR_BACKWARD);
336 }
337 } else {
338 if (caret > LayoutIndexToTextIndex(run->range.start())) {
339 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
340 return SelectionModel(caret, CURSOR_FORWARD);
341 }
342 }
343 // The cursor is at the edge of a run; move to the visually adjacent run.
344 run_index += (direction == CURSOR_LEFT) ? -1 : 1;
345 if (run_index < 0 || run_index >= static_cast<int>(num_runs_))
346 return EdgeSelectionModel(direction);
347 run = &runs_[run_index];
348 }
349 bool forward_motion =
350 (run->direction == UBIDI_RTL) == (direction == CURSOR_LEFT);
351 return forward_motion ? FirstSelectionModelInsideRun(run) :
352 LastSelectionModelInsideRun(run);
353 }
354
355 SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel(
356 const SelectionModel& selection,
357 VisualCursorDirection direction) {
358 if (obscured())
359 return EdgeSelectionModel(direction);
360
361 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
362 bool success = iter.Init();
363 DCHECK(success);
364 if (!success)
365 return selection;
366
367 size_t pos;
368 if (direction == CURSOR_RIGHT) {
369 pos = std::min(selection.caret_pos() + 1, text().length());
370 while (iter.Advance()) {
371 pos = iter.pos();
372 if (iter.IsWord() && pos > selection.caret_pos())
373 break;
374 }
375 } else { // direction == CURSOR_LEFT
376 // Notes: We always iterate words from the beginning.
377 // This is probably fast enough for our usage, but we may
378 // want to modify WordIterator so that it can start from the
379 // middle of string and advance backwards.
380 pos = std::max<int>(selection.caret_pos() - 1, 0);
381 while (iter.Advance()) {
382 if (iter.IsWord()) {
383 size_t begin = iter.pos() - iter.GetString().length();
384 if (begin == selection.caret_pos()) {
385 // The cursor is at the beginning of a word.
386 // Move to previous word.
387 break;
388 } else if (iter.pos() >= selection.caret_pos()) {
389 // The cursor is in the middle or at the end of a word.
390 // Move to the top of current word.
391 pos = begin;
392 break;
393 } else {
394 pos = iter.pos() - iter.GetString().length();
395 }
396 }
397 }
398 }
399 return SelectionModel(pos, CURSOR_FORWARD);
400 }
401
402 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) {
403 const size_t run_index =
404 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
405 // Return edge bounds if the index is invalid or beyond the layout text size.
406 if (run_index >= num_runs_)
407 return Range(GetStringSize().width());
408 const size_t layout_index = TextIndexToLayoutIndex(index);
409 return Range(GetGlyphXBoundary(run_index, layout_index, false),
410 GetGlyphXBoundary(run_index, layout_index, true));
411 }
412
413 std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) {
414 DCHECK(!needs_layout_);
415 DCHECK(Range(0, text().length()).Contains(range));
416 Range layout_range(TextIndexToLayoutIndex(range.start()),
417 TextIndexToLayoutIndex(range.end()));
418 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range));
419
420 std::vector<Rect> rects;
421 if (layout_range.is_empty())
422 return rects;
423 std::vector<Range> bounds;
424
425 // Add a Range for each run/selection intersection.
426 // TODO(msw): The bounds should probably not always be leading the range ends.
427 for (size_t i = 0; i < num_runs_; ++i) {
428 Range intersection = runs_[i].range.Intersect(layout_range);
429 if (intersection.IsValid()) {
430 DCHECK(!intersection.is_reversed());
431 Range range_x(GetGlyphXBoundary(i, intersection.start(), false),
432 GetGlyphXBoundary(i, intersection.end(), false));
433 if (range_x.is_empty())
434 continue;
435 range_x = Range(range_x.GetMin(), range_x.GetMax());
436 // Union this with the last range if they're adjacent.
437 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin());
438 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) {
439 range_x = Range(bounds.back().GetMin(), range_x.GetMax());
440 bounds.pop_back();
441 }
442 bounds.push_back(range_x);
443 }
444 }
445 for (size_t i = 0; i < bounds.size(); ++i) {
446 std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]);
447 rects.insert(rects.end(), current_rects.begin(), current_rects.end());
448 }
449 return rects;
450 }
451
452 size_t RenderTextHarfBuzz::TextIndexToLayoutIndex(size_t index) const {
453 DCHECK_LE(index, text().length());
454 ptrdiff_t i = obscured() ? UTF16IndexToOffset(text(), 0, index) : index;
455 CHECK_GE(i, 0);
456 // Clamp layout indices to the length of the text actually used for layout.
457 return std::min<size_t>(GetLayoutText().length(), i);
458 }
459
460 size_t RenderTextHarfBuzz::LayoutIndexToTextIndex(size_t index) const {
461 if (!obscured())
462 return index;
463
464 DCHECK_LE(index, GetLayoutText().length());
465 const size_t text_index = UTF16OffsetToIndex(text(), 0, index);
466 DCHECK_LE(text_index, text().length());
467 return text_index;
468 }
469
470 void RenderTextHarfBuzz::ResetLayout() {
471 needs_layout_ = true;
472 }
473
474 void RenderTextHarfBuzz::EnsureLayout() {
475 if (needs_layout_) {
476 // TODO: Skip complex processing if text isn't complex.
477 // TODO: Handle styles.
478 UErrorCode result = U_ZERO_ERROR;
479 const base::string16& text = GetLayoutText();
480
481 UBiDi* line = ubidi_openSized(text.length(), 0, &result);
482 CHECK(U_SUCCESS(result));
483
484 ubidi_setPara(line, text.c_str(), text.length(), UBIDI_DEFAULT_LTR, NULL,
485 &result);
486 CHECK(U_SUCCESS(result));
487
488 num_runs_ = ubidi_countRuns(line, &result);
489 CHECK(U_SUCCESS(result));
490
491 runs_.reset(new internal::TextRunHarfBuzz[num_runs_]);
492 for (size_t i = 0; i < num_runs_; ++i) {
493 internal::TextRunHarfBuzz& run = runs_[i];
494 int32 start = 0;
495 int32 length = 0;
496 UBiDiDirection direction = ubidi_getVisualRun(line, i, &start, &length);
497 DCHECK_GT(length, 0);
498 ShapeRun(Range(start, start + length), direction, &runs_[i]);
499 }
500
501 ubidi_close(line);
502 needs_layout_ = false;
503 std::vector<internal::Line> empty_lines;
504 set_lines(&empty_lines);
505 }
506
507 if (lines().empty()) {
508 // TODO: implement single-line lines() support for now.
509 std::vector<internal::Line> lines;
510 lines.push_back(internal::Line());
511 int current_x = 0;
512 for (size_t i = 0; i < num_runs_; ++i) {
513 const internal::TextRunHarfBuzz& run = runs_[i];
514 internal::LineSegment segment;
515 segment.x_range = Range(current_x, current_x + run.width);
516 segment.char_range = run.range;
517 segment.run = i;
518 lines[0].segments.push_back(segment);
519
520 Canvas canvas;
521 internal::SkiaTextRenderer renderer(&canvas);
522 renderer.SetTypeface(run.skia_face);
523 renderer.SetTextSize(run.font_size);
524 SkPaint::FontMetrics metrics;
525 renderer.paint().getFontMetrics(&metrics);
526
527 lines[0].size.set_width(lines[0].size.width() + run.width);
528 lines[0].size.set_height(std::max(lines[0].size.height(),
529 SkScalarRoundToInt(metrics.fDescent - metrics.fAscent)));
530 lines[0].baseline = std::max(lines[0].baseline,
531 SkScalarRoundToInt(-metrics.fAscent));
532 }
533 set_lines(&lines);
534 }
535 }
536
537 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) {
538 int current_x = 0;
539
540 internal::SkiaTextRenderer renderer(canvas);
541 ApplyFadeEffects(&renderer);
542 ApplyTextShadows(&renderer);
543
544 ApplyCompositionAndSelectionStyles();
545
546 const Vector2d line_offset = GetLineOffset(0);
547
548 for (size_t i = 0; i < num_runs_; ++i) {
549 const internal::TextRunHarfBuzz& run = runs_[i];
550
551 renderer.SetTypeface(run.skia_face);
552 renderer.SetTextSize(run.font_size);
553
554 canvas->Save();
555 canvas->Translate(line_offset + Vector2d(current_x, 0));
556
557 for (BreakList<SkColor>::const_iterator it =
558 colors().GetBreak(run.range.start());
559 it != colors().breaks().end() && it->first < run.range.end();
560 ++it) {
561 const Range intersection = colors().GetRange(it).Intersect(run.range);
562 const Range colored_glyphs = CharRangeToGlyphRange(run, intersection);
563 DCHECK(!colored_glyphs.is_empty());
564
565 renderer.SetForegroundColor(it->second);
566 renderer.DrawPosText(&run.positions[colored_glyphs.start()],
567 &run.glyphs[colored_glyphs.start()],
568 colored_glyphs.length());
569 }
570
571 canvas->Restore();
572 current_x += run.width;
573 }
574
575 UndoCompositionAndSelectionStyles();
576 }
577
578 void RenderTextHarfBuzz::ShapeRun(Range range,
579 UBiDiDirection direction,
580 internal::TextRunHarfBuzz* run) {
581 const base::string16& text = GetLayoutText();
582
583 run->range = range;
584 run->direction = direction;
585
586 const Font& primary_font = font_list().GetPrimaryFont();
587 Canvas canvas;
588 internal::SkiaTextRenderer renderer(&canvas);
589
590 ApplyFadeEffects(&renderer);
591 ApplyTextShadows(&renderer);
592
593 renderer.SetFontSmoothingSettings(true, true);
594 renderer.SetTextSize(primary_font.GetFontSize());
595 renderer.SetFontFamilyWithStyle(primary_font.GetFontName(), 0);
596
597 run->skia_face = SkRef(renderer.paint().getTypeface());
598 run->font_size = renderer.paint().getTextSize();
599 hb_font_t* harfbuzz_font = CreateHarfBuzzFont(run->skia_face, run->font_size);
600
601 hb_buffer_t* text_buffer = hb_buffer_create();
602 hb_buffer_add_utf16(text_buffer,
603 reinterpret_cast<const uint16*>(text.c_str()),
604 text.length(), run->range.start(), run->range.length());
605 hb_buffer_guess_segment_properties(text_buffer);
eae 2014/03/07 18:43:06 You really don't want to do this. We've had a lot
ckocagil 2014/05/01 22:02:00 Done, we now split text by script runs taking into
606
607 hb_shape(harfbuzz_font, text_buffer, NULL, 0);
608
609 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(text_buffer,
610 &run->glyph_count);
611 CHECK(run->glyph_count != -1);
612 run->glyphs.reset(new uint16[run->glyph_count]);
613 for (size_t j = 0; j < run->glyph_count; ++j)
614 run->glyphs[j] = infos[j].codepoint;
615
616 run->glyph_to_char.reset(new uint32[run->glyph_count]);
617 for (size_t j = 0; j < run->glyph_count; ++j)
618 run->glyph_to_char[j] = infos[j].cluster;
619
620 SkPaint::FontMetrics metrics;
621 renderer.paint().getFontMetrics(&metrics);
622
623 hb_glyph_position_t* hb_positions =
624 hb_buffer_get_glyph_positions(text_buffer, NULL);
625
626 run->positions.reset(new SkPoint[run->glyph_count]);
627
628 for (size_t j = 0; j < run->glyph_count; ++j) {
629 const int x_offset =
630 SkScalarRoundToInt(SkFixedToScalar(hb_positions[j].x_offset));
631 const int y_offset =
632 SkScalarRoundToInt(SkFixedToScalar(hb_positions[j].y_offset));
633 run->positions[j].set(run->width + x_offset,
634 y_offset - metrics.fAscent);
635 run->width +=
636 SkScalarRoundToInt(SkFixedToScalar(hb_positions[j].x_advance));
637 }
638
639 hb_buffer_destroy(text_buffer);
640 hb_font_destroy(harfbuzz_font);
641 }
642
643 size_t RenderTextHarfBuzz::GetRunContainingCaret(
644 const SelectionModel& caret) const {
645 DCHECK(!needs_layout_);
646 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos());
647 LogicalCursorDirection affinity = caret.caret_affinity();
648 for (size_t run = 0; run < num_runs_; ++run)
649 if (RangeContainsCaret(runs_[run].range, layout_position, affinity))
650 return run;
651 return num_runs_;
652 }
653
654 int RenderTextHarfBuzz::GetGlyphXBoundary(size_t run_index,
655 size_t text_index,
656 bool trailing) {
657 internal::TextRunHarfBuzz& run = runs_[run_index];
658
659 int x = 0;
660
661 for (size_t i = 0; i < run_index; ++i)
662 x += runs_[i].width;
663
664 const bool last_glyph = text_index == run.range.end();
665 Range glyph_range;
666 if (last_glyph) {
667 trailing = true;
668 glyph_range = run.direction == UBIDI_LTR ?
669 Range(run.glyph_count - 1, run.glyph_count) : Range(0, 1);
670 } else {
671 glyph_range = CharRangeToGlyphRange(run, Range(text_index, text_index + 1));
672 }
673 int trailing_step = trailing;
674 size_t glyph_pos = glyph_range.start() +
675 (run.direction == UBIDI_LTR ? trailing : !trailing);
676 x += glyph_pos < run.glyph_count ?
677 SkScalarRoundToInt(run.positions[glyph_pos].x()) : run.width;
678 return x;
679 }
680
681 SelectionModel RenderTextHarfBuzz::FirstSelectionModelInsideRun(
682 const internal::TextRunHarfBuzz* run) {
683 size_t position = LayoutIndexToTextIndex(run->range.start());
684 position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD);
685 return SelectionModel(position, CURSOR_BACKWARD);
686 }
687
688 SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun(
689 const internal::TextRunHarfBuzz* run) {
690 size_t position = LayoutIndexToTextIndex(run->range.end());
691 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
692 return SelectionModel(position, CURSOR_FORWARD);
693 }
694
695 size_t RenderTextHarfBuzz::GetRunContainingXCoord(int x, int* offset) const {
696 DCHECK(!needs_layout_);
697 if (x < 0)
698 return num_runs_;
699 // Find the text run containing the argument point (assumed already offset).
700 int current_x = 0;
701 for (size_t run = 0; run < num_runs_; ++run) {
702 current_x += runs_[run].width;
703 if (x < current_x) {
704 *offset = x - (current_x - runs_[run].width);
705 return run;
706 }
707 }
708 return num_runs_;
709 }
710
711 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text_harfbuzz.h ('k') | ui/gfx/render_text_mac.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698