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/char_iterator.h" | 10 #include "base/i18n/char_iterator.h" |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |