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 9namespace skia { 10namespace text { 11 12class VisualRun { 13 public: 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 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 } 47 SkScalar calculateWidth(GlyphIndex start, GlyphIndex end) const { 48 return calculateWidth(GlyphRange(start, end)); 49 } 50 SkScalar width() const { return fAdvance.fX; } 51 SkScalar height() const { return fAdvance.fY; } 52 SkScalar firstGlyphPosition() const { return fPositions[0].fX; } 53 TextMetrics textMetrics() const { return fTextMetrics; } 54 55 bool leftToRight() const { return fDirTextRange.fLeftToRight; } 56 size_t size() const { return fGlyphs.size(); } 57 SkScalar baseLine() const { return fLineBaseLine; } 58 GlyphIndex trailingSpacesStart() const { return fTrailingSpacesStart; } 59 DirTextRange dirTextRange() const { return fDirTextRange; } 60 61 template <typename 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 98class VisualLine { 99public: 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 123 SkScalar baseline() const { return fTextMetrics.baseline(); } 124 TextRange text() const { return fText; } 125 GlyphRange trailingSpaces() const { return fTrailingSpaces; } 126 bool isHardBreak() const { return fIsHardBreak; } 127 size_t glyphCount() const { return fGlyphCount; } 128 129 bool isFirst(VisualRun* run) { return &fRuns.front() == run; } 130 bool isLast(VisualRun* run) { return &fRuns.back() == run; } 131private: 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