• 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/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     , fHeightMultiplier(heightMultiplier)
31     , fUseHalfLeading(useHalfLeading)
32     , fBaselineShift(baselineShift)
33 {
34     fBidiLevel = info.fBidiLevel;
35     fAdvance = info.fAdvance;
36     fIndex = index;
37     fUtf8Range = info.utf8Range;
38     fOffset = SkVector::Make(offsetX, 0);
39     fGlyphs.push_back_n(info.glyphCount);
40     fBounds.push_back_n(info.glyphCount);
41     fPositions.push_back_n(info.glyphCount + 1);
42     fClusterIndexes.push_back_n(info.glyphCount + 1);
43     fShifts.push_back_n(info.glyphCount + 1, 0.0);
44     info.fFont.getMetrics(&fFontMetrics);
45 
46     this->calculateMetrics();
47 
48     fSpaced = false;
49     // To make edge cases easier:
50     fPositions[info.glyphCount] = fOffset + fAdvance;
51     fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
52     fEllipsis = false;
53     fPlaceholderIndex = std::numeric_limits<size_t>::max();
54 }
55 
calculateMetrics()56 void Run::calculateMetrics() {
57     fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5;
58     fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5;
59     fCorrectLeading = 0;
60     if (SkScalarNearlyZero(fHeightMultiplier)) {
61         return;
62     }
63     const auto runHeight = fHeightMultiplier * fFont.getSize();
64     const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent;
65     if (fUseHalfLeading) {
66         const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2;
67         fCorrectAscent -= extraLeading;
68         fCorrectDescent += extraLeading;
69     } else {
70         const auto multiplier = runHeight / fontIntrinsicHeight;
71         fCorrectAscent *= multiplier;
72         fCorrectDescent *= multiplier;
73     }
74     // If we shift the baseline we need to make sure the shifted text fits the line
75     fCorrectAscent += fBaselineShift;
76     fCorrectDescent += fBaselineShift;
77 }
78 
newRunBuffer()79 SkShaper::RunHandler::Buffer Run::newRunBuffer() {
80     return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
81 }
82 
commit()83 void Run::commit() {
84     fFont.getBounds(fGlyphs.data(), fGlyphs.size(), fBounds.data(), nullptr);
85 }
86 
copyTo(SkTextBlobBuilder & builder,size_t pos,size_t size) const87 void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size) const {
88     SkASSERT(pos + size <= this->size());
89     const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
90     sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
91 
92     if (!fSpaced && fJustificationShifts.empty()) {
93         sk_careful_memcpy(blobBuffer.points(), fPositions.data() + pos, size * sizeof(SkPoint));
94     } else {
95         for (size_t i = 0; i < size; ++i) {
96             auto point = fPositions[i + pos];
97             if (fSpaced) {
98                 point.fX += fShifts[i + pos];
99             }
100             if (!fJustificationShifts.empty()) {
101                 point.fX += fJustificationShifts[i + pos].fX;
102             }
103             blobBuffer.points()[i] = point;
104         }
105     }
106 }
107 
108 // Find a cluster range from text range (within one run)
109 // Cluster range is normalized ([start:end) start < end regardless of TextDirection
110 // Boolean value in triple indicates whether the cluster range was found or not
findLimitingClusters(TextRange text) const111 std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) const {
112     if (text.width() == 0) {
113         // Special Flutter case for "\n" and "...\n"
114         if (text.end > this->fTextRange.start) {
115             ClusterIndex index = fOwner->clusterIndex(text.end - 1);
116             return std::make_tuple(true, index, index);
117         } else {
118             return std::make_tuple(false, 0, 0);
119         }
120     }
121 
122     ClusterIndex startIndex = fOwner->clusterIndex(text.start);
123     ClusterIndex endIndex = fOwner->clusterIndex(text.end - 1);
124     if (!leftToRight()) {
125         std::swap(startIndex, endIndex);
126     }
127     return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
128 }
129 
130 // Adjust the text to grapheme edges so the first grapheme start is in the text and the last grapheme start is in the text
131 // It actually means that the first grapheme is entirely in the text and the last grapheme does not have to be
findLimitingGraphemes(TextRange text) const132 std::tuple<bool, TextIndex, TextIndex> Run::findLimitingGraphemes(TextRange text) const {
133     TextIndex start = fOwner->findNextGraphemeBoundary(text.start);
134     TextIndex end = fOwner->findNextGraphemeBoundary(text.end);
135     return std::make_tuple(true, start, end);
136 }
137 
iterateThroughClusters(const ClusterVisitor & visitor)138 void Run::iterateThroughClusters(const ClusterVisitor& visitor) {
139 
140     for (size_t index = 0; index < fClusterRange.width(); ++index) {
141         auto correctIndex = leftToRight() ? fClusterRange.start + index : fClusterRange.end - index - 1;
142         auto cluster = &fOwner->cluster(correctIndex);
143         visitor(cluster);
144     }
145 }
146 
addSpacesAtTheEnd(SkScalar space,Cluster * cluster)147 SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
148     if (cluster->endPos() == cluster->startPos()) {
149         return 0;
150     }
151 
152     fShifts[cluster->endPos() - 1] += space;
153     // Increment the run width
154     fSpaced = true;
155     fAdvance.fX += space;
156     // Increment the cluster width
157     cluster->space(space, space);
158 
159     return space;
160 }
161 
addSpacesEvenly(SkScalar space,Cluster * cluster)162 SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
163     // Offset all the glyphs in the cluster
164     SkScalar shift = 0;
165     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
166         fShifts[i] += shift;
167         shift += space;
168     }
169     if (this->size() == cluster->endPos()) {
170         // To make calculations easier
171         fShifts[cluster->endPos()] += shift;
172     }
173     // Increment the run width
174     fSpaced = true;
175     fAdvance.fX += shift;
176     // Increment the cluster width
177     cluster->space(shift, space);
178     cluster->setHalfLetterSpacing(space / 2);
179 
180     return shift;
181 }
182 
shift(const Cluster * cluster,SkScalar offset)183 void Run::shift(const Cluster* cluster, SkScalar offset) {
184     if (offset == 0) {
185         return;
186     }
187 
188     fSpaced = true;
189     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
190         fShifts[i] += offset;
191     }
192     if (this->size() == cluster->endPos()) {
193         // To make calculations easier
194         fShifts[cluster->endPos()] += offset;
195     }
196 }
197 
updateMetrics(InternalLineMetrics * endlineMetrics)198 void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
199 
200     SkASSERT(isPlaceholder());
201     auto placeholderStyle = this->placeholderStyle();
202     // Difference between the placeholder baseline and the line bottom
203     SkScalar baselineAdjustment = 0;
204     switch (placeholderStyle->fBaseline) {
205         case TextBaseline::kAlphabetic:
206             break;
207 
208         case TextBaseline::kIdeographic:
209             baselineAdjustment = endlineMetrics->deltaBaselines() / 2;
210             break;
211     }
212 
213     auto height = placeholderStyle->fHeight;
214     auto offset = placeholderStyle->fBaselineOffset;
215 
216     fFontMetrics.fLeading = 0;
217     switch (placeholderStyle->fAlignment) {
218         case PlaceholderAlignment::kBaseline:
219             fFontMetrics.fAscent = baselineAdjustment - offset;
220             fFontMetrics.fDescent = baselineAdjustment + height - offset;
221             break;
222 
223         case PlaceholderAlignment::kAboveBaseline:
224             fFontMetrics.fAscent = baselineAdjustment - height;
225             fFontMetrics.fDescent = baselineAdjustment;
226             break;
227 
228         case PlaceholderAlignment::kBelowBaseline:
229             fFontMetrics.fAscent = baselineAdjustment;
230             fFontMetrics.fDescent = baselineAdjustment + height;
231             break;
232 
233         case PlaceholderAlignment::kTop:
234             fFontMetrics.fDescent = height + fFontMetrics.fAscent;
235             break;
236 
237         case PlaceholderAlignment::kBottom:
238             fFontMetrics.fAscent = fFontMetrics.fDescent - height;
239             break;
240 
241         case PlaceholderAlignment::kMiddle:
242             auto mid = (-fFontMetrics.fDescent - fFontMetrics.fAscent)/2.0;
243             fFontMetrics.fDescent = height/2.0 - mid;
244             fFontMetrics.fAscent =  - height/2.0 - mid;
245             break;
246     }
247 
248     this->calculateMetrics();
249 
250     // Make sure the placeholder can fit the line
251     endlineMetrics->add(this);
252 }
253 
sizeToChar(TextIndex ch) const254 SkScalar Cluster::sizeToChar(TextIndex ch) const {
255     if (ch < fTextRange.start || ch >= fTextRange.end) {
256         return 0;
257     }
258     auto shift = ch - fTextRange.start;
259     auto ratio = shift * 1.0 / fTextRange.width();
260 
261     return SkDoubleToScalar(fWidth * ratio);
262 }
263 
sizeFromChar(TextIndex ch) const264 SkScalar Cluster::sizeFromChar(TextIndex ch) const {
265     if (ch < fTextRange.start || ch >= fTextRange.end) {
266         return 0;
267     }
268     auto shift = fTextRange.end - ch - 1;
269     auto ratio = shift * 1.0 / fTextRange.width();
270 
271     return SkDoubleToScalar(fWidth * ratio);
272 }
273 
roundPos(SkScalar s) const274 size_t Cluster::roundPos(SkScalar s) const {
275     auto ratio = (s * 1.0) / fWidth;
276     return sk_double_floor2int(ratio * size());
277 }
278 
trimmedWidth(size_t pos) const279 SkScalar Cluster::trimmedWidth(size_t pos) const {
280     // Find the width until the pos and return the min between trimmedWidth and the width(pos)
281     // We don't have to take in account cluster shift since it's the same for 0 and for pos
282     auto& run = fOwner->run(fRunIndex);
283     return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
284 }
285 
positionX(size_t pos) const286 SkScalar Run::positionX(size_t pos) const {
287     return posX(pos) + fShifts[pos] +
288             (fJustificationShifts.empty() ? 0 : fJustificationShifts[pos].fY);
289 }
290 
placeholderStyle() const291 PlaceholderStyle* Run::placeholderStyle() const {
292     if (isPlaceholder()) {
293         return &fOwner->placeholders()[fPlaceholderIndex].fStyle;
294     } else {
295         return nullptr;
296     }
297 }
298 
runOrNull() const299 Run* Cluster::runOrNull() const {
300     if (fRunIndex >= fOwner->runs().size()) {
301         return nullptr;
302     }
303     return &fOwner->run(fRunIndex);
304 }
305 
run() const306 Run& Cluster::run() const {
307     SkASSERT(fRunIndex < fOwner->runs().size());
308     return fOwner->run(fRunIndex);
309 }
310 
font() const311 SkFont Cluster::font() const {
312     SkASSERT(fRunIndex < fOwner->runs().size());
313     return fOwner->run(fRunIndex).font();
314 }
315 
isSoftBreak() const316 bool Cluster::isSoftBreak() const {
317     return fOwner->codeUnitHasProperty(fTextRange.end, CodeUnitFlags::kSoftLineBreakBefore);
318 }
319 
isGraphemeBreak() const320 bool Cluster::isGraphemeBreak() const {
321     return fOwner->codeUnitHasProperty(fTextRange.end, CodeUnitFlags::kGraphemeStart);
322 }
323 }  // namespace textlayout
324 }  // namespace skia
325