• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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