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

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

Powered by Google App Engine
This is Rietveld 408576698