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

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: Seek parity with RenderTextLinux, nix tentative word breaking, address comments. Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | 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);
xji 2011/08/26 17:59:26 DCHECK_GE()
msw 2011/08/26 20:10:43 Done.
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 if (break_type == CHARACTER_BREAK)
195 return LeftSelectionModel(selection);
196 // TODO(msw): Implement word breaking.
197 return RenderText::GetLeftSelectionModel(selection, break_type);
198 }
199
200 SelectionModel RenderTextWin::GetRightSelectionModel(
201 const SelectionModel& selection,
202 BreakType break_type) {
203 if (break_type == LINE_BREAK || text().empty())
204 return RightEndSelectionModel();
205 if (break_type == CHARACTER_BREAK)
206 return RightSelectionModel(selection);
207 // TODO(msw): Implement word breaking.
208 return RenderText::GetRightSelectionModel(selection, break_type);
209 }
210
211 SelectionModel RenderTextWin::LeftEndSelectionModel() {
212 if (text().empty())
213 return SelectionModel(0, 0, SelectionModel::LEADING);
214 size_t cursor = base::i18n::IsRTL() ? text().length() : 0;
215 internal::TextRun* run = runs_[visual_to_logical_[0]];
216 bool rtl = run->script_analysis.fRTL;
217 size_t caret = rtl ? run->range.end() - 1 : run->range.start();
218 SelectionModel::CaretPlacement placement =
219 rtl ? SelectionModel::TRAILING : SelectionModel::LEADING;
220 return SelectionModel(cursor, caret, placement);
221 }
222
223 SelectionModel RenderTextWin::RightEndSelectionModel() {
224 if (text().empty())
225 return SelectionModel(0, 0, SelectionModel::LEADING);
226 size_t cursor = base::i18n::IsRTL() ? 0 : text().length();
227 internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]];
228 bool rtl = run->script_analysis.fRTL;
229 size_t caret = rtl ? run->range.start() : run->range.end() - 1;
230 SelectionModel::CaretPlacement placement =
231 rtl ? SelectionModel::LEADING : SelectionModel::TRAILING;
232 return SelectionModel(cursor, caret, placement);
233 }
234
235 size_t RenderTextWin::GetIndexOfPreviousGrapheme(size_t position) {
236 return IndexOfAdjacentGrapheme(position, false);
237 }
238
239 std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
240 ui::Range range(from, to);
241 DCHECK(ui::Range(0, text().length()).Contains(range));
242 Point display_offset(GetUpdatedDisplayOffset());
243 std::vector<Rect> bounds;
244 HRESULT hr = 0;
245
246 // Add a Rect for each run/selection intersection.
247 // TODO(msw): The bounds should probably not always be leading the range ends.
248 for (size_t i = 0; i < runs_.size(); ++i) {
249 internal::TextRun* run = runs_[visual_to_logical_[i]];
250 ui::Range intersection = run->range.Intersect(range);
251 if (intersection.IsValid()) {
252 DCHECK(!intersection.is_reversed());
253 int start_offset = 0;
254 hr = ScriptCPtoX(intersection.start() - run->range.start(),
255 false,
256 run->range.length(),
257 run->glyph_count,
258 run->logical_clusters.get(),
259 run->visible_attributes.get(),
260 run->advance_widths.get(),
261 &(run->script_analysis),
262 &start_offset);
263 DCHECK(SUCCEEDED(hr));
264 int end_offset = 0;
265 hr = ScriptCPtoX(intersection.end() - run->range.start(),
266 false,
267 run->range.length(),
268 run->glyph_count,
269 run->logical_clusters.get(),
270 run->visible_attributes.get(),
271 run->advance_widths.get(),
272 &(run->script_analysis),
273 &end_offset);
274 DCHECK(SUCCEEDED(hr));
275 if (start_offset > end_offset)
276 std::swap(start_offset, end_offset);
277 Rect rect(run->preceding_run_widths + start_offset, 0,
278 end_offset - start_offset, run->font.GetHeight());
279 // Center the rect vertically in the display area.
280 rect.Offset(0, (display_rect().height() - rect.height()) / 2);
281 rect.set_origin(ToViewPoint(rect.origin()));
282 // Union this with the last rect if they're adjacent.
283 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) {
284 rect = rect.Union(bounds.back());
285 bounds.pop_back();
286 }
287 bounds.push_back(rect);
288 }
289 }
290 return bounds;
291 }
292
293 void RenderTextWin::ItemizeLogicalText() {
294 text_is_dirty_ = false;
295 STLDeleteContainerPointers(runs_.begin(), runs_.end());
296 runs_.clear();
297 if (text().empty())
298 return;
299
300 const wchar_t* raw_text = text().c_str();
301 const int text_length = text().length();
302
303 HRESULT hr = E_OUTOFMEMORY;
304 int script_items_count = 0;
305 scoped_array<SCRIPT_ITEM> script_items;
306 for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
307 // Derive the array of Uniscribe script items from the logical text.
308 // ScriptItemize always adds a terminal array item so that the length of the
309 // last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
310 script_items.reset(new SCRIPT_ITEM[n]);
311 hr = ScriptItemize(raw_text,
312 text_length,
313 n - 1,
314 &script_control_,
315 &script_state_,
316 script_items.get(),
317 &script_items_count);
318 }
319 DCHECK(SUCCEEDED(hr));
320
321 if (script_items_count <= 0)
322 return;
323
324 // Build the list of runs, merge font/underline styles.
325 // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
326 // TODO(msw): Apply the overriding selection and composition styles.
327 StyleRanges::const_iterator style = style_ranges().begin();
328 SCRIPT_ITEM* script_item = script_items.get();
329 for (int run_break = 0; run_break < text_length;) {
330 internal::TextRun* run = new internal::TextRun();
331 run->range.set_start(run_break);
332 run->font = !style->underline ? style->font :
333 style->font.DeriveFont(0, style->font.GetStyle() | Font::UNDERLINED);
334 run->foreground = style->foreground;
335 run->strike = style->strike;
336 run->script_analysis = script_item->a;
337
338 // Find the range end and advance the structures as needed.
339 int script_item_end = (script_item + 1)->iCharPos;
340 int style_range_end = style->range.end();
341 run_break = std::min(script_item_end, style_range_end);
342 if (script_item_end <= style_range_end)
343 script_item++;
344 if (script_item_end >= style_range_end)
345 style++;
346 run->range.set_end(run_break);
347 runs_.push_back(run);
348 }
349 }
350
351 void RenderTextWin::LayoutVisualText(HDC hdc) {
352 HRESULT hr = 0;
353 std::vector<internal::TextRun*>::const_iterator run_iter;
354 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) {
355 internal::TextRun* run = *run_iter;
356 int run_length = run->range.length();
357 string16 run_string(text().substr(run->range.start(), run_length));
358 const wchar_t* run_text = run_string.c_str();
359 // Select the font desired for glyph generation
360 SelectObject(hdc, run->font.GetNativeFont());
361
362 run->logical_clusters.reset(new WORD[run_length]);
363 run->glyph_count = 0;
364 hr = E_OUTOFMEMORY;
365 // kGuess comes from: http://msdn.microsoft.com/en-us/library/dd368564.aspx
366 const int kGuess = static_cast<int>(1.5 * run_length + 16);
367 for (size_t n = kGuess; hr == E_OUTOFMEMORY && n < kMaxGlyphs; n *= 2) {
368 run->glyphs.reset(new WORD[n]);
369 run->visible_attributes.reset(new SCRIPT_VISATTR[n]);
370 hr = ScriptShape(hdc,
371 &script_cache_,
372 run_text,
373 run_length,
374 n,
375 &(run->script_analysis),
376 run->glyphs.get(),
377 run->logical_clusters.get(),
378 run->visible_attributes.get(),
379 &(run->glyph_count));
380 }
381 DCHECK(SUCCEEDED(hr));
382
383 if (run->glyph_count > 0) {
384 run->advance_widths.reset(new int[run->glyph_count]);
385 run->offsets.reset(new GOFFSET[run->glyph_count]);
386 hr = ScriptPlace(hdc,
387 &script_cache_,
388 run->glyphs.get(),
389 run->glyph_count,
390 run->visible_attributes.get(),
391 &(run->script_analysis),
392 run->advance_widths.get(),
393 run->offsets.get(),
394 &(run->abc_widths));
395 DCHECK(SUCCEEDED(hr));
396 }
397 }
398
399 if (runs_.size() > 0) {
400 // Build the array of bidirectional embedding levels.
401 scoped_array<BYTE> levels(new BYTE[runs_.size()]);
402 for (size_t i = 0; i < runs_.size(); ++i)
403 levels[i] = runs_[i]->script_analysis.s.uBidiLevel;
404
405 // Get the maps between visual and logical run indices.
406 visual_to_logical_.reset(new int[runs_.size()]);
407 logical_to_visual_.reset(new int[runs_.size()]);
408 hr = ScriptLayout(runs_.size(),
409 levels.get(),
410 visual_to_logical_.get(),
411 logical_to_visual_.get());
412 DCHECK(SUCCEEDED(hr));
413 }
414
415 // Precalculate run width information.
416 size_t preceding_run_widths = 0;
417 for (size_t i = 0; i < runs_.size(); ++i) {
418 internal::TextRun* run = runs_[visual_to_logical_[i]];
419 run->preceding_run_widths = preceding_run_widths;
420 const ABC& abc = run->abc_widths;
421 run->width = abc.abcA + abc.abcB + abc.abcC;
422 preceding_run_widths += run->width;
423 }
424 string_width_ = preceding_run_widths;
425 }
426
427 size_t RenderTextWin::GetRunContainingPosition(size_t position) const {
428 // Find the text run containing the argument position.
429 size_t run = 0;
430 for (; run < runs_.size(); ++run)
431 if (runs_[run]->range.start() <= position &&
432 runs_[run]->range.end() > position)
433 break;
434 return run;
435 }
436
437 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
438 // Find the text run containing the argument point (assumed already offset).
439 size_t run = 0;
440 for (; run < runs_.size(); ++run)
441 if (runs_[run]->preceding_run_widths <= point.x() &&
442 runs_[run]->preceding_run_widths + runs_[run]->width > point.x())
443 break;
444 return run;
445 }
446
447 size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) const {
448 size_t run_index = GetRunContainingPosition(index);
449 internal::TextRun* run = run_index < runs_.size() ? runs_[run_index] : NULL;
450 long start = run ? run->range.start() : 0;
451 long length = run ? run->range.length() : text().length();
452 long ch = index - start;
453 WORD cluster = run ? run->logical_clusters[ch] : 0;
454
455 if (!next) {
456 do {
457 ch--;
458 } while (ch >= 0 && run && run->logical_clusters[ch] == cluster);
459 } else {
460 while (ch < length && run && run->logical_clusters[ch] == cluster)
461 ch++;
462 }
463 return std::max(static_cast<long>(std::min(ch, length) + start), 0L);
464 }
465
466 SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
467 internal::TextRun* run) const {
468 size_t caret = run->range.start();
469 size_t cursor = IndexOfAdjacentGrapheme(caret, true);
470 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
471 }
472
473 SelectionModel RenderTextWin::LastSelectionModelInsideRun(
474 internal::TextRun* run) const {
475 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false);
476 return SelectionModel(caret, caret, SelectionModel::LEADING);
477 }
478
479 SelectionModel RenderTextWin::LeftSelectionModel(
480 const SelectionModel& selection) {
481 size_t caret = selection.caret_pos();
482 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
483 size_t run_index = GetRunContainingPosition(caret);
484 DCHECK(run_index < runs_.size());
485 internal::TextRun* run = runs_[run_index];
486
487 // If the caret's associated character is in a LTR run.
488 if (!run->script_analysis.fRTL) {
489 if (caret_placement == SelectionModel::TRAILING)
490 return SelectionModel(caret, caret, SelectionModel::LEADING);
491 else if (caret > run->range.start()) {
492 caret = IndexOfAdjacentGrapheme(caret, false);
493 return SelectionModel(caret, caret, SelectionModel::LEADING);
494 }
495 } else { // The caret's associated character is in a RTL run.
496 if (caret_placement == SelectionModel::LEADING) {
497 size_t cursor = IndexOfAdjacentGrapheme(caret, true);
498 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
499 } else if (selection.selection_end() < run->range.end()) {
500 caret = IndexOfAdjacentGrapheme(caret, true);
501 size_t cursor = IndexOfAdjacentGrapheme(caret, true);
502 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
503 }
504 }
505
506 // The character is at the begin of its run; go to the previous visual run.
507 size_t visual_index = logical_to_visual_[run_index];
508 if (visual_index == 0)
509 return LeftEndSelectionModel();
510 internal::TextRun* prev = runs_[visual_to_logical_[visual_index - 1]];
511 return prev->script_analysis.fRTL ? FirstSelectionModelInsideRun(prev) :
512 LastSelectionModelInsideRun(prev);
513 }
514
515 SelectionModel RenderTextWin::RightSelectionModel(
516 const SelectionModel& selection) {
517 size_t caret = selection.caret_pos();
518 SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
519 size_t run_index = GetRunContainingPosition(caret);
520 DCHECK(run_index < runs_.size());
521 internal::TextRun* run = runs_[run_index];
522
523 // If the caret's associated character is in a LTR run.
524 if (!run->script_analysis.fRTL) {
525 if (caret_placement == SelectionModel::LEADING) {
526 size_t cursor = IndexOfAdjacentGrapheme(caret, true);
527 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
528 } else if (selection.selection_end() < run->range.end()) {
529 caret = IndexOfAdjacentGrapheme(caret, true);
530 size_t cursor = IndexOfAdjacentGrapheme(caret, true);
531 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
532 }
533 } else { // The caret's associated character is in a RTL run.
534 if (caret_placement == SelectionModel::TRAILING)
535 return SelectionModel(caret, caret, SelectionModel::LEADING);
536 else if (caret > run->range.start()) {
537 caret = IndexOfAdjacentGrapheme(caret, false);
538 return SelectionModel(caret, caret, SelectionModel::LEADING);
539 }
540 }
541
542 // The character is at the end of its run; go to the next visual run.
543 size_t visual_index = logical_to_visual_[run_index];
544 if (visual_index == runs_.size() - 1)
545 return RightEndSelectionModel();
546 internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]];
547 return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) :
548 FirstSelectionModelInsideRun(next);
549 }
550
551 void RenderTextWin::DrawSelection(Canvas* canvas) {
552 std::vector<Rect> sel(
553 GetSubstringBounds(GetSelectionStart(), GetCursorPosition()));
554 SkColor color = focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
555 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
556 canvas->FillRectInt(color, i->x(), i->y(), i->width(), i->height());
557 }
558
559 void RenderTextWin::DrawVisualText(Canvas* canvas) {
560 if (text().empty())
561 return;
562
563 skia::ScopedPlatformPaint scoped_platform_paint(canvas->AsCanvasSkia());
564 HDC hdc = scoped_platform_paint.GetPlatformSurface();
565
566 // Set the background mix mode to transparent.
567 int previous_background_mode = SetBkMode(hdc, TRANSPARENT);
568
569 Point offset(ToViewPoint(Point()));
570 // TODO(msw): Establish a vertical baseline for strings of mixed font heights.
571 size_t height = default_style().font.GetHeight();
572 // Center the text vertically in the display area.
573 offset.Offset(0, (display_rect().height() - height) / 2);
574
575 HRESULT hr = 0;
576 RECT rect = display_rect().ToRECT();
577 for (size_t i = 0; i < runs_.size(); ++i) {
578 // Get the run specified by the visual-to-logical map.
579 internal::TextRun* run = runs_[visual_to_logical_[i]];
580
581 // Set the font and color.
582 SelectObject(hdc, run->font.GetNativeFont());
583 SetTextColor(hdc, skia::SkColorToCOLORREF(run->foreground));
584
585 hr = ScriptTextOut(hdc,
586 &script_cache_,
587 offset.x(),
588 offset.y(),
589 0,
590 &rect,
591 &(run->script_analysis),
592 NULL,
593 0,
594 run->glyphs.get(),
595 run->glyph_count,
596 run->advance_widths.get(),
597 NULL,
598 run->offsets.get());
599 DCHECK(SUCCEEDED(hr));
600
601 // Draw the strikethrough.
602 if (run->strike) {
603 Rect bounds(offset, Size(run->width, run->font.GetHeight()));
604 SkPaint paint;
605 paint.setAntiAlias(true);
606 paint.setStyle(SkPaint::kFill_Style);
607 paint.setColor(run->foreground);
608 paint.setStrokeWidth(kStrikeWidth);
609 canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()),
610 SkIntToScalar(bounds.bottom()),
611 SkIntToScalar(bounds.right()),
612 SkIntToScalar(bounds.y()),
613 paint);
614 }
615
616 offset.Offset(run->width, 0);
617 }
618
619 // Restore the previous background mix mode.
620 SetBkMode(hdc, previous_background_mode);
621 }
622
623 void RenderTextWin::DrawCursor(Canvas* canvas) {
624 // Paint cursor. Replace cursor is drawn as rectangle for now.
625 // TODO(msw): Draw a better cursor with a better indication of association.
626 if (cursor_visible() && focused()) {
627 Rect r(GetUpdatedCursorBounds());
628 canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height());
629 }
14 } 630 }
15 631
16 RenderText* RenderText::CreateRenderText() { 632 RenderText* RenderText::CreateRenderText() {
17 return new RenderTextWin; 633 return new RenderTextWin;
18 } 634 }
19 635
20 } // namespace gfx 636 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698