| // Copyright 2019 Google LLC. |
| #ifndef TextLine_DEFINED |
| #define TextLine_DEFINED |
| |
| #include "include/core/SkPoint.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkScalar.h" |
| #include "include/private/SkBitmaskEnum.h" // IWYU pragma: keep |
| #include "include/private/base/SkTArray.h" |
| #include "modules/skparagraph/include/DartTypes.h" |
| #include "modules/skparagraph/include/Metrics.h" |
| #include "modules/skparagraph/include/ParagraphPainter.h" |
| #include "modules/skparagraph/include/TextStyle.h" |
| #include "modules/skparagraph/src/Run.h" |
| |
| #include <stddef.h> |
| #include <functional> |
| #include <memory> |
| #include <vector> |
| |
| class SkString; |
| |
| namespace skia { |
| namespace textlayout { |
| |
| class ParagraphImpl; |
| |
| class TextLine { |
| public: |
| |
| struct ClipContext { |
| const Run* run; |
| size_t pos; |
| size_t size; |
| SkScalar fTextShift; // Shifts the text inside the run so it's placed at the right position |
| SkRect clip; |
| SkScalar fExcludedTrailingSpaces; |
| bool clippingNeeded; |
| }; |
| |
| enum TextAdjustment { |
| GlyphCluster = 0x01, // All text producing glyphs pointing to the same ClusterIndex |
| GlyphemeCluster = 0x02, // base glyph + all attached diacritics |
| Grapheme = 0x04, // Text adjusted to graphemes |
| GraphemeGluster = 0x05, // GlyphCluster & Grapheme |
| }; |
| |
| TextLine() = default; |
| TextLine(const TextLine&) = delete; |
| TextLine& operator=(const TextLine&) = delete; |
| TextLine(TextLine&&) = default; |
| TextLine& operator=(TextLine&&) = default; |
| ~TextLine() = default; |
| |
| TextLine(ParagraphImpl* owner, |
| SkVector offset, |
| SkVector advance, |
| BlockRange blocks, |
| TextRange textExcludingSpaces, |
| TextRange text, |
| TextRange textIncludingNewlines, |
| ClusterRange clusters, |
| ClusterRange clustersWithGhosts, |
| SkScalar widthWithSpaces, |
| InternalLineMetrics sizes); |
| |
| TextRange trimmedText() const { return fTextExcludingSpaces; } |
| TextRange textWithNewlines() const { return fTextIncludingNewlines; } |
| TextRange text() const { return fText; } |
| ClusterRange clusters() const { return fClusterRange; } |
| ClusterRange clustersWithSpaces() const { return fGhostClusterRange; } |
| Run* ellipsis() const { return fEllipsis.get(); } |
| InternalLineMetrics sizes() const { return fSizes; } |
| bool empty() const { return fTextExcludingSpaces.empty(); } |
| |
| SkScalar spacesWidth() const { return fWidthWithSpaces - width(); } |
| SkScalar height() const { return fAdvance.fY; } |
| SkScalar width() const { |
| return fAdvance.fX + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0); |
| } |
| SkScalar widthWithoutEllipsis() const { return fAdvance.fX; } |
| SkVector offset() const; |
| |
| SkScalar alphabeticBaseline() const { return fSizes.alphabeticBaseline(); } |
| SkScalar ideographicBaseline() const { return fSizes.ideographicBaseline(); } |
| SkScalar baseline() const { return fSizes.baseline(); } |
| |
| using RunVisitor = std::function<bool( |
| const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width)>; |
| void iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisitor& runVisitor) const; |
| using RunStyleVisitor = std::function<void( |
| TextRange textRange, const TextStyle& style, const ClipContext& context)>; |
| SkScalar iterateThroughSingleRunByStyles(TextAdjustment textAdjustment, |
| const Run* run, |
| SkScalar runOffset, |
| TextRange textRange, |
| StyleType styleType, |
| const RunStyleVisitor& visitor) const; |
| |
| using ClustersVisitor = std::function<bool(const Cluster* cluster, bool ghost)>; |
| void iterateThroughClustersInGlyphsOrder(bool reverse, |
| bool includeGhosts, |
| const ClustersVisitor& visitor) const; |
| |
| void format(TextAlign align, SkScalar maxWidth); |
| void paint(ParagraphPainter* painter, SkScalar x, SkScalar y); |
| void visit(SkScalar x, SkScalar y); |
| void ensureTextBlobCachePopulated(); |
| |
| void createEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr); |
| |
| // For testing internal structures |
| void scanStyles(StyleType style, const RunStyleVisitor& visitor); |
| |
| void setMaxRunMetrics(const InternalLineMetrics& metrics) { fMaxRunMetrics = metrics; } |
| InternalLineMetrics getMaxRunMetrics() const { return fMaxRunMetrics; } |
| |
| bool isFirstLine() const; |
| bool isLastLine() const; |
| void getRectsForRange(TextRange textRange, |
| RectHeightStyle rectHeightStyle, |
| RectWidthStyle rectWidthStyle, |
| std::vector<TextBox>& boxes) const; |
| void getRectsForPlaceholders(std::vector<TextBox>& boxes); |
| PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx); |
| |
| ClipContext measureTextInsideOneRun(TextRange textRange, |
| const Run* run, |
| SkScalar runOffsetInLine, |
| SkScalar textOffsetInRunInLine, |
| bool includeGhostSpaces, |
| TextAdjustment textAdjustment) const; |
| |
| LineMetrics getMetrics() const; |
| |
| SkRect extendHeight(const ClipContext& context) const; |
| |
| void shiftVertically(SkScalar shift) { fOffset.fY += shift; } |
| |
| void setAscentStyle(LineMetricStyle style) { fAscentStyle = style; } |
| void setDescentStyle(LineMetricStyle style) { fDescentStyle = style; } |
| |
| bool endsWithHardLineBreak() const; |
| |
| private: |
| std::unique_ptr<Run> shapeEllipsis(const SkString& ellipsis, const Cluster* cluster); |
| void justify(SkScalar maxWidth); |
| |
| void buildTextBlob(TextRange textRange, const TextStyle& style, const ClipContext& context); |
| void paintBackground(ParagraphPainter* painter, |
| SkScalar x, |
| SkScalar y, |
| TextRange textRange, |
| const TextStyle& style, |
| const ClipContext& context) const; |
| void paintShadow(ParagraphPainter* painter, |
| SkScalar x, |
| SkScalar y, |
| TextRange textRange, |
| const TextStyle& style, |
| const ClipContext& context) const; |
| void paintDecorations(ParagraphPainter* painter, |
| SkScalar x, |
| SkScalar y, |
| TextRange textRange, |
| const TextStyle& style, |
| const ClipContext& context) const; |
| |
| void shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift); |
| |
| ParagraphImpl* fOwner; |
| BlockRange fBlockRange; |
| TextRange fTextExcludingSpaces; |
| TextRange fText; |
| TextRange fTextIncludingNewlines; |
| ClusterRange fClusterRange; |
| ClusterRange fGhostClusterRange; |
| // Avoid the malloc/free in the common case of one run per line |
| SkSTArray<1, size_t, true> fRunsInVisualOrder; |
| SkVector fAdvance; // Text size |
| SkVector fOffset; // Text position |
| SkScalar fShift; // Let right |
| SkScalar fWidthWithSpaces; |
| std::unique_ptr<Run> fEllipsis; // In case the line ends with the ellipsis |
| InternalLineMetrics fSizes; // Line metrics as a max of all run metrics and struts |
| InternalLineMetrics fMaxRunMetrics; // No struts - need it for GetRectForRange(max height) |
| bool fHasBackground; |
| bool fHasShadows; |
| bool fHasDecorations; |
| |
| LineMetricStyle fAscentStyle; |
| LineMetricStyle fDescentStyle; |
| |
| struct TextBlobRecord { |
| void paint(ParagraphPainter* painter, SkScalar x, SkScalar y); |
| |
| sk_sp<SkTextBlob> fBlob; |
| SkPoint fOffset = SkPoint::Make(0.0f, 0.0f); |
| ParagraphPainter::SkPaintOrID fPaint; |
| SkRect fBounds = SkRect::MakeEmpty(); |
| bool fClippingNeeded = false; |
| SkRect fClipRect = SkRect::MakeEmpty(); |
| |
| // Extra fields only used for the (experimental) visitor |
| const Run* fVisitor_Run; |
| size_t fVisitor_Pos; |
| }; |
| bool fTextBlobCachePopulated; |
| public: |
| std::vector<TextBlobRecord> fTextBlobCache; |
| }; |
| } // namespace textlayout |
| } // namespace skia |
| |
| namespace sknonstd { |
| template <> struct is_bitmask_enum<skia::textlayout::TextLine::TextAdjustment> : std::true_type {}; |
| } // namespace sknonstd |
| |
| #endif // TextLine_DEFINED |