Index: ui/gfx/render_text_linux.cc |
=================================================================== |
--- ui/gfx/render_text_linux.cc (revision 113213) |
+++ ui/gfx/render_text_linux.cc (working copy) |
@@ -6,10 +6,12 @@ |
#include <pango/pangocairo.h> |
#include <algorithm> |
+#include <vector> |
#include "base/i18n/break_iterator.h" |
#include "base/logging.h" |
#include "ui/gfx/canvas_skia.h" |
+#include "ui/gfx/font.h" |
#include "ui/gfx/pango_util.h" |
#include "unicode/uchar.h" |
#include "unicode/ustring.h" |
@@ -22,6 +24,18 @@ |
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 |
// TODO(xji): index saved in upper layer is utf16 index. Pango uses utf8 index. |
@@ -154,7 +168,7 @@ |
PangoLayoutRun* first_visual_run = |
reinterpret_cast<PangoLayoutRun*>(current_line_->runs->data); |
PangoItem* item = first_visual_run->item; |
- if (item->analysis.level % 2 == 0) { // LTR. |
+ if (IsRunLTR(item)) { |
xji
2011/12/07 19:52:26
thanks for making those changes.
|
size_t caret = Utf8IndexToUtf16Index(item->offset); |
return SelectionModel(text().length(), caret, SelectionModel::LEADING); |
} else { // RTL. |
@@ -172,7 +186,7 @@ |
PangoLayoutRun* last_visual_run = GetLastRun(); |
if (last_visual_run) { |
PangoItem* item = last_visual_run->item; |
- if (item->analysis.level % 2 == 0) { // LTR. |
+ if (IsRunLTR(item)) { |
size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length, |
false); |
return SelectionModel(text().length(), caret, SelectionModel::TRAILING); |
@@ -260,27 +274,99 @@ |
} |
void RenderTextLinux::DrawVisualText(Canvas* canvas) { |
- Rect bounds(display_rect()); |
+ DCHECK(layout_); |
- // Clip the canvas to the text display area. |
- SkCanvas* canvas_skia = canvas->GetSkCanvas(); |
+ Point offset(GetOriginForSkiaDrawing()); |
+ SkScalar x = SkIntToScalar(offset.x()); |
+ SkScalar y = SkIntToScalar(offset.y()); |
- skia::ScopedPlatformPaint scoped_platform_paint(canvas_skia); |
- cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); |
- cairo_save(cr); |
- cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height()); |
- cairo_clip(cr); |
+ std::vector<SkPoint> pos; |
+ std::vector<uint16> glyphs; |
- int text_width, text_height; |
- pango_layout_get_pixel_size(layout_, &text_width, &text_height); |
- Point offset(ToViewPoint(Point())); |
- // Vertically centered. |
- int text_y = offset.y() + ((bounds.height() - text_height) / 2); |
- // TODO(xji): need to use SkCanvas->drawPosText() for gpu acceleration. |
- cairo_move_to(cr, offset.x(), text_y); |
- pango_cairo_show_layout(cr, layout_); |
+ StyleRanges styles(style_ranges()); |
+ ApplyCompositionAndSelectionStyles(&styles); |
- cairo_restore(cr); |
+ // 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; |
+ } |
+ |
+ internal::SkiaTextRenderer renderer(canvas); |
+ 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; |
+ |
+ size_t run_start = run->item->offset; |
+ size_t first_glyph_byte_index = run_start + run->glyphs->log_clusters[0]; |
+ size_t style_increment = IsRunLTR(run->item) ? 1 : -1; |
+ |
+ // 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)) { |
xji
2011/12/07 19:52:26
Ah, sorry that I just realized does this (using gl
Alexei Svitkine (slow)
2011/12/07 19:59:25
I am not sure how its possible to render a single
xji
2011/12/07 23:17:42
I am not sure about this part.
I remember that you
behdad_google
2011/12/07 23:35:05
Yeah, this is broken. That's why I suggested that
xji
2011/12/08 00:27:56
Does pango handle using 2 different foregrounds (n
xji
2011/12/08 00:27:56
'fi' ligature is different. it is one glyph, but 2
behdad_google
2011/12/14 06:29:11
No. Pango doesn't by itself. But if you try sele
|
+ style = i; |
+ break; |
+ } |
+ } |
+ DCHECK_GE(style, 0); |
xji
2011/12/07 19:52:26
nit: do you want to dcheck "style < style_ranges.u
Alexei Svitkine (slow)
2011/12/07 19:59:25
It's not possible given the above loop.
xji
2011/12/07 23:17:42
Ah, you right.
|
+ |
+ 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]; |
+ if (!IndexInRange(style_ranges_utf8[style], glyph_byte_index)) { |
xji
2011/12/07 19:52:26
same question for whether glyph's style set works
|
+ 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); |
+ } |
+ |
+ style += style_increment; |
+ start = i; |
+ start_x = glyph_x; |
+ } |
+ } |
+ |
+ // Draw the remaining glyphs. |
+ renderer.SetForegroundColor(styles[style].foreground); |
+ renderer.DrawPosText(&pos[start], &glyphs[start], glyph_count - start); |
+ if (styles[style].underline || styles[style].strike) { |
+ renderer.DrawDecorations(start_x, y, glyph_x - start_x, |
+ styles[style].underline, |
+ styles[style].strike); |
+ } |
+ |
+ x = glyph_x; |
+ } |
} |
size_t RenderTextLinux::IndexOfAdjacentGrapheme(size_t index, bool next) { |
@@ -380,7 +466,7 @@ |
size_t run_start = Utf8IndexToUtf16Index(item->offset); |
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) |
return SelectionModel(caret, caret, SelectionModel::LEADING); |
else if (caret > run_start) { |
@@ -405,8 +491,8 @@ |
return LeftEndSelectionModel(); |
item = prev_run->item; |
- return (item->analysis.level % 2) ? FirstSelectionModelInsideRun(item) : |
- LastSelectionModelInsideRun(item); |
+ return IsRunLTR(item) ? LastSelectionModelInsideRun(item) : |
+ FirstSelectionModelInsideRun(item); |
} |
// Assume caret_pos in |current| is n, 'l' represents leading in |
@@ -438,7 +524,7 @@ |
size_t run_start = Utf8IndexToUtf16Index(item->offset); |
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) { |
size_t cursor = GetIndexOfNextGrapheme(caret); |
return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
@@ -462,8 +548,8 @@ |
return RightEndSelectionModel(); |
item = reinterpret_cast<PangoLayoutRun*>(next_run->data)->item; |
- return (item->analysis.level % 2) ? LastSelectionModelInsideRun(item) : |
- FirstSelectionModelInsideRun(item); |
+ return IsRunLTR(item) ? FirstSelectionModelInsideRun(item) : |
+ LastSelectionModelInsideRun(item); |
} |
SelectionModel RenderTextLinux::LeftSelectionModelByWord( |
@@ -483,7 +569,7 @@ |
DCHECK(run); |
PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
size_t cursor = left.selection_end(); |
- if (item->analysis.level % 2 == 0) { // LTR run. |
+ if (IsRunLTR(item)) { |
if (iter.IsStartOfWord(cursor)) |
return left; |
} else { // RTL run. |
@@ -512,7 +598,7 @@ |
DCHECK(run); |
PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
size_t cursor = right.selection_end(); |
- if (item->analysis.level % 2 == 0) { // LTR run. |
+ if (IsRunLTR(item)) { |
if (iter.IsEndOfWord(cursor)) |
return right; |
} else { // RTL run. |