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

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: comments addressed Created 7 years, 4 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 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 } else { 147 } else {
148 result = ui::Range(run.logical_clusters[run_range.start()], 148 result = ui::Range(run.logical_clusters[run_range.start()],
149 run_range.end() < run.range.length() ? 149 run_range.end() < run.range.length() ?
150 run.logical_clusters[run_range.end()] : run.glyph_count); 150 run.logical_clusters[run_range.end()] : run.glyph_count);
151 } 151 }
152 DCHECK(!result.is_reversed()); 152 DCHECK(!result.is_reversed());
153 DCHECK(ui::Range(0, run.glyph_count).Contains(result)); 153 DCHECK(ui::Range(0, run.glyph_count).Contains(result));
154 return result; 154 return result;
155 } 155 }
156 156
157 struct BreakRunResults {
158 // Whether an overflow occurred.
159 bool overflowed;
160
161 // If |overflowed|, holds the width of [start_char, overflow_pos].
162 // Otherwise, holds the width of [start_char, run.range.end()).
163 int width;
164
165 // Index of the first character that doesn't fit the given width.
166 size_t overflow_pos;
167
168 // Width of the character at |overflow_pos|.
169 int char_rollback_width;
170
171 // Index of the last word break before |overflow_pos|.
172 size_t word_rollback_pos;
173
174 // Width of [word_rollback_pos, overflow_pos].
175 int word_rollback_width;
176 };
177
178 // Starting from |start_char|, finds the first character that doesn't fit the
179 // given |width_cap|. If |width_cap| is reached, returns true and fills all the
180 // fields in the result. Otherwise returns false and only fills |width|.
181 BreakRunResults BreakRunAtWidth(const internal::TextRun& run,
Alexei Svitkine (slow) 2013/08/12 20:42:33 Returning the whole struct by value is a bit more
ckocagil 2013/08/13 20:22:28 Done.
182 const BreakList<size_t>& breaks,
183 size_t start_char,
184 int width_cap) {
Alexei Svitkine (slow) 2013/08/12 20:42:33 Nit: width_cap -> available_width
ckocagil 2013/08/13 20:22:28 Done.
185 DCHECK(run.range.Contains(ui::Range(start_char)));
186 BreakRunResults results;
Alexei Svitkine (slow) 2013/08/12 20:42:33 Either add a default ctor that initializes the mem
ckocagil 2013/08/13 20:22:28 Done, added memset.
187 size_t current_word = start_char;
188 // x distance from |current_word|.
189 int current_word_x = 0;
190 int x = 0;
191
Alexei Svitkine (slow) 2013/08/12 20:42:33 I'd like like to see this code try the whole run f
ckocagil 2013/08/13 20:22:28 I added run width checking to LineBreaker::AddRun.
192 for (size_t i = start_char; i < run.range.end(); ++i) {
193 size_t next_word = std::max(breaks.GetBreak(i)->first, start_char);
Alexei Svitkine (slow) 2013/08/12 20:42:33 You only need to do this if i < previous next_word
ckocagil 2013/08/13 20:22:28 I did something different, this now stores two Bre
194 if (next_word != current_word) {
195 current_word = next_word;
196 current_word_x = 0;
197 }
198
199 ui::Range glyphs = CharRangeToGlyphRange(run, ui::Range(i, i + 1));
200 int char_width = 0;
201 for (size_t j = glyphs.start(); j < glyphs.end(); ++j)
202 char_width += run.advance_widths[j];
203
204 x += char_width;
205 current_word_x += char_width;
206
207 if (x > width_cap) {
208 results.overflowed = true;
209 results.overflow_pos = i;
210 results.width = x;
211 results.char_rollback_width = char_width;
212 results.word_rollback_pos = current_word;
213 results.word_rollback_width = current_word_x;
214 return results;
215 }
216 }
217
218 results.overflowed = false;
219 results.width = x;
220 return results;
221 }
222
157 } // namespace 223 } // namespace
158 224
159 namespace internal { 225 namespace internal {
160 226
161 TextRun::TextRun() 227 TextRun::TextRun()
162 : font_style(0), 228 : font_style(0),
163 strike(false), 229 strike(false),
164 diagonal_strike(false), 230 diagonal_strike(false),
165 underline(false), 231 underline(false),
166 width(0), 232 width(0),
(...skipping 23 matching lines...) Expand all
190 run->glyph_count, 256 run->glyph_count,
191 run->logical_clusters.get(), 257 run->logical_clusters.get(),
192 run->visible_attributes.get(), 258 run->visible_attributes.get(),
193 run->advance_widths.get(), 259 run->advance_widths.get(),
194 &run->script_analysis, 260 &run->script_analysis,
195 &x); 261 &x);
196 DCHECK(SUCCEEDED(hr)); 262 DCHECK(SUCCEEDED(hr));
197 return run->preceding_run_widths + x; 263 return run->preceding_run_widths + x;
198 } 264 }
199 265
266 struct LineSegmentWin : LineSegment {
267 const internal::TextRun* run;
268 };
269
270 // Internal class to help break a text into lines.
271 class LineBreaker {
272 public:
273 LineBreaker(int max_width, bool multiline, const BreakList<size_t>* words)
274 : max_width_(max_width),
275 multiline_(multiline),
276 words_(words),
277 preceding_line_heights_(0),
278 pos_(0),
279 text_x_(0),
280 line_x_(0),
281 current_pos_(0),
282 current_delta_(0) {
283 SkipLine();
284 }
285
286 // Breaks the given |runs| into |lines|. Should be called at most once for
287 // each instance. If |multiline| is false, doesn't do any breaking and fills
288 // |lines| with a single Line.
289 void AddRun(const internal::TextRun& run) {
290 ResetPos(run.range.start());
291 int width = multiline_ ? BreakRun(run) : run.width;
292 // Remaining part of the run fits the line, add it as well.
293 Advance(run.range.end(), width);
294 CommitSegment(run);
295 }
296
297 // Finishes line breaking and outputs the results. Can be called at most once.
298 void Finalize(std::vector<internal::Line>* lines) {
299 lines->swap(lines_);
Alexei Svitkine (slow) 2013/08/12 20:42:33 Add a DCHECK() that !lines_.empty().
ckocagil 2013/08/13 20:22:28 Done.
300 }
301
302 private:
303 // RTL runs are broken in logical order but displayed in visual order. To find
304 // the text-space coordinate (where it would fall in a single-line text)
305 // |x_pos| of RTL segments, text-space coordinate of the run beginning is
306 // saved and the segment widths are applied in reverse order.
307 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}.
308 struct RtlData {
309 int run_x_; // Beginning of the RTL run in text-space.
Alexei Svitkine (slow) 2013/08/12 20:42:33 It's a struct, so no need for trailing _'s. Altho
ckocagil 2013/08/13 20:22:28 Got rid of this struct.
310 std::vector<LineSegment*> segments_;
311 };
312
313 // Breaks a run into segments of at most |max_width| width, adds the segments,
314 // returns the width of the final segment that fits the current line.
315 int BreakRun(const internal::TextRun& run) {
316 BreakRunResults results =
317 BreakRunAtWidth(run, *words_, pos_, max_width_ - line_x_);
318
319 // Break the run until it fits the current line.
320 while (results.overflowed) {
321 DCHECK(pos_ < run.range.end());
322 Advance(results.overflow_pos + 1, results.width);
323 // First, try rolling back one word to move it to the next line. If
324 // it's not possible, roll back one character. If neither of these
325 // could be done, don't do any rollback since each line must have at
326 // least one character.
327 if (line_x_ > 0 || results.word_rollback_width < results.width)
Alexei Svitkine (slow) 2013/08/12 20:42:33 I don't understand this logic. Why do we Rollback(
ckocagil 2013/08/13 20:22:28 This condition is to avoid rolling back the entire
Alexei Svitkine (slow) 2013/08/13 20:56:32 I see. I think I understand how this works now. Yo
ckocagil 2013/08/14 11:40:25 Good call. Done.
328 Rollback(results.word_rollback_pos, results.word_rollback_width);
329 else if (line_x_ > 0 || results.char_rollback_width < results.width)
330 Rollback(results.overflow_pos, results.char_rollback_width);
331 CommitSegment(run);
332 SkipLine();
333
334 results = BreakRunAtWidth(run, *words_, pos_, max_width_ - line_x_);
335 }
336
337 return results.width;
338 }
339
340 // Applies |rtl_data_|.
341 void PopRtl() {
342 int x = rtl_data_.run_x_;
343 while (!rtl_data_.segments_.empty()) {
Alexei Svitkine (slow) 2013/08/12 20:42:33 I think it would be cleaner to just iterate over t
ckocagil 2013/08/13 20:22:28 Done, it now iterates and .clear()s.
344 LineSegment* segment = rtl_data_.segments_.back();
345 rtl_data_.segments_.pop_back();
346 segment->x_pos = ui::Range(x, x + segment->x_pos.length());
347 x += segment->x_pos.length();
348 }
349 }
350
351 void ResetPos(size_t new_pos) {
352 pos_ = new_pos;
353 current_pos_ = new_pos;
354 current_delta_ = 0;
355 }
356
357 void Advance(size_t new_pos, int amount) {
358 current_pos_ = new_pos;
359 current_delta_ += amount;
360 }
361
362 void Rollback(size_t new_pos, int amount) {
363 Advance(new_pos, -amount);
364 }
365
366 void SkipLine() {
367 if (!lines_.empty())
368 preceding_line_heights_ += lines_.back().height;
369 line_x_ = 0;
370 lines_.push_back(internal::Line());
371 }
372
373 void CommitSegment(const internal::TextRun& run) {
374 if (pos_ == current_pos_) {
375 DCHECK(current_delta_ == 0);
376 return;
377 }
378 internal::LineSegmentWin* segment = new internal::LineSegmentWin;
379 segment->run = &run;
380 segment->char_pos = ui::Range(pos_, current_pos_);
381 segment->x_pos = ui::Range(text_x_, text_x_ + current_delta_);
382 lines_.back().segments.push_back(segment);
383 lines_.back().width += segment->x_pos.length();
384 lines_.back().height = std::max(lines_.back().height,
385 segment->run->font.GetHeight());
386 lines_.back().baseline = std::max(lines_.back().baseline,
387 segment->run->font.GetBaseline());
388 lines_.back().preceding_heights = preceding_line_heights_;
389 if (run.script_analysis.fRTL) {
390 if (pos_ == run.range.start())
391 rtl_data_.run_x_ = text_x_;
392 rtl_data_.segments_.push_back(segment);
Alexei Svitkine (slow) 2013/08/12 20:42:33 It seems rtl_data_.segments_ is just a list of seg
ckocagil 2013/08/13 20:22:28 I realized that |rtl_data_.run_x_| isn't really ne
393 if (current_pos_ == run.range.end())
394 PopRtl();
395 }
396 pos_ = current_pos_;
397 text_x_ += current_delta_;
398 line_x_ += current_delta_;
399 current_delta_ = 0;
400 }
401
402 std::vector<internal::Line> lines_;
403 int max_width_;
404 bool multiline_;
405 const BreakList<size_t>* words_;
406 int preceding_line_heights_;
407 size_t pos_;
Alexei Svitkine (slow) 2013/08/12 20:42:33 Can you document these vars? I'd like to e.g. unde
ckocagil 2013/08/13 20:22:28 Done both.
408 int text_x_;
409 int line_x_;
410 size_t current_pos_;
411 int current_delta_;
412 RtlData rtl_data_;
Alexei Svitkine (slow) 2013/08/12 20:42:33 Hmm, I'm not convinced this needs to be its own st
ckocagil 2013/08/13 20:22:28 Got rid of the struct.
413
414 DISALLOW_COPY_AND_ASSIGN(LineBreaker);
415 };
416
200 } // namespace internal 417 } // namespace internal
201 418
202 // static 419 // static
203 HDC RenderTextWin::cached_hdc_ = NULL; 420 HDC RenderTextWin::cached_hdc_ = NULL;
204 421
205 // static 422 // static
206 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; 423 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_;
207 424
208 RenderTextWin::RenderTextWin() 425 RenderTextWin::RenderTextWin()
209 : RenderText(), 426 : RenderText(),
210 common_baseline_(0), 427 common_baseline_(0),
211 needs_layout_(false) { 428 needs_layout_(false) {
212 set_truncate_length(kMaxUniscribeTextLength); 429 set_truncate_length(kMaxUniscribeTextLength);
213 430
214 memset(&script_control_, 0, sizeof(script_control_)); 431 memset(&script_control_, 0, sizeof(script_control_));
215 memset(&script_state_, 0, sizeof(script_state_)); 432 memset(&script_state_, 0, sizeof(script_state_));
216 433
217 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); 434 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT));
218 } 435 }
219 436
220 RenderTextWin::~RenderTextWin() { 437 RenderTextWin::~RenderTextWin() {
221 } 438 }
222 439
223 Size RenderTextWin::GetStringSize() { 440 Size RenderTextWin::GetStringSize() {
224 EnsureLayout(); 441 EnsureLayout();
225 return string_size_; 442 return string_size_;
226 } 443 }
227 444
445 Size RenderTextWin::GetMultilineTextSize() {
446 EnsureLayout();
447 if (!multiline())
448 return Size(display_rect().width(), string_size_.height());
449 return Size(display_rect().width(),
450 lines().back().preceding_heights + lines().back().height);
451 }
452
228 int RenderTextWin::GetBaseline() { 453 int RenderTextWin::GetBaseline() {
229 EnsureLayout(); 454 EnsureLayout();
230 return common_baseline_; 455 return common_baseline_;
231 } 456 }
232 457
233 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { 458 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
234 if (text().empty()) 459 if (text().empty())
235 return SelectionModel(); 460 return SelectionModel();
236 461
237 EnsureLayout(); 462 EnsureLayout();
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
376 GetGlyphXBoundary(run, layout_index, true)); 601 GetGlyphXBoundary(run, layout_index, true));
377 } 602 }
378 603
379 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { 604 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) {
380 DCHECK(!needs_layout_); 605 DCHECK(!needs_layout_);
381 DCHECK(ui::Range(0, text().length()).Contains(range)); 606 DCHECK(ui::Range(0, text().length()).Contains(range));
382 ui::Range layout_range(TextIndexToLayoutIndex(range.start()), 607 ui::Range layout_range(TextIndexToLayoutIndex(range.start()),
383 TextIndexToLayoutIndex(range.end())); 608 TextIndexToLayoutIndex(range.end()));
384 DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range)); 609 DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range));
385 610
386 std::vector<Rect> bounds; 611 std::vector<ui::Range> bounds;
612 std::vector<Rect> rects;
387 if (layout_range.is_empty()) 613 if (layout_range.is_empty())
388 return bounds; 614 return rects;
389 615
390 // Add a Rect for each run/selection intersection. 616 // Add a Range for each run/selection intersection.
391 // TODO(msw): The bounds should probably not always be leading the range ends. 617 // TODO(msw): The bounds should probably not always be leading the range ends.
392 for (size_t i = 0; i < runs_.size(); ++i) { 618 for (size_t i = 0; i < runs_.size(); ++i) {
393 const internal::TextRun* run = runs_[visual_to_logical_[i]]; 619 const internal::TextRun* run = runs_[visual_to_logical_[i]];
394 ui::Range intersection = run->range.Intersect(layout_range); 620 ui::Range intersection = run->range.Intersect(layout_range);
395 if (intersection.IsValid()) { 621 if (intersection.IsValid()) {
396 DCHECK(!intersection.is_reversed()); 622 DCHECK(!intersection.is_reversed());
397 ui::Range range_x(GetGlyphXBoundary(run, intersection.start(), false), 623 ui::Range range_x(GetGlyphXBoundary(run, intersection.start(), false),
398 GetGlyphXBoundary(run, intersection.end(), false)); 624 GetGlyphXBoundary(run, intersection.end(), false));
399 Rect rect(range_x.GetMin(), 0, range_x.length(), run->font.GetHeight()); 625 if (range_x.is_empty())
400 rect.set_origin(ToViewPoint(rect.origin())); 626 continue;
401 // Union this with the last rect if they're adjacent. 627 range_x = ui::Range(range_x.GetMin(), range_x.GetMax());
402 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { 628 // Union this with the last range if they're adjacent.
403 rect.Union(bounds.back()); 629 DCHECK(bounds.empty() || bounds.back().GetMin() != range_x.GetMax());
630 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) {
631 range_x = ui::Range(bounds.back().GetMin(), range_x.GetMax());
404 bounds.pop_back(); 632 bounds.pop_back();
405 } 633 }
406 bounds.push_back(rect); 634 bounds.push_back(range_x);
407 } 635 }
408 } 636 }
409 return bounds; 637 for (size_t i = 0; i < bounds.size(); ++i) {
638 std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]);
639 rects.insert(rects.end(), current_rects.begin(), current_rects.end());
640 }
641 return rects;
410 } 642 }
411 643
412 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { 644 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const {
413 DCHECK_LE(index, text().length()); 645 DCHECK_LE(index, text().length());
414 ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index; 646 ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index;
415 CHECK_GE(i, 0); 647 CHECK_GE(i, 0);
416 // Clamp layout indices to the length of the text actually used for layout. 648 // Clamp layout indices to the length of the text actually used for layout.
417 return std::min<size_t>(GetLayoutText().length(), i); 649 return std::min<size_t>(GetLayoutText().length(), i);
418 } 650 }
419 651
(...skipping 21 matching lines...) Expand all
441 position < LayoutIndexToTextIndex(GetLayoutText().length()) && 673 position < LayoutIndexToTextIndex(GetLayoutText().length()) &&
442 GetGlyphBounds(position) != GetGlyphBounds(position - 1); 674 GetGlyphBounds(position) != GetGlyphBounds(position - 1);
443 } 675 }
444 676
445 void RenderTextWin::ResetLayout() { 677 void RenderTextWin::ResetLayout() {
446 // Layout is performed lazily as needed for drawing/metrics. 678 // Layout is performed lazily as needed for drawing/metrics.
447 needs_layout_ = true; 679 needs_layout_ = true;
448 } 680 }
449 681
450 void RenderTextWin::EnsureLayout() { 682 void RenderTextWin::EnsureLayout() {
451 if (!needs_layout_) 683 if (needs_layout_) {
452 return; 684 // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
453 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. 685 ItemizeLogicalText();
454 ItemizeLogicalText(); 686 if (!runs_.empty())
455 if (!runs_.empty()) 687 LayoutVisualText();
456 LayoutVisualText(); 688 needs_layout_ = false;
457 needs_layout_ = false; 689 std::vector<internal::Line> lines;
690 set_lines(&lines);
691 }
692 // Compute lines if they're not valid. This is separate from the layout steps
693 // above to avoid text layout and shaping when we resize |display_rect_|.
694 if (lines().empty())
695 ComputeLines();
696 }
697
698 void RenderTextWin::ComputeLines() {
699 DCHECK(!needs_layout_);
700 std::vector<internal::Line> lines;
701 internal::LineBreaker line_breaker(display_rect().width(), multiline(),
702 multiline() ? &GetLineBreaks() : 0);
Alexei Svitkine (slow) 2013/08/12 20:42:33 Nit: Align with previous' line's params.
ckocagil 2013/08/13 20:22:28 Done.
703 for (size_t i = 0; i < runs_.size(); ++i)
704 line_breaker.AddRun(*runs_[visual_to_logical_[i]]);
705 line_breaker.Finalize(&lines);
706 DCHECK(!lines.empty());
707 set_lines(&lines);
458 } 708 }
459 709
460 void RenderTextWin::DrawVisualText(Canvas* canvas) { 710 void RenderTextWin::DrawVisualText(Canvas* canvas) {
461 DCHECK(!needs_layout_); 711 DCHECK(!needs_layout_);
462 712 DCHECK(!multiline() || !lines().empty());
463 // Skia will draw glyphs with respect to the baseline.
464 Vector2d offset(GetTextOffset() + Vector2d(0, common_baseline_));
465
466 SkScalar x = SkIntToScalar(offset.x());
467 SkScalar y = SkIntToScalar(offset.y());
468 713
469 std::vector<SkPoint> pos; 714 std::vector<SkPoint> pos;
470 715
471 internal::SkiaTextRenderer renderer(canvas); 716 internal::SkiaTextRenderer renderer(canvas);
472 ApplyFadeEffects(&renderer); 717 ApplyFadeEffects(&renderer);
473 ApplyTextShadows(&renderer); 718 ApplyTextShadows(&renderer);
474 719
475 bool smoothing_enabled; 720 bool smoothing_enabled;
476 bool cleartype_enabled; 721 bool cleartype_enabled;
477 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); 722 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled);
478 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. 723 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|.
479 renderer.SetFontSmoothingSettings( 724 renderer.SetFontSmoothingSettings(
480 smoothing_enabled, cleartype_enabled && !background_is_transparent()); 725 smoothing_enabled, cleartype_enabled && !background_is_transparent());
481 726
482 ApplyCompositionAndSelectionStyles(); 727 ApplyCompositionAndSelectionStyles();
483 728
484 for (size_t i = 0; i < runs_.size(); ++i) { 729 for (size_t i = 0; i < lines().size(); ++i) {
485 // Get the run specified by the visual-to-logical map. 730 const internal::Line& line = lines()[i];
486 internal::TextRun* run = runs_[visual_to_logical_[i]]; 731 Vector2d line_offset = GetLineOffset(i);
732 Vector2d text_offset = line_offset + Vector2d(0, line.baseline);
733 int preceding_segment_widths = 0;
487 734
488 // Skip painting empty runs and runs outside the display rect area. 735 // Skip painting empty lines or lines outside the display rect area.
489 if ((run->glyph_count == 0) || (x >= display_rect().right()) || 736 if (!display_rect().Intersects(Rect(PointAtOffsetFromOrigin(line_offset),
490 (x + run->width <= display_rect().x())) { 737 Size(line.width, line.height))))
491 x += run->width;
492 continue; 738 continue;
739
740 for (size_t j = 0; j < line.segments.size(); ++j) {
741 const internal::LineSegmentWin* segment =
742 static_cast<internal::LineSegmentWin*>(line.segments[j]);
743 const int segment_width = segment->x_pos.length();
744 const internal::TextRun* run = segment->run;
745 DCHECK(!segment->char_pos.is_empty());
746 DCHECK(run->range.Contains(segment->char_pos));
747 ui::Range glyphs = CharRangeToGlyphRange(*run, segment->char_pos);
748 if (glyphs.is_empty()) {
749 DCHECK(segment_width == 0);
750 continue;
751 }
752 // Skip painting segments outside the display rect area.
753 Rect segment_bounds(PointAtOffsetFromOrigin(line_offset) +
754 Vector2d(preceding_segment_widths, 0),
755 Size(segment_width, line.height));
756 if (!display_rect().Intersects(segment_bounds)) {
757 preceding_segment_widths += segment_width;
758 continue;
759 }
760
761 int segment_x = 0;
762 pos.resize(glyphs.length());
763 for (size_t g = glyphs.start(); g < glyphs.end(); ++g) {
764 pos[g - glyphs.start()].set(
765 SkIntToScalar(text_offset.x() + preceding_segment_widths +
766 segment_x + run->offsets[g].du),
767 SkIntToScalar(text_offset.y() + run->offsets[g].dv));
768 segment_x += run->advance_widths[g];
769 }
770
771 renderer.SetTextSize(run->font.GetFontSize());
772 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style);
773
774 for (BreakList<SkColor>::const_iterator it =
775 colors().GetBreak(run->range.start());
776 it != colors().breaks().end() && it->first < run->range.end();
777 ++it) {
778 ui::Range intersection = colors().GetRange(it).Intersect(
779 segment->char_pos);
780 ui::Range colored_glyphs = CharRangeToGlyphRange(*run, intersection);
781 DCHECK(glyphs.Contains(colored_glyphs));
782 if (colored_glyphs.is_empty())
783 continue;
784 renderer.SetForegroundColor(it->second);
785 renderer.DrawPosText(&pos[colored_glyphs.start() - glyphs.start()],
786 &run->glyphs[colored_glyphs.start()],
787 colored_glyphs.length());
788 SkScalar width = (colored_glyphs.end() < glyphs.end()
789 ? pos[colored_glyphs.end() - glyphs.start()].x()
790 : pos[0].x() + SkIntToScalar(segment_width))
791 - pos[colored_glyphs.start() - glyphs.start()].x();
792 renderer.DrawDecorations(
793 pos[colored_glyphs.start() - glyphs.start()].x(), text_offset.y(),
794 SkScalarCeilToInt(width), run->underline, run->strike,
795 run->diagonal_strike);
796 }
797
798 preceding_segment_widths += segment_width;
493 } 799 }
494
495 // Based on WebCore::skiaDrawText. |pos| contains the positions of glyphs.
496 // An extra terminal |pos| entry is added to simplify width calculations.
497 pos.resize(run->glyph_count + 1);
498 SkScalar glyph_x = x;
499 for (int glyph = 0; glyph < run->glyph_count; glyph++) {
500 pos[glyph].set(glyph_x + run->offsets[glyph].du,
501 y + run->offsets[glyph].dv);
502 glyph_x += SkIntToScalar(run->advance_widths[glyph]);
503 }
504 pos.back().set(glyph_x, y);
505
506 renderer.SetTextSize(run->font.GetFontSize());
507 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style);
508
509 for (BreakList<SkColor>::const_iterator it =
510 colors().GetBreak(run->range.start());
511 it != colors().breaks().end() && it->first < run->range.end();
512 ++it) {
513 const ui::Range glyph_range = CharRangeToGlyphRange(*run,
514 colors().GetRange(it).Intersect(run->range));
515 if (glyph_range.is_empty())
516 continue;
517 renderer.SetForegroundColor(it->second);
518 renderer.DrawPosText(&pos[glyph_range.start()],
519 &run->glyphs[glyph_range.start()],
520 glyph_range.length());
521 const SkScalar width = pos[glyph_range.end()].x() -
522 pos[glyph_range.start()].x();
523 renderer.DrawDecorations(pos[glyph_range.start()].x(), y,
524 SkScalarCeilToInt(width), run->underline,
525 run->strike, run->diagonal_strike);
526 }
527
528 DCHECK_EQ(glyph_x - x, run->width);
529 x = glyph_x;
530 } 800 }
531 801
532 UndoCompositionAndSelectionStyles(); 802 UndoCompositionAndSelectionStyles();
533 } 803 }
534 804
535 void RenderTextWin::ItemizeLogicalText() { 805 void RenderTextWin::ItemizeLogicalText() {
536 runs_.clear(); 806 runs_.clear();
537 // Make |string_size_|'s height and |common_baseline_| tall enough to draw 807 // Make |string_size_|'s height and |common_baseline_| tall enough to draw
538 // often-used characters which are rendered with fonts in the font list. 808 // often-used characters which are rendered with fonts in the font list.
539 string_size_ = Size(0, font_list().GetHeight()); 809 string_size_ = Size(0, font_list().GetHeight());
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after
897 size_t position = LayoutIndexToTextIndex(run->range.end()); 1167 size_t position = LayoutIndexToTextIndex(run->range.end());
898 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); 1168 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
899 return SelectionModel(position, CURSOR_FORWARD); 1169 return SelectionModel(position, CURSOR_FORWARD);
900 } 1170 }
901 1171
902 RenderText* RenderText::CreateInstance() { 1172 RenderText* RenderText::CreateInstance() {
903 return new RenderTextWin; 1173 return new RenderTextWin;
904 } 1174 }
905 1175
906 } // namespace gfx 1176 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698