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

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

Issue 16867016: Windows implementation of multiline RenderText (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: rebased Created 7 years, 5 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
OLDNEW
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698