From 2f9e0ff1beaba7b84ed3f5ba99f44ff82f16aa2a Mon Sep 17 00:00:00 2001 From: luhan Date: Wed, 12 Jun 2024 21:52:49 +0800 Subject: [PATCH] auto spacing fix Signed-off-by: luhan Change-Id: I7de04e4addce91e594283fd7f72d268fb1836fd6 --- modules/skparagraph/src/ParagraphCache.cpp | 2 + modules/skparagraph/src/ParagraphImpl.cpp | 93 ++++++++++++++-------- modules/skparagraph/src/ParagraphImpl.h | 4 + modules/skparagraph/src/Run.h | 18 +++-- modules/skparagraph/src/TextLine.cpp | 19 +---- modules/skparagraph/src/TextLine.h | 1 - modules/skparagraph/src/TextWrapper.cpp | 30 +------ modules/skparagraph/src/TextWrapper.h | 13 +-- 8 files changed, 90 insertions(+), 90 deletions(-) diff --git a/modules/skparagraph/src/ParagraphCache.cpp b/modules/skparagraph/src/ParagraphCache.cpp index 199524bae6..6ca0d5bdc3 100644 --- a/modules/skparagraph/src/ParagraphCache.cpp +++ b/modules/skparagraph/src/ParagraphCache.cpp @@ -363,6 +363,7 @@ void ParagraphCache::SetStoredLayoutImpl(ParagraphImpl& paragraph, ParagraphCach if (paragraph.fRuns.size() == value->fRuns.size()) { // update PlaceholderRun metrics cache value for placeholder alignment for (size_t idx = 0; idx < value->fRuns.size(); ++idx) { + value->fRuns[idx].fAutoSpacings = paragraph.fRuns[idx].fAutoSpacings; if (!value->fRuns[idx].isPlaceholder()) { continue; } @@ -420,6 +421,7 @@ bool ParagraphCache::GetStoredLayout(ParagraphImpl& paragraph) { if (paragraph.fRuns.size() == value->fRuns.size()) { // get PlaceholderRun metrics for placeholder alignment for (size_t idx = 0; idx < value->fRuns.size(); ++idx) { + paragraph.fRuns[idx].fAutoSpacings = value->fRuns[idx].fAutoSpacings; if (!value->fRuns[idx].isPlaceholder()) { continue; } diff --git a/modules/skparagraph/src/ParagraphImpl.cpp b/modules/skparagraph/src/ParagraphImpl.cpp index 833ed9c90c..5a846d7a04 100644 --- a/modules/skparagraph/src/ParagraphImpl.cpp +++ b/modules/skparagraph/src/ParagraphImpl.cpp @@ -790,54 +790,76 @@ static bool is_ascii_7bit_space(int c) { } #ifdef OHOS_SUPPORT -static std::vector> CJK_UNICODE_SET = { - SkRange(0x4E00, 0x9FFF), - SkRange(0x3400, 0x4DBF), - SkRange(0x20000, 0x2A6DF), - SkRange(0x2A700, 0x2B73F), - SkRange(0x2B740, 0x2B81F), - SkRange(0x2B820, 0x2CEAF), - SkRange(0x2CEB0, 0x2EBEF), - SkRange(0x30000, 0x3134F), - SkRange(0xF900, 0xFAFF), - SkRange(0x3040, 0x309F), - SkRange(0x30A0, 0x30FF), - SkRange(0x31F0, 0x31FF), +static const std::vector> CJK_UNICODE_SET = { SkRange(0x1100, 0x11FF), + SkRange(0x2E80, 0x2EFF), + // [0x3040, 0x309F](Hiragana) + [0x30A0, 0x30FF](Katakana) + SkRange(0x3040, 0x30FF), SkRange(0x3130, 0x318F), + // [0x31C0, 0x31EF](CJK Strokes) + [0x31F0, 0x31FF](Katakana Phonetic Extensions) + SkRange(0x31C0, 0x31FF), + SkRange(0x3400, 0x4DBF), + SkRange(0x4E00, 0x9FFF), SkRange(0xAC00, 0xD7AF), - SkRange(0x31C0, 0x31EF), - SkRange(0x2E80, 0x2EFF), + SkRange(0xF900, 0xFAFF), + SkRange(0x20000, 0x2A6DF), +/* + [0x2A700, 0x2B73F](CJK Unified Ideographs Extension C) + + [0x2B740, 0x2B81F](CJK Unified Ideographs Extension D) + + [0x2B820, 0x2CEAF](CJK Unified Ideographs Extension E) + + [0x2CEB0, 0x2EBEF](CJK Unified Ideographs Extension F) +*/ + SkRange(0x2A700, 0x2EBEF), SkRange(0x2F800, 0x2FA1F), + SkRange(0x30000, 0x3134F), }; -static std::vector> WESTERN_UNICODE_SET = { +static const std::vector> WESTERN_UNICODE_SET = { + SkRange(0x0030, 0x0039), SkRange(0x0041, 0x005A), SkRange(0x0061, 0x007A), - SkRange(0x0030, 0x0039), }; constexpr SkUnichar COPYRIGHT_UNICODE = 0x00A9; -struct UnicodeSet { - std::unordered_set set_; - explicit UnicodeSet(const std::vector>& unicodeSet) { +struct UnicodeIdentifier { + static bool cmp(SkRange a, SkRange b) { + return a.start < b.start; + } + const std::vector>& fUnicodeSet; + explicit UnicodeIdentifier(const std::vector>& unicodeSet) : fUnicodeSet(unicodeSet) {} + bool exist(SkUnichar c) const { if (!TextParameter::GetAutoSpacingEnable()) { - return; + return false; } - for (auto unicodeSetRange : unicodeSet) { - for (auto i = unicodeSetRange.start; i <= unicodeSetRange.end; ++i) { - set_.insert(i); - } + auto pos = std::upper_bound(fUnicodeSet.begin(), fUnicodeSet.end(), SkRange(c, c), cmp); + if (pos == fUnicodeSet.begin()) { + return false; } - } - bool exist(SkUnichar c) const { - return set_.find(c) != set_.end(); + --pos; + return pos->end >= c; } }; -static const UnicodeSet CJK_SET(CJK_UNICODE_SET); -static const UnicodeSet WESTERN_SET(WESTERN_UNICODE_SET); +static const UnicodeIdentifier CJK_IDENTIFIER(CJK_UNICODE_SET); +static const UnicodeIdentifier WESTERN_IDENTIFIER(WESTERN_UNICODE_SET); + +static Cluster::AutoSpacingFlag recognizeUnicodeAutoSpacingFlag(SkUnichar unicode) +{ + if (WESTERN_IDENTIFIER.exist(unicode)) { + return Cluster::AutoSpacingFlag::Western; + } + + if (CJK_IDENTIFIER.exist(unicode)) { + return Cluster::AutoSpacingFlag::CJK; + } + + if (unicode == COPYRIGHT_UNICODE) { + return Cluster::AutoSpacingFlag::Copyright; + } + + return Cluster::AutoSpacingFlag::NoFlag; +} #endif Cluster::Cluster(ParagraphImpl* owner, @@ -887,16 +909,19 @@ Cluster::Cluster(ParagraphImpl* owner, #ifdef OHOS_SUPPORT fIsTabulation = fOwner->codeUnitHasProperty(fTextRange.start, SkUnicode::CodeUnitFlags::kTabulation); -#endif auto unicodeStart = fOwner->getUnicodeIndex(fTextRange.start); auto unicodeEnd = fOwner->getUnicodeIndex(fTextRange.end); SkUnichar unicode = 0; if (unicodeEnd - unicodeStart == 1 && unicodeStart < fOwner->unicodeText().size()) { unicode = fOwner->unicodeText()[unicodeStart]; } - fIsCopyright = unicode == COPYRIGHT_UNICODE; - fIsCJK = CJK_SET.exist(unicode); - fIsWestern = WESTERN_SET.exist(unicode); + + auto curAutoSpacingFlag = recognizeUnicodeAutoSpacingFlag(unicode); + auto lastAutoSpacingFlag = fOwner->getLastAutoSpacingFlag(); + fNeedAutoSpacing = curAutoSpacingFlag != Cluster::AutoSpacingFlag::NoFlag && + curAutoSpacingFlag != lastAutoSpacingFlag && lastAutoSpacingFlag != Cluster::AutoSpacingFlag::NoFlag; + fOwner->setLastAutoSpacingFlag(curAutoSpacingFlag); +#endif } SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const { diff --git a/modules/skparagraph/src/ParagraphImpl.h b/modules/skparagraph/src/ParagraphImpl.h index 51008d3c9b..edef75c3db 100644 --- a/modules/skparagraph/src/ParagraphImpl.h +++ b/modules/skparagraph/src/ParagraphImpl.h @@ -323,6 +323,8 @@ public: #ifdef OHOS_SUPPORT size_t GetMaxLines() const override { return fParagraphStyle.getMaxLines(); } + void setLastAutoSpacingFlag(Cluster::AutoSpacingFlag flag) { fLastAutoSpacingFlag = flag; } + const Cluster::AutoSpacingFlag& getLastAutoSpacingFlag() const { return fLastAutoSpacingFlag; } #endif private: @@ -444,6 +446,8 @@ private: #ifdef OHOS_SUPPORT TextRange fEllipsisRange{EMPTY_RANGE}; std::optional fPaintRegion; + // just for building cluster table, record the last built unicode autospacing flag; + Cluster::AutoSpacingFlag fLastAutoSpacingFlag; #endif }; } // namespace textlayout diff --git a/modules/skparagraph/src/Run.h b/modules/skparagraph/src/Run.h index 2268c0e0b4..1587d5fb19 100644 --- a/modules/skparagraph/src/Run.h +++ b/modules/skparagraph/src/Run.h @@ -388,6 +388,16 @@ void Run::iterateGlyphRangeInTextOrder(const GlyphRange& glyphRange, Visitor vis class Cluster { public: + +#ifdef OHOS_SUPPORT + enum AutoSpacingFlag { + NoFlag = 0, + CJK, + Western, + Copyright + }; +#endif + enum BreakType { None, GraphemeBreak, // calculated for all clusters (UBRK_CHARACTER) @@ -436,11 +446,9 @@ public: bool isHardBreak() const { return fIsHardBreak; } bool isIdeographic() const { return fIsIdeographic; } bool isWordBreak() const { return isWhitespaceBreak() || isHardBreak() || isSoftBreak() || run().isPlaceholder(); } - bool isCJK() const { return fIsCJK; } - bool isCopyright() const { return fIsCopyright; } - bool isWestern() const { return fIsWestern; } #ifdef OHOS_SUPPORT bool isTabulation() const { return fIsTabulation; } + bool needAutoSpacing() const { return fNeedAutoSpacing; } #endif bool isSoftBreak() const; @@ -503,11 +511,9 @@ private: bool fIsIntraWordBreak; bool fIsHardBreak; bool fIsIdeographic; - bool fIsCJK; - bool fIsCopyright; - bool fIsWestern; #ifdef OHOS_SUPPORT bool fIsTabulation; + bool fNeedAutoSpacing; // depend on last cluster flag #endif }; diff --git a/modules/skparagraph/src/TextLine.cpp b/modules/skparagraph/src/TextLine.cpp index 8efa4330b0..c4b61b58c9 100644 --- a/modules/skparagraph/src/TextLine.cpp +++ b/modules/skparagraph/src/TextLine.cpp @@ -526,23 +526,6 @@ void TextLine::format(TextAlign align, SkScalar maxWidth, EllipsisModal ellipsis } } -SkScalar TextLine::calculateSpacing(const Cluster prevCluster, const Cluster curCluster) -{ - if (prevCluster.isWhitespaceBreak() || curCluster.isWhitespaceBreak()) { - return 0; - } - if (prevCluster.isHardBreak() || curCluster.isHardBreak()) { - return 0; - } - if (prevCluster.isCopyright() || curCluster.isCopyright()) { - return prevCluster.getFontSize() / AUTO_SPACING_WIDTH_RATIO; - } - if ((curCluster.isCJK() && prevCluster.isWestern()) || (curCluster.isWestern() && prevCluster.isCJK())) { - return prevCluster.getFontSize() / AUTO_SPACING_WIDTH_RATIO; - } - return 0; -} - #ifdef OHOS_SUPPORT SkScalar TextLine::autoSpacing() { if (!TextParameter::GetAutoSpacingEnable()) { @@ -553,7 +536,7 @@ SkScalar TextLine::autoSpacing() { for (auto clusterIndex = fClusterRange.start + 1; clusterIndex < fClusterRange.end; ++clusterIndex) { auto prevSpacing = spacing; auto& cluster = fOwner->cluster(clusterIndex); - spacing += calculateSpacing(prevCluster, cluster); + spacing += cluster.needAutoSpacing() ? prevCluster.getFontSize() / AUTO_SPACING_WIDTH_RATIO : 0; spacingCluster(&cluster, spacing, prevSpacing); prevCluster = cluster; } diff --git a/modules/skparagraph/src/TextLine.h b/modules/skparagraph/src/TextLine.h index 1d0a4dd492..292e47e260 100644 --- a/modules/skparagraph/src/TextLine.h +++ b/modules/skparagraph/src/TextLine.h @@ -150,7 +150,6 @@ public: const ClustersVisitor& visitor) const; void format(TextAlign align, SkScalar maxWidth, EllipsisModal ellipsisModal); - SkScalar calculateSpacing(const Cluster prevCluster, const Cluster curCluster); SkScalar autoSpacing(); void paint(ParagraphPainter* painter, SkScalar x, SkScalar y); void paint(ParagraphPainter* painter, const RSPath* path, SkScalar hOffset, SkScalar vOffset); diff --git a/modules/skparagraph/src/TextWrapper.cpp b/modules/skparagraph/src/TextWrapper.cpp index ff0e6a9248..e809720910 100644 --- a/modules/skparagraph/src/TextWrapper.cpp +++ b/modules/skparagraph/src/TextWrapper.cpp @@ -56,30 +56,10 @@ struct LineBreakerWithLittleRounding { } // namespace #ifdef OHOS_SUPPORT -SkScalar TextWrapper::calculateFakeSpacing(Cluster* cluster, bool autoSpacingEnable) -{ - if (!autoSpacingEnable || cluster == fEndLine.endCluster()) { - return 0; - } - if ((cluster - 1)->isWhitespaceBreak() || cluster->isWhitespaceBreak()) { - return 0; - } - if ((cluster - 1)->isHardBreak() || cluster->isHardBreak()) { - return 0; - } - if ((cluster - 1)->isCopyright() || cluster->isCopyright()) { - return (cluster - 1)->getFontSize() / AUTO_SPACING_WIDTH_RATIO; - } - if ((cluster->isCJK() && (cluster - 1)->isWestern()) || (cluster->isWestern() && (cluster - 1)->isCJK())) { - return (cluster - 1)->getFontSize() / AUTO_SPACING_WIDTH_RATIO; - } - return 0; -} - // Since we allow cluster clipping when they don't fit // we have to work with stretches - parts of clusters void TextWrapper::lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack, - WordBreakType wordBreakType, bool autoSpacingEnable) { + WordBreakType wordBreakType) { reset(); fEndLine.metrics().clean(); @@ -95,8 +75,8 @@ void TextWrapper::lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool appl Cluster* nextNonBreakingSpace = nullptr; SkScalar totalFakeSpacing = 0.0; for (auto cluster = fEndLine.endCluster(); cluster < endOfClusters; ++cluster) { - auto fakeSpacing = calculateFakeSpacing(cluster, autoSpacingEnable); - totalFakeSpacing += fakeSpacing; + totalFakeSpacing += (cluster->needAutoSpacing() && cluster != fEndLine.endCluster()) ? + (cluster - 1)->getFontSize() / AUTO_SPACING_WIDTH_RATIO : 0; if (cluster->isHardBreak()) { if (cluster != fEndLine.endCluster()) { isFirstWord = false; @@ -746,7 +726,6 @@ void TextWrapper::breakTextIntoLines(ParagraphImpl* parent, auto disableLastDescent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent; bool firstLine = true; // We only interested in fist line if we have to disable the first ascent - bool autoSpacingEnableFlag = TextParameter::GetAutoSpacingEnable(); // Resolve balanced line widths std::vector balancedWidths; @@ -778,8 +757,7 @@ void TextWrapper::breakTextIntoLines(ParagraphImpl* parent, } else { newWidth = maxWidth - parent->detectIndents(fLineNumber - 1); } - this->lookAhead(newWidth, end, parent->getApplyRoundingHack(), parent->getWordBreakType(), - autoSpacingEnableFlag); + this->lookAhead(newWidth, end, parent->getApplyRoundingHack(), parent->getWordBreakType()); auto lastLine = (hasEllipsis && unlimitedLines) || fLineNumber >= maxLines; needEllipsis = hasEllipsis && !endlessLine && lastLine; diff --git a/modules/skparagraph/src/TextWrapper.h b/modules/skparagraph/src/TextWrapper.h index a734136d7f..fbbd8bbd89 100644 --- a/modules/skparagraph/src/TextWrapper.h +++ b/modules/skparagraph/src/TextWrapper.h @@ -228,15 +228,18 @@ private: fHardLineBreak = false; } - SkScalar calculateFakeSpacing(Cluster* cluster, bool autoSpacingEnable); - void lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack, WordBreakType wordBreakType, - bool autoSpacingEnable); +#ifdef OHOS_SUPPORT + void lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack, WordBreakType wordBreakType); void moveForward(bool hasEllipsis, bool breakAll); // breakAll = true, break occurs after each character + uint64_t CalculateBestScore(std::vector& widthOut, + SkScalar maxWidth, ParagraphImpl* parent, size_t maxLines); +#else + void lookAhead(SkScalar maxWidth, Cluster* endOfClusters, bool applyRoundingHack); + void moveForward(bool hasEllipsis); +#endif void trimEndSpaces(TextAlign align); std::tuple trimStartSpaces(Cluster* endOfClusters); SkScalar getClustersTrimmedWidth(); - uint64_t CalculateBestScore(std::vector& widthOut, - SkScalar maxWidth, ParagraphImpl* parent, size_t maxLines); }; } // namespace textlayout } // namespace skia -- Gitee