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 304 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
315 | 315 |
316 SetupPangoAttributes(layout_); | 316 SetupPangoAttributes(layout_); |
317 | 317 |
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) { |
Alexei Svitkine (slow)
2013/01/29 22:07:25
Is it possible to add a test for this function?
S
msw
2013/01/31 01:48:54
Done.
| |
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 accommodate 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 BreakList<bool>::const_iterator bold = styles()[BOLD].breaks().begin(); |
331 // In Pango, different fonts means different runs, and it breaks Arabic | 331 BreakList<bool>::const_iterator italic = styles()[ITALIC].breaks().begin(); |
332 // shaping across run boundaries. So, set font only when it is different | 332 while (bold != styles()[BOLD].breaks().end() && |
333 // from the default font. | 333 italic != styles()[ITALIC].breaks().end()) { |
334 // TODO(xji): We'll eventually need to split up StyleRange into components | 334 const int style = (bold->second ? Font::BOLD : 0) | |
335 // (ColorRange, FontRange, etc.) so that we can combine adjacent ranges | 335 (italic->second ? Font::ITALIC : 0); |
336 // with the same Fonts (to avoid unnecessarily splitting up runs). | 336 const size_t bold_end = TextIndexToLayoutIndex(std::min(text().length(), |
Alexei Svitkine (slow)
2013/01/29 22:07:25
Can you add the std::min() logic to style.GetNextB
msw
2013/01/31 01:48:54
I revised BreakList to support a max value.
StyleI
| |
337 if (i->font_style != default_font_style) { | 337 styles()[BOLD].GetBreakEnd(bold))); |
338 FontList derived_font_list = font_list().DeriveFontList(i->font_style); | 338 const size_t italic_end = TextIndexToLayoutIndex(std::min(text().length(), |
339 styles()[ITALIC].GetBreakEnd(italic))); | |
340 const size_t end = std::min(bold_end, italic_end); | |
341 if (style != Font::NORMAL) { | |
Alexei Svitkine (slow)
2013/01/29 22:07:25
Shouldn't you compare it to the style of the font
msw
2013/01/31 01:48:54
Done.
| |
342 FontList derived_font_list = font_list().DeriveFontList(style); | |
339 ScopedPangoFontDescription desc(pango_font_description_from_string( | 343 ScopedPangoFontDescription desc(pango_font_description_from_string( |
340 derived_font_list.GetFontDescriptionString().c_str())); | 344 derived_font_list.GetFontDescriptionString().c_str())); |
341 | 345 |
342 PangoAttribute* pango_attr = pango_attr_font_desc_new(desc.get()); | 346 PangoAttribute* pango_attr = pango_attr_font_desc_new(desc.get()); |
343 pango_attr->start_index = TextIndexToLayoutIndex(i->range.start()); | 347 pango_attr->start_index = |
344 pango_attr->end_index = TextIndexToLayoutIndex(i->range.end()); | 348 TextIndexToLayoutIndex(std::max(bold->first, italic->first)); |
349 pango_attr->end_index = end; | |
345 pango_attr_list_insert(attrs, pango_attr); | 350 pango_attr_list_insert(attrs, pango_attr); |
346 } | 351 } |
352 bold += bold_end == end ? 1 : 0; | |
353 italic += italic_end == end ? 1 : 0; | |
347 } | 354 } |
355 DCHECK(bold == styles()[BOLD].breaks().end()); | |
356 DCHECK(italic == styles()[ITALIC].breaks().end()); | |
348 | 357 |
349 pango_layout_set_attributes(layout, attrs); | 358 pango_layout_set_attributes(layout, attrs); |
350 pango_attr_list_unref(attrs); | 359 pango_attr_list_unref(attrs); |
351 } | 360 } |
352 | 361 |
353 void RenderTextLinux::DrawVisualText(Canvas* canvas) { | 362 void RenderTextLinux::DrawVisualText(Canvas* canvas) { |
354 DCHECK(layout_); | 363 DCHECK(layout_); |
355 | 364 |
356 Vector2d offset(GetOffsetForDrawing()); | 365 Vector2d offset(GetOffsetForDrawing()); |
357 // Skia will draw glyphs with respect to the baseline. | 366 // Skia will draw glyphs with respect to the baseline. |
358 offset += Vector2d(0, PANGO_PIXELS(pango_layout_get_baseline(layout_))); | 367 offset += Vector2d(0, PANGO_PIXELS(pango_layout_get_baseline(layout_))); |
359 | 368 |
360 SkScalar x = SkIntToScalar(offset.x()); | 369 SkScalar x = SkIntToScalar(offset.x()); |
361 SkScalar y = SkIntToScalar(offset.y()); | 370 SkScalar y = SkIntToScalar(offset.y()); |
362 | 371 |
363 std::vector<SkPoint> pos; | 372 std::vector<SkPoint> pos; |
364 std::vector<uint16> glyphs; | 373 std::vector<uint16> glyphs; |
365 | 374 |
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); | 375 internal::SkiaTextRenderer renderer(canvas); |
381 ApplyFadeEffects(&renderer); | 376 ApplyFadeEffects(&renderer); |
382 ApplyTextShadows(&renderer); | 377 ApplyTextShadows(&renderer); |
383 | 378 |
384 // TODO(derat): Use font-specific params: http://crbug.com/125235 | 379 // TODO(derat): Use font-specific params: http://crbug.com/125235 |
385 const gfx::FontRenderParams& render_params = | 380 const gfx::FontRenderParams& render_params = |
386 gfx::GetDefaultFontRenderParams(); | 381 gfx::GetDefaultFontRenderParams(); |
387 const bool use_subpixel_rendering = | 382 const bool use_subpixel_rendering = |
388 render_params.subpixel_rendering != | 383 render_params.subpixel_rendering != |
389 gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE; | 384 gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE; |
390 renderer.SetFontSmoothingSettings( | 385 renderer.SetFontSmoothingSettings( |
391 render_params.antialiasing, | 386 render_params.antialiasing, |
392 use_subpixel_rendering && !background_is_transparent()); | 387 use_subpixel_rendering && !background_is_transparent()); |
393 | 388 |
389 // Temporarily apply composition underlines and selection colors. | |
390 ApplyCompositionAndSelectionStyles(); | |
391 | |
392 internal::StyleIterator style(colors(), styles()); | |
394 for (GSList* it = current_line_->runs; it; it = it->next) { | 393 for (GSList* it = current_line_->runs; it; it = it->next) { |
395 PangoLayoutRun* run = reinterpret_cast<PangoLayoutRun*>(it->data); | 394 PangoLayoutRun* run = reinterpret_cast<PangoLayoutRun*>(it->data); |
396 int glyph_count = run->glyphs->num_glyphs; | 395 int glyph_count = run->glyphs->num_glyphs; |
397 if (glyph_count == 0) | 396 if (glyph_count == 0) |
398 continue; | 397 continue; |
399 | 398 |
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( | 399 ScopedPangoFontDescription desc( |
416 pango_font_describe(run->item->analysis.font)); | 400 pango_font_describe(run->item->analysis.font)); |
417 | 401 |
418 const std::string family_name = | 402 const std::string family_name = |
419 pango_font_description_get_family(desc.get()); | 403 pango_font_description_get_family(desc.get()); |
420 renderer.SetTextSize(GetPangoFontSizeInPixels(desc.get())); | 404 renderer.SetTextSize(GetPangoFontSizeInPixels(desc.get())); |
421 | 405 |
422 SkScalar glyph_x = x; | 406 SkScalar glyph_x = x; |
423 SkScalar start_x = x; | 407 SkScalar start_x = x; |
424 int start = 0; | 408 int start = 0; |
425 | 409 |
426 glyphs.resize(glyph_count); | 410 glyphs.resize(glyph_count); |
427 pos.resize(glyph_count); | 411 pos.resize(glyph_count); |
428 | 412 |
429 for (int i = 0; i < glyph_count; ++i) { | 413 for (int i = 0; i < glyph_count; ++i) { |
430 const PangoGlyphInfo& glyph = run->glyphs->glyphs[i]; | 414 const PangoGlyphInfo& glyph = run->glyphs->glyphs[i]; |
431 glyphs[i] = static_cast<uint16>(glyph.glyph); | 415 glyphs[i] = static_cast<uint16>(glyph.glyph); |
432 // Use pango_units_to_double() rather than PANGO_PIXELS() here so that | 416 // 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 | 417 // 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), | 418 pos[i].set(glyph_x + pango_units_to_double(glyph.geometry.x_offset), |
436 y + pango_units_to_double(glyph.geometry.y_offset)); | 419 y + pango_units_to_double(glyph.geometry.y_offset)); |
437 | 420 |
438 // If this glyph is beyond the current style, draw the glyphs so far and | 421 // Find the end of the current ranged style. If this glyph is beyond the |
439 // advance to the next style. | 422 // 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]; | 423 size_t glyph_index = run->item->offset + run->glyphs->log_clusters[i]; |
441 DCHECK_GE(style, 0); | 424 const size_t style_end_index = TextIndexToLayoutIndex( |
442 DCHECK_LT(style, static_cast<int>(styles.size())); | 425 std::min(text().length(), style.GetNextBreak())); |
443 if (!IndexInRange(style_ranges_utf8[style], glyph_byte_index)) { | 426 if (glyph_index >= style_end_index) { |
444 // TODO(asvitkine): For cases like "fi", where "fi" is a single glyph | 427 // TODO(asvitkine): For cases like "fi", where "fi" is a single glyph |
445 // but can span multiple styles, Pango splits the | 428 // but can span multiple styles, Pango splits the |
446 // styles evenly over the glyph. We can do this too by | 429 // styles evenly over the glyph. We can do this too by |
447 // clipping and drawing the glyph several times. | 430 // clipping and drawing the glyph several times. |
448 renderer.SetForegroundColor(styles[style].foreground); | 431 renderer.SetForegroundColor(style.color()); |
449 renderer.SetFontFamilyWithStyle(family_name, styles[style].font_style); | 432 const int font_style = (style.style(BOLD) ? Font::BOLD : 0) | |
433 (style.style(ITALIC) ? Font::ITALIC : 0); | |
434 renderer.SetFontFamilyWithStyle(family_name, font_style); | |
450 renderer.DrawPosText(&pos[start], &glyphs[start], i - start); | 435 renderer.DrawPosText(&pos[start], &glyphs[start], i - start); |
451 if (styles[style].underline) | 436 if (style.style(UNDERLINE)) |
452 SetPangoUnderlineMetrics(desc.get(), &renderer); | 437 SetPangoUnderlineMetrics(desc.get(), &renderer); |
453 renderer.DrawDecorations(start_x, y, glyph_x - start_x, styles[style]); | 438 renderer.DrawDecorations(start_x, y, glyph_x - start_x, |
439 style.style(UNDERLINE), style.style(STRIKE), | |
440 style.style(DIAGONAL_STRIKE)); | |
454 | 441 |
455 start = i; | 442 start = i; |
456 start_x = glyph_x; | 443 start_x = glyph_x; |
457 // Loop to find the next style, in case the glyph spans multiple styles. | 444 style.UpdatePosition(LayoutIndexToTextIndex(style_end_index)); |
Alexei Svitkine (slow)
2013/01/29 22:07:25
Are you sure that |style_end_index| is correct for
msw
2013/01/31 01:48:54
You are right, my previous patch set was wrong; th
| |
458 do { | |
459 style += style_increment; | |
460 } while (style >= 0 && style < static_cast<int>(styles.size()) && | |
461 !IndexInRange(style_ranges_utf8[style], glyph_byte_index)); | |
462 } | 445 } |
463 | 446 |
464 glyph_x += pango_units_to_double(glyph.geometry.width); | 447 glyph_x += pango_units_to_double(glyph.geometry.width); |
465 } | 448 } |
466 | 449 |
467 // Draw the remaining glyphs. | 450 // Draw the remaining glyphs. |
468 renderer.SetForegroundColor(styles[style].foreground); | 451 renderer.SetForegroundColor(style.color()); |
469 renderer.SetFontFamilyWithStyle(family_name, styles[style].font_style); | 452 const int font_style = (style.style(BOLD) ? Font::BOLD : 0) | |
453 (style.style(ITALIC) ? Font::ITALIC : 0); | |
454 renderer.SetFontFamilyWithStyle(family_name, font_style); | |
470 renderer.DrawPosText(&pos[start], &glyphs[start], glyph_count - start); | 455 renderer.DrawPosText(&pos[start], &glyphs[start], glyph_count - start); |
471 if (styles[style].underline) | 456 if (style.style(UNDERLINE)) |
472 SetPangoUnderlineMetrics(desc.get(), &renderer); | 457 SetPangoUnderlineMetrics(desc.get(), &renderer); |
473 renderer.DrawDecorations(start_x, y, glyph_x - start_x, styles[style]); | 458 renderer.DrawDecorations(start_x, y, glyph_x - start_x, |
459 style.style(UNDERLINE), style.style(STRIKE), | |
460 style.style(DIAGONAL_STRIKE)); | |
461 | |
474 x = glyph_x; | 462 x = glyph_x; |
475 } | 463 } |
464 | |
465 // Undo the temporarily applied composition underlines and selection colors. | |
466 UndoCompositionAndSelectionStyles(); | |
476 } | 467 } |
477 | 468 |
478 GSList* RenderTextLinux::GetRunContainingCaret( | 469 GSList* RenderTextLinux::GetRunContainingCaret( |
479 const SelectionModel& caret) const { | 470 const SelectionModel& caret) const { |
480 size_t position = TextIndexToLayoutIndex(caret.caret_pos()); | 471 size_t position = TextIndexToLayoutIndex(caret.caret_pos()); |
481 LogicalCursorDirection affinity = caret.caret_affinity(); | 472 LogicalCursorDirection affinity = caret.caret_affinity(); |
482 GSList* run = current_line_->runs; | 473 GSList* run = current_line_->runs; |
483 while (run) { | 474 while (run) { |
484 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; | 475 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
485 ui::Range item_range(item->offset, item->offset + item->length); | 476 ui::Range item_range(item->offset, item->offset + item->length); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
536 if (selection_visual_bounds_.empty()) | 527 if (selection_visual_bounds_.empty()) |
537 selection_visual_bounds_ = CalculateSubstringBounds(selection()); | 528 selection_visual_bounds_ = CalculateSubstringBounds(selection()); |
538 return selection_visual_bounds_; | 529 return selection_visual_bounds_; |
539 } | 530 } |
540 | 531 |
541 RenderText* RenderText::CreateInstance() { | 532 RenderText* RenderText::CreateInstance() { |
542 return new RenderTextLinux; | 533 return new RenderTextLinux; |
543 } | 534 } |
544 | 535 |
545 } // namespace gfx | 536 } // namespace gfx |
OLD | NEW |