• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google LLC.
2 #ifndef TextLine_DEFINED
3 #define TextLine_DEFINED
4 
5 #include "include/core/SkPoint.h"
6 #include "include/core/SkRect.h"
7 #include "include/core/SkScalar.h"
8 #include "include/private/base/SkTArray.h"
9 #include "modules/skparagraph/include/DartTypes.h"
10 #include "modules/skparagraph/include/Metrics.h"
11 #include "modules/skparagraph/include/ParagraphPainter.h"
12 #ifdef ENABLE_TEXT_ENHANCE
13 #include "modules/skparagraph/include/RunBase.h"
14 #include "modules/skparagraph/include/TextLineBase.h"
15 #include "modules/skparagraph/include/ParagraphStyle.h"
16 #endif
17 #include "modules/skparagraph/include/TextStyle.h"
18 #include "modules/skparagraph/src/Run.h"
19 #include "src/base/SkBitmaskEnum.h"
20 
21 #include <stddef.h>
22 #include <functional>
23 #include <memory>
24 #include <vector>
25 
26 class SkString;
27 
28 namespace skia {
29 namespace textlayout {
30 #ifdef ENABLE_TEXT_ENHANCE
31 const size_t BOTTOM_PADDING_FACTOR = 8;
32 #endif
33 
34 class ParagraphImpl;
35 #ifdef ENABLE_TEXT_ENHANCE
36 struct DecorationContext {
37     SkScalar thickness{0.0f};
38     SkScalar underlinePosition{0.0f};
39     SkScalar textBlobTop{0.0f};
40     SkScalar lineHeight{0.0f};
41 };
42 
43 struct IterateRunsContext {
44     size_t runIndex{0};
45     SkScalar width{0};
46     SkScalar runOffset{0};
47     SkScalar totalWidth{0};
48     bool isAlreadyUseEllipsis{false};
49     TextRange lineIntersection;
50     EllipsisModal ellipsisMode{EllipsisModal::NONE};
51 };
52 #endif
53 class TextLine {
54 public:
55 
56     struct ClipContext {
57       const Run* run;
58       size_t pos;
59       size_t size;
60       SkScalar fTextShift; // Shifts the text inside the run so it's placed at the right position
61       SkRect clip;
62       SkScalar fExcludedTrailingSpaces;
63       bool clippingNeeded;
64 #ifdef ENABLE_TEXT_ENHANCE
65       bool fIsTrimTrailingSpaceWidth{false};
66       SkScalar fTrailingSpaceWidth{0.0f};
67 #endif
68     };
69 
70 #ifdef ENABLE_TEXT_ENHANCE
71     struct PathParameters {
72         const RSPath* recordPath{nullptr};
73         SkScalar hOffset{0};
74         SkScalar vOffset{0};
75     } pathParameters;
76 #endif
77 
78     enum TextAdjustment {
79         GlyphCluster = 0x01,    // All text producing glyphs pointing to the same ClusterIndex
80         GlyphemeCluster = 0x02, // base glyph + all attached diacritics
81         Grapheme = 0x04,        // Text adjusted to graphemes
82         GraphemeGluster = 0x05, // GlyphCluster & Grapheme
83     };
84 
85 #ifdef ENABLE_TEXT_ENHANCE
86     enum EllipsisReadStrategy {
87         DEFAULT = 0,            // default
88         READ_REPLACED_WORD = 1,  // read replaced word
89         READ_ELLIPSIS_WORD = 2, // read ellipsis word
90     };
91 
92     struct HighLevelInfo {
93         ClusterIndex clusterIndex{SIZE_MAX};
94         bool isClusterPunct{false};
95         SkScalar punctWidths{0.0f};
96         SkScalar highLevelOffset{0.0f};
97     };
98 
99     struct MiddleLevelInfo {
100         ClusterIndex clusterIndex{SIZE_MAX};
101         bool isPrevClusterSpace{true};
102     };
103 
104     struct ClusterLevelsIndices {
105         std::vector<HighLevelInfo> highLevelIndices;
106         std::vector<MiddleLevelInfo> middleLevelIndices;
107         std::vector<ClusterIndex> LowLevelIndices;
108         SkScalar middleLevelOffset{0.0f};
109         SkScalar lowLevelOffset{0.0f};
110 
emptyClusterLevelsIndices111         bool empty()
112         {
113             return highLevelIndices.empty() && middleLevelIndices.empty() && LowLevelIndices.empty();
114         }
115     };
116 
117     enum class ShiftLevel {
118         Undefined,
119         HighLevel, // Level 1 Label: Punctuation
120         MiddleLevel, // Level-2 label: WhitespaceBreak, between ideographic and non-ideographic characters
121         LowLevel // Level-3 label: Between ideographic characters
122     };
123 #endif
124 
125     TextLine() = default;
126     TextLine(const TextLine&) = delete;
127     TextLine& operator=(const TextLine&) = delete;
128     TextLine(TextLine&&) = default;
129     TextLine& operator=(TextLine&&) = default;
130     ~TextLine() = default;
131 
132     TextLine(ParagraphImpl* owner,
133              SkVector offset,
134              SkVector advance,
135              BlockRange blocks,
136              TextRange textExcludingSpaces,
137              TextRange text,
138              TextRange textIncludingNewlines,
139              ClusterRange clusters,
140              ClusterRange clustersWithGhosts,
141              SkScalar widthWithSpaces,
142              InternalLineMetrics sizes);
143 
trimmedText()144     TextRange trimmedText() const { return fTextExcludingSpaces; }
textWithNewlines()145     TextRange textWithNewlines() const { return fTextIncludingNewlines; }
text()146     TextRange text() const { return fText; }
clusters()147     ClusterRange clusters() const { return fClusterRange; }
clustersWithSpaces()148     ClusterRange clustersWithSpaces() const { return fGhostClusterRange; }
ellipsis()149     Run* ellipsis() const { return fEllipsis.get(); }
sizes()150     InternalLineMetrics sizes() const { return fSizes; }
empty()151     bool empty() const { return fTextExcludingSpaces.empty(); }
152 
spacesWidth()153     SkScalar spacesWidth() const { return fWidthWithSpaces - width(); }
height()154     SkScalar height() const { return fAdvance.fY; }
width()155     SkScalar width() const {
156         return fAdvance.fX + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0);
157     }
widthWithoutEllipsis()158     SkScalar widthWithoutEllipsis() const { return fAdvance.fX; }
159 #ifdef ENABLE_TEXT_ENHANCE
widthWithEllipsisSpaces()160     SkScalar widthWithEllipsisSpaces() const {
161         return fWidthWithSpaces + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0);
162     }
163 #endif
164     SkVector offset() const;
165 #ifdef ENABLE_TEXT_ENHANCE
setLineOffsetX(SkScalar x)166     void setLineOffsetX(SkScalar x) {
167         fOffset.set(x, fOffset.y());
168     }
169 #endif
170 
alphabeticBaseline()171     SkScalar alphabeticBaseline() const { return fSizes.alphabeticBaseline(); }
ideographicBaseline()172     SkScalar ideographicBaseline() const { return fSizes.ideographicBaseline(); }
baseline()173     SkScalar baseline() const { return fSizes.baseline(); }
174 #ifdef ENABLE_TEXT_ENHANCE
175     void extendCoordinateRange(PositionWithAffinity& positionWithAffinity);
176 #endif
177 
178     using RunVisitor = std::function<bool(
179             const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width)>;
180 
181 #ifdef ENABLE_TEXT_ENHANCE
182     bool processEllipsisRun(IterateRunsContext& context,
183                             EllipsisReadStrategy ellipsisReadStrategy,
184                             const RunVisitor& visitor,
185                             SkScalar& runWidthInLine) const;
186     bool processInsertedRun(const Run* run,
187                             SkScalar& runOffset,
188                             EllipsisReadStrategy ellipsisReadStrategy,
189                             const RunVisitor& visitor,
190                             SkScalar& runWidthInLine) const;
191     void iterateThroughVisualRuns(EllipsisReadStrategy ellipsisReadStrategy,
192                                   bool includingGhostSpaces,
193                                   const RunVisitor& runVisitor) const;
194     bool handleMiddleEllipsisMode(const Run* run, IterateRunsContext& context,
195                                   EllipsisReadStrategy& ellipsisReadStrategy, const RunVisitor& runVisitor) const;
196 #else
197     void iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisitor& runVisitor) const;
198 #endif
199     using RunStyleVisitor = std::function<void(
200             TextRange textRange, const TextStyle& style, const ClipContext& context)>;
201     SkScalar iterateThroughSingleRunByStyles(TextAdjustment textAdjustment,
202                                              const Run* run,
203                                              SkScalar runOffset,
204                                              TextRange textRange,
205                                              StyleType styleType,
206                                              const RunStyleVisitor& visitor) const;
207 
208     using ClustersVisitor = std::function<bool(const Cluster* cluster, ClusterIndex index, bool ghost)>;
209     void iterateThroughClustersInGlyphsOrder(bool reverse,
210                                              bool includeGhosts,
211                                              const ClustersVisitor& visitor) const;
212 
213 #ifdef ENABLE_TEXT_ENHANCE
214     void format(TextAlign align, SkScalar maxWidth, EllipsisModal ellipsisModal);
215     SkScalar autoSpacing();
216 #else
217 	void format(TextAlign align, SkScalar maxWidth);
218 #endif
219     void paint(ParagraphPainter* painter, SkScalar x, SkScalar y);
220     void visit(SkScalar x, SkScalar y);
221     void ensureTextBlobCachePopulated();
222 
223     void createEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr);
224 
225 #ifdef ENABLE_TEXT_ENHANCE
setParagraphImpl(ParagraphImpl * newpara)226     void setParagraphImpl(ParagraphImpl* newpara) { fOwner = newpara; }
setBlockRange(const BlockRange & blockRange)227     void setBlockRange(const BlockRange& blockRange) { fBlockRange = blockRange; }
228     void countWord(int& wordCount, bool& inWord);
229     void ellipsisNotFitProcess(EllipsisModal ellipsisModal);
230 
231     void createTailEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr, WordBreakType wordBreakType);
232     void handleTailEllipsisInEmptyLine(std::unique_ptr<Run>& ellipsisRun, const SkString& ellipsis,
233         SkScalar width, WordBreakType wordBreakType);
234     void TailEllipsisUpdateLine(Cluster& cluster, float width, size_t clusterIndex, WordBreakType wordBreakType);
235     void createHeadEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr);
236     void paint(ParagraphPainter* painter, const RSPath* path, SkScalar hOffset, SkScalar vOffset);
237     void createMiddleEllipsis(SkScalar maxWidth, const SkString& ellipsis);
238     void middleEllipsisUpdateLine(ClusterIndex& indexS, ClusterIndex& indexE, SkScalar width);
239     bool isLineHeightDominatedByRun(const Run& run);
240     SkScalar updateBlobShift(const Run& run, SkScalar verticalShift, bool isReset);
241     void updateBlobShift(const Run& run, SkScalar& verticalShift);
242     void resetBlobShift(const Run& run);
243     void shiftPlaceholderByVerticalAlignMode(Run& run, TextVerticalAlign VerticalAlignment);
244     void shiftTextByVerticalAlignment(Run& run, TextVerticalAlign VerticalAlignment);
245     void applyPlaceholderVerticalShift();
246     void applyVerticalShift();
247     void updateBlobAndRunShift(Run& run);
248     void refresh();
setLineAllRuns(skia_private::STArray<1,size_t,true> & runsInVisualOrder)249     void setLineAllRuns(skia_private::STArray<1, size_t, true>& runsInVisualOrder) {
250         fRunsInVisualOrder = std::move(runsInVisualOrder);
251     }
setEllipsisRunIndex(size_t runIndex)252     void setEllipsisRunIndex(size_t runIndex) { fEllipsisIndex = runIndex; }
253 #endif
254 
255     // For testing internal structures
256     void scanStyles(StyleType style, const RunStyleVisitor& visitor);
257 
setMaxRunMetrics(const InternalLineMetrics & metrics)258     void setMaxRunMetrics(const InternalLineMetrics& metrics) { fMaxRunMetrics = metrics; }
getMaxRunMetrics()259     InternalLineMetrics getMaxRunMetrics() const { return fMaxRunMetrics; }
260 
261     bool isFirstLine() const;
262     bool isLastLine() const;
263     void getRectsForRange(TextRange textRange,
264                           RectHeightStyle rectHeightStyle,
265                           RectWidthStyle rectWidthStyle,
266                           std::vector<TextBox>& boxes) const;
267     void getRectsForPlaceholders(std::vector<TextBox>& boxes);
268     PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx);
269 
270 #ifdef ENABLE_TEXT_ENHANCE
271     TextLine::ClipContext getRunClipContextByRange(
272         const Run* run, TextRange textRange, TextLine::TextAdjustment textAdjustment, SkScalar textStartInLine) const;
273 #endif
274     ClipContext measureTextInsideOneRun(TextRange textRange,
275                                         const Run* run,
276                                         SkScalar runOffsetInLine,
277                                         SkScalar textOffsetInRunInLine,
278                                         bool includeGhostSpaces,
279                                         TextAdjustment textAdjustment) const;
280 
281     LineMetrics getMetrics() const;
282 
283     SkRect extendHeight(const ClipContext& context) const;
284 
shiftVertically(SkScalar shift)285     void shiftVertically(SkScalar shift) { fOffset.fY += shift; }
286 
setAscentStyle(LineMetricStyle style)287     void setAscentStyle(LineMetricStyle style) { fAscentStyle = style; }
setDescentStyle(LineMetricStyle style)288     void setDescentStyle(LineMetricStyle style) { fDescentStyle = style; }
289 
290     bool endsWithHardLineBreak() const;
291 
292     std::unique_ptr<Run> shapeEllipsis(const SkString& ellipsis, const Cluster* cluster);
293 
294 #ifdef ENABLE_TEXT_ENHANCE
295     std::vector<std::unique_ptr<RunBase>> getGlyphRuns() const;
296     double getTypographicBounds(double* ascent, double* descent, double* leading) const;
297     RSRect getImageBounds() const;
298     int32_t getStringIndexForPosition(SkPoint point) const;
299     size_t getGlyphCount() const;
300     bool endsWithOnlyHardBreak() const;
301     std::unique_ptr<Run> shapeString(const SkString& string, const Cluster* cluster);
getLineAllRuns()302     skia_private::STArray<1, size_t, true> getLineAllRuns() const { return fRunsInVisualOrder; };
303     TextLine CloneSelf();
getTextRangeReplacedByEllipsis()304     TextRange getTextRangeReplacedByEllipsis() const { return fTextRangeReplacedByEllipsis; }
setTextBlobCachePopulated(const bool textBlobCachePopulated)305     void setTextBlobCachePopulated(const bool textBlobCachePopulated) {
306         fTextBlobCachePopulated = textBlobCachePopulated;
307     }
308     SkScalar usingAutoSpaceWidth(const Cluster* cluster) const;
309     std::unique_ptr<TextLineBase> createTruncatedLine(double width, EllipsisModal ellipsisMode,
310         const std::string& ellipsisStr);
311 
312     double getTrailingSpaceWidth() const;
313     double getOffsetForStringIndex(int32_t index) const;
314     std::map<int32_t, double> getIndexAndOffsets(bool& isHardBreak) const;
315     double getAlignmentOffset(double alignmentFactor, double alignmentWidth) const;
316     SkRect generatePaintRegion(SkScalar x, SkScalar y);
317     void updateClusterOffsets(const Cluster* cluster, SkScalar shift, SkScalar prevShift);
318     void justifyUpdateRtlWidth(const SkScalar maxWidth, const SkScalar textLen);
319     void setBreakWithHyphen(bool breakWithHyphen);
320     bool getBreakWithHyphen() const;
321     void updateTextLinePaintAttributes();
322 #endif
323 private:
324 #ifdef ENABLE_TEXT_ENHANCE
325     struct RoundRectAttr {
326         int styleId;
327         RectStyle roundRectStyle;
328         SkRect rect;
329         const Run* run;
330         RoundRectType fRoundRectType = RoundRectType::NONE;
331     };
332 #endif
333     void justify(SkScalar maxWidth);
334 
335     void buildTextBlob(TextRange textRange, const TextStyle& style, const ClipContext& context);
336     void paintBackground(ParagraphPainter* painter,
337                          SkScalar x,
338                          SkScalar y,
339                          TextRange textRange,
340                          const TextStyle& style,
341                          const ClipContext& context) const;
342     void paintShadow(ParagraphPainter* painter,
343                      SkScalar x,
344                      SkScalar y,
345                      TextRange textRange,
346                      const TextStyle& style,
347                      const ClipContext& context) const;
348     void paintDecorations(ParagraphPainter* painter,
349                           SkScalar x,
350                           SkScalar y,
351                           TextRange textRange,
352                           const TextStyle& style,
353                           const ClipContext& context) const;
354 
355     void shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift);
356 #ifdef ENABLE_TEXT_ENHANCE
357     void paintRoundRect(ParagraphPainter* painter, SkScalar x, SkScalar y) const;
358     void spacingCluster(const Cluster* cluster, SkScalar spacing, SkScalar prevSpacing);
359     bool hasBackgroundRect(const RoundRectAttr& attr);
360     void computeRoundRect(int& index, int& preIndex, std::vector<Run*>& groupRuns, Run* run);
361     void prepareRoundRect();
362     SkScalar calculateThickness(const TextStyle& style, const ClipContext& context);
363     void measureTextWithSpacesAtTheEnd(ClipContext& context, bool includeGhostSpaces) const;
364     void computeNextPaintGlyphRange(ClipContext& context, const TextRange& lastGlyphRange, StyleType styleType) const;
365     SkRect computeShadowRect(SkScalar x, SkScalar y, const TextStyle& style, const ClipContext& context) const;
366     SkRect getAllShadowsRect(SkScalar x, SkScalar y) const;
367 #endif
368 
369     ParagraphImpl* fOwner;
370     BlockRange fBlockRange;
371     TextRange fTextExcludingSpaces;
372     TextRange fText;
373     TextRange fTextIncludingNewlines;
374     ClusterRange fClusterRange;
375     ClusterRange fGhostClusterRange;
376     // Avoid the malloc/free in the common case of one run per line
377     skia_private::STArray<1, size_t, true> fRunsInVisualOrder;
378     SkVector fAdvance;                  // Text size
379     SkVector fOffset;                   // Text position
380     SkScalar fShift;                    // Let right
381     SkScalar fWidthWithSpaces;
382     std::unique_ptr<Run> fEllipsis;     // In case the line ends with the ellipsis
383     InternalLineMetrics fSizes;                 // Line metrics as a max of all run metrics and struts
384     InternalLineMetrics fMaxRunMetrics;         // No struts - need it for GetRectForRange(max height)
385     bool fHasBackground;
386     bool fHasShadows;
387     bool fHasDecorations;
388 #ifdef ENABLE_TEXT_ENHANCE
389     size_t fEllipsisIndex = EMPTY_INDEX;
390     TextRange fTextRangeReplacedByEllipsis;     // text range replaced by ellipsis
391     bool fIsArcText;
392     bool fArcTextState;
393     bool fLastClipRunLtr;
394 #endif
395 
396     LineMetricStyle fAscentStyle;
397     LineMetricStyle fDescentStyle;
398 
399     struct TextBlobRecord {
400         void paint(ParagraphPainter* painter, SkScalar x, SkScalar y);
401 
402 #ifdef ENABLE_TEXT_ENHANCE
403         void paint(ParagraphPainter* painter);
404         std::shared_ptr<RSTextBlob> fBlob;
405         size_t fVisitor_Size;
406 #else
407         sk_sp<SkTextBlob> fBlob;
408 #endif
409         SkPoint fOffset = SkPoint::Make(0.0f, 0.0f);
410         ParagraphPainter::SkPaintOrID fPaint;
411         SkRect fBounds = SkRect::MakeEmpty();
412         bool fClippingNeeded = false;
413         SkRect fClipRect = SkRect::MakeEmpty();
414 
415         // Extra fields only used for the (experimental) visitor
416         const Run* fVisitor_Run;
417         size_t     fVisitor_Pos;
418     };
419     bool fTextBlobCachePopulated;
420 #ifdef ENABLE_TEXT_ENHANCE
421     DecorationContext fDecorationContext;
422     std::vector<RoundRectAttr> fRoundRectAttrs = {};
423     bool fIsTextLineEllipsisHeadModal = false;
424 #endif
425 public:
426     std::vector<TextBlobRecord> fTextBlobCache;
427 #ifdef ENABLE_TEXT_ENHANCE
428     SkString fEllipsisString;
429     bool fBreakWithHyphen{false};
430     std::unique_ptr<Run> fHyphenRun;
431     size_t fHyphenIndex = EMPTY_INDEX;
432 #endif
433 };
434 }  // namespace textlayout
435 }  // namespace skia
436 
437 namespace sknonstd {
438     template <> struct is_bitmask_enum<skia::textlayout::TextLine::TextAdjustment> : std::true_type {};
439 }  // namespace sknonstd
440 
441 #endif  // TextLine_DEFINED
442