Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gfx/pango_util.h" | 5 #include "ui/gfx/pango_util.h" |
| 6 | 6 |
| 7 #include <cairo/cairo.h> | 7 #include <cairo/cairo.h> |
| 8 #include <pango/pango.h> | 8 #include <pango/pango.h> |
| 9 #include <pango/pangocairo.h> | 9 #include <pango/pangocairo.h> |
| 10 | 10 |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
| 13 #include "ui/gfx/canvas.h" | 13 #include "ui/gfx/canvas.h" |
| 14 #include "ui/gfx/font.h" | 14 #include "ui/gfx/font.h" |
| 15 #include "ui/gfx/platform_font_pango.h" | |
| 16 #include "ui/gfx/rect.h" | |
| 15 | 17 |
| 16 #if !defined(USE_WAYLAND) && defined(TOOLKIT_USES_GTK) | 18 #if !defined(USE_WAYLAND) && defined(TOOLKIT_USES_GTK) |
| 17 #include <gtk/gtk.h> | 19 #include <gtk/gtk.h> |
| 18 #include "ui/gfx/gtk_util.h" | 20 #include "ui/gfx/gtk_util.h" |
| 19 #else | 21 #else |
| 20 #include "ui/gfx/linux_util.h" | 22 #include "ui/gfx/linux_util.h" |
| 21 #endif | 23 #endif |
| 22 | 24 |
| 23 #include "ui/gfx/skia_util.h" | 25 #include "ui/gfx/skia_util.h" |
| 24 | 26 |
| 25 namespace { | 27 namespace { |
| 26 | 28 |
| 27 // Marker for accelerators in the text. | 29 // Marker for accelerators in the text. |
| 28 const gunichar kAcceleratorChar = '&'; | 30 const gunichar kAcceleratorChar = '&'; |
| 29 | 31 |
| 32 // Multiply by the text height to determine how much text should be faded | |
| 33 // when elliding. | |
| 34 const double kFadeWidthFactor = 1.5; | |
| 35 | |
| 36 // End state of the elliding fade. | |
| 37 const double kFadeFinalAlpha = 0.15; | |
| 38 | |
| 30 // Return |cairo_font_options|. If needed, allocate and update it based on | 39 // Return |cairo_font_options|. If needed, allocate and update it based on |
| 31 // GtkSettings. | 40 // GtkSettings. |
| 32 cairo_font_options_t* GetCairoFontOptions() { | 41 cairo_font_options_t* GetCairoFontOptions() { |
| 33 // Font settings that we initialize once and then use when drawing text. | 42 // Font settings that we initialize once and then use when drawing text. |
| 34 static cairo_font_options_t* cairo_font_options = NULL; | 43 static cairo_font_options_t* cairo_font_options = NULL; |
| 35 | 44 |
| 36 if (cairo_font_options) | 45 if (cairo_font_options) |
| 37 return cairo_font_options; | 46 return cairo_font_options; |
| 38 | 47 |
| 39 cairo_font_options = cairo_font_options_create(); | 48 cairo_font_options = cairo_font_options_create(); |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 100 if (rgba_style) | 109 if (rgba_style) |
| 101 g_free(rgba_style); | 110 g_free(rgba_style); |
| 102 | 111 |
| 103 return cairo_font_options; | 112 return cairo_font_options; |
| 104 } | 113 } |
| 105 | 114 |
| 106 } // namespace | 115 } // namespace |
| 107 | 116 |
| 108 namespace gfx { | 117 namespace gfx { |
| 109 | 118 |
| 119 void DrawTextOntoCairoSurface(cairo_t* cr, | |
| 120 const string16& text, | |
| 121 const gfx::Font& font, | |
| 122 const gfx::Rect& bounds, | |
| 123 const gfx::Rect& clip, | |
| 124 const SkColor& text_color, | |
| 125 int flags) { | |
| 126 PangoLayout* layout = pango_cairo_create_layout(cr); | |
| 127 base::i18n::TextDirection text_direction = | |
| 128 base::i18n::GetFirstStrongCharacterDirection(text); | |
| 129 Rect text_rect(bounds.x(), bounds.y(), 0, 0); | |
| 130 DCHECK(!bounds.IsEmpty()); | |
| 131 | |
| 132 gfx::SetupPangoLayout( | |
| 133 layout, text, font, bounds.width(), text_direction, flags); | |
| 134 | |
| 135 pango_layout_set_height(layout, bounds.height() * PANGO_SCALE); | |
| 136 | |
| 137 cairo_save(cr); | |
| 138 cairo_rectangle(cr, clip.x(), clip.y(), clip.width(), clip.height()); | |
| 139 cairo_clip(cr); | |
| 140 | |
| 141 AdjustTextRectBasedOnLayout(layout, bounds, flags, &text_rect); | |
| 142 | |
| 143 DrawPangoLayout(cr, layout, font, bounds, text_rect, | |
| 144 text_color, text_direction, flags); | |
| 145 | |
| 146 cairo_restore(cr); | |
| 147 g_object_unref(layout); | |
| 148 } | |
| 149 | |
|
Evan Stade
2011/10/26 21:46:05
^H
| |
| 150 | |
| 110 // Pass a width greater than 0 to force wrapping and eliding. | 151 // Pass a width greater than 0 to force wrapping and eliding. |
| 111 void SetupPangoLayout(PangoLayout* layout, | 152 void SetupPangoLayout(PangoLayout* layout, |
| 112 const string16& text, | 153 const string16& text, |
| 113 const Font& font, | 154 const Font& font, |
| 114 int width, | 155 int width, |
| 115 base::i18n::TextDirection text_direction, | 156 base::i18n::TextDirection text_direction, |
| 116 int flags) { | 157 int flags) { |
| 117 cairo_font_options_t* cairo_font_options = GetCairoFontOptions(); | 158 cairo_font_options_t* cairo_font_options = GetCairoFontOptions(); |
| 118 // This needs to be done early on; it has no effect when called just before | 159 // This needs to be done early on; it has no effect when called just before |
| 119 // pango_cairo_show_layout(). | 160 // pango_cairo_show_layout(). |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 NULL, NULL); | 227 NULL, NULL); |
| 187 utf8 = UTF16ToUTF8(accelerator_removed); | 228 utf8 = UTF16ToUTF8(accelerator_removed); |
| 188 } else { | 229 } else { |
| 189 utf8 = UTF16ToUTF8(text); | 230 utf8 = UTF16ToUTF8(text); |
| 190 } | 231 } |
| 191 | 232 |
| 192 pango_layout_set_text(layout, utf8.data(), utf8.size()); | 233 pango_layout_set_text(layout, utf8.data(), utf8.size()); |
| 193 } | 234 } |
| 194 } | 235 } |
| 195 | 236 |
| 237 void AdjustTextRectBasedOnLayout(PangoLayout* layout, | |
| 238 const gfx::Rect& bounds, | |
| 239 int flags, | |
| 240 gfx::Rect* text_rect) { | |
| 241 int text_width, text_height; | |
| 242 pango_layout_get_pixel_size(layout, &text_width, &text_height); | |
| 243 text_rect->set_width(text_width); | |
| 244 text_rect->set_height(text_height); | |
| 245 | |
| 246 if (flags & gfx::Canvas::TEXT_VALIGN_TOP) { | |
| 247 // Cairo should draw from the top left corner already. | |
| 248 } else if (flags & gfx::Canvas::TEXT_VALIGN_BOTTOM) { | |
| 249 text_rect->set_y(text_rect->y() + bounds.height() - text_rect->height()); | |
| 250 } else { | |
| 251 // Vertically centered. | |
| 252 text_rect->set_y(text_rect->y() + | |
| 253 ((bounds.height() - text_rect->height()) / 2)); | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 void DrawPangoLayout(cairo_t* cr, | |
| 258 PangoLayout* layout, | |
| 259 const Font& font, | |
| 260 const gfx::Rect& bounds, | |
| 261 const gfx::Rect& text_rect, | |
| 262 const SkColor& text_color, | |
| 263 base::i18n::TextDirection text_direction, | |
| 264 int flags) { | |
| 265 double r = SkColorGetR(text_color) / 255.0, | |
| 266 g = SkColorGetG(text_color) / 255.0, | |
| 267 b = SkColorGetB(text_color) / 255.0, | |
| 268 a = SkColorGetA(text_color) / 255.0; | |
| 269 | |
| 270 cairo_pattern_t* pattern = NULL; | |
| 271 | |
| 272 cairo_save(cr); | |
| 273 | |
| 274 // If we're not eliding, use a fixed color. | |
| 275 // Otherwise, create a gradient pattern to use as the source. | |
| 276 if (text_direction == base::i18n::RIGHT_TO_LEFT || | |
| 277 (flags & gfx::Canvas::NO_ELLIPSIS) || | |
| 278 text_rect.width() <= bounds.width()) { | |
| 279 cairo_set_source_rgba(cr, r, g, b, a); | |
| 280 } else { | |
| 281 // Fade to semi-transparent to elide. | |
| 282 int fade_width = static_cast<double>(text_rect.height()) * kFadeWidthFactor; | |
| 283 if (fade_width > (bounds.width() / 2)) { | |
| 284 // Don't fade more than half the text. | |
| 285 fade_width = bounds.width() / 2; | |
| 286 } | |
| 287 int fade_x = bounds.x() + bounds.width() - fade_width; | |
| 288 | |
| 289 pattern = cairo_pattern_create_linear( | |
| 290 fade_x, bounds.y(), bounds.x() + bounds.width(), bounds.y()); | |
| 291 cairo_pattern_add_color_stop_rgba(pattern, 0, r, g, b, a); | |
| 292 cairo_pattern_add_color_stop_rgba(pattern, 1, r, g, b, kFadeFinalAlpha); | |
| 293 cairo_set_source(cr, pattern); | |
| 294 } | |
| 295 | |
| 296 cairo_move_to(cr, text_rect.x(), text_rect.y()); | |
| 297 pango_cairo_show_layout(cr, layout); | |
| 298 | |
| 299 if (font.GetStyle() & gfx::Font::UNDERLINED) { | |
| 300 gfx::PlatformFontPango* platform_font = | |
| 301 static_cast<gfx::PlatformFontPango*>(font.platform_font()); | |
| 302 DrawPangoTextUnderline(cr, platform_font, 0.0, text_rect); | |
| 303 } | |
| 304 | |
| 305 if (pattern) | |
| 306 cairo_pattern_destroy(pattern); | |
| 307 | |
| 308 cairo_restore(cr); | |
| 309 } | |
| 310 | |
| 311 void DrawPangoTextUnderline(cairo_t* cr, | |
| 312 gfx::PlatformFontPango* platform_font, | |
| 313 double extra_edge_width, | |
| 314 const Rect& text_rect) { | |
| 315 const double underline_y = | |
| 316 static_cast<double>(text_rect.y()) + text_rect.height() + | |
| 317 platform_font->underline_position(); | |
| 318 cairo_set_line_width( | |
| 319 cr, platform_font->underline_thickness() + 2 * extra_edge_width); | |
| 320 cairo_move_to(cr, | |
| 321 text_rect.x() - extra_edge_width, | |
| 322 underline_y); | |
| 323 cairo_line_to(cr, | |
| 324 text_rect.x() + text_rect.width() + extra_edge_width, | |
| 325 underline_y); | |
| 326 cairo_stroke(cr); | |
| 327 } | |
| 328 | |
| 196 } // namespace gfx | 329 } // namespace gfx |
| OLD | NEW |