| // Copyright 2019 Google LLC | |
| #ifndef LineBreaker_DEFINED | |
| #define LineBreaker_DEFINED | |
| #include <functional> // std::function | |
| #include <queue> | |
| #include "include/core/SkSpan.h" | |
| #include "modules/skparagraph/include/TextStyle.h" | |
| #include "modules/skparagraph/src/ParagraphImpl.h" | |
| #include "modules/skparagraph/src/Run.h" | |
| namespace skia { | |
| namespace textlayout { | |
| class ParagraphImpl; | |
| class OneLineShaper : public SkShaper::RunHandler { | |
| public: | |
| explicit OneLineShaper(ParagraphImpl* paragraph) | |
| : fParagraph(paragraph) | |
| , fHeight(0.0f) | |
| , fUseHalfLeading(false) | |
| , fBaselineShift(0.0f) | |
| , fAdvance(SkPoint::Make(0.0f, 0.0f)) | |
| , fUnresolvedGlyphs(0) | |
| , fUniqueRunId(paragraph->fRuns.size()){ } | |
| bool shape(); | |
| size_t unresolvedGlyphs() { return fUnresolvedGlyphs; } | |
| /** | |
| * This method is based on definition of https://unicode.org/reports/tr51/#def_emoji_sequence | |
| * It determines if the string begins with an emoji sequence and, | |
| * if so, return the first codepoint, moving 'begin' pointer to the next once. | |
| * Otherwise it does not move the pointer and returns -1. | |
| */ | |
| static SkUnichar getEmojiSequenceStart(SkUnicode* unicode, const char** begin, const char* end); | |
| private: | |
| struct RunBlock { | |
| RunBlock() : fRun(nullptr) { } | |
| // First unresolved block | |
| explicit RunBlock(TextRange text) : fRun(nullptr), fText(text) { } | |
| RunBlock(std::shared_ptr<Run> run, TextRange text, GlyphRange glyphs, size_t score) | |
| : fRun(std::move(run)) | |
| , fText(text) | |
| , fGlyphs(glyphs) { } | |
| // Entire run comes as one block fully resolved | |
| explicit RunBlock(std::shared_ptr<Run> run) | |
| : fRun(std::move(run)) | |
| , fText(fRun->fTextRange) | |
| , fGlyphs(GlyphRange(0, fRun->size())) { } | |
| std::shared_ptr<Run> fRun; | |
| TextRange fText; | |
| GlyphRange fGlyphs; | |
| bool isFullyResolved() { return fRun != nullptr && fGlyphs.width() == fRun->size(); } | |
| }; | |
| using ShapeVisitor = | |
| std::function<SkScalar(TextRange textRange, SkSpan<Block>, SkScalar&, TextIndex, uint8_t)>; | |
| bool iterateThroughShapingRegions(const ShapeVisitor& shape); | |
| using ShapeSingleFontVisitor = | |
| std::function<void(Block, skia_private::TArray<SkShaper::Feature>)>; | |
| void iterateThroughFontStyles( | |
| TextRange textRange, SkSpan<Block> styleSpan, const ShapeSingleFontVisitor& visitor); | |
| enum Resolved { | |
| Nothing, | |
| Something, | |
| Everything | |
| }; | |
| using TypefaceVisitor = std::function<Resolved(sk_sp<SkTypeface> typeface)>; | |
| void matchResolvedFonts(const TextStyle& textStyle, const TypefaceVisitor& visitor); | |
| #ifdef SK_DEBUG | |
| void printState(); | |
| #endif | |
| void finish(const Block& block, SkScalar height, SkScalar& advanceX); | |
| void beginLine() override {} | |
| void runInfo(const RunInfo&) override {} | |
| void commitRunInfo() override {} | |
| void commitLine() override {} | |
| Buffer runBuffer(const RunInfo& info) override { | |
| fCurrentRun = std::make_shared<Run>(fParagraph, | |
| info, | |
| fCurrentText.start, | |
| fHeight, | |
| fUseHalfLeading, | |
| fBaselineShift, | |
| ++fUniqueRunId, | |
| fAdvance.fX); | |
| return fCurrentRun->newRunBuffer(); | |
| } | |
| void commitRunBuffer(const RunInfo&) override; | |
| TextRange clusteredText(GlyphRange& glyphs); | |
| ClusterIndex clusterIndex(GlyphIndex glyph) { | |
| return fCurrentText.start + fCurrentRun->fClusterIndexes[glyph]; | |
| } | |
| void addFullyResolved(); | |
| void addUnresolvedWithRun(GlyphRange glyphRange); | |
| void sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock); | |
| ClusterRange normalizeTextRange(GlyphRange glyphRange); | |
| void fillGaps(size_t); | |
| ParagraphImpl* fParagraph; | |
| TextRange fCurrentText; | |
| SkScalar fHeight; | |
| bool fUseHalfLeading; | |
| SkScalar fBaselineShift; | |
| SkVector fAdvance; | |
| size_t fUnresolvedGlyphs; | |
| size_t fUniqueRunId; | |
| // TODO: Something that is not thead-safe since we don't need it | |
| std::shared_ptr<Run> fCurrentRun; | |
| std::deque<RunBlock> fUnresolvedBlocks; | |
| std::vector<RunBlock> fResolvedBlocks; | |
| // Keeping all resolved typefaces | |
| struct FontKey { | |
| FontKey() {} | |
| FontKey(SkUnichar unicode, SkFontStyle fontStyle, SkString locale, const std::optional<FontArguments>& fontArgs) | |
| : fUnicode(unicode), fFontStyle(fontStyle), fLocale(std::move(locale)), fFontArgs(fontArgs) { } | |
| SkUnichar fUnicode; | |
| SkFontStyle fFontStyle; | |
| SkString fLocale; | |
| std::optional<FontArguments> fFontArgs; | |
| bool operator==(const FontKey& other) const; | |
| struct Hasher { | |
| uint32_t operator()(const FontKey& key) const; | |
| }; | |
| }; | |
| skia_private::THashMap<FontKey, sk_sp<SkTypeface>, FontKey::Hasher> fFallbackFonts; | |
| }; | |
| } // namespace textlayout | |
| } // namespace skia | |
| #endif |