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