Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: third_party/WebKit/Source/platform/fonts/shaping/ShapingLineBreaker.cpp

Issue 2875933006: [LayoutNG] Misc fixes in ShapingLineBreaker (Closed)
Patch Set: eae review + RTL snapping fixes Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 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 "platform/fonts/shaping/ShapingLineBreaker.h" 5 #include "platform/fonts/shaping/ShapingLineBreaker.h"
6 6
7 #include "platform/fonts/Font.h" 7 #include "platform/fonts/Font.h"
8 #include "platform/fonts/shaping/HarfBuzzShaper.h" 8 #include "platform/fonts/shaping/HarfBuzzShaper.h"
9 #include "platform/fonts/shaping/ShapeResult.h" 9 #include "platform/fonts/shaping/ShapeResult.h"
10 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" 10 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h"
11 #include "platform/text/TextBreakIterator.h" 11 #include "platform/text/TextBreakIterator.h"
12 12
13 namespace blink { 13 namespace blink {
14 14
15 ShapingLineBreaker::ShapingLineBreaker(const HarfBuzzShaper* shaper, 15 ShapingLineBreaker::ShapingLineBreaker(
16 const Font* font, 16 const HarfBuzzShaper* shaper,
17 const ShapeResult* result, 17 const Font* font,
18 const AtomicString locale, 18 const ShapeResult* result,
19 LineBreakType break_type) 19 const LazyLineBreakIterator* break_iterator)
20 : shaper_(shaper), 20 : shaper_(shaper),
21 font_(font), 21 font_(font),
22 result_(result), 22 result_(result),
23 locale_(locale), 23 break_iterator_(break_iterator) {
24 break_type_(break_type) {
25 text_ = String(shaper->GetText(), shaper->TextLength()); 24 text_ = String(shaper->GetText(), shaper->TextLength());
26 } 25 }
27 26
28 namespace { 27 namespace {
29 28
30 unsigned PreviousSafeToBreakAfter(const UChar* text, 29 unsigned PreviousSafeToBreakAfter(const UChar* text,
31 unsigned start, 30 unsigned start,
32 unsigned offset) { 31 unsigned offset) {
33 // TODO(eae): This is quite incorrect. It should be changed to use the 32 // TODO(eae): This is quite incorrect. It should be changed to use the
34 // HarfBuzzHarfBuzz safe to break info when available. 33 // HarfBuzzHarfBuzz safe to break info when available.
35 for (; offset > start; offset--) { 34 for (; offset > start; offset--) {
36 if (text[offset - 1] == kSpaceCharacter) 35 if (text[offset - 1] == kSpaceCharacter)
37 break; 36 break;
38 } 37 }
39 return offset; 38 return offset;
40 } 39 }
41 40
42 unsigned NextSafeToBreakBefore(const UChar* text, 41 unsigned NextSafeToBreakBefore(const UChar* text,
43 unsigned end, 42 unsigned end,
44 unsigned offset) { 43 unsigned offset) {
45 // TODO(eae): This is quite incorrect. It should be changed to use the 44 // TODO(eae): This is quite incorrect. It should be changed to use the
46 // HarfBuzzHarfBuzz safe to break info when available. 45 // HarfBuzzHarfBuzz safe to break info when available.
47 for (; offset < end; offset++) { 46 for (; offset < end; offset++) {
48 if (text[offset] == kSpaceCharacter) 47 if (text[offset] == kSpaceCharacter)
49 break; 48 break;
50 } 49 }
51 return offset; 50 return offset;
52 } 51 }
53 52
53 // ShapingLineBreaker computes using visual positions. This function flips
54 // logical advance to visual, or vice versa.
55 LayoutUnit FlipRtl(LayoutUnit value, TextDirection direction) {
56 return direction != TextDirection::kRtl ? value : -value;
57 }
58
59 // Snaps a visual position to the line start direction.
60 LayoutUnit SnapStart(float value, TextDirection direction) {
61 return direction != TextDirection::kRtl ? LayoutUnit::FromFloatFloor(value)
62 : LayoutUnit::FromFloatCeil(value);
63 }
64
65 // Snaps a visual position to the line end direction.
66 LayoutUnit SnapEnd(float value, TextDirection direction) {
67 return direction != TextDirection::kRtl ? LayoutUnit::FromFloatCeil(value)
68 : LayoutUnit::FromFloatFloor(value);
69 }
70
54 } // namespace 71 } // namespace
55 72
56 // Shapes a line of text by finding a valid and appropriate break opportunity 73 // Shapes a line of text by finding a valid and appropriate break opportunity
57 // based on the shaping results for the entire paragraph. Re-shapes the start 74 // based on the shaping results for the entire paragraph. Re-shapes the start
58 // and end of the line as needed. 75 // and end of the line as needed.
59 // 76 //
60 // Definitions: 77 // Definitions:
61 // Candidate break opportunity: Ideal point to break, disregarding line 78 // Candidate break opportunity: Ideal point to break, disregarding line
62 // breaking rules. May be in the middle of a word 79 // breaking rules. May be in the middle of a word
63 // or inside a ligature. 80 // or inside a ligature.
(...skipping 15 matching lines...) Expand all
79 // The candidate (or ideal) break opportunity would be at an offset of 10 as 96 // The candidate (or ideal) break opportunity would be at an offset of 10 as
80 // the break would happen at exactly 100px in that case. 97 // the break would happen at exactly 100px in that case.
81 // The previous valid break opportunity though is at an offset of 5. 98 // The previous valid break opportunity though is at an offset of 5.
82 // If we further assume that the font kerns with space then even though it's a 99 // If we further assume that the font kerns with space then even though it's a
83 // valid break opportunity reshaping is required as the combined width of the 100 // valid break opportunity reshaping is required as the combined width of the
84 // two segments "Line " and "breaking" may be different from "Line breaking". 101 // two segments "Line " and "breaking" may be different from "Line breaking".
85 PassRefPtr<ShapeResult> ShapingLineBreaker::ShapeLine( 102 PassRefPtr<ShapeResult> ShapingLineBreaker::ShapeLine(
86 unsigned start, 103 unsigned start,
87 LayoutUnit available_space, 104 LayoutUnit available_space,
88 unsigned* break_offset) { 105 unsigned* break_offset) {
106 DCHECK_GE(available_space, LayoutUnit(0));
107 unsigned range_start = result_->StartIndexForResult();
108 unsigned range_end = result_->EndIndexForResult();
109 DCHECK_GE(start, range_start);
110 DCHECK_LT(start, range_end);
111
89 // The start position in the original shape results. 112 // The start position in the original shape results.
90 LayoutUnit start_position = result_->SnappedStartPositionForOffset(start); 113 float start_position_float = result_->PositionForOffset(start - range_start);
91 TextDirection direction = result_->Direction(); 114 TextDirection direction = result_->Direction();
115 LayoutUnit start_position = SnapStart(start_position_float, direction);
116
117 // Find a candidate break opportunity by identifying the last offset before
118 // exceeding the available space and the determine the closest valid break
119 // preceding the candidate.
120 LayoutUnit end_position = SnapEnd(start_position_float, direction) +
121 FlipRtl(available_space, direction);
122 DCHECK_GE(FlipRtl(end_position - start_position, direction), LayoutUnit(0));
123 unsigned candidate_break =
124 result_->OffsetForPosition(end_position, false) + range_start;
125 // candidate_break should be >= start, but rounding errors can chime in when
126 // comparing floats. See ShapeLineZeroAvailableWidth on Linux/Mac.
127 candidate_break = std::max(candidate_break, start);
128 unsigned break_opportunity =
129 break_iterator_->PreviousBreakOpportunity(candidate_break, start);
130 if (break_opportunity <= start) {
131 break_opportunity = break_iterator_->NextBreakOpportunity(
132 std::max(candidate_break, start + 1));
133 }
134 DCHECK_GT(break_opportunity, start);
92 135
93 // If the start offset is not at a safe-to-break boundary the content between 136 // If the start offset is not at a safe-to-break boundary the content between
94 // the start and the next safe-to-break boundary needs to be reshaped and the 137 // the start and the next safe-to-break boundary needs to be reshaped and the
95 // available space adjusted to take the reshaping into account. 138 // available space adjusted to take the reshaping into account.
96 RefPtr<ShapeResult> line_start_result; 139 RefPtr<ShapeResult> line_start_result;
97 unsigned first_safe = 140 unsigned first_safe =
98 NextSafeToBreakBefore(shaper_->GetText(), shaper_->TextLength(), start); 141 NextSafeToBreakBefore(shaper_->GetText(), shaper_->TextLength(), start);
99 if (first_safe != start) { 142 DCHECK_GE(first_safe, start);
143 // Reshape takes place only when first_safe is before the break opportunity.
144 // Otherwise reshape will be part of line_end_result.
145 if (first_safe != start && first_safe < break_opportunity) {
100 LayoutUnit original_width = 146 LayoutUnit original_width =
101 result_->SnappedEndPositionForOffset(first_safe) - start_position; 147 FlipRtl(SnapEnd(result_->PositionForOffset(first_safe - range_start),
148 direction) -
149 start_position,
150 direction);
102 line_start_result = shaper_->Shape(font_, direction, start, first_safe); 151 line_start_result = shaper_->Shape(font_, direction, start, first_safe);
103 available_space += line_start_result->SnappedWidth() - original_width; 152 available_space += line_start_result->SnappedWidth() - original_width;
104 } 153 }
105 154
106 // Find a candidate break opportunity by identifying the last offset before
107 // exceeding the available space and the determine the closest valid break
108 // preceding the candidate.
109 LazyLineBreakIterator break_iterator(text_, locale_, break_type_);
110 LayoutUnit end_position = start_position + available_space;
111 unsigned candidate_break = result_->OffsetForPosition(end_position, false);
112 unsigned break_opportunity =
113 break_iterator.PreviousBreakOpportunity(candidate_break, start);
114 if (break_opportunity <= start) {
115 break_opportunity = break_iterator.NextBreakOpportunity(candidate_break);
116 }
117
118 RefPtr<ShapeResult> line_end_result; 155 RefPtr<ShapeResult> line_end_result;
119 unsigned last_safe = break_opportunity; 156 unsigned last_safe = break_opportunity;
120 while (break_opportunity > start) { 157 while (break_opportunity > start) {
121 // If the previous valid break opportunity is not at a safe-to-break 158 // If the previous valid break opportunity is not at a safe-to-break
122 // boundary reshape between the safe-to-break offset and the valid break 159 // boundary reshape between the safe-to-break offset and the valid break
123 // offset. If the resulting width exceeds the available space the 160 // offset. If the resulting width exceeds the available space the
124 // preceding boundary is tried until the available space is sufficient. 161 // preceding boundary is tried until the available space is sufficient.
125 unsigned previous_safe = std::max( 162 unsigned previous_safe = std::max(
126 PreviousSafeToBreakAfter(shaper_->GetText(), start, break_opportunity), 163 PreviousSafeToBreakAfter(shaper_->GetText(), start, break_opportunity),
127 start); 164 start);
165 DCHECK_LE(previous_safe, break_opportunity);
128 if (previous_safe != break_opportunity) { 166 if (previous_safe != break_opportunity) {
129 LayoutUnit safe_position = 167 LayoutUnit safe_position = SnapStart(
130 result_->SnappedStartPositionForOffset(previous_safe); 168 result_->PositionForOffset(previous_safe - range_start), direction);
131 while (break_opportunity > previous_safe && previous_safe > start) { 169 while (break_opportunity > previous_safe && previous_safe >= start) {
132 line_end_result = 170 line_end_result =
133 shaper_->Shape(font_, direction, previous_safe, break_opportunity); 171 shaper_->Shape(font_, direction, previous_safe,
134 if (safe_position + line_end_result->SnappedWidth() <= end_position) 172 std::min(break_opportunity, range_end));
173 if (line_end_result->SnappedWidth() <=
174 FlipRtl(end_position - safe_position, direction))
135 break; 175 break;
176 // Doesn't fit after the reshape. Try previous break opportunity, or
177 // overflow if there were none.
178 unsigned previous_break_opportunity =
179 break_iterator_->PreviousBreakOpportunity(break_opportunity - 1,
180 start);
181 if (previous_break_opportunity <= start)
182 break;
183 break_opportunity = previous_break_opportunity;
136 line_end_result = nullptr; 184 line_end_result = nullptr;
137 break_opportunity = break_iterator.PreviousBreakOpportunity(
138 break_opportunity - 1, start);
139 } 185 }
140 } 186 }
141 187
142 if (break_opportunity > start) { 188 if (break_opportunity > start) {
143 last_safe = previous_safe; 189 last_safe = previous_safe;
144 break; 190 break;
145 } 191 }
146 192
147 // No suitable break opportunity, not exceeding the available space, 193 // No suitable break opportunity, not exceeding the available space,
148 // found. Choose the next valid one even though it will overflow. 194 // found. Choose the next valid one even though it will overflow.
149 break_opportunity = break_iterator.NextBreakOpportunity(candidate_break); 195 break_opportunity = break_iterator_->NextBreakOpportunity(candidate_break);
150 } 196 }
151 197
152 // Create shape results for the line by copying from the re-shaped result (if 198 // Create shape results for the line by copying from the re-shaped result (if
153 // reshaping was needed) and the original shape results. 199 // reshaping was needed) and the original shape results.
154 RefPtr<ShapeResult> line_result = ShapeResult::Create(font_, 0, direction); 200 RefPtr<ShapeResult> line_result = ShapeResult::Create(font_, 0, direction);
155 unsigned max_length = std::numeric_limits<unsigned>::max(); 201 unsigned max_length = std::numeric_limits<unsigned>::max();
156 if (line_start_result) 202 if (line_start_result)
157 line_start_result->CopyRange(0, max_length, line_result.Get()); 203 line_start_result->CopyRange(0, max_length, line_result.Get());
158 if (last_safe > first_safe) 204 if (last_safe > first_safe)
159 result_->CopyRange(first_safe, last_safe, line_result.Get()); 205 result_->CopyRange(first_safe, last_safe, line_result.Get());
160 if (line_end_result) 206 if (line_end_result)
161 line_end_result->CopyRange(last_safe, max_length, line_result.Get()); 207 line_end_result->CopyRange(last_safe, max_length, line_result.Get());
162 208
209 DCHECK_GT(break_opportunity, start);
210 DCHECK_EQ(std::min(break_opportunity, range_end) - start,
211 line_result->NumCharacters());
212
163 *break_offset = break_opportunity; 213 *break_offset = break_opportunity;
164 return line_result.Release(); 214 return line_result.Release();
165 } 215 }
166 216
167 } // namespace blink 217 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698