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