| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/render_text_linux.h" | 5 #include "ui/gfx/render_text_linux.h" |
| 6 | 6 |
| 7 #include <pango/pangocairo.h> | 7 #include <pango/pangocairo.h> |
| 8 #include <algorithm> | 8 #include <algorithm> |
| 9 #include <string> | 9 #include <string> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 318 current_line_ = pango_layout_get_line_readonly(layout_, 0); | 318 current_line_ = pango_layout_get_line_readonly(layout_, 0); |
| 319 pango_layout_line_ref(current_line_); | 319 pango_layout_line_ref(current_line_); |
| 320 | 320 |
| 321 pango_layout_get_log_attrs(layout_, &log_attrs_, &num_log_attrs_); | 321 pango_layout_get_log_attrs(layout_, &log_attrs_, &num_log_attrs_); |
| 322 } | 322 } |
| 323 } | 323 } |
| 324 | 324 |
| 325 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) { | 325 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) { |
| 326 PangoAttrList* attrs = pango_attr_list_new(); | 326 PangoAttrList* attrs = pango_attr_list_new(); |
| 327 | 327 |
| 328 int default_font_style = font_list().GetFontStyle(); | 328 // Splitting text runs to accomodate styling can break Arabic glyph shaping. |
| 329 for (StyleRanges::const_iterator i = style_ranges().begin(); | 329 // Only split text runs as needed for bold and italic font styles changes. |
| 330 i < style_ranges().end(); ++i) { | 330 StyleBreaks::const_iterator bold = styles(BOLD).begin(); |
| 331 // In Pango, different fonts means different runs, and it breaks Arabic | 331 StyleBreaks::const_iterator italic = styles(ITALIC).begin(); |
| 332 // shaping across run boundaries. So, set font only when it is different | 332 while (bold != styles(BOLD).end() && italic != styles(ITALIC).end()) { |
| 333 // from the default font. | 333 const int style = (bold->second ? Font::BOLD : 0) | |
| 334 // TODO(xji): We'll eventually need to split up StyleRange into components | 334 (italic->second ? Font::ITALIC : 0); |
| 335 // (ColorRange, FontRange, etc.) so that we can combine adjacent ranges | 335 const size_t bold_end = GetBreakEnd(styles(BOLD), bold); |
| 336 // with the same Fonts (to avoid unnecessarily splitting up runs). | 336 const size_t italic_end = GetBreakEnd(styles(ITALIC), italic); |
| 337 if (i->font_style != default_font_style) { | 337 const size_t end = std::min(bold_end, italic_end); |
| 338 FontList derived_font_list = font_list().DeriveFontList(i->font_style); | 338 if (style != Font::NORMAL) { |
| 339 FontList derived_font_list = font_list().DeriveFontList(style); |
| 339 ScopedPangoFontDescription desc(pango_font_description_from_string( | 340 ScopedPangoFontDescription desc(pango_font_description_from_string( |
| 340 derived_font_list.GetFontDescriptionString().c_str())); | 341 derived_font_list.GetFontDescriptionString().c_str())); |
| 341 | 342 |
| 342 PangoAttribute* pango_attr = pango_attr_font_desc_new(desc.get()); | 343 PangoAttribute* pango_attr = pango_attr_font_desc_new(desc.get()); |
| 343 pango_attr->start_index = TextIndexToLayoutIndex(i->range.start()); | 344 pango_attr->start_index = |
| 344 pango_attr->end_index = TextIndexToLayoutIndex(i->range.end()); | 345 TextIndexToLayoutIndex(std::max(bold->first, italic->first)); |
| 346 pango_attr->end_index = TextIndexToLayoutIndex(end); |
| 345 pango_attr_list_insert(attrs, pango_attr); | 347 pango_attr_list_insert(attrs, pango_attr); |
| 346 } | 348 } |
| 349 bold += bold_end == end ? 1 : 0; |
| 350 italic += italic_end == end ? 1 : 0; |
| 347 } | 351 } |
| 352 DCHECK(bold == styles(BOLD).end()); |
| 353 DCHECK(italic == styles(ITALIC).end()); |
| 348 | 354 |
| 349 pango_layout_set_attributes(layout, attrs); | 355 pango_layout_set_attributes(layout, attrs); |
| 350 pango_attr_list_unref(attrs); | 356 pango_attr_list_unref(attrs); |
| 351 } | 357 } |
| 352 | 358 |
| 353 void RenderTextLinux::DrawVisualText(Canvas* canvas) { | 359 void RenderTextLinux::DrawVisualText(Canvas* canvas) { |
| 354 DCHECK(layout_); | 360 DCHECK(layout_); |
| 355 | 361 |
| 356 Vector2d offset(GetOffsetForDrawing()); | 362 Vector2d offset(GetOffsetForDrawing()); |
| 357 // Skia will draw glyphs with respect to the baseline. | 363 // Skia will draw glyphs with respect to the baseline. |
| 358 offset += Vector2d(0, PANGO_PIXELS(pango_layout_get_baseline(layout_))); | 364 offset += Vector2d(0, PANGO_PIXELS(pango_layout_get_baseline(layout_))); |
| 359 | 365 |
| 360 SkScalar x = SkIntToScalar(offset.x()); | 366 SkScalar x = SkIntToScalar(offset.x()); |
| 361 SkScalar y = SkIntToScalar(offset.y()); | 367 SkScalar y = SkIntToScalar(offset.y()); |
| 362 | 368 |
| 363 std::vector<SkPoint> pos; | 369 std::vector<SkPoint> pos; |
| 364 std::vector<uint16> glyphs; | 370 std::vector<uint16> glyphs; |
| 365 | 371 |
| 366 StyleRanges styles(style_ranges()); | |
| 367 ApplyCompositionAndSelectionStyles(&styles); | |
| 368 | |
| 369 // Pre-calculate UTF8 indices from UTF16 indices. | |
| 370 // TODO(asvitkine): Can we cache these? | |
| 371 std::vector<ui::Range> style_ranges_utf8; | |
| 372 style_ranges_utf8.reserve(styles.size()); | |
| 373 size_t start_index = 0; | |
| 374 for (size_t i = 0; i < styles.size(); ++i) { | |
| 375 size_t end_index = TextIndexToLayoutIndex(styles[i].range.end()); | |
| 376 style_ranges_utf8.push_back(ui::Range(start_index, end_index)); | |
| 377 start_index = end_index; | |
| 378 } | |
| 379 | |
| 380 internal::SkiaTextRenderer renderer(canvas); | 372 internal::SkiaTextRenderer renderer(canvas); |
| 381 ApplyFadeEffects(&renderer); | 373 ApplyFadeEffects(&renderer); |
| 382 ApplyTextShadows(&renderer); | 374 ApplyTextShadows(&renderer); |
| 383 | 375 |
| 384 // TODO(derat): Use font-specific params: http://crbug.com/125235 | 376 // TODO(derat): Use font-specific params: http://crbug.com/125235 |
| 385 const gfx::FontRenderParams& render_params = | 377 const gfx::FontRenderParams& render_params = |
| 386 gfx::GetDefaultFontRenderParams(); | 378 gfx::GetDefaultFontRenderParams(); |
| 387 const bool use_subpixel_rendering = | 379 const bool use_subpixel_rendering = |
| 388 render_params.subpixel_rendering != | 380 render_params.subpixel_rendering != |
| 389 gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE; | 381 gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE; |
| 390 renderer.SetFontSmoothingSettings( | 382 renderer.SetFontSmoothingSettings( |
| 391 render_params.antialiasing, | 383 render_params.antialiasing, |
| 392 use_subpixel_rendering && !background_is_transparent()); | 384 use_subpixel_rendering && !background_is_transparent()); |
| 393 | 385 |
| 386 // Adjust the text colors to reflect the selection range. |
| 387 ColorBreaks adjusted_colors(colors()); |
| 388 ApplySelectionColor(&adjusted_colors); |
| 389 |
| 390 // Adjust the underline styling to reflect composition ranges. |
| 391 const StyleBreaks* adjusted_styles[NUM_TEXT_STYLES]; |
| 392 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i) |
| 393 adjusted_styles[i] = &styles(static_cast<TextStyle>(i)); |
| 394 StyleBreaks adjusted_underlines(styles(UNDERLINE)); |
| 395 ApplyCompositionStyle(&adjusted_underlines); |
| 396 adjusted_styles[UNDERLINE] = &adjusted_underlines; |
| 397 |
| 398 // Track the current color and style with iterators. |
| 399 ColorBreaks::const_iterator color = adjusted_colors.begin(); |
| 400 StyleBreaks::const_iterator style[NUM_TEXT_STYLES]; |
| 401 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i) |
| 402 style[i] = adjusted_styles[i]->begin(); |
| 403 size_t style_end[NUM_TEXT_STYLES]; |
| 404 |
| 394 for (GSList* it = current_line_->runs; it; it = it->next) { | 405 for (GSList* it = current_line_->runs; it; it = it->next) { |
| 395 PangoLayoutRun* run = reinterpret_cast<PangoLayoutRun*>(it->data); | 406 PangoLayoutRun* run = reinterpret_cast<PangoLayoutRun*>(it->data); |
| 396 int glyph_count = run->glyphs->num_glyphs; | 407 int glyph_count = run->glyphs->num_glyphs; |
| 397 if (glyph_count == 0) | 408 if (glyph_count == 0) |
| 398 continue; | 409 continue; |
| 399 | 410 |
| 400 size_t run_start = run->item->offset; | |
| 401 size_t first_glyph_byte_index = run_start + run->glyphs->log_clusters[0]; | |
| 402 size_t style_increment = IsForwardMotion(CURSOR_RIGHT, run->item) ? 1 : -1; | |
| 403 | |
| 404 // Find the initial style for this run. | |
| 405 // TODO(asvitkine): Can we avoid looping here, e.g. by caching this per run? | |
| 406 int style = -1; | |
| 407 for (size_t i = 0; i < style_ranges_utf8.size(); ++i) { | |
| 408 if (IndexInRange(style_ranges_utf8[i], first_glyph_byte_index)) { | |
| 409 style = i; | |
| 410 break; | |
| 411 } | |
| 412 } | |
| 413 DCHECK_GE(style, 0); | |
| 414 | |
| 415 ScopedPangoFontDescription desc( | 411 ScopedPangoFontDescription desc( |
| 416 pango_font_describe(run->item->analysis.font)); | 412 pango_font_describe(run->item->analysis.font)); |
| 417 | 413 |
| 418 const std::string family_name = | 414 const std::string family_name = |
| 419 pango_font_description_get_family(desc.get()); | 415 pango_font_description_get_family(desc.get()); |
| 420 renderer.SetTextSize(GetPangoFontSizeInPixels(desc.get())); | 416 renderer.SetTextSize(GetPangoFontSizeInPixels(desc.get())); |
| 421 | 417 |
| 422 SkScalar glyph_x = x; | 418 SkScalar glyph_x = x; |
| 423 SkScalar start_x = x; | 419 SkScalar start_x = x; |
| 424 int start = 0; | 420 int start = 0; |
| 425 | 421 |
| 426 glyphs.resize(glyph_count); | 422 glyphs.resize(glyph_count); |
| 427 pos.resize(glyph_count); | 423 pos.resize(glyph_count); |
| 428 | 424 |
| 429 for (int i = 0; i < glyph_count; ++i) { | 425 for (int i = 0; i < glyph_count; ++i) { |
| 430 const PangoGlyphInfo& glyph = run->glyphs->glyphs[i]; | 426 const PangoGlyphInfo& glyph = run->glyphs->glyphs[i]; |
| 431 glyphs[i] = static_cast<uint16>(glyph.glyph); | 427 glyphs[i] = static_cast<uint16>(glyph.glyph); |
| 432 // Use pango_units_to_double() rather than PANGO_PIXELS() here so that | 428 // Use pango_units_to_double() rather than PANGO_PIXELS() here so units |
| 433 // units won't get rounded to the pixel grid if we're using subpixel | 429 // are not rounded to the pixel grid if subpixel positioning is enabled. |
| 434 // positioning. | |
| 435 pos[i].set(glyph_x + pango_units_to_double(glyph.geometry.x_offset), | 430 pos[i].set(glyph_x + pango_units_to_double(glyph.geometry.x_offset), |
| 436 y + pango_units_to_double(glyph.geometry.y_offset)); | 431 y + pango_units_to_double(glyph.geometry.y_offset)); |
| 437 | 432 |
| 438 // If this glyph is beyond the current style, draw the glyphs so far and | 433 // Find the end of the current ranged style. If this glyph is beyond the |
| 439 // advance to the next style. | 434 // current style, draw the glyphs so far and advance to the next style. |
| 440 size_t glyph_byte_index = run_start + run->glyphs->log_clusters[i]; | 435 size_t glyph_index = run->item->offset + run->glyphs->log_clusters[i]; |
| 441 DCHECK_GE(style, 0); | 436 const size_t color_end = GetBreakEnd(adjusted_colors, color); |
| 442 DCHECK_LT(style, static_cast<int>(styles.size())); | 437 size_t style_end_index = color_end; |
| 443 if (!IndexInRange(style_ranges_utf8[style], glyph_byte_index)) { | 438 for (size_t j = 0; j < NUM_TEXT_STYLES; ++j) { |
| 439 style_end[j] = GetBreakEnd(*adjusted_styles[j], style[j]); |
| 440 style_end_index = std::min(style_end_index, style_end[j]); |
| 441 } |
| 442 if (glyph_index >= TextIndexToLayoutIndex(style_end_index)) { |
| 444 // TODO(asvitkine): For cases like "fi", where "fi" is a single glyph | 443 // TODO(asvitkine): For cases like "fi", where "fi" is a single glyph |
| 445 // but can span multiple styles, Pango splits the | 444 // but can span multiple styles, Pango splits the |
| 446 // styles evenly over the glyph. We can do this too by | 445 // styles evenly over the glyph. We can do this too by |
| 447 // clipping and drawing the glyph several times. | 446 // clipping and drawing the glyph several times. |
| 448 renderer.SetForegroundColor(styles[style].foreground); | 447 renderer.SetForegroundColor(color->second); |
| 449 renderer.SetFontFamilyWithStyle(family_name, styles[style].font_style); | 448 const int font_style = (style[BOLD]->second ? Font::BOLD : 0) | |
| 449 (style[ITALIC]->second ? Font::ITALIC : 0); |
| 450 renderer.SetFontFamilyWithStyle(family_name, font_style); |
| 450 renderer.DrawPosText(&pos[start], &glyphs[start], i - start); | 451 renderer.DrawPosText(&pos[start], &glyphs[start], i - start); |
| 451 if (styles[style].underline) | 452 if (style[UNDERLINE]->second) |
| 452 SetPangoUnderlineMetrics(desc.get(), &renderer); | 453 SetPangoUnderlineMetrics(desc.get(), &renderer); |
| 453 renderer.DrawDecorations(start_x, y, glyph_x - start_x, styles[style]); | 454 renderer.DrawDecorations(start_x, y, glyph_x - start_x, |
| 455 style[UNDERLINE]->second, style[STRIKE]->second, |
| 456 style[DIAGONAL_STRIKE]->second); |
| 454 | 457 |
| 455 start = i; | 458 start = i; |
| 456 start_x = glyph_x; | 459 start_x = glyph_x; |
| 457 // Loop to find the next style, in case the glyph spans multiple styles. | 460 |
| 458 do { | 461 color += color_end == style_end_index ? 1 : 0; |
| 459 style += style_increment; | 462 for (size_t j = 0; j < NUM_TEXT_STYLES; ++j) |
| 460 } while (style >= 0 && style < static_cast<int>(styles.size()) && | 463 style[j] += style_end[j] == style_end_index ? 1 : 0; |
| 461 !IndexInRange(style_ranges_utf8[style], glyph_byte_index)); | |
| 462 } | 464 } |
| 463 | 465 |
| 464 glyph_x += pango_units_to_double(glyph.geometry.width); | 466 glyph_x += pango_units_to_double(glyph.geometry.width); |
| 465 } | 467 } |
| 466 | 468 |
| 467 // Draw the remaining glyphs. | 469 // Draw the remaining glyphs. |
| 468 renderer.SetForegroundColor(styles[style].foreground); | 470 renderer.SetForegroundColor(color->second); |
| 469 renderer.SetFontFamilyWithStyle(family_name, styles[style].font_style); | 471 const int font_style = (style[BOLD]->second ? Font::BOLD : 0) | |
| 472 (style[ITALIC]->second ? Font::ITALIC : 0); |
| 473 renderer.SetFontFamilyWithStyle(family_name, font_style); |
| 470 renderer.DrawPosText(&pos[start], &glyphs[start], glyph_count - start); | 474 renderer.DrawPosText(&pos[start], &glyphs[start], glyph_count - start); |
| 471 if (styles[style].underline) | 475 if (style[UNDERLINE]->second) |
| 472 SetPangoUnderlineMetrics(desc.get(), &renderer); | 476 SetPangoUnderlineMetrics(desc.get(), &renderer); |
| 473 renderer.DrawDecorations(start_x, y, glyph_x - start_x, styles[style]); | 477 renderer.DrawDecorations(start_x, y, glyph_x - start_x, |
| 478 style[UNDERLINE]->second, style[STRIKE]->second, |
| 479 style[DIAGONAL_STRIKE]->second); |
| 480 |
| 474 x = glyph_x; | 481 x = glyph_x; |
| 475 } | 482 } |
| 476 } | 483 } |
| 477 | 484 |
| 478 GSList* RenderTextLinux::GetRunContainingCaret( | 485 GSList* RenderTextLinux::GetRunContainingCaret( |
| 479 const SelectionModel& caret) const { | 486 const SelectionModel& caret) const { |
| 480 size_t position = TextIndexToLayoutIndex(caret.caret_pos()); | 487 size_t position = TextIndexToLayoutIndex(caret.caret_pos()); |
| 481 LogicalCursorDirection affinity = caret.caret_affinity(); | 488 LogicalCursorDirection affinity = caret.caret_affinity(); |
| 482 GSList* run = current_line_->runs; | 489 GSList* run = current_line_->runs; |
| 483 while (run) { | 490 while (run) { |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 536 if (selection_visual_bounds_.empty()) | 543 if (selection_visual_bounds_.empty()) |
| 537 selection_visual_bounds_ = CalculateSubstringBounds(selection()); | 544 selection_visual_bounds_ = CalculateSubstringBounds(selection()); |
| 538 return selection_visual_bounds_; | 545 return selection_visual_bounds_; |
| 539 } | 546 } |
| 540 | 547 |
| 541 RenderText* RenderText::CreateInstance() { | 548 RenderText* RenderText::CreateInstance() { |
| 542 return new RenderTextLinux; | 549 return new RenderTextLinux; |
| 543 } | 550 } |
| 544 | 551 |
| 545 } // namespace gfx | 552 } // namespace gfx |
| OLD | NEW |