1 // Copyright 2019 Google LLC.
2 #include "include/core/SkFontMetrics.h"
3 #include "include/core/SkTextBlob.h"
4 #include "include/private/SkFloatingPoint.h"
5 #include "include/private/SkMalloc.h"
6 #include "include/private/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/utils/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 if (this->index() == 0) {
188 fPositions[i].fX += space / 2; // offset by space / 2
189 }
190 shift += space;
191 }
192 if (this->size() == cluster->endPos()) {
193 // To make calculations easier
194 fPositions[cluster->endPos()].fX += shift;
195 }
196 // Increment the run width
197 fAdvance.fX += shift;
198 // Increment the cluster width
199 cluster->space(shift);
200 cluster->setHalfLetterSpacing(space / 2);
201
202 return shift;
203 }
204
shift(const Cluster * cluster,SkScalar offset)205 void Run::shift(const Cluster* cluster, SkScalar offset) {
206 if (offset == 0) {
207 return;
208 }
209
210 for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
211 fPositions[i].fX += offset;
212 }
213 if (this->size() == cluster->endPos()) {
214 // To make calculations easier
215 fPositions[cluster->endPos()].fX += offset;
216 }
217 }
218
updateMetrics(InternalLineMetrics * endlineMetrics)219 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
220
221 SkASSERT(isPlaceholder());
222 auto placeholderStyle = this->placeholderStyle();
223 // Difference between the placeholder baseline and the line bottom
224 SkScalar baselineAdjustment = 0;
225 switch (placeholderStyle->fBaseline) {
226 case TextBaseline::kAlphabetic:
227 break;
228
229 case TextBaseline::kIdeographic:
230 baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
231 break;
232 }
233
234 auto height = placeholderStyle->fHeight;
235 auto offset = placeholderStyle->fBaselineOffset;
236
237 fFontMetrics.fLeading = 0;
238 switch (placeholderStyle->fAlignment) {
239 case PlaceholderAlignment::kBaseline:
240 fFontMetrics.fAscent = baselineAdjustment - offset;
241 fFontMetrics.fDescent = baselineAdjustment + height - offset;
242 break;
243
244 case PlaceholderAlignment::kAboveBaseline:
245 fFontMetrics.fAscent = baselineAdjustment - height;
246 fFontMetrics.fDescent = baselineAdjustment;
247 break;
248
249 case PlaceholderAlignment::kBelowBaseline:
250 fFontMetrics.fAscent = baselineAdjustment;
251 fFontMetrics.fDescent = baselineAdjustment + height;
252 break;
253
254 case PlaceholderAlignment::kTop:
255 fFontMetrics.fDescent = height + fFontMetrics.fAscent;
256 break;
257
258 case PlaceholderAlignment::kBottom:
259 fFontMetrics.fAscent = fFontMetrics.fDescent - height;
260 break;
261
262 case PlaceholderAlignment::kMiddle:
263 auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
264 fFontMetrics.fDescent = height/2.0 - mid;
265 fFontMetrics.fAscent = - height/2.0 - mid;
266 break;
267 }
268
269 this->calculateMetrics();
270
271 // Make sure the placeholder can fit the line
272 endlineMetrics->add(this);
273 }
274
sizeToChar(TextIndex ch) const275 SkScalar Cluster::sizeToChar(TextIndex ch) const {
276 if (ch < fTextRange.start || ch >= fTextRange.end) {
277 return 0;
278 }
279 auto shift = ch - fTextRange.start;
280 auto ratio = shift * 1.0 / fTextRange.width();
281
282 return SkDoubleToScalar(fWidth * ratio);
283 }
284
sizeFromChar(TextIndex ch) const285 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
286 if (ch < fTextRange.start || ch >= fTextRange.end) {
287 return 0;
288 }
289 auto shift = fTextRange.end - ch - 1;
290 auto ratio = shift * 1.0 / fTextRange.width();
291
292 return SkDoubleToScalar(fWidth * ratio);
293 }
294
roundPos(SkScalar s) const295 size_t Cluster::roundPos(SkScalar s) const {
296 auto ratio = (s * 1.0) / fWidth;
297 return sk_double_floor2int(ratio * size());
298 }
299
trimmedWidth(size_t pos) const300 SkScalar Cluster::trimmedWidth(size_t pos) const {
301 // Find the width until the pos and return the min between trimmedWidth and the width(pos)
302 // We don't have to take in account cluster shift since it's the same for 0 and for pos
303 auto& run = fOwner->run(fRunIndex);
304 return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
305 }
306
positionX(size_t pos) const307 SkScalar Run::positionX(size_t pos) const {
308 return posX(pos) + (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY);
309 }
310
placeholderStyle() const311 PlaceholderStyle* Run::placeholderStyle() const {
312 if (isPlaceholder()) {
313 return &fOwner->placeholders()[fPlaceholderIndex].fStyle;
314 } else {
315 return nullptr;
316 }
317 }
318
isResolved() const319 bool Run::isResolved() const {
320 for (auto& glyph :fGlyphs) {
321 if (glyph == 0) {
322 return false;
323 }
324 }
325 return true;
326 }
327
runOrNull() const328 Run* Cluster::runOrNull() const {
329 if (fRunIndex >= fOwner->runs().size()) {
330 return nullptr;
331 }
332 return &fOwner->run(fRunIndex);
333 }
334
run() const335 Run& Cluster::run() const {
336 SkASSERT(fRunIndex < fOwner->runs().size());
337 return fOwner->run(fRunIndex);
338 }
339
font() const340 SkFont Cluster::font() const {
341 SkASSERT(fRunIndex < fOwner->runs().size());
342 return fOwner->run(fRunIndex).font();
343 }
344
isSoftBreak() const345 bool Cluster::isSoftBreak() const {
346 return fOwner->codeUnitHasProperty(fTextRange.end,
347 SkUnicode::CodeUnitFlags::kSoftLineBreakBefore);
348 }
349
isGraphemeBreak() const350 bool Cluster::isGraphemeBreak() const {
351 return fOwner->codeUnitHasProperty(fTextRange.end, SkUnicode::CodeUnitFlags::kGraphemeStart);
352 }
353 } // namespace textlayout
354 } // namespace skia
355