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

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

Issue 7458014: Implement Uniscribe RenderText for Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix RenderText::RightEndSelectionModel. Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "base/i18n/break_iterator.h"
8 #include "base/logging.h"
9 #include "base/stl_util.h"
10 #include "skia/ext/skia_utils_win.h"
11 #include "ui/gfx/canvas.h"
12 #include "ui/gfx/canvas_skia.h"
13
14 namespace {
15
16 // The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes.
17 // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
18 const int kGuessItems = 100;
19 const int kMaxItems = 10000;
20
21 // The maximum supported number of Uniscribe glyphs; a glyph is 1 word.
22 // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
23 const int kMaxGlyphs = 100000;
24
25 } // namespace
26
7 namespace gfx { 27 namespace gfx {
8 28
29 namespace internal {
30
31 TextRun::TextRun()
32 : strike(false),
33 width(0),
34 preceding_run_widths(0),
35 glyph_count(0) {
36 }
37
38 } // namespace internal
39
9 RenderTextWin::RenderTextWin() 40 RenderTextWin::RenderTextWin()
10 : RenderText() { 41 : RenderText(),
42 script_control_(),
43 script_state_(),
44 script_cache_(NULL),
45 string_width_(0) {
46 // Omitting default constructors for script_* would leave POD uninitialized.
47 HRESULT hr = 0;
48
49 // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message.
50 // TODO(msw): Use Chrome/profile locale/language settings?
51 hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_);
52 DCHECK(SUCCEEDED(hr));
53
54 hr = ScriptApplyDigitSubstitution(&digit_substitute_,
55 &script_control_,
56 &script_state_);
57 DCHECK(SUCCEEDED(hr));
58 script_control_.fMergeNeutralItems = true;
59
60 MoveCursorTo(LeftEndSelectionModel());
11 } 61 }
12 62
13 RenderTextWin::~RenderTextWin() { 63 RenderTextWin::~RenderTextWin() {
64 ScriptFreeCache(&script_cache_);
65 STLDeleteContainerPointers(runs_.begin(), runs_.end());
66 }
67
68 void RenderTextWin::SetText(const string16& text) {
69 // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
70 RenderText::SetText(text);
71 ItemizeLogicalText();
72 LayoutVisualText(CreateCompatibleDC(NULL));
73 }
74
75 void RenderTextWin::SetDisplayRect(const Rect& r) {
76 RenderText::SetDisplayRect(r);
77 ItemizeLogicalText();
78 LayoutVisualText(CreateCompatibleDC(NULL));
79 }
80
81 void RenderTextWin::ApplyStyleRange(StyleRange style_range) {
82 RenderText::ApplyStyleRange(style_range);
83 ItemizeLogicalText();
84 LayoutVisualText(CreateCompatibleDC(NULL));
85 }
86
87 void RenderTextWin::ApplyDefaultStyle() {
88 RenderText::ApplyDefaultStyle();
89 ItemizeLogicalText();
90 LayoutVisualText(CreateCompatibleDC(NULL));
91 }
92
93 int RenderTextWin::GetStringWidth() {
94 return string_width_;
95 }
96
97 void RenderTextWin::Draw(Canvas* canvas) {
98 skia::ScopedPlatformPaint scoped_platform_paint(canvas->AsCanvasSkia());
99 HDC hdc = scoped_platform_paint.GetPlatformSurface();
100 int saved_dc = SaveDC(hdc);
101 DrawSelection(canvas);
102 DrawVisualText(canvas);
103 DrawCursor(canvas);
104 RestoreDC(hdc, saved_dc);
105 }
106
107 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
108 if (text().empty())
109 return SelectionModel();
110
111 // Find the run that contains the point and adjust the argument location.
112 Point p(ToTextPoint(point));
113 size_t run_index = GetRunContainingPoint(p);
114 if (run_index == runs_.size())
115 return (p.x() < 0) ? LeftEndSelectionModel() : RightEndSelectionModel();
116 internal::TextRun* run = runs_[run_index];
117
118 int position = 0, trailing = 0;
119 HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths,
120 run->range.length(),
121 run->glyph_count,
122 run->logical_clusters.get(),
123 run->visible_attributes.get(),
124 run->advance_widths.get(),
125 &(run->script_analysis),
126 &position,
127 &trailing);
128 DCHECK(SUCCEEDED(hr));
129 position += run->range.start();
130
131 size_t cursor = position + trailing;
132 DCHECK_GT(cursor, 0U);
133 DCHECK_LE(cursor, text().length());
134 return SelectionModel(cursor, position,
135 (trailing > 0) ? SelectionModel::TRAILING : SelectionModel::LEADING);
136 }
137
138 Rect RenderTextWin::GetCursorBounds(const SelectionModel& selection,
139 bool insert_mode) {
140 // Highlight the logical cursor (selection end) when not in insert mode.
141 size_t pos = insert_mode ? selection.caret_pos() : selection.selection_end();
142 size_t run_index = GetRunContainingPosition(pos);
143 internal::TextRun* run = run_index == runs_.size() ? NULL : runs_[run_index];
144
145 int start_x = 0, end_x = 0;
146 if (run) {
147 HRESULT hr = 0;
148 hr = ScriptCPtoX(pos - run->range.start(),
149 false,
150 run->range.length(),
151 run->glyph_count,
152 run->logical_clusters.get(),
153 run->visible_attributes.get(),
154 run->advance_widths.get(),
155 &(run->script_analysis),
156 &start_x);
157 DCHECK(SUCCEEDED(hr));
158 hr = ScriptCPtoX(pos - run->range.start(),
159 true,
160 run->range.length(),
161 run->glyph_count,
162 run->logical_clusters.get(),
163 run->visible_attributes.get(),
164 run->advance_widths.get(),
165 &(run->script_analysis),
166 &end_x);
167 DCHECK(SUCCEEDED(hr));
168 }
169 // TODO(msw): Use the last visual run's font instead of the default font?
170 int height = run ? run->font.GetHeight() : default_style().font.GetHeight();
171 Rect rect(std::min(start_x, end_x), 0, std::abs(end_x - start_x), height);
172 // Offset to the run start or the right/left end for an out of bounds index.
173 // Also center the rect vertically in the display area.
174 int text_end_offset = base::i18n::IsRTL() ? 0 : GetStringWidth();
175 rect.Offset((run ? run->preceding_run_widths : text_end_offset),
176 (display_rect().height() - rect.height()) / 2);
177 // Adjust for leading/trailing in insert mode.
178 if (insert_mode && run) {
179 bool leading = selection.caret_placement() == SelectionModel::LEADING;
180 // Adjust the x value for right-side placement.
181 if (run->script_analysis.fRTL == leading)
182 rect.set_x(rect.right());
183 rect.set_width(0);
184 }
185 rect.set_origin(ToViewPoint(rect.origin()));
186 return rect;
187 }
188
189 SelectionModel RenderTextWin::GetLeftSelectionModel(
190 const SelectionModel& selection,
191 BreakType break_type) {
192 if (break_type == LINE_BREAK || text().empty())
193 return LeftEndSelectionModel();
194
195 scoped_ptr<base::i18n::BreakIterator> iter;
196 if (break_type == WORD_BREAK) {
197 iter.reset(new base::i18n::BreakIterator(text(),
198 base::i18n::BreakIterator::BREAK_WORD));
199 bool success = iter->Init();
200 DCHECK(success);
201 if (!success)
202 return selection;
203 }
204
205 bool character_break = break_type == CHARACTER_BREAK;
206 bool at_word_break = false;
207 SelectionModel left(selection);
208 SCRIPT_LOGATTR attributes;
209 do {
210 left = LeftSelectionModel(left);
211 size_t position = left.caret_pos();
212 // TODO(msw): Implement GetIndexOfNextGrapheme and fix word breaking logic.
213 if (left.caret_placement() == SelectionModel::TRAILING)
214 position++;
215 size_t run_index = GetRunContainingPosition(position);
216 if (run_index == runs_.size() || left.Equals(LeftEndSelectionModel()))
xji 2011/08/25 05:58:38 the changed logic actually has the same functional
msw 2011/08/26 16:26:25 I removed the word break code and rewrote this to
217 return left;
218 size_t char_index = position - runs_[run_index]->range.start();
219 attributes = runs_[run_index]->logical_attributes[char_index];
220 at_word_break = iter.get() && iter->IsWordBreakAt(position);
221 } while (!(character_break && attributes.fCharStop) && !at_word_break);
222 return left;
223 }
224
225 SelectionModel RenderTextWin::GetRightSelectionModel(
226 const SelectionModel& selection,
227 BreakType break_type) {
228 if (break_type == LINE_BREAK || text().empty())
229 return RightEndSelectionModel();
230
231 scoped_ptr<base::i18n::BreakIterator> iter;
232 if (break_type == WORD_BREAK) {
233 iter.reset(new base::i18n::BreakIterator(text(),
234 base::i18n::BreakIterator::BREAK_WORD));
235 bool success = iter->Init();
236 DCHECK(success);
237 if (!success)
238 return selection;
239 }
240
241 bool character_break = break_type == CHARACTER_BREAK;
242 bool at_word_break = false;
243 SelectionModel right(selection);
244 SCRIPT_LOGATTR attributes;
245 do {
246 right = RightSelectionModel(right);
247 size_t position = right.caret_pos();
248 // TODO(msw): Implement GetIndexOfNextGrapheme and fix word breaking logic.
249 if (right.caret_placement() == SelectionModel::TRAILING)
250 position++;
251 size_t run_index = GetRunContainingPosition(position);
xji 2011/08/25 05:58:38 ditto
msw 2011/08/26 16:26:25 See above.
252 if (run_index == runs_.size() || right.Equals(RightEndSelectionModel()))
253 return right;
254 size_t char_index = position - runs_[run_index]->range.start();
255 attributes = runs_[run_index]->logical_attributes[char_index];
256 at_word_break = iter.get() && iter->IsWordBreakAt(position);
257 } while (!(character_break && attributes.fCharStop) && !at_word_break);
258 return right;
259 }
260
261 SelectionModel RenderTextWin::LeftEndSelectionModel() {
262 if (text().empty())
263 return SelectionModel(0, 0, SelectionModel::LEADING);
264 size_t cursor = base::i18n::IsRTL() ? text().length() : 0;
265 internal::TextRun* run = runs_[visual_to_logical_[0]];
266 bool rtl = run->script_analysis.fRTL;
267 size_t caret = rtl ? run->range.end() - 1 : run->range.start();
268 SelectionModel::CaretPlacement placement =
269 rtl ? SelectionModel::TRAILING : SelectionModel::LEADING;
270 return SelectionModel(cursor, caret, placement);
271 }
272
273 SelectionModel RenderTextWin::RightEndSelectionModel() {
274 if (text().empty())
275 return SelectionModel(0, 0, SelectionModel::LEADING);
276 size_t cursor = base::i18n::IsRTL() ? 0 : text().length();
277 internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]];
278 bool rtl = run->script_analysis.fRTL;
279 size_t caret = rtl ? run->range.start() : run->range.end() - 1;
280 SelectionModel::CaretPlacement placement =
281 rtl ? SelectionModel::LEADING : SelectionModel::TRAILING;
282 return SelectionModel(cursor, caret, placement);
283 }
284
285 std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
286 ui::Range range(from, to);
287 DCHECK(ui::Range(0, text().length()).Contains(range));
288 Point display_offset(GetUpdatedDisplayOffset());
289 std::vector<Rect> bounds;
290 HRESULT hr = 0;
291
292 // Add a Rect for each run/selection intersection.
293 // TODO(msw): The bounds should probably not always be leading the range ends.
294 for (size_t i = 0; i < runs_.size(); ++i) {
295 internal::TextRun* run = runs_[visual_to_logical_[i]];
296 ui::Range intersection = run->range.Intersect(range);
297 if (intersection.IsValid()) {
298 DCHECK(!intersection.is_reversed());
299 int start_offset = 0;
300 hr = ScriptCPtoX(intersection.start() - run->range.start(),
301 false,
302 run->range.length(),
303 run->glyph_count,
304 run->logical_clusters.get(),
305 run->visible_attributes.get(),
306 run->advance_widths.get(),
307 &(run->script_analysis),
308 &start_offset);
309 DCHECK(SUCCEEDED(hr));
310 int end_offset = 0;
311 hr = ScriptCPtoX(intersection.end() - run->range.start(),
312 false,
313 run->range.length(),
314 run->glyph_count,
315 run->logical_clusters.get(),
316 run->visible_attributes.get(),
317 run->advance_widths.get(),
318 &(run->script_analysis),
319 &end_offset);
320 DCHECK(SUCCEEDED(hr));
321 if (start_offset > end_offset)
322 std::swap(start_offset, end_offset);
323 Rect rect(run->preceding_run_widths + start_offset, 0,
324 end_offset - start_offset, run->font.GetHeight());
325 // Center the rect vertically in the display area.
326 rect.Offset(0, (display_rect().height() - rect.height()) / 2);
327 rect.set_origin(ToViewPoint(rect.origin()));
328 // Union this with the last rect if they're adjacent.
329 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) {
330 rect = rect.Union(bounds.back());
331 bounds.pop_back();
332 }
333 bounds.push_back(rect);
334 }
335 }
336 return bounds;
337 }
338
339 void RenderTextWin::ItemizeLogicalText() {
340 text_is_dirty_ = false;
341 STLDeleteContainerPointers(runs_.begin(), runs_.end());
342 runs_.clear();
343 if (text().empty())
344 return;
345
346 const wchar_t* raw_text = text().c_str();
347 const int text_length = text().length();
348
349 HRESULT hr = E_OUTOFMEMORY;
350 int script_items_count = 0;
351 scoped_array<SCRIPT_ITEM> script_items;
352 for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
353 // Derive the array of Uniscribe script items from the logical text.
354 // ScriptItemize always adds a terminal array item so that the length of the
355 // last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
356 script_items.reset(new SCRIPT_ITEM[n]);
357 hr = ScriptItemize(raw_text,
358 text_length,
359 n - 1,
360 &script_control_,
361 &script_state_,
362 script_items.get(),
363 &script_items_count);
364 }
365 DCHECK(SUCCEEDED(hr));
366
367 if (script_items_count <= 0)
368 return;
369
370 // Build the list of runs, merge font/underline styles.
371 // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
372 // TODO(msw): Apply the overriding selection and composition styles.
373 StyleRanges::const_iterator style = style_ranges().begin();
374 SCRIPT_ITEM* script_item = script_items.get();
375 for (int run_break = 0; run_break < text_length;) {
376 internal::TextRun* run = new internal::TextRun();
377 run->range.set_start(run_break);
378 run->font = !style->underline ? style->font :
379 style->font.DeriveFont(0, style->font.GetStyle() | Font::UNDERLINED);
380 run->foreground = style->foreground;
381 run->strike = style->strike;
382 run->script_analysis = script_item->a;
383
384 // Find the range end and advance the structures as needed.
385 int script_item_end = (script_item + 1)->iCharPos;
386 int style_range_end = style->range.end();
387 run_break = std::min(script_item_end, style_range_end);
388 if (script_item_end <= style_range_end)
389 script_item++;
390 if (script_item_end >= style_range_end)
391 style++;
392 run->range.set_end(run_break);
393 runs_.push_back(run);
394 }
395 }
396
397 void RenderTextWin::LayoutVisualText(HDC hdc) {
398 HRESULT hr = 0;
399 std::vector<internal::TextRun*>::const_iterator run_iter;
400 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) {
401 internal::TextRun* run = *run_iter;
402 int run_length = run->range.length();
403 string16 run_string(text().substr(run->range.start(), run_length));
404 const wchar_t* run_text = run_string.c_str();
405 // Select the font desired for glyph generation
406 SelectObject(hdc, run->font.GetNativeFont());
407
408 run->logical_clusters.reset(new WORD[run_length]);
409 run->glyph_count = 0;
410 hr = E_OUTOFMEMORY;
411 // kGuess comes from: http://msdn.microsoft.com/en-us/library/dd368564.aspx
412 const int kGuess = static_cast<int>(1.5 * run_length + 16);
413 for (size_t n = kGuess; hr == E_OUTOFMEMORY && n < kMaxGlyphs; n *= 2) {
414 run->glyphs.reset(new WORD[n]);
415 run->visible_attributes.reset(new SCRIPT_VISATTR[n]);
416 hr = ScriptShape(hdc,
417 &script_cache_,
418 run_text,
419 run_length,
420 n,
421 &(run->script_analysis),
422 run->glyphs.get(),
423 run->logical_clusters.get(),
424 run->visible_attributes.get(),
425 &(run->glyph_count));
426 }
427 DCHECK(SUCCEEDED(hr));
428
429 if (run->glyph_count > 0) {
430 run->advance_widths.reset(new int[run->glyph_count]);
431 run->offsets.reset(new GOFFSET[run->glyph_count]);
432 hr = ScriptPlace(hdc,
433 &script_cache_,
434 run->glyphs.get(),
435 run->glyph_count,
436 run->visible_attributes.get(),
437 &(run->script_analysis),
438 run->advance_widths.get(),
439 run->offsets.get(),
440 &(run->abc_widths));
441 DCHECK(SUCCEEDED(hr));
442
443 // TODO(msw): The character break information can also be derived from the
444 // run's logical clusters structure, so ScriptBreak may not be needed.
445 run->logical_attributes.reset(new SCRIPT_LOGATTR[run_length]);
446 hr = ScriptBreak(run_text,
447 run_length,
448 &(run->script_analysis),
449 run->logical_attributes.get());
450 DCHECK(SUCCEEDED(hr));
451 }
452 }
453
454 if (runs_.size() > 0) {
455 // Build the array of bidirectional embedding levels.
456 scoped_array<BYTE> levels(new BYTE[runs_.size()]);
457 for (size_t i = 0; i < runs_.size(); ++i)
458 levels[i] = runs_[i]->script_analysis.s.uBidiLevel;
459
460 // Get the maps between visual and logical run indices.
461 visual_to_logical_.reset(new int[runs_.size()]);
462 logical_to_visual_.reset(new int[runs_.size()]);
463 hr = ScriptLayout(runs_.size(),
464 levels.get(),
465 visual_to_logical_.get(),
466 logical_to_visual_.get());
467 DCHECK(SUCCEEDED(hr));
468 }
469
470 // Precalculate run width information.
471 size_t preceding_run_widths = 0;
472 for (size_t i = 0; i < runs_.size(); ++i) {
473 internal::TextRun* run = runs_[visual_to_logical_[i]];
474 run->preceding_run_widths = preceding_run_widths;
475 const ABC& abc = run->abc_widths;
476 run->width = abc.abcA + abc.abcB + abc.abcC;
477 preceding_run_widths += run->width;
478 }
479 string_width_ = preceding_run_widths;
480 }
481
482 size_t RenderTextWin::GetRunContainingPosition(size_t position) const {
483 // Find the text run containing the argument position.
484 size_t run = 0;
485 for (; run < runs_.size(); ++run)
486 if (runs_[run]->range.start() <= position &&
487 runs_[run]->range.end() > position)
488 break;
489 return run;
490 }
491
492 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
493 // Find the text run containing the argument point (assumed already offset).
494 size_t run = 0;
495 for (; run < runs_.size(); ++run)
496 if (runs_[run]->preceding_run_widths <= point.x() &&
497 runs_[run]->preceding_run_widths + runs_[run]->width > point.x())
498 break;
499 return run;
500 }
501
502 SelectionModel RenderTextWin::LeftSelectionModel(
503 const SelectionModel& selection) {
504 size_t caret = selection.caret_pos();
505 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
506 size_t run_index = GetRunContainingPosition(caret);
507 DCHECK(run_index < runs_.size());
508 internal::TextRun* run = runs_[run_index];
509
510 // If the caret's associated character is in a LTR run.
511 if (!run->script_analysis.fRTL) {
512 if (caret_placement == SelectionModel::TRAILING)
513 return SelectionModel(caret, caret, SelectionModel::LEADING);
514 else if (caret > run->range.start())
515 return SelectionModel(caret - 1, caret - 1, SelectionModel::LEADING);
516 } else { // The caret's associated character is in a RTL run.
517 if (caret_placement == SelectionModel::LEADING)
518 return SelectionModel(caret + 1, caret, SelectionModel::TRAILING);
519 else if (caret < run->range.end() - 1)
520 return SelectionModel(caret + 2, caret + 1, SelectionModel::TRAILING);
521 }
522
523 // The character is at the begin of its run; go to the previous visual run.
524 size_t visual_index = logical_to_visual_[run_index];
525 if (visual_index == 0)
526 return LeftEndSelectionModel();
527 internal::TextRun* previous_run = runs_[visual_to_logical_[visual_index - 1]];
528 if (!previous_run->script_analysis.fRTL) {
529 size_t pos = previous_run->range.end() - 1;
530 return SelectionModel(pos, pos, SelectionModel::LEADING);
531 }
532 size_t pos = previous_run->range.start();
533 return SelectionModel(pos + 1, pos, SelectionModel::TRAILING);
534 }
535
536 SelectionModel RenderTextWin::RightSelectionModel(
537 const SelectionModel& selection) {
538 size_t caret = selection.caret_pos();
539 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
540 size_t run_index = GetRunContainingPosition(caret);
541 DCHECK(run_index < runs_.size());
542 internal::TextRun* run = runs_[run_index];
543
544 // If the caret's associated character is in a LTR run.
545 if (!run->script_analysis.fRTL) {
546 if (caret_placement == SelectionModel::LEADING)
547 return SelectionModel(caret + 1, caret, SelectionModel::TRAILING);
548 else if (caret < run->range.end() - 1)
549 return SelectionModel(caret + 2, caret + 1, SelectionModel::TRAILING);
550 } else { // The caret's associated character is in a RTL run.
551 if (caret_placement == SelectionModel::TRAILING)
552 return SelectionModel(caret, caret, SelectionModel::LEADING);
553 else if (caret > run->range.start())
554 return SelectionModel(caret - 1, caret - 1, SelectionModel::LEADING);
555 }
556
557 // The character is at the end of its run; go to the next visual run.
558 size_t visual_index = logical_to_visual_[run_index];
559 if (visual_index == runs_.size() - 1)
560 return RightEndSelectionModel();
561 internal::TextRun* next_run = runs_[visual_to_logical_[visual_index + 1]];
562 if (!next_run->script_analysis.fRTL) {
563 size_t pos = next_run->range.start();
564 return SelectionModel(pos + 1, pos, SelectionModel::TRAILING);
565 }
566 size_t pos = next_run->range.end() - 1;
567 return SelectionModel(pos, pos, SelectionModel::LEADING);
568 }
569
570 void RenderTextWin::DrawSelection(Canvas* canvas) {
571 std::vector<Rect> sel(
572 GetSubstringBounds(GetSelectionStart(), GetCursorPosition()));
573 SkColor color = focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
574 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
575 canvas->FillRectInt(color, i->x(), i->y(), i->width(), i->height());
576 }
577
578 void RenderTextWin::DrawVisualText(Canvas* canvas) {
579 if (text().empty())
580 return;
581
582 skia::ScopedPlatformPaint scoped_platform_paint(canvas->AsCanvasSkia());
583 HDC hdc = scoped_platform_paint.GetPlatformSurface();
584
585 // Set the background mix mode to transparent.
586 int previous_background_mode = SetBkMode(hdc, TRANSPARENT);
587
588 Point offset(ToViewPoint(Point()));
589 // TODO(msw): Establish a vertical baseline for strings of mixed font heights.
590 size_t height = default_style().font.GetHeight();
591 // Center the text vertically in the display area.
592 offset.Offset(0, (display_rect().height() - height) / 2);
593
594 HRESULT hr = 0;
595 RECT rect = display_rect().ToRECT();
596 for (size_t i = 0; i < runs_.size(); ++i) {
597 // Get the run specified by the visual-to-logical map.
598 internal::TextRun* run = runs_[visual_to_logical_[i]];
599
600 // Set the font and color.
601 SelectObject(hdc, run->font.GetNativeFont());
602 SetTextColor(hdc, skia::SkColorToCOLORREF(run->foreground));
603
604 hr = ScriptTextOut(hdc,
605 &script_cache_,
606 offset.x(),
607 offset.y(),
608 0,
609 &rect,
610 &(run->script_analysis),
611 NULL,
612 0,
613 run->glyphs.get(),
614 run->glyph_count,
615 run->advance_widths.get(),
616 NULL,
617 run->offsets.get());
618 DCHECK(SUCCEEDED(hr));
619
620 // Draw the strikethrough.
621 if (run->strike) {
622 Rect bounds(offset, Size(run->width, run->font.GetHeight()));
623 SkPaint paint;
624 paint.setAntiAlias(true);
625 paint.setStyle(SkPaint::kFill_Style);
626 paint.setColor(run->foreground);
627 paint.setStrokeWidth(kStrikeWidth);
628 canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()),
629 SkIntToScalar(bounds.bottom()),
630 SkIntToScalar(bounds.right()),
631 SkIntToScalar(bounds.y()),
632 paint);
633 }
634
635 offset.Offset(run->width, 0);
636 }
637
638 // Restore the previous background mix mode.
639 SetBkMode(hdc, previous_background_mode);
640 }
641
642 void RenderTextWin::DrawCursor(Canvas* canvas) {
643 // Paint cursor. Replace cursor is drawn as rectangle for now.
644 // TODO(msw): Draw a better cursor with a better indication of association.
645 if (cursor_visible() && focused()) {
646 Rect r(GetUpdatedCursorBounds());
647 canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height());
648 }
14 } 649 }
15 650
16 RenderText* RenderText::CreateRenderText() { 651 RenderText* RenderText::CreateRenderText() {
17 return new RenderTextWin; 652 return new RenderTextWin;
18 } 653 }
19 654
20 } // namespace gfx 655 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698