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 Processor;
13
14 class TextMetrics {
15
16 public:
TextMetrics()17 TextMetrics() {
18 clean();
19 }
20
TextMetrics(const SkFont & font)21 TextMetrics(const SkFont& font) {
22
23 SkFontMetrics metrics;
24 font.getMetrics(&metrics);
25 fAscent = metrics.fAscent;
26 fDescent = metrics.fDescent;
27 fLeading = metrics.fLeading;
28 }
29
30 TextMetrics(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
52 private:
53 SkScalar fAscent;
54 SkScalar fDescent;
55 SkScalar fLeading;
56 };
57
58 class GlyphPos {
59 public:
60
GlyphPos()61 GlyphPos() : fRunIndex(EMPTY_INDEX), fGlyphIndex(EMPTY_INDEX) { }
GlyphPos(size_t runIndex,size_t glyphIndex)62 GlyphPos(size_t runIndex, size_t glyphIndex) : fRunIndex(runIndex), fGlyphIndex(glyphIndex) { }
63
64 bool operator==(const GlyphPos& other) const {
65 return this->fRunIndex == other.fRunIndex && this->fGlyphIndex == other.fGlyphIndex;
66 }
67
runIndex()68 size_t runIndex() const { return fRunIndex; }
glyphIndex()69 size_t glyphIndex() const { return fGlyphIndex; }
setGlyphIndex(size_t index)70 void setGlyphIndex(size_t index) { fGlyphIndex = index; }
71
isEmpty()72 bool isEmpty() const { return fRunIndex == EMPTY_INDEX; }
73
74 private:
75 size_t fRunIndex;
76 size_t fGlyphIndex;
77 };
78
79 class Stretch {
80 public:
81
Stretch()82 Stretch() : fGlyphStart(), fGlyphEnd(), fWidth(0), fTextRange(0, 0), fTextMetrics() { }
83
Stretch(GlyphPos glyphStart,size_t textIndex,const TextMetrics & metrics)84 Stretch(GlyphPos glyphStart, size_t textIndex, const TextMetrics& metrics)
85 : fGlyphStart(glyphStart)
86 , fGlyphEnd(glyphStart)
87 , fWidth(0.0)
88 , fTextRange(textIndex, textIndex)
89 , fTextMetrics(metrics) { }
90
91 Stretch(const Stretch&) = default;
92 Stretch(Stretch&&) = default;
93 Stretch& operator=(Stretch&&) = default;
94 Stretch& operator=(const Stretch&) = default;
95
isEmpty()96 bool isEmpty() const {
97 if (this->fGlyphStart.isEmpty()) {
98 SkASSERT(this->fGlyphEnd.isEmpty());
99 return true;
100 } else {
101 SkASSERT(!this->fGlyphEnd.isEmpty());
102 return false;
103 //return (this->fGlyphStart.runIndex() == this->fGlyphEnd.runIndex() &&
104 // this->fGlyphStart.glyphIndex() == this->fGlyphEnd.glyphIndex());
105 }
106 }
107
clean()108 void clean() {
109 fGlyphStart = fGlyphEnd;
110 fTextRange.fStart = fTextRange.fEnd;
111 fWidth = 0.0f;
112 fTextMetrics.clean();
113 }
114
moveTo(Stretch & tail)115 void moveTo(Stretch& tail) {
116
117 if (tail.isEmpty()) {
118 return;
119 }
120
121 if (this->isEmpty()) {
122 if (!tail.isEmpty()) {
123 this->fGlyphStart = tail.fGlyphStart;
124 this->fGlyphEnd = tail.fGlyphEnd;
125 this->fWidth = tail.fWidth;
126 this->fTextRange = tail.fTextRange;
127 this->fTextMetrics = tail.fTextMetrics;
128 }
129 tail.clean();
130 return;
131 }
132
133 SkASSERT(this->fGlyphEnd.runIndex() != tail.fGlyphStart.runIndex() ||
134 this->fGlyphEnd.glyphIndex() == tail.fGlyphStart.glyphIndex());
135 this->fGlyphEnd = tail.fGlyphEnd;
136 this->fWidth += tail.fWidth;
137 this->fTextRange.merge(tail.fTextRange);
138 this->fTextMetrics.merge(tail.fTextMetrics);
139 tail.clean();
140 }
141
finish(size_t glyphIndex,size_t textIndex,SkScalar width)142 void finish(size_t glyphIndex, size_t textIndex, SkScalar width) {
143 this->fTextRange.fEnd = textIndex;
144 this->fGlyphEnd.setGlyphIndex(glyphIndex);
145 this->fWidth = width;
146 }
147
width()148 SkScalar width() const { return fWidth; }
textRange()149 TextRange textRange() const { return fTextRange; }
setTextRange(TextRange range)150 void setTextRange(TextRange range) { fTextRange = range; }
151
textMetrics()152 const TextMetrics& textMetrics() const { return fTextMetrics; }
glyphStart()153 GlyphPos glyphStart() const { return fGlyphStart; }
glyphEnd()154 GlyphPos glyphEnd() const { return fGlyphEnd; }
glyphStartIndex()155 size_t glyphStartIndex() const { return fGlyphStart.glyphIndex(); }
textStart()156 size_t textStart() const { return fTextRange.fStart; }
157
158 private:
159 GlyphPos fGlyphStart;
160 GlyphPos fGlyphEnd;
161 SkScalar fWidth;
162 TextRange fTextRange;
163 TextMetrics fTextMetrics;
164 };
165
166 class Line {
167 public:
168 Line(Processor* processor, const Stretch& stretch, const Stretch& spaces);
169 ~Line() = default;
170
171 private:
172 friend class Processor;
173
174 GlyphPos fTextStart;
175 GlyphPos fTextEnd;
176 GlyphPos fWhitespacesEnd;
177 TextRange fText;
178 TextRange fWhitespaces;
179 SkScalar fTextWidth;
180 SkScalar fSpacesWidth;
181 TextMetrics fTextMetrics;
182 SkSTArray<1, size_t, true> fRunsInVisualOrder;
183 Processor* fProcessor;
184 };
185
186 } // namespace text
187 } // namespace skia
188 #endif
189