1 // Copyright 2021 Google LLC. 2 #ifndef VisualRun_DEFINED 3 #define VisualRun_DEFINED 4 5 #include "experimental/sktext/include/Types.h" 6 #include "experimental/sktext/src/Line.h" 7 #include "modules/skshaper/include/SkShaper.h" 8 9 namespace skia { 10 namespace text { 11 12 class VisualRun { 13 public: VisualRun(TextRange textRange,GlyphIndex trailingSpacesStart,const SkFont & font,SkScalar lineBaseLine,SkPoint runOffset,bool leftToRight,SkSpan<SkPoint> positions,SkSpan<SkGlyphID> glyphs,SkSpan<uint32_t> clusters)14 VisualRun(TextRange textRange, GlyphIndex trailingSpacesStart, const SkFont& font, SkScalar lineBaseLine, 15 SkPoint runOffset, 16 bool leftToRight, 17 SkSpan<SkPoint> positions, SkSpan<SkGlyphID> glyphs, SkSpan<uint32_t> clusters) 18 : fFont(font) 19 , fTextMetrics(TextMetrics(fFont)) 20 , fLineBaseLine(lineBaseLine) 21 , fDirTextRange(textRange, leftToRight) 22 , fTrailingSpacesStart(trailingSpacesStart) { 23 if (positions.size() == 0) { 24 SkASSERT(false); 25 return; 26 } 27 fPositions.reserve_back(positions.size()); 28 runOffset -= SkPoint::Make(positions[0].fX, - fLineBaseLine); 29 for (auto& pos : positions) { 30 fPositions.emplace_back(pos + runOffset); 31 } 32 fGlyphs.reserve_back(glyphs.size()); 33 for (auto glyph : glyphs) { 34 fGlyphs.emplace_back(glyph); 35 } 36 fClusters.reserve_back(clusters.size()); 37 for (auto cluster : clusters) { 38 fClusters.emplace_back(SkToU16(cluster)); 39 } 40 fAdvance= SkVector::Make(this->calculateWidth(0, glyphs.size()), fTextMetrics.height()); 41 } 42 calculateWidth(GlyphRange glyphRange)43 SkScalar calculateWidth(GlyphRange glyphRange) const { 44 SkASSERT(glyphRange.fStart <= glyphRange.fEnd && 45 glyphRange.fEnd < SkToSizeT(fPositions.size())); 46 return fPositions[glyphRange.fEnd].fX - fPositions[glyphRange.fStart].fX; 47 } calculateWidth(GlyphIndex start,GlyphIndex end)48 SkScalar calculateWidth(GlyphIndex start, GlyphIndex end) const { 49 return calculateWidth(GlyphRange(start, end)); 50 } width()51 SkScalar width() const { return fAdvance.fX; } height()52 SkScalar height() const { return fAdvance.fY; } firstGlyphPosition()53 SkScalar firstGlyphPosition() const { return fPositions[0].fX; } textMetrics()54 TextMetrics textMetrics() const { return fTextMetrics; } 55 leftToRight()56 bool leftToRight() const { return fDirTextRange.fLeftToRight; } size()57 size_t size() const { return fGlyphs.size(); } baseLine()58 SkScalar baseLine() const { return fLineBaseLine; } trailingSpacesStart()59 GlyphIndex trailingSpacesStart() const { return fTrailingSpacesStart; } dirTextRange()60 DirTextRange dirTextRange() const { return fDirTextRange; } 61 62 template <typename Callback> forEachTextBlockInGlyphRange(SkSpan<TextIndex> textBlock,Callback && callback)63 void forEachTextBlockInGlyphRange(SkSpan<TextIndex> textBlock, Callback&& callback) const { 64 if (this->leftToRight()) { 65 DirTextRange dirTextRange(fDirTextRange.fStart, fDirTextRange.fStart, fDirTextRange.fLeftToRight); 66 for (auto currentIndex : textBlock) { 67 if (currentIndex >= fDirTextRange.fEnd) { 68 break; 69 } 70 if (currentIndex < fDirTextRange.fStart) { 71 continue; 72 } 73 dirTextRange.fStart = dirTextRange.fEnd; 74 dirTextRange.fEnd = currentIndex; 75 dirTextRange.fEnd = std::min(fDirTextRange.fEnd, dirTextRange.fEnd); 76 77 callback(dirTextRange); 78 } 79 } else { 80 // Revert chunks 81 std::vector<TextIndex> revertedChunks; 82 } 83 } 84 85 private: 86 friend class WrappedText; 87 SkFont fFont; 88 TextMetrics fTextMetrics; 89 SkScalar fLineBaseLine; 90 91 SkVector fAdvance; 92 DirTextRange fDirTextRange; 93 SkSTArray<128, SkGlyphID, true> fGlyphs; 94 SkSTArray<128, SkPoint, true> fPositions; 95 SkSTArray<128, TextIndex, true> fClusters; 96 GlyphIndex fTrailingSpacesStart; 97 }; 98 99 class VisualLine { 100 public: VisualLine(TextRange text,bool hardLineBreak,SkScalar verticalOffset,SkSpan<VisualRun> runs)101 VisualLine(TextRange text, bool hardLineBreak, SkScalar verticalOffset, SkSpan<VisualRun> runs) 102 : fText(text) 103 , fRuns(runs) 104 , fTrailingSpaces(0, 0) 105 , fOffset(SkPoint::Make(0, verticalOffset)) 106 , fActualWidth(0.0f) 107 , fTextMetrics() 108 , fIsHardBreak(hardLineBreak) 109 , fGlyphCount(0ul) { 110 // Calculate all the info 111 for (auto& run : fRuns) { 112 fTextMetrics.merge(run.textMetrics()); 113 fActualWidth += run.width(); // What about trailing spaces? 114 if (run.trailingSpacesStart() == 0) { 115 // The entire run is trailing spaces, do not move the counter 116 } else { 117 // We can reset the trailing spaces counter 118 fTrailingSpaces.fStart = fTrailingSpaces.fEnd + run.trailingSpacesStart(); 119 } 120 fTrailingSpaces.fEnd += run.size(); 121 } 122 } 123 baseline()124 SkScalar baseline() const { return fTextMetrics.baseline(); } text()125 TextRange text() const { return fText; } trailingSpaces()126 GlyphRange trailingSpaces() const { return fTrailingSpaces; } isHardBreak()127 bool isHardBreak() const { return fIsHardBreak; } glyphCount()128 size_t glyphCount() const { return fGlyphCount; } 129 isFirst(VisualRun * run)130 bool isFirst(VisualRun* run) { return &fRuns.front() == run; } isLast(VisualRun * run)131 bool isLast(VisualRun* run) { return &fRuns.back() == run; } 132 private: 133 friend class WrappedText; 134 friend class VisualRun; 135 TextRange fText; 136 SkSpan<VisualRun> fRuns; 137 GlyphRange fTrailingSpaces; // This is a zero-based index across the line 138 SkPoint fOffset; // For left/right/center formatting and for height 139 SkScalar fActualWidth; 140 TextMetrics fTextMetrics; 141 bool fIsHardBreak; 142 size_t fGlyphCount; 143 }; 144 } // namespace text 145 } // namespace skia 146 #endif 147