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