// Copyright 2019 Google LLC. #ifndef Run_DEFINED #define Run_DEFINED #include "include/core/SkFontMetrics.h" #include "include/core/SkPoint.h" #include "include/core/SkTextBlob.h" #include "modules/skparagraph/include/DartTypes.h" #include "modules/skparagraph/include/TextStyle.h" #include "modules/skshaper/include/SkShaper.h" #include "src/core/SkSpan.h" #include "src/core/SkTraceEvent.h" namespace skia { namespace textlayout { class ParagraphImpl; class Cluster; class Run; typedef size_t RunIndex; const size_t EMPTY_RUN = EMPTY_INDEX; typedef size_t ClusterIndex; typedef SkRange ClusterRange; const size_t EMPTY_CLUSTER = EMPTY_INDEX; const SkRange EMPTY_CLUSTERS = EMPTY_RANGE; typedef size_t GraphemeIndex; typedef SkRange GraphemeRange; typedef size_t CodepointIndex; typedef SkRange CodepointRange; typedef size_t BlockIndex; typedef SkRange BlockRange; const size_t EMPTY_BLOCK = EMPTY_INDEX; const SkRange EMPTY_BLOCKS = EMPTY_RANGE; struct RunShifts { RunShifts() { } RunShifts(size_t count) { fShifts.push_back_n(count, 0.0); } SkSTArray<128, SkScalar, true> fShifts; }; class Run { public: Run() = default; Run(ParagraphImpl* master, const SkShaper::RunHandler::RunInfo& info, SkScalar lineHeight, size_t index, SkScalar shiftX); ~Run() {} void setMaster(ParagraphImpl* master) { fMaster = master; } SkShaper::RunHandler::Buffer newRunBuffer(); size_t size() const { return fGlyphs.size(); } void setWidth(SkScalar width) { fAdvance.fX = width; } void setHeight(SkScalar height) { fAdvance.fY = height; } void shift(SkScalar shiftX, SkScalar shiftY) { fOffset.fX += shiftX; fOffset.fY += shiftY; } SkVector advance() const { return SkVector::Make(fAdvance.fX, fFontMetrics.fDescent - fFontMetrics.fAscent); } SkVector offset() const { return fOffset; } SkScalar ascent() const { return fFontMetrics.fAscent; } //SkScalar descent() const { return fFontMetrics.fDescent; } //SkScalar leading() const { return fFontMetrics.fLeading; } SkScalar correctAscent() const { if (fHeightMultiplier == 0 || fHeightMultiplier == 1) { return fFontMetrics.fAscent - fFontMetrics.fLeading / 2; } return fFontMetrics.fAscent * fHeightMultiplier * fFont.getSize() / (fFontMetrics.fDescent - fFontMetrics.fAscent + fFontMetrics.fLeading / 2); } SkScalar correctDescent() const { if (fHeightMultiplier == 0 || fHeightMultiplier == 1) { return fFontMetrics.fDescent + fFontMetrics.fLeading / 2; } return fFontMetrics.fDescent * fHeightMultiplier * fFont.getSize() / (fFontMetrics.fDescent - fFontMetrics.fAscent + fFontMetrics.fLeading / 2); } SkScalar correctLeading() const { if (fHeightMultiplier == 0 || fHeightMultiplier == 1) { return fFontMetrics.fAscent; } return fFontMetrics.fLeading * fHeightMultiplier * fFont.getSize() / (fFontMetrics.fDescent - fFontMetrics.fAscent + fFontMetrics.fLeading); } const SkFont& font() const { return fFont; } bool leftToRight() const { return fBidiLevel % 2 == 0; } size_t index() const { return fIndex; } SkScalar lineHeight() const { return fHeightMultiplier; } size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; } SkScalar positionX(size_t pos) const; TextRange textRange() { return fTextRange; } ClusterRange clusterRange() { return fClusterRange; } void setClusterRange(size_t from, size_t to) { fClusterRange = ClusterRange(from, to); } SkRect clip() const { return SkRect::MakeXYWH(fOffset.fX, fOffset.fY, fAdvance.fX, fAdvance.fY); } SkScalar addSpacesAtTheEnd(SkScalar space, Cluster* cluster); SkScalar addSpacesEvenly(SkScalar space, Cluster* cluster); void shift(const Cluster* cluster, SkScalar offset); SkScalar calculateHeight() const { if (fHeightMultiplier == 0 || fHeightMultiplier == 1) { return fFontMetrics.fDescent - fFontMetrics.fAscent; } return fHeightMultiplier * fFont.getSize(); } SkScalar calculateWidth(size_t start, size_t end, bool clip) const; void copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const; using ClusterVisitor = std::function; void iterateThroughClustersInTextOrder(const ClusterVisitor& visitor); std::tuple findLimitingClusters(TextRange); SkSpan glyphs() const { return SkSpan(fGlyphs.begin(), fGlyphs.size()); } SkSpan positions() const { return SkSpan(fPositions.begin(), fPositions.size()); } SkSpan clusterIndexes() const { return SkSpan(fClusterIndexes.begin(), fClusterIndexes.size()); } SkSpan offsets() const { return SkSpan(fOffsets.begin(), fOffsets.size()); } private: friend class ParagraphImpl; friend class TextLine; friend class LineMetrics; friend class ParagraphCache; ParagraphImpl* fMaster; TextRange fTextRange; ClusterRange fClusterRange; SkFont fFont; SkFontMetrics fFontMetrics; SkScalar fHeightMultiplier; size_t fIndex; uint8_t fBidiLevel; SkVector fAdvance; SkVector fOffset; SkShaper::RunHandler::Range fUtf8Range; SkSTArray<128, SkGlyphID, false> fGlyphs; SkSTArray<128, SkPoint, true> fPositions; SkSTArray<128, uint32_t, true> fClusterIndexes; SkSTArray<128, SkScalar, true> fOffsets; // For formatting (letter/word spacing, justification) bool fSpaced; }; struct Codepoint { Codepoint(GraphemeIndex graphemeIndex, TextIndex textIndex) : fGrapeme(graphemeIndex), fTextIndex(textIndex) { } GraphemeIndex fGrapeme; TextIndex fTextIndex; // Used for getGlyphPositionAtCoordinate }; struct Grapheme { Grapheme(CodepointRange codepoints, TextRange textRange) : fCodepointRange(codepoints), fTextRange(textRange) { } CodepointRange fCodepointRange; TextRange fTextRange; // Used for getRectsForRange }; class Cluster { public: enum BreakType { None, CharacterBoundary, // not yet in use (UBRK_CHARACTER) WordBoundary, // calculated for all clusters (UBRK_WORD) WordBreakWithoutHyphen, // calculated only for hyphenated words WordBreakWithHyphen, SoftLineBreak, // calculated for all clusters (UBRK_LINE) HardLineBreak, // calculated for all clusters (UBRK_LINE) }; Cluster() : fMaster(nullptr) , fRunIndex(EMPTY_RUN) , fTextRange(EMPTY_TEXT) , fGraphemeRange(EMPTY_RANGE) , fStart(0) , fEnd() , fWidth() , fSpacing(0) , fHeight() , fWhiteSpaces(false) , fBreakType(None) {} Cluster(ParagraphImpl* master, RunIndex runIndex, size_t start, size_t end, SkSpan text, SkScalar width, SkScalar height); Cluster(TextRange textRange) : fTextRange(textRange), fGraphemeRange(EMPTY_RANGE) { } ~Cluster() = default; void setMaster(ParagraphImpl* master) { fMaster = master; } SkScalar sizeToChar(TextIndex ch) const; SkScalar sizeFromChar(TextIndex ch) const; size_t roundPos(SkScalar s) const; void space(SkScalar shift, SkScalar space) { fSpacing += space; fWidth += shift; } void setBreakType(BreakType type) { fBreakType = type; } bool isWhitespaces() const { return fWhiteSpaces; } bool canBreakLineAfter() const { return fBreakType == SoftLineBreak || fBreakType == HardLineBreak; } bool isHardBreak() const { return fBreakType == HardLineBreak; } bool isSoftBreak() const { return fBreakType == SoftLineBreak; } size_t startPos() const { return fStart; } size_t endPos() const { return fEnd; } SkScalar width() const { return fWidth; } SkScalar trimmedWidth() const { return fWidth - fSpacing; } SkScalar lastSpacing() const { return fSpacing; } SkScalar height() const { return fHeight; } size_t size() const { return fEnd - fStart; } TextRange textRange() const { return fTextRange; } RunIndex runIndex() const { return fRunIndex; } Run* run() const; SkFont font() const; SkScalar trimmedWidth(size_t pos) const; void setIsWhiteSpaces(); bool contains(TextIndex ch) const { return ch >= fTextRange.start && ch < fTextRange.end; } bool belongs(TextRange text) const { return fTextRange.start >= text.start && fTextRange.end <= text.end; } bool startsIn(TextRange text) const { return fTextRange.start >= text.start && fTextRange.start < text.end; } private: friend ParagraphImpl; ParagraphImpl* fMaster; RunIndex fRunIndex; TextRange fTextRange; GraphemeRange fGraphemeRange; size_t fStart; size_t fEnd; SkScalar fWidth; SkScalar fSpacing; SkScalar fHeight; bool fWhiteSpaces; BreakType fBreakType; }; class LineMetrics { public: LineMetrics() { clean(); } LineMetrics(bool forceStrut) : fForceStrut(forceStrut) { clean(); } LineMetrics(SkScalar a, SkScalar d, SkScalar l) : fForceStrut(false) { fAscent = a; fDescent = d; fLeading = l; } LineMetrics(const SkFont& font, bool forceStrut) : fForceStrut(forceStrut) { SkFontMetrics metrics; font.getMetrics(&metrics); fAscent = metrics.fAscent; fDescent = metrics.fDescent; fLeading = metrics.fLeading; } void add(Run* run) { if (fForceStrut) { return; } fAscent = SkTMin(fAscent, run->correctAscent()); fDescent = SkTMax(fDescent, run->correctDescent()); fLeading = SkTMax(fLeading, run->correctLeading()); } void add(LineMetrics other) { fAscent = SkTMin(fAscent, other.fAscent); fDescent = SkTMax(fDescent, other.fDescent); fLeading = SkTMax(fLeading, other.fLeading); } void clean() { fAscent = 0; fDescent = 0; fLeading = 0; } SkScalar delta() const { return height() - ideographicBaseline(); } void updateLineMetrics(LineMetrics& metrics) { metrics.fAscent = SkTMin(metrics.fAscent, fAscent); metrics.fDescent = SkTMax(metrics.fDescent, fDescent); metrics.fLeading = SkTMax(metrics.fLeading, fLeading); } SkScalar runTop(Run* run) const { return fLeading / 2 - fAscent + run->ascent() + delta(); } SkScalar height() const { return SkScalarRoundToInt(fDescent - fAscent + fLeading); } SkScalar alphabeticBaseline() const { return fLeading / 2 - fAscent; } SkScalar ideographicBaseline() const { return fDescent - fAscent + fLeading; } SkScalar baseline() const { return fLeading / 2 - fAscent; } SkScalar ascent() const { return fAscent; } SkScalar descent() const { return fDescent; } SkScalar leading() const { return fLeading; } private: SkScalar fAscent; SkScalar fDescent; SkScalar fLeading; bool fForceStrut; }; } // namespace textlayout } // namespace skia #endif // Run_DEFINED