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 && glyphRange.fEnd < fPositions.size()); 45 return fPositions[glyphRange.fEnd].fX - fPositions[glyphRange.fStart].fX; 46 } calculateWidth(GlyphIndex start,GlyphIndex end)47 SkScalar calculateWidth(GlyphIndex start, GlyphIndex end) const { 48 return calculateWidth(GlyphRange(start, end)); 49 } width()50 SkScalar width() const { return fAdvance.fX; } height()51 SkScalar height() const { return fAdvance.fY; } firstGlyphPosition()52 SkScalar firstGlyphPosition() const { return fPositions[0].fX; } textMetrics()53 TextMetrics textMetrics() const { return fTextMetrics; } 54 leftToRight()55 bool leftToRight() const { return fDirTextRange.fLeftToRight; } size()56 size_t size() const { return fGlyphs.size(); } baseLine()57 SkScalar baseLine() const { return fLineBaseLine; } trailingSpacesStart()58 GlyphIndex trailingSpacesStart() const { return fTrailingSpacesStart; } dirTextRange()59 DirTextRange dirTextRange() const { return fDirTextRange; } 60 61 template <typename Callback> forEachTextBlockInGlyphRange(SkSpan<TextIndex> textBlock,Callback && callback)62 void forEachTextBlockInGlyphRange(SkSpan<TextIndex> textBlock, Callback&& callback) const { 63 if (this->leftToRight()) { 64 DirTextRange dirTextRange(fDirTextRange.fStart, fDirTextRange.fStart, fDirTextRange.fLeftToRight); 65 for (auto currentIndex : textBlock) { 66 if (currentIndex >= fDirTextRange.fEnd) { 67 break; 68 } 69 if (currentIndex < fDirTextRange.fStart) { 70 continue; 71 } 72 dirTextRange.fStart = dirTextRange.fEnd; 73 dirTextRange.fEnd = currentIndex; 74 dirTextRange.fEnd = std::min(fDirTextRange.fEnd, dirTextRange.fEnd); 75 76 callback(dirTextRange); 77 } 78 } else { 79 // Revert chunks 80 std::vector<TextIndex> revertedChunks; 81 } 82 } 83 84 private: 85 friend class WrappedText; 86 SkFont fFont; 87 TextMetrics fTextMetrics; 88 SkScalar fLineBaseLine; 89 90 SkVector fAdvance; 91 DirTextRange fDirTextRange; 92 SkSTArray<128, SkGlyphID, true> fGlyphs; 93 SkSTArray<128, SkPoint, true> fPositions; 94 SkSTArray<128, TextIndex, true> fClusters; 95 GlyphIndex fTrailingSpacesStart; 96 }; 97 98 class VisualLine { 99 public: VisualLine(TextRange text,bool hardLineBreak,SkScalar verticalOffset,SkSpan<VisualRun> runs)100 VisualLine(TextRange text, bool hardLineBreak, SkScalar verticalOffset, SkSpan<VisualRun> runs) 101 : fText(text) 102 , fRuns(runs) 103 , fTrailingSpaces(0, 0) 104 , fOffset(SkPoint::Make(0, verticalOffset)) 105 , fActualWidth(0.0f) 106 , fTextMetrics() 107 , fIsHardBreak(hardLineBreak) 108 , fGlyphCount(0ul) { 109 // Calculate all the info 110 for (auto& run : fRuns) { 111 fTextMetrics.merge(run.textMetrics()); 112 fActualWidth += run.width(); // What about trailing spaces? 113 if (run.trailingSpacesStart() == 0) { 114 // The entire run is trailing spaces, do not move the counter 115 } else { 116 // We can reset the trailing spaces counter 117 fTrailingSpaces.fStart = fTrailingSpaces.fEnd + run.trailingSpacesStart(); 118 } 119 fTrailingSpaces.fEnd += run.size(); 120 } 121 } 122 baseline()123 SkScalar baseline() const { return fTextMetrics.baseline(); } text()124 TextRange text() const { return fText; } trailingSpaces()125 GlyphRange trailingSpaces() const { return fTrailingSpaces; } isHardBreak()126 bool isHardBreak() const { return fIsHardBreak; } glyphCount()127 size_t glyphCount() const { return fGlyphCount; } 128 isFirst(VisualRun * run)129 bool isFirst(VisualRun* run) { return &fRuns.front() == run; } isLast(VisualRun * run)130 bool isLast(VisualRun* run) { return &fRuns.back() == run; } 131 private: 132 friend class WrappedText; 133 friend class VisualRun; 134 TextRange fText; 135 SkSpan<VisualRun> fRuns; 136 GlyphRange fTrailingSpaces; // This is a zero-based index across the line 137 SkPoint fOffset; // For left/right/center formatting and for height 138 SkScalar fActualWidth; 139 TextMetrics fTextMetrics; 140 bool fIsHardBreak; 141 size_t fGlyphCount; 142 }; 143 }; // namespace text 144 } // namespace skia 145 #endif 146