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