// 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 bool operator==(const SkSpan& a, const SkSpan& b) { return a.size() == b.size() && a.begin() == b.begin(); } template bool operator<=(const SkSpan& a, const SkSpan& b) { return a.begin() >= b.begin() && a.end() <= b.end(); } template 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 blocks, sk_sp fonts); ParagraphImpl(const std::u16string& utf16text, ParagraphStyle style, SkTArray blocks, sk_sp fonts); ~ParagraphImpl() override; void layout(SkScalar width) override; void paint(SkCanvas* canvas, SkScalar x, SkScalar y) override; std::vector getRectsForRange(unsigned start, unsigned end, RectHeightStyle rectHeightStyle, RectWidthStyle rectWidthStyle) override; PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx, SkScalar dy) override; SkRange 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 text() const { return fTextSpan; } InternalState state() const { return fState; } SkSpan runs() { return SkSpan(fRuns.data(), fRuns.size()); } const SkTArray& switches() const { return fFontResolver.switches(); } SkSpan styles() { return SkSpan(fTextStyles.data(), fTextStyles.size()); } SkSpan lines() { return SkSpan(fLines.data(), fLines.size()); } const ParagraphStyle& paragraphStyle() const { return fParagraphStyle; } SkSpan clusters() { return SkSpan(fClusters.begin(), fClusters.size()); } sk_sp 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 text(TextRange textRange); SkSpan clusters(ClusterRange clusterRange); Cluster& cluster(ClusterIndex clusterIndex); Run& run(RunIndex runIndex); SkSpan blocks(BlockRange blockRange); Block& block(BlockIndex blockIndex); void markDirty() override { fState = kUnknown; } FontResolver& getResolver() { return fFontResolver; } void setState(InternalState state); sk_sp 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> fLetterSpaceStyles; SkTArray> fWordSpaceStyles; SkTArray> fBackgroundStyles; SkTArray> fForegroundStyles; SkTArray>> fShadowStyles; SkTArray> fDecorationStyles; SkTArray fTextStyles; // TODO: take out only the font stuff SkString fText; SkSpan fTextSpan; // Internal structures InternalState fState; SkTArray fRuns; // kShaped SkTArray fClusters; // kClusterized (cached: text, word spacing, letter spacing, resolved fonts) SkTArray fGraphemes; SkTArray fCodePoints; SkTArray fRunShifts; SkTArray fLines; // kFormatted (cached: width, max lines, ellipsis, text align) sk_sp fPicture; // kRecorded (cached: text styles) LineMetrics fStrutMetrics; FontResolver fFontResolver; SkScalar fOldWidth; SkScalar fOldHeight; SkScalar fMaxWidthWithTrailingSpaces; }; } // namespace textlayout } // namespace skia #endif // ParagraphImpl_DEFINED