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

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

Powered by Google App Engine
This is Rietveld 408576698