| // Copyright 2021 Google LLC. |
| #ifndef VisualRun_DEFINED |
| #define VisualRun_DEFINED |
| |
| #include "experimental/sktext/include/Types.h" |
| #include "experimental/sktext/src/Line.h" |
| #include "include/private/base/SkTArray.h" |
| #include "modules/skshaper/include/SkShaper.h" |
| |
| namespace skia { |
| namespace text { |
| |
| class VisualRun { |
| 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) |
| : fFont(font) |
| , fTextMetrics(TextMetrics(fFont)) |
| , fLineBaseLine(lineBaseLine) |
| , fDirTextRange(textRange, leftToRight) |
| , fTrailingSpacesStart(trailingSpacesStart) { |
| if (positions.size() == 0) { |
| SkASSERT(false); |
| return; |
| } |
| fPositions.reserve_back(positions.size()); |
| runOffset -= SkPoint::Make(positions[0].fX, - fLineBaseLine); |
| for (auto& pos : positions) { |
| fPositions.emplace_back(pos + runOffset); |
| } |
| fGlyphs.reserve_back(glyphs.size()); |
| for (auto glyph : glyphs) { |
| fGlyphs.emplace_back(glyph); |
| } |
| fClusters.reserve_back(clusters.size()); |
| for (auto cluster : clusters) { |
| fClusters.emplace_back(SkToU16(cluster)); |
| } |
| fAdvance= SkVector::Make(this->calculateWidth(0, glyphs.size()), fTextMetrics.height()); |
| } |
| |
| SkScalar calculateWidth(GlyphRange glyphRange) const { |
| SkASSERT(glyphRange.fStart <= glyphRange.fEnd && |
| glyphRange.fEnd < SkToSizeT(fPositions.size())); |
| return fPositions[glyphRange.fEnd].fX - fPositions[glyphRange.fStart].fX; |
| } |
| SkScalar calculateWidth(GlyphIndex start, GlyphIndex end) const { |
| return calculateWidth(GlyphRange(start, end)); |
| } |
| SkScalar width() const { return fAdvance.fX; } |
| SkScalar height() const { return fAdvance.fY; } |
| SkScalar firstGlyphPosition() const { return fPositions[0].fX; } |
| TextMetrics textMetrics() const { return fTextMetrics; } |
| |
| bool leftToRight() const { return fDirTextRange.fLeftToRight; } |
| size_t size() const { return fGlyphs.size(); } |
| SkScalar baseLine() const { return fLineBaseLine; } |
| GlyphIndex trailingSpacesStart() const { return fTrailingSpacesStart; } |
| DirTextRange dirTextRange() const { return fDirTextRange; } |
| |
| template <typename Callback> |
| void forEachTextBlockInGlyphRange(SkSpan<TextIndex> textBlock, Callback&& callback) const { |
| if (this->leftToRight()) { |
| DirTextRange dirTextRange(fDirTextRange.fStart, fDirTextRange.fStart, fDirTextRange.fLeftToRight); |
| for (auto currentIndex : textBlock) { |
| if (currentIndex >= fDirTextRange.fEnd) { |
| break; |
| } |
| if (currentIndex < fDirTextRange.fStart) { |
| continue; |
| } |
| dirTextRange.fStart = dirTextRange.fEnd; |
| dirTextRange.fEnd = currentIndex; |
| dirTextRange.fEnd = std::min(fDirTextRange.fEnd, dirTextRange.fEnd); |
| |
| callback(dirTextRange); |
| } |
| } else { |
| // Revert chunks |
| std::vector<TextIndex> revertedChunks; |
| } |
| } |
| |
| private: |
| friend class WrappedText; |
| SkFont fFont; |
| TextMetrics fTextMetrics; |
| SkScalar fLineBaseLine; |
| |
| SkVector fAdvance; |
| DirTextRange fDirTextRange; |
| SkSTArray<128, SkGlyphID, true> fGlyphs; |
| SkSTArray<128, SkPoint, true> fPositions; |
| SkSTArray<128, TextIndex, true> fClusters; |
| GlyphIndex fTrailingSpacesStart; |
| }; |
| |
| class VisualLine { |
| public: |
| VisualLine(TextRange text, bool hardLineBreak, SkScalar verticalOffset, SkSpan<VisualRun> runs) |
| : fText(text) |
| , fRuns(runs) |
| , fTrailingSpaces(0, 0) |
| , fOffset(SkPoint::Make(0, verticalOffset)) |
| , fActualWidth(0.0f) |
| , fTextMetrics() |
| , fIsHardBreak(hardLineBreak) |
| , fGlyphCount(0ul) { |
| // Calculate all the info |
| for (auto& run : fRuns) { |
| fTextMetrics.merge(run.textMetrics()); |
| fActualWidth += run.width(); // What about trailing spaces? |
| if (run.trailingSpacesStart() == 0) { |
| // The entire run is trailing spaces, do not move the counter |
| } else { |
| // We can reset the trailing spaces counter |
| fTrailingSpaces.fStart = fTrailingSpaces.fEnd + run.trailingSpacesStart(); |
| } |
| fTrailingSpaces.fEnd += run.size(); |
| } |
| } |
| |
| SkScalar baseline() const { return fTextMetrics.baseline(); } |
| TextRange text() const { return fText; } |
| GlyphRange trailingSpaces() const { return fTrailingSpaces; } |
| bool isHardBreak() const { return fIsHardBreak; } |
| size_t glyphCount() const { return fGlyphCount; } |
| |
| bool isFirst(VisualRun* run) { return &fRuns.front() == run; } |
| bool isLast(VisualRun* run) { return &fRuns.back() == run; } |
| private: |
| friend class WrappedText; |
| friend class VisualRun; |
| TextRange fText; |
| SkSpan<VisualRun> fRuns; |
| GlyphRange fTrailingSpaces; // This is a zero-based index across the line |
| SkPoint fOffset; // For left/right/center formatting and for height |
| SkScalar fActualWidth; |
| TextMetrics fTextMetrics; |
| bool fIsHardBreak; |
| size_t fGlyphCount; |
| }; |
| } // namespace text |
| } // namespace skia |
| #endif |