Draw text via Skia in RenderTextLinux.

Refactors some common Skia drawing code into RenderText internal
class SkiaTextRenderer. Rewrite RenderTextLinux::DrawVisualText()
to use SkiaTextRenderer and refactor RenderTextWin::DrawVisualText()
to also use SkiaTextRenderer, to re-use code.

BUG=103648
TEST=Run Linux views_examples_exe --use-pure-views. In the textfield
example tab, the text field should draw its text correctly.

Review URL: http://codereview.chromium.org/8725002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113706 0039d316-1c4b-4281-b951-d872f2087c98
parent de09a216
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/i18n/break_iterator.h" #include "base/i18n/break_iterator.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "ui/gfx/canvas.h" #include "ui/gfx/canvas.h"
#include "ui/gfx/canvas_skia.h" #include "ui/gfx/canvas_skia.h"
#include "unicode/uchar.h" #include "unicode/uchar.h"
...@@ -79,6 +80,79 @@ void ApplyStyleRangeImpl(gfx::StyleRanges* style_ranges, ...@@ -79,6 +80,79 @@ void ApplyStyleRangeImpl(gfx::StyleRanges* style_ranges,
namespace gfx { namespace gfx {
namespace internal {
SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas)
: canvas_skia_(canvas->GetSkCanvas()) {
DCHECK(canvas_skia_);
paint_.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint_.setStyle(SkPaint::kFill_Style);
paint_.setAntiAlias(true);
paint_.setSubpixelText(true);
paint_.setLCDRenderText(true);
}
SkiaTextRenderer::~SkiaTextRenderer() {
}
void SkiaTextRenderer::SetFont(const gfx::Font& font) {
SkAutoTUnref<SkTypeface> typeface(
SkTypeface::CreateFromName(font.GetFontName().c_str(),
SkTypeface::kNormal));
if (typeface.get()) {
// |paint_| adds its own ref. So don't |release()| it from the ref ptr here.
paint_.setTypeface(typeface.get());
}
paint_.setTextSize(font.GetFontSize());
}
void SkiaTextRenderer::SetForegroundColor(SkColor foreground) {
paint_.setColor(foreground);
}
void SkiaTextRenderer::DrawPosText(const SkPoint* pos,
const uint16* glyphs,
size_t glyph_count) {
size_t byte_length = glyph_count * sizeof(glyphs[0]);
canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_);
}
// Draw underline and strike through text decorations.
// Based on |SkCanvas::DrawTextDecorations()| and constants from:
// third_party/skia/src/core/SkTextFormatParams.h
void SkiaTextRenderer::DrawDecorations(int x, int y, int width,
bool underline, bool strike) {
// Fraction of the text size to lower a strike through below the baseline.
const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21);
// Fraction of the text size to lower an underline below the baseline.
const SkScalar kUnderlineOffset = (SK_Scalar1 / 9);
// Fraction of the text size to use for a strike through or under-line.
const SkScalar kLineThickness = (SK_Scalar1 / 18);
SkScalar text_size = paint_.getTextSize();
SkScalar height = SkScalarMul(text_size, kLineThickness);
SkRect r;
r.fLeft = x;
r.fRight = x + width;
if (underline) {
SkScalar offset = SkScalarMulAdd(text_size, kUnderlineOffset, y);
r.fTop = offset;
r.fBottom = offset + height;
canvas_skia_->drawRect(r, paint_);
}
if (strike) {
SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
r.fTop = offset;
r.fBottom = offset + height;
canvas_skia_->drawRect(r, paint_);
}
}
} // namespace internal
StyleRange::StyleRange() StyleRange::StyleRange()
: font(), : font(),
foreground(SK_ColorBLACK), foreground(SK_ColorBLACK),
...@@ -555,6 +629,18 @@ Point RenderText::ToViewPoint(const Point& point) { ...@@ -555,6 +629,18 @@ Point RenderText::ToViewPoint(const Point& point) {
return p; return p;
} }
Point RenderText::GetOriginForSkiaDrawing() {
Point origin(ToViewPoint(Point()));
// TODO(msw): Establish a vertical baseline for strings of mixed font heights.
const Font& font = default_style().font;
size_t height = font.GetHeight();
// Center the text vertically in the display area.
origin.Offset(0, (display_rect().height() - height) / 2);
// Offset by the font size to account for Skia expecting y to be the bottom.
origin.Offset(0, font.GetFontSize());
return origin;
}
void RenderText::MoveCursorTo(size_t position, bool select) { void RenderText::MoveCursorTo(size_t position, bool select) {
size_t cursor = std::min(position, text().length()); size_t cursor = std::min(position, text().length());
size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); size_t caret_pos = GetIndexOfPreviousGrapheme(cursor);
......
...@@ -13,14 +13,47 @@ ...@@ -13,14 +13,47 @@
#include "base/i18n/rtl.h" #include "base/i18n/rtl.h"
#include "base/string16.h" #include "base/string16.h"
#include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "ui/base/range/range.h" #include "ui/base/range/range.h"
#include "ui/gfx/font.h" #include "ui/gfx/font.h"
#include "ui/gfx/point.h" #include "ui/gfx/point.h"
#include "ui/gfx/rect.h" #include "ui/gfx/rect.h"
#include "ui/gfx/selection_model.h" #include "ui/gfx/selection_model.h"
class SkCanvas;
struct SkPoint;
namespace gfx { namespace gfx {
class Canvas;
class RenderTextTest;
namespace internal {
// Internal helper class used by derived classes to draw text through Skia.
class SkiaTextRenderer {
public:
explicit SkiaTextRenderer(Canvas* canvas);
~SkiaTextRenderer();
void SetFont(const gfx::Font& font);
void SetForegroundColor(SkColor foreground);
void DrawSelection(const std::vector<Rect>& selection, SkColor color);
void DrawPosText(const SkPoint* pos,
const uint16* glyphs,
size_t glyph_count);
void DrawDecorations(int x, int y, int width, bool underline, bool strike);
void DrawCursor(const gfx::Rect& bounds);
private:
SkCanvas* canvas_skia_;
SkPaint paint_;
DISALLOW_COPY_AND_ASSIGN(SkiaTextRenderer);
};
} // namespace internal
// Color settings for text, backgrounds and cursor. // Color settings for text, backgrounds and cursor.
// These are tentative, and should be derived from theme, system // These are tentative, and should be derived from theme, system
// settings and current settings. // settings and current settings.
...@@ -31,9 +64,6 @@ const SkColor kFocusedSelectionColor = SkColorSetRGB(30, 144, 255); ...@@ -31,9 +64,6 @@ const SkColor kFocusedSelectionColor = SkColorSetRGB(30, 144, 255);
const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY; const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY;
const SkColor kCursorColor = SK_ColorBLACK; const SkColor kCursorColor = SK_ColorBLACK;
class Canvas;
class RenderTextTest;
// A visual style applicable to a range of text. // A visual style applicable to a range of text.
struct UI_EXPORT StyleRange { struct UI_EXPORT StyleRange {
StyleRange(); StyleRange();
...@@ -248,6 +278,9 @@ class UI_EXPORT RenderText { ...@@ -248,6 +278,9 @@ class UI_EXPORT RenderText {
Point ToTextPoint(const Point& point); Point ToTextPoint(const Point& point);
Point ToViewPoint(const Point& point); Point ToViewPoint(const Point& point);
// Returns the origin point for drawing text via Skia.
Point GetOriginForSkiaDrawing();
private: private:
friend class RenderTextTest; friend class RenderTextTest;
......
...@@ -6,10 +6,12 @@ ...@@ -6,10 +6,12 @@
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
#include <algorithm> #include <algorithm>
#include <vector>
#include "base/i18n/break_iterator.h" #include "base/i18n/break_iterator.h"
#include "base/logging.h" #include "base/logging.h"
#include "ui/gfx/canvas_skia.h" #include "ui/gfx/canvas_skia.h"
#include "ui/gfx/font.h"
#include "ui/gfx/pango_util.h" #include "ui/gfx/pango_util.h"
#include "unicode/uchar.h" #include "unicode/uchar.h"
#include "unicode/ustring.h" #include "unicode/ustring.h"
...@@ -22,6 +24,18 @@ int ConvertColorFrom8BitTo16Bit(int c) { ...@@ -22,6 +24,18 @@ int ConvertColorFrom8BitTo16Bit(int c) {
return (c << 8) + c; return (c << 8) + c;
} }
// Returns whether the given Pango item is Left to Right.
bool IsRunLTR(const PangoItem* item) {
return (item->analysis.level & 1) == 0;
}
// Checks whether |range| contains |index|. This is not the same as calling
// |range.Contains(ui::Range(index))| - as that would return true when
// |index| == |range.end()|.
bool IndexInRange(const ui::Range& range, size_t index) {
return index >= range.start() && index < range.end();
}
} // namespace } // namespace
// TODO(xji): index saved in upper layer is utf16 index. Pango uses utf8 index. // TODO(xji): index saved in upper layer is utf16 index. Pango uses utf8 index.
...@@ -154,7 +168,7 @@ SelectionModel RenderTextLinux::LeftEndSelectionModel() { ...@@ -154,7 +168,7 @@ SelectionModel RenderTextLinux::LeftEndSelectionModel() {
PangoLayoutRun* first_visual_run = PangoLayoutRun* first_visual_run =
reinterpret_cast<PangoLayoutRun*>(current_line_->runs->data); reinterpret_cast<PangoLayoutRun*>(current_line_->runs->data);
PangoItem* item = first_visual_run->item; PangoItem* item = first_visual_run->item;
if (item->analysis.level % 2 == 0) { // LTR. if (IsRunLTR(item)) {
size_t caret = Utf8IndexToUtf16Index(item->offset); size_t caret = Utf8IndexToUtf16Index(item->offset);
return SelectionModel(text().length(), caret, SelectionModel::LEADING); return SelectionModel(text().length(), caret, SelectionModel::LEADING);
} else { // RTL. } else { // RTL.
...@@ -172,7 +186,7 @@ SelectionModel RenderTextLinux::RightEndSelectionModel() { ...@@ -172,7 +186,7 @@ SelectionModel RenderTextLinux::RightEndSelectionModel() {
PangoLayoutRun* last_visual_run = GetLastRun(); PangoLayoutRun* last_visual_run = GetLastRun();
if (last_visual_run) { if (last_visual_run) {
PangoItem* item = last_visual_run->item; PangoItem* item = last_visual_run->item;
if (item->analysis.level % 2 == 0) { // LTR. if (IsRunLTR(item)) {
size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length, size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length,
false); false);
return SelectionModel(text().length(), caret, SelectionModel::TRAILING); return SelectionModel(text().length(), caret, SelectionModel::TRAILING);
...@@ -260,27 +274,109 @@ void RenderTextLinux::EnsureLayout() { ...@@ -260,27 +274,109 @@ void RenderTextLinux::EnsureLayout() {
} }
void RenderTextLinux::DrawVisualText(Canvas* canvas) { void RenderTextLinux::DrawVisualText(Canvas* canvas) {
Rect bounds(display_rect()); DCHECK(layout_);
Point offset(GetOriginForSkiaDrawing());
SkScalar x = SkIntToScalar(offset.x());
SkScalar y = SkIntToScalar(offset.y());
std::vector<SkPoint> pos;
std::vector<uint16> glyphs;
StyleRanges styles(style_ranges());
ApplyCompositionAndSelectionStyles(&styles);
// Pre-calculate UTF8 indices from UTF16 indices.
// TODO(asvitkine): Can we cache these?
std::vector<ui::Range> style_ranges_utf8;
style_ranges_utf8.reserve(styles.size());
size_t start_index = 0;
for (size_t i = 0; i < styles.size(); ++i) {
size_t end_index = Utf16IndexToUtf8Index(styles[i].range.end());
style_ranges_utf8.push_back(ui::Range(start_index, end_index));
start_index = end_index;
}
// Clip the canvas to the text display area. internal::SkiaTextRenderer renderer(canvas);
SkCanvas* canvas_skia = canvas->GetSkCanvas(); for (GSList* it = current_line_->runs; it; it = it->next) {
PangoLayoutRun* run = reinterpret_cast<PangoLayoutRun*>(it->data);
int glyph_count = run->glyphs->num_glyphs;
if (glyph_count == 0)
continue;
skia::ScopedPlatformPaint scoped_platform_paint(canvas_skia); size_t run_start = run->item->offset;
cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); size_t first_glyph_byte_index = run_start + run->glyphs->log_clusters[0];
cairo_save(cr); size_t style_increment = IsRunLTR(run->item) ? 1 : -1;
cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height());
cairo_clip(cr); // Find the initial style for this run.
// TODO(asvitkine): Can we avoid looping here, e.g. by caching this per run?
int style = -1;
for (size_t i = 0; i < style_ranges_utf8.size(); ++i) {
if (IndexInRange(style_ranges_utf8[i], first_glyph_byte_index)) {
style = i;
break;
}
}
DCHECK_GE(style, 0);
PangoFontDescription* native_font =
pango_font_describe(run->item->analysis.font);
renderer.SetFont(gfx::Font(native_font));
pango_font_description_free(native_font);
SkScalar glyph_x = x;
SkScalar start_x = x;
int start = 0;
glyphs.resize(glyph_count);
pos.resize(glyph_count);
for (int i = 0; i < glyph_count; ++i) {
const PangoGlyphInfo& glyph = run->glyphs->glyphs[i];
glyphs[i] = static_cast<uint16>(glyph.glyph);
pos[i].set(glyph_x + PANGO_PIXELS(glyph.geometry.x_offset),
y + PANGO_PIXELS(glyph.geometry.y_offset));
glyph_x += PANGO_PIXELS(glyph.geometry.width);
// If this glyph is beyond the current style, draw the glyphs so far and
// advance to the next style.
size_t glyph_byte_index = run_start + run->glyphs->log_clusters[i];
DCHECK_GE(style, 0);
DCHECK_LT(style, static_cast<int>(styles.size()));
if (!IndexInRange(style_ranges_utf8[style], glyph_byte_index)) {
// TODO(asvitkine): For cases like "fi", where "fi" is a single glyph
// but can span multiple styles, Pango splits the
// styles evenly over the glyph. We can do this too by
// clipping and drawing the glyph several times.
renderer.SetForegroundColor(styles[style].foreground);
renderer.DrawPosText(&pos[start], &glyphs[start], i - start);
if (styles[style].underline || styles[style].strike) {
renderer.DrawDecorations(start_x, y, glyph_x - start_x,
styles[style].underline,
styles[style].strike);
}
start = i;
start_x = glyph_x;
// Loop to find the next style, in case the glyph spans multiple styles.
do {
style += style_increment;
} while (style >= 0 && style < static_cast<int>(styles.size()) &&
!IndexInRange(style_ranges_utf8[style], glyph_byte_index));
}
}
int text_width, text_height; // Draw the remaining glyphs.
pango_layout_get_pixel_size(layout_, &text_width, &text_height); renderer.SetForegroundColor(styles[style].foreground);
Point offset(ToViewPoint(Point())); renderer.DrawPosText(&pos[start], &glyphs[start], glyph_count - start);
// Vertically centered. if (styles[style].underline || styles[style].strike) {
int text_y = offset.y() + ((bounds.height() - text_height) / 2); renderer.DrawDecorations(start_x, y, glyph_x - start_x,
// TODO(xji): need to use SkCanvas->drawPosText() for gpu acceleration. styles[style].underline,
cairo_move_to(cr, offset.x(), text_y); styles[style].strike);
pango_cairo_show_layout(cr, layout_); }
cairo_restore(cr); x = glyph_x;
}
} }
size_t RenderTextLinux::IndexOfAdjacentGrapheme(size_t index, bool next) { size_t RenderTextLinux::IndexOfAdjacentGrapheme(size_t index, bool next) {
...@@ -382,7 +478,7 @@ SelectionModel RenderTextLinux::LeftSelectionModel( ...@@ -382,7 +478,7 @@ SelectionModel RenderTextLinux::LeftSelectionModel(
size_t run_start = Utf8IndexToUtf16Index(item->offset); size_t run_start = Utf8IndexToUtf16Index(item->offset);
size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length); size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length);
if (item->analysis.level % 2 == 0) { // LTR run. if (IsRunLTR(item)) {
if (caret_placement == SelectionModel::TRAILING) if (caret_placement == SelectionModel::TRAILING)
return SelectionModel(caret, caret, SelectionModel::LEADING); return SelectionModel(caret, caret, SelectionModel::LEADING);
else if (caret > run_start) { else if (caret > run_start) {
...@@ -407,8 +503,8 @@ SelectionModel RenderTextLinux::LeftSelectionModel( ...@@ -407,8 +503,8 @@ SelectionModel RenderTextLinux::LeftSelectionModel(
return LeftEndSelectionModel(); return LeftEndSelectionModel();
item = prev_run->item; item = prev_run->item;
return (item->analysis.level % 2) ? FirstSelectionModelInsideRun(item) : return IsRunLTR(item) ? LastSelectionModelInsideRun(item) :
LastSelectionModelInsideRun(item); FirstSelectionModelInsideRun(item);
} }
// Assume caret_pos in |current| is n, 'l' represents leading in // Assume caret_pos in |current| is n, 'l' represents leading in
...@@ -440,7 +536,7 @@ SelectionModel RenderTextLinux::RightSelectionModel( ...@@ -440,7 +536,7 @@ SelectionModel RenderTextLinux::RightSelectionModel(
size_t run_start = Utf8IndexToUtf16Index(item->offset); size_t run_start = Utf8IndexToUtf16Index(item->offset);
size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length); size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length);
if (item->analysis.level % 2 == 0) { // LTR run. if (IsRunLTR(item)) {
if (caret_placement == SelectionModel::LEADING) { if (caret_placement == SelectionModel::LEADING) {
size_t cursor = GetIndexOfNextGrapheme(caret); size_t cursor = GetIndexOfNextGrapheme(caret);
return SelectionModel(cursor, caret, SelectionModel::TRAILING); return SelectionModel(cursor, caret, SelectionModel::TRAILING);
...@@ -464,8 +560,8 @@ SelectionModel RenderTextLinux::RightSelectionModel( ...@@ -464,8 +560,8 @@ SelectionModel RenderTextLinux::RightSelectionModel(
return RightEndSelectionModel(); return RightEndSelectionModel();
item = reinterpret_cast<PangoLayoutRun*>(next_run->data)->item; item = reinterpret_cast<PangoLayoutRun*>(next_run->data)->item;
return (item->analysis.level % 2) ? LastSelectionModelInsideRun(item) : return IsRunLTR(item) ? FirstSelectionModelInsideRun(item) :
FirstSelectionModelInsideRun(item); LastSelectionModelInsideRun(item);
} }
SelectionModel RenderTextLinux::LeftSelectionModelByWord( SelectionModel RenderTextLinux::LeftSelectionModelByWord(
...@@ -485,7 +581,7 @@ SelectionModel RenderTextLinux::LeftSelectionModelByWord( ...@@ -485,7 +581,7 @@ SelectionModel RenderTextLinux::LeftSelectionModelByWord(
DCHECK(run); DCHECK(run);
PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
size_t cursor = left.selection_end(); size_t cursor = left.selection_end();
if (item->analysis.level % 2 == 0) { // LTR run. if (IsRunLTR(item)) {
if (iter.IsStartOfWord(cursor)) if (iter.IsStartOfWord(cursor))
return left; return left;
} else { // RTL run. } else { // RTL run.
...@@ -514,7 +610,7 @@ SelectionModel RenderTextLinux::RightSelectionModelByWord( ...@@ -514,7 +610,7 @@ SelectionModel RenderTextLinux::RightSelectionModelByWord(
DCHECK(run); DCHECK(run);
PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
size_t cursor = right.selection_end(); size_t cursor = right.selection_end();
if (item->analysis.level % 2 == 0) { // LTR run. if (IsRunLTR(item)) {
if (iter.IsEndOfWord(cursor)) if (iter.IsEndOfWord(cursor))
return right; return right;
} else { // RTL run. } else { // RTL run.
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include "base/string_util.h" #include "base/string_util.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "base/win/scoped_hdc.h" #include "base/win/scoped_hdc.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "ui/gfx/canvas.h" #include "ui/gfx/canvas.h"
#include "ui/gfx/canvas_skia.h" #include "ui/gfx/canvas_skia.h"
#include "ui/gfx/platform_font.h" #include "ui/gfx/platform_font.h"
...@@ -27,42 +26,6 @@ const int kMaxItems = 10000; ...@@ -27,42 +26,6 @@ const int kMaxItems = 10000;
// TODO(msw): Review memory use/failure? Max string length? Alternate approach? // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
const int kMaxGlyphs = 100000; const int kMaxGlyphs = 100000;
// Draw underline and strike through text decorations.
// Based on |SkCanvas::DrawTextDecorations()| and constants from:
// third_party/skia/src/core/SkTextFormatParams.h
void DrawTextRunDecorations(SkCanvas* canvas_skia,
const SkPaint& paint,
const gfx::internal::TextRun& run,
SkScalar x,
SkScalar y) {
// Fraction of the text size to lower a strike through below the baseline.
const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21);
// Fraction of the text size to lower an underline below the baseline.
const SkScalar kUnderlineOffset = (SK_Scalar1 / 9);
// Fraction of the text size to use for a strike through or under-line.
const SkScalar kLineThickness = (SK_Scalar1 / 18);
SkScalar text_size = paint.getTextSize();
SkScalar height = SkScalarMul(text_size, kLineThickness);
SkRect r;
r.fLeft = x;
r.fRight = x + run.width;
if (run.underline) {
SkScalar offset = SkScalarMulAdd(text_size, kUnderlineOffset, y);
r.fTop = offset;
r.fBottom = offset + height;
canvas_skia->drawRect(r, paint);
}
if (run.strike) {
SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
r.fTop = offset;
r.fBottom = offset + height;
canvas_skia->drawRect(r, paint);
}
}
// Callback to |EnumEnhMetaFile()| to intercept font creation. // Callback to |EnumEnhMetaFile()| to intercept font creation.
int CALLBACK MetaFileEnumProc(HDC hdc, int CALLBACK MetaFileEnumProc(HDC hdc,
HANDLETABLE* table, HANDLETABLE* table,
...@@ -414,58 +377,36 @@ void RenderTextWin::EnsureLayout() { ...@@ -414,58 +377,36 @@ void RenderTextWin::EnsureLayout() {
} }
void RenderTextWin::DrawVisualText(Canvas* canvas) { void RenderTextWin::DrawVisualText(Canvas* canvas) {
SkCanvas* canvas_skia = canvas->GetSkCanvas(); DCHECK(!needs_layout_);
Point offset(ToViewPoint(Point()));
// TODO(msw): Establish a vertical baseline for strings of mixed font heights.
size_t height = default_style().font.GetHeight();
Point offset(GetOriginForSkiaDrawing());
SkScalar x = SkIntToScalar(offset.x()); SkScalar x = SkIntToScalar(offset.x());
SkScalar y = SkIntToScalar(offset.y()); SkScalar y = SkIntToScalar(offset.y());
// Center the text vertically in the display area.
y += (display_rect().height() - height) / 2;
// Offset by the font size to account for Skia expecting y to be the bottom.
y += default_style().font.GetFontSize();
SkPaint paint;
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setStyle(SkPaint::kFill_Style);
paint.setAntiAlias(true);
paint.setSubpixelText(true);
paint.setLCDRenderText(true);
std::vector<SkPoint> pos; std::vector<SkPoint> pos;
internal::SkiaTextRenderer renderer(canvas);
for (size_t i = 0; i < runs_.size(); ++i) { for (size_t i = 0; i < runs_.size(); ++i) {
// Get the run specified by the visual-to-logical map. // Get the run specified by the visual-to-logical map.
internal::TextRun* run = runs_[visual_to_logical_[i]]; internal::TextRun* run = runs_[visual_to_logical_[i]];
// TODO(msw): Font default/fallback and style integration.
SkTypeface::Style style = SkTypeface::kNormal;
SkTypeface* typeface =
SkTypeface::CreateFromName(run->font.GetFontName().c_str(), style);
if (typeface) {
paint.setTypeface(typeface);
// |paint| adds its own ref. Release the ref from CreateFromName.
typeface->unref();
}
paint.setTextSize(run->font.GetFontSize());
paint.setColor(run->foreground);
SkScalar run_x = x;
// Based on WebCore::skiaDrawText. // Based on WebCore::skiaDrawText.
pos.resize(run->glyph_count); pos.resize(run->glyph_count);
SkScalar glyph_x = x;
for (int glyph = 0; glyph < run->glyph_count; glyph++) { for (int glyph = 0; glyph < run->glyph_count; glyph++) {
pos[glyph].set(x + run->offsets[glyph].du, pos[glyph].set(glyph_x + run->offsets[glyph].du,
y + run->offsets[glyph].dv); y + run->offsets[glyph].dv);
x += SkIntToScalar(run->advance_widths[glyph]); glyph_x += SkIntToScalar(run->advance_widths[glyph]);
} }
size_t byte_length = run->glyph_count * sizeof(WORD); renderer.SetFont(run->font);
canvas_skia->drawPosText(run->glyphs.get(), byte_length, &pos[0], paint); renderer.SetForegroundColor(run->foreground);
renderer.DrawPosText(&pos[0], run->glyphs.get(), run->glyph_count);
if (run->strike || run->underline) if (run->strike || run->underline)
DrawTextRunDecorations(canvas_skia, paint, *run, run_x, y); renderer.DrawDecorations(x, y, run->width, run->underline, run->strike);
x = glyph_x;
} }
} }
...@@ -550,8 +491,9 @@ void RenderTextWin::ItemizeLogicalText() { ...@@ -550,8 +491,9 @@ void RenderTextWin::ItemizeLogicalText() {
// Build the list of runs, merge font/underline styles. // Build the list of runs, merge font/underline styles.
// TODO(msw): Only break for font changes, not color etc. See TextRun comment. // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
// TODO(msw): Apply the overriding selection and composition styles. StyleRanges styles(style_ranges());
StyleRanges::const_iterator style = style_ranges().begin(); ApplyCompositionAndSelectionStyles(&styles);
StyleRanges::const_iterator style = styles.begin();
SCRIPT_ITEM* script_item = &script_items[0]; SCRIPT_ITEM* script_item = &script_items[0];
for (int run_break = 0; run_break < text_length;) { for (int run_break = 0; run_break < text_length;) {
internal::TextRun* run = new internal::TextRun(); internal::TextRun* run = new internal::TextRun();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment