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 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 140 return ui::Range(run.logical_clusters[range.end() - 1], | 140 return ui::Range(run.logical_clusters[range.end() - 1], |
| 141 range.start() > 0 ? run.logical_clusters[range.start() - 1] | 141 range.start() > 0 ? run.logical_clusters[range.start() - 1] |
| 142 : run.glyph_count); | 142 : run.glyph_count); |
| 143 } else { | 143 } else { |
| 144 return ui::Range(run.logical_clusters[range.start()], | 144 return ui::Range(run.logical_clusters[range.start()], |
| 145 range.end() < run.range.length() ? run.logical_clusters[range.end()] | 145 range.end() < run.range.length() ? run.logical_clusters[range.end()] |
| 146 : run.glyph_count); | 146 : run.glyph_count); |
| 147 } | 147 } |
| 148 } | 148 } |
| 149 | 149 |
| 150 // Starting from |start_char|, finds the first character that doesn't fit the | |
| 151 // given |width_cap|. If |width_cap| is reached, returns true and fills the | |
| 152 // output arguments with overflow and rollback info. Otherwise returns false and | |
| 153 // only fills |width|. | |
| 154 // |overflow|: Index of the first character that doesn't fit the given width. | |
| 155 // |width|: If the run doesn't fit, holds the width of [start_char, *overflow]. | |
| 156 // Otherwise, holds the width of [start_char, run.range.end()). | |
| 157 // |char_rollback_width|: Width of |*overflow|. | |
| 158 // |word_rollback_pos|: Index of the last word break before |*overflow|. | |
| 159 // |word_rollback_width|: Width of [*word_rollback_pos, *overflow]. | |
| 160 bool BreakRun(const internal::TextRun& run, const BreakList<size_t>& breaks, | |
| 161 size_t start_char, int width_cap, int* width, size_t* overflow, | |
| 162 int* char_rollback_width, size_t* word_rollback_pos, | |
| 163 int* word_rollback_width) { | |
| 164 DCHECK(run.range.Contains(ui::Range(start_char))); | |
| 165 size_t current_word = start_char; | |
| 166 int current_word_x = 0; // x from |current_word| | |
| 167 int x = 0; | |
| 168 | |
| 169 for (size_t i = start_char; i < run.range.end(); ++i) { | |
| 170 const size_t offset = i - run.range.start(); | |
| 171 | |
| 172 size_t next_word = std::max(breaks.GetBreak(i)->first, start_char); | |
| 173 if (next_word != current_word) { | |
| 174 current_word = next_word; | |
| 175 current_word_x = 0; | |
| 176 } | |
| 177 | |
| 178 ui::Range glyphs = CharRangeToGlyphRange(run, | |
| 179 ui::Range(offset, offset + 1)); | |
| 180 int char_width = 0; | |
| 181 for (size_t j = glyphs.start(); j < glyphs.end(); ++j) | |
| 182 char_width += run.advance_widths[j]; | |
| 183 | |
| 184 x += char_width; | |
| 185 current_word_x += char_width; | |
| 186 | |
| 187 if (x > width_cap) { | |
| 188 *overflow = i; | |
| 189 *width = x; | |
| 190 *char_rollback_width = char_width; | |
| 191 *word_rollback_pos = current_word; | |
| 192 *word_rollback_width = current_word_x; | |
| 193 return true; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 *width = x; | |
| 198 return false; | |
| 199 } | |
| 200 | |
| 150 } // namespace | 201 } // namespace |
| 151 | 202 |
| 152 namespace internal { | 203 namespace internal { |
| 153 | 204 |
| 154 TextRun::TextRun() | 205 TextRun::TextRun() |
| 155 : font_style(0), | 206 : font_style(0), |
| 156 strike(false), | 207 strike(false), |
| 157 diagonal_strike(false), | 208 diagonal_strike(false), |
| 158 underline(false), | 209 underline(false), |
| 159 width(0), | 210 width(0), |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 183 run->glyph_count, | 234 run->glyph_count, |
| 184 run->logical_clusters.get(), | 235 run->logical_clusters.get(), |
| 185 run->visible_attributes.get(), | 236 run->visible_attributes.get(), |
| 186 run->advance_widths.get(), | 237 run->advance_widths.get(), |
| 187 &run->script_analysis, | 238 &run->script_analysis, |
| 188 &x); | 239 &x); |
| 189 DCHECK(SUCCEEDED(hr)); | 240 DCHECK(SUCCEEDED(hr)); |
| 190 return run->preceding_run_widths + x; | 241 return run->preceding_run_widths + x; |
| 191 } | 242 } |
| 192 | 243 |
| 244 struct LineSegmentWin : LineSegment { | |
| 245 internal::TextRun* run; | |
| 246 }; | |
| 247 | |
| 248 // Internal class to help break a text into lines. | |
| 249 class LineBreaker { | |
| 250 public: | |
| 251 LineBreaker() : preceding_line_heights_(0), | |
| 252 pos_(0), | |
| 253 text_x_(0), | |
| 254 line_x_(0), | |
| 255 current_pos_(0), | |
| 256 current_delta_(0) {} | |
| 257 | |
| 258 // Breaks the given |runs| into |lines|. Should be called at most once for | |
| 259 // each instance. If |multiline| is false, doesn't do any breaking and fills | |
| 260 // |lines| with a single Line. | |
| 261 void Break(const ScopedVector<internal::TextRun>& runs, | |
| 262 const BreakList<size_t>& words, | |
| 263 int max_width, | |
| 264 bool multiline, | |
| 265 int* visual_to_logical, | |
| 266 std::vector<internal::Line>* lines) { | |
| 267 SkipLine(); | |
| 268 | |
| 269 for (size_t i = 0; i < runs.size(); ++i) { | |
| 270 internal::TextRun* run = runs[visual_to_logical[i]]; | |
| 271 | |
| 272 pos_ = run->range.start(); | |
| 273 current_pos_ = run->range.start(); | |
| 274 | |
| 275 int width = run->width; | |
| 276 size_t overflow; | |
| 277 int char_rollback_width; | |
| 278 size_t word_rollback_pos; | |
| 279 int word_rollback_width; | |
| 280 // Break the run until it fits the current line. | |
| 281 while (multiline && | |
|
Alexei Svitkine (slow)
2013/07/22 20:52:05
It would be less confusing if the multline check w
ckocagil
2013/08/07 12:59:20
Done (well, it's a ternary operation now). Done.
| |
| 282 BreakRun(*run, words, pos_, max_width - line_x_, | |
| 283 &width, &overflow, &char_rollback_width, | |
| 284 &word_rollback_pos, &word_rollback_width)) { | |
| 285 DCHECK(pos_ < run->range.end()); | |
| 286 Advance(overflow + 1, width); | |
| 287 // First, try rolling back one word to move it to the next line. If | |
| 288 // it's not possible, roll back one character. If neither of these | |
| 289 // could be done, don't do any rollback since each line must have at | |
| 290 // least one character. | |
| 291 if (line_x_ > 0 || word_rollback_width < width) | |
| 292 Rollback(word_rollback_pos, word_rollback_width); | |
| 293 else if (line_x_ > 0 || char_rollback_width < width) | |
| 294 Rollback(overflow, char_rollback_width); | |
| 295 CommitSegment(run); | |
| 296 SkipLine(); | |
| 297 } | |
| 298 | |
| 299 // Remaining part of the run fits the line, add it. | |
| 300 Advance(run->range.end(), width); | |
| 301 CommitSegment(run); | |
| 302 } | |
| 303 | |
| 304 lines->swap(lines_); | |
| 305 } | |
| 306 | |
| 307 private: | |
| 308 // RTL runs are broken in logical order but displayed in visual order. To find | |
| 309 // the text-space coordinate (where it would fall in a single-line text) | |
| 310 // |x_pos| of RTL segments, text-space coordinate of the run beginning is | |
| 311 // saved and the segment widths are applied in reverse order. | |
| 312 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. | |
| 313 struct RtlData { | |
| 314 int run_x_; // Beginning of the RTL run in text-space. | |
| 315 std::vector<LineSegment*> segments_; | |
| 316 }; | |
| 317 | |
| 318 // Applies |rtl_data_|. | |
| 319 void PopRtl() { | |
| 320 int x = rtl_data_.run_x_; | |
| 321 while (!rtl_data_.segments_.empty()) { | |
| 322 LineSegment* segment = rtl_data_.segments_.back(); | |
| 323 rtl_data_.segments_.pop_back(); | |
| 324 segment->x_pos = ui::Range(x, x + segment->x_pos.length()); | |
| 325 x += segment->x_pos.length(); | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 void Advance(size_t new_pos, int amount) { | |
| 330 current_pos_ = new_pos; | |
| 331 current_delta_ += amount; | |
| 332 } | |
| 333 | |
| 334 void Rollback(size_t new_pos, int amount) { | |
| 335 Advance(new_pos, -amount); | |
| 336 } | |
| 337 | |
| 338 void SkipLine() { | |
| 339 if (!lines_.empty()) | |
| 340 preceding_line_heights_ += lines_.back().height; | |
| 341 line_x_ = 0; | |
| 342 lines_.push_back(internal::Line()); | |
| 343 } | |
| 344 | |
| 345 void CommitSegment(internal::TextRun* run) { | |
| 346 if (pos_ == current_pos_) { | |
| 347 DCHECK(current_delta_ == 0); | |
| 348 return; | |
| 349 } | |
| 350 internal::LineSegmentWin* segment = new internal::LineSegmentWin; | |
| 351 segment->run = run; | |
| 352 segment->char_pos = ui::Range(pos_, current_pos_); | |
| 353 segment->x_pos = ui::Range(text_x_, text_x_ + current_delta_); | |
| 354 lines_.back().segments.push_back(segment); | |
| 355 lines_.back().width += segment->x_pos.length(); | |
| 356 lines_.back().height = std::max(lines_.back().height, | |
| 357 segment->run->font.GetHeight()); | |
| 358 lines_.back().baseline = std::max(lines_.back().baseline, | |
| 359 segment->run->font.GetBaseline()); | |
| 360 lines_.back().preceding_heights = preceding_line_heights_; | |
| 361 if (run->script_analysis.fRTL) { | |
| 362 if (pos_ == run->range.start()) | |
| 363 rtl_data_.run_x_ = text_x_; | |
| 364 rtl_data_.segments_.push_back(segment); | |
| 365 if (current_pos_ == run->range.end()) | |
| 366 PopRtl(); | |
| 367 } | |
| 368 pos_ = current_pos_; | |
| 369 text_x_ += current_delta_; | |
| 370 line_x_ += current_delta_; | |
| 371 current_delta_ = 0; | |
| 372 } | |
| 373 | |
| 374 std::vector<internal::Line> lines_; | |
| 375 int preceding_line_heights_; | |
| 376 size_t pos_; | |
| 377 int text_x_; | |
| 378 int line_x_; | |
| 379 size_t current_pos_; | |
| 380 int current_delta_; | |
| 381 RtlData rtl_data_; | |
| 382 | |
| 383 DISALLOW_COPY_AND_ASSIGN(LineBreaker); | |
| 384 }; | |
| 385 | |
| 193 } // namespace internal | 386 } // namespace internal |
| 194 | 387 |
| 195 // static | 388 // static |
| 196 HDC RenderTextWin::cached_hdc_ = NULL; | 389 HDC RenderTextWin::cached_hdc_ = NULL; |
| 197 | 390 |
| 198 // static | 391 // static |
| 199 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; | 392 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; |
| 200 | 393 |
| 201 RenderTextWin::RenderTextWin() | 394 RenderTextWin::RenderTextWin() |
| 202 : RenderText(), | 395 : RenderText(), |
| 203 common_baseline_(0), | 396 common_baseline_(0), |
| 204 needs_layout_(false) { | 397 needs_layout_(false) { |
| 205 set_truncate_length(kMaxUniscribeTextLength); | 398 set_truncate_length(kMaxUniscribeTextLength); |
| 206 | 399 |
| 207 memset(&script_control_, 0, sizeof(script_control_)); | 400 memset(&script_control_, 0, sizeof(script_control_)); |
| 208 memset(&script_state_, 0, sizeof(script_state_)); | 401 memset(&script_state_, 0, sizeof(script_state_)); |
| 209 | 402 |
| 210 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); | 403 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); |
| 211 } | 404 } |
| 212 | 405 |
| 213 RenderTextWin::~RenderTextWin() { | 406 RenderTextWin::~RenderTextWin() { |
| 214 } | 407 } |
| 215 | 408 |
| 216 Size RenderTextWin::GetStringSize() { | 409 Size RenderTextWin::GetStringSize() { |
| 217 EnsureLayout(); | 410 EnsureLayout(); |
| 218 return string_size_; | 411 return Size(string_size_.width(), string_size_.height()); |
| 412 } | |
| 413 | |
| 414 Size RenderTextWin::GetMultilineTextSize() { | |
| 415 EnsureLayout(); | |
| 416 if (!multiline()) | |
| 417 return Size(display_rect().width(), string_size_.height()); | |
| 418 return Size(display_rect().width(), | |
| 419 lines().back().preceding_heights + lines().back().height); | |
| 219 } | 420 } |
| 220 | 421 |
| 221 int RenderTextWin::GetBaseline() { | 422 int RenderTextWin::GetBaseline() { |
| 222 EnsureLayout(); | 423 EnsureLayout(); |
| 223 return common_baseline_; | 424 return common_baseline_; |
| 224 } | 425 } |
| 225 | 426 |
| 226 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { | 427 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { |
| 227 if (text().empty()) | 428 if (text().empty()) |
| 228 return SelectionModel(); | 429 return SelectionModel(); |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 369 GetGlyphXBoundary(run, layout_index, true)); | 570 GetGlyphXBoundary(run, layout_index, true)); |
| 370 } | 571 } |
| 371 | 572 |
| 372 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { | 573 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { |
| 373 DCHECK(!needs_layout_); | 574 DCHECK(!needs_layout_); |
| 374 DCHECK(ui::Range(0, text().length()).Contains(range)); | 575 DCHECK(ui::Range(0, text().length()).Contains(range)); |
| 375 ui::Range layout_range(TextIndexToLayoutIndex(range.start()), | 576 ui::Range layout_range(TextIndexToLayoutIndex(range.start()), |
| 376 TextIndexToLayoutIndex(range.end())); | 577 TextIndexToLayoutIndex(range.end())); |
| 377 DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range)); | 578 DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range)); |
| 378 | 579 |
| 379 std::vector<Rect> bounds; | 580 std::vector<ui::Range> bounds; |
| 581 std::vector<Rect> rects; | |
| 380 if (layout_range.is_empty()) | 582 if (layout_range.is_empty()) |
| 381 return bounds; | 583 return rects; |
| 382 | 584 |
| 383 // Add a Rect for each run/selection intersection. | 585 // Add a Range for each run/selection intersection. |
| 384 // TODO(msw): The bounds should probably not always be leading the range ends. | 586 // TODO(msw): The bounds should probably not always be leading the range ends. |
| 385 for (size_t i = 0; i < runs_.size(); ++i) { | 587 for (size_t i = 0; i < runs_.size(); ++i) { |
| 386 const internal::TextRun* run = runs_[visual_to_logical_[i]]; | 588 const internal::TextRun* run = runs_[visual_to_logical_[i]]; |
| 387 ui::Range intersection = run->range.Intersect(layout_range); | 589 ui::Range intersection = run->range.Intersect(layout_range); |
| 388 if (intersection.IsValid()) { | 590 if (intersection.IsValid()) { |
| 389 DCHECK(!intersection.is_reversed()); | 591 DCHECK(!intersection.is_reversed()); |
| 390 ui::Range range_x(GetGlyphXBoundary(run, intersection.start(), false), | 592 ui::Range range_x(GetGlyphXBoundary(run, intersection.start(), false), |
| 391 GetGlyphXBoundary(run, intersection.end(), false)); | 593 GetGlyphXBoundary(run, intersection.end(), false)); |
| 392 Rect rect(range_x.GetMin(), 0, range_x.length(), run->font.GetHeight()); | 594 if (range_x.is_empty()) |
| 393 rect.set_origin(ToViewPoint(rect.origin())); | 595 continue; |
| 394 // Union this with the last rect if they're adjacent. | 596 range_x = ui::Range(range_x.GetMin(), range_x.GetMax()); |
| 395 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { | 597 // Union this with the last range if they're adjacent. |
| 396 rect.Union(bounds.back()); | 598 DCHECK(bounds.empty() || bounds.back().GetMin() != range_x.GetMax()); |
| 599 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { | |
| 600 range_x = ui::Range(bounds.back().GetMin(), range_x.GetMax()); | |
| 397 bounds.pop_back(); | 601 bounds.pop_back(); |
| 398 } | 602 } |
| 399 bounds.push_back(rect); | 603 bounds.push_back(range_x); |
| 400 } | 604 } |
| 401 } | 605 } |
| 402 return bounds; | 606 for (size_t i = 0; i < bounds.size(); ++i) { |
| 607 std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]); | |
| 608 rects.insert(rects.end(), current_rects.begin(), current_rects.end()); | |
| 609 } | |
| 610 return rects; | |
| 403 } | 611 } |
| 404 | 612 |
| 405 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { | 613 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { |
| 406 DCHECK_LE(index, text().length()); | 614 DCHECK_LE(index, text().length()); |
| 407 ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index; | 615 ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index; |
| 408 CHECK_GE(i, 0); | 616 CHECK_GE(i, 0); |
| 409 // Clamp layout indices to the length of the text actually used for layout. | 617 // Clamp layout indices to the length of the text actually used for layout. |
| 410 return std::min<size_t>(GetLayoutText().length(), i); | 618 return std::min<size_t>(GetLayoutText().length(), i); |
| 411 } | 619 } |
| 412 | 620 |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 430 return ui::IsValidCodePointIndex(text(), position) && | 638 return ui::IsValidCodePointIndex(text(), position) && |
| 431 GetGlyphBounds(position) != GetGlyphBounds(position - 1); | 639 GetGlyphBounds(position) != GetGlyphBounds(position - 1); |
| 432 } | 640 } |
| 433 | 641 |
| 434 void RenderTextWin::ResetLayout() { | 642 void RenderTextWin::ResetLayout() { |
| 435 // Layout is performed lazily as needed for drawing/metrics. | 643 // Layout is performed lazily as needed for drawing/metrics. |
| 436 needs_layout_ = true; | 644 needs_layout_ = true; |
| 437 } | 645 } |
| 438 | 646 |
| 439 void RenderTextWin::EnsureLayout() { | 647 void RenderTextWin::EnsureLayout() { |
| 440 if (!needs_layout_) | 648 if (needs_layout_) { |
| 441 return; | 649 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. |
| 442 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. | 650 ItemizeLogicalText(); |
| 443 ItemizeLogicalText(); | 651 if (!runs_.empty()) |
| 444 if (!runs_.empty()) | 652 LayoutVisualText(); |
| 445 LayoutVisualText(); | 653 needs_layout_ = false; |
| 446 needs_layout_ = false; | 654 std::vector<internal::Line> lines; |
| 655 set_lines(&lines); | |
| 656 } | |
| 657 // Compute lines if they're not valid. This is separate from the layout steps | |
| 658 // above to avoid text layout and shaping when we resize |display_rect_|. | |
| 659 if (lines().empty()) | |
| 660 ComputeLines(); | |
| 661 } | |
| 662 | |
| 663 void RenderTextWin::ComputeLines() { | |
| 664 DCHECK(!needs_layout_); | |
| 665 std::vector<internal::Line> lines; | |
| 666 internal::LineBreaker line_breaker; | |
| 667 line_breaker.Break(runs_, line_breaks(), display_rect().width(), multiline(), | |
| 668 visual_to_logical_.get(), &lines); | |
| 669 set_lines(&lines); | |
| 447 } | 670 } |
| 448 | 671 |
| 449 void RenderTextWin::DrawVisualText(Canvas* canvas) { | 672 void RenderTextWin::DrawVisualText(Canvas* canvas) { |
| 450 DCHECK(!needs_layout_); | 673 DCHECK(!needs_layout_); |
| 451 | 674 DCHECK(!multiline() || !lines().empty()); |
| 452 // Skia will draw glyphs with respect to the baseline. | |
| 453 Vector2d offset(GetTextOffset() + Vector2d(0, common_baseline_)); | |
| 454 | |
| 455 SkScalar x = SkIntToScalar(offset.x()); | |
| 456 SkScalar y = SkIntToScalar(offset.y()); | |
| 457 | 675 |
| 458 std::vector<SkPoint> pos; | 676 std::vector<SkPoint> pos; |
| 459 | 677 |
| 460 internal::SkiaTextRenderer renderer(canvas); | 678 internal::SkiaTextRenderer renderer(canvas); |
| 461 ApplyFadeEffects(&renderer); | 679 ApplyFadeEffects(&renderer); |
| 462 ApplyTextShadows(&renderer); | 680 ApplyTextShadows(&renderer); |
| 463 | 681 |
| 464 bool smoothing_enabled; | 682 bool smoothing_enabled; |
| 465 bool cleartype_enabled; | 683 bool cleartype_enabled; |
| 466 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); | 684 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); |
| 467 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. | 685 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. |
| 468 renderer.SetFontSmoothingSettings( | 686 renderer.SetFontSmoothingSettings( |
| 469 smoothing_enabled, cleartype_enabled && !background_is_transparent()); | 687 smoothing_enabled, cleartype_enabled && !background_is_transparent()); |
| 470 | 688 |
| 471 ApplyCompositionAndSelectionStyles(); | 689 ApplyCompositionAndSelectionStyles(); |
| 472 | 690 |
| 473 for (size_t i = 0; i < runs_.size(); ++i) { | 691 SkScalar glyph_x = SkIntToScalar(0); |
| 474 // Get the run specified by the visual-to-logical map. | |
| 475 internal::TextRun* run = runs_[visual_to_logical_[i]]; | |
| 476 | 692 |
| 477 if (run->glyph_count == 0) | 693 for (size_t line = 0; line < lines().size(); ++line) { |
| 478 continue; | 694 Vector2d offset(0, lines()[line].baseline); |
| 695 offset += GetLineOffset(line); | |
| 696 for (size_t i = 0; i < lines()[line].segments.size(); ++i) { | |
| 697 const internal::LineSegmentWin* segment = | |
| 698 static_cast<internal::LineSegmentWin*>(lines()[line].segments[i]); | |
| 699 internal::TextRun* run = segment->run; | |
| 700 DCHECK(!segment->char_pos.is_empty()); | |
| 701 DCHECK(run->range.Contains(segment->char_pos)); | |
| 702 ui::Range glyphs = CharRangeToGlyphRange(*run, | |
| 703 ui::Range(segment->char_pos.start() - run->range.start(), | |
| 704 segment->char_pos.end() - run->range.start())); | |
| 705 if (glyphs.is_empty()) { | |
| 706 DCHECK(segment->x_pos.is_empty()); | |
| 707 continue; | |
| 708 } | |
| 709 pos.resize(glyphs.length()); | |
| 710 for (size_t g = glyphs.start(); g < glyphs.end(); ++g) { | |
| 711 pos[g - glyphs.start()].set(glyph_x + offset.x() + run->offsets[g].du, | |
| 712 offset.y() + run->offsets[g].dv); | |
| 713 glyph_x += SkIntToScalar(run->advance_widths[g]); | |
| 714 } | |
| 715 renderer.SetTextSize(run->font.GetFontSize()); | |
| 716 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style); | |
| 479 | 717 |
| 480 // Based on WebCore::skiaDrawText. | 718 for (BreakList<SkColor>::const_iterator it = |
| 481 pos.resize(run->glyph_count); | 719 colors().GetBreak(run->range.start()); |
| 482 SkScalar glyph_x = x; | 720 it != colors().breaks().end() && it->first < run->range.end(); |
| 483 for (int glyph = 0; glyph < run->glyph_count; glyph++) { | 721 ++it) { |
| 484 pos[glyph].set(glyph_x + run->offsets[glyph].du, | 722 ui::Range intersection = colors().GetRange(it).Intersect( |
| 485 y + run->offsets[glyph].dv); | 723 segment->char_pos); |
| 486 glyph_x += SkIntToScalar(run->advance_widths[glyph]); | 724 ui::Range colored_glyphs = CharRangeToGlyphRange(*run, |
| 725 ui::Range(intersection.start() - run->range.start(), | |
| 726 intersection.end() - run->range.start())); | |
| 727 DCHECK(glyphs.Contains(colored_glyphs)); | |
| 728 renderer.SetForegroundColor(it->second); | |
| 729 renderer.DrawPosText(&pos[colored_glyphs.start() - glyphs.start()], | |
| 730 &run->glyphs[colored_glyphs.start()], | |
| 731 colored_glyphs.length()); | |
| 732 SkScalar width = (colored_glyphs.end() < glyphs.end() | |
| 733 ? pos[colored_glyphs.end() - glyphs.start()].x() | |
| 734 : pos[0].x() + SkIntToScalar(segment->x_pos.length())) | |
| 735 - pos[colored_glyphs.start() - glyphs.start()].x(); | |
| 736 renderer.DrawDecorations( | |
| 737 pos[colored_glyphs.start() - glyphs.start()].x(), offset.y(), | |
| 738 SkScalarCeilToInt(width), run->underline, run->strike, | |
| 739 run->diagonal_strike); | |
| 740 } | |
| 487 } | 741 } |
| 488 | 742 |
| 489 renderer.SetTextSize(run->font.GetFontSize()); | 743 glyph_x = SkIntToScalar(0); |
| 490 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style); | |
| 491 | |
| 492 for (BreakList<SkColor>::const_iterator it = | |
| 493 colors().GetBreak(run->range.start()); | |
| 494 it != colors().breaks().end() && it->first < run->range.end(); | |
| 495 ++it) { | |
| 496 ui::Range intersection = colors().GetRange(it).Intersect(run->range); | |
| 497 ui::Range glyphs = CharRangeToGlyphRange(*run, | |
| 498 ui::Range(intersection.start() - run->range.start(), | |
| 499 intersection.end() - run->range.start())); | |
| 500 renderer.SetForegroundColor(it->second); | |
| 501 renderer.DrawPosText(&pos[glyphs.start()], &run->glyphs[glyphs.start()], | |
| 502 glyphs.length()); | |
| 503 SkScalar width = (static_cast<int>(glyphs.end()) < run->glyph_count ? | |
| 504 pos[glyphs.end()].x() : pos[0].x() + SkIntToScalar(run->width)) | |
| 505 - pos[glyphs.start()].x(); | |
| 506 renderer.DrawDecorations(pos[glyphs.start()].x(), y, | |
| 507 SkScalarCeilToInt(width), run->underline, | |
| 508 run->strike, run->diagonal_strike); | |
| 509 } | |
| 510 | |
| 511 x = glyph_x; | |
| 512 } | 744 } |
| 513 | 745 |
| 514 UndoCompositionAndSelectionStyles(); | 746 UndoCompositionAndSelectionStyles(); |
| 515 } | 747 } |
| 516 | 748 |
| 517 void RenderTextWin::ItemizeLogicalText() { | 749 void RenderTextWin::ItemizeLogicalText() { |
| 518 runs_.clear(); | 750 runs_.clear(); |
| 519 string_size_ = Size(0, GetFont().GetHeight()); | 751 string_size_ = Size(0, GetFont().GetHeight()); |
| 520 common_baseline_ = 0; | 752 common_baseline_ = 0; |
| 521 | 753 |
| (...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 858 size_t position = LayoutIndexToTextIndex(run->range.end()); | 1090 size_t position = LayoutIndexToTextIndex(run->range.end()); |
| 859 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); | 1091 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); |
| 860 return SelectionModel(position, CURSOR_FORWARD); | 1092 return SelectionModel(position, CURSOR_FORWARD); |
| 861 } | 1093 } |
| 862 | 1094 |
| 863 RenderText* RenderText::CreateInstance() { | 1095 RenderText* RenderText::CreateInstance() { |
| 864 return new RenderTextWin; | 1096 return new RenderTextWin; |
| 865 } | 1097 } |
| 866 | 1098 |
| 867 } // namespace gfx | 1099 } // namespace gfx |
| OLD | NEW |