| // Copyright 2021 Google LLC. |
| #ifndef TextLine_DEFINED |
| #define TextLine_DEFINED |
| |
| #include "experimental/sktext/include/Types.h" |
| #include "include/core/SkFont.h" |
| #include "include/core/SkFontMetrics.h" |
| |
| namespace skia { |
| namespace text { |
| |
| class TextMetrics { |
| |
| public: |
| TextMetrics() { |
| clean(); |
| } |
| |
| TextMetrics(const SkFont& font) { |
| |
| SkFontMetrics metrics; |
| font.getMetrics(&metrics); |
| fAscent = metrics.fAscent; |
| fDescent = metrics.fDescent; |
| fLeading = metrics.fLeading; |
| } |
| |
| TextMetrics(const TextMetrics&) = default; |
| |
| TextMetrics& operator=(const TextMetrics&) = default; |
| |
| void merge(TextMetrics tail) { |
| this->fAscent = std::min(this->fAscent, tail.fAscent); |
| this->fDescent = std::max(this->fDescent, tail.fDescent); |
| this->fLeading = std::max(this->fLeading, tail.fLeading); |
| } |
| |
| void clean() { |
| this->fAscent = 0; |
| this->fDescent = 0; |
| this->fLeading = 0; |
| } |
| |
| SkScalar height() const { |
| return this->fDescent - this->fAscent + this->fLeading; |
| } |
| |
| SkScalar baseline() const { |
| return - this->fAscent + this->fLeading / 2; |
| } |
| |
| SkScalar above() const { return - this->fAscent + this->fLeading / 2; } |
| SkScalar below() const { return this->fDescent + this->fLeading / 2; } |
| |
| private: |
| SkScalar fAscent; |
| SkScalar fDescent; |
| SkScalar fLeading; |
| }; |
| |
| class GlyphPos { |
| public: |
| |
| GlyphPos() : fRunIndex(EMPTY_INDEX), fGlyphIndex(EMPTY_INDEX) { } |
| GlyphPos(size_t runIndex, size_t glyphIndex) : fRunIndex(runIndex), fGlyphIndex(glyphIndex) { } |
| |
| bool operator==(const GlyphPos& other) const { |
| return this->fRunIndex == other.fRunIndex && this->fGlyphIndex == other.fGlyphIndex; |
| } |
| |
| size_t runIndex() const { return fRunIndex; } |
| size_t glyphIndex() const { return fGlyphIndex; } |
| void setGlyphIndex(size_t index) { fGlyphIndex = index; } |
| |
| bool isEmpty() const { return fRunIndex == EMPTY_INDEX; } |
| |
| private: |
| size_t fRunIndex; |
| size_t fGlyphIndex; |
| }; |
| |
| class Stretch { |
| public: |
| |
| Stretch() : fGlyphStart(), fGlyphEnd(), fWidth(0), fTextRange(EMPTY_RANGE), fTextMetrics() { } |
| |
| Stretch(GlyphPos glyphStart, size_t textIndex, const TextMetrics& metrics) |
| : fGlyphStart(glyphStart) |
| , fGlyphEnd(glyphStart) |
| , fWidth(0.0) |
| , fTextRange(textIndex, textIndex) |
| , fTextMetrics(metrics) { } |
| |
| Stretch(RunIndex runIndex, GlyphRange glyphRange, TextRange textRange, SkScalar width, const TextMetrics& metrics) |
| : fGlyphStart(runIndex, glyphRange.fStart) |
| , fGlyphEnd(runIndex, glyphRange.fEnd) |
| , fWidth(width) |
| , fTextRange(textRange) |
| , fTextMetrics(metrics) { } |
| |
| Stretch(const Stretch&) = default; |
| Stretch(Stretch&&) = default; |
| Stretch& operator=(Stretch&&) = default; |
| Stretch& operator=(const Stretch&) = default; |
| |
| bool isEmpty() const { |
| if (fGlyphStart.isEmpty() || fGlyphEnd.isEmpty()) { |
| return true; |
| } else { |
| return fGlyphStart == fGlyphEnd; |
| } |
| } |
| |
| void clean() { |
| fGlyphStart = fGlyphEnd; |
| fTextRange.fStart = fTextRange.fEnd; |
| fWidth = 0.0f; |
| fTextMetrics.clean(); |
| } |
| |
| void moveTo(Stretch& tail) { |
| |
| if (tail.isEmpty()) { |
| return; |
| } |
| |
| if (this->isEmpty()) { |
| if (!tail.isEmpty()) { |
| this->fGlyphStart = tail.fGlyphStart; |
| this->fGlyphEnd = tail.fGlyphEnd; |
| this->fWidth = tail.fWidth; |
| this->fTextRange = tail.fTextRange; |
| this->fTextMetrics = tail.fTextMetrics; |
| } |
| tail.clean(); |
| return; |
| } |
| |
| SkASSERT(this->fGlyphEnd.runIndex() != tail.fGlyphStart.runIndex() || |
| this->fGlyphEnd.glyphIndex() == tail.fGlyphStart.glyphIndex()); |
| this->fGlyphEnd = tail.fGlyphEnd; |
| this->fWidth += tail.fWidth; |
| this->fTextRange.merge(tail.fTextRange); |
| this->fTextMetrics.merge(tail.fTextMetrics); |
| tail.clean(); |
| } |
| |
| void finish(size_t glyphIndex, size_t textIndex, SkScalar width) { |
| this->fTextRange.fEnd = textIndex; |
| this->fGlyphEnd.setGlyphIndex(glyphIndex); |
| this->fWidth = width; |
| } |
| |
| SkScalar width() const { return fWidth; } |
| TextRange textRange() const { return fTextRange; } |
| void setTextRange(TextRange range) { fTextRange = range; } |
| |
| const TextMetrics& textMetrics() const { return fTextMetrics; } |
| GlyphPos glyphStart() const { return fGlyphStart; } |
| GlyphPos glyphEnd() const { return fGlyphEnd; } |
| size_t glyphStartIndex() const { return fGlyphStart.glyphIndex(); } |
| size_t textStart() const { return fTextRange.fStart; } |
| |
| private: |
| GlyphPos fGlyphStart; |
| GlyphPos fGlyphEnd; |
| SkScalar fWidth; |
| TextRange fTextRange; |
| TextMetrics fTextMetrics; |
| }; |
| |
| class LogicalLine { |
| public: |
| LogicalLine(const Stretch& stretch, const Stretch& spaces, SkScalar verticalOffset, bool hardLineBreak); |
| ~LogicalLine() = default; |
| |
| TextMetrics getMetrics() const { return fTextMetrics; } |
| GlyphPos glyphStart() const { return fTextStart; } |
| GlyphPos glyphEnd() const { return fTextEnd; } |
| GlyphPos glyphTrailingEnd() const { return fWhitespacesEnd; } |
| SkScalar width() const { return fTextWidth; } |
| SkScalar withWithTrailingSpaces() const { return fTextWidth + fSpacesWidth; } |
| SkScalar horizontalOffset() const { return fHorizontalOffset; } |
| SkScalar verticalOffset() const { return fVerticalOffset; } |
| SkScalar height() const { return fTextMetrics.height(); } |
| SkScalar baseline() const { return fTextMetrics.baseline(); } |
| TextRange text() const { return fText; } |
| TextRange whitespaces() const { return fWhitespaces; } |
| bool isHardLineBreak() const { return fHardLineBreak; } |
| |
| private: |
| friend class WrappedText; |
| |
| GlyphPos fTextStart; |
| GlyphPos fTextEnd; |
| GlyphPos fWhitespacesEnd; |
| TextRange fText; |
| TextRange fWhitespaces; |
| SkScalar fTextWidth; |
| SkScalar fSpacesWidth; |
| SkScalar fHorizontalOffset; |
| SkScalar fVerticalOffset; |
| TextMetrics fTextMetrics; |
| bool fHardLineBreak; |
| }; |
| |
| } // namespace text |
| } // namespace skia |
| #endif |