Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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_win.h" | 5 #include "ui/gfx/render_text_win.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/i18n/break_iterator.h" | 9 #include "base/i18n/break_iterator.h" |
| 10 #include "base/i18n/rtl.h" | 10 #include "base/i18n/rtl.h" |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 118 bool IsUnicodeBidiControlCharacter(char16 c) { | 118 bool IsUnicodeBidiControlCharacter(char16 c) { |
| 119 return c == base::i18n::kRightToLeftMark || | 119 return c == base::i18n::kRightToLeftMark || |
| 120 c == base::i18n::kLeftToRightMark || | 120 c == base::i18n::kLeftToRightMark || |
| 121 c == base::i18n::kLeftToRightEmbeddingMark || | 121 c == base::i18n::kLeftToRightEmbeddingMark || |
| 122 c == base::i18n::kRightToLeftEmbeddingMark || | 122 c == base::i18n::kRightToLeftEmbeddingMark || |
| 123 c == base::i18n::kPopDirectionalFormatting || | 123 c == base::i18n::kPopDirectionalFormatting || |
| 124 c == base::i18n::kLeftToRightOverride || | 124 c == base::i18n::kLeftToRightOverride || |
| 125 c == base::i18n::kRightToLeftOverride; | 125 c == base::i18n::kRightToLeftOverride; |
| 126 } | 126 } |
| 127 | 127 |
| 128 ui::Range CharRangeToGlyphRange(const ui::Range& range, | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Add a comment.
ckocagil
2013/07/13 16:05:10
Done.
| |
| 129 internal::TextRun* run) { | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
This should be const and possibly by ref to preven
ckocagil
2013/07/13 16:05:10
Done.
| |
| 130 DCHECK_GE(range.start(), 0u); | |
| 131 DCHECK_LE(range.end(), run->range.length()); | |
|
msw
2013/07/10 04:01:56
nit: Can you just [D]CHECK(run->range.Contains(ran
ckocagil
2013/07/13 16:05:10
Done.
Done.
Done.
| |
| 132 DCHECK_LE(range.start(), range.end()); | |
| 133 bool rtl = run->script_analysis.fRTL; | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Inline this, since you only use it once.
ckocagil
2013/07/13 16:05:10
Done.
| |
| 134 int start_char = range.start(); | |
| 135 int end_char = range.end(); | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Nit: You don't need local vars for start_char and
ckocagil
2013/07/13 16:05:10
Done.
| |
| 136 int start, end; | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Nit: Separate lines.
msw
2013/07/10 04:01:56
Also, explicitly init each to 0.
ckocagil
2013/07/13 16:05:10
Both done.
| |
| 137 if (rtl) { | |
| 138 start = run->logical_clusters[end_char - 1]; | |
| 139 end = start_char > 0 ? run->logical_clusters[start_char - 1] | |
| 140 : run->glyph_count; | |
| 141 } else { | |
| 142 start = run->logical_clusters[start_char]; | |
| 143 end = end_char < run->glyph_count ? run->logical_clusters[end_char] | |
|
msw
2013/07/10 04:01:56
Shouldn't this be end_char < run->range.length() ?
ckocagil
2013/07/13 16:05:10
Shame on me, this would be a subtle bug. Thanks! D
| |
| 144 : run->glyph_count; | |
| 145 } | |
| 146 return ui::Range(start, end); | |
| 147 } | |
| 148 | |
| 128 } // namespace | 149 } // namespace |
| 129 | 150 |
| 130 namespace internal { | 151 namespace internal { |
| 131 | 152 |
| 132 TextRun::TextRun() | 153 TextRun::TextRun() |
| 133 : foreground(0), | 154 : foreground(0), |
| 134 font_style(0), | 155 font_style(0), |
| 135 strike(false), | 156 strike(false), |
| 136 diagonal_strike(false), | 157 diagonal_strike(false), |
| 137 underline(false), | 158 underline(false), |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 185 memset(&script_state_, 0, sizeof(script_state_)); | 206 memset(&script_state_, 0, sizeof(script_state_)); |
| 186 | 207 |
| 187 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); | 208 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); |
| 188 } | 209 } |
| 189 | 210 |
| 190 RenderTextWin::~RenderTextWin() { | 211 RenderTextWin::~RenderTextWin() { |
| 191 } | 212 } |
| 192 | 213 |
| 193 Size RenderTextWin::GetStringSize() { | 214 Size RenderTextWin::GetStringSize() { |
| 194 EnsureLayout(); | 215 EnsureLayout(); |
| 195 return string_size_; | 216 // TODO: is this the right place to add +1 for cursor? |
| 217 return Size(string_size_.width() + 1, string_size_.height()); | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Can you explain why this is needed (and why it was
msw
2013/07/10 04:01:56
I think it's already handled by RenderText::GetCon
ckocagil
2013/07/13 16:05:10
Yes, I later noticed it. Removing the +1 here.
| |
| 218 } | |
| 219 | |
| 220 Size RenderTextWin::GetMultilineTextSize() { | |
| 221 EnsureLayout(); | |
| 222 if (!multiline()) | |
| 223 return Size(display_rect().width(), string_size_.height()); | |
| 224 ComputeLines(); | |
|
msw
2013/07/10 04:01:56
Do this as needed in EnsureLayout().
ckocagil
2013/07/13 16:05:10
Done.
| |
| 225 return Size(display_rect().width(), | |
| 226 string_size_.height() * lines().size()); | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Are all lines guaranteed to have the same height?
ckocagil
2013/07/13 16:05:10
Done, we use per-line heights now.
| |
| 196 } | 227 } |
| 197 | 228 |
| 198 int RenderTextWin::GetBaseline() { | 229 int RenderTextWin::GetBaseline() { |
| 199 EnsureLayout(); | 230 EnsureLayout(); |
| 200 return common_baseline_; | 231 return common_baseline_; |
| 201 } | 232 } |
| 202 | 233 |
| 203 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { | 234 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { |
| 204 if (text().empty()) | 235 if (text().empty()) |
| 205 return SelectionModel(); | 236 return SelectionModel(); |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 352 GetGlyphXBoundary(run, layout_index, true)); | 383 GetGlyphXBoundary(run, layout_index, true)); |
| 353 } | 384 } |
| 354 | 385 |
| 355 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { | 386 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { |
| 356 DCHECK(!needs_layout_); | 387 DCHECK(!needs_layout_); |
| 357 DCHECK(ui::Range(0, text().length()).Contains(range)); | 388 DCHECK(ui::Range(0, text().length()).Contains(range)); |
| 358 ui::Range layout_range(TextIndexToLayoutIndex(range.start()), | 389 ui::Range layout_range(TextIndexToLayoutIndex(range.start()), |
| 359 TextIndexToLayoutIndex(range.end())); | 390 TextIndexToLayoutIndex(range.end())); |
| 360 DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range)); | 391 DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range)); |
| 361 | 392 |
| 362 std::vector<Rect> bounds; | 393 std::vector<ui::Range> bounds; |
| 394 std::vector<Rect> rects; | |
| 363 if (layout_range.is_empty()) | 395 if (layout_range.is_empty()) |
| 364 return bounds; | 396 return rects; |
| 365 | 397 |
| 366 // Add a Rect for each run/selection intersection. | 398 // Add a Rect for each run/selection intersection. |
| 367 // TODO(msw): The bounds should probably not always be leading the range ends. | 399 // TODO(msw): The bounds should probably not always be leading the range ends. |
| 368 for (size_t i = 0; i < runs_.size(); ++i) { | 400 for (size_t i = 0; i < runs_.size(); ++i) { |
| 369 const internal::TextRun* run = runs_[visual_to_logical_[i]]; | 401 const internal::TextRun* run = runs_[visual_to_logical_[i]]; |
| 370 ui::Range intersection = run->range.Intersect(layout_range); | 402 ui::Range intersection = run->range.Intersect(layout_range); |
| 371 if (intersection.IsValid()) { | 403 if (intersection.IsValid()) { |
| 372 DCHECK(!intersection.is_reversed()); | 404 DCHECK(!intersection.is_reversed()); |
| 373 ui::Range range_x(GetGlyphXBoundary(run, intersection.start(), false), | 405 ui::Range range_x(GetGlyphXBoundary(run, intersection.start(), false), |
| 374 GetGlyphXBoundary(run, intersection.end(), false)); | 406 GetGlyphXBoundary(run, intersection.end(), false)); |
| 375 Rect rect(range_x.GetMin(), 0, range_x.length(), run->font.GetHeight()); | 407 range_x = ui::Range(range_x.GetMin(), range_x.GetMax()); |
| 376 rect.set_origin(ToViewPoint(rect.origin())); | 408 // Union this with the last range if they're adjacent. |
| 377 // Union this with the last rect if they're adjacent. | 409 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { |
|
msw
2013/07/10 04:01:56
This will miss cases where range_x.GetMax() == bou
ckocagil
2013/07/13 16:05:10
Added a DCHECK for this case but it doesn't seem t
| |
| 378 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { | 410 range_x = ui::Range(bounds.back().GetMin(), range_x.GetMax()); |
| 379 rect.Union(bounds.back()); | |
| 380 bounds.pop_back(); | 411 bounds.pop_back(); |
| 381 } | 412 } |
| 382 bounds.push_back(rect); | 413 bounds.push_back(range_x); |
| 383 } | 414 } |
| 384 } | 415 } |
| 385 return bounds; | 416 for (size_t i = 0; i < bounds.size(); ++i) { |
| 417 std::vector<Rect> current_rects = RangeToViewRects(bounds[i], 0); | |
| 418 rects.insert(rects.end(), current_rects.begin(), current_rects.end()); | |
| 419 } | |
| 420 return rects; | |
| 386 } | 421 } |
| 387 | 422 |
| 388 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { | 423 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { |
| 389 if (!obscured()) | 424 if (!obscured()) |
| 390 return index; | 425 return index; |
| 391 | 426 |
| 392 DCHECK_LE(index, text().length()); | 427 DCHECK_LE(index, text().length()); |
| 393 const ptrdiff_t offset = ui::UTF16IndexToOffset(text(), 0, index); | 428 const ptrdiff_t offset = ui::UTF16IndexToOffset(text(), 0, index); |
| 394 DCHECK_GE(offset, 0); | 429 DCHECK_GE(offset, 0); |
| 395 DCHECK_LE(static_cast<size_t>(offset), GetLayoutText().length()); | 430 DCHECK_LE(static_cast<size_t>(offset), GetLayoutText().length()); |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 413 | 448 |
| 414 // Check if the index is at a valid code point (not mid-surrgate-pair) with | 449 // Check if the index is at a valid code point (not mid-surrgate-pair) with |
| 415 // distinct glyph bounds (not mid-multi-character-grapheme, eg. \x0915\x093f). | 450 // distinct glyph bounds (not mid-multi-character-grapheme, eg. \x0915\x093f). |
| 416 return ui::IsValidCodePointIndex(text(), position) && | 451 return ui::IsValidCodePointIndex(text(), position) && |
| 417 GetGlyphBounds(position) != GetGlyphBounds(position - 1); | 452 GetGlyphBounds(position) != GetGlyphBounds(position - 1); |
| 418 } | 453 } |
| 419 | 454 |
| 420 void RenderTextWin::ResetLayout() { | 455 void RenderTextWin::ResetLayout() { |
| 421 // Layout is performed lazily as needed for drawing/metrics. | 456 // Layout is performed lazily as needed for drawing/metrics. |
| 422 needs_layout_ = true; | 457 needs_layout_ = true; |
| 458 set_lines_valid(false); // TODO: do we need this? | |
|
msw
2013/07/10 04:01:56
I think lines_valid_ should implicitly be part of
ckocagil
2013/07/13 16:05:10
Done.
| |
| 423 } | 459 } |
| 424 | 460 |
| 425 void RenderTextWin::EnsureLayout() { | 461 void RenderTextWin::EnsureLayout() { |
| 426 if (!needs_layout_) | 462 if (!needs_layout_) |
| 427 return; | 463 return; |
| 464 | |
| 428 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. | 465 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. |
| 429 ItemizeLogicalText(); | 466 ItemizeLogicalText(); |
| 430 if (!runs_.empty()) | 467 if (!runs_.empty()) |
| 431 LayoutVisualText(); | 468 LayoutVisualText(); |
| 469 | |
| 432 needs_layout_ = false; | 470 needs_layout_ = false; |
| 433 } | 471 } |
| 434 | 472 |
| 473 void RenderTextWin::ComputeLines() { | |
| 474 if (lines_valid() || !multiline()) | |
| 475 return; | |
| 476 | |
| 477 EnsureLayout(); | |
| 478 | |
| 479 std::vector<ScopedVector<internal::LineSegment> > lines; | |
|
msw
2013/07/10 04:01:56
Should these be LineSegmentWin?
ckocagil
2013/07/13 16:05:10
We now use Line structs here.
| |
| 480 lines.push_back(ScopedVector<internal::LineSegment>()); | |
| 481 | |
| 482 int max_width = display_rect().width(); | |
| 483 | |
| 484 std::vector<size_t> breaks = line_breaks(); | |
| 485 for (size_t i = 0; i < runs_.size(); ++i) | |
| 486 breaks.push_back(runs_[i]->range.start()); | |
| 487 breaks.push_back(0); // TODO: necessary? | |
|
msw
2013/07/10 04:01:56
Probably not necessary, resolve the TODO.
ckocagil
2013/07/13 16:05:10
We need at least one element, now I'm only adding
| |
| 488 breaks.push_back(-1); | |
|
msw
2013/07/10 04:01:56
What's this unsigned wraparound max size_t used fo
ckocagil
2013/07/13 16:05:10
This was used as an anchor so we didn't go past th
| |
| 489 std::sort(breaks.begin(), breaks.end()); | |
| 490 std::unique(breaks.begin(), breaks.end()); | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Is this needed? If so, perhaps add a test that wil
ckocagil
2013/07/13 16:05:10
This shouldn't be needed. Removing.
| |
| 491 | |
| 492 size_t* last_break = &breaks[0]; | |
| 493 | |
| 494 int line_x = 0; // x from the line beginning | |
| 495 int text_x = 0; // x from the text beginning | |
| 496 size_t segment_start_char = 0; // offset of current segment beginning | |
| 497 int segment_start_pos = 0; // position of current segment beginning | |
| 498 internal::TextRun* run; | |
| 499 size_t line_beginning = 0; | |
| 500 | |
| 501 for (size_t i = 0; i < runs_.size(); ++i) { | |
|
msw
2013/07/10 04:01:56
This function needs comments explaining what it's
ckocagil
2013/07/13 16:05:10
Added a comment explaining what this function does
| |
| 502 // TODO: might need |line_break = range.start| to break at run beginnings | |
|
msw
2013/07/10 04:01:56
nit: resolve TODO
ckocagil
2013/07/13 16:05:10
Done.
| |
| 503 run = runs_[visual_to_logical_[i]]; | |
| 504 bool rtl = run->script_analysis.fRTL; | |
| 505 int break_x = 0; | |
| 506 | |
| 507 last_break = &breaks[0]; | |
| 508 | |
| 509 segment_start_char = run->range.start(); | |
| 510 segment_start_pos = text_x; | |
| 511 | |
| 512 int run_beginning_x = text_x; | |
| 513 typedef std::pair<internal::LineSegment*, int> RtlWidth; | |
|
msw
2013/07/10 04:01:56
Comment here, what's an RtlWidth?
ckocagil
2013/07/13 16:05:10
Done.
| |
| 514 std::vector<RtlWidth> rtl_widths; | |
| 515 | |
| 516 for (size_t c = 0; c < run->range.length(); ++c) { | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Use i, j, k for loops, so this should be j and the
msw
2013/07/10 04:01:56
There must be a better approach than fitting lines
ckocagil
2013/07/13 16:05:10
Actually I have thought about most of the stuff yo
ckocagil
2013/07/13 16:05:10
Done.
| |
| 517 size_t offset = c + run->range.start(); | |
| 518 while (offset >= *(last_break + 1)) { | |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Can you you use ui/gfx/break_list.h or are the sem
ckocagil
2013/07/13 16:05:10
I can use a break list but then every time I use G
| |
| 519 ++last_break; | |
| 520 break_x = 0; | |
| 521 } | |
| 522 | |
| 523 ui::Range glyphs = CharRangeToGlyphRange(ui::Range(c, c + 1), run); | |
| 524 int start = glyphs.start(); | |
| 525 int end = glyphs.end(); | |
| 526 | |
| 527 int char_width = 0; | |
| 528 for (int g = start; g < end; ++g) | |
| 529 char_width += run->advance_widths[g]; | |
| 530 | |
| 531 line_x += char_width; | |
| 532 text_x += char_width; | |
| 533 break_x += char_width; | |
| 534 | |
| 535 if (line_x > max_width) { | |
| 536 if (*last_break > line_beginning) { | |
| 537 // Roll back to last break. | |
| 538 text_x -= break_x; | |
| 539 c = *last_break - run->range.start() - 1; | |
| 540 offset = *last_break; | |
| 541 } else if (line_x != char_width) { | |
| 542 // Roll back one character. | |
| 543 text_x -= char_width; | |
| 544 --c; | |
| 545 } // else: Assume the current character fits; continue from next line. | |
| 546 line_beginning = offset; | |
| 547 line_x = 0; | |
| 548 break_x = 0; | |
| 549 | |
| 550 internal::LineSegmentWin* segment = new internal::LineSegmentWin; | |
| 551 if (rtl) | |
| 552 rtl_widths.push_back(RtlWidth(segment, text_x - segment_start_pos)); | |
| 553 else | |
| 554 segment->x_pos = ui::Range(segment_start_pos, text_x); | |
| 555 segment->char_pos = ui::Range(segment_start_char, offset); | |
| 556 segment->run = run; | |
| 557 lines.back().push_back(segment); | |
| 558 lines.push_back(ScopedVector<internal::LineSegment>()); | |
| 559 | |
| 560 segment_start_pos = text_x; | |
| 561 segment_start_char = offset; | |
| 562 continue; | |
| 563 } | |
| 564 } | |
| 565 if (segment_start_char != run->range.end()) { | |
| 566 internal::LineSegmentWin* segment = new internal::LineSegmentWin; | |
| 567 if (rtl) | |
| 568 rtl_widths.push_back(RtlWidth(segment, text_x - segment_start_pos)); | |
| 569 else | |
| 570 segment->x_pos = ui::Range(segment_start_pos, text_x); | |
| 571 segment->char_pos = ui::Range(segment_start_char, run->range.end()); | |
| 572 segment->run = run; | |
| 573 lines.back().push_back(segment); | |
| 574 segment_start_char = run->range.end(); | |
| 575 segment_start_pos = text_x; | |
| 576 } | |
| 577 | |
| 578 int rtl_total_widths = 0; | |
| 579 while (!rtl_widths.empty()) { | |
| 580 const RtlWidth& rtl_width = rtl_widths.back(); | |
| 581 rtl_width.first->x_pos = ui::Range(run_beginning_x + rtl_total_widths, | |
| 582 run_beginning_x + rtl_total_widths + rtl_width.second); | |
| 583 rtl_widths.pop_back(); | |
| 584 rtl_total_widths += rtl_width.second; | |
| 585 } | |
| 586 } | |
| 587 | |
| 588 set_lines(&lines); | |
| 589 set_lines_valid(true); | |
| 590 } | |
| 591 | |
| 435 void RenderTextWin::DrawVisualText(Canvas* canvas) { | 592 void RenderTextWin::DrawVisualText(Canvas* canvas) { |
| 436 DCHECK(!needs_layout_); | 593 DCHECK(!needs_layout_); |
| 437 | 594 |
| 438 // Skia will draw glyphs with respect to the baseline. | 595 if (multiline()) |
| 439 Vector2d offset(GetTextOffset() + Vector2d(0, common_baseline_)); | 596 ComputeLines(); |
|
msw
2013/07/10 04:01:56
I think ComputeLines should be done as a step in E
ckocagil
2013/07/13 16:05:10
Done.
| |
| 440 | |
| 441 SkScalar x = SkIntToScalar(offset.x()); | |
| 442 SkScalar y = SkIntToScalar(offset.y()); | |
| 443 | 597 |
| 444 std::vector<SkPoint> pos; | 598 std::vector<SkPoint> pos; |
| 445 | 599 |
| 446 internal::SkiaTextRenderer renderer(canvas); | 600 internal::SkiaTextRenderer renderer(canvas); |
| 447 ApplyFadeEffects(&renderer); | 601 ApplyFadeEffects(&renderer); |
| 448 ApplyTextShadows(&renderer); | 602 ApplyTextShadows(&renderer); |
| 449 | 603 |
| 450 bool smoothing_enabled; | 604 bool smoothing_enabled; |
| 451 bool cleartype_enabled; | 605 bool cleartype_enabled; |
| 452 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); | 606 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); |
| 453 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. | 607 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. |
| 454 renderer.SetFontSmoothingSettings( | 608 renderer.SetFontSmoothingSettings( |
| 455 smoothing_enabled, cleartype_enabled && !background_is_transparent()); | 609 smoothing_enabled, cleartype_enabled && !background_is_transparent()); |
| 456 | 610 |
| 457 for (size_t i = 0; i < runs_.size(); ++i) { | 611 SkScalar glyph_x = SkIntToScalar(0); |
| 458 // Get the run specified by the visual-to-logical map. | 612 SkScalar glyph_y = SkIntToScalar(0); |
| 459 internal::TextRun* run = runs_[visual_to_logical_[i]]; | |
| 460 | 613 |
| 461 if (run->glyph_count == 0) | 614 if (!multiline()) { |
| 462 continue; | 615 // Skia will draw glyphs with respect to the baseline. |
| 616 Vector2d offset(0, common_baseline_); | |
| 617 offset += GetTextOffset(GetContentWidth()); | |
| 463 | 618 |
| 464 // Based on WebCore::skiaDrawText. | 619 for (size_t i = 0; i < runs_.size(); ++i) { |
| 465 pos.resize(run->glyph_count); | 620 // Get the run specified by the visual-to-logical map. |
| 466 SkScalar glyph_x = x; | 621 internal::TextRun* run = runs_[visual_to_logical_[i]]; |
| 467 for (int glyph = 0; glyph < run->glyph_count; glyph++) { | 622 |
| 468 pos[glyph].set(glyph_x + run->offsets[glyph].du, | 623 if (run->glyph_count == 0) |
| 469 y + run->offsets[glyph].dv); | 624 continue; |
| 470 glyph_x += SkIntToScalar(run->advance_widths[glyph]); | 625 |
| 626 // Based on WebCore::skiaDrawText. | |
| 627 pos.resize(run->glyph_count); | |
| 628 SkScalar run_x = glyph_x; | |
| 629 for (int glyph = 0; glyph < run->glyph_count; glyph++) { | |
| 630 pos[glyph].set(offset.x() + glyph_x + run->offsets[glyph].du, | |
| 631 offset.y() + run->offsets[glyph].dv); | |
| 632 glyph_x += SkIntToScalar(run->advance_widths[glyph]); | |
| 633 } | |
| 634 | |
| 635 renderer.SetTextSize(run->font.GetFontSize()); | |
| 636 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style); | |
| 637 renderer.SetForegroundColor(run->foreground); | |
| 638 renderer.DrawPosText(&pos[0], run->glyphs.get(), run->glyph_count); | |
| 639 renderer.DrawDecorations(run_x, offset.y(), run->width, run->underline, | |
| 640 run->strike, run->diagonal_strike); | |
| 471 } | 641 } |
| 642 return; | |
| 643 } | |
| 472 | 644 |
| 473 renderer.SetTextSize(run->font.GetFontSize()); | 645 for (size_t line = 0; line < lines().size(); ++line) { |
|
Alexei Svitkine (slow)
2013/07/09 15:35:13
Ideally, I'd like to see a single codepath that ha
ckocagil
2013/07/13 16:05:10
Done, added TODO.
| |
| 474 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style); | 646 Vector2d offset(0, common_baseline_); |
| 475 renderer.SetForegroundColor(run->foreground); | 647 offset += GetTextOffset(LineWidth(line)); |
| 476 renderer.DrawPosText(&pos[0], run->glyphs.get(), run->glyph_count); | 648 for (size_t i = 0; i < lines()[line].size(); ++i) { |
| 477 renderer.DrawDecorations(x, y, run->width, run->underline, run->strike, | 649 const internal::LineSegmentWin* segment = |
| 478 run->diagonal_strike); | 650 static_cast<const internal::LineSegmentWin*>(lines()[line][i]); |
| 479 | 651 if (segment->char_pos.is_empty()) |
| 480 x = glyph_x; | 652 continue; // TODO: maybe prevent this from happening in the first place? |
| 653 int segment_start_pos = glyph_x; | |
| 654 internal::TextRun* run = segment->run; | |
| 655 DCHECK_GE(segment->char_pos.start(), run->range.start()); | |
| 656 DCHECK_LT(segment->char_pos.start(), | |
| 657 run->range.start() + run->glyph_count); | |
| 658 int start_char = segment->char_pos.start() - run->range.start(); | |
| 659 int end_char = start_char + segment->char_pos.length(); | |
| 660 ui::Range glyphs = CharRangeToGlyphRange(ui::Range(start_char, end_char), | |
| 661 run); | |
| 662 int start = glyphs.start(); | |
| 663 int end = glyphs.end(); | |
| 664 DCHECK_LE(start, end); | |
| 665 pos.resize(end - start); | |
| 666 for (int g = start; g < end; ++g) { | |
| 667 pos[g - start].set(glyph_x + offset.x() + run->offsets[g].du, | |
| 668 glyph_y + offset.y() + run->offsets[g].dv); | |
| 669 glyph_x += SkIntToScalar(run->advance_widths[g]); | |
| 670 } | |
| 671 renderer.SetTextSize(run->font.GetFontSize()); | |
| 672 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style); | |
| 673 renderer.SetForegroundColor(run->foreground); | |
| 674 renderer.DrawPosText(&pos[0], &run->glyphs[start], end - start); | |
| 675 renderer.DrawDecorations(segment_start_pos + offset.x(), | |
| 676 glyph_y + offset.y(), segment->x_pos.length(), | |
| 677 run->underline, run->strike, | |
| 678 run->diagonal_strike); | |
| 679 } | |
| 680 glyph_x = SkIntToScalar(0); | |
| 681 glyph_y += SkIntToScalar(string_size_.height()); | |
| 481 } | 682 } |
| 482 } | 683 } |
| 483 | 684 |
| 484 void RenderTextWin::ItemizeLogicalText() { | 685 void RenderTextWin::ItemizeLogicalText() { |
| 485 runs_.clear(); | 686 runs_.clear(); |
| 486 string_size_ = Size(0, GetFont().GetHeight()); | 687 string_size_ = Size(0, GetFont().GetHeight()); |
| 487 common_baseline_ = 0; | 688 common_baseline_ = 0; |
| 488 | 689 |
| 489 // Set Uniscribe's base text direction. | 690 // Set Uniscribe's base text direction. |
| 490 script_state_.uBidiLevel = | 691 script_state_.uBidiLevel = |
| (...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 828 size_t position = LayoutIndexToTextIndex(run->range.end()); | 1029 size_t position = LayoutIndexToTextIndex(run->range.end()); |
| 829 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); | 1030 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); |
| 830 return SelectionModel(position, CURSOR_FORWARD); | 1031 return SelectionModel(position, CURSOR_FORWARD); |
| 831 } | 1032 } |
| 832 | 1033 |
| 833 RenderText* RenderText::CreateInstance() { | 1034 RenderText* RenderText::CreateInstance() { |
| 834 return new RenderTextWin; | 1035 return new RenderTextWin; |
| 835 } | 1036 } |
| 836 | 1037 |
| 837 } // namespace gfx | 1038 } // namespace gfx |
| OLD | NEW |