1 // Copyright 2019 Google LLC.
2 #include "include/core/SkFontMetrics.h"
3 #include "include/core/SkTextBlob.h"
4 #include "include/private/base/SkFloatingPoint.h"
5 #include "include/private/base/SkMalloc.h"
6 #include "include/private/base/SkTo.h"
7 #include "modules/skparagraph/include/DartTypes.h"
8 #include "modules/skparagraph/include/TextStyle.h"
9 #include "modules/skparagraph/src/ParagraphImpl.h"
10 #include "modules/skparagraph/src/Run.h"
11 #include "modules/skshaper/include/SkShaper.h"
12 #include "src/base/SkUTF.h"
13
14 namespace skia {
15 namespace textlayout {
16
Run(ParagraphImpl * owner,const SkShaper::RunHandler::RunInfo & info,size_t firstChar,SkScalar heightMultiplier,bool useHalfLeading,SkScalar baselineShift,size_t index,SkScalar offsetX)17 Run::Run(ParagraphImpl* owner,
18 const SkShaper::RunHandler::RunInfo& info,
19 size_t firstChar,
20 SkScalar heightMultiplier,
21 bool useHalfLeading,
22 SkScalar baselineShift,
23 size_t index,
24 SkScalar offsetX)
25 : fOwner(owner)
26 , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
27 , fClusterRange(EMPTY_CLUSTERS)
28 , fFont(info.fFont)
29 , fClusterStart(firstChar)
30 , fGlyphData(std::make_shared<GlyphData>())
31 , fGlyphs(fGlyphData->glyphs)
32 , fPositions(fGlyphData->positions)
33 , fOffsets(fGlyphData->offsets)
34 , fClusterIndexes(fGlyphData->clusterIndexes)
35 , fHeightMultiplier(heightMultiplier)
36 , fUseHalfLeading(useHalfLeading)
37 , fBaselineShift(baselineShift)
38 {
39 fBidiLevel = info.fBidiLevel;
40 fAdvance = info.fAdvance;
41 fIndex = index;
42 fUtf8Range = info.utf8Range;
43 fOffset = SkVector::Make(offsetX, 0);
44
45 fGlyphs.push_back_n(info.glyphCount);
46 fPositions.push_back_n(info.glyphCount + 1);
47 fOffsets.push_back_n(info.glyphCount + 1);
48 fClusterIndexes.push_back_n(info.glyphCount + 1);
49 info.fFont.getMetrics(&fFontMetrics);
50
51 this->calculateMetrics();
52
53 // To make edge cases easier:
54 fPositions[info.glyphCount] = fOffset + fAdvance;
55 fOffsets[info.glyphCount] = {0, 0};
56 fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
57 fEllipsis = false;
58 fPlaceholderIndex = std::numeric_limits<size_t>::max();
59 }
60
calculateMetrics()61 void Run::calculateMetrics() {
62 fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5;
63 fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5;
64 fCorrectLeading = 0;
65 if (SkScalarNearlyZero(fHeightMultiplier)) {
66 return;
67 }
68 const auto runHeight = fHeightMultiplier * fFont.getSize();
69 const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent;
70 if (fUseHalfLeading) {
71 const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2;
72 fCorrectAscent -= extraLeading;
73 fCorrectDescent += extraLeading;
74 } else {
75 const auto multiplier = runHeight / fontIntrinsicHeight;
76 fCorrectAscent *= multiplier;
77 fCorrectDescent *= multiplier;
78 }
79 // If we shift the baseline we need to make sure the shifted text fits the line
80 fCorrectAscent += fBaselineShift;
81 fCorrectDescent += fBaselineShift;
82 }
83
newRunBuffer()84 SkShaper::RunHandler::Buffer Run::newRunBuffer() {
85 return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
86 }
87
copyTo(SkTextBlobBuilder & builder,size_t pos,size_t size) const88 void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size) const {
89 SkASSERT(pos + size <= this->size());
90 const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
91 sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
92
93 for (size_t i = 0; i < size; ++i) {
94 auto point = fPositions[i + pos];
95 if (!fJustificationShifts.empty()) {
96 point.fX += fJustificationShifts[i + pos].fX;
97 }
98 point += fOffsets[i + pos];
99 blobBuffer.points()[i] = point;
100 }
101 }
102
103 // Find a cluster range from text range (within one run)
104 // Cluster range is normalized ([start:end) start < end regardless of TextDirection
105 // Boolean value in triple indicates whether the cluster range was found or not
findLimitingClusters(TextRange text) const106 std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) const {
107 if (text.width() == 0) {
108 // Special Flutter case for "\n" and "...\n"
109 if (text.end > this->fTextRange.start) {
110 ClusterIndex index = fOwner->clusterIndex(text.end - 1);
111 return std::make_tuple(true, index, index);
112 } else {
113 return std::make_tuple(false, 0, 0);
114 }
115 }
116
117 ClusterRange clusterRange;
118 bool found = true;
119 // Deal with the case when either start or end are not align with glyph cluster edge
120 // In such case we shift the text range to the right
121 // (cutting from the left and adding to the right)
122 if (leftToRight()) {
123 // LTR: [start:end)
124 found = clusterRange.start != fClusterRange.end;
125 clusterRange.start = fOwner->clusterIndex(text.start);
126 clusterRange.end = fOwner->clusterIndex(text.end - 1);
127 } else {
128 // RTL: (start:end]
129 clusterRange.start = fOwner->clusterIndex(text.end);
130 clusterRange.end = fOwner->clusterIndex(text.start + 1);
131 found = clusterRange.end != fClusterRange.start;
132 }
133
134 return std::make_tuple(
135 found,
136 clusterRange.start,
137 clusterRange.end);
138 }
139
findLimitingGlyphClusters(TextRange text) const140 std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGlyphClusters(TextRange text) const {
141 TextIndex start = fOwner->findPreviousGlyphClusterBoundary(text.start);
142 TextIndex end = fOwner->findNextGlyphClusterBoundary(text.end);
143 return std::make_tuple(true, start, end);
144 }
145
146 // Adjust the text to grapheme edges so the first grapheme start is in the text and the last grapheme start is in the text
147 // It actually means that the first grapheme is entirely in the text and the last grapheme does not have to be
148 // 12345 234 2:2 -> 2,5 4:4
findLimitingGraphemes(TextRange text) const149 std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGraphemes(TextRange text) const {
150 TextIndex start = fOwner->findPreviousGraphemeBoundary(text.start);
151 TextIndex end = fOwner->findNextGraphemeBoundary(text.end);
152 return std::make_tuple(true, start, end);
153 }
154
iterateThroughClusters(const ClusterVisitor & visitor)155 void Run::iterateThroughClusters(const ClusterVisitor& visitor) {
156
157 for (size_t index = 0; index < fClusterRange.width(); ++index) {
158 auto correctIndex = leftToRight() ? fClusterRange.start + index : fClusterRange.end - index - 1;
159 auto cluster = &fOwner->cluster(correctIndex);
160 visitor(cluster);
161 }
162 }
163
addSpacesAtTheEnd(SkScalar space,Cluster * cluster)164 void Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
165 // Increment the run width
166 fAdvance.fX += space;
167 // Increment the cluster width
168 cluster->space(space);
169 }
170
addSpacesEvenly(SkScalar space)171 SkScalar Run::addSpacesEvenly(SkScalar space) {
172 SkScalar shift = 0;
173 for (size_t i = 0; i < this->size(); ++i) {
174 fPositions[i].fX += shift;
175 shift += space;
176 }
177 fPositions[this->size()].fX += shift;
178 fAdvance.fX += shift;
179 return shift;
180 }
181
addSpacesEvenly(SkScalar space,Cluster * cluster)182 SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
183 // Offset all the glyphs in the cluster
184 SkScalar shift = 0;
185 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
186 fPositions[i].fX += shift;
187 shift += space;
188 }
189 if (this->size() == cluster->endPos()) {
190 // To make calculations easier
191 fPositions[cluster->endPos()].fX += shift;
192 }
193 // Increment the run width
194 fAdvance.fX += shift;
195 // Increment the cluster width
196 cluster->space(shift);
197 cluster->setHalfLetterSpacing(space / 2);
198
199 return shift;
200 }
201
shift(const Cluster * cluster,SkScalar offset)202 void Run::shift(const Cluster* cluster, SkScalar offset) {
203 if (offset == 0) {
204 return;
205 }
206
207 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
208 fPositions[i].fX += offset;
209 }
210 if (this->size() == cluster->endPos()) {
211 // To make calculations easier
212 fPositions[cluster->endPos()].fX += offset;
213 }
214 }
215
updateMetrics(InternalLineMetrics * endlineMetrics)216 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
217
218 SkASSERT(isPlaceholder());
219 auto placeholderStyle = this->placeholderStyle();
220 // Difference between the placeholder baseline and the line bottom
221 SkScalar baselineAdjustment = 0;
222 switch (placeholderStyle->fBaseline) {
223 case TextBaseline::kAlphabetic:
224 break;
225
226 case TextBaseline::kIdeographic:
227 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
228 break;
229 }
230
231 auto height = placeholderStyle->fHeight;
232 auto offset = placeholderStyle->fBaselineOffset;
233
234 fFontMetrics.fLeading = 0;
235 switch (placeholderStyle->fAlignment) {
236 case PlaceholderAlignment::kBaseline:
237 fFontMetrics.fAscent = baselineAdjustment - offset;
238 fFontMetrics.fDescent = baselineAdjustment + height - offset;
239 break;
240
241 case PlaceholderAlignment::kAboveBaseline:
242 fFontMetrics.fAscent = baselineAdjustment - height;
243 fFontMetrics.fDescent = baselineAdjustment;
244 break;
245
246 case PlaceholderAlignment::kBelowBaseline:
247 fFontMetrics.fAscent = baselineAdjustment;
248 fFontMetrics.fDescent = baselineAdjustment + height;
249 break;
250
251 case PlaceholderAlignment::kTop:
252 fFontMetrics.fDescent = height + fFontMetrics.fAscent;
253 break;
254
255 case PlaceholderAlignment::kBottom:
256 fFontMetrics.fAscent = fFontMetrics.fDescent - height;
257 break;
258
259 case PlaceholderAlignment::kMiddle:
260 auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
261 fFontMetrics.fDescent = height/2.0 - mid;
262 fFontMetrics.fAscent = - height/2.0 - mid;
263 break;
264 }
265
266 this->calculateMetrics();
267
268 // Make sure the placeholder can fit the line
269 endlineMetrics->add(this);
270 }
271
sizeToChar(TextIndex ch) const272 SkScalar Cluster::sizeToChar(TextIndex ch) const {
273 if (ch < fTextRange.start || ch >= fTextRange.end) {
274 return 0;
275 }
276 auto shift = ch - fTextRange.start;
277 auto ratio = shift * 1.0 / fTextRange.width();
278
279 return SkDoubleToScalar(fWidth * ratio);
280 }
281
sizeFromChar(TextIndex ch) const282 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
283 if (ch < fTextRange.start || ch >= fTextRange.end) {
284 return 0;
285 }
286 auto shift = fTextRange.end - ch - 1;
287 auto ratio = shift * 1.0 / fTextRange.width();
288
289 return SkDoubleToScalar(fWidth * ratio);
290 }
291
roundPos(SkScalar s) const292 size_t Cluster::roundPos(SkScalar s) const {
293 auto ratio = (s * 1.0) / fWidth;
294 return sk_double_floor2int(ratio * size());
295 }
296
trimmedWidth(size_t pos) const297 SkScalar Cluster::trimmedWidth(size_t pos) const {
298 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
299 // We don't have to take in account cluster shift since it's the same for 0 and for pos
300 auto& run = fOwner->run(fRunIndex);
301 return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
302 }
303
positionX(size_t pos) const304 SkScalar Run::positionX(size_t pos) const {
305 return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY);
306 }
307
placeholderStyle() const308 PlaceholderStyle* Run::placeholderStyle() const {
309 if (isPlaceholder()) {
310 return &fOwner->placeholders()[fPlaceholderIndex].fStyle;
311 } else {
312 return nullptr;
313 }
314 }
315
isResolved() const316 bool Run::isResolved() const {
317 for (auto& glyph :fGlyphs) {
318 if (glyph == 0) {
319 return false;
320 }
321 }
322 return true;
323 }
324
runOrNull() const325 Run* Cluster::runOrNull() const {
326 if (fRunIndex >= fOwner->runs().size()) {
327 return nullptr;
328 }
329 return &fOwner->run(fRunIndex);
330 }
331
run() const332 Run& Cluster::run() const {
333 SkASSERT(fRunIndex < fOwner->runs().size());
334 return fOwner->run(fRunIndex);
335 }
336
font() const337 SkFont Cluster::font() const {
338 SkASSERT(fRunIndex < fOwner->runs().size());
339 return fOwner->run(fRunIndex).font();
340 }
341
isSoftBreak() const342 bool Cluster::isSoftBreak() const {
343 return fOwner->codeUnitHasProperty(fTextRange.end,
344 SkUnicode::CodeUnitFlags::kSoftLineBreakBefore);
345 }
346
isGraphemeBreak() const347 bool Cluster::isGraphemeBreak() const {
348 return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kGraphemeStart);
349 }
350 } // namespace textlayout
351 } // namespace skia
352