// Copyright 2019 Google LLC. #ifndef ParagraphImpl_DEFINED #define ParagraphImpl_DEFINED #include "include/core/SkFont.h" #include "include/core/SkPaint.h" #include "include/core/SkPicture.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkSpan.h" #include "include/core/SkString.h" #include "include/core/SkTypes.h" #include "include/private/SkBitmaskEnum.h" #include "include/private/SkOnce.h" #include "include/private/SkTArray.h" #include "include/private/SkTemplates.h" #include "modules/skparagraph/include/DartTypes.h" #include "modules/skparagraph/include/FontCollection.h" #include "modules/skparagraph/include/Paragraph.h" #include "modules/skparagraph/include/ParagraphCache.h" #include "modules/skparagraph/include/ParagraphStyle.h" #include "modules/skparagraph/include/TextLineBase.h" #include "modules/skparagraph/include/TextShadow.h" #include "modules/skparagraph/include/TextStyle.h" #include "modules/skparagraph/src/Run.h" #include "modules/skparagraph/src/TextLine.h" #include "modules/skunicode/include/SkUnicode.h" #include "include/private/SkTHash.h" #include #include #include class SkCanvas; namespace skia { namespace textlayout { class LineMetrics; class TextLine; 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; }; struct ResolvedFontDescriptor { #ifndef USE_SKIA_TXT ResolvedFontDescriptor(TextIndex index, SkFont font) : fFont(font), fTextStart(index) { } SkFont fFont; #else ResolvedFontDescriptor(TextIndex index, RSFont font) : fFont(font), fTextStart(index) { } RSFont fFont; #endif TextIndex fTextStart; }; enum InternalState { kUnknown = 0, kIndexed = 1, // Text is indexed kShaped = 2, // Text is shaped kLineBroken = 5, kFormatted = 6, kDrawn = 7 }; /* struct BidiRegion { BidiRegion(size_t start, size_t end, uint8_t dir) : text(start, end), direction(dir) { } TextRange text; uint8_t direction; }; */ class ParagraphImpl final : public Paragraph { public: ParagraphImpl(); ParagraphImpl(const SkString& text, ParagraphStyle style, SkTArray blocks, SkTArray placeholders, sk_sp fonts, std::shared_ptr unicode); ParagraphImpl(const std::u16string& utf16text, ParagraphStyle style, SkTArray blocks, SkTArray placeholders, sk_sp fonts, std::shared_ptr unicode); ~ParagraphImpl() override; void layout(SkScalar width) override; void paint(SkCanvas* canvas, SkScalar x, SkScalar y) override; void paint(ParagraphPainter* canvas, SkScalar x, SkScalar y) override; void paint(ParagraphPainter* canvas, RSPath* path, SkScalar hOffset, SkScalar vOffset) override; std::vector getRectsForRange(unsigned start, unsigned end, RectHeightStyle rectHeightStyle, RectWidthStyle rectWidthStyle) override; std::vector getRectsForPlaceholders() override; void getLineMetrics(std::vector&) override; PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx, SkScalar dy) override; SkRange getWordBoundary(unsigned offset) override; bool getApplyRoundingHack() const { return false; } size_t lineNumber() override { return fLineNumber; } #ifdef OHOS_SUPPORT TextRange getEllipsisTextRange() override; #endif TextLine& addLine(SkVector offset, SkVector advance, TextRange textExcludingSpaces, TextRange text, TextRange textIncludingNewlines, ClusterRange clusters, ClusterRange clustersWithGhosts, SkScalar widthWithSpaces, InternalLineMetrics sizes); SkSpan text() const { return SkSpan(fText.c_str(), fText.size()); } std::vector convertUtf8ToUnicode(const SkString& utf8); #ifdef OHOS_SUPPORT std::unique_ptr createCroppedCopy( size_t startIndex, size_t count = std::numeric_limits::max()) override; void initUnicodeText() override; const std::vector& unicodeText() const override { return fUnicodeText; } size_t getUnicodeIndex(TextIndex index) const override { if (index >= fUnicodeIndexForUTF8Index.size()) { return fUnicodeIndexForUTF8Index.empty() ? 0 : fUnicodeIndexForUTF8Index.back() + 1; } return fUnicodeIndexForUTF8Index[index]; } #else const std::vector& unicodeText() const { return fUnicodeText; } size_t getUnicodeIndex(TextIndex index) const { if (index >= fUnicodeIndexForUTF8Index.size()) { return fUnicodeIndexForUTF8Index.empty() ? 0 : fUnicodeIndexForUTF8Index.back() + 1; } return fUnicodeIndexForUTF8Index[index]; } #endif InternalState state() const { return fState; } SkSpan runs() { return SkSpan(fRuns.data(), fRuns.size()); } SkSpan styles() { return SkSpan(fTextStyles.data(), fTextStyles.size()); } SkSpan placeholders() { return SkSpan(fPlaceholders.data(), fPlaceholders.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 ensureUTF16Mapping(); SkTArray countSurroundingGraphemes(TextRange textRange) const; TextIndex findNextGraphemeBoundary(TextIndex utf8) const; TextIndex findPreviousGraphemeBoundary(TextIndex utf8) const; TextIndex findNextGlyphClusterBoundary(TextIndex utf8) const; TextIndex findPreviousGlyphClusterBoundary(TextIndex utf8) const; size_t getUTF16Index(TextIndex index) const { return fUTF16IndexForUTF8Index[index]; } bool strutEnabled() const { return paragraphStyle().getStrutStyle().getStrutEnabled(); } bool strutForceHeight() const { return paragraphStyle().getStrutStyle().getForceStrutHeight(); } bool strutHeightOverride() const { return paragraphStyle().getStrutStyle().getHeightOverride(); } InternalLineMetrics strutMetrics() const { return fStrutMetrics; } SkString getEllipsis() const; WordBreakType getWordBreakType() const; LineBreakStrategy getLineBreakStrategy() const; SkSpan text(TextRange textRange); SkSpan clusters(ClusterRange clusterRange); Cluster& cluster(ClusterIndex clusterIndex); ClusterIndex clusterIndex(TextIndex textIndex) { auto clusterIndex = this->fClustersIndexFromCodeUnit[textIndex]; SkASSERT(clusterIndex != EMPTY_INDEX); return clusterIndex; } Run& run(RunIndex runIndex) { SkASSERT(runIndex < SkToSizeT(fRuns.size())); return fRuns[runIndex]; } Run& runByCluster(ClusterIndex clusterIndex); SkSpan blocks(BlockRange blockRange); Block& block(BlockIndex blockIndex); SkTArray resolvedFonts() const { return fFontSwitches; } void markDirty() override { if (fState > kIndexed) { fState = kIndexed; } } int32_t unresolvedGlyphs() override; std::unordered_set unresolvedCodepoints() override; void addUnresolvedCodepoints(TextRange textRange); void setState(InternalState state); sk_sp getPicture() { return fPicture; } SkScalar widthWithTrailingSpaces() { return fMaxWidthWithTrailingSpaces; } SkScalar getMaxWidth() { return fOldMaxWidth; } void resetContext(); void resolveStrut(); bool computeCodeUnitProperties(); void applySpacingAndBuildClusterTable(); void buildClusterTable(); bool shapeTextIntoEndlessLine(); void positionShapedTextIntoLine(SkScalar maxWidth); void breakShapedTextIntoLines(SkScalar maxWidth); void updateTextAlign(TextAlign textAlign) override; void updateFontSize(size_t from, size_t to, SkScalar fontSize) override; void updateForegroundPaint(size_t from, size_t to, SkPaint paint) override; void updateBackgroundPaint(size_t from, size_t to, SkPaint paint) override; #ifdef OHOS_SUPPORT std::vector updateColor(size_t from, size_t to, SkColor color) override; #endif void visit(const Visitor&) override; void setIndents(const std::vector& indents) override; int getLineNumberAt(TextIndex codeUnitIndex) const override; bool getLineMetricsAt(int lineNumber, LineMetrics* lineMetrics) const override; TextRange getActualTextRange(int lineNumber, bool includeSpaces) const override; bool getGlyphClusterAt(TextIndex codeUnitIndex, GlyphClusterInfo* glyphInfo) override; bool getClosestGlyphClusterAt(SkScalar dx, SkScalar dy, GlyphClusterInfo* glyphInfo) override; #ifndef USE_SKIA_TXT SkFont getFontAt(TextIndex codeUnitIndex) const override; #else RSFont getFontAt(TextIndex codeUnitIndex) const override; #endif std::vector getFonts() const override; InternalLineMetrics getEmptyMetrics() const { return fEmptyMetrics; } InternalLineMetrics getStrutMetrics() const { return fStrutMetrics; } BlockRange findAllBlocks(TextRange textRange); void resetShifts() { for (auto& run : fRuns) { run.resetJustificationShifts(); } } void resetAutoSpacing() { for (auto& run : fRuns) { run.resetAutoSpacing(); } } void scanTextCutPoint(const std::vector& rawTextSize, size_t& start, size_t& end); bool middleEllipsisDeal(); bool codeUnitHasProperty(size_t index, SkUnicode::CodeUnitFlags property) const { return (fCodeUnitProperties[index] & property) == property; } SkUnicode* getUnicode() { return fUnicode.get(); } SkScalar detectIndents(size_t index) override; SkScalar getTextSplitRatio() const override { return fParagraphStyle.getTextSplitRatio(); } #ifndef USE_SKIA_TXT SkFontMetrics measureText() override; #else RSFontMetrics measureText() override; #endif bool &getEllipsisState() { return isMiddleEllipsis; } #ifndef USE_SKIA_TXT bool GetLineFontMetrics(const size_t lineNumber, size_t& charNumber, std::vector& fontMetrics) override; #else bool GetLineFontMetrics(const size_t lineNumber, size_t& charNumber, std::vector& fontMetrics) override; #endif std::vector> GetTextLines() override; std::unique_ptr CloneSelf() override; uint32_t& hash() { return hash_; } #ifdef OHOS_SUPPORT size_t GetMaxLines() const override { return fParagraphStyle.getMaxLines(); } #endif private: friend class ParagraphBuilder; friend class ParagraphCacheKey; friend class ParagraphCacheValue; friend class ParagraphCache; friend class TextWrapper; friend class OneLineShaper; #ifdef OHOS_SUPPORT void middleEllipsisLtrDeal(size_t& end, size_t& charbegin, size_t& charend); void middleEllipsisRtlDeal(size_t& end, size_t& charbegin, size_t& charend); #endif void computeEmptyMetrics(); void middleEllipsisAddText(size_t charStart, size_t charEnd, SkScalar& allTextWidth, SkScalar width, bool isLeftToRight); SkScalar resetEllipsisWidth(SkScalar ellipsisWidth, size_t& lastRunIndex, const size_t textIndex); void scanRTLTextCutPoint(const std::vector& rawTextSize, size_t& start, size_t& end); void scanLTRTextCutPoint(const std::vector& rawTextSize, size_t& start, size_t& end); void prepareForMiddleEllipsis(SkScalar rawWidth); bool shapeForMiddleEllipsis(SkScalar rawWidth); TextRange resetRangeWithDeletedRange(const TextRange& sourceRange, const TextRange& deletedRange, const size_t& ellSize); void resetTextStyleRange(const TextRange& deletedRange); void resetPlaceholderRange(const TextRange& deletedRange); void setSize(SkScalar height, SkScalar width, SkScalar longestLine) { fHeight = height; fWidth = width; fLongestLine = longestLine; } void getSize(SkScalar& height, SkScalar& width, SkScalar& longestLine) { height = fHeight; width = fWidth; longestLine = fLongestLine; } void setIntrinsicSize(SkScalar maxIntrinsicWidth, SkScalar minIntrinsicWidth, SkScalar alphabeticBaseline, SkScalar ideographicBaseline, bool exceededMaxLines) { fMaxIntrinsicWidth = maxIntrinsicWidth; fMinIntrinsicWidth = minIntrinsicWidth; fAlphabeticBaseline = alphabeticBaseline; fIdeographicBaseline = ideographicBaseline; fExceededMaxLines = exceededMaxLines; } void getIntrinsicSize(SkScalar& maxIntrinsicWidth, SkScalar& minIntrinsicWidth, SkScalar& alphabeticBaseline, SkScalar& ideographicBaseline, bool& exceededMaxLines) { maxIntrinsicWidth = fMaxIntrinsicWidth; minIntrinsicWidth = fMinIntrinsicWidth; alphabeticBaseline = fAlphabeticBaseline ; ideographicBaseline = fIdeographicBaseline; exceededMaxLines = fExceededMaxLines; } #ifdef OHOS_SUPPORT ParagraphPainter::PaintID updateTextStyleColorAndForeground(TextStyle& TextStyle, SkColor color); TextBox getEmptyTextRect(RectHeightStyle rectHeightStyle) const; size_t prefixByteCountUntilChar(size_t index); void copyProperties(const ParagraphImpl& source); #endif // Input SkTArray> fLetterSpaceStyles; SkTArray> fWordSpaceStyles; SkTArray> fBackgroundStyles; SkTArray> fForegroundStyles; SkTArray>> fShadowStyles; SkTArray> fDecorationStyles; SkTArray fTextStyles; // TODO: take out only the font stuff SkTArray fPlaceholders; SkString fText; std::vector fUnicodeText; // Internal structures InternalState fState; SkTArray fRuns; // kShaped SkTArray fClusters; // kClusterized (cached: text, word spacing, letter spacing, resolved fonts) SkTArray fCodeUnitProperties; SkTArray fClustersIndexFromCodeUnit; std::vector fWords; std::vector fIndents; std::vector rtlTextSize; std::vector ltrTextSize; std::vector fBidiRegions; // These two arrays are used in measuring methods (getRectsForRange, getGlyphPositionAtCoordinate) // They are filled lazily whenever they need and cached SkTArray fUTF8IndexForUTF16Index; SkTArray fUTF16IndexForUTF8Index; SkTArray fUnicodeIndexForUTF8Index; SkOnce fillUTF16MappingOnce; size_t fUnresolvedGlyphs; bool isMiddleEllipsis; std::unordered_set fUnresolvedCodepoints; SkTArray fLines; // kFormatted (cached: width, max lines, ellipsis, text align) sk_sp fPicture; // kRecorded (cached: text styles) SkTArray fFontSwitches; InternalLineMetrics fEmptyMetrics; InternalLineMetrics fStrutMetrics; SkScalar fOldWidth; SkScalar fOldHeight; SkScalar fMaxWidthWithTrailingSpaces; SkScalar fOldMaxWidth; SkScalar allTextWidth; std::shared_ptr fUnicode; bool fHasLineBreaks; bool fHasWhitespacesInside; TextIndex fTrailingSpaces; SkScalar fLayoutRawWidth {0}; size_t fLineNumber; uint32_t hash_{0u}; #ifdef OHOS_SUPPORT TextRange fEllipsisRange{EMPTY_RANGE}; #endif }; } // namespace textlayout } // namespace skia #endif // ParagraphImpl_DEFINED