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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: ui/gfx/render_text_win.cc
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
index 9f47946d4e970599a9e4fb6c5be2dc9a0b2ab9a7..4dee3977300d1882e57460fb89564d70abdb7ae8 100644
--- a/ui/gfx/render_text_win.cc
+++ b/ui/gfx/render_text_win.cc
@@ -4,13 +4,646 @@
#include "ui/gfx/render_text_win.h"
+#include "base/i18n/break_iterator.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "skia/ext/skia_utils_win.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/canvas_skia.h"
+
+namespace {
+
+// The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes.
+// TODO(msw): Review memory use/failure? Max string length? Alternate approach?
+const int kGuessItems = 100;
+const int kMaxItems = 10000;
+
+} // namespace
+
namespace gfx {
+TextRun::TextRun()
+ : range(),
+ font(),
+ foreground(),
+ strike(false),
+ width(0),
+ preceding_run_widths(0),
+ script_analysis(),
+ glyphs(NULL),
+ logical_clusters(NULL),
+ visible_attributes(NULL),
+ glyph_count(0),
+ advance_widths(NULL),
+ offsets(NULL),
+ abc_widths(),
+ logical_attributes(NULL) {
+}
+
RenderTextWin::RenderTextWin()
- : RenderText() {
+ : RenderText(),
+ digit_substitute_(),
+ script_control_(),
+ script_state_(),
+ script_cache_(NULL),
+ runs_(),
+ string_width_(0),
+ visual_to_logical_(),
+ 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
+ HRESULT hr = 0;
+
+ // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message.
+ // TODO(msw): Use Chrome/profile locale/language settings?
+ hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_);
+ DCHECK(SUCCEEDED(hr));
+
+ hr = ScriptApplyDigitSubstitution(&digit_substitute_,
+ &script_control_,
+ &script_state_);
+ DCHECK(SUCCEEDED(hr));
+ script_control_.fMergeNeutralItems = true;
+
+ SetSelectionModel(LeftEndSelectionModel());
}
RenderTextWin::~RenderTextWin() {
+ 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.
+}
+
+void RenderTextWin::SetText(const string16& text) {
+ // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
+ RenderText::SetText(text);
+ ItemizeLogicalText();
+ LayoutVisualText(CreateCompatibleDC(NULL));
+}
+
+void RenderTextWin::ApplyStyleRange(StyleRange style_range) {
+ RenderText::ApplyStyleRange(style_range);
+ ItemizeLogicalText();
+ LayoutVisualText(CreateCompatibleDC(NULL));
+}
+
+int RenderTextWin::GetStringWidth() {
+ return string_width_;
+}
+
+void RenderTextWin::Draw(Canvas* canvas) {
+ skia::ScopedPlatformPaint scoped_platform_paint(canvas->AsCanvasSkia());
+ HDC hdc = scoped_platform_paint.GetPlatformSurface();
+ int saved_dc = SaveDC(hdc);
+ DrawSelection(canvas);
+ DrawVisualText(canvas);
+ DrawCursor(canvas);
+ RestoreDC(hdc, saved_dc);
+}
+
+SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
+ if (text().empty())
+ return SelectionModel();
+
+ // Find the run that contains the point and adjust the argument location.
+ Point p(ToTextPoint(point));
+ size_t run_index = GetRunContainingPoint(p);
+ if (run_index == runs_.size())
+ return (p.x() < 0) ? LeftEndSelectionModel() : RightEndSelectionModel();
+ TextRun* run = runs_[run_index];
+
+ int position = 0, trailing = 0;
+ HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &position,
+ &trailing);
+ DCHECK(SUCCEEDED(hr));
+ position += run->range.start();
+
+ // Show an alternate visual cursor for points trailing the end of a run.
+ if (position == static_cast<int>(run->range.end() - 1) && trailing > 0)
+ return SelectionModel(position + trailing, position,
+ SelectionModel::TRAILING);
+
+ position += trailing;
+ DCHECK(position >= 0 && position <= static_cast<int>(text().length()));
+ return SelectionModel(position, position, SelectionModel::LEADING);
+}
+
+std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
+ ui::Range range(from, to);
+ DCHECK(ui::Range(0, text().length()).Contains(range));
+ Point display_offset(GetUpdatedDisplayOffset());
+ std::vector<Rect> bounds;
+ HRESULT hr = 0;
+
+ // Add a Rect for each run/selection intersection.
+ for (size_t i = 0; i < runs_.size(); ++i) {
+ TextRun* run = runs_[visual_to_logical_[i]];
+ ui::Range intersection = run->range.Intersect(range);
+ if (intersection.IsValid()) {
+ DCHECK(!intersection.is_reversed());
+ int start_offset = 0;
+ hr = ScriptCPtoX(intersection.start() - run->range.start(),
+ false,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &start_offset);
+ DCHECK(SUCCEEDED(hr));
+ int end_offset = 0;
+ hr = ScriptCPtoX(intersection.end() - run->range.start(),
+ false,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &end_offset);
+ DCHECK(SUCCEEDED(hr));
+ if (start_offset > end_offset)
+ std::swap(start_offset, end_offset);
+ Rect rect(run->preceding_run_widths + start_offset, 0,
+ end_offset - start_offset, run->font.GetHeight());
+ // Center the rect vertically in the display area.
+ rect.Offset(0, (display_rect().height() - rect.height()) / 2);
+ rect.set_origin(ToViewPoint(rect.origin()));
+ // Union this with the last rect if they're adjacent.
+ if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) {
+ rect = rect.Union(bounds.back());
+ bounds.pop_back();
+ }
+ bounds.push_back(rect);
+ }
+ }
+ return bounds;
+}
+
+Rect RenderTextWin::GetCursorBounds(const SelectionModel& selection,
+ bool insert_mode) {
+ // Highlight the logical cursor (selection end) when not in insert mode.
+ size_t pos = insert_mode ? selection.caret_pos() : selection.selection_end();
+ size_t run_index = GetRunContainingPosition(pos);
+ TextRun* run = (run_index == runs_.size()) ? NULL : runs_[run_index];
+
+ int start_x = 0, end_x = 0;
+ if (run) {
+ HRESULT hr = 0;
+ hr = ScriptCPtoX(pos - run->range.start(),
+ false,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &start_x);
+ DCHECK(SUCCEEDED(hr));
+ hr = ScriptCPtoX(pos - run->range.start(),
+ true,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &end_x);
+ DCHECK(SUCCEEDED(hr));
+ }
+ // TODO(msw): Use the last visual run's font instead of the default font?
+ int height = run ? run->font.GetHeight() : default_style().font.GetHeight();
+ Rect rect(std::min(start_x, end_x), 0, std::abs(end_x - start_x), height);
+ size_t text_end_offset = base::i18n::IsRTL() ? 0 : string_width_;
+ // Offset to the run start and center the rect vertically in the display area.
+ rect.Offset((run ? run->preceding_run_widths : text_end_offset),
+ (display_rect().height() - rect.height()) / 2);
+ // Adjust for leading/trailing in insert mode.
+ if (insert_mode) {
+ bool leading = (selection.caret_placement() == SelectionModel::LEADING);
+ // Adjust the x value for right-side placement.
+ if (run && (run->script_analysis.fRTL == leading))
+ rect.set_x(rect.right());
+ rect.set_width(0);
+ }
+ rect.set_origin(ToViewPoint(rect.origin()));
+ return rect;
+}
+
+SelectionModel RenderTextWin::GetLeftSelectionModel(
+ const SelectionModel& selection,
+ BreakType break_type) {
+ if (break_type == LINE_BREAK || text().empty())
+ return LeftEndSelectionModel();
+
+ scoped_ptr<base::i18n::BreakIterator> iter;
+ if (break_type == WORD_BREAK) {
+ iter.reset(new base::i18n::BreakIterator(text(),
+ base::i18n::BreakIterator::BREAK_WORD));
+ bool success = iter->Init();
+ DCHECK(success);
+ if (!success)
+ return selection;
+ }
+
+ bool character_break = (break_type == CHARACTER_BREAK);
+ bool at_word_break = false;
+ SelectionModel left(selection);
+ SCRIPT_LOGATTR attributes;
+ do {
+ left = LeftSelectionModel(left);
+ size_t run_index = GetRunContainingPosition(left.selection_end());
+ if (run_index == runs_.size() || left.Equals(LeftEndSelectionModel()))
+ return left;
+ size_t char_index = left.selection_end() - runs_[run_index]->range.start();
+ attributes = runs_[run_index]->logical_attributes[char_index];
+ bool advancing = runs_[run_index]->script_analysis.fRTL;
+ at_word_break = iter.get() && iter->IsWordBreakAt(left.selection_end());
+ } while (!(character_break && attributes.fCharStop) && !at_word_break);
+ return left;
+}
+
+SelectionModel RenderTextWin::GetRightSelectionModel(
+ const SelectionModel& selection,
+ BreakType break_type) {
+ if (break_type == LINE_BREAK || text().empty())
+ return RightEndSelectionModel();
+
+ scoped_ptr<base::i18n::BreakIterator> iter;
+ if (break_type == WORD_BREAK) {
+ iter.reset(new base::i18n::BreakIterator(text(),
+ base::i18n::BreakIterator::BREAK_WORD));
+ bool success = iter->Init();
+ DCHECK(success);
+ if (!success)
+ return selection;
+ }
+
+ 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.
+ bool at_word_break = false;
+ SelectionModel right(selection);
+ SCRIPT_LOGATTR attributes;
+ do {
+ right = RightSelectionModel(right);
+ size_t run_index = GetRunContainingPosition(right.selection_end());
+ if (run_index == runs_.size() || right.Equals(RightEndSelectionModel()))
+ return right;
+ size_t char_index = right.selection_end() - runs_[run_index]->range.start();
+ attributes = runs_[run_index]->logical_attributes[char_index];
+ bool advancing = !runs_[run_index]->script_analysis.fRTL;
+ at_word_break = iter.get() && iter->IsWordBreakAt(right.selection_end());
+ } while (!(character_break && attributes.fCharStop) && !at_word_break);
+ return right;
+}
+
+void RenderTextWin::ItemizeLogicalText() {
+ text_is_dirty_ = false;
+ STLDeleteContainerPointers(runs_.begin(), runs_.end());
+ runs_.clear();
+ if (text().empty())
+ return;
+
+ const wchar_t* raw_text = text().c_str();
+ const int text_length = text().length();
+
+ HRESULT hr = E_OUTOFMEMORY;
+ int script_items_count = 0;
+ scoped_array<SCRIPT_ITEM> script_items;
+ for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
+ // Derive the array of Uniscribe script items from the logical text.
+ // ScriptItemize always adds a terminal array item so that the length of the
+ // last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
+ script_items.reset(new SCRIPT_ITEM[n]);
+ hr = ScriptItemize(raw_text,
+ text_length,
+ n - 1,
+ &script_control_,
+ &script_state_,
+ script_items.get(),
+ &script_items_count);
+ }
+ DCHECK(SUCCEEDED(hr));
+
+ if (script_items_count <= 0)
+ return;
+
+ // Build the list of runs, merge font/underline styles.
+ // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
+ // TODO(msw): Apply the overriding selection and composition styles.
+ StyleRanges::const_iterator style = style_ranges().begin();
+ SCRIPT_ITEM* script_item = script_items.get();
+ for (int run_break = 0; run_break < text_length;) {
+ TextRun* run = new TextRun();
+ run->range.set_start(run_break);
+ run->font = !style->underline ? style->font :
+ style->font.DeriveFont(0, style->font.GetStyle() | Font::UNDERLINED);
+ run->foreground = style->foreground;
+ run->strike = style->strike;
+ run->script_analysis = script_item->a;
+
+ // Find the range end and advance the structures as needed.
+ int script_item_end = (script_item + 1)->iCharPos;
+ int style_range_end = style->range.end();
+ run_break = std::min(script_item_end, style_range_end);
+ if (script_item_end <= style_range_end)
+ script_item++;
+ if (script_item_end >= style_range_end)
+ style++;
+ run->range.set_end(run_break);
+ runs_.push_back(run);
+ }
+}
+
+void RenderTextWin::LayoutVisualText(HDC hdc) {
+ HRESULT hr = 0;
+ std::vector<TextRun*>::const_iterator run_iter;
+ for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) {
+ TextRun* run = *run_iter;
+ int run_length = run->range.length();
+ string16 run_string(text().substr(run->range.start(), run_length));
+ const wchar_t* run_text = run_string.c_str();
+ // Select the font desired for glyph generation
+ SelectObject(hdc, run->font.GetNativeFont());
+
+ const int max_glyphs = static_cast<int>(1.5 * run_length + 16);
+ run->glyphs.reset(new WORD[max_glyphs]);
+ run->logical_clusters.reset(new WORD[run_length]);
+ run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
+ run->glyph_count = 0;
+ hr = ScriptShape(hdc,
+ &script_cache_,
+ run_text,
+ run_length,
+ max_glyphs,
+ &(run->script_analysis),
+ run->glyphs.get(),
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ &(run->glyph_count));
+ DCHECK(SUCCEEDED(hr));
+
+ if (run->glyph_count > 0) {
+ run->advance_widths.reset(new int[run->glyph_count]);
+ run->offsets.reset(new GOFFSET[run->glyph_count]);
+ hr = ScriptPlace(hdc,
+ &script_cache_,
+ run->glyphs.get(),
+ run->glyph_count,
+ run->visible_attributes.get(),
+ &(run->script_analysis),
+ run->advance_widths.get(),
+ run->offsets.get(),
+ &(run->abc_widths));
+ DCHECK(SUCCEEDED(hr));
+
+ run->logical_attributes.reset(new SCRIPT_LOGATTR[run_length]);
+ hr = ScriptBreak(run_text,
+ run_length,
+ &(run->script_analysis),
+ run->logical_attributes.get());
+ DCHECK(SUCCEEDED(hr));
+ }
+ }
+
+ if (runs_.size() > 0) {
+ // Build the array of bidirectional embedding levels.
+ scoped_array<BYTE> levels(new BYTE[runs_.size()]);
+ for (size_t i = 0; i < runs_.size(); ++i)
+ levels[i] = runs_[i]->script_analysis.s.uBidiLevel;
+
+ // Get the maps between visual and logical run indices.
+ visual_to_logical_.reset(new int[runs_.size()]);
+ logical_to_visual_.reset(new int[runs_.size()]);
+ hr = ScriptLayout(runs_.size(),
+ levels.get(),
+ visual_to_logical_.get(),
+ logical_to_visual_.get());
+ DCHECK(SUCCEEDED(hr));
+ }
+
+ // Precalculate run width information.
+ size_t preceding_run_widths = 0;
+ for (size_t i = 0; i < runs_.size(); ++i) {
+ TextRun* run = runs_[visual_to_logical_[i]];
+ run->preceding_run_widths = preceding_run_widths;
+ const ABC& abc = run->abc_widths;
+ run->width = abc.abcA + abc.abcB + abc.abcC;
+ preceding_run_widths += run->width;
+ }
+ string_width_ = preceding_run_widths;
+}
+
+size_t RenderTextWin::GetRunContainingPosition(size_t position) const {
+ // Find the text run containing the argument position.
+ size_t run = 0;
+ for (; run < runs_.size(); ++run)
+ if (runs_[run]->range.start() <= position &&
+ runs_[run]->range.end() > position)
+ break;
+ return run;
+}
+
+size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
+ // Find the text run containing the argument point (assumed already offset).
+ size_t run = 0;
+ for (; run < runs_.size(); ++run)
+ if (runs_[run]->preceding_run_widths <= point.x() &&
+ runs_[run]->preceding_run_widths + runs_[run]->width > point.x())
+ break;
+ return run;
+}
+
+SelectionModel RenderTextWin::LeftSelectionModel(
+ const SelectionModel& selection) const {
+ size_t caret = selection.caret_pos();
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
+ size_t run_index = GetRunContainingPosition(caret);
+ DCHECK(run_index < runs_.size());
+ TextRun* run = runs_[run_index];
+
+ // If the caret's associated character is in a LTR run.
+ if (!run->script_analysis.fRTL) {
+ if (caret_placement == SelectionModel::TRAILING)
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ else if (caret > run->range.start())
+ return SelectionModel(caret - 1, caret - 1, SelectionModel::LEADING);
+ } else { // The caret's associated character is in a RTL run.
+ if (caret_placement == SelectionModel::LEADING)
+ return SelectionModel(caret + 1, caret, SelectionModel::TRAILING);
+ else if (caret < run->range.end() - 1)
+ return SelectionModel(caret + 2, caret + 1, SelectionModel::TRAILING);
+ }
+
+ // The character is at the end of its run; advance to the next visual run.
+ size_t visual_index = logical_to_visual_[run_index];
+ if (visual_index == 0)
+ return LeftEndSelectionModel();
+ TextRun* next_run = runs_[visual_to_logical_[visual_index - 1]];
+ if (!next_run->script_analysis.fRTL) {
+ size_t pos = next_run->range.end() - 1;
+ return SelectionModel(pos, pos, SelectionModel::LEADING);
+ }
+ size_t pos = next_run->range.start();
+ return SelectionModel(pos + 1, pos, SelectionModel::TRAILING);
+}
+
+SelectionModel RenderTextWin::RightSelectionModel(
+ const SelectionModel& selection) const {
+ size_t caret = selection.caret_pos();
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
+ size_t run_index = GetRunContainingPosition(caret);
+ DCHECK(run_index < runs_.size());
+ TextRun* run = runs_[run_index];
+
+ // If the caret's associated character is in a LTR run.
+ if (!run->script_analysis.fRTL) {
+ if (caret_placement == SelectionModel::LEADING)
+ return SelectionModel(caret + 1, caret, SelectionModel::TRAILING);
+ else if (caret < run->range.end() - 1)
+ return SelectionModel(caret + 2, caret + 1, SelectionModel::TRAILING);
+ } else { // The caret's associated character is in a RTL run.
+ if (caret_placement == SelectionModel::TRAILING)
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ else if (caret > run->range.start())
+ return SelectionModel(caret - 1, caret - 1, SelectionModel::LEADING);
+ }
+
+ // The character is at the end of its run; advance to the next visual run.
+ size_t visual_index = logical_to_visual_[run_index];
+ if (visual_index == runs_.size() - 1)
+ return RightEndSelectionModel();
+ TextRun* next_run = runs_[visual_to_logical_[visual_index + 1]];
+ if (!next_run->script_analysis.fRTL) {
+ size_t pos = next_run->range.start();
+ return SelectionModel(pos + 1, pos, SelectionModel::TRAILING);
+ }
+ size_t pos = next_run->range.end() - 1;
+ return SelectionModel(pos, pos, SelectionModel::LEADING);
+}
+
+SelectionModel RenderTextWin::LeftEndSelectionModel() const {
+ if (text().empty())
+ return SelectionModel(0, 0, SelectionModel::LEADING);
+ size_t cursor = base::i18n::IsRTL() ? text().length() : 0;
+ TextRun* run = runs_[visual_to_logical_[0]];
+ bool rtl = run->script_analysis.fRTL;
+ size_t caret = rtl ? run->range.end() - 1 : run->range.start();
+ SelectionModel::CaretPlacement placement =
+ rtl ? SelectionModel::TRAILING : SelectionModel::LEADING;
+ return SelectionModel(cursor, caret, placement);
+}
+
+SelectionModel RenderTextWin::RightEndSelectionModel() const {
+ if (text().empty())
+ return SelectionModel(0, 0, SelectionModel::LEADING);
+ size_t cursor = base::i18n::IsRTL() ? 0 : text().length();
+ TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]];
+ bool rtl = run->script_analysis.fRTL;
+ size_t caret = rtl ? run->range.start() : run->range.end() - 1;
+ SelectionModel::CaretPlacement placement =
+ rtl ? SelectionModel::LEADING : SelectionModel::TRAILING;
+ return SelectionModel(cursor, caret, placement);
+}
+
+void RenderTextWin::DrawSelection(Canvas* canvas) {
+ std::vector<Rect> sel(
+ GetSubstringBounds(GetSelectionStart(), GetCursorPosition()));
+ SkColor color = focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
+ for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
+ canvas->FillRectInt(color, i->x(), i->y(), i->width(), i->height());
+}
+
+void RenderTextWin::DrawVisualText(Canvas* canvas) {
+ if (text().empty())
+ return;
+
+ skia::ScopedPlatformPaint scoped_platform_paint(canvas->AsCanvasSkia());
+ HDC hdc = scoped_platform_paint.GetPlatformSurface();
+
+ // Set the background mix mode to transparent.
+ int previous_background_mode = SetBkMode(hdc, TRANSPARENT);
+
+ Point offset(ToViewPoint(Point()));
+ // TODO(msw): Establish a vertical baseline for strings of mixed font heights.
+ size_t height = default_style().font.GetHeight();
+ // Center the text vertically in the display area.
+ offset.Offset(0, (display_rect().height() - height) / 2);
+
+ HRESULT hr = 0;
+ RECT rect = display_rect().ToRECT();
+ for (size_t i = 0; i < runs_.size(); ++i) {
+ // Get the run specified by the visual-to-logical map.
+ TextRun* run = runs_[visual_to_logical_[i]];
+
+ // Set the font and color.
+ SelectObject(hdc, run->font.GetNativeFont());
+ SetTextColor(hdc, skia::SkColorToCOLORREF(run->foreground));
+
+ hr = ScriptTextOut(hdc,
+ &script_cache_,
+ offset.x(),
+ offset.y(),
+ 0,
+ &rect,
+ &(run->script_analysis),
+ NULL,
+ 0,
+ run->glyphs.get(),
+ run->glyph_count,
+ run->advance_widths.get(),
+ NULL,
+ run->offsets.get());
+ DCHECK(SUCCEEDED(hr));
+
+ // Draw the strikethrough.
+ if (run->strike) {
+ Rect bounds(offset, Size(run->width, run->font.GetHeight()));
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(run->foreground);
+ paint.setStrokeWidth(kStrikeWidth);
+ canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()),
+ SkIntToScalar(bounds.bottom()),
+ SkIntToScalar(bounds.right()),
+ SkIntToScalar(bounds.y()),
+ paint);
+ }
+
+ offset.Offset(run->width, 0);
+ }
+
+ // Restore the previous background mix mode.
+ SetBkMode(hdc, previous_background_mode);
+}
+
+void RenderTextWin::DrawCursor(Canvas* canvas) {
+ // Paint cursor. Replace cursor is drawn as rectangle for now.
+ // TODO(msw): Draw a better cursor with a better indication of association.
+ if (cursor_visible() && focused()) {
+ Rect r(GetUpdatedCursorBounds());
+ canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height());
+ }
+}
+
+Point RenderTextWin::ToTextPoint(const Point& point) {
+ Point p(point.Subtract(display_rect().origin()));
+ p.Subtract(GetUpdatedDisplayOffset());
+ if (base::i18n::IsRTL())
+ p.Offset(GetStringWidth() - display_rect().width() + 1, 0);
+ return p;
+}
+
+Point RenderTextWin::ToViewPoint(const Point& point) {
+ Point p(point.Add(display_rect().origin()));
+ p.Add(GetUpdatedDisplayOffset());
+ if (base::i18n::IsRTL())
+ p.Offset(display_rect().width() - GetStringWidth() - 1, 0);
+ return p;
}
RenderText* RenderText::CreateRenderText() {

Powered by Google App Engine
This is Rietveld 408576698