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

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: min height/baseline; update tests Created 7 years, 3 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
« no previous file with comments | « ui/gfx/render_text_win.h ('k') | ui/views/examples/example_base.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/char_iterator.h" 10 #include "base/i18n/char_iterator.h"
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 } else { 149 } else {
150 result = gfx::Range(run.logical_clusters[run_range.start()], 150 result = gfx::Range(run.logical_clusters[run_range.start()],
151 run_range.end() < run.range.length() ? 151 run_range.end() < run.range.length() ?
152 run.logical_clusters[run_range.end()] : run.glyph_count); 152 run.logical_clusters[run_range.end()] : run.glyph_count);
153 } 153 }
154 DCHECK(!result.is_reversed()); 154 DCHECK(!result.is_reversed());
155 DCHECK(gfx::Range(0, run.glyph_count).Contains(result)); 155 DCHECK(gfx::Range(0, run.glyph_count).Contains(result));
156 return result; 156 return result;
157 } 157 }
158 158
159 // Starting from |start_char|, finds a suitable line break position at or before
160 // |available_width| using word break info from |breaks|. If |empty_line| is
161 // true, this function will not roll back to |start_char| and |*next_char| will
162 // be greater than |start_char| (to avoid constructing empty lines).
163 // TODO(ckocagil): Do not break ligatures and diacritics.
164 // TextRun::logical_clusters might help.
165 // TODO(ckocagil): We might have to reshape after breaking at ligatures.
166 // See whether resolving the TODO above resolves this too.
167 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines.
168 void BreakRunAtWidth(const internal::TextRun& run,
169 const BreakList<size_t>& breaks,
170 size_t start_char,
171 int available_width,
172 bool empty_line,
173 int* width,
174 size_t* next_char) {
175 DCHECK(run.range.Contains(Range(start_char, start_char + 1)));
176 BreakList<size_t>::const_iterator word = breaks.GetBreak(start_char);
177 BreakList<size_t>::const_iterator next_word = word + 1;
178 // Width from |std::max(word->first, start_char)| to the current character.
179 int word_width = 0;
180 *width = 0;
181
182 for (size_t i = start_char; i < run.range.end(); ++i) {
183 // |word| holds the word boundary at or before |i|, and |next_word| holds
184 // the word boundary right after |i|. Advance both |word| and |next_word|
185 // when |i| reaches |next_word|.
186 if (next_word != breaks.breaks().end() && i >= next_word->first) {
187 word = next_word++;
188 word_width = 0;
189 }
190
191 Range glyph_range = CharRangeToGlyphRange(run, Range(i, i + 1));
192 int char_width = 0;
193 for (size_t j = glyph_range.start(); j < glyph_range.end(); ++j)
194 char_width += run.advance_widths[j];
195
196 *width += char_width;
197 word_width += char_width;
198
199 if (*width > available_width) {
200 if (!empty_line || word_width < *width) {
201 *width -= word_width;
202 *next_char = std::max(word->first, start_char);
203 } else if (char_width < *width) {
204 *width -= char_width;
205 *next_char = i;
206 } else {
207 *next_char = i + 1;
208 }
209
210 return;
211 }
212 }
213
214 *next_char = run.range.end();
215 }
216
217 // For segments in the same run, checks the continuity and order of |x_range|
218 // and |char_range| fields.
219 void CheckLineIntegrity(const std::vector<internal::Line>& lines,
220 const ScopedVector<internal::TextRun>& runs) {
221 size_t previous_segment_line = 0;
222 const internal::LineSegment* previous_segment = NULL;
223
224 for (size_t i = 0; i < lines.size(); ++i) {
225 for (size_t j = 0; j < lines[i].segments.size(); ++j) {
226 const internal::LineSegment* segment = &lines[i].segments[j];
227 internal::TextRun* run = runs[segment->run];
228
229 if (!previous_segment) {
230 previous_segment = segment;
231 } else if (runs[previous_segment->run] != run) {
232 previous_segment = NULL;
233 } else {
234 DCHECK_EQ(previous_segment->char_range.end(),
235 segment->char_range.start());
236 if (!run->script_analysis.fRTL) {
237 DCHECK_EQ(previous_segment->x_range.end(), segment->x_range.start());
238 } else {
239 DCHECK_EQ(segment->x_range.end(), previous_segment->x_range.start());
240 }
241
242 previous_segment = segment;
243 previous_segment_line = i;
244 }
245 }
246 }
247 }
248
159 } // namespace 249 } // namespace
160 250
161 namespace internal { 251 namespace internal {
162 252
163 TextRun::TextRun() 253 TextRun::TextRun()
164 : font_style(0), 254 : font_style(0),
165 strike(false), 255 strike(false),
166 diagonal_strike(false), 256 diagonal_strike(false),
167 underline(false), 257 underline(false),
168 width(0), 258 width(0),
(...skipping 23 matching lines...) Expand all
192 run->glyph_count, 282 run->glyph_count,
193 run->logical_clusters.get(), 283 run->logical_clusters.get(),
194 run->visible_attributes.get(), 284 run->visible_attributes.get(),
195 run->advance_widths.get(), 285 run->advance_widths.get(),
196 &run->script_analysis, 286 &run->script_analysis,
197 &x); 287 &x);
198 DCHECK(SUCCEEDED(hr)); 288 DCHECK(SUCCEEDED(hr));
199 return run->preceding_run_widths + x; 289 return run->preceding_run_widths + x;
200 } 290 }
201 291
292 // Internal class to generate Line structures. If |multiline| is true, the text
293 // is broken into lines at |words| boundaries such that each line is no longer
294 // than |max_width|. If |multiline| is false, only outputs a single Line from
295 // the given runs. |min_baseline| and |min_height| are the minimum baseline and
296 // height for each line.
297 // TODO(ckocagil): Expose the interface of this class in the header and test
298 // this class directly.
299 class LineBreaker {
300 public:
301 LineBreaker(int max_width,
302 int min_baseline,
303 int min_height,
304 bool multiline,
305 const BreakList<size_t>* words,
306 const ScopedVector<TextRun>& runs)
307 : max_width_(max_width),
308 min_baseline_(min_baseline),
309 min_height_(min_height),
310 multiline_(multiline),
311 words_(words),
312 runs_(runs),
313 text_x_(0),
314 line_x_(0),
315 line_ascent_(0),
316 line_descent_(0) {
317 AdvanceLine();
318 }
319
320 // Breaks the run at given |run_index| into Line structs.
321 void AddRun(int run_index) {
322 const TextRun* run = runs_[run_index];
323 if (multiline_ && line_x_ + run->width > max_width_)
324 BreakRun(run_index);
325 else
326 AddSegment(run_index, run->range, run->width);
327 }
328
329 // Finishes line breaking and outputs the results. Can be called at most once.
330 void Finalize(std::vector<Line>* lines, Size* size) {
331 DCHECK(!lines_.empty());
332 // Add an empty line to finish the line size calculation and remove it.
333 AdvanceLine();
334 lines_.pop_back();
335 *size = total_size_;
336 lines->swap(lines_);
337 }
338
339 private:
340 // A (line index, segment index) pair that specifies a segment in |lines_|.
341 typedef std::pair<size_t, size_t> SegmentHandle;
342
343 LineSegment* SegmentFromHandle(const SegmentHandle& handle) {
344 return &lines_[handle.first].segments[handle.second];
345 }
346
347 // Breaks a run into segments that fit in the last line in |lines_| and adds
348 // them. Adds a new Line to the back of |lines_| whenever a new segment can't
349 // be added without the Line's width exceeding |max_width_|.
350 void BreakRun(int run_index) {
351 DCHECK(words_);
352 const TextRun* const run = runs_[run_index];
353 int width = 0;
354 size_t next_char = run->range.start();
355
356 // Break the run until it fits the current line.
357 while (next_char < run->range.end()) {
358 const size_t current_char = next_char;
359 BreakRunAtWidth(*run, *words_, current_char, max_width_ - line_x_,
360 line_x_ == 0, &width, &next_char);
361 AddSegment(run_index, Range(current_char, next_char), width);
362 if (next_char < run->range.end())
363 AdvanceLine();
364 }
365 }
366
367 // RTL runs are broken in logical order but displayed in visual order. To find
368 // the text-space coordinate (where it would fall in a single-line text)
369 // |x_range| of RTL segments, segment widths are applied in reverse order.
370 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}.
371 void UpdateRTLSegmentRanges() {
372 if (rtl_segments_.empty())
373 return;
374 int x = SegmentFromHandle(rtl_segments_[0])->x_range.start();
375 for (size_t i = rtl_segments_.size(); i > 0; --i) {
376 LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]);
377 const size_t segment_width = segment->x_range.length();
378 segment->x_range = Range(x, x + segment_width);
379 x += segment_width;
380 }
381 rtl_segments_.clear();
382 }
383
384 // Finishes the size calculations of the last Line in |lines_|. Adds a new
385 // Line to the back of |lines_|.
386 void AdvanceLine() {
387 if (!lines_.empty()) {
388 Line* line = &lines_.back();
389 // TODO(ckocagil): Determine optimal multiline height behavior.
390 if (line_ascent_ + line_descent_ == 0) {
391 line_ascent_ = min_baseline_;
392 line_descent_ = min_height_ - min_baseline_;
393 }
394 // Set the single-line mode Line's metrics to be at least
395 // |RenderText::font_list()| to not break the current single-line code.
396 line_ascent_ = std::max(line_ascent_, min_baseline_);
397 line_descent_ = std::max(line_descent_, min_height_ - min_baseline_);
398
399 line->baseline = line_ascent_;
400 line->size.set_height(line_ascent_ + line_descent_);
401 line->preceding_heights = total_size_.height();
402 total_size_.set_height(total_size_.height() + line->size.height());
403 total_size_.set_width(std::max(total_size_.width(), line->size.width()));
404 }
405 line_x_ = 0;
406 line_ascent_ = 0;
407 line_descent_ = 0;
408 lines_.push_back(Line());
409 }
410
411 // Adds a new segment with the given properties to |lines_.back()|.
412 void AddSegment(int run_index, Range char_range, int width) {
413 if (char_range.is_empty()) {
414 DCHECK_EQ(width, 0);
415 return;
416 }
417 const TextRun* run = runs_[run_index];
418 line_ascent_ = std::max(line_ascent_, run->font.GetBaseline());
419 line_descent_ = std::max(line_descent_,
420 run->font.GetHeight() - run->font.GetBaseline());
421
422 LineSegment segment;
423 segment.run = run_index;
424 segment.char_range = char_range;
425 segment.x_range = Range(text_x_, text_x_ + width);
426
427 Line* line = &lines_.back();
428 line->segments.push_back(segment);
429 line->size.set_width(line->size.width() + segment.x_range.length());
430 if (run->script_analysis.fRTL) {
431 rtl_segments_.push_back(SegmentHandle(lines_.size() - 1,
432 line->segments.size() - 1));
433 // If this is the last segment of an RTL run, reprocess the text-space x
434 // ranges of all segments from the run.
435 if (char_range.end() == run->range.end())
436 UpdateRTLSegmentRanges();
437 }
438 text_x_ += width;
439 line_x_ += width;
440 }
441
442 const int max_width_;
443 const int min_baseline_;
444 const int min_height_;
445 const bool multiline_;
446 const BreakList<size_t>* const words_;
447 const ScopedVector<TextRun>& runs_;
448
449 // Stores the resulting lines.
450 std::vector<Line> lines_;
451
452 // Text space and line space x coordinates of the next segment to be added.
453 int text_x_;
454 int line_x_;
455
456 // Size of the multiline text, not including the currently processed line.
457 Size total_size_;
458
459 // Ascent and descent values of the current line, |lines_.back()|.
460 int line_ascent_;
461 int line_descent_;
462
463 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|.
464 std::vector<SegmentHandle> rtl_segments_;
465
466 DISALLOW_COPY_AND_ASSIGN(LineBreaker);
467 };
468
202 } // namespace internal 469 } // namespace internal
203 470
204 // static 471 // static
205 HDC RenderTextWin::cached_hdc_ = NULL; 472 HDC RenderTextWin::cached_hdc_ = NULL;
206 473
207 // static 474 // static
208 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; 475 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_;
209 476
210 RenderTextWin::RenderTextWin() 477 RenderTextWin::RenderTextWin()
211 : RenderText(), 478 : RenderText(),
212 common_baseline_(0),
213 needs_layout_(false) { 479 needs_layout_(false) {
214 set_truncate_length(kMaxUniscribeTextLength); 480 set_truncate_length(kMaxUniscribeTextLength);
215 481
216 memset(&script_control_, 0, sizeof(script_control_)); 482 memset(&script_control_, 0, sizeof(script_control_));
217 memset(&script_state_, 0, sizeof(script_state_)); 483 memset(&script_state_, 0, sizeof(script_state_));
218 484
219 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); 485 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT));
220 } 486 }
221 487
222 RenderTextWin::~RenderTextWin() { 488 RenderTextWin::~RenderTextWin() {
223 } 489 }
224 490
225 Size RenderTextWin::GetStringSize() { 491 Size RenderTextWin::GetStringSize() {
226 EnsureLayout(); 492 EnsureLayout();
227 return string_size_; 493 return multiline_string_size_;
228 } 494 }
229 495
230 int RenderTextWin::GetBaseline() { 496 int RenderTextWin::GetBaseline() {
231 EnsureLayout(); 497 EnsureLayout();
232 return common_baseline_; 498 return lines()[0].baseline;
233 } 499 }
234 500
235 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { 501 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
236 if (text().empty()) 502 if (text().empty())
237 return SelectionModel(); 503 return SelectionModel();
238 504
239 EnsureLayout(); 505 EnsureLayout();
240 // Find the run that contains the point and adjust the argument location. 506 // Find the run that contains the point and adjust the argument location.
241 int x = ToTextPoint(point).x(); 507 int x = ToTextPoint(point).x();
242 size_t run_index = GetRunContainingXCoord(x); 508 size_t run_index = GetRunContainingXCoord(x);
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 } 630 }
365 } 631 }
366 return SelectionModel(pos, CURSOR_FORWARD); 632 return SelectionModel(pos, CURSOR_FORWARD);
367 } 633 }
368 634
369 gfx::Range RenderTextWin::GetGlyphBounds(size_t index) { 635 gfx::Range RenderTextWin::GetGlyphBounds(size_t index) {
370 const size_t run_index = 636 const size_t run_index =
371 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); 637 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
372 // Return edge bounds if the index is invalid or beyond the layout text size. 638 // Return edge bounds if the index is invalid or beyond the layout text size.
373 if (run_index >= runs_.size()) 639 if (run_index >= runs_.size())
374 return gfx::Range(string_size_.width()); 640 return Range(string_width_);
375 internal::TextRun* run = runs_[run_index]; 641 internal::TextRun* run = runs_[run_index];
376 const size_t layout_index = TextIndexToLayoutIndex(index); 642 const size_t layout_index = TextIndexToLayoutIndex(index);
377 return gfx::Range(GetGlyphXBoundary(run, layout_index, false), 643 return gfx::Range(GetGlyphXBoundary(run, layout_index, false),
378 GetGlyphXBoundary(run, layout_index, true)); 644 GetGlyphXBoundary(run, layout_index, true));
379 } 645 }
380 646
381 std::vector<Rect> RenderTextWin::GetSubstringBounds(const gfx::Range& range) { 647 std::vector<Rect> RenderTextWin::GetSubstringBounds(const gfx::Range& range) {
382 DCHECK(!needs_layout_); 648 DCHECK(!needs_layout_);
383 DCHECK(gfx::Range(0, text().length()).Contains(range)); 649 DCHECK(gfx::Range(0, text().length()).Contains(range));
384 gfx::Range layout_range(TextIndexToLayoutIndex(range.start()), 650 gfx::Range layout_range(TextIndexToLayoutIndex(range.start()),
385 TextIndexToLayoutIndex(range.end())); 651 TextIndexToLayoutIndex(range.end()));
386 DCHECK(gfx::Range(0, GetLayoutText().length()).Contains(layout_range)); 652 DCHECK(gfx::Range(0, GetLayoutText().length()).Contains(layout_range));
387 653
388 std::vector<Rect> bounds; 654 std::vector<Rect> rects;
389 if (layout_range.is_empty()) 655 if (layout_range.is_empty())
390 return bounds; 656 return rects;
657 std::vector<Range> bounds;
391 658
392 // Add a Rect for each run/selection intersection. 659 // Add a Range for each run/selection intersection.
393 // TODO(msw): The bounds should probably not always be leading the range ends. 660 // TODO(msw): The bounds should probably not always be leading the range ends.
394 for (size_t i = 0; i < runs_.size(); ++i) { 661 for (size_t i = 0; i < runs_.size(); ++i) {
395 const internal::TextRun* run = runs_[visual_to_logical_[i]]; 662 const internal::TextRun* run = runs_[visual_to_logical_[i]];
396 gfx::Range intersection = run->range.Intersect(layout_range); 663 gfx::Range intersection = run->range.Intersect(layout_range);
397 if (intersection.IsValid()) { 664 if (intersection.IsValid()) {
398 DCHECK(!intersection.is_reversed()); 665 DCHECK(!intersection.is_reversed());
399 gfx::Range range_x(GetGlyphXBoundary(run, intersection.start(), false), 666 gfx::Range range_x(GetGlyphXBoundary(run, intersection.start(), false),
400 GetGlyphXBoundary(run, intersection.end(), false)); 667 GetGlyphXBoundary(run, intersection.end(), false));
401 Rect rect(range_x.GetMin(), 0, range_x.length(), run->font.GetHeight()); 668 if (range_x.is_empty())
402 rect.set_origin(ToViewPoint(rect.origin())); 669 continue;
403 // Union this with the last rect if they're adjacent. 670 range_x = Range(range_x.GetMin(), range_x.GetMax());
404 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { 671 // Union this with the last range if they're adjacent.
405 rect.Union(bounds.back()); 672 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin());
673 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) {
674 range_x = Range(bounds.back().GetMin(), range_x.GetMax());
406 bounds.pop_back(); 675 bounds.pop_back();
407 } 676 }
408 bounds.push_back(rect); 677 bounds.push_back(range_x);
409 } 678 }
410 } 679 }
411 return bounds; 680 for (size_t i = 0; i < bounds.size(); ++i) {
681 std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]);
682 rects.insert(rects.end(), current_rects.begin(), current_rects.end());
683 }
684 return rects;
412 } 685 }
413 686
414 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { 687 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const {
415 DCHECK_LE(index, text().length()); 688 DCHECK_LE(index, text().length());
416 ptrdiff_t i = obscured() ? gfx::UTF16IndexToOffset(text(), 0, index) : index; 689 ptrdiff_t i = obscured() ? gfx::UTF16IndexToOffset(text(), 0, index) : index;
417 CHECK_GE(i, 0); 690 CHECK_GE(i, 0);
418 // Clamp layout indices to the length of the text actually used for layout. 691 // Clamp layout indices to the length of the text actually used for layout.
419 return std::min<size_t>(GetLayoutText().length(), i); 692 return std::min<size_t>(GetLayoutText().length(), i);
420 } 693 }
421 694
(...skipping 21 matching lines...) Expand all
443 position < LayoutIndexToTextIndex(GetLayoutText().length()) && 716 position < LayoutIndexToTextIndex(GetLayoutText().length()) &&
444 GetGlyphBounds(position) != GetGlyphBounds(position - 1); 717 GetGlyphBounds(position) != GetGlyphBounds(position - 1);
445 } 718 }
446 719
447 void RenderTextWin::ResetLayout() { 720 void RenderTextWin::ResetLayout() {
448 // Layout is performed lazily as needed for drawing/metrics. 721 // Layout is performed lazily as needed for drawing/metrics.
449 needs_layout_ = true; 722 needs_layout_ = true;
450 } 723 }
451 724
452 void RenderTextWin::EnsureLayout() { 725 void RenderTextWin::EnsureLayout() {
453 if (!needs_layout_) 726 if (needs_layout_) {
454 return; 727 // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
455 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. 728 ItemizeLogicalText();
456 ItemizeLogicalText(); 729 if (!runs_.empty())
457 if (!runs_.empty()) 730 LayoutVisualText();
458 LayoutVisualText(); 731 needs_layout_ = false;
459 needs_layout_ = false; 732 std::vector<internal::Line> lines;
733 set_lines(&lines);
734 }
735
736 // Compute lines if they're not valid. This is separate from the layout steps
737 // above to avoid text layout and shaping when we resize |display_rect_|.
738 if (lines().empty()) {
739 DCHECK(!needs_layout_);
740 std::vector<internal::Line> lines;
741 internal::LineBreaker line_breaker(display_rect().width() - 1,
742 font_list().GetBaseline(),
743 font_list().GetHeight(), multiline(),
744 multiline() ? &GetLineBreaks() : NULL,
745 runs_);
746 for (size_t i = 0; i < runs_.size(); ++i)
747 line_breaker.AddRun(visual_to_logical_[i]);
748 line_breaker.Finalize(&lines, &multiline_string_size_);
749 DCHECK(!lines.empty());
750 #ifndef NDEBUG
751 CheckLineIntegrity(lines, runs_);
752 #endif
753 set_lines(&lines);
754 }
460 } 755 }
461 756
462 void RenderTextWin::DrawVisualText(Canvas* canvas) { 757 void RenderTextWin::DrawVisualText(Canvas* canvas) {
463 DCHECK(!needs_layout_); 758 DCHECK(!needs_layout_);
464 759 DCHECK(!lines().empty());
465 // Skia will draw glyphs with respect to the baseline.
466 Vector2d offset(GetTextOffset() + Vector2d(0, common_baseline_));
467
468 SkScalar x = SkIntToScalar(offset.x());
469 SkScalar y = SkIntToScalar(offset.y());
470 760
471 std::vector<SkPoint> pos; 761 std::vector<SkPoint> pos;
472 762
473 internal::SkiaTextRenderer renderer(canvas); 763 internal::SkiaTextRenderer renderer(canvas);
474 ApplyFadeEffects(&renderer); 764 ApplyFadeEffects(&renderer);
475 ApplyTextShadows(&renderer); 765 ApplyTextShadows(&renderer);
476 766
477 bool smoothing_enabled; 767 bool smoothing_enabled;
478 bool cleartype_enabled; 768 bool cleartype_enabled;
479 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); 769 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled);
480 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. 770 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|.
481 renderer.SetFontSmoothingSettings( 771 renderer.SetFontSmoothingSettings(
482 smoothing_enabled, cleartype_enabled && !background_is_transparent()); 772 smoothing_enabled, cleartype_enabled && !background_is_transparent());
483 773
484 ApplyCompositionAndSelectionStyles(); 774 ApplyCompositionAndSelectionStyles();
485 775
486 for (size_t i = 0; i < runs_.size(); ++i) { 776 for (size_t i = 0; i < lines().size(); ++i) {
487 // Get the run specified by the visual-to-logical map. 777 const internal::Line& line = lines()[i];
488 internal::TextRun* run = runs_[visual_to_logical_[i]]; 778 const Vector2d line_offset = GetLineOffset(i);
489 779
490 // Skip painting empty runs and runs outside the display rect area. 780 // Skip painting empty lines or lines outside the display rect area.
491 if ((run->glyph_count == 0) || (x >= display_rect().right()) || 781 if (!display_rect().Intersects(Rect(PointAtOffsetFromOrigin(line_offset),
492 (x + run->width <= display_rect().x())) { 782 line.size)))
493 x += run->width;
494 continue; 783 continue;
784
785 const Vector2d text_offset = line_offset + Vector2d(0, line.baseline);
786 int preceding_segment_widths = 0;
787
788 for (size_t j = 0; j < line.segments.size(); ++j) {
789 const internal::LineSegment* segment = &line.segments[j];
790 const int segment_width = segment->x_range.length();
791 const internal::TextRun* run = runs_[segment->run];
792 DCHECK(!segment->char_range.is_empty());
793 DCHECK(run->range.Contains(segment->char_range));
794 Range glyph_range = CharRangeToGlyphRange(*run, segment->char_range);
795 DCHECK(!glyph_range.is_empty());
796 // Skip painting segments outside the display rect area.
797 if (!multiline()) {
798 const Rect segment_bounds(PointAtOffsetFromOrigin(line_offset) +
799 Vector2d(preceding_segment_widths, 0),
800 Size(segment_width, line.size.height()));
801 if (!display_rect().Intersects(segment_bounds)) {
802 preceding_segment_widths += segment_width;
803 continue;
804 }
805 }
806
807 // |pos| contains the positions of glyphs. An extra terminal |pos| entry
808 // is added to simplify width calculations.
809 int segment_x = preceding_segment_widths;
810 pos.resize(glyph_range.length() + 1);
811 for (size_t k = glyph_range.start(); k < glyph_range.end(); ++k) {
812 pos[k - glyph_range.start()].set(
813 SkIntToScalar(text_offset.x() + run->offsets[k].du + segment_x),
814 SkIntToScalar(text_offset.y() + run->offsets[k].dv));
815 segment_x += run->advance_widths[k];
816 }
817 pos.back().set(SkIntToScalar(text_offset.x() + segment_x),
818 SkIntToScalar(text_offset.y()));
819
820 renderer.SetTextSize(run->font.GetFontSize());
821 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style);
822
823 for (BreakList<SkColor>::const_iterator it =
824 colors().GetBreak(segment->char_range.start());
825 it != colors().breaks().end() &&
826 it->first < segment->char_range.end();
827 ++it) {
828 const Range intersection =
829 colors().GetRange(it).Intersect(segment->char_range);
830 const Range colored_glyphs = CharRangeToGlyphRange(*run, intersection);
831 DCHECK(glyph_range.Contains(colored_glyphs));
832 DCHECK(!colored_glyphs.is_empty());
833 const SkPoint& start_pos =
834 pos[colored_glyphs.start() - glyph_range.start()];
835 const SkPoint& end_pos =
836 pos[colored_glyphs.end() - glyph_range.start()];
837
838 renderer.SetForegroundColor(it->second);
839 renderer.DrawPosText(&start_pos, &run->glyphs[colored_glyphs.start()],
840 colored_glyphs.length());
841 renderer.DrawDecorations(start_pos.x(), text_offset.y(),
842 SkScalarCeilToInt(end_pos.x() - start_pos.x()),
843 run->underline, run->strike,
844 run->diagonal_strike);
845 }
846
847 preceding_segment_widths += segment_width;
495 } 848 }
496
497 // Based on WebCore::skiaDrawText. |pos| contains the positions of glyphs.
498 // An extra terminal |pos| entry is added to simplify width calculations.
499 pos.resize(run->glyph_count + 1);
500 SkScalar glyph_x = x;
501 for (int glyph = 0; glyph < run->glyph_count; glyph++) {
502 pos[glyph].set(glyph_x + run->offsets[glyph].du,
503 y + run->offsets[glyph].dv);
504 glyph_x += SkIntToScalar(run->advance_widths[glyph]);
505 }
506 pos.back().set(glyph_x, y);
507
508 renderer.SetTextSize(run->font.GetFontSize());
509 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style);
510
511 for (BreakList<SkColor>::const_iterator it =
512 colors().GetBreak(run->range.start());
513 it != colors().breaks().end() && it->first < run->range.end();
514 ++it) {
515 const gfx::Range glyph_range = CharRangeToGlyphRange(*run,
516 colors().GetRange(it).Intersect(run->range));
517 if (glyph_range.is_empty())
518 continue;
519 renderer.SetForegroundColor(it->second);
520 renderer.DrawPosText(&pos[glyph_range.start()],
521 &run->glyphs[glyph_range.start()],
522 glyph_range.length());
523 const SkScalar width = pos[glyph_range.end()].x() -
524 pos[glyph_range.start()].x();
525 renderer.DrawDecorations(pos[glyph_range.start()].x(), y,
526 SkScalarCeilToInt(width), run->underline,
527 run->strike, run->diagonal_strike);
528 }
529
530 DCHECK_EQ(glyph_x - x, run->width);
531 x = glyph_x;
532 } 849 }
533 850
534 UndoCompositionAndSelectionStyles(); 851 UndoCompositionAndSelectionStyles();
535 } 852 }
536 853
537 void RenderTextWin::ItemizeLogicalText() { 854 void RenderTextWin::ItemizeLogicalText() {
538 runs_.clear(); 855 runs_.clear();
539 // Make |string_size_|'s height and |common_baseline_| tall enough to draw 856 string_width_ = 0;
540 // often-used characters which are rendered with fonts in the font list. 857 multiline_string_size_ = Size();
541 string_size_ = Size(0, font_list().GetHeight());
542 common_baseline_ = font_list().GetBaseline();
543 858
544 // Set Uniscribe's base text direction. 859 // Set Uniscribe's base text direction.
545 script_state_.uBidiLevel = 860 script_state_.uBidiLevel =
546 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) ? 1 : 0; 861 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) ? 1 : 0;
547 862
548 const base::string16& layout_text = GetLayoutText(); 863 const base::string16& layout_text = GetLayoutText();
549 if (layout_text.empty()) 864 if (layout_text.empty())
550 return; 865 return;
551 866
552 HRESULT hr = E_OUTOFMEMORY; 867 HRESULT hr = E_OUTOFMEMORY;
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
662 run->glyphs.get(), 977 run->glyphs.get(),
663 run->glyph_count, 978 run->glyph_count,
664 run->visible_attributes.get(), 979 run->visible_attributes.get(),
665 &(run->script_analysis), 980 &(run->script_analysis),
666 run->advance_widths.get(), 981 run->advance_widths.get(),
667 run->offsets.get(), 982 run->offsets.get(),
668 &(run->abc_widths)); 983 &(run->abc_widths));
669 DCHECK(SUCCEEDED(hr)); 984 DCHECK(SUCCEEDED(hr));
670 } 985 }
671 } 986 }
672 string_size_.set_height(ascent + descent);
673 common_baseline_ = ascent;
674 987
675 // Build the array of bidirectional embedding levels. 988 // Build the array of bidirectional embedding levels.
676 scoped_ptr<BYTE[]> levels(new BYTE[runs_.size()]); 989 scoped_ptr<BYTE[]> levels(new BYTE[runs_.size()]);
677 for (size_t i = 0; i < runs_.size(); ++i) 990 for (size_t i = 0; i < runs_.size(); ++i)
678 levels[i] = runs_[i]->script_analysis.s.uBidiLevel; 991 levels[i] = runs_[i]->script_analysis.s.uBidiLevel;
679 992
680 // Get the maps between visual and logical run indices. 993 // Get the maps between visual and logical run indices.
681 visual_to_logical_.reset(new int[runs_.size()]); 994 visual_to_logical_.reset(new int[runs_.size()]);
682 logical_to_visual_.reset(new int[runs_.size()]); 995 logical_to_visual_.reset(new int[runs_.size()]);
683 hr = ScriptLayout(runs_.size(), 996 hr = ScriptLayout(runs_.size(),
684 levels.get(), 997 levels.get(),
685 visual_to_logical_.get(), 998 visual_to_logical_.get(),
686 logical_to_visual_.get()); 999 logical_to_visual_.get());
687 DCHECK(SUCCEEDED(hr)); 1000 DCHECK(SUCCEEDED(hr));
688 1001
689 // Precalculate run width information. 1002 // Precalculate run width information.
690 size_t preceding_run_widths = 0; 1003 size_t preceding_run_widths = 0;
691 for (size_t i = 0; i < runs_.size(); ++i) { 1004 for (size_t i = 0; i < runs_.size(); ++i) {
692 internal::TextRun* run = runs_[visual_to_logical_[i]]; 1005 internal::TextRun* run = runs_[visual_to_logical_[i]];
693 run->preceding_run_widths = preceding_run_widths; 1006 run->preceding_run_widths = preceding_run_widths;
694 const ABC& abc = run->abc_widths; 1007 const ABC& abc = run->abc_widths;
695 run->width = abc.abcA + abc.abcB + abc.abcC; 1008 run->width = abc.abcA + abc.abcB + abc.abcC;
696 preceding_run_widths += run->width; 1009 preceding_run_widths += run->width;
697 } 1010 }
698 string_size_.set_width(preceding_run_widths); 1011 string_width_ = preceding_run_widths;
699 } 1012 }
700 1013
701 void RenderTextWin::LayoutTextRun(internal::TextRun* run) { 1014 void RenderTextWin::LayoutTextRun(internal::TextRun* run) {
702 const size_t run_length = run->range.length(); 1015 const size_t run_length = run->range.length();
703 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); 1016 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
704 Font original_font = run->font; 1017 Font original_font = run->font;
705 LinkedFontsIterator fonts(original_font); 1018 LinkedFontsIterator fonts(original_font);
706 bool tried_cached_font = false; 1019 bool tried_cached_font = false;
707 bool tried_fallback = false; 1020 bool tried_fallback = false;
708 // Keep track of the font that is able to display the greatest number of 1021 // Keep track of the font that is able to display the greatest number of
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
922 size_t position = LayoutIndexToTextIndex(run->range.end()); 1235 size_t position = LayoutIndexToTextIndex(run->range.end());
923 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); 1236 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
924 return SelectionModel(position, CURSOR_FORWARD); 1237 return SelectionModel(position, CURSOR_FORWARD);
925 } 1238 }
926 1239
927 RenderText* RenderText::CreateInstance() { 1240 RenderText* RenderText::CreateInstance() {
928 return new RenderTextWin; 1241 return new RenderTextWin;
929 } 1242 }
930 1243
931 } // namespace gfx 1244 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text_win.h ('k') | ui/views/examples/example_base.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698