Commit 36970fe8 authored by Fernando Serboncini's avatar Fernando Serboncini Committed by Commit Bot

Add support for mid ligature positions

Makes XPositionForOffset and CharacterIndexForPosition support
return results inside a ligature glyph (while still not breaking ZWJs)
by using a character-based TextBreakIterator inside glyphs.

Reland of 1e3235b3

Bug: 473476
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng
Change-Id: I80348a10044edc89f0e85789c71dc17a56f6393d
Reviewed-on: https://chromium-review.googlesource.com/1140275
Commit-Queue: Fernando Serboncini <fserb@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#575822}
parent a4b35d74
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x600
LayoutBlockFlow {HTML} at (0,0) size 800x600
LayoutBlockFlow {BODY} at (8,8) size 784x584
LayoutBlockFlow {P} at (0,0) size 784x40
LayoutText {#text} at (0,0) size 212x19
text run at (0,0) width 212: "This tests for a regression against "
LayoutInline {I} at (0,0) size 767x39
LayoutInline {A} at (0,0) size 348x19 [color=#0000EE]
LayoutText {#text} at (212,0) size 348x19
text run at (212,0) width 348: "http://bugzilla.opendarwin.org/show_bug.cgi?id=6673"
LayoutText {#text} at (559,0) size 767x39
text run at (559,0) width 5: " "
text run at (563,0) width 204: "Selection highlight doesn't scroll"
text run at (0,20) width 369: "along with an image contained in an overflow:scroll block"
LayoutText {#text} at (368,20) size 5x19
text run at (368,20) width 5: "."
LayoutBlockFlow {P} at (0,56) size 784x40
LayoutText {#text} at (0,0) size 748x39
text run at (0,0) width 412: "There should be one contiguous highlight from \x{201C}elit\x{201D} to \x{201C}Etiam\x{201D}, "
text run at (412,0) width 336: "including the orange square, and no other highlighted"
text run at (0,20) width 36: "areas."
LayoutBlockFlow {HR} at (0,112) size 784x2 [border: (1px inset #EEEEEE)]
layer at (8,130) size 100x200 clip at (8,130) size 85x200 scrollY 40.00 scrollHeight 320
LayoutBlockFlow {DIV} at (0,122) size 100x200
LayoutText {#text} at (0,0) size 84x119
text run at (0,0) width 84: "Lorem ipsum"
text run at (0,20) width 51: "dolor sit"
text run at (0,40) width 34: "amet,"
text run at (0,60) width 78: "consectetuer"
text run at (0,80) width 64: "adipiscing"
text run at (0,100) width 27: "elit. "
LayoutImage {IMG} at (27,105) size 10x10
LayoutText {#text} at (37,100) size 83x219
text run at (37,100) width 4: " "
text run at (41,100) width 37: "Etiam"
text run at (0,120) width 57: "et ipsum."
text run at (0,140) width 31: "Nam"
text run at (0,160) width 78: "consectetuer"
text run at (0,180) width 81: "mi eget velit."
text run at (0,200) width 83: "Sed nec risus"
text run at (0,220) width 60: "vitae felis"
text run at (0,240) width 39: "auctor"
text run at (0,260) width 53: "ultricies."
text run at (0,280) width 79: "Pellentesque"
text run at (0,300) width 54: "aliquet..."
selection start: position 58 of child 0 {#text} of child 7 {DIV} of body
selection end: position 11 of child 2 {#text} of child 7 {DIV} of body
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x600
LayoutBlockFlow {HTML} at (0,0) size 800x600
LayoutBlockFlow {BODY} at (8,8) size 784x576
LayoutBlockFlow {P} at (0,0) size 784x40
LayoutText {#text} at (0,0) size 52x19
text run at (0,0) width 52: "Test for "
LayoutInline {I} at (0,0) size 749x39
LayoutInline {A} at (0,0) size 299x19 [color=#0000EE]
LayoutText {#text} at (51,0) size 299x19
text run at (51,0) width 299: "http://bugs.webkit.org/show_bug.cgi?id=11124"
LayoutText {#text} at (349,0) size 749x39
text run at (349,0) width 5: " "
text run at (353,0) width 396: "REGRESSION (r14297): No drag image for partially-selected"
text run at (0,20) width 79: "complex text"
LayoutText {#text} at (79,20) size 4x19
text run at (79,20) width 4: "."
LayoutBlockFlow {P} at (0,56) size 784x20
LayoutText {#text} at (0,0) size 144x19
text run at (0,0) width 144: "This should look like \x{201C}"
LayoutInline {SPAN} at (0,0) size 84x19 [color=#008000]
LayoutText {#text} at (144,0) size 84x19
text run at (144,0) width 84: "Lore\x{300}m ipsum"
LayoutText {#text} at (228,0) size 15x19
text run at (228,0) width 15: "\x{201D}: "
LayoutInline {SPAN} at (0,0) size 88x19
LayoutText {#text} at (243,0) size 88x19
text run at (243,0) width 88: " Lore\x{300}m ipsum"
LayoutText {#text} at (0,0) size 0x0
selection start: position 1 of child 0 {#text} of child 3 {SPAN} of child 2 {P} of body
selection end: position 13 of child 0 {#text} of child 3 {SPAN} of child 2 {P} of body
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x600
LayoutBlockFlow {HTML} at (0,0) size 800x600
LayoutBlockFlow {BODY} at (8,8) size 784x584
LayoutBlockFlow {P} at (0,0) size 784x20
LayoutText {#text} at (0,0) size 106x19
text run at (0,0) width 106: "Test for revision "
LayoutInline {A} at (0,0) size 49x19 [color=#0000EE]
LayoutText {#text} at (105,0) size 49x19
text run at (105,0) width 49: "#20574"
LayoutText {#text} at (153,0) size 5x19
text run at (153,0) width 5: "."
LayoutBlockFlow {P} at (0,36) size 784x20
LayoutText {#text} at (0,0) size 251x19
text run at (0,0) width 251: "The two blue boxes should be identical."
LayoutBlockFlow {DIV} at (0,72) size 106x46 [border: (3px solid #0000FF)]
LayoutText {#text} at (3,3) size 10x19
text run at (3,3) width 10: "L"
LayoutInline {SPAN} at (0,0) size 40x19 [color=#008000] [bgcolor=#FFFF00]
LayoutText {#text} at (13,3) size 40x19
text run at (13,3) width 40: "o r"
LayoutText {#text} at (53,3) size 100x39
text run at (53,3) width 50: "e mi"
text run at (3,23) width 67: "psumdolor"
LayoutBlockFlow (anonymous) at (0,118) size 784x20
LayoutBR {BR} at (0,0) size 0x19
LayoutBlockFlow {DIV} at (0,138) size 106x46 [border: (3px solid #0000FF)]
LayoutText {#text} at (3,3) size 100x39
text run at (3,3) width 100: "Lo re mi"
text run at (3,23) width 67: "psumdolor"
selection start: position 1 of child 0 {#text} of child 8 {DIV} of body
selection end: position 4 of child 0 {#text} of child 8 {DIV} of body
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x244
LayoutBlockFlow {HTML} at (0,0) size 800x244
LayoutBlockFlow {BODY} at (8,30) size 784x206
LayoutBlockFlow {P} at (0,0) size 784x70
LayoutText {#text} at (0,0) size 766x69
text run at (0,0) width 766: "The selection should be painted correctly from and including \x{732B}"
text run at (0,35) width 579: "until and including C, then from right to left for "
text run at (579,35) width 39 RTL: "\x{627}\x{644}\x{639}\x{631}"
text run at (618,35) width 8: "."
LayoutBlockFlow {DIV} at (0,100) size 784x53
LayoutText {#text} at (0,7) size 415x34
text run at (0,7) width 351: "\x{543E}\x{8F29}\x{306F}\x{732B}\x{3067} \x{92E}\x{93E}\x{928}\x{915} \x{939}\x{93F}\x{928}\x{94D}\x{926}\x{940}ABC"
text run at (351,7) width 64 RTL: "\x{627}\x{644}\x{639}\x{631}\x{628}\x{64A}\x{629}"
LayoutBlockFlow {DIV} at (0,153) size 784x53
LayoutText {#text} at (0,7) size 415x34
text run at (0,7) width 351: "\x{543E}\x{8F29}\x{306F}\x{732B}\x{3067} \x{92E}\x{93E}\x{928}\x{915} \x{939}\x{93F}\x{928}\x{94D}\x{926}\x{940}ABC"
text run at (351,7) width 64 RTL: "\x{627}\x{644}\x{639}\x{631}\x{628}\x{64A}\x{629}"
selection start: position 3 of child 0 {#text} of child 5 {DIV} of body
selection end: position 24 of child 0 {#text} of child 5 {DIV} of body
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x600
LayoutBlockFlow {HTML} at (0,0) size 800x600
LayoutBlockFlow {BODY} at (8,8) size 784x584
LayoutBlockFlow {P} at (0,0) size 784x40
LayoutText {#text} at (0,0) size 52x19
text run at (0,0) width 52: "Test for "
LayoutInline {I} at (0,0) size 734x39
LayoutText {#text} at (51,0) size 734x39
text run at (51,0) width 683: "http://bugzilla.opendarwin.org/show_bug.cgi?id=6132 Incorrect selection highlighting for ATSUI text when"
text run at (0,20) width 176: "selected range is \"CG-safe\""
LayoutText {#text} at (176,20) size 4x19
text run at (176,20) width 4: "."
LayoutBlockFlow {P} at (0,56) size 784x20
LayoutText {#text} at (0,0) size 708x19
text run at (0,0) width 708: "The word \x{201C}dolor\x{201D} below should be highlighted in its entirety. The highlight should not extend beyond that word."
LayoutBlockFlow {HR} at (0,92) size 784x2 [border: (1px inset #EEEEEE)]
LayoutBlockFlow (anonymous) at (0,102) size 784x20
LayoutInline {SPAN} at (0,0) size 173x19
LayoutText {#text} at (0,0) size 173x19
text run at (0,0) width 173: "Lo\x{308}re\x{300}m ipsum dolor sit amet"
LayoutText {#text} at (0,0) size 0x0
selection start: position 14 of child 0 {#text} of child 7 {SPAN} of body
selection end: position 19 of child 0 {#text} of child 7 {SPAN} of body
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (418,0) size 382x600
LayoutBlockFlow {HTML} at (0,0) size 382x600 [border: (10px solid #800000)]
LayoutBlockFlow {BODY} at (18,18) size 346x564 [border: (5px solid #000000)]
LayoutBlockFlow {DIV} at (5,105) size 336x400
LayoutText {#text} at (0,0) size 335x399
text run at (0,0) width 399: "\x{305B}\x{3063}\x{304B}\x{304F}\x{898B}\x{3064}\x{3051}\x{305F}\x{3059}\x{3070}\x{3089}\x{3057}\x{3044}\x{8A18}\x{4E8B}\x{304C}\x{3069}\x{3053}\x{306B}"
text run at (24,0) width 399: "\x{3042}\x{3063}\x{305F}\x{304B}\x{5FD8}\x{308C}\x{3066}\x{3057}\x{307E}\x{3063}\x{305F}\x{7D4C}\x{9A13}\x{306F}\x{3042}\x{308A}\x{307E}\x{3059}\x{304B}"
text run at (48,0) width 399: "\x{306A}\x{3089}\x{30BF}\x{30A4}\x{30C8}\x{30EB}\x{3068}\x{30A2}\x{30C9}\x{30EC}\x{30B9}\x{3060}\x{3051}\x{3067}\x{306A}\x{304F}\x{3001}\x{8A2A}\x{554F}"
text run at (72,0) width 399: "\x{3057}\x{305F}\x{30A6}\x{30A7}\x{30D6}\x{30DA}\x{30FC}\x{30B8}\x{306E}\x{30B3}\x{30F3}\x{30C6}\x{30F3}\x{30C4}\x{304B}\x{3089}\x{3082}\x{691C}\x{7D22}"
text run at (96,0) width 399: "\x{3059}\x{308B}\x{3053}\x{3068}\x{304C}\x{3067}\x{304D}\x{307E}\x{3059}\x{3002}\x{305B}\x{3063}\x{304B}\x{304F}\x{898B}\x{3064}\x{3051}\x{305F}\x{3059}"
text run at (120,0) width 399: "\x{3070}\x{3089}\x{3057}\x{3044}\x{8A18}\x{4E8B}\x{304C}\x{3069}\x{3053}\x{306B}\x{3042}\x{3063}\x{305F}\x{304B}\x{5FD8}\x{308C}\x{3066}\x{3057}\x{307E}"
text run at (144,0) width 383: "\x{3063}\x{305F}\x{7D4C}\x{9A13}\x{306F}\x{3042}\x{308A}\x{307E}\x{3059}\x{304B} \x{306A}\x{3089}\x{30BF}\x{30A4}\x{30C8}\x{30EB}\x{3068}\x{30A2}"
text run at (168,0) width 399: "\x{30C9}\x{30EC}\x{30B9}\x{3060}\x{3051}\x{3067}\x{306A}\x{304F}\x{3001}\x{8A2A}\x{554F}\x{3057}\x{305F}\x{30A6}\x{30A7}\x{30D6}\x{30DA}\x{30FC}\x{30B8}"
text run at (192,0) width 399: "\x{306E}\x{30B3}\x{30F3}\x{30C6}\x{30F3}\x{30C4}\x{304B}\x{3089}\x{3082}\x{691C}\x{7D22}\x{3059}\x{308B}\x{3053}\x{3068}\x{304C}\x{3067}\x{304D}\x{307E}"
text run at (216,0) width 399: "\x{3059}\x{3002}\x{8A2A}\x{554F}\x{3057}\x{305F}\x{30A6}\x{30A7}\x{30D6}\x{30DA}\x{30FC}\x{30B8}\x{306E}\x{30B3}\x{30F3}\x{30C6}\x{30F3}\x{30C4}\x{304B}"
text run at (240,0) width 399: "\x{3089}\x{3082}\x{691C}\x{7D22}\x{3059}\x{308B}\x{3053}\x{3068}\x{304C}\x{3067}\x{304D}\x{307E}\x{3059}\x{3002}\x{305B}\x{3063}\x{304B}\x{304F}\x{898B}"
text run at (264,0) width 399: "\x{3064}\x{3051}\x{305F}\x{3059}\x{3070}\x{3089}\x{3057}\x{3044}\x{8A18}\x{4E8B}\x{304C}\x{3069}\x{3053}\x{306B}\x{3042}\x{3063}\x{305F}\x{304B}\x{5FD8}"
text run at (288,0) width 383: "\x{308C}\x{3066}\x{3057}\x{307E}\x{3063}\x{305F}\x{7D4C}\x{9A13}\x{306F}\x{3042}\x{308A}\x{307E}\x{3059}\x{304B} \x{306A}\x{3089}\x{30BF}\x{30A4}"
text run at (312,0) width 315: "\x{30C8}\x{30EB}\x{3068}\x{30A2}\x{30C9}\x{30EC}\x{30B9}\x{3060}\x{3051}\x{3067}\x{306A}\x{304F}\x{3001}\x{8A2A}\x{554F}"
selection start: position 5 of child 0 {#text} of child 1 {DIV} of body
selection end: position 252 of child 0 {#text} of child 1 {DIV} of body
This tests clicking on the left of RTL text puts the caret at the end of the line.
PASS: on ך לכ, caret is at 4 initially
PASS: on ך לכ, caret is at 2 after moving upwards once
PASS: on כ ככ כככ, caret is at 8 initially
PASS: on כ ככ כככ, caret is at 5 after moving upwards once
PASS: on כ ככ כככ, caret is at 2 after moving upwards twice
PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 18 initially
FAIL: on גכ יגכ יגכ יגכ יגכ, caret is at 15 after moving upwards once but expected at 14
This tests clicking on the left of RTL text puts the caret at the end of the line.
PASS: on ך לכ, caret is at 4 initially
PASS: on ך לכ, caret is at 2 after moving upwards once
PASS: on כ ככ כככ, caret is at 8 initially
PASS: on כ ככ כככ, caret is at 5 after moving upwards once
PASS: on כ ככ כככ, caret is at 2 after moving upwards twice
PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 18 initially
FAIL: on גכ יגכ יגכ יגכ יגכ, caret is at 15 after moving upwards once but expected at 14
This tests clicking on the left of RTL text puts the caret at the end of the line.
PASS: on ך לכ, caret is at 4 initially
PASS: on ך לכ, caret is at 2 after moving upwards once
PASS: on כ ככ כככ, caret is at 8 initially
PASS: on כ ככ כככ, caret is at 5 after moving upwards once
PASS: on כ ככ כככ, caret is at 2 after moving upwards twice
PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 18 initially
FAIL: on גכ יגכ יגכ יגכ יגכ, caret is at 15 after moving upwards once but expected at 14
...@@ -683,7 +683,9 @@ PositionWithAffinity LayoutText::PositionForPoint( ...@@ -683,7 +683,9 @@ PositionWithAffinity LayoutText::PositionForPoint(
if (LineDirectionPointFitsInBox(point_line_direction.ToInt(), box, if (LineDirectionPointFitsInBox(point_line_direction.ToInt(), box,
should_affinity_be_downstream)) { should_affinity_be_downstream)) {
return CreatePositionWithAffinityForBoxAfterAdjustingOffsetForBiDi( return CreatePositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(
box, box->OffsetForPosition(point_line_direction), box,
box->OffsetForPosition(point_line_direction, IncludePartialGlyphs,
BreakGlyphs),
should_affinity_be_downstream); should_affinity_be_downstream);
} }
} }
...@@ -697,7 +699,9 @@ PositionWithAffinity LayoutText::PositionForPoint( ...@@ -697,7 +699,9 @@ PositionWithAffinity LayoutText::PositionForPoint(
should_affinity_be_downstream); should_affinity_be_downstream);
return CreatePositionWithAffinityForBoxAfterAdjustingOffsetForBiDi( return CreatePositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(
last_box, last_box,
last_box->OffsetForPosition(point_line_direction) + last_box->Start(), last_box->OffsetForPosition(point_line_direction, IncludePartialGlyphs,
BreakGlyphs) +
last_box->Start(),
should_affinity_be_downstream); should_affinity_be_downstream);
} }
return CreatePositionWithAffinity(0); return CreatePositionWithAffinity(0);
......
...@@ -866,7 +866,8 @@ ALWAYS_INLINE bool BreakingContext::RewindToMidWordBreak( ...@@ -866,7 +866,8 @@ ALWAYS_INLINE bool BreakingContext::RewindToMidWordBreak(
x_pos_to_break += LayoutUnit::Epsilon(); x_pos_to_break += LayoutUnit::Epsilon();
if (run.Rtl()) if (run.Rtl())
x_pos_to_break = word_measurement.width - x_pos_to_break; x_pos_to_break = word_measurement.width - x_pos_to_break;
len = font.OffsetForPosition(run, x_pos_to_break, false); len = font.OffsetForPosition(run, x_pos_to_break, OnlyFullGlyphs,
DontBreakGlyphs);
int end = start + len; int end = start + len;
if (len) { if (len) {
end = break_iterator.PreviousBreakOpportunity(end, start); end = break_iterator.PreviousBreakOpportunity(end, start);
...@@ -908,8 +909,9 @@ ALWAYS_INLINE bool BreakingContext::Hyphenate( ...@@ -908,8 +909,9 @@ ALWAYS_INLINE bool BreakingContext::Hyphenate(
TextRun run = ConstructTextRun(font, text, start, len, style); TextRun run = ConstructTextRun(font, text, start, len, style);
run.SetTabSize(!collapse_white_space_, style.GetTabSize()); run.SetTabSize(!collapse_white_space_, style.GetTabSize());
run.SetXPos(width_.CurrentWidth()); run.SetXPos(width_.CurrentWidth());
unsigned max_prefix_length = // TODO(fserb): Check if this need to be BreakGlyphs.
font.OffsetForPosition(run, max_prefix_width, false); unsigned max_prefix_length = font.OffsetForPosition(
run, max_prefix_width, OnlyFullGlyphs, DontBreakGlyphs);
if (max_prefix_length < Hyphenation::kMinimumPrefixLength) if (max_prefix_length < Hyphenation::kMinimumPrefixLength)
return false; return false;
......
...@@ -424,7 +424,10 @@ LayoutUnit InlineTextBox::PlaceEllipsisBox(bool flow_is_ltr, ...@@ -424,7 +424,10 @@ LayoutUnit InlineTextBox::PlaceEllipsisBox(bool flow_is_ltr,
// more accurate position in rtl text. // more accurate position in rtl text.
// TODO(crbug.com/722043: This doesn't always give the best results. // TODO(crbug.com/722043: This doesn't always give the best results.
bool ltr = IsLeftToRightDirection(); bool ltr = IsLeftToRightDirection();
int offset = OffsetForPosition(ellipsis_x, !ltr); int offset = OffsetForPosition(ellipsis_x,
ltr ? OnlyFullGlyphs : IncludePartialGlyphs,
DontBreakGlyphs);
// Full truncation is only necessary when we're flowing left-to-right. // Full truncation is only necessary when we're flowing left-to-right.
if (flow_is_ltr && offset == 0 && ltr == flow_is_ltr) { if (flow_is_ltr && offset == 0 && ltr == flow_is_ltr) {
// No characters should be laid out. Set ourselves to full truncation and // No characters should be laid out. Set ourselves to full truncation and
...@@ -625,7 +628,8 @@ LayoutUnit InlineTextBox::TextPos() const { ...@@ -625,7 +628,8 @@ LayoutUnit InlineTextBox::TextPos() const {
} }
int InlineTextBox::OffsetForPosition(LayoutUnit line_offset, int InlineTextBox::OffsetForPosition(LayoutUnit line_offset,
bool include_partial_glyphs) const { IncludePartialGlyphsOption partial_glyphs,
BreakGlyphsOption break_glyphs) const {
if (IsLineBreak()) if (IsLineBreak())
return 0; return 0;
...@@ -639,7 +643,7 @@ int InlineTextBox::OffsetForPosition(LayoutUnit line_offset, ...@@ -639,7 +643,7 @@ int InlineTextBox::OffsetForPosition(LayoutUnit line_offset,
const Font& font = style.GetFont(); const Font& font = style.GetFont();
return font.OffsetForPosition(ConstructTextRun(style), return font.OffsetForPosition(ConstructTextRun(style),
(line_offset - LogicalLeft()).ToFloat(), (line_offset - LogicalLeft()).ToFloat(),
include_partial_glyphs); partial_glyphs, break_glyphs);
} }
LayoutUnit InlineTextBox::PositionForOffset(int offset) const { LayoutUnit InlineTextBox::PositionForOffset(int offset) const {
......
...@@ -206,7 +206,8 @@ class CORE_EXPORT InlineTextBox : public InlineBox { ...@@ -206,7 +206,8 @@ class CORE_EXPORT InlineTextBox : public InlineBox {
public: public:
virtual int OffsetForPosition(LayoutUnit x, virtual int OffsetForPosition(LayoutUnit x,
bool include_partial_glyphs = true) const; IncludePartialGlyphsOption,
BreakGlyphsOption) const;
virtual LayoutUnit PositionForOffset(int offset) const; virtual LayoutUnit PositionForOffset(int offset) const;
// Returns false for offset after line break. // Returns false for offset after line break.
......
...@@ -192,9 +192,9 @@ unsigned NGPhysicalTextFragment::TextOffsetForPoint( ...@@ -192,9 +192,9 @@ unsigned NGPhysicalTextFragment::TextOffsetForPoint(
DCHECK(TextShapeResult()); DCHECK(TextShapeResult());
const LayoutUnit& point_in_line_direction = const LayoutUnit& point_in_line_direction =
Style().IsHorizontalWritingMode() ? point.left : point.top; Style().IsHorizontalWritingMode() ? point.left : point.top;
const bool include_partial_glyphs = true;
return TextShapeResult()->OffsetForPosition(point_in_line_direction.ToFloat(), return TextShapeResult()->OffsetForPosition(point_in_line_direction.ToFloat(),
include_partial_glyphs) + IncludePartialGlyphs,
BreakGlyphs) +
StartOffset(); StartOffset();
} }
......
...@@ -57,7 +57,9 @@ void SVGInlineTextBox::DirtyLineBoxes() { ...@@ -57,7 +57,9 @@ void SVGInlineTextBox::DirtyLineBoxes() {
next_box->DirtyLineBoxes(); next_box->DirtyLineBoxes();
} }
int SVGInlineTextBox::OffsetForPosition(LayoutUnit, bool) const { int SVGInlineTextBox::OffsetForPosition(LayoutUnit,
IncludePartialGlyphsOption,
BreakGlyphsOption) const {
// SVG doesn't use the standard offset <-> position selection system, as it's // SVG doesn't use the standard offset <-> position selection system, as it's
// not suitable for SVGs complex needs. Vertical text selection, inline boxes // not suitable for SVGs complex needs. Vertical text selection, inline boxes
// spanning multiple lines (contrary to HTML, etc.) // spanning multiple lines (contrary to HTML, etc.)
...@@ -80,11 +82,10 @@ int SVGInlineTextBox::OffsetForPositionInFragment( ...@@ -80,11 +82,10 @@ int SVGInlineTextBox::OffsetForPositionInFragment(
if (fragment.AffectedByTextLength()) if (fragment.AffectedByTextLength())
position /= fragment.length_adjust_scale; position /= fragment.length_adjust_scale;
const bool include_partial_glyphs = true;
TextRun text_run = ConstructTextRun(line_layout_item.StyleRef(), fragment); TextRun text_run = ConstructTextRun(line_layout_item.StyleRef(), fragment);
return fragment.character_offset - Start() + return fragment.character_offset - Start() +
line_layout_item.ScaledFont().OffsetForPosition( line_layout_item.ScaledFont().OffsetForPosition(
text_run, position, include_partial_glyphs); text_run, position, IncludePartialGlyphs, BreakGlyphs);
} }
LayoutUnit SVGInlineTextBox::PositionForOffset(int) const { LayoutUnit SVGInlineTextBox::PositionForOffset(int) const {
......
...@@ -39,7 +39,8 @@ class SVGInlineTextBox final : public InlineTextBox { ...@@ -39,7 +39,8 @@ class SVGInlineTextBox final : public InlineTextBox {
void SetLogicalHeight(LayoutUnit height) { logical_height_ = height; } void SetLogicalHeight(LayoutUnit height) { logical_height_ = height; }
int OffsetForPosition(LayoutUnit x, int OffsetForPosition(LayoutUnit x,
bool include_partial_glyphs = true) const override; IncludePartialGlyphsOption,
BreakGlyphsOption) const override;
LayoutUnit PositionForOffset(int offset) const override; LayoutUnit PositionForOffset(int offset) const override;
void Paint(const PaintInfo&, void Paint(const PaintInfo&,
......
...@@ -1732,6 +1732,7 @@ jumbo_source_set("blink_platform_unittests_sources") { ...@@ -1732,6 +1732,7 @@ jumbo_source_set("blink_platform_unittests_sources") {
"feature_policy/feature_policy_test.cc", "feature_policy/feature_policy_test.cc",
"fonts/android/font_cache_android_test.cc", "fonts/android/font_cache_android_test.cc",
"fonts/bitmap_glyphs_blacklist_test.cc", "fonts/bitmap_glyphs_blacklist_test.cc",
"fonts/cursor_position_test.cc",
"fonts/font_cache_test.cc", "fonts/font_cache_test.cc",
"fonts/font_description_test.cc", "fonts/font_description_test.cc",
"fonts/font_family_test.cc", "fonts/font_family_test.cc",
...@@ -1973,6 +1974,7 @@ test("blink_platform_perftests") { ...@@ -1973,6 +1974,7 @@ test("blink_platform_perftests") {
"testing/blink_perf_test_suite.cc", "testing/blink_perf_test_suite.cc",
"testing/blink_perf_test_suite.h", "testing/blink_perf_test_suite.h",
"testing/run_all_perf_tests.cc", "testing/run_all_perf_tests.cc",
"testing/shape_result_perf_test.cc",
"testing/shaping_line_breaker_perf_test.cc", "testing/shaping_line_breaker_perf_test.cc",
] ]
......
...@@ -108,7 +108,8 @@ int WebFont::CalculateWidth(const WebTextRun& run) const { ...@@ -108,7 +108,8 @@ int WebFont::CalculateWidth(const WebTextRun& run) const {
} }
int WebFont::OffsetForPosition(const WebTextRun& run, float position) const { int WebFont::OffsetForPosition(const WebTextRun& run, float position) const {
return private_->GetFont().OffsetForPosition(run, position, true); return private_->GetFont().OffsetForPosition(
run, position, IncludePartialGlyphs, DontBreakGlyphs);
} }
WebFloatRect WebFont::SelectionRectForText(const WebTextRun& run, WebFloatRect WebFont::SelectionRectForText(const WebTextRun& run,
......
...@@ -390,10 +390,11 @@ FloatRect Font::BoundingBox(const TextRun& run, int from, int to) const { ...@@ -390,10 +390,11 @@ FloatRect Font::BoundingBox(const TextRun& run, int from, int to) const {
int Font::OffsetForPosition(const TextRun& run, int Font::OffsetForPosition(const TextRun& run,
float x_float, float x_float,
bool include_partial_glyphs) const { IncludePartialGlyphsOption partial_glyphs,
BreakGlyphsOption break_glyphs) const {
FontCachePurgePreventer purge_preventer; FontCachePurgePreventer purge_preventer;
CachingWordShaper shaper(*this); CachingWordShaper shaper(*this);
return shaper.OffsetForPosition(run, x_float, include_partial_glyphs); return shaper.OffsetForPosition(run, x_float, partial_glyphs, break_glyphs);
} }
ShapeCache* Font::GetShapeCache() const { ShapeCache* Font::GetShapeCache() const {
......
...@@ -143,7 +143,8 @@ class PLATFORM_EXPORT Font { ...@@ -143,7 +143,8 @@ class PLATFORM_EXPORT Font {
int OffsetForPosition(const TextRun&, int OffsetForPosition(const TextRun&,
float position, float position,
bool include_partial_glyphs) const; IncludePartialGlyphsOption,
BreakGlyphsOption) const;
FloatRect SelectionRectForText(const TextRun&, FloatRect SelectionRectForText(const TextRun&,
const FloatPoint&, const FloatPoint&,
int h, int h,
......
...@@ -80,13 +80,15 @@ static inline float ShapeResultsForRun(ShapeCache* shape_cache, ...@@ -80,13 +80,15 @@ static inline float ShapeResultsForRun(ShapeCache* shape_cache,
return total_width; return total_width;
} }
int CachingWordShaper::OffsetForPosition(const TextRun& run, int CachingWordShaper::OffsetForPosition(
float target_x, const TextRun& run,
bool include_partial_glyphs) { float target_x,
IncludePartialGlyphsOption partial_glyphs,
BreakGlyphsOption break_glyphs) {
ShapeResultBuffer buffer; ShapeResultBuffer buffer;
ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer); ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer);
return buffer.OffsetForPosition(run, target_x, include_partial_glyphs); return buffer.OffsetForPosition(run, target_x, partial_glyphs, break_glyphs);
} }
void CachingWordShaper::FillResultBuffer(const TextRunPaintInfo& run_info, void CachingWordShaper::FillResultBuffer(const TextRunPaintInfo& run_info,
...@@ -101,7 +103,7 @@ CharacterRange CachingWordShaper::GetCharacterRange(const TextRun& run, ...@@ -101,7 +103,7 @@ CharacterRange CachingWordShaper::GetCharacterRange(const TextRun& run,
ShapeResultBuffer buffer; ShapeResultBuffer buffer;
float total_width = ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer); float total_width = ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer);
return buffer.GetCharacterRange(run.Direction(), total_width, from, to); return buffer.GetCharacterRange(total_width, run.Direction(), from, to);
} }
Vector<CharacterRange> CachingWordShaper::IndividualCharacterRanges( Vector<CharacterRange> CachingWordShaper::IndividualCharacterRanges(
......
...@@ -55,7 +55,8 @@ class PLATFORM_EXPORT CachingWordShaper final { ...@@ -55,7 +55,8 @@ class PLATFORM_EXPORT CachingWordShaper final {
FloatRect* glyph_bounds); FloatRect* glyph_bounds);
int OffsetForPosition(const TextRun&, int OffsetForPosition(const TextRun&,
float target_x, float target_x,
bool include_partial_glyphs); IncludePartialGlyphsOption,
BreakGlyphsOption);
void FillResultBuffer(const TextRunPaintInfo&, ShapeResultBuffer*); void FillResultBuffer(const TextRunPaintInfo&, ShapeResultBuffer*);
CharacterRange GetCharacterRange(const TextRun&, unsigned from, unsigned to); CharacterRange GetCharacterRange(const TextRun&, unsigned from, unsigned to);
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
#include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h" #include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
#include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h" #include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
#include "third_party/blink/renderer/platform/wtf/compiler.h" #include "third_party/blink/renderer/platform/wtf/compiler.h"
#include "third_party/blink/renderer/platform/wtf/deque.h" #include "third_party/blink/renderer/platform/wtf/deque.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h"
...@@ -337,10 +338,13 @@ void HarfBuzzShaper::CommitGlyphs(RangeData* range_data, ...@@ -337,10 +338,13 @@ void HarfBuzzShaper::CommitGlyphs(RangeData* range_data,
// Here we need to specify glyph positions. // Here we need to specify glyph positions.
BufferSlice next_slice; BufferSlice next_slice;
for (const BufferSlice* current_slice = &slice;;) { for (const BufferSlice* current_slice = &slice;;) {
Vector<unsigned> graphemes;
GraphemesClusterList(text_, current_slice->start_character_index,
current_slice->num_characters, &graphemes);
ShapeResult::RunInfo* run = new ShapeResult::RunInfo( ShapeResult::RunInfo* run = new ShapeResult::RunInfo(
current_font, direction, canvas_rotation, script, current_font, direction, canvas_rotation, script,
current_slice->start_character_index, current_slice->num_glyphs, current_slice->start_character_index, current_slice->num_glyphs,
current_slice->num_characters); current_slice->num_characters, graphemes);
shape_result->InsertRun(base::WrapUnique(run), shape_result->InsertRun(base::WrapUnique(run),
current_slice->start_glyph_index, current_slice->start_glyph_index,
current_slice->num_glyphs, range_data->buffer); current_slice->num_glyphs, range_data->buffer);
......
...@@ -67,6 +67,22 @@ struct ShapeResultCharacterData { ...@@ -67,6 +67,22 @@ struct ShapeResultCharacterData {
unsigned safe_to_break_before : 1; unsigned safe_to_break_before : 1;
}; };
// There are two options for how OffsetForPosition behaves:
// IncludePartialGlyphs - decides what to do when the position hits more than
// 50% of the glyph. If enabled, we count that glyph, if disable we don't.
enum IncludePartialGlyphsOption {
OnlyFullGlyphs,
IncludePartialGlyphs,
};
// BreakGlyphs - allows OffsetForPosition to consider graphemes separations
// inside a glyph. It allows the function to return a point inside a glyph when
// multiple graphemes share a glyph (for example, in a ligature)
enum BreakGlyphsOption {
DontBreakGlyphs,
BreakGlyphs,
};
class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> { class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> {
public: public:
static scoped_refptr<ShapeResult> Create(const Font* font, static scoped_refptr<ShapeResult> Create(const Font* font,
...@@ -124,18 +140,21 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> { ...@@ -124,18 +140,21 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> {
// Returns the offset, relative to StartIndexForResult, whose (origin, // Returns the offset, relative to StartIndexForResult, whose (origin,
// origin+advance) contains |x|. // origin+advance) contains |x|.
unsigned OffsetForPosition(float x) const; unsigned OffsetForPosition(float x, BreakGlyphsOption) const;
// Returns the offset whose glyph boundary is nearest to |x|. Depends on // Returns the offset whose glyph boundary is nearest to |x|. Depends on
// whether |x| is on the left-half or the right-half of the glyph, it // whether |x| is on the left-half or the right-half of the glyph, it
// determines the left-boundary or the right-boundary, then computes the // determines the left-boundary or the right-boundary, then computes the
// offset from the bidi direction. // offset from the bidi direction.
unsigned OffsetForHitTest(float x) const; unsigned OffsetForHitTest(float x, BreakGlyphsOption) const;
// Returns the offset that can fit to between |x| and the left or the right // Returns the offset that can fit to between |x| and the left or the right
// edge. The side of the edge is determined by |line_direction|. // edge. The side of the edge is determined by |line_direction|.
unsigned OffsetToFit(float x, TextDirection line_direction) const; unsigned OffsetToFit(float x, TextDirection line_direction) const;
unsigned OffsetForPosition(float x, bool include_partial_glyphs) const { unsigned OffsetForPosition(float x,
return !include_partial_glyphs ? OffsetForPosition(x) : OffsetForHitTest(x); IncludePartialGlyphsOption include_partial_glyphs,
BreakGlyphsOption break_glyphs_option) const {
return include_partial_glyphs == OnlyFullGlyphs
? OffsetForPosition(x, break_glyphs_option)
: OffsetForHitTest(x, break_glyphs_option);
} }
// Returns the position for a given offset, relative to StartIndexForResult. // Returns the position for a given offset, relative to StartIndexForResult.
...@@ -198,7 +217,8 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> { ...@@ -198,7 +217,8 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> {
RunInfo* InsertRunForTesting(unsigned start_index, RunInfo* InsertRunForTesting(unsigned start_index,
unsigned num_characters, unsigned num_characters,
TextDirection, TextDirection,
Vector<uint16_t> safe_break_offsets = {}); Vector<uint16_t> safe_break_offsets = {},
Vector<unsigned> graphemes = {});
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
void CheckConsistency() const; void CheckConsistency() const;
#endif #endif
...@@ -221,26 +241,21 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> { ...@@ -221,26 +241,21 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> {
unsigned run_index = 0; unsigned run_index = 0;
// The total number of characters of runs_[0..run_index - 1]. // The total number of characters of runs_[0..run_index - 1].
unsigned characters_on_left_runs = 0; unsigned characters_on_left_runs = 0;
unsigned character_index = 0;
unsigned glyph_index = 0; // Those are the left and right character indexes of the group of glyphs
// |next_glyph_index| may not be |glyph_index| + 1 when a cluster is of // that were selected by OffsetForPosition.
// multiple glyphs; i.e., ligatures or combining glyphs. unsigned left_character_index = 0;
unsigned next_glyph_index = 0; unsigned right_character_index = 0;
// The glyph origin of the glyph. // The glyph origin of the glyph.
float origin_x = 0; float origin_x = 0;
// The advance of the glyph. // The advance of the glyph.
float advance = 0; float advance = 0;
// True if the position was found on a run. False otherwise.
bool IsInRun() const { return next_glyph_index; }
}; };
unsigned OffsetLtr(const GlyphIndexResult&) const; void OffsetForPosition(float target_x,
unsigned OffsetRtl(const GlyphIndexResult&, float x) const; BreakGlyphsOption,
unsigned OffsetRightLtr(const GlyphIndexResult&) const; GlyphIndexResult*) const;
unsigned OffsetLeftRtl(const GlyphIndexResult&) const;
void OffsetForPosition(float target_x, GlyphIndexResult*) const;
// Helper class storing a map between offsets and x-positions. // Helper class storing a map between offsets and x-positions.
// Unlike the RunInfo and GlyphData structures in ShapeResult, which operates // Unlike the RunInfo and GlyphData structures in ShapeResult, which operates
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
#include "third_party/blink/renderer/platform/fonts/character_range.h" #include "third_party/blink/renderer/platform/fonts/character_range.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
#include "third_party/blink/renderer/platform/fonts/simple_font_data.h" #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
#include "third_party/blink/renderer/platform/geometry/float_point.h" #include "third_party/blink/renderer/platform/geometry/float_point.h"
...@@ -27,6 +28,13 @@ CharacterRange ShapeResultBuffer::GetCharacterRange( ...@@ -27,6 +28,13 @@ CharacterRange ShapeResultBuffer::GetCharacterRange(
return GetCharacterRangeInternal(results, direction, total_width, from, to); return GetCharacterRangeInternal(results, direction, total_width, from, to);
} }
CharacterRange ShapeResultBuffer::GetCharacterRange(float total_width,
TextDirection direction,
unsigned from,
unsigned to) const {
return GetCharacterRangeInternal(results_, direction, total_width, from, to);
}
CharacterRange ShapeResultBuffer::GetCharacterRangeInternal( CharacterRange ShapeResultBuffer::GetCharacterRangeInternal(
const Vector<scoped_refptr<const ShapeResult>, 64>& results, const Vector<scoped_refptr<const ShapeResult>, 64>& results,
TextDirection direction, TextDirection direction,
...@@ -123,13 +131,6 @@ CharacterRange ShapeResultBuffer::GetCharacterRangeInternal( ...@@ -123,13 +131,6 @@ CharacterRange ShapeResultBuffer::GetCharacterRangeInternal(
return CharacterRange(to_x, from_x, -min_y, max_y); return CharacterRange(to_x, from_x, -min_y, max_y);
} }
CharacterRange ShapeResultBuffer::GetCharacterRange(TextDirection direction,
float total_width,
unsigned from,
unsigned to) const {
return GetCharacterRangeInternal(results_, direction, total_width, from, to);
}
void ShapeResultBuffer::AddRunInfoRanges(const ShapeResult::RunInfo& run_info, void ShapeResultBuffer::AddRunInfoRanges(const ShapeResult::RunInfo& run_info,
float offset, float offset,
Vector<CharacterRange>& ranges) { Vector<CharacterRange>& ranges) {
...@@ -172,9 +173,11 @@ Vector<CharacterRange> ShapeResultBuffer::IndividualCharacterRanges( ...@@ -172,9 +173,11 @@ Vector<CharacterRange> ShapeResultBuffer::IndividualCharacterRanges(
return ranges; return ranges;
} }
int ShapeResultBuffer::OffsetForPosition(const TextRun& run, int ShapeResultBuffer::OffsetForPosition(
float target_x, const TextRun& run,
bool include_partial_glyphs) const { float target_x,
IncludePartialGlyphsOption partial_glyphs,
BreakGlyphsOption break_glyphs) const {
unsigned total_offset; unsigned total_offset;
if (run.Rtl()) { if (run.Rtl()) {
total_offset = run.length(); total_offset = run.length();
...@@ -184,8 +187,8 @@ int ShapeResultBuffer::OffsetForPosition(const TextRun& run, ...@@ -184,8 +187,8 @@ int ShapeResultBuffer::OffsetForPosition(const TextRun& run,
continue; continue;
total_offset -= word_result->NumCharacters(); total_offset -= word_result->NumCharacters();
if (target_x >= 0 && target_x <= word_result->Width()) { if (target_x >= 0 && target_x <= word_result->Width()) {
int offset_for_word = int offset_for_word = word_result->OffsetForPosition(
word_result->OffsetForPosition(target_x, include_partial_glyphs); target_x, partial_glyphs, break_glyphs);
return total_offset + offset_for_word; return total_offset + offset_for_word;
} }
target_x -= word_result->Width(); target_x -= word_result->Width();
...@@ -195,8 +198,8 @@ int ShapeResultBuffer::OffsetForPosition(const TextRun& run, ...@@ -195,8 +198,8 @@ int ShapeResultBuffer::OffsetForPosition(const TextRun& run,
for (const auto& word_result : results_) { for (const auto& word_result : results_) {
if (!word_result) if (!word_result)
continue; continue;
int offset_for_word = int offset_for_word = word_result->OffsetForPosition(
word_result->OffsetForPosition(target_x, include_partial_glyphs); target_x, partial_glyphs, break_glyphs);
DCHECK_GE(offset_for_word, 0); DCHECK_GE(offset_for_word, 0);
total_offset += offset_for_word; total_offset += offset_for_word;
if (target_x >= 0 && target_x <= word_result->Width()) if (target_x >= 0 && target_x <= word_result->Width())
......
...@@ -33,11 +33,12 @@ class PLATFORM_EXPORT ShapeResultBuffer { ...@@ -33,11 +33,12 @@ class PLATFORM_EXPORT ShapeResultBuffer {
bool HasVerticalOffsets() const { return has_vertical_offsets_; } bool HasVerticalOffsets() const { return has_vertical_offsets_; }
int OffsetForPosition(const TextRun&, int OffsetForPosition(const TextRun& run,
float target_x, float target_x,
bool include_partial_glyphs) const; IncludePartialGlyphsOption,
CharacterRange GetCharacterRange(TextDirection, BreakGlyphsOption) const;
float total_width, CharacterRange GetCharacterRange(float total_width,
TextDirection,
unsigned from, unsigned from,
unsigned to) const; unsigned to) const;
Vector<CharacterRange> IndividualCharacterRanges(TextDirection, Vector<CharacterRange> IndividualCharacterRanges(TextDirection,
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/allocator.h"
#include "third_party/blink/renderer/platform/wtf/noncopyable.h" #include "third_party/blink/renderer/platform/wtf/noncopyable.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink { namespace blink {
...@@ -72,12 +73,14 @@ struct ShapeResult::RunInfo { ...@@ -72,12 +73,14 @@ struct ShapeResult::RunInfo {
hb_script_t script, hb_script_t script,
unsigned start_index, unsigned start_index,
unsigned num_glyphs, unsigned num_glyphs,
unsigned num_characters) unsigned num_characters,
Vector<unsigned> graphemes)
: font_data_(const_cast<SimpleFontData*>(font)), : font_data_(const_cast<SimpleFontData*>(font)),
direction_(dir), direction_(dir),
canvas_rotation_(canvas_rotation), canvas_rotation_(canvas_rotation),
script_(script), script_(script),
glyph_data_(num_glyphs), glyph_data_(num_glyphs),
graphemes_(graphemes),
start_index_(start_index), start_index_(start_index),
num_characters_(num_characters), num_characters_(num_characters),
width_(0.0f) {} width_(0.0f) {}
...@@ -88,6 +91,7 @@ struct ShapeResult::RunInfo { ...@@ -88,6 +91,7 @@ struct ShapeResult::RunInfo {
canvas_rotation_(other.canvas_rotation_), canvas_rotation_(other.canvas_rotation_),
script_(other.script_), script_(other.script_),
glyph_data_(other.glyph_data_), glyph_data_(other.glyph_data_),
graphemes_(other.graphemes_),
start_index_(other.start_index_), start_index_(other.start_index_),
num_characters_(other.num_characters_), num_characters_(other.num_characters_),
width_(other.width_) {} width_(other.width_) {}
...@@ -100,12 +104,21 @@ struct ShapeResult::RunInfo { ...@@ -100,12 +104,21 @@ struct ShapeResult::RunInfo {
unsigned PreviousSafeToBreakOffset(unsigned) const; unsigned PreviousSafeToBreakOffset(unsigned) const;
float XPositionForVisualOffset(unsigned, AdjustMidCluster) const; float XPositionForVisualOffset(unsigned, AdjustMidCluster) const;
float XPositionForOffset(unsigned, AdjustMidCluster) const; float XPositionForOffset(unsigned, AdjustMidCluster) const;
void CharacterIndexForXPosition(float, GlyphIndexResult*) const; void CharacterIndexForXPosition(float,
BreakGlyphsOption,
GlyphIndexResult*) const;
void SetGlyphAndPositions(unsigned index,
uint16_t glyph_id,
float advance,
float offset_x,
float offset_y);
size_t GlyphToCharacterIndex(size_t i) const { size_t GlyphToCharacterIndex(size_t i) const {
return start_index_ + glyph_data_[i].character_index; return start_index_ + glyph_data_[i].character_index;
} }
unsigned NumGraphemes(unsigned start, unsigned end) const;
// For memory reporting. // For memory reporting.
size_t ByteSize() const { size_t ByteSize() const {
return sizeof(this) + glyph_data_.size() * sizeof(HarfBuzzRunGlyphData); return sizeof(this) + glyph_data_.size() * sizeof(HarfBuzzRunGlyphData);
...@@ -159,9 +172,18 @@ struct ShapeResult::RunInfo { ...@@ -159,9 +172,18 @@ struct ShapeResult::RunInfo {
auto glyphs = FindGlyphDataRange(start, end); auto glyphs = FindGlyphDataRange(start, end);
unsigned number_of_glyphs = std::distance(glyphs.begin, glyphs.end); unsigned number_of_glyphs = std::distance(glyphs.begin, glyphs.end);
Vector<unsigned> sub_graphemes;
if (graphemes_.size()) {
sub_graphemes.resize(number_of_characters);
for (unsigned i = 0; i < number_of_characters; ++i) {
sub_graphemes[i] = graphemes_[start + i];
}
}
auto run = std::make_unique<RunInfo>( auto run = std::make_unique<RunInfo>(
font_data_.get(), direction_, canvas_rotation_, script_, font_data_.get(), direction_, canvas_rotation_, script_,
start_index_ + start, number_of_glyphs, number_of_characters); start_index_ + start, number_of_glyphs, number_of_characters,
std::move(sub_graphemes));
static_assert(base::is_trivially_copyable<HarfBuzzRunGlyphData>::value, static_assert(base::is_trivially_copyable<HarfBuzzRunGlyphData>::value,
"HarfBuzzRunGlyphData should be trivially copyable"); "HarfBuzzRunGlyphData should be trivially copyable");
...@@ -270,6 +292,11 @@ struct ShapeResult::RunInfo { ...@@ -270,6 +292,11 @@ struct ShapeResult::RunInfo {
CanvasRotationInVertical canvas_rotation_; CanvasRotationInVertical canvas_rotation_;
hb_script_t script_; hb_script_t script_;
Vector<HarfBuzzRunGlyphData> glyph_data_; Vector<HarfBuzzRunGlyphData> glyph_data_;
// graphemes_[i] is the number of graphemes up to (and including) the ith
// character in the run.
Vector<unsigned> graphemes_;
unsigned start_index_; unsigned start_index_;
unsigned num_characters_; unsigned num_characters_;
float width_; float width_;
......
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/time/time.h"
#include "cc/base/lap_timer.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_test.h"
#include "third_party/blink/renderer/platform/fonts/font.h"
#include "third_party/blink/renderer/platform/fonts/font_description.h"
#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
using blink::test::CreateTestFont;
namespace blink {
static const int kTimeLimitMillis = 3000;
static const int kWarmupRuns = 10000;
static const int kTimeCheckInterval = 1000000;
class ShapeResultPerfTest {
public:
enum FontName {
ahem,
amiri,
megalopolis,
roboto,
};
ShapeResultPerfTest()
: timer(kWarmupRuns,
base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
kTimeCheckInterval) {}
protected:
TextRun SetupFont(FontName font_name, const String& text, bool ltr) {
FontDescription::VariantLigatures ligatures(
FontDescription::kEnabledLigaturesState);
font = CreateTestFont("TestFont",
test::PlatformTestDataPath(font_path[font_name]), 100,
&ligatures);
return TextRun(
text, /* xpos */ 0, /* expansion */ 0,
TextRun::kAllowTrailingExpansion | TextRun::kForbidLeadingExpansion,
ltr ? TextDirection::kLtr : TextDirection::kRtl, false);
}
Font font;
std::map<FontName, String> font_path = {
{ahem, "Ahem.woff"},
{amiri, "third_party/Amiri/amiri_arabic.woff2"},
{megalopolis, "third_party/MEgalopolis/MEgalopolisExtra.woff"},
{roboto, "third_party/Roboto/roboto-regular.woff2"},
};
cc::LapTimer timer;
};
class OffsetForPositionPerfTest : public ShapeResultPerfTest,
public testing::TestWithParam<float> {
public:
void OffsetForPosition(TextRun& run,
IncludePartialGlyphsOption partial,
BreakGlyphsOption breakopt) {
timer.Reset();
float position = GetParam();
do {
font.OffsetForPosition(run, position, partial, breakopt);
timer.NextLap();
} while (!timer.HasTimeLimitExpired());
}
};
class CharacterRangePerfTest : public ShapeResultPerfTest,
public testing::TestWithParam<int> {
public:
void GetCharacter(TextRun& run) {
timer.Reset();
int endpos = GetParam();
do {
font.SelectionRectForText(run, FloatPoint(), 100, 0, endpos);
timer.NextLap();
} while (!timer.HasTimeLimitExpired());
}
};
TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionFullBreak) {
TextRun run = SetupFont(ahem, "FURACOLO", true);
OffsetForPosition(run, OnlyFullGlyphs, BreakGlyphs);
perf_test::PrintResult("OffsetForPositionPerfTest", " LTR full break", "",
timer.LapsPerSecond(), "runs/s", true);
}
TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionFullDontBreak) {
TextRun run = SetupFont(ahem, "FURACOLO", true);
OffsetForPosition(run, OnlyFullGlyphs, DontBreakGlyphs);
perf_test::PrintResult("OffsetForPositionPerfTest", " LTR full", "",
timer.LapsPerSecond(), "runs/s", true);
}
TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionIncludePartialBreak) {
TextRun run = SetupFont(ahem, "FURACOLO", true);
OffsetForPosition(run, IncludePartialGlyphs, BreakGlyphs);
perf_test::PrintResult("OffsetForPositionPerfTest", " LTR partial break", "",
timer.LapsPerSecond(), "runs/s", true);
}
TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionIncludePartialDontBreak) {
TextRun run = SetupFont(ahem, "FURACOLO", true);
OffsetForPosition(run, IncludePartialGlyphs, DontBreakGlyphs);
perf_test::PrintResult("OffsetForPositionPerfTest", " LTR partial", "",
timer.LapsPerSecond(), "runs/s", true);
}
TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionFullBreak) {
TextRun run = SetupFont(ahem, "OLOCARUF", false);
OffsetForPosition(run, OnlyFullGlyphs, BreakGlyphs);
perf_test::PrintResult("OffsetForPositionPerfTest", " RTL full break", "",
timer.LapsPerSecond(), "runs/s", true);
}
TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionFullDontBreak) {
TextRun run = SetupFont(ahem, "OLOCARUF", false);
OffsetForPosition(run, OnlyFullGlyphs, DontBreakGlyphs);
perf_test::PrintResult("OffsetForPositionPerfTest", " RTL full", "",
timer.LapsPerSecond(), "runs/s", true);
}
TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionIncludePartialBreak) {
TextRun run = SetupFont(ahem, "OLOCARUF", false);
OffsetForPosition(run, IncludePartialGlyphs, BreakGlyphs);
perf_test::PrintResult("OffsetForPositionPerfTest", " RTL partial break", "",
timer.LapsPerSecond(), "runs/s", true);
}
TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionIncludePartialDontBreak) {
TextRun run = SetupFont(ahem, "OLOCARUF", false);
OffsetForPosition(run, IncludePartialGlyphs, DontBreakGlyphs);
perf_test::PrintResult("OffsetForPositionPerfTest", " RTL partial", "",
timer.LapsPerSecond(), "runs/s", true);
}
INSTANTIATE_TEST_CASE_P(OffsetForPosition,
OffsetForPositionPerfTest,
testing::Values(0, 10, 60, 100, 200, 350));
TEST_P(CharacterRangePerfTest, LTRCharacterForPosition) {
TextRun run = SetupFont(ahem, "FURACOLO", true);
GetCharacter(run);
perf_test::PrintResult("CharacterRangePerfTest", " LTR", "",
timer.LapsPerSecond(), "runs/s", true);
}
TEST_P(CharacterRangePerfTest, RTLCharacterForPosition) {
TextRun run = SetupFont(ahem, "OLOCARUF", false);
GetCharacter(run);
perf_test::PrintResult("CharacterRangePerfTest", " RTL", "",
timer.LapsPerSecond(), "runs/s", true);
}
INSTANTIATE_TEST_CASE_P(CharacterRange,
CharacterRangePerfTest,
testing::Values(0, 1, 2, 4, 8));
} // namespace blink
...@@ -53,8 +53,7 @@ unsigned NumGraphemeClusters(const String& string) { ...@@ -53,8 +53,7 @@ unsigned NumGraphemeClusters(const String& string) {
return num; return num;
} }
void GraphemesClusterList(const UChar* text, void GraphemesClusterList(String text,
unsigned text_length,
unsigned start, unsigned start,
unsigned length, unsigned length,
Vector<unsigned>* graphemes) { Vector<unsigned>* graphemes) {
...@@ -62,8 +61,8 @@ void GraphemesClusterList(const UChar* text, ...@@ -62,8 +61,8 @@ void GraphemesClusterList(const UChar* text,
if (!length) if (!length)
return; return;
DCHECK_LE(static_cast<unsigned>(start + length), text_length); String substring = text.Substring(start, length);
NonSharedCharacterBreakIterator it(&text[start], length); NonSharedCharacterBreakIterator it(substring);
int cursor_pos = it.Next(); int cursor_pos = it.Next();
unsigned count = 0; unsigned count = 0;
......
...@@ -365,8 +365,7 @@ PLATFORM_EXPORT unsigned LengthOfGraphemeCluster(const String&, unsigned = 0); ...@@ -365,8 +365,7 @@ PLATFORM_EXPORT unsigned LengthOfGraphemeCluster(const String&, unsigned = 0);
// Returns a list of graphemes cluster at each character using character break // Returns a list of graphemes cluster at each character using character break
// rules. // rules.
PLATFORM_EXPORT void GraphemesClusterList(const UChar* text, PLATFORM_EXPORT void GraphemesClusterList(String text,
unsigned text_length,
unsigned start, unsigned start,
unsigned length, unsigned length,
Vector<unsigned>* graphemes); Vector<unsigned>* graphemes);
......
...@@ -75,8 +75,7 @@ class TextBreakIteratorTest : public testing::Test { ...@@ -75,8 +75,7 @@ class TextBreakIteratorTest : public testing::Test {
unsigned start, unsigned start,
unsigned length) { unsigned length) {
Vector<unsigned> result; Vector<unsigned> result;
::blink::GraphemesClusterList(input.Characters16(), input.length(), start, ::blink::GraphemesClusterList(input, start, length, &result);
length, &result);
return result; return result;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment