• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 Google LLC.
2 #ifndef TextLine_DEFINED
3 #define TextLine_DEFINED
4 
5 #include "experimental/sktext/include/Types.h"
6 #include "include/core/SkFont.h"
7 #include "include/core/SkFontMetrics.h"
8 
9 namespace skia {
10 namespace text {
11 
12 class TextMetrics {
13 
14 public:
TextMetrics()15   TextMetrics() {
16       clean();
17   }
18 
TextMetrics(const SkFont & font)19   TextMetrics(const SkFont& font) {
20 
21       SkFontMetrics metrics;
22       font.getMetrics(&metrics);
23       fAscent = metrics.fAscent;
24       fDescent = metrics.fDescent;
25       fLeading = metrics.fLeading;
26   }
27 
28   TextMetrics(const TextMetrics&) = default;
29 
30   TextMetrics& operator=(const TextMetrics&) = default;
31 
merge(TextMetrics tail)32   void merge(TextMetrics tail) {
33       this->fAscent = std::min(this->fAscent, tail.fAscent);
34       this->fDescent = std::max(this->fDescent, tail.fDescent);
35       this->fLeading = std::max(this->fLeading, tail.fLeading);
36   }
37 
clean()38   void clean() {
39       this->fAscent = 0;
40       this->fDescent = 0;
41       this->fLeading = 0;
42   }
43 
height()44   SkScalar height() const {
45       return this->fDescent - this->fAscent + this->fLeading;
46   }
47 
baseline()48     SkScalar baseline() const {
49           return - this->fAscent + this->fLeading / 2;
50     }
51 
above()52     SkScalar above() const { return - this->fAscent + this->fLeading / 2; }
below()53     SkScalar below() const { return this->fDescent + this->fLeading / 2; }
54 
55 private:
56     SkScalar fAscent;
57     SkScalar fDescent;
58     SkScalar fLeading;
59 };
60 
61 class GlyphPos {
62 public:
63 
GlyphPos()64     GlyphPos() : fRunIndex(EMPTY_INDEX), fGlyphIndex(EMPTY_INDEX) { }
GlyphPos(size_t runIndex,size_t glyphIndex)65     GlyphPos(size_t runIndex, size_t glyphIndex) : fRunIndex(runIndex), fGlyphIndex(glyphIndex) { }
66 
67     bool operator==(const GlyphPos& other) const {
68         return this->fRunIndex == other.fRunIndex && this->fGlyphIndex == other.fGlyphIndex;
69     }
70 
runIndex()71     size_t runIndex() const { return fRunIndex; }
glyphIndex()72     size_t glyphIndex() const { return fGlyphIndex; }
setGlyphIndex(size_t index)73     void setGlyphIndex(size_t index) { fGlyphIndex = index; }
74 
isEmpty()75     bool isEmpty() const { return fRunIndex == EMPTY_INDEX; }
76 
77 private:
78     size_t fRunIndex;
79     size_t fGlyphIndex;
80 };
81 
82 class Stretch {
83 public:
84 
Stretch()85     Stretch() : fGlyphStart(), fGlyphEnd(), fWidth(0), fTextRange(EMPTY_RANGE), fTextMetrics() { }
86 
Stretch(GlyphPos glyphStart,size_t textIndex,const TextMetrics & metrics)87     Stretch(GlyphPos glyphStart, size_t textIndex, const TextMetrics& metrics)
88         : fGlyphStart(glyphStart)
89         , fGlyphEnd(glyphStart)
90         , fWidth(0.0)
91         , fTextRange(textIndex, textIndex)
92         , fTextMetrics(metrics) { }
93 
Stretch(RunIndex runIndex,GlyphRange glyphRange,TextRange textRange,SkScalar width,const TextMetrics & metrics)94     Stretch(RunIndex runIndex, GlyphRange glyphRange, TextRange textRange, SkScalar width, const TextMetrics& metrics)
95         : fGlyphStart(runIndex, glyphRange.fStart)
96         , fGlyphEnd(runIndex, glyphRange.fEnd)
97         , fWidth(width)
98         , fTextRange(textRange)
99         , fTextMetrics(metrics) { }
100 
101     Stretch(const Stretch&) = default;
102     Stretch(Stretch&&) = default;
103     Stretch& operator=(Stretch&&) = default;
104     Stretch& operator=(const Stretch&) = default;
105 
isEmpty()106     bool isEmpty() const {
107         if (fGlyphStart.isEmpty() || fGlyphEnd.isEmpty()) {
108             return true;
109         } else {
110             return fGlyphStart == fGlyphEnd;
111         }
112     }
113 
clean()114     void clean() {
115         fGlyphStart = fGlyphEnd;
116         fTextRange.fStart = fTextRange.fEnd;
117         fWidth = 0.0f;
118         fTextMetrics.clean();
119     }
120 
moveTo(Stretch & tail)121     void moveTo(Stretch& tail) {
122 
123         if (tail.isEmpty()) {
124             return;
125         }
126 
127         if (this->isEmpty()) {
128             if (!tail.isEmpty()) {
129                 this->fGlyphStart = tail.fGlyphStart;
130                 this->fGlyphEnd = tail.fGlyphEnd;
131                 this->fWidth = tail.fWidth;
132                 this->fTextRange = tail.fTextRange;
133                 this->fTextMetrics = tail.fTextMetrics;
134             }
135             tail.clean();
136             return;
137         }
138 
139         SkASSERT(this->fGlyphEnd.runIndex() != tail.fGlyphStart.runIndex() ||
140                        this->fGlyphEnd.glyphIndex() == tail.fGlyphStart.glyphIndex());
141         this->fGlyphEnd = tail.fGlyphEnd;
142         this->fWidth += tail.fWidth;
143         this->fTextRange.merge(tail.fTextRange);
144         this->fTextMetrics.merge(tail.fTextMetrics);
145         tail.clean();
146     }
147 
finish(size_t glyphIndex,size_t textIndex,SkScalar width)148     void finish(size_t glyphIndex, size_t textIndex, SkScalar width) {
149         this->fTextRange.fEnd = textIndex;
150         this->fGlyphEnd.setGlyphIndex(glyphIndex);
151         this->fWidth = width;
152     }
153 
width()154     SkScalar width() const { return fWidth; }
textRange()155     TextRange textRange() const { return fTextRange; }
setTextRange(TextRange range)156     void setTextRange(TextRange range) { fTextRange = range; }
157 
textMetrics()158     const TextMetrics& textMetrics() const { return fTextMetrics; }
glyphStart()159     GlyphPos glyphStart() const { return fGlyphStart; }
glyphEnd()160     GlyphPos glyphEnd() const { return fGlyphEnd; }
glyphStartIndex()161     size_t glyphStartIndex() const { return fGlyphStart.glyphIndex(); }
textStart()162     size_t textStart() const { return fTextRange.fStart; }
163 
164 private:
165     GlyphPos fGlyphStart;
166     GlyphPos fGlyphEnd;
167     SkScalar fWidth;
168     TextRange fTextRange;
169     TextMetrics fTextMetrics;
170 };
171 
172 class LogicalLine {
173 public:
174     LogicalLine(const Stretch& stretch, const Stretch& spaces, SkScalar verticalOffset, bool hardLineBreak);
175     ~LogicalLine() = default;
176 
getMetrics()177     TextMetrics getMetrics() const { return fTextMetrics; }
glyphStart()178     GlyphPos glyphStart() const { return fTextStart; }
glyphEnd()179     GlyphPos glyphEnd() const { return fTextEnd; }
glyphTrailingEnd()180     GlyphPos glyphTrailingEnd() const { return fWhitespacesEnd; }
width()181     SkScalar width() const { return fTextWidth; }
withWithTrailingSpaces()182     SkScalar withWithTrailingSpaces() const { return fTextWidth + fSpacesWidth; }
horizontalOffset()183     SkScalar horizontalOffset() const { return fHorizontalOffset; }
verticalOffset()184     SkScalar verticalOffset() const { return fVerticalOffset; }
height()185     SkScalar height() const { return fTextMetrics.height(); }
baseline()186     SkScalar baseline() const { return fTextMetrics.baseline(); }
text()187     TextRange text() const { return fText; }
whitespaces()188     TextRange whitespaces() const { return fWhitespaces; }
isHardLineBreak()189     bool isHardLineBreak() const { return fHardLineBreak; }
190 
191 private:
192     friend class WrappedText;
193 
194     GlyphPos fTextStart;
195     GlyphPos fTextEnd;
196     GlyphPos fWhitespacesEnd;
197     TextRange fText;
198     TextRange fWhitespaces;
199     SkScalar fTextWidth;
200     SkScalar fSpacesWidth;
201     SkScalar fHorizontalOffset;
202     SkScalar fVerticalOffset;
203     TextMetrics fTextMetrics;
204     bool fHardLineBreak;
205 };
206 
207 } // namespace text
208 } // namespace skia
209 #endif
210