Home | History | Annotate | Download | only in gfx
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/gfx/render_text.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/i18n/break_iterator.h"
     10 #include "base/logging.h"
     11 #include "base/stl_util.h"
     12 #include "third_party/icu/source/common/unicode/rbbi.h"
     13 #include "third_party/icu/source/common/unicode/utf16.h"
     14 #include "third_party/skia/include/core/SkTypeface.h"
     15 #include "third_party/skia/include/effects/SkGradientShader.h"
     16 #include "ui/base/text/text_elider.h"
     17 #include "ui/base/text/utf16_indexing.h"
     18 #include "ui/gfx/canvas.h"
     19 #include "ui/gfx/insets.h"
     20 #include "ui/gfx/skia_util.h"
     21 #include "ui/gfx/text_constants.h"
     22 
     23 namespace gfx {
     24 
     25 namespace {
     26 
     27 // All chars are replaced by this char when the password style is set.
     28 // TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*'
     29 // that's available in the font (find_invisible_char() in gtkentry.c).
     30 const char16 kPasswordReplacementChar = '*';
     31 
     32 // Default color used for the text and cursor.
     33 const SkColor kDefaultColor = SK_ColorBLACK;
     34 
     35 // Default color used for drawing selection background.
     36 const SkColor kDefaultSelectionBackgroundColor = SK_ColorGRAY;
     37 
     38 // Fraction of the text size to lower a strike through below the baseline.
     39 const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21);
     40 // Fraction of the text size to lower an underline below the baseline.
     41 const SkScalar kUnderlineOffset = (SK_Scalar1 / 9);
     42 // Fraction of the text size to use for a strike through or under-line.
     43 const SkScalar kLineThickness = (SK_Scalar1 / 18);
     44 // Fraction of the text size to use for a top margin of a diagonal strike.
     45 const SkScalar kDiagonalStrikeMarginOffset = (SK_Scalar1 / 4);
     46 
     47 // Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags.
     48 SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) {
     49   int skia_style = SkTypeface::kNormal;
     50   skia_style |= (font_style & gfx::Font::BOLD) ? SkTypeface::kBold : 0;
     51   skia_style |= (font_style & gfx::Font::ITALIC) ? SkTypeface::kItalic : 0;
     52   return static_cast<SkTypeface::Style>(skia_style);
     53 }
     54 
     55 // Given |font| and |display_width|, returns the width of the fade gradient.
     56 int CalculateFadeGradientWidth(const Font& font, int display_width) {
     57   // Fade in/out about 2.5 characters of the beginning/end of the string.
     58   // The .5 here is helpful if one of the characters is a space.
     59   // Use a quarter of the display width if the display width is very short.
     60   const int average_character_width = font.GetAverageCharacterWidth();
     61   const double gradient_width = std::min(average_character_width * 2.5,
     62                                          display_width / 4.0);
     63   DCHECK_GE(gradient_width, 0.0);
     64   return static_cast<int>(floor(gradient_width + 0.5));
     65 }
     66 
     67 // Appends to |positions| and |colors| values corresponding to the fade over
     68 // |fade_rect| from color |c0| to color |c1|.
     69 void AddFadeEffect(const Rect& text_rect,
     70                    const Rect& fade_rect,
     71                    SkColor c0,
     72                    SkColor c1,
     73                    std::vector<SkScalar>* positions,
     74                    std::vector<SkColor>* colors) {
     75   const SkScalar left = static_cast<SkScalar>(fade_rect.x() - text_rect.x());
     76   const SkScalar width = static_cast<SkScalar>(fade_rect.width());
     77   const SkScalar p0 = left / text_rect.width();
     78   const SkScalar p1 = (left + width) / text_rect.width();
     79   // Prepend 0.0 to |positions|, as required by Skia.
     80   if (positions->empty() && p0 != 0.0) {
     81     positions->push_back(0.0);
     82     colors->push_back(c0);
     83   }
     84   positions->push_back(p0);
     85   colors->push_back(c0);
     86   positions->push_back(p1);
     87   colors->push_back(c1);
     88 }
     89 
     90 // Creates a SkShader to fade the text, with |left_part| specifying the left
     91 // fade effect, if any, and |right_part| specifying the right fade effect.
     92 skia::RefPtr<SkShader> CreateFadeShader(const Rect& text_rect,
     93                                         const Rect& left_part,
     94                                         const Rect& right_part,
     95                                         SkColor color) {
     96   // Fade alpha of 51/255 corresponds to a fade of 0.2 of the original color.
     97   const SkColor fade_color = SkColorSetA(color, 51);
     98   std::vector<SkScalar> positions;
     99   std::vector<SkColor> colors;
    100 
    101   if (!left_part.IsEmpty())
    102     AddFadeEffect(text_rect, left_part, fade_color, color,
    103                   &positions, &colors);
    104   if (!right_part.IsEmpty())
    105     AddFadeEffect(text_rect, right_part, color, fade_color,
    106                   &positions, &colors);
    107   DCHECK(!positions.empty());
    108 
    109   // Terminate |positions| with 1.0, as required by Skia.
    110   if (positions.back() != 1.0) {
    111     positions.push_back(1.0);
    112     colors.push_back(colors.back());
    113   }
    114 
    115   SkPoint points[2];
    116   points[0].iset(text_rect.x(), text_rect.y());
    117   points[1].iset(text_rect.right(), text_rect.y());
    118 
    119   return skia::AdoptRef(
    120       SkGradientShader::CreateLinear(&points[0], &colors[0], &positions[0],
    121                                      colors.size(), SkShader::kClamp_TileMode));
    122 }
    123 
    124 }  // namespace
    125 
    126 namespace internal {
    127 
    128 // Value of |underline_thickness_| that indicates that underline metrics have
    129 // not been set explicitly.
    130 const SkScalar kUnderlineMetricsNotSet = -1.0f;
    131 
    132 SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas)
    133     : canvas_skia_(canvas->sk_canvas()),
    134       started_drawing_(false),
    135       underline_thickness_(kUnderlineMetricsNotSet),
    136       underline_position_(0.0f) {
    137   DCHECK(canvas_skia_);
    138   paint_.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    139   paint_.setStyle(SkPaint::kFill_Style);
    140   paint_.setAntiAlias(true);
    141   paint_.setSubpixelText(true);
    142   paint_.setLCDRenderText(true);
    143   bounds_.setEmpty();
    144 }
    145 
    146 SkiaTextRenderer::~SkiaTextRenderer() {
    147   // Work-around for http://crbug.com/122743, where non-ClearType text is
    148   // rendered with incorrect gamma when using the fade shader. Draw the text
    149   // to a layer and restore it faded by drawing a rect in kDstIn_Mode mode.
    150   //
    151   // TODO(asvitkine): Remove this work-around once the Skia bug is fixed.
    152   //                  http://code.google.com/p/skia/issues/detail?id=590
    153   if (deferred_fade_shader_.get()) {
    154     paint_.setShader(deferred_fade_shader_.get());
    155     paint_.setXfermodeMode(SkXfermode::kDstIn_Mode);
    156     canvas_skia_->drawRect(bounds_, paint_);
    157     canvas_skia_->restore();
    158   }
    159 }
    160 
    161 void SkiaTextRenderer::SetDrawLooper(SkDrawLooper* draw_looper) {
    162   paint_.setLooper(draw_looper);
    163 }
    164 
    165 void SkiaTextRenderer::SetFontSmoothingSettings(bool enable_smoothing,
    166                                                 bool enable_lcd_text) {
    167   paint_.setAntiAlias(enable_smoothing);
    168   paint_.setSubpixelText(enable_smoothing);
    169   paint_.setLCDRenderText(enable_lcd_text);
    170 }
    171 
    172 void SkiaTextRenderer::SetTypeface(SkTypeface* typeface) {
    173   paint_.setTypeface(typeface);
    174 }
    175 
    176 void SkiaTextRenderer::SetTextSize(SkScalar size) {
    177   paint_.setTextSize(size);
    178 }
    179 
    180 void SkiaTextRenderer::SetFontFamilyWithStyle(const std::string& family,
    181                                               int style) {
    182   DCHECK(!family.empty());
    183 
    184   SkTypeface::Style skia_style = ConvertFontStyleToSkiaTypefaceStyle(style);
    185   skia::RefPtr<SkTypeface> typeface =
    186       skia::AdoptRef(SkTypeface::CreateFromName(family.c_str(), skia_style));
    187   if (typeface) {
    188     // |paint_| adds its own ref. So don't |release()| it from the ref ptr here.
    189     SetTypeface(typeface.get());
    190 
    191     // Enable fake bold text if bold style is needed but new typeface does not
    192     // have it.
    193     paint_.setFakeBoldText((skia_style & SkTypeface::kBold) &&
    194                            !typeface->isBold());
    195   }
    196 }
    197 
    198 void SkiaTextRenderer::SetForegroundColor(SkColor foreground) {
    199   paint_.setColor(foreground);
    200 }
    201 
    202 void SkiaTextRenderer::SetShader(SkShader* shader, const Rect& bounds) {
    203   bounds_ = RectToSkRect(bounds);
    204   paint_.setShader(shader);
    205 }
    206 
    207 void SkiaTextRenderer::SetUnderlineMetrics(SkScalar thickness,
    208                                            SkScalar position) {
    209   underline_thickness_ = thickness;
    210   underline_position_ = position;
    211 }
    212 
    213 void SkiaTextRenderer::DrawPosText(const SkPoint* pos,
    214                                    const uint16* glyphs,
    215                                    size_t glyph_count) {
    216   if (!started_drawing_) {
    217     started_drawing_ = true;
    218     // Work-around for http://crbug.com/122743, where non-ClearType text is
    219     // rendered with incorrect gamma when using the fade shader. Draw the text
    220     // to a layer and restore it faded by drawing a rect in kDstIn_Mode mode.
    221     //
    222     // Skip this when there is a looper which seems not working well with
    223     // deferred paint. Currently a looper is only used for text shadows.
    224     //
    225     // TODO(asvitkine): Remove this work-around once the Skia bug is fixed.
    226     //                  http://code.google.com/p/skia/issues/detail?id=590
    227     if (!paint_.isLCDRenderText() &&
    228         paint_.getShader() &&
    229         !paint_.getLooper()) {
    230       deferred_fade_shader_ = skia::SharePtr(paint_.getShader());
    231       paint_.setShader(NULL);
    232       canvas_skia_->saveLayer(&bounds_, NULL);
    233     }
    234   }
    235 
    236   const size_t byte_length = glyph_count * sizeof(glyphs[0]);
    237   canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_);
    238 }
    239 
    240 void SkiaTextRenderer::DrawDecorations(int x, int y, int width, bool underline,
    241                                        bool strike, bool diagonal_strike) {
    242   if (underline)
    243     DrawUnderline(x, y, width);
    244   if (strike)
    245     DrawStrike(x, y, width);
    246   if (diagonal_strike)
    247     DrawDiagonalStrike(x, y, width);
    248 }
    249 
    250 void SkiaTextRenderer::DrawUnderline(int x, int y, int width) {
    251   SkRect r = SkRect::MakeLTRB(x, y + underline_position_, x + width,
    252                               y + underline_position_ + underline_thickness_);
    253   if (underline_thickness_ == kUnderlineMetricsNotSet) {
    254     const SkScalar text_size = paint_.getTextSize();
    255     r.fTop = SkScalarMulAdd(text_size, kUnderlineOffset, y);
    256     r.fBottom = r.fTop + SkScalarMul(text_size, kLineThickness);
    257   }
    258   canvas_skia_->drawRect(r, paint_);
    259 }
    260 
    261 void SkiaTextRenderer::DrawStrike(int x, int y, int width) const {
    262   const SkScalar text_size = paint_.getTextSize();
    263   const SkScalar height = SkScalarMul(text_size, kLineThickness);
    264   const SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
    265   const SkRect r = SkRect::MakeLTRB(x, offset, x + width, offset + height);
    266   canvas_skia_->drawRect(r, paint_);
    267 }
    268 
    269 void SkiaTextRenderer::DrawDiagonalStrike(int x, int y, int width) const {
    270   const SkScalar text_size = paint_.getTextSize();
    271   const SkScalar offset = SkScalarMul(text_size, kDiagonalStrikeMarginOffset);
    272 
    273   SkPaint paint(paint_);
    274   paint.setAntiAlias(true);
    275   paint.setStyle(SkPaint::kFill_Style);
    276   paint.setStrokeWidth(SkScalarMul(text_size, kLineThickness) * 2);
    277   canvas_skia_->drawLine(x, y, x + width, y - text_size + offset, paint);
    278 }
    279 
    280 StyleIterator::StyleIterator(const BreakList<SkColor>& colors,
    281                              const std::vector<BreakList<bool> >& styles)
    282     : colors_(colors),
    283       styles_(styles) {
    284   color_ = colors_.breaks().begin();
    285   for (size_t i = 0; i < styles_.size(); ++i)
    286     style_.push_back(styles_[i].breaks().begin());
    287 }
    288 
    289 StyleIterator::~StyleIterator() {}
    290 
    291 ui::Range StyleIterator::GetRange() const {
    292   ui::Range range(colors_.GetRange(color_));
    293   for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
    294     range = range.Intersect(styles_[i].GetRange(style_[i]));
    295   return range;
    296 }
    297 
    298 void StyleIterator::UpdatePosition(size_t position) {
    299   color_ = colors_.GetBreak(position);
    300   for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
    301     style_[i] = styles_[i].GetBreak(position);
    302 }
    303 
    304 }  // namespace internal
    305 
    306 RenderText::~RenderText() {
    307 }
    308 
    309 void RenderText::SetText(const base::string16& text) {
    310   DCHECK(!composition_range_.IsValid());
    311   text_ = text;
    312 
    313   // Adjust ranged styles and colors to accommodate a new text length.
    314   const size_t text_length = text_.length();
    315   colors_.SetMax(text_length);
    316   for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
    317     styles_[style].SetMax(text_length);
    318   cached_bounds_and_offset_valid_ = false;
    319 
    320   // Reset selection model. SetText should always followed by SetSelectionModel
    321   // or SetCursorPosition in upper layer.
    322   SetSelectionModel(SelectionModel());
    323 
    324   // Invalidate the cached text direction if it depends on the text contents.
    325   if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT)
    326     text_direction_ = base::i18n::UNKNOWN_DIRECTION;
    327 
    328   obscured_reveal_index_ = -1;
    329   UpdateLayoutText();
    330   ResetLayout();
    331 }
    332 
    333 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) {
    334   if (horizontal_alignment_ != alignment) {
    335     horizontal_alignment_ = alignment;
    336     display_offset_ = Vector2d();
    337     cached_bounds_and_offset_valid_ = false;
    338   }
    339 }
    340 
    341 void RenderText::SetVerticalAlignment(VerticalAlignment alignment) {
    342   if (vertical_alignment_ != alignment) {
    343     vertical_alignment_ = alignment;
    344     display_offset_ = Vector2d();
    345     cached_bounds_and_offset_valid_ = false;
    346   }
    347 }
    348 
    349 void RenderText::SetFontList(const FontList& font_list) {
    350   font_list_ = font_list;
    351   cached_bounds_and_offset_valid_ = false;
    352   ResetLayout();
    353 }
    354 
    355 void RenderText::SetFont(const Font& font) {
    356   SetFontList(FontList(font));
    357 }
    358 
    359 void RenderText::SetFontSize(int size) {
    360   SetFontList(font_list_.DeriveFontListWithSize(size));
    361 }
    362 
    363 const Font& RenderText::GetPrimaryFont() const {
    364   return font_list_.GetPrimaryFont();
    365 }
    366 
    367 void RenderText::SetCursorEnabled(bool cursor_enabled) {
    368   cursor_enabled_ = cursor_enabled;
    369   cached_bounds_and_offset_valid_ = false;
    370 }
    371 
    372 void RenderText::ToggleInsertMode() {
    373   insert_mode_ = !insert_mode_;
    374   cached_bounds_and_offset_valid_ = false;
    375 }
    376 
    377 void RenderText::SetObscured(bool obscured) {
    378   if (obscured != obscured_) {
    379     obscured_ = obscured;
    380     obscured_reveal_index_ = -1;
    381     cached_bounds_and_offset_valid_ = false;
    382     UpdateLayoutText();
    383     ResetLayout();
    384   }
    385 }
    386 
    387 void RenderText::SetObscuredRevealIndex(int index) {
    388   if (obscured_reveal_index_ == index)
    389     return;
    390 
    391   obscured_reveal_index_ = index;
    392   cached_bounds_and_offset_valid_ = false;
    393   UpdateLayoutText();
    394   ResetLayout();
    395 }
    396 
    397 void RenderText::SetDisplayRect(const Rect& r) {
    398   display_rect_ = r;
    399   cached_bounds_and_offset_valid_ = false;
    400 }
    401 
    402 void RenderText::SetCursorPosition(size_t position) {
    403   MoveCursorTo(position, false);
    404 }
    405 
    406 void RenderText::MoveCursor(BreakType break_type,
    407                             VisualCursorDirection direction,
    408                             bool select) {
    409   SelectionModel position(cursor_position(), selection_model_.caret_affinity());
    410   // Cancelling a selection moves to the edge of the selection.
    411   if (break_type != LINE_BREAK && !selection().is_empty() && !select) {
    412     SelectionModel selection_start = GetSelectionModelForSelectionStart();
    413     int start_x = GetCursorBounds(selection_start, true).x();
    414     int cursor_x = GetCursorBounds(position, true).x();
    415     // Use the selection start if it is left (when |direction| is CURSOR_LEFT)
    416     // or right (when |direction| is CURSOR_RIGHT) of the selection end.
    417     if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x)
    418       position = selection_start;
    419     // For word breaks, use the nearest word boundary in the appropriate
    420     // |direction|.
    421     if (break_type == WORD_BREAK)
    422       position = GetAdjacentSelectionModel(position, break_type, direction);
    423   } else {
    424     position = GetAdjacentSelectionModel(position, break_type, direction);
    425   }
    426   if (select)
    427     position.set_selection_start(selection().start());
    428   MoveCursorTo(position);
    429 }
    430 
    431 bool RenderText::MoveCursorTo(const SelectionModel& model) {
    432   // Enforce valid selection model components.
    433   size_t text_length = text().length();
    434   ui::Range range(std::min(model.selection().start(), text_length),
    435                   std::min(model.caret_pos(), text_length));
    436   // The current model only supports caret positions at valid character indices.
    437   if (!IsCursorablePosition(range.start()) ||
    438       !IsCursorablePosition(range.end()))
    439     return false;
    440   SelectionModel sel(range, model.caret_affinity());
    441   bool changed = sel != selection_model_;
    442   SetSelectionModel(sel);
    443   return changed;
    444 }
    445 
    446 bool RenderText::MoveCursorTo(const Point& point, bool select) {
    447   SelectionModel position = FindCursorPosition(point);
    448   if (select)
    449     position.set_selection_start(selection().start());
    450   return MoveCursorTo(position);
    451 }
    452 
    453 bool RenderText::SelectRange(const ui::Range& range) {
    454   ui::Range sel(std::min(range.start(), text().length()),
    455                 std::min(range.end(), text().length()));
    456   if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end()))
    457     return false;
    458   LogicalCursorDirection affinity =
    459       (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD;
    460   SetSelectionModel(SelectionModel(sel, affinity));
    461   return true;
    462 }
    463 
    464 bool RenderText::IsPointInSelection(const Point& point) {
    465   if (selection().is_empty())
    466     return false;
    467   SelectionModel cursor = FindCursorPosition(point);
    468   return RangeContainsCaret(
    469       selection(), cursor.caret_pos(), cursor.caret_affinity());
    470 }
    471 
    472 void RenderText::ClearSelection() {
    473   SetSelectionModel(SelectionModel(cursor_position(),
    474                                    selection_model_.caret_affinity()));
    475 }
    476 
    477 void RenderText::SelectAll(bool reversed) {
    478   const size_t length = text().length();
    479   const ui::Range all = reversed ? ui::Range(length, 0) : ui::Range(0, length);
    480   const bool success = SelectRange(all);
    481   DCHECK(success);
    482 }
    483 
    484 void RenderText::SelectWord() {
    485   if (obscured_) {
    486     SelectAll(false);
    487     return;
    488   }
    489 
    490   size_t selection_max = selection().GetMax();
    491 
    492   base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
    493   bool success = iter.Init();
    494   DCHECK(success);
    495   if (!success)
    496     return;
    497 
    498   size_t selection_min = selection().GetMin();
    499   if (selection_min == text().length() && selection_min != 0)
    500     --selection_min;
    501 
    502   for (; selection_min != 0; --selection_min) {
    503     if (iter.IsStartOfWord(selection_min) ||
    504         iter.IsEndOfWord(selection_min))
    505       break;
    506   }
    507 
    508   if (selection_min == selection_max && selection_max != text().length())
    509     ++selection_max;
    510 
    511   for (; selection_max < text().length(); ++selection_max)
    512     if (iter.IsEndOfWord(selection_max) || iter.IsStartOfWord(selection_max))
    513       break;
    514 
    515   const bool reversed = selection().is_reversed();
    516   MoveCursorTo(reversed ? selection_max : selection_min, false);
    517   MoveCursorTo(reversed ? selection_min : selection_max, true);
    518 }
    519 
    520 const ui::Range& RenderText::GetCompositionRange() const {
    521   return composition_range_;
    522 }
    523 
    524 void RenderText::SetCompositionRange(const ui::Range& composition_range) {
    525   CHECK(!composition_range.IsValid() ||
    526         ui::Range(0, text_.length()).Contains(composition_range));
    527   composition_range_.set_end(composition_range.end());
    528   composition_range_.set_start(composition_range.start());
    529   ResetLayout();
    530 }
    531 
    532 void RenderText::SetColor(SkColor value) {
    533   colors_.SetValue(value);
    534 
    535 #if defined(OS_WIN)
    536   // TODO(msw): Windows applies colors and decorations in the layout process.
    537   cached_bounds_and_offset_valid_ = false;
    538   ResetLayout();
    539 #endif
    540 }
    541 
    542 void RenderText::ApplyColor(SkColor value, const ui::Range& range) {
    543   colors_.ApplyValue(value, range);
    544 
    545 #if defined(OS_WIN)
    546   // TODO(msw): Windows applies colors and decorations in the layout process.
    547   cached_bounds_and_offset_valid_ = false;
    548   ResetLayout();
    549 #endif
    550 }
    551 
    552 void RenderText::SetStyle(TextStyle style, bool value) {
    553   styles_[style].SetValue(value);
    554 
    555   // Only invalidate the layout on font changes; not for colors or decorations.
    556   bool invalidate = (style == BOLD) || (style == ITALIC);
    557 #if defined(OS_WIN)
    558   // TODO(msw): Windows applies colors and decorations in the layout process.
    559   invalidate = true;
    560 #endif
    561   if (invalidate) {
    562     cached_bounds_and_offset_valid_ = false;
    563     ResetLayout();
    564   }
    565 }
    566 
    567 void RenderText::ApplyStyle(TextStyle style,
    568                             bool value,
    569                             const ui::Range& range) {
    570   styles_[style].ApplyValue(value, range);
    571 
    572   // Only invalidate the layout on font changes; not for colors or decorations.
    573   bool invalidate = (style == BOLD) || (style == ITALIC);
    574 #if defined(OS_WIN)
    575   // TODO(msw): Windows applies colors and decorations in the layout process.
    576   invalidate = true;
    577 #endif
    578   if (invalidate) {
    579     cached_bounds_and_offset_valid_ = false;
    580     ResetLayout();
    581   }
    582 }
    583 
    584 bool RenderText::GetStyle(TextStyle style) const {
    585   return (styles_[style].breaks().size() == 1) &&
    586       styles_[style].breaks().front().second;
    587 }
    588 
    589 void RenderText::SetDirectionalityMode(DirectionalityMode mode) {
    590   if (mode == directionality_mode_)
    591     return;
    592 
    593   directionality_mode_ = mode;
    594   text_direction_ = base::i18n::UNKNOWN_DIRECTION;
    595   ResetLayout();
    596 }
    597 
    598 base::i18n::TextDirection RenderText::GetTextDirection() {
    599   if (text_direction_ == base::i18n::UNKNOWN_DIRECTION) {
    600     switch (directionality_mode_) {
    601       case DIRECTIONALITY_FROM_TEXT:
    602         // Derive the direction from the display text, which differs from text()
    603         // in the case of obscured (password) textfields.
    604         text_direction_ =
    605             base::i18n::GetFirstStrongCharacterDirection(GetLayoutText());
    606         break;
    607       case DIRECTIONALITY_FROM_UI:
    608         text_direction_ = base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT :
    609                                                 base::i18n::LEFT_TO_RIGHT;
    610         break;
    611       case DIRECTIONALITY_FORCE_LTR:
    612         text_direction_ = base::i18n::LEFT_TO_RIGHT;
    613         break;
    614       case DIRECTIONALITY_FORCE_RTL:
    615         text_direction_ = base::i18n::RIGHT_TO_LEFT;
    616         break;
    617       default:
    618         NOTREACHED();
    619     }
    620   }
    621 
    622   return text_direction_;
    623 }
    624 
    625 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
    626   return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ?
    627       CURSOR_RIGHT : CURSOR_LEFT;
    628 }
    629 
    630 int RenderText::GetContentWidth() {
    631   return GetStringSize().width() + (cursor_enabled_ ? 1 : 0);
    632 }
    633 
    634 void RenderText::Draw(Canvas* canvas) {
    635   EnsureLayout();
    636 
    637   if (clip_to_display_rect()) {
    638     Rect clip_rect(display_rect());
    639     clip_rect.Inset(ShadowValue::GetMargin(text_shadows_));
    640 
    641     canvas->Save();
    642     canvas->ClipRect(clip_rect);
    643   }
    644 
    645   if (!text().empty() && focused())
    646     DrawSelection(canvas);
    647 
    648   if (cursor_enabled() && cursor_visible() && focused())
    649     DrawCursor(canvas, selection_model_);
    650 
    651   if (!text().empty())
    652     DrawVisualText(canvas);
    653 
    654   if (clip_to_display_rect())
    655     canvas->Restore();
    656 }
    657 
    658 void RenderText::DrawCursor(Canvas* canvas, const SelectionModel& position) {
    659   // Paint cursor. Replace cursor is drawn as rectangle for now.
    660   // TODO(msw): Draw a better cursor with a better indication of association.
    661   canvas->FillRect(GetCursorBounds(position, true), cursor_color_);
    662 }
    663 
    664 void RenderText::DrawSelectedTextForDrag(Canvas* canvas) {
    665   EnsureLayout();
    666   const std::vector<Rect> sel = GetSubstringBounds(selection());
    667 
    668   // Override the selection color with black, and force the background to be
    669   // transparent so that it's rendered without subpixel antialiasing.
    670   const bool saved_background_is_transparent = background_is_transparent();
    671   const SkColor saved_selection_color = selection_color();
    672   set_background_is_transparent(true);
    673   set_selection_color(SK_ColorBLACK);
    674 
    675   for (size_t i = 0; i < sel.size(); ++i) {
    676     canvas->Save();
    677     canvas->ClipRect(sel[i]);
    678     DrawVisualText(canvas);
    679     canvas->Restore();
    680   }
    681 
    682   // Restore saved transparency and selection color.
    683   set_selection_color(saved_selection_color);
    684   set_background_is_transparent(saved_background_is_transparent);
    685 }
    686 
    687 Rect RenderText::GetCursorBounds(const SelectionModel& caret,
    688                                  bool insert_mode) {
    689   EnsureLayout();
    690 
    691   size_t caret_pos = caret.caret_pos();
    692   DCHECK(IsCursorablePosition(caret_pos));
    693   // In overtype mode, ignore the affinity and always indicate that we will
    694   // overtype the next character.
    695   LogicalCursorDirection caret_affinity =
    696       insert_mode ? caret.caret_affinity() : CURSOR_FORWARD;
    697   int x = 0, width = 1;
    698   Size size = GetStringSize();
    699   if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) {
    700     // The caret is attached to the boundary. Always return a 1-dip width caret,
    701     // since there is nothing to overtype.
    702     if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0))
    703       x = size.width();
    704   } else {
    705     size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ?
    706         caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD);
    707     ui::Range xspan(GetGlyphBounds(grapheme_start));
    708     if (insert_mode) {
    709       x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start();
    710     } else {  // overtype mode
    711       x = xspan.GetMin();
    712       width = xspan.length();
    713     }
    714   }
    715   return Rect(ToViewPoint(Point(x, 0)), Size(width, size.height()));
    716 }
    717 
    718 const Rect& RenderText::GetUpdatedCursorBounds() {
    719   UpdateCachedBoundsAndOffset();
    720   return cursor_bounds_;
    721 }
    722 
    723 size_t RenderText::IndexOfAdjacentGrapheme(size_t index,
    724                                            LogicalCursorDirection direction) {
    725   if (index > text().length())
    726     return text().length();
    727 
    728   EnsureLayout();
    729 
    730   if (direction == CURSOR_FORWARD) {
    731     while (index < text().length()) {
    732       index++;
    733       if (IsCursorablePosition(index))
    734         return index;
    735     }
    736     return text().length();
    737   }
    738 
    739   while (index > 0) {
    740     index--;
    741     if (IsCursorablePosition(index))
    742       return index;
    743   }
    744   return 0;
    745 }
    746 
    747 SelectionModel RenderText::GetSelectionModelForSelectionStart() {
    748   const ui::Range& sel = selection();
    749   if (sel.is_empty())
    750     return selection_model_;
    751   return SelectionModel(sel.start(),
    752                         sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
    753 }
    754 
    755 void RenderText::SetTextShadows(const ShadowValues& shadows) {
    756   text_shadows_ = shadows;
    757 }
    758 
    759 RenderText::RenderText()
    760     : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
    761       vertical_alignment_(ALIGN_VCENTER),
    762       directionality_mode_(DIRECTIONALITY_FROM_TEXT),
    763       text_direction_(base::i18n::UNKNOWN_DIRECTION),
    764       cursor_enabled_(true),
    765       cursor_visible_(false),
    766       insert_mode_(true),
    767       cursor_color_(kDefaultColor),
    768       selection_color_(kDefaultColor),
    769       selection_background_focused_color_(kDefaultSelectionBackgroundColor),
    770       focused_(false),
    771       composition_range_(ui::Range::InvalidRange()),
    772       colors_(kDefaultColor),
    773       styles_(NUM_TEXT_STYLES),
    774       composition_and_selection_styles_applied_(false),
    775       obscured_(false),
    776       obscured_reveal_index_(-1),
    777       truncate_length_(0),
    778       fade_head_(false),
    779       fade_tail_(false),
    780       background_is_transparent_(false),
    781       clip_to_display_rect_(true),
    782       cached_bounds_and_offset_valid_(false) {
    783 }
    784 
    785 const Vector2d& RenderText::GetUpdatedDisplayOffset() {
    786   UpdateCachedBoundsAndOffset();
    787   return display_offset_;
    788 }
    789 
    790 SelectionModel RenderText::GetAdjacentSelectionModel(
    791     const SelectionModel& current,
    792     BreakType break_type,
    793     VisualCursorDirection direction) {
    794   EnsureLayout();
    795 
    796   if (break_type == LINE_BREAK || text().empty())
    797     return EdgeSelectionModel(direction);
    798   if (break_type == CHARACTER_BREAK)
    799     return AdjacentCharSelectionModel(current, direction);
    800   DCHECK(break_type == WORD_BREAK);
    801   return AdjacentWordSelectionModel(current, direction);
    802 }
    803 
    804 SelectionModel RenderText::EdgeSelectionModel(
    805     VisualCursorDirection direction) {
    806   if (direction == GetVisualDirectionOfLogicalEnd())
    807     return SelectionModel(text().length(), CURSOR_FORWARD);
    808   return SelectionModel(0, CURSOR_BACKWARD);
    809 }
    810 
    811 void RenderText::SetSelectionModel(const SelectionModel& model) {
    812   DCHECK_LE(model.selection().GetMax(), text().length());
    813   selection_model_ = model;
    814   cached_bounds_and_offset_valid_ = false;
    815 }
    816 
    817 const base::string16& RenderText::GetLayoutText() const {
    818   return layout_text_.empty() ? text_ : layout_text_;
    819 }
    820 
    821 void RenderText::ApplyCompositionAndSelectionStyles() {
    822   // Save the underline and color breaks to undo the temporary styles later.
    823   DCHECK(!composition_and_selection_styles_applied_);
    824   saved_colors_ = colors_;
    825   saved_underlines_ = styles_[UNDERLINE];
    826 
    827   // Apply an underline to the composition range in |underlines|.
    828   if (composition_range_.IsValid() && !composition_range_.is_empty())
    829     styles_[UNDERLINE].ApplyValue(true, composition_range_);
    830 
    831   // Apply the selected text color to the [un-reversed] selection range.
    832   if (!selection().is_empty()) {
    833     const ui::Range range(selection().GetMin(), selection().GetMax());
    834     colors_.ApplyValue(selection_color_, range);
    835   }
    836   composition_and_selection_styles_applied_ = true;
    837 }
    838 
    839 void RenderText::UndoCompositionAndSelectionStyles() {
    840   // Restore the underline and color breaks to undo the temporary styles.
    841   DCHECK(composition_and_selection_styles_applied_);
    842   colors_ = saved_colors_;
    843   styles_[UNDERLINE] = saved_underlines_;
    844   composition_and_selection_styles_applied_ = false;
    845 }
    846 
    847 Vector2d RenderText::GetTextOffset() {
    848   Vector2d offset = display_rect().OffsetFromOrigin();
    849   offset.Add(GetUpdatedDisplayOffset());
    850   offset.Add(GetAlignmentOffset());
    851   return offset;
    852 }
    853 
    854 Point RenderText::ToTextPoint(const Point& point) {
    855   return point - GetTextOffset();
    856 }
    857 
    858 Point RenderText::ToViewPoint(const Point& point) {
    859   return point + GetTextOffset();
    860 }
    861 
    862 Vector2d RenderText::GetAlignmentOffset() {
    863   Vector2d offset;
    864   if (horizontal_alignment_ != ALIGN_LEFT) {
    865     offset.set_x(display_rect().width() - GetContentWidth());
    866     if (horizontal_alignment_ == ALIGN_CENTER)
    867       offset.set_x(offset.x() / 2);
    868   }
    869   if (vertical_alignment_ != ALIGN_TOP) {
    870     offset.set_y(display_rect().height() - GetStringSize().height());
    871     if (vertical_alignment_ == ALIGN_VCENTER)
    872       offset.set_y(offset.y() / 2);
    873   }
    874   return offset;
    875 }
    876 
    877 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
    878   if (!fade_head() && !fade_tail())
    879     return;
    880 
    881   const int text_width = GetStringSize().width();
    882   const int display_width = display_rect().width();
    883 
    884   // If the text fits as-is, no need to fade.
    885   if (text_width <= display_width)
    886     return;
    887 
    888   int gradient_width = CalculateFadeGradientWidth(GetPrimaryFont(),
    889                                                   display_width);
    890   if (gradient_width == 0)
    891     return;
    892 
    893   bool fade_left = fade_head();
    894   bool fade_right = fade_tail();
    895   // Under RTL, |fade_right| == |fade_head|.
    896   // TODO(asvitkine): This is currently not based on GetTextDirection() because
    897   //                  RenderTextWin does not return a direction that's based on
    898   //                  the text content.
    899   if (horizontal_alignment_ == ALIGN_RIGHT)
    900     std::swap(fade_left, fade_right);
    901 
    902   Rect solid_part = display_rect();
    903   Rect left_part;
    904   Rect right_part;
    905   if (fade_left) {
    906     left_part = solid_part;
    907     left_part.Inset(0, 0, solid_part.width() - gradient_width, 0);
    908     solid_part.Inset(gradient_width, 0, 0, 0);
    909   }
    910   if (fade_right) {
    911     right_part = solid_part;
    912     right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0);
    913     solid_part.Inset(0, 0, gradient_width, 0);
    914   }
    915 
    916   Rect text_rect = display_rect();
    917   text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0);
    918 
    919   // TODO(msw): Use the actual text colors corresponding to each faded part.
    920   skia::RefPtr<SkShader> shader = CreateFadeShader(
    921       text_rect, left_part, right_part, colors_.breaks().front().second);
    922   if (shader)
    923     renderer->SetShader(shader.get(), display_rect());
    924 }
    925 
    926 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) {
    927   skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_);
    928   renderer->SetDrawLooper(looper.get());
    929 }
    930 
    931 // static
    932 bool RenderText::RangeContainsCaret(const ui::Range& range,
    933                                     size_t caret_pos,
    934                                     LogicalCursorDirection caret_affinity) {
    935   // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
    936   size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
    937       caret_pos - 1 : caret_pos + 1;
    938   return range.Contains(ui::Range(caret_pos, adjacent));
    939 }
    940 
    941 void RenderText::MoveCursorTo(size_t position, bool select) {
    942   size_t cursor = std::min(position, text().length());
    943   if (IsCursorablePosition(cursor))
    944     SetSelectionModel(SelectionModel(
    945         ui::Range(select ? selection().start() : cursor, cursor),
    946         (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
    947 }
    948 
    949 void RenderText::UpdateLayoutText() {
    950   layout_text_.clear();
    951 
    952   if (obscured_) {
    953     size_t obscured_text_length =
    954         static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length()));
    955     layout_text_.assign(obscured_text_length, kPasswordReplacementChar);
    956 
    957     if (obscured_reveal_index_ >= 0 &&
    958         obscured_reveal_index_ < static_cast<int>(text_.length())) {
    959       // Gets the index range in |text_| to be revealed.
    960       size_t start = obscured_reveal_index_;
    961       U16_SET_CP_START(text_.data(), 0, start);
    962       size_t end = start;
    963       UChar32 unused_char;
    964       U16_NEXT(text_.data(), end, text_.length(), unused_char);
    965 
    966       // Gets the index in |layout_text_| to be replaced.
    967       const size_t cp_start =
    968           static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, start));
    969       if (layout_text_.length() > cp_start)
    970         layout_text_.replace(cp_start, 1, text_.substr(start, end - start));
    971     }
    972   }
    973 
    974   const base::string16& text = obscured_ ? layout_text_ : text_;
    975   if (truncate_length_ > 0 && truncate_length_ < text.length()) {
    976     // Truncate the text at a valid character break and append an ellipsis.
    977     icu::StringCharacterIterator iter(text.c_str());
    978     iter.setIndex32(truncate_length_ - 1);
    979     layout_text_.assign(text.substr(0, iter.getIndex()) + ui::kEllipsisUTF16);
    980   }
    981 }
    982 
    983 void RenderText::UpdateCachedBoundsAndOffset() {
    984   if (cached_bounds_and_offset_valid_)
    985     return;
    986 
    987   // First, set the valid flag true to calculate the current cursor bounds using
    988   // the stale |display_offset_|. Applying |delta_offset| at the end of this
    989   // function will set |cursor_bounds_| and |display_offset_| to correct values.
    990   cached_bounds_and_offset_valid_ = true;
    991   cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_);
    992 
    993   // Update |display_offset_| to ensure the current cursor is visible.
    994   const int display_width = display_rect_.width();
    995   const int content_width = GetContentWidth();
    996 
    997   int delta_x = 0;
    998   if (content_width <= display_width || !cursor_enabled()) {
    999     // Don't pan if the text fits in the display width or when the cursor is
   1000     // disabled.
   1001     delta_x = -display_offset_.x();
   1002   } else if (cursor_bounds_.right() > display_rect_.right()) {
   1003     // TODO(xji): when the character overflow is a RTL character, currently, if
   1004     // we pan cursor at the rightmost position, the entered RTL character is not
   1005     // displayed. Should pan cursor to show the last logical characters.
   1006     //
   1007     // Pan to show the cursor when it overflows to the right.
   1008     delta_x = display_rect_.right() - cursor_bounds_.right();
   1009   } else if (cursor_bounds_.x() < display_rect_.x()) {
   1010     // TODO(xji): have similar problem as above when overflow character is a
   1011     // LTR character.
   1012     //
   1013     // Pan to show the cursor when it overflows to the left.
   1014     delta_x = display_rect_.x() - cursor_bounds_.x();
   1015   } else if (display_offset_.x() != 0) {
   1016     // Reduce the pan offset to show additional overflow text when the display
   1017     // width increases.
   1018     const int negate_rtl = horizontal_alignment_ == ALIGN_RIGHT ? -1 : 1;
   1019     const int offset = negate_rtl * display_offset_.x();
   1020     if (display_width > (content_width + offset)) {
   1021       delta_x = negate_rtl * (display_width - (content_width + offset));
   1022     }
   1023   }
   1024 
   1025   Vector2d delta_offset(delta_x, 0);
   1026   display_offset_ += delta_offset;
   1027   cursor_bounds_ += delta_offset;
   1028 }
   1029 
   1030 void RenderText::DrawSelection(Canvas* canvas) {
   1031   const std::vector<Rect> sel = GetSubstringBounds(selection());
   1032   for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
   1033     canvas->FillRect(*i, selection_background_focused_color_);
   1034 }
   1035 
   1036 }  // namespace gfx
   1037