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 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 122 bool IsUnicodeBidiControlCharacter(char16 c) { | 122 bool IsUnicodeBidiControlCharacter(char16 c) { |
| 123 return c == base::i18n::kRightToLeftMark || | 123 return c == base::i18n::kRightToLeftMark || |
| 124 c == base::i18n::kLeftToRightMark || | 124 c == base::i18n::kLeftToRightMark || |
| 125 c == base::i18n::kLeftToRightEmbeddingMark || | 125 c == base::i18n::kLeftToRightEmbeddingMark || |
| 126 c == base::i18n::kRightToLeftEmbeddingMark || | 126 c == base::i18n::kRightToLeftEmbeddingMark || |
| 127 c == base::i18n::kPopDirectionalFormatting || | 127 c == base::i18n::kPopDirectionalFormatting || |
| 128 c == base::i18n::kLeftToRightOverride || | 128 c == base::i18n::kLeftToRightOverride || |
| 129 c == base::i18n::kRightToLeftOverride; | 129 c == base::i18n::kRightToLeftOverride; |
| 130 } | 130 } |
| 131 | 131 |
| 132 // Returns the corresponding glyph range of the given character range. | |
| 133 ui::Range CharRangeToGlyphRange(const ui::Range& range, | |
| 134 const internal::TextRun& run) { | |
| 135 CHECK(run.range.Contains(ui::Range(range.start() + run.range.start(), | |
| 136 range.end() + run.range.start()))); | |
| 137 CHECK(!range.is_reversed()); | |
| 138 CHECK(!range.is_empty()); | |
| 139 int start = 0; | |
| 140 int end = 0; | |
| 141 if (run.script_analysis.fRTL) { | |
| 142 start = run.logical_clusters[range.end() - 1]; | |
| 143 end = range.start() > 0 ? run.logical_clusters[range.start() - 1] | |
| 144 : run.glyph_count; | |
| 145 } else { | |
| 146 start = run.logical_clusters[range.start()]; | |
| 147 end = range.end() < run.range.length() ? run.logical_clusters[range.end()] | |
| 148 : run.glyph_count; | |
| 149 } | |
| 150 return ui::Range(start, end); | |
| 151 } | |
| 152 | |
| 132 } // namespace | 153 } // namespace |
| 133 | 154 |
| 134 namespace internal { | 155 namespace internal { |
| 135 | 156 |
| 136 TextRun::TextRun() | 157 TextRun::TextRun() |
| 137 : foreground(0), | 158 : foreground(0), |
| 138 font_style(0), | 159 font_style(0), |
| 139 strike(false), | 160 strike(false), |
| 140 diagonal_strike(false), | 161 diagonal_strike(false), |
| 141 underline(false), | 162 underline(false), |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 166 run->glyph_count, | 187 run->glyph_count, |
| 167 run->logical_clusters.get(), | 188 run->logical_clusters.get(), |
| 168 run->visible_attributes.get(), | 189 run->visible_attributes.get(), |
| 169 run->advance_widths.get(), | 190 run->advance_widths.get(), |
| 170 &run->script_analysis, | 191 &run->script_analysis, |
| 171 &x); | 192 &x); |
| 172 DCHECK(SUCCEEDED(hr)); | 193 DCHECK(SUCCEEDED(hr)); |
| 173 return run->preceding_run_widths + x; | 194 return run->preceding_run_widths + x; |
| 174 } | 195 } |
| 175 | 196 |
| 197 struct LineSegmentWin : LineSegment { | |
| 198 internal::TextRun* run; | |
| 199 }; | |
| 200 | |
| 176 } // namespace internal | 201 } // namespace internal |
| 177 | 202 |
| 178 // static | 203 // static |
| 179 HDC RenderTextWin::cached_hdc_ = NULL; | 204 HDC RenderTextWin::cached_hdc_ = NULL; |
| 180 | 205 |
| 181 // static | 206 // static |
| 182 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; | 207 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; |
| 183 | 208 |
| 184 RenderTextWin::RenderTextWin() | 209 RenderTextWin::RenderTextWin() |
| 185 : RenderText(), | 210 : RenderText(), |
| 186 common_baseline_(0), | 211 common_baseline_(0), |
| 187 needs_layout_(false) { | 212 needs_layout_(false) { |
| 188 set_truncate_length(kMaxUniscribeTextLength); | 213 set_truncate_length(kMaxUniscribeTextLength); |
| 189 | 214 |
| 190 memset(&script_control_, 0, sizeof(script_control_)); | 215 memset(&script_control_, 0, sizeof(script_control_)); |
| 191 memset(&script_state_, 0, sizeof(script_state_)); | 216 memset(&script_state_, 0, sizeof(script_state_)); |
| 192 | 217 |
| 193 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); | 218 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); |
| 194 } | 219 } |
| 195 | 220 |
| 196 RenderTextWin::~RenderTextWin() { | 221 RenderTextWin::~RenderTextWin() { |
| 197 } | 222 } |
| 198 | 223 |
| 199 Size RenderTextWin::GetStringSize() { | 224 Size RenderTextWin::GetStringSize() { |
| 200 EnsureLayout(); | 225 EnsureLayout(); |
| 201 return string_size_; | 226 return Size(string_size_.width(), string_size_.height()); |
| 227 } | |
| 228 | |
| 229 Size RenderTextWin::GetMultilineTextSize() { | |
| 230 EnsureLayout(); | |
| 231 if (!multiline()) | |
| 232 return Size(display_rect().width(), string_size_.height()); | |
| 233 return Size(display_rect().width(), | |
| 234 lines().back().preceding_heights + lines().back().height); | |
| 202 } | 235 } |
| 203 | 236 |
| 204 int RenderTextWin::GetBaseline() { | 237 int RenderTextWin::GetBaseline() { |
| 205 EnsureLayout(); | 238 EnsureLayout(); |
| 206 return common_baseline_; | 239 return common_baseline_; |
| 207 } | 240 } |
| 208 | 241 |
| 209 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { | 242 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { |
| 210 if (text().empty()) | 243 if (text().empty()) |
| 211 return SelectionModel(); | 244 return SelectionModel(); |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 360 GetGlyphXBoundary(run, layout_index, true)); | 393 GetGlyphXBoundary(run, layout_index, true)); |
| 361 } | 394 } |
| 362 | 395 |
| 363 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { | 396 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { |
| 364 DCHECK(!needs_layout_); | 397 DCHECK(!needs_layout_); |
| 365 DCHECK(ui::Range(0, text().length()).Contains(range)); | 398 DCHECK(ui::Range(0, text().length()).Contains(range)); |
| 366 ui::Range layout_range(TextIndexToLayoutIndex(range.start()), | 399 ui::Range layout_range(TextIndexToLayoutIndex(range.start()), |
| 367 TextIndexToLayoutIndex(range.end())); | 400 TextIndexToLayoutIndex(range.end())); |
| 368 DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range)); | 401 DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range)); |
| 369 | 402 |
| 370 std::vector<Rect> bounds; | 403 std::vector<ui::Range> bounds; |
| 404 std::vector<Rect> rects; | |
| 371 if (layout_range.is_empty()) | 405 if (layout_range.is_empty()) |
| 372 return bounds; | 406 return rects; |
| 373 | 407 |
| 374 // Add a Rect for each run/selection intersection. | 408 // Add a Rect for each run/selection intersection. |
| 375 // TODO(msw): The bounds should probably not always be leading the range ends. | 409 // TODO(msw): The bounds should probably not always be leading the range ends. |
| 376 for (size_t i = 0; i < runs_.size(); ++i) { | 410 for (size_t i = 0; i < runs_.size(); ++i) { |
| 377 const internal::TextRun* run = runs_[visual_to_logical_[i]]; | 411 const internal::TextRun* run = runs_[visual_to_logical_[i]]; |
| 378 ui::Range intersection = run->range.Intersect(layout_range); | 412 ui::Range intersection = run->range.Intersect(layout_range); |
| 379 if (intersection.IsValid()) { | 413 if (intersection.IsValid()) { |
| 380 DCHECK(!intersection.is_reversed()); | 414 DCHECK(!intersection.is_reversed()); |
| 381 ui::Range range_x(GetGlyphXBoundary(run, intersection.start(), false), | 415 ui::Range range_x(GetGlyphXBoundary(run, intersection.start(), false), |
| 382 GetGlyphXBoundary(run, intersection.end(), false)); | 416 GetGlyphXBoundary(run, intersection.end(), false)); |
| 383 Rect rect(range_x.GetMin(), 0, range_x.length(), run->font.GetHeight()); | 417 range_x = ui::Range(range_x.GetMin(), range_x.GetMax()); |
| 384 rect.set_origin(ToViewPoint(rect.origin())); | 418 // Union this with the last range if they're adjacent. |
| 385 // Union this with the last rect if they're adjacent. | 419 DCHECK(bounds.empty() || bounds.back().GetMin() != range_x.GetMax()); |
| 386 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { | 420 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { |
| 387 rect.Union(bounds.back()); | 421 range_x = ui::Range(bounds.back().GetMin(), range_x.GetMax()); |
| 388 bounds.pop_back(); | 422 bounds.pop_back(); |
| 389 } | 423 } |
| 390 bounds.push_back(rect); | 424 bounds.push_back(range_x); |
| 391 } | 425 } |
| 392 } | 426 } |
| 393 return bounds; | 427 for (size_t i = 0; i < bounds.size(); ++i) { |
| 428 std::vector<Rect> current_rects = RangeToViewRects(bounds[i]); | |
| 429 rects.insert(rects.end(), current_rects.begin(), current_rects.end()); | |
| 430 } | |
| 431 return rects; | |
| 394 } | 432 } |
| 395 | 433 |
| 396 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { | 434 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { |
| 397 DCHECK_LE(index, text().length()); | 435 DCHECK_LE(index, text().length()); |
| 398 ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index; | 436 ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index; |
| 399 CHECK_GE(i, 0); | 437 CHECK_GE(i, 0); |
| 400 // Clamp layout indices to the length of the text actually used for layout. | 438 // Clamp layout indices to the length of the text actually used for layout. |
| 401 return std::min<size_t>(GetLayoutText().length(), i); | 439 return std::min<size_t>(GetLayoutText().length(), i); |
| 402 } | 440 } |
| 403 | 441 |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 421 return ui::IsValidCodePointIndex(text(), position) && | 459 return ui::IsValidCodePointIndex(text(), position) && |
| 422 GetGlyphBounds(position) != GetGlyphBounds(position - 1); | 460 GetGlyphBounds(position) != GetGlyphBounds(position - 1); |
| 423 } | 461 } |
| 424 | 462 |
| 425 void RenderTextWin::ResetLayout() { | 463 void RenderTextWin::ResetLayout() { |
| 426 // Layout is performed lazily as needed for drawing/metrics. | 464 // Layout is performed lazily as needed for drawing/metrics. |
| 427 needs_layout_ = true; | 465 needs_layout_ = true; |
| 428 } | 466 } |
| 429 | 467 |
| 430 void RenderTextWin::EnsureLayout() { | 468 void RenderTextWin::EnsureLayout() { |
| 431 if (!needs_layout_) | 469 if (needs_layout_) { |
| 432 return; | 470 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. |
| 433 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. | 471 ItemizeLogicalText(); |
| 434 ItemizeLogicalText(); | 472 if (!runs_.empty()) |
| 435 if (!runs_.empty()) | 473 LayoutVisualText(); |
| 436 LayoutVisualText(); | 474 needs_layout_ = false; |
| 437 needs_layout_ = false; | 475 set_lines_valid(false); |
| 476 } | |
| 477 if (!lines_valid()) { | |
|
msw
2013/07/17 06:47:18
Why is this a separate step at all? I figure multi
ckocagil
2013/07/19 19:40:50
Yes, the separate step is to avoid itemizing, glyp
| |
| 478 ComputeLines(); | |
| 479 set_lines_valid(true); | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 void RenderTextWin::ComputeLines() { | |
|
ckocagil
2013/07/19 19:40:50
I refactored this method, sorry for not doing this
| |
| 484 DCHECK(!needs_layout_); | |
| 485 | |
| 486 std::vector<internal::Line> lines; | |
| 487 lines.push_back(internal::Line()); | |
| 488 | |
| 489 int max_width = display_rect().width(); | |
|
msw
2013/07/17 06:47:18
nit: const.
| |
| 490 | |
| 491 std::vector<size_t> breaks = line_breaks(); | |
| 492 for (size_t i = 0; i < runs_.size(); ++i) | |
| 493 breaks.push_back(runs_[i]->range.start()); | |
| 494 if (breaks.empty()) | |
| 495 breaks.push_back(0); | |
| 496 std::sort(breaks.begin(), breaks.end()); | |
|
msw
2013/07/17 06:47:18
Is this necessary? The BreakIterator advancement s
| |
| 497 | |
| 498 size_t* last_break = &breaks[0]; | |
| 499 | |
| 500 int line_x = 0; // x from the line beginning | |
| 501 int text_x = 0; // x from the text beginning | |
| 502 size_t segment_start_char = 0; // offset of current segment beginning | |
| 503 int segment_start_pos = 0; // position of current segment beginning | |
| 504 internal::TextRun* run; | |
| 505 size_t line_beginning = 0; | |
| 506 int preceeding_line_heights = 0; | |
| 507 | |
| 508 // Traverse all runs in visual order. If a run doesn't fit the current line, | |
| 509 // iterate over each character in the run in logical order until the next | |
| 510 // character doesn't fit the current line, then do the one of the following: | |
| 511 // - If the last line break before current character is not a line beginning, | |
| 512 // roll back there and skip a line. | |
| 513 // - If the last line break is at the line beginning and there are more than | |
| 514 // one characters in the line, roll back one character and skip a line. | |
| 515 // - If neither conditions are met, assume we could fit this character and | |
| 516 // skip a line. | |
| 517 for (size_t i = 0; i < runs_.size(); ++i) { | |
|
msw
2013/07/17 06:47:18
This whole loop is still pretty difficult to follo
| |
| 518 run = runs_[visual_to_logical_[i]]; | |
| 519 | |
| 520 if (!multiline() || line_x + run->width <= max_width) { | |
|
msw
2013/07/17 06:47:18
nit: an example comment here would be:
// The whol
| |
| 521 internal::LineSegmentWin* segment = new internal::LineSegmentWin; | |
| 522 segment->char_pos = run->range; | |
| 523 segment->x_pos = ui::Range(text_x, text_x + run->width); | |
| 524 segment->run = run; | |
| 525 lines.back().segments.push_back(segment); | |
| 526 lines.back().width += run->width; | |
| 527 lines.back().height = std::max(lines.back().height, | |
|
msw
2013/07/17 06:47:18
This height and baseline calculation should work s
| |
| 528 run->font.GetHeight()); | |
| 529 lines.back().baseline = std::max(lines.back().baseline, | |
| 530 run->font.GetBaseline()); | |
| 531 segment_start_char += run->range.length(); | |
| 532 segment_start_pos += run->width; | |
| 533 line_x += run->width; | |
| 534 text_x += run->width; | |
| 535 continue; | |
| 536 } | |
| 537 | |
| 538 bool rtl = run->script_analysis.fRTL; | |
| 539 int break_x = 0; | |
|
msw
2013/07/17 06:47:18
Comment on what this is.
| |
| 540 | |
| 541 last_break = &breaks[0]; | |
| 542 | |
| 543 segment_start_char = run->range.start(); | |
| 544 segment_start_pos = text_x; | |
| 545 | |
| 546 int run_beginning_x = text_x; | |
| 547 // To find the text coordinates of RTL LineSegments, store their widths in a | |
|
msw
2013/07/17 06:47:18
I'm still having trouble understanding exactly how
| |
| 548 // vector and apply them after processing all segments in the run. | |
| 549 typedef std::pair<internal::LineSegment*, int> RtlWidth; | |
| 550 std::vector<RtlWidth> rtl_widths; | |
| 551 | |
| 552 for (size_t j = 0; j < run->range.length(); ++j) { | |
|
msw
2013/07/17 06:47:18
I think you'll need to check IsCursorablePosition
| |
| 553 size_t offset = j + run->range.start(); | |
| 554 while (last_break != &breaks.back() && offset >= *(last_break + 1)) { | |
|
msw
2013/07/17 06:47:18
Comment on what this is doing; it's not obvious.
| |
| 555 ++last_break; | |
| 556 break_x = 0; | |
|
msw
2013/07/17 06:47:18
Must this be reset in the loop to correspond with
| |
| 557 } | |
| 558 | |
| 559 ui::Range glyphs = CharRangeToGlyphRange(ui::Range(j, j + 1), *run); | |
|
msw
2013/07/17 06:47:18
CharRangeToGlyphRange is a decent example of a sim
| |
| 560 | |
| 561 int char_width = 0; | |
| 562 for (size_t k = glyphs.start(); k < glyphs.end(); ++k) | |
| 563 char_width += run->advance_widths[k]; | |
| 564 | |
| 565 line_x += char_width; | |
| 566 text_x += char_width; | |
| 567 break_x += char_width; | |
| 568 | |
| 569 if (line_x > max_width) { | |
|
msw
2013/07/17 06:47:18
Could this check "|| j == (run.range.length() - 1)
| |
| 570 if (*last_break > line_beginning) { | |
| 571 // Roll back to last break. | |
| 572 text_x -= break_x; | |
| 573 j = *last_break - run->range.start() - 1; | |
| 574 offset = *last_break; | |
| 575 } else if (line_x != char_width) { | |
|
msw
2013/07/17 06:47:18
nit: should this be line_x > char_width?
| |
| 576 // Roll back one character. | |
| 577 text_x -= char_width; | |
| 578 --j; | |
| 579 } // else: Assume the current character fits; continue from next line. | |
|
msw
2013/07/17 06:47:18
Does this mean that the if last condition failed t
| |
| 580 line_beginning = offset; | |
| 581 line_x = 0; | |
| 582 break_x = 0; | |
| 583 | |
| 584 internal::LineSegmentWin* segment = new internal::LineSegmentWin; | |
| 585 if (rtl) | |
| 586 rtl_widths.push_back(RtlWidth(segment, text_x - segment_start_pos)); | |
| 587 else | |
| 588 segment->x_pos = ui::Range(segment_start_pos, text_x); | |
| 589 segment->char_pos = ui::Range(segment_start_char, offset); | |
| 590 segment->run = run; | |
| 591 lines.back().segments.push_back(segment); | |
| 592 lines.back().width += text_x - segment_start_pos; | |
| 593 lines.back().height = std::max(lines.back().height, | |
| 594 segment->run->font.GetHeight()); | |
| 595 preceeding_line_heights += lines.back().height; | |
| 596 lines.back().baseline = std::max(lines.back().baseline, | |
| 597 segment->run->font.GetBaseline()); | |
| 598 | |
| 599 lines.push_back(internal::Line()); | |
| 600 lines.back().preceding_heights = preceeding_line_heights; | |
| 601 | |
| 602 segment_start_pos = text_x; | |
| 603 segment_start_char = offset; | |
| 604 continue; | |
| 605 } | |
| 606 } | |
| 607 if (segment_start_char != run->range.end()) { | |
|
msw
2013/07/17 06:47:18
Add a blank line above and comment here, what is t
| |
| 608 internal::LineSegmentWin* segment = new internal::LineSegmentWin; | |
| 609 if (rtl) | |
| 610 rtl_widths.push_back(RtlWidth(segment, text_x - segment_start_pos)); | |
| 611 else | |
| 612 segment->x_pos = ui::Range(segment_start_pos, text_x); | |
| 613 segment->char_pos = ui::Range(segment_start_char, run->range.end()); | |
| 614 segment->run = run; | |
| 615 lines.back().segments.push_back(segment); | |
| 616 lines.back().width += text_x - segment_start_pos; | |
| 617 lines.back().height = std::max(lines.back().height, | |
| 618 segment->run->font.GetHeight()); | |
| 619 lines.back().baseline = std::max(lines.back().baseline, | |
| 620 segment->run->font.GetBaseline()); | |
| 621 segment_start_char = run->range.end(); | |
| 622 segment_start_pos = text_x; | |
| 623 } | |
| 624 | |
| 625 int rtl_total_widths = 0; | |
| 626 while (!rtl_widths.empty()) { | |
| 627 const RtlWidth& rtl_width = rtl_widths.back(); | |
| 628 rtl_width.first->x_pos = ui::Range(run_beginning_x + rtl_total_widths, | |
| 629 run_beginning_x + rtl_total_widths + rtl_width.second); | |
| 630 rtl_widths.pop_back(); | |
| 631 rtl_total_widths += rtl_width.second; | |
| 632 } | |
| 633 } | |
| 634 | |
| 635 set_lines(&lines); | |
| 438 } | 636 } |
| 439 | 637 |
| 440 void RenderTextWin::DrawVisualText(Canvas* canvas) { | 638 void RenderTextWin::DrawVisualText(Canvas* canvas) { |
| 441 DCHECK(!needs_layout_); | 639 DCHECK(!needs_layout_); |
| 442 | 640 DCHECK(!multiline() || lines_valid()); |
| 443 // Skia will draw glyphs with respect to the baseline. | |
| 444 Vector2d offset(GetTextOffset() + Vector2d(0, common_baseline_)); | |
| 445 | |
| 446 SkScalar x = SkIntToScalar(offset.x()); | |
| 447 SkScalar y = SkIntToScalar(offset.y()); | |
| 448 | 641 |
| 449 std::vector<SkPoint> pos; | 642 std::vector<SkPoint> pos; |
| 450 | 643 |
| 451 internal::SkiaTextRenderer renderer(canvas); | 644 internal::SkiaTextRenderer renderer(canvas); |
| 452 ApplyFadeEffects(&renderer); | 645 ApplyFadeEffects(&renderer); |
| 453 ApplyTextShadows(&renderer); | 646 ApplyTextShadows(&renderer); |
| 454 | 647 |
| 455 bool smoothing_enabled; | 648 bool smoothing_enabled; |
| 456 bool cleartype_enabled; | 649 bool cleartype_enabled; |
| 457 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); | 650 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); |
| 458 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. | 651 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. |
| 459 renderer.SetFontSmoothingSettings( | 652 renderer.SetFontSmoothingSettings( |
| 460 smoothing_enabled, cleartype_enabled && !background_is_transparent()); | 653 smoothing_enabled, cleartype_enabled && !background_is_transparent()); |
| 461 | 654 |
| 462 for (size_t i = 0; i < runs_.size(); ++i) { | 655 SkScalar glyph_x = SkIntToScalar(0); |
| 463 // Get the run specified by the visual-to-logical map. | |
| 464 internal::TextRun* run = runs_[visual_to_logical_[i]]; | |
| 465 | 656 |
| 466 if (run->glyph_count == 0) | 657 for (size_t line = 0; line < lines().size(); ++line) { |
| 467 continue; | 658 Vector2d offset(0, lines()[line].baseline); |
| 468 | 659 offset += GetLineOffset(line); |
| 469 // Based on WebCore::skiaDrawText. | 660 for (size_t i = 0; i < lines()[line].segments.size(); ++i) { |
| 470 pos.resize(run->glyph_count); | 661 const internal::LineSegmentWin* segment = |
| 471 SkScalar glyph_x = x; | 662 static_cast<internal::LineSegmentWin*>(lines()[line].segments[i]); |
| 472 for (int glyph = 0; glyph < run->glyph_count; glyph++) { | 663 // TODO(ckocagil): necessary? maybe prevent this from happening. |
|
msw
2013/07/17 06:47:18
Does it currently happen? Can you just [D]CHECK ag
ckocagil
2013/07/19 19:40:50
Done.
| |
| 473 pos[glyph].set(glyph_x + run->offsets[glyph].du, | 664 if (segment->char_pos.is_empty()) |
| 474 y + run->offsets[glyph].dv); | 665 continue; |
| 475 glyph_x += SkIntToScalar(run->advance_widths[glyph]); | 666 int segment_start_pos = glyph_x; |
| 667 internal::TextRun* run = segment->run; | |
| 668 DCHECK_GE(segment->char_pos.start(), run->range.start()); | |
| 669 DCHECK_LT(segment->char_pos.start(), | |
|
msw
2013/07/17 06:47:18
Did you mean segment->char_pos.end()?
ckocagil
2013/07/19 19:40:50
I changed this to use ui::Range::Contains.
| |
| 670 run->range.start() + run->glyph_count); | |
|
msw
2013/07/17 06:47:18
Did you mean run->range.start() + run->range.lengt
ckocagil
2013/07/19 19:40:50
Same as above.
| |
| 671 int start_char = segment->char_pos.start() - run->range.start(); | |
|
msw
2013/07/17 06:47:18
nit: const here and for the three ints below too.
ckocagil
2013/07/19 19:40:50
I inlined these three ints.
| |
| 672 int end_char = start_char + segment->char_pos.length(); | |
| 673 ui::Range glyphs = CharRangeToGlyphRange(ui::Range(start_char, end_char), | |
| 674 *run); | |
| 675 int start = glyphs.start(); | |
|
msw
2013/07/17 06:47:18
Consider just inlining use of the |glyphs| range i
ckocagil
2013/07/19 19:40:50
Done.
| |
| 676 int end = glyphs.end(); | |
| 677 DCHECK_LE(start, end); | |
|
msw
2013/07/17 06:47:18
Will this code work okay if start == end?
ckocagil
2013/07/19 19:40:50
It now skips this case and DCHECKs that the segmen
| |
| 678 pos.resize(end - start); | |
| 679 for (int g = start; g < end; ++g) { | |
| 680 pos[g - start].set(glyph_x + offset.x() + run->offsets[g].du, | |
| 681 offset.y() + run->offsets[g].dv); | |
| 682 glyph_x += SkIntToScalar(run->advance_widths[g]); | |
| 683 } | |
| 684 renderer.SetTextSize(run->font.GetFontSize()); | |
| 685 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style); | |
| 686 renderer.SetForegroundColor(run->foreground); | |
| 687 renderer.DrawPosText(&pos[0], &run->glyphs[start], end - start); | |
| 688 renderer.DrawDecorations(segment_start_pos + offset.x(), | |
| 689 offset.y(), segment->x_pos.length(), | |
| 690 run->underline, run->strike, | |
| 691 run->diagonal_strike); | |
| 476 } | 692 } |
| 477 | 693 glyph_x = SkIntToScalar(0); |
| 478 renderer.SetTextSize(run->font.GetFontSize()); | |
| 479 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style); | |
| 480 renderer.SetForegroundColor(run->foreground); | |
| 481 renderer.DrawPosText(&pos[0], run->glyphs.get(), run->glyph_count); | |
| 482 renderer.DrawDecorations(x, y, run->width, run->underline, run->strike, | |
| 483 run->diagonal_strike); | |
| 484 | |
| 485 x = glyph_x; | |
| 486 } | 694 } |
| 487 } | 695 } |
| 488 | 696 |
| 489 void RenderTextWin::ItemizeLogicalText() { | 697 void RenderTextWin::ItemizeLogicalText() { |
| 490 runs_.clear(); | 698 runs_.clear(); |
| 491 string_size_ = Size(0, GetFont().GetHeight()); | 699 string_size_ = Size(0, GetFont().GetHeight()); |
| 492 common_baseline_ = 0; | 700 common_baseline_ = 0; |
| 493 | 701 |
| 494 // Set Uniscribe's base text direction. | 702 // Set Uniscribe's base text direction. |
| 495 script_state_.uBidiLevel = | 703 script_state_.uBidiLevel = |
| (...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 829 size_t position = LayoutIndexToTextIndex(run->range.end()); | 1037 size_t position = LayoutIndexToTextIndex(run->range.end()); |
| 830 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); | 1038 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); |
| 831 return SelectionModel(position, CURSOR_FORWARD); | 1039 return SelectionModel(position, CURSOR_FORWARD); |
| 832 } | 1040 } |
| 833 | 1041 |
| 834 RenderText* RenderText::CreateInstance() { | 1042 RenderText* RenderText::CreateInstance() { |
| 835 return new RenderTextWin; | 1043 return new RenderTextWin; |
| 836 } | 1044 } |
| 837 | 1045 |
| 838 } // namespace gfx | 1046 } // namespace gfx |
| OLD | NEW |