// Copyright 2019 Google LLC. #ifndef LineBreaker_DEFINED #define LineBreaker_DEFINED #include // std::function #include #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; } private: struct RunBlock { RunBlock() : fRun(nullptr) { } // First unresolved block explicit RunBlock(TextRange text) : fRun(nullptr), fText(text) { } RunBlock(std::shared_ptr 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) : fRun(std::move(run)) , fText(fRun->fTextRange) , fGlyphs(GlyphRange(0, fRun->size())) { } std::shared_ptr fRun; TextRange fText; GlyphRange fGlyphs; bool isFullyResolved() { return fRun != nullptr && fGlyphs.width() == fRun->size(); } }; using ShapeVisitor = std::function, SkScalar&, TextIndex, uint8_t)>; bool iterateThroughShapingRegions(const ShapeVisitor& shape); using ShapeSingleFontVisitor = std::function)>; void iterateThroughFontStyles(TextRange textRange, SkSpan styleSpan, const ShapeSingleFontVisitor& visitor); enum Resolved { Nothing, Something, Everything }; using TypefaceVisitor = std::function 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(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&& 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 fCurrentRun; std::deque fUnresolvedBlocks; std::vector fResolvedBlocks; // Keeping all resolved typefaces struct FontKey { FontKey() {} FontKey(SkUnichar unicode, SkFontStyle fontStyle, SkString locale) : fUnicode(unicode), fFontStyle(fontStyle), fLocale(locale) { } SkUnichar fUnicode; SkFontStyle fFontStyle; SkString fLocale; bool operator==(const FontKey& other) const; struct Hasher { size_t operator()(const FontKey& key) const; }; }; SkTHashMap, FontKey::Hasher> fFallbackFonts; }; } // namespace textlayout } // namespace skia #endif