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