| // Copyright 2019 Google LLC. |
| #ifndef ParagraphImpl_DEFINED |
| #define ParagraphImpl_DEFINED |
| |
| #include "include/core/SkPicture.h" |
| #include "include/private/SkMutex.h" |
| #include "include/private/SkTHash.h" |
| #include "modules/skparagraph/include/Paragraph.h" |
| #include "modules/skparagraph/include/ParagraphStyle.h" |
| #include "modules/skparagraph/include/TextStyle.h" |
| #include "modules/skparagraph/src/FontResolver.h" |
| #include "modules/skparagraph/src/Run.h" |
| #include "modules/skparagraph/src/TextLine.h" |
| |
| class SkCanvas; |
| |
| namespace skia { |
| namespace textlayout { |
| |
| template <typename T> bool operator==(const SkSpan<T>& a, const SkSpan<T>& b) { |
| return a.size() == b.size() && a.begin() == b.begin(); |
| } |
| |
| template <typename T> bool operator<=(const SkSpan<T>& a, const SkSpan<T>& b) { |
| return a.begin() >= b.begin() && a.end() <= b.end(); |
| } |
| |
| template <typename TStyle> |
| struct StyleBlock { |
| StyleBlock() : fRange(EMPTY_RANGE), fStyle() { } |
| StyleBlock(size_t start, size_t end, const TStyle& style) : fRange(start, end), fStyle(style) {} |
| StyleBlock(TextRange textRange, const TStyle& style) : fRange(textRange), fStyle(style) {} |
| void add(TextRange tail) { |
| SkASSERT(fRange.end == tail.start); |
| fRange = TextRange(fRange.start, fRange.start + fRange.width() + tail.width()); |
| } |
| TextRange fRange; |
| TStyle fStyle; |
| }; |
| |
| class ParagraphImpl final : public Paragraph { |
| |
| public: |
| |
| ParagraphImpl(const SkString& text, |
| ParagraphStyle style, |
| SkTArray<Block, true> blocks, |
| sk_sp<FontCollection> fonts); |
| |
| ParagraphImpl(const std::u16string& utf16text, |
| ParagraphStyle style, |
| SkTArray<Block, true> blocks, |
| sk_sp<FontCollection> fonts); |
| ~ParagraphImpl() override; |
| |
| void layout(SkScalar width) override; |
| void paint(SkCanvas* canvas, SkScalar x, SkScalar y) override; |
| std::vector<TextBox> getRectsForRange(unsigned start, |
| unsigned end, |
| RectHeightStyle rectHeightStyle, |
| RectWidthStyle rectWidthStyle) override; |
| PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx, SkScalar dy) override; |
| SkRange<size_t> getWordBoundary(unsigned offset) override; |
| bool didExceedMaxLines() override { |
| return !fParagraphStyle.unlimited_lines() && fLines.size() > fParagraphStyle.getMaxLines(); |
| } |
| |
| size_t lineNumber() override { return fLines.size(); } |
| |
| TextLine& addLine(SkVector offset, SkVector advance, TextRange text, TextRange textWithSpaces, |
| ClusterRange clusters, ClusterRange clustersWithGhosts, SkScalar AddLineToParagraph, |
| LineMetrics sizes); |
| |
| SkSpan<const char> text() const { return fTextSpan; } |
| InternalState state() const { return fState; } |
| SkSpan<Run> runs() { return SkSpan<Run>(fRuns.data(), fRuns.size()); } |
| const SkTArray<FontDescr>& switches() const { return fFontResolver.switches(); } |
| SkSpan<Block> styles() { |
| return SkSpan<Block>(fTextStyles.data(), fTextStyles.size()); |
| } |
| SkSpan<TextLine> lines() { return SkSpan<TextLine>(fLines.data(), fLines.size()); } |
| const ParagraphStyle& paragraphStyle() const { return fParagraphStyle; } |
| SkSpan<Cluster> clusters() { return SkSpan<Cluster>(fClusters.begin(), fClusters.size()); } |
| sk_sp<FontCollection> fontCollection() const { return fFontCollection; } |
| void formatLines(SkScalar maxWidth); |
| |
| void shiftCluster(ClusterIndex index, SkScalar shift) { |
| auto& cluster = fClusters[index]; |
| auto& run = fRunShifts[cluster.runIndex()]; |
| for (size_t pos = cluster.startPos(); pos < cluster.endPos(); ++pos) { |
| run.fShifts[pos] = shift; |
| } |
| } |
| |
| SkScalar posShift(RunIndex index, size_t pos) const { |
| if (fRunShifts.count() == 0) return 0.0; |
| return fRunShifts[index].fShifts[pos]; |
| } |
| |
| SkScalar lineShift(size_t index) { return fLines[index].shift(); } |
| |
| bool strutEnabled() const { return paragraphStyle().getStrutStyle().getStrutEnabled(); } |
| bool strutForceHeight() const { |
| return paragraphStyle().getStrutStyle().getForceStrutHeight(); |
| } |
| bool strutHeightOverride() const { |
| return paragraphStyle().getStrutStyle().getHeightOverride(); |
| } |
| LineMetrics strutMetrics() const { return fStrutMetrics; } |
| |
| Measurement measurement() { |
| return { |
| fAlphabeticBaseline, |
| fIdeographicBaseline, |
| fHeight, |
| fWidth, |
| fMaxIntrinsicWidth, |
| fMinIntrinsicWidth, |
| }; |
| } |
| void setMeasurement(Measurement m) { |
| fAlphabeticBaseline = m.fAlphabeticBaseline; |
| fIdeographicBaseline = m.fIdeographicBaseline; |
| fHeight = m.fHeight; |
| fWidth = m.fWidth; |
| fMaxIntrinsicWidth = m.fMaxIntrinsicWidth; |
| fMinIntrinsicWidth = m.fMinIntrinsicWidth; |
| } |
| |
| SkSpan<const char> text(TextRange textRange); |
| SkSpan<Cluster> clusters(ClusterRange clusterRange); |
| Cluster& cluster(ClusterIndex clusterIndex); |
| Run& run(RunIndex runIndex); |
| SkSpan<Block> blocks(BlockRange blockRange); |
| Block& block(BlockIndex blockIndex); |
| |
| void markDirty() override { fState = kUnknown; } |
| FontResolver& getResolver() { return fFontResolver; } |
| void setState(InternalState state); |
| sk_sp<SkPicture> getPicture() { return fPicture; } |
| |
| void resetContext(); |
| void resolveStrut(); |
| void resetRunShifts(); |
| void buildClusterTable(); |
| void markLineBreaks(); |
| bool shapeTextIntoEndlessLine(); |
| void breakShapedTextIntoLines(SkScalar maxWidth); |
| void paintLinesIntoPicture(); |
| |
| private: |
| friend class ParagraphBuilder; |
| friend class ParagraphCacheKey; |
| friend class ParagraphCacheValue; |
| friend class ParagraphCache; |
| |
| friend class TextWrapper; |
| |
| BlockRange findAllBlocks(TextRange textRange); |
| void extractStyles(); |
| |
| void markGraphemes(); |
| |
| // Input |
| SkTArray<StyleBlock<SkScalar>> fLetterSpaceStyles; |
| SkTArray<StyleBlock<SkScalar>> fWordSpaceStyles; |
| SkTArray<StyleBlock<SkPaint>> fBackgroundStyles; |
| SkTArray<StyleBlock<SkPaint>> fForegroundStyles; |
| SkTArray<StyleBlock<std::vector<TextShadow>>> fShadowStyles; |
| SkTArray<StyleBlock<Decoration>> fDecorationStyles; |
| SkTArray<Block, true> fTextStyles; // TODO: take out only the font stuff |
| SkString fText; |
| SkSpan<const char> fTextSpan; |
| |
| // Internal structures |
| InternalState fState; |
| SkTArray<Run> fRuns; // kShaped |
| SkTArray<Cluster, true> fClusters; // kClusterized (cached: text, word spacing, letter spacing, resolved fonts) |
| SkTArray<Grapheme, true> fGraphemes; |
| SkTArray<Codepoint, true> fCodePoints; |
| |
| SkTArray<RunShifts, true> fRunShifts; |
| SkTArray<TextLine, true> fLines; // kFormatted (cached: width, max lines, ellipsis, text align) |
| sk_sp<SkPicture> fPicture; // kRecorded (cached: text styles) |
| |
| LineMetrics fStrutMetrics; |
| FontResolver fFontResolver; |
| |
| SkScalar fOldWidth; |
| SkScalar fOldHeight; |
| SkScalar fMaxWidthWithTrailingSpaces; |
| }; |
| } // namespace textlayout |
| } // namespace skia |
| |
| #endif // ParagraphImpl_DEFINED |