已合并
Add lineHeight and lineSpacing ability #2040
FTL1ght创建于 2025年8月25日
Add lineHeight and lineSpacing ability #2040
已合并
从refs/pull/2040/head合入到master
共 18 个文件变更+428-55
| @@ -153,18 +153,19 @@ struct ParagraphStyle { | |||
| 153 | 153 | ||
| 154 | bool operator==(const ParagraphStyle& rhs) const { | 154 | bool operator==(const ParagraphStyle& rhs) const { |
| 155 | 155 | ||
| 156 | - return this->fHeight == rhs.fHeight && this->fEllipsis == rhs.fEllipsis && | 156 | + return nearlyEqual(this->fHeight, rhs.fHeight) && this->fEllipsis == rhs.fEllipsis && |
| 157 | this->fEllipsisUtf16 == rhs.fEllipsisUtf16 && | 157 | this->fEllipsisUtf16 == rhs.fEllipsisUtf16 && |
| 158 | this->fTextDirection == rhs.fTextDirection && this->fTextAlign == rhs.fTextAlign && | 158 | this->fTextDirection == rhs.fTextDirection && this->fTextAlign == rhs.fTextAlign && |
| 159 | this->fDefaultTextStyle == rhs.fDefaultTextStyle && | 159 | this->fDefaultTextStyle == rhs.fDefaultTextStyle && |
| 160 | this->fEllipsisModal == rhs.fEllipsisModal && | 160 | this->fEllipsisModal == rhs.fEllipsisModal && |
| 161 | this->fTextOverflower == rhs.fTextOverflower && | 161 | this->fTextOverflower == rhs.fTextOverflower && |
| 162 | this->fReplaceTabCharacters == rhs.fReplaceTabCharacters && | 162 | this->fReplaceTabCharacters == rhs.fReplaceTabCharacters && |
| 163 | - this->fTextTab == rhs.fTextTab && this->fParagraphSpacing == rhs.fParagraphSpacing && | 163 | + this->fTextTab == rhs.fTextTab && nearlyEqual(this->fParagraphSpacing, rhs.fParagraphSpacing) && |
| 164 | this->fIsEndAddParagraphSpacing == rhs.fIsEndAddParagraphSpacing && | 164 | this->fIsEndAddParagraphSpacing == rhs.fIsEndAddParagraphSpacing && |
| 165 | this->fIsTrailingSpaceOptimized == rhs.fIsTrailingSpaceOptimized && | 165 | this->fIsTrailingSpaceOptimized == rhs.fIsTrailingSpaceOptimized && |
| 166 | this->fEnableAutoSpace == rhs.fEnableAutoSpace && | 166 | this->fEnableAutoSpace == rhs.fEnableAutoSpace && |
| 167 | this->fVerticalAlignment == rhs.fVerticalAlignment && | 167 | this->fVerticalAlignment == rhs.fVerticalAlignment && |
| 168 | + nearlyEqual(this->fLineSpacing, rhs.fLineSpacing) && | ||
| 168 | nearlyEqual(this->fTextSplitRatio, rhs.fTextSplitRatio); | 169 | nearlyEqual(this->fTextSplitRatio, rhs.fTextSplitRatio); |
| 169 | 170 | ||
| 170 | return this->fHeight == rhs.fHeight && | 171 | return this->fHeight == rhs.fHeight && |
| @@ -249,6 +250,8 @@ struct ParagraphStyle { | |||
| 249 | { | 250 | { |
| 250 | fEnableAutoSpace = enableAutoSpace; | 251 | fEnableAutoSpace = enableAutoSpace; |
| 251 | } | 252 | } |
| 253 | + SkScalar getLineSpacing() const { return fLineSpacing; } | ||
| 254 | + void setLineSpacing(SkScalar lineSpacing) { fLineSpacing = lineSpacing; } | ||
| 252 | 255 | ||
| 253 | bool getApplyRoundingHack() const { return fApplyRoundingHack; } | 256 | bool getApplyRoundingHack() const { return fApplyRoundingHack; } |
| 254 | void setApplyRoundingHack(bool value) { fApplyRoundingHack = value; } | 257 | void setApplyRoundingHack(bool value) { fApplyRoundingHack = value; } |
| @@ -276,6 +279,7 @@ private: | |||
| 276 | bool fIsTrailingSpaceOptimized{false}; | 279 | bool fIsTrailingSpaceOptimized{false}; |
| 277 | bool fEnableAutoSpace{false}; | 280 | bool fEnableAutoSpace{false}; |
| 278 | TextVerticalAlign fVerticalAlignment{TextVerticalAlign::BASELINE}; | 281 | TextVerticalAlign fVerticalAlignment{TextVerticalAlign::BASELINE}; |
| 282 | + SkScalar fLineSpacing{0.0f}; | ||
| 279 | 283 | ||
| 280 | }; | 284 | }; |
| 281 | } // namespace textlayout | 285 | } // namespace textlayout |
| @@ -51,6 +51,13 @@ static inline bool nearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance = SK_S | |||
| 51 | return x == y; | 51 | return x == y; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | + | ||
| 55 | +enum class LineHeightStyle { | ||
| 56 | + kFontSize, | ||
| 57 | + kFontHeight | ||
| 58 | +}; | ||
| 59 | + | ||
| 60 | + | ||
| 54 | // Multiple decorations can be applied at once. Ex: Underline and overline is | 61 | // Multiple decorations can be applied at once. Ex: Underline and overline is |
| 55 | // (0x1 | 0x2) | 62 | // (0x1 | 0x2) |
| 56 | enum TextDecoration { | 63 | enum TextDecoration { |
| @@ -383,6 +390,24 @@ public: | |||
| 383 | SkScalar getBadgeBaseLineShift() const; | 390 | SkScalar getBadgeBaseLineShift() const; |
| 384 | 391 | ||
| 385 | SkScalar getCorrectFontSize() const; | 392 | SkScalar getCorrectFontSize() const; |
| 393 | + | ||
| 394 | + SkScalar getMaxLineHeight() const { return fMaxLineHeight; } | ||
| 395 | + | ||
| 396 | + void setMaxLineHeight(SkScalar maxLineHeight) { fMaxLineHeight = maxLineHeight; } | ||
| 397 | + | ||
| 398 | + SkScalar getMinLineHeight() const { return fMinLineHeight; } | ||
| 399 | + | ||
| 400 | + void setMinLineHeight(SkScalar minLineHeight) { fMinLineHeight = minLineHeight; } | ||
| 401 | + | ||
| 402 | + LineHeightStyle getLineHeightStyle() const { return fLineHeightStyle; } | ||
| 403 | + | ||
| 404 | + void setLineHeightStyle(LineHeightStyle lineHeightStyle) { fLineHeightStyle = lineHeightStyle; } | ||
| 405 | + | ||
| 406 | + bool equalsByTextShadow(const TextStyle& other) const; | ||
| 407 | + | ||
| 408 | + bool equalsByFontFeatures(const TextStyle& other) const; | ||
| 409 | + | ||
| 410 | + bool equalsByShape(const TextStyle& other) const; | ||
| 386 | 411 | ||
| 387 | private: | 412 | private: |
| 388 | static const std::vector<SkString>* kDefaultFontFamilies; | 413 | static const std::vector<SkString>* kDefaultFontFamilies; |
| @@ -424,6 +449,9 @@ private: | |||
| 424 | SkColor fStyleId = {0}; | 449 | SkColor fStyleId = {0}; |
| 425 | size_t fTextStyleUid{0}; | 450 | size_t fTextStyleUid{0}; |
| 426 | SkScalar fVerticalAlignShift{0.0f}; | 451 | SkScalar fVerticalAlignShift{0.0f}; |
| 452 | + SkScalar fMaxLineHeight{std::numeric_limits<float>::max()}; | ||
| 453 | + SkScalar fMinLineHeight{0.0f}; | ||
| 454 | + LineHeightStyle fLineHeightStyle{LineHeightStyle::kFontSize}; | ||
| 427 | 455 | ||
| 428 | 456 | ||
| 429 | TextBaseline fTextBaseline = TextBaseline::kAlphabetic; | 457 | TextBaseline fTextBaseline = TextBaseline::kAlphabetic; |
| @@ -153,6 +153,7 @@ void ParagraphCacheKey::computeHashMix(uint32_t& hash) const { | |||
| 153 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getIsEndAddParagraphSpacing())); | 153 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getIsEndAddParagraphSpacing())); |
| 154 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getEnableAutoSpace())); | 154 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getEnableAutoSpace())); |
| 155 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getVerticalAlignment())); | 155 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getVerticalAlignment())); |
| 156 | + hash = mix(hash, SkGoodHash()(relax(fParagraphStyle.getLineSpacing()))); | ||
| 156 | 157 | ||
| 157 | auto& strutStyle = fParagraphStyle.getStrutStyle(); | 158 | auto& strutStyle = fParagraphStyle.getStrutStyle(); |
| 158 | if (strutStyle.getStrutEnabled()) { | 159 | if (strutStyle.getStrutEnabled()) { |
| @@ -211,6 +212,9 @@ uint32_t ParagraphCacheKey::computeHash() const { | |||
| 211 | hash = mix(hash, SkGoodHash()(ts.fRange)); | 212 | hash = mix(hash, SkGoodHash()(ts.fRange)); |
| 212 | hash = mix(hash, SkGoodHash()(ts.fStyle.getTextBadgeType())); | 213 | hash = mix(hash, SkGoodHash()(ts.fStyle.getTextBadgeType())); |
| 213 | hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getTotalVerticalShift()))); | 214 | hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getTotalVerticalShift()))); |
| 215 | + hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getMaxLineHeight()))); | ||
| 216 | + hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getMinLineHeight()))); | ||
| 217 | + hash = mix(hash, SkGoodHash()(ts.fStyle.getLineHeightStyle())); | ||
| 214 | } | 218 | } |
| 215 | 219 | ||
| 216 | computeHashMix(hash); | 220 | computeHashMix(hash); |
| @@ -1277,6 +1277,9 @@ void ParagraphImpl::positionShapedTextIntoLine(SkScalar maxWidth) { | |||
| 1277 | } while (trailingSpaces != 0); | 1277 | } while (trailingSpaces != 0); |
| 1278 | 1278 | ||
| 1279 | advance.fY = metrics.height(); | 1279 | advance.fY = metrics.height(); |
| 1280 | + if (this->paragraphStyle().getLineSpacing() > 0 && !disableLastDescent) { | ||
| 1281 | + advance.fY += this->paragraphStyle().getLineSpacing(); | ||
| 1282 | + } | ||
| 1280 | SkScalar heightWithParagraphSpacing = advance.fY; | 1283 | SkScalar heightWithParagraphSpacing = advance.fY; |
paragraph 才有 cloneSelf ![]() ![]() clone是不是忘记加了 ![]() ![]() | |||
| 1281 | if (this->paragraphStyle().getIsEndAddParagraphSpacing() && | 1284 | if (this->paragraphStyle().getIsEndAddParagraphSpacing() && |
| 1282 | this->paragraphStyle().getParagraphSpacing() > 0) { | 1285 | this->paragraphStyle().getParagraphSpacing() > 0) { |
| @@ -200,6 +200,7 @@ Run::Run(ParagraphImpl* owner, | |||
| 200 | metricsIncludeFontPadding(&fFontMetrics, decompressFont); | 200 | metricsIncludeFontPadding(&fFontMetrics, decompressFont); |
| 201 | auto config = findCompressionConfigWithFont(decompressFont); | 201 | auto config = findCompressionConfigWithFont(decompressFont); |
| 202 | fCompressionBaselineShift = (fFontMetrics.fDescent - fFontMetrics.fAscent) * config.baselineShiftScale; | 202 | fCompressionBaselineShift = (fFontMetrics.fDescent - fFontMetrics.fAscent) * config.baselineShiftScale; |
| 203 | + initLimitHeightParam(); | ||
| 203 | 204 | ||
| 204 | 205 | ||
| 205 | this->calculateMetrics(); | 206 | this->calculateMetrics(); |
| @@ -222,21 +223,65 @@ Run::Run(ParagraphImpl* owner, | |||
| 222 | fPlaceholderIndex = std::numeric_limits<size_t>::max(); | 223 | fPlaceholderIndex = std::numeric_limits<size_t>::max(); |
| 223 | } | 224 | } |
| 224 | 225 | ||
| 226 | + | ||
| 227 | +void Run::initLimitHeightParam() { | ||
| 228 | + if (fOwner == nullptr) { | ||
| 229 | + return; | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + BlockRange blockRange = fOwner->findAllBlocks(fTextRange); | ||
| 233 | + const SkSpan<Block>& blocks = fOwner->blocks(blockRange); | ||
| 234 | + // Preprocessing has been completed, and within the same run, | ||
| 235 | + // all textstyle's minLineHeight, maxLineHeight, and lineHeightStyle are equal. | ||
| 236 | + if (!blocks.empty()) { | ||
| 237 | + const TextStyle& style = blocks.front().fStyle; | ||
| 238 | + setLineHeightStyle(style.getLineHeightStyle()); | ||
| 239 | + style.getMaxLineHeight() > 0 ? | ||
| 240 | + setMaxLineHeight(style.getMaxLineHeight()) : setMaxLineHeight(std::numeric_limits<float>::max()); | ||
| 241 | + setMinLineHeight(style.getMinLineHeight()); | ||
| 242 | + } | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | +void Run::calculateMetricsWithoutHeightScale() { | ||
| 246 | + if (nearlyZero(fFont.GetSize())) { | ||
| 247 | + return; | ||
| 248 | + } | ||
| 249 | + SkScalar originRunHeight = fCorrectDescent - fCorrectAscent; | ||
| 250 | + SkScalar runHeight = std::min(std::max(originRunHeight, getMinLineHeight()), getMaxLineHeight()); | ||
| 251 | + const auto multiplier = nearlyZero(originRunHeight) ? 0 : runHeight / originRunHeight; | ||
| 252 | + fCorrectAscent *= multiplier; | ||
| 253 | + fCorrectDescent *= multiplier; | ||
| 254 | +} | ||
| 255 | + | ||
| 256 | + | ||
| 225 | void Run::calculateMetrics() { | 257 | void Run::calculateMetrics() { |
| 226 | fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5; | 258 | fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5; |
| 227 | fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5; | 259 | fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5; |
| 228 | fCorrectLeading = 0; | 260 | fCorrectLeading = 0; |
| 261 | + | ||
| 262 | + if (SkScalarNearlyZero(fHeightMultiplier)) { | ||
| 263 | + calculateMetricsWithoutHeightScale(); | ||
| 264 | + return; | ||
| 265 | + } | ||
| 266 | + auto decompressFont = fFont; | ||
| 267 | + scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS); | ||
| 268 | + const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent; | ||
| 269 | + if (nearlyZero(fontIntrinsicHeight)) { | ||
| 270 | + return; | ||
| 271 | + } | ||
| 272 | + SkScalar runHeight = fLineHeightStyle == LineHeightStyle::kFontSize ? | ||
| 273 | + fHeightMultiplier * decompressFont.GetSize() : fHeightMultiplier * fontIntrinsicHeight; | ||
| 274 | + // If the maxLineHeight is less than 0, it is meaningless and the it is set to max of type float | ||
特殊处理加个注释 ![]() ![]() | |||
| 275 | + if (runHeight >= 0) { | ||
| 276 | + runHeight = std::min(std::max(runHeight, getMinLineHeight()), getMaxLineHeight()); | ||
| 277 | + } | ||
| 278 | + | ||
| 229 | if (SkScalarNearlyZero(fHeightMultiplier)) { | 279 | if (SkScalarNearlyZero(fHeightMultiplier)) { |
| 230 | return; | 280 | return; |
| 231 | } | 281 | } |
| 232 | - | ||
| 233 | - auto decompressFont = fFont; | ||
| 234 | - scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS); | ||
| 235 | - const auto runHeight = fHeightMultiplier * decompressFont.GetSize(); | ||
| 236 | - | ||
| 237 | const auto runHeight = fHeightMultiplier * fFont.getSize(); | 282 | const auto runHeight = fHeightMultiplier * fFont.getSize(); |
| 238 | - | ||
| 239 | const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent; | 283 | const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent; |
| 284 | + | ||
| 240 | if (fUseHalfLeading) { | 285 | if (fUseHalfLeading) { |
| 241 | const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2; | 286 | const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2; |
| 242 | fCorrectAscent -= extraLeading; | 287 | fCorrectAscent -= extraLeading; |
| @@ -149,6 +149,8 @@ public: | |||
| 149 | size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; } | 149 | size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; } |
| 150 | 150 | ||
| 151 | size_t globalClusterIndex(size_t pos) const; | 151 | size_t globalClusterIndex(size_t pos) const; |
| 152 | + void initLimitHeightParam(); | ||
| 153 | + void calculateMetricsWithoutHeightScale(); | ||
| 152 | 154 | ||
| 153 | size_t globalClusterIndex(size_t pos) const { return fClusterStart + fClusterIndexes[pos]; } | 155 | size_t globalClusterIndex(size_t pos) const { return fClusterStart + fClusterIndexes[pos]; } |
| 154 | 156 | ||
| @@ -268,6 +270,12 @@ public: | |||
| 268 | void setIndexInLine(size_t index) { indexInLine = index; } | 270 | void setIndexInLine(size_t index) { indexInLine = index; } |
| 269 | SkScalar getVerticalAlignShift() const { return fVerticalAlignShift; } | 271 | SkScalar getVerticalAlignShift() const { return fVerticalAlignShift; } |
| 270 | void setVerticalAlignShift(SkScalar verticalAlignShift) { fVerticalAlignShift = verticalAlignShift; } | 272 | void setVerticalAlignShift(SkScalar verticalAlignShift) { fVerticalAlignShift = verticalAlignShift; } |
| 273 | + SkScalar getMaxLineHeight() const { return fMaxLineHeight; } | ||
| 274 | + void setMaxLineHeight(SkScalar maxLineHeight) { fMaxLineHeight = maxLineHeight; } | ||
| 275 | + SkScalar getMinLineHeight() const { return fMinLineHeight; } | ||
| 276 | + void setMinLineHeight(SkScalar minLineHeight) { fMinLineHeight = minLineHeight; } | ||
| 277 | + LineHeightStyle getLineHeightStyle() const { return fLineHeightStyle; } | ||
| 278 | + void setLineHeightStyle(LineHeightStyle lineHeightStyle) { fLineHeightStyle = lineHeightStyle; } | ||
| 271 | 279 | ||
| 272 | private: | 280 | private: |
| 273 | friend class ParagraphImpl; | 281 | friend class ParagraphImpl; |
| @@ -336,6 +344,9 @@ private: | |||
| 336 | size_t indexInLine; | 344 | size_t indexInLine; |
| 337 | SkScalar fCompressionBaselineShift{0.0f}; | 345 | SkScalar fCompressionBaselineShift{0.0f}; |
| 338 | SkScalar fVerticalAlignShift{0.0f}; | 346 | SkScalar fVerticalAlignShift{0.0f}; |
| 347 | + SkScalar fMaxLineHeight{std::numeric_limits<float>::max()}; | ||
| 348 | + SkScalar fMinLineHeight{0.0f}; | ||
| 349 | + LineHeightStyle fLineHeightStyle{LineHeightStyle::kFontSize}; | ||
| 339 | 350 | ||
| 340 | }; | 351 | }; |
| 341 | 352 | ||
| @@ -56,6 +56,68 @@ TextStyle TextStyle::cloneForPlaceholder() { | |||
| 56 | return result; | 56 | return result; |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | + | ||
| 60 | +bool TextStyle::equalsByTextShadow(const TextStyle& other) const { | ||
| 61 | + if (fTextShadows.size() != other.fTextShadows.size()) { | ||
| 62 | + return false; | ||
| 63 | + } | ||
| 64 | + for (size_t i = 0; i < fTextShadows.size(); ++i) { | ||
| 65 | + if (fTextShadows[i] != other.fTextShadows[i]) { | ||
fTextShadows和下面的fFontFeatures均是vector,equal判断是否需要包序?比如:featuresA为 [{A,1},{B,1}]与featuresB为[{B,1},{A,1}],两个应用返回true还是false ![]() ![]()
fTextShadows vector 内元素顺序决定了绘制顺序, features 顺序不影响效果 此处只是移动代码, 未修改原始逻辑, 建议后续统一整改 ![]() ![]() | |||
| 66 | + return false; | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + return true; | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +bool TextStyle::equalsByFontFeatures(const TextStyle& other) const { | ||
| 73 | + if (fFontFeatures.size() != other.fFontFeatures.size()) { | ||
| 74 | + return false; | ||
| 75 | + } | ||
| 76 | + for (size_t i = 0; i < fFontFeatures.size(); ++i) { | ||
| 77 | + if (!(fFontFeatures[i] == other.fFontFeatures[i])) { | ||
fFontFeatures[i] != other.fFontFeatures[i] ![]() ![]()
fFontFeatures[i] 是结构体,没有重载 != ![]() ![]() | |||
| 78 | + return false; | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + return true; | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +bool TextStyle::equalsByShape(const TextStyle& other) const { | ||
| 85 | + return fFontStyle == other.fFontStyle && | ||
| 86 | + fLocale == other.fLocale && | ||
| 87 | + fFontFamilies == other.fFontFamilies && | ||
| 88 | + getCorrectFontSize() == other.getCorrectFontSize() && | ||
| 89 | + fHeightOverride == other.fHeightOverride && | ||
| 90 | + fHeight == other.fHeight && | ||
| 91 | + fHalfLeading == other.fHalfLeading && | ||
| 92 | + nearlyEqual(getTotalVerticalShift(), other.getTotalVerticalShift()) && | ||
| 93 | + fFontArguments == other.fFontArguments && | ||
| 94 | + fStyleId == other.fStyleId && | ||
| 95 | + fBackgroundRect == other.fBackgroundRect && | ||
| 96 | + nearlyEqual(fBaselineShift, other.fBaselineShift) && | ||
| 97 | + nearlyEqual(fMaxLineHeight, other.fMaxLineHeight) && | ||
| 98 | + nearlyEqual(fMinLineHeight, other.fMinLineHeight) && | ||
| 99 | + fLineHeightStyle == other.fLineHeightStyle && | ||
| 100 | + fBadgeType == other.fBadgeType; | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +bool TextStyle::equals(const TextStyle& other) const { | ||
| 104 | + if (fIsPlaceholder || other.fIsPlaceholder) { | ||
| 105 | + return false; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + return fColor == other.fColor && | ||
| 109 | + fDecoration == other.fDecoration && | ||
| 110 | + nearlyEqual(fLetterSpacing, other.fLetterSpacing) && | ||
| 111 | + nearlyEqual(fWordSpacing, other.fWordSpacing) && | ||
| 112 | + fHasForeground == other.fHasForeground && | ||
| 113 | + fForeground == other.fForeground && | ||
| 114 | + fHasBackground == other.fHasBackground && | ||
| 115 | + fBackground == other.fBackground && | ||
| 116 | + equalsByFontFeatures(other) && | ||
| 117 | + equalsByTextShadow(other) && | ||
| 118 | + equalsByShape(other); | ||
| 119 | +} | ||
| 120 | + | ||
| 59 | bool TextStyle::equals(const TextStyle& other) const { | 121 | bool TextStyle::equals(const TextStyle& other) const { |
| 60 | 122 | ||
| 61 | if (fIsPlaceholder || other.fIsPlaceholder) { | 123 | if (fIsPlaceholder || other.fIsPlaceholder) { |
| @@ -123,13 +185,9 @@ bool TextStyle::equals(const TextStyle& other) const { | |||
| 123 | if (fFontArguments != other.fFontArguments) { | 185 | if (fFontArguments != other.fFontArguments) { |
| 124 | return false; | 186 | return false; |
| 125 | } | 187 | } |
| 126 | - | ||
| 127 | - if (fStyleId != other.fStyleId || fBackgroundRect != other.fBackgroundRect) { | ||
| 128 | - return false; | ||
| 129 | - } | ||
| 130 | - | ||
| 131 | return true; | 188 | return true; |
| 132 | } | 189 | } |
| 190 | + | ||
| 133 | 191 | ||
| 134 | bool TextStyle::equalsByFonts(const TextStyle& that) const { | 192 | bool TextStyle::equalsByFonts(const TextStyle& that) const { |
| 135 | 193 | ||
| @@ -152,6 +210,33 @@ bool TextStyle::equalsByFonts(const TextStyle& that) const { | |||
| 152 | 210 | ||
| 153 | } | 211 | } |
| 154 | 212 | ||
| 213 | + | ||
| 214 | +bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) const { | ||
| 215 | + switch (styleType) { | ||
| 216 | + case kForeground: | ||
| 217 | + return (!fHasForeground && !other.fHasForeground && fColor == other.fColor) || | ||
| 218 | + (fHasForeground && other.fHasForeground && fForeground == other.fForeground); | ||
(后面的空格去掉 ![]() ![]()
原生代码 ![]() ![]() | |||
| 219 | + case kBackground: | ||
| 220 | + return (!fHasBackground && !other.fHasBackground) || | ||
| 221 | + (fHasBackground && other.fHasBackground && fBackground == other.fBackground); | ||
(后面的空格去掉 ![]() ![]() | |||
| 222 | + case kShadow: | ||
| 223 | + return equalsByTextShadow(other); | ||
| 224 | + case kDecorations: | ||
| 225 | + return this->fDecoration == other.fDecoration; | ||
| 226 | + case kLetterSpacing: | ||
| 227 | + return fLetterSpacing == other.fLetterSpacing; | ||
| 228 | + case kWordSpacing: | ||
| 229 | + return fWordSpacing == other.fWordSpacing; | ||
| 230 | + case kAllAttributes: | ||
| 231 | + return this->equals(other); | ||
| 232 | + case kFont: | ||
| 233 | + return equalsByShape(other); | ||
| 234 | + default: | ||
| 235 | + SkASSERT(false); | ||
| 236 | + return false; | ||
| 237 | + } | ||
| 238 | +} | ||
| 239 | + | ||
| 155 | bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) const { | 240 | bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) const { |
| 156 | switch (styleType) { | 241 | switch (styleType) { |
| 157 | case kForeground: | 242 | case kForeground: |
| @@ -191,26 +276,17 @@ bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) c | |||
| 191 | return fFontStyle == other.fFontStyle && | 276 | return fFontStyle == other.fFontStyle && |
| 192 | fLocale == other.fLocale && | 277 | fLocale == other.fLocale && |
| 193 | fFontFamilies == other.fFontFamilies && | 278 | fFontFamilies == other.fFontFamilies && |
| 194 | - | ||
| 195 | - getCorrectFontSize() == other.getCorrectFontSize() && | ||
| 196 | - fHeight == other.fHeight && | ||
| 197 | - fHalfLeading == other.fHalfLeading && | ||
| 198 | - getTotalVerticalShift() == other.getTotalVerticalShift() && | ||
| 199 | - fFontArguments == other.fFontArguments && | ||
| 200 | - fStyleId == other.fStyleId && | ||
| 201 | - fBackgroundRect == other.fBackgroundRect; | ||
| 202 | - | ||
| 203 | fFontSize == other.fFontSize && | 279 | fFontSize == other.fFontSize && |
| 204 | fHeight == other.fHeight && | 280 | fHeight == other.fHeight && |
| 205 | fHalfLeading == other.fHalfLeading && | 281 | fHalfLeading == other.fHalfLeading && |
| 206 | fBaselineShift == other.fBaselineShift && | 282 | fBaselineShift == other.fBaselineShift && |
| 207 | fFontArguments == other.fFontArguments; | 283 | fFontArguments == other.fFontArguments; |
| 208 | - | ||
| 209 | default: | 284 | default: |
| 210 | SkASSERT(false); | 285 | SkASSERT(false); |
| 211 | return false; | 286 | return false; |
| 212 | } | 287 | } |
| 213 | } | 288 | } |
| 289 | + | ||
| 214 | 290 | ||
| 215 | 291 | ||
| 216 | void TextStyle::getFontMetrics(RSFontMetrics* metrics) const { | 292 | void TextStyle::getFontMetrics(RSFontMetrics* metrics) const { |
| @@ -955,6 +955,8 @@ void TextWrapper::layoutLinesSimple(ParagraphImpl* parent, | |||
| 955 | auto disableFirstAscent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent; | 955 | auto disableFirstAscent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent; |
| 956 | auto disableLastDescent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent; | 956 | auto disableLastDescent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent; |
| 957 | bool firstLine = true; // We only interested in fist line if we have to disable the first ascent | 957 | bool firstLine = true; // We only interested in fist line if we have to disable the first ascent |
| 958 | + SkScalar lineSpacing = parent->paragraphStyle().getLineSpacing(); | ||
| 959 | + bool needLineSpacing = lineSpacing > 0; | ||
| 958 | 960 | ||
| 959 | // Resolve balanced line widths | 961 | // Resolve balanced line widths |
| 960 | std::vector<SkScalar> balancedWidths; | 962 | std::vector<SkScalar> balancedWidths; |
| @@ -1050,6 +1052,7 @@ void TextWrapper::layoutLinesSimple(ParagraphImpl* parent, | |||
| 1050 | } | 1052 | } |
| 1051 | if (disableLastDescent && (lastLine || (startLine == end && !fHardLineBreak))) { | 1053 | if (disableLastDescent && (lastLine || (startLine == end && !fHardLineBreak))) { |
| 1052 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; | 1054 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; |
| 1055 | + needLineSpacing = false; | ||
| 1053 | } | 1056 | } |
| 1054 | 1057 | ||
| 1055 | if (parent->strutEnabled()) { | 1058 | if (parent->strutEnabled()) { |
| @@ -1058,6 +1061,9 @@ void TextWrapper::layoutLinesSimple(ParagraphImpl* parent, | |||
| 1058 | } | 1061 | } |
| 1059 | 1062 | ||
| 1060 | SkScalar lineHeight = fEndLine.metrics().height(); | 1063 | SkScalar lineHeight = fEndLine.metrics().height(); |
| 1064 | + if (needLineSpacing) { | ||
| 1065 | + lineHeight += lineSpacing; | ||
| 1066 | + } | ||
| 1061 | if (fHardLineBreak && !lastLine && parent->paragraphStyle().getParagraphSpacing() > 0) { | 1067 | if (fHardLineBreak && !lastLine && parent->paragraphStyle().getParagraphSpacing() > 0) { |
| 1062 | lineHeight += parent->paragraphStyle().getParagraphSpacing(); | 1068 | lineHeight += parent->paragraphStyle().getParagraphSpacing(); |
| 1063 | } | 1069 | } |
| @@ -1494,7 +1500,9 @@ void TextWrapper::initializeFormattingState(SkScalar maxWidth, const SkSpan<Clus | |||
| 1494 | style.getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent, | 1500 | style.getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent, |
| 1495 | style.getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent, | 1501 | style.getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent, |
| 1496 | style.getMaxLines(), | 1502 | style.getMaxLines(), |
| 1497 | - style.effective_align()}; | 1503 | + style.effective_align(), |
| 1504 | + fParent->paragraphStyle().getLineSpacing() > 0, | ||
| 1505 | + fParent->paragraphStyle().getLineSpacing()}; | ||
| 1498 | 1506 | ||
| 1499 | fFirstLine = true; | 1507 | fFirstLine = true; |
| 1500 | fSoftLineMaxIntrinsicWidth = 0; | 1508 | fSoftLineMaxIntrinsicWidth = 0; |
| @@ -1517,14 +1525,13 @@ void TextWrapper::processLineStretches(SkScalar maxWidth, const AddLineToParagra | |||
| 1517 | } | 1525 | } |
| 1518 | 1526 | ||
| 1519 | void TextWrapper::finalizeTextLayout(const AddLineToParagraph& addLine) { | 1527 | void TextWrapper::finalizeTextLayout(const AddLineToParagraph& addLine) { |
| 1520 | - processRemainingClusters(); | ||
| 1521 | - addFinalLineBreakIfNeeded(addLine); | ||
| 1522 | - adjustFirstLastLineMetrics(); | ||
| 1523 | - | ||
| 1524 | if (fParent->paragraphStyle().getIsEndAddParagraphSpacing() && | 1528 | if (fParent->paragraphStyle().getIsEndAddParagraphSpacing() && |
| 1525 | fParent->paragraphStyle().getParagraphSpacing() > 0) { | 1529 | fParent->paragraphStyle().getParagraphSpacing() > 0) { |
| 1526 | fHeight += fParent->paragraphStyle().getParagraphSpacing(); | 1530 | fHeight += fParent->paragraphStyle().getParagraphSpacing(); |
| 1527 | } | 1531 | } |
| 1532 | + processRemainingClusters(); | ||
| 1533 | + addFinalLineBreakIfNeeded(addLine); | ||
| 1534 | + adjustFirstLastLineMetrics(); | ||
| 1528 | } | 1535 | } |
| 1529 | 1536 | ||
| 1530 | void TextWrapper::prepareLineForFormatting(TextStretch& line) { | 1537 | void TextWrapper::prepareLineForFormatting(TextStretch& line) { |
| @@ -1541,10 +1548,10 @@ void TextWrapper::formatCurrentLine(const AddLineToParagraph& addLine) { | |||
| 1541 | } | 1548 | } |
| 1542 | 1549 | ||
| 1543 | bool TextWrapper::determineIfEllipsisNeeded() { | 1550 | bool TextWrapper::determineIfEllipsisNeeded() { |
| 1544 | - bool lastLine = (fFormattingContext.hasEllipsis && fFormattingContext.unlimitedLines) || | 1551 | + fIsLastLine = (fFormattingContext.hasEllipsis && fFormattingContext.unlimitedLines) || |
| 1545 | fLineNumber >= fFormattingContext.maxLines; | 1552 | fLineNumber >= fFormattingContext.maxLines; |
| 1546 | bool needEllipsis = | 1553 | bool needEllipsis = |
| 1547 | - fFormattingContext.hasEllipsis && !fFormattingContext.endlessLine && lastLine; | 1554 | + fFormattingContext.hasEllipsis && !fFormattingContext.endlessLine && fIsLastLine; |
| 1548 | 1555 | ||
| 1549 | if (fParent->paragraphStyle().getEllipsisMod() == EllipsisModal::HEAD && | 1556 | if (fParent->paragraphStyle().getEllipsisMod() == EllipsisModal::HEAD && |
| 1550 | fFormattingContext.hasEllipsis) { | 1557 | fFormattingContext.hasEllipsis) { |
| @@ -1593,8 +1600,9 @@ void TextWrapper::adjustLineMetricsForFirstLastLine() { | |||
| 1593 | if (fFormattingContext.disableFirstAscent && fFirstLine) { | 1600 | if (fFormattingContext.disableFirstAscent && fFirstLine) { |
| 1594 | fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; | 1601 | fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; |
| 1595 | } | 1602 | } |
| 1596 | - if (fFormattingContext.disableLastDescent && isLastLine()) { | 1603 | + if (fFormattingContext.disableLastDescent && (fIsLastLine || (fCurrentStartLine == fEnd && !fHardLineBreak))) { |
||(之间加空格 ![]() ![]() | |||
| 1597 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; | 1604 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; |
| 1605 | + fFormattingContext.needLineSpacing = false; | ||
| 1598 | } | 1606 | } |
| 1599 | } | 1607 | } |
| 1600 | 1608 | ||
| @@ -1639,6 +1647,9 @@ TextWrapper::LineTextRanges TextWrapper::calculateLineTextRanges() { | |||
| 1639 | 1647 | ||
| 1640 | SkScalar TextWrapper::calculateLineHeight() { | 1648 | SkScalar TextWrapper::calculateLineHeight() { |
| 1641 | SkScalar height = fEndLine.metrics().height(); | 1649 | SkScalar height = fEndLine.metrics().height(); |
| 1650 | + if (fFormattingContext.needLineSpacing) { | ||
| 1651 | + height += fFormattingContext.lineSpacing; | ||
| 1652 | + } | ||
| 1642 | if (fHardLineBreak && !isLastLine() && fParent->paragraphStyle().getParagraphSpacing() > 0) { | 1653 | if (fHardLineBreak && !isLastLine() && fParent->paragraphStyle().getParagraphSpacing() > 0) { |
| 1643 | height += fParent->paragraphStyle().getParagraphSpacing(); | 1654 | height += fParent->paragraphStyle().getParagraphSpacing(); |
| 1644 | } | 1655 | } |
| @@ -1768,7 +1779,7 @@ void TextWrapper::adjustMetricsForEmptyParagraph() { | |||
| 1768 | if (fFormattingContext.disableFirstAscent) { | 1779 | if (fFormattingContext.disableFirstAscent) { |
| 1769 | fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; | 1780 | fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; |
| 1770 | } | 1781 | } |
| 1771 | - if (fFormattingContext.disableLastDescent && !fHardLineBreak) { | 1782 | + if (fFormattingContext.disableLastDescent && (fIsLastLine || (fEndLine.endCluster() == fEnd && !fHardLineBreak))) { |
| 1772 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; | 1783 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; |
| 1773 | } | 1784 | } |
| 1774 | } | 1785 | } |
| @@ -265,6 +265,8 @@ private: | |||
| 265 | bool disableLastDescent{false}; | 265 | bool disableLastDescent{false}; |
| 266 | size_t maxLines{0}; | 266 | size_t maxLines{0}; |
| 267 | TextAlign align{TextAlign::kLeft}; | 267 | TextAlign align{TextAlign::kLeft}; |
| 268 | + bool needLineSpacing{false}; | ||
| 269 | + SkScalar lineSpacing{0.0f}; | ||
| 268 | }; | 270 | }; |
| 269 | 271 | ||
| 270 | struct LineTextRanges { | 272 | struct LineTextRanges { |
| @@ -407,6 +409,7 @@ private: | |||
| 407 | size_t fCurrentStartPos{0}; | 409 | size_t fCurrentStartPos{0}; |
| 408 | Cluster* fStart{nullptr}; | 410 | Cluster* fStart{nullptr}; |
| 409 | Cluster* fEnd{nullptr}; | 411 | Cluster* fEnd{nullptr}; |
| 412 | + bool fIsLastLine{false}; | ||
| 410 | 413 | ||
| 411 | }; | 414 | }; |
| 412 | } // namespace textlayout | 415 | } // namespace textlayout |
| @@ -152,6 +152,7 @@ struct ParagraphStyle { | |||
| 152 | this->fIsTrailingSpaceOptimized == rhs.fIsTrailingSpaceOptimized && | 152 | this->fIsTrailingSpaceOptimized == rhs.fIsTrailingSpaceOptimized && |
| 153 | this->fEnableAutoSpace == rhs.fEnableAutoSpace && | 153 | this->fEnableAutoSpace == rhs.fEnableAutoSpace && |
| 154 | this->fVerticalAlignment == rhs.fVerticalAlignment && | 154 | this->fVerticalAlignment == rhs.fVerticalAlignment && |
| 155 | + this->fLineSpacing == rhs.fLineSpacing && | ||
| 155 | 156 | ||
| 156 | nearlyEqual(this->fTextSplitRatio, rhs.fTextSplitRatio); | 157 | nearlyEqual(this->fTextSplitRatio, rhs.fTextSplitRatio); |
| 157 | } | 158 | } |
| @@ -231,6 +232,8 @@ struct ParagraphStyle { | |||
| 231 | { | 232 | { |
| 232 | fEnableAutoSpace = enableAutoSpace; | 233 | fEnableAutoSpace = enableAutoSpace; |
| 233 | } | 234 | } |
| 235 | + SkScalar getLineSpacing() const { return fLineSpacing; } | ||
| 236 | + void setLineSpacing(SkScalar lineSpacing) { fLineSpacing = lineSpacing; } | ||
| 234 | 237 | ||
| 235 | private: | 238 | private: |
| 236 | StrutStyle fStrutStyle; | 239 | StrutStyle fStrutStyle; |
| @@ -254,6 +257,7 @@ private: | |||
| 254 | bool fIsTrailingSpaceOptimized{false}; | 257 | bool fIsTrailingSpaceOptimized{false}; |
| 255 | bool fEnableAutoSpace{false}; | 258 | bool fEnableAutoSpace{false}; |
| 256 | TextVerticalAlign fVerticalAlignment{TextVerticalAlign::BASELINE}; | 259 | TextVerticalAlign fVerticalAlignment{TextVerticalAlign::BASELINE}; |
| 260 | + SkScalar fLineSpacing{0.0f}; | ||
| 257 | 261 | ||
| 258 | }; | 262 | }; |
| 259 | } // namespace textlayout | 263 | } // namespace textlayout |
| @@ -48,6 +48,13 @@ static inline bool nearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance = SK_S | |||
| 48 | return x == y; | 48 | return x == y; |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | + | ||
| 52 | +enum class LineHeightStyle { | ||
| 53 | + kFontSize, | ||
| 54 | + kFontHeight | ||
| 55 | +}; | ||
| 56 | + | ||
| 57 | + | ||
| 51 | // Multiple decorations can be applied at once. Ex: Underline and overline is | 58 | // Multiple decorations can be applied at once. Ex: Underline and overline is |
| 52 | // (0x1 | 0x2) | 59 | // (0x1 | 0x2) |
| 53 | enum TextDecoration { | 60 | enum TextDecoration { |
| @@ -385,6 +392,24 @@ public: | |||
| 385 | SkScalar getBadgeBaseLineShift() const; | 392 | SkScalar getBadgeBaseLineShift() const; |
| 386 | 393 | ||
| 387 | SkScalar getCorrectFontSize() const; | 394 | SkScalar getCorrectFontSize() const; |
| 395 | + | ||
| 396 | + SkScalar getMaxLineHeight() const { return fMaxLineHeight; } | ||
| 397 | + | ||
| 398 | + void setMaxLineHeight(SkScalar maxLineHeight) { fMaxLineHeight = maxLineHeight; } | ||
| 399 | + | ||
| 400 | + SkScalar getMinLineHeight() const { return fMinLineHeight; } | ||
| 401 | + | ||
| 402 | + void setMinLineHeight(SkScalar minLineHeight) { fMinLineHeight = minLineHeight; } | ||
| 403 | + | ||
| 404 | + LineHeightStyle getLineHeightStyle() const { return fLineHeightStyle; } | ||
| 405 | + | ||
| 406 | + void setLineHeightStyle(LineHeightStyle lineHeightStyle) { fLineHeightStyle = lineHeightStyle; } | ||
| 407 | + | ||
| 408 | + bool equalsByTextShadow(const TextStyle& other) const; | ||
| 409 | + | ||
| 410 | + bool equalsByFontFeatures(const TextStyle& other) const; | ||
| 411 | + | ||
| 412 | + bool equalsByShape(const TextStyle& other) const; | ||
| 388 | 413 | ||
| 389 | 414 | ||
| 390 | private: | 415 | private: |
| @@ -421,6 +446,9 @@ private: | |||
| 421 | 446 | ||
| 422 | size_t fTextStyleUid{0}; | 447 | size_t fTextStyleUid{0}; |
| 423 | SkScalar fVerticalAlignShift{0.0f}; | 448 | SkScalar fVerticalAlignShift{0.0f}; |
| 449 | + SkScalar fMaxLineHeight{std::numeric_limits<float>::max()}; | ||
| 450 | + SkScalar fMinLineHeight{0.0f}; | ||
| 451 | + LineHeightStyle fLineHeightStyle{LineHeightStyle::kFontSize}; | ||
| 424 | 452 | ||
| 425 | SkScalar fLetterSpacing = 0.0; | 453 | SkScalar fLetterSpacing = 0.0; |
| 426 | SkScalar fWordSpacing = 0.0; | 454 | SkScalar fWordSpacing = 0.0; |
| @@ -175,11 +175,15 @@ uint32_t hash = 0; | |||
| 175 | 175 | ||
| 176 | hash = mix(hash, SkGoodHash()(ts.fStyle.getTextBadgeType())); | 176 | hash = mix(hash, SkGoodHash()(ts.fStyle.getTextBadgeType())); |
| 177 | hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getTotalVerticalShift()))); | 177 | hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getTotalVerticalShift()))); |
| 178 | + hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getMaxLineHeight()))); | ||
| 179 | + hash = mix(hash, SkGoodHash()(relax(ts.fStyle.getMinLineHeight()))); | ||
| 180 | + hash = mix(hash, SkGoodHash()(ts.fStyle.getLineHeightStyle())); | ||
| 178 | 181 | ||
| 179 | } | 182 | } |
| 180 | 183 | ||
| 181 | 184 | ||
| 182 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getVerticalAlignment())); | 185 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getVerticalAlignment())); |
| 186 | + hash = mix(hash, SkGoodHash()(relax(fParagraphStyle.getLineSpacing()))); | ||
| 183 | 187 | ||
| 184 | hash = mix(hash, SkGoodHash()(relax(fParagraphStyle.getHeight()))); | 188 | hash = mix(hash, SkGoodHash()(relax(fParagraphStyle.getHeight()))); |
| 185 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getTextDirection())); | 189 | hash = mix(hash, SkGoodHash()(fParagraphStyle.getTextDirection())); |
| @@ -1197,6 +1197,9 @@ void ParagraphImpl::positionShapedTextIntoLine(SkScalar maxWidth) { | |||
| 1197 | } while (trailingSpaces != 0); | 1197 | } while (trailingSpaces != 0); |
| 1198 | 1198 | ||
| 1199 | advance.fY = metrics.height(); | 1199 | advance.fY = metrics.height(); |
| 1200 | + if (this->paragraphStyle().getLineSpacing() > 0 && !disableLastDescent) { | ||
| 1201 | + advance.fY += this->paragraphStyle().getLineSpacing(); | ||
| 1202 | + } | ||
| 1200 | SkScalar heightWithParagraphSpacing = advance.fY; | 1203 | SkScalar heightWithParagraphSpacing = advance.fY; |
| 1201 | if (this->paragraphStyle().getIsEndAddParagraphSpacing() && | 1204 | if (this->paragraphStyle().getIsEndAddParagraphSpacing() && |
| 1202 | this->paragraphStyle().getParagraphSpacing() > 0) { | 1205 | this->paragraphStyle().getParagraphSpacing() > 0) { |
| @@ -248,6 +248,7 @@ Run::Run(ParagraphImpl* owner, | |||
| 248 | metricsIncludeFontPadding(&fFontMetrics, decompressFont); | 248 | metricsIncludeFontPadding(&fFontMetrics, decompressFont); |
| 249 | auto config = findCompressionConfigWithFont(decompressFont); | 249 | auto config = findCompressionConfigWithFont(decompressFont); |
| 250 | fCompressionBaselineShift = (fFontMetrics.fDescent - fFontMetrics.fAscent) * config.baselineShiftScale; | 250 | fCompressionBaselineShift = (fFontMetrics.fDescent - fFontMetrics.fAscent) * config.baselineShiftScale; |
| 251 | + initLimitHeightParam(); | ||
| 251 | 252 | ||
| 252 | 253 | ||
| 253 | this->calculateMetrics(); | 254 | this->calculateMetrics(); |
| @@ -270,6 +271,37 @@ Run::Run(ParagraphImpl* owner, | |||
| 270 | fPlaceholderIndex = std::numeric_limits<size_t>::max(); | 271 | fPlaceholderIndex = std::numeric_limits<size_t>::max(); |
| 271 | } | 272 | } |
| 272 | 273 | ||
| 274 | + | ||
| 275 | +void Run::initLimitHeightParam() { | ||
| 276 | + if (fOwner == nullptr) { | ||
| 277 | + return; | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + BlockRange blockRange = fOwner->findAllBlocks(fTextRange); | ||
| 281 | + const SkSpan<Block>& blocks = fOwner->blocks(blockRange); | ||
| 282 | + // Preprocessing has been completed, and within the same run, | ||
| 283 | + // all textstyle's minLineHeight, maxLineHeight, and lineHeightStyle are equal. | ||
| 284 | + if (!blocks.empty()) { | ||
| 285 | + const TextStyle& style = blocks.front().fStyle; | ||
| 286 | + setLineHeightStyle(style.getLineHeightStyle()); | ||
| 287 | + style.getMaxLineHeight() > 0 ? | ||
| 288 | + setMaxLineHeight(style.getMaxLineHeight()) : setMaxLineHeight(std::numeric_limits<float>::max()); | ||
| 289 | + setMinLineHeight(style.getMinLineHeight()); | ||
| 290 | + } | ||
| 291 | +} | ||
| 292 | + | ||
| 293 | +void Run::calculateMetricsWithoutHeightScale() { | ||
| 294 | + if (nearlyZero(fFont.GetSize())) { | ||
| 295 | + return; | ||
| 296 | + } | ||
| 297 | + SkScalar originRunHeight = fCorrectDescent - fCorrectAscent; | ||
| 298 | + SkScalar runHeight = std::min(std::max(originRunHeight, getMinLineHeight()), getMaxLineHeight()); | ||
| 299 | + const auto multiplier = nearlyZero(originRunHeight) ? 0 : runHeight / originRunHeight; | ||
可能是0吗? ![]() ![]() | |||
| 300 | + fCorrectAscent *= multiplier; | ||
| 301 | + fCorrectDescent *= multiplier; | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | + | ||
| 273 | void Run::calculateMetrics() { | 305 | void Run::calculateMetrics() { |
| 274 | fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5; | 306 | fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5; |
| 275 | fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5; | 307 | fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5; |
| @@ -279,12 +311,24 @@ void Run::calculateMetrics() { | |||
| 279 | } | 311 | } |
| 280 | 312 | ||
| 281 | const auto runHeight = fHeightMultiplier * fFont.getSize(); | 313 | const auto runHeight = fHeightMultiplier * fFont.getSize(); |
| 314 | + const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent; | ||
| 282 | 315 | ||
| 316 | + if (SkScalarNearlyZero(fHeightMultiplier)) { | ||
| 317 | + calculateMetricsWithoutHeightScale(); | ||
| 318 | + return; | ||
| 319 | + } | ||
| 283 | auto decompressFont = fFont; | 320 | auto decompressFont = fFont; |
| 284 | scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS); | 321 | scaleFontWithCompressionConfig(decompressFont, ScaleOP::DECOMPRESS); |
| 285 | - const auto runHeight = fHeightMultiplier * decompressFont.GetSize(); | ||
| 286 | - | ||
| 287 | const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent; | 322 | const auto fontIntrinsicHeight = fCorrectDescent - fCorrectAscent; |
| 323 | + if (nearlyZero(fontIntrinsicHeight)) { | ||
| 324 | + return; | ||
| 325 | + } | ||
| 326 | + SkScalar runHeight = fLineHeightStyle == LineHeightStyle::kFontSize ? | ||
| 327 | + fHeightMultiplier * decompressFont.GetSize() : fHeightMultiplier * fontIntrinsicHeight; | ||
| 328 | + if (runHeight >= 0) { | ||
| 329 | + runHeight = std::min(std::max(runHeight, getMinLineHeight()), getMaxLineHeight()); | ||
| 330 | + } | ||
| 331 | + | ||
| 288 | if (fUseHalfLeading) { | 332 | if (fUseHalfLeading) { |
| 289 | const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2; | 333 | const auto extraLeading = (runHeight - fontIntrinsicHeight) / 2; |
| 290 | fCorrectAscent -= extraLeading; | 334 | fCorrectAscent -= extraLeading; |
| @@ -150,6 +150,8 @@ public: | |||
| 150 | size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; } | 150 | size_t clusterIndex(size_t pos) const { return fClusterIndexes[pos]; } |
| 151 | 151 | ||
| 152 | size_t globalClusterIndex(size_t pos) const; | 152 | size_t globalClusterIndex(size_t pos) const; |
| 153 | + void initLimitHeightParam(); | ||
| 154 | + void calculateMetricsWithoutHeightScale(); | ||
| 153 | 155 | ||
| 154 | size_t globalClusterIndex(size_t pos) const { return fClusterStart + fClusterIndexes[pos]; } | 156 | size_t globalClusterIndex(size_t pos) const { return fClusterStart + fClusterIndexes[pos]; } |
| 155 | 157 | ||
| @@ -218,6 +220,12 @@ public: | |||
| 218 | void iterateGlyphRangeInTextOrder(const GlyphRange& glyphRange, Visitor visitor); | 220 | void iterateGlyphRangeInTextOrder(const GlyphRange& glyphRange, Visitor visitor); |
| 219 | SkScalar getVerticalAlignShift() const { return fVerticalAlignShift; } | 221 | SkScalar getVerticalAlignShift() const { return fVerticalAlignShift; } |
| 220 | void setVerticalAlignShift(SkScalar verticalAlignShift) { fVerticalAlignShift = verticalAlignShift; } | 222 | void setVerticalAlignShift(SkScalar verticalAlignShift) { fVerticalAlignShift = verticalAlignShift; } |
| 223 | + SkScalar getMaxLineHeight() const { return fMaxLineHeight; } | ||
| 224 | + void setMaxLineHeight(SkScalar maxLineHeight) { fMaxLineHeight = maxLineHeight; } | ||
| 225 | + SkScalar getMinLineHeight() const { return fMinLineHeight; } | ||
| 226 | + void setMinLineHeight(SkScalar minLineHeight) { fMinLineHeight = minLineHeight; } | ||
| 227 | + LineHeightStyle getLineHeightStyle() const { return fLineHeightStyle; } | ||
| 228 | + void setLineHeightStyle(LineHeightStyle lineHeightStyle) { fLineHeightStyle = lineHeightStyle; } | ||
| 221 | 229 | ||
| 222 | 230 | ||
| 223 | using ClusterVisitor = std::function<void(Cluster* cluster)>; | 231 | using ClusterVisitor = std::function<void(Cluster* cluster)>; |
| @@ -318,7 +326,7 @@ private: | |||
| 318 | 326 | ||
| 319 | RSFontMetrics fFontMetrics; | 327 | RSFontMetrics fFontMetrics; |
| 320 | 328 | ||
| 321 | - const SkScalar fHeightMultiplier; | 329 | + SkScalar fHeightMultiplier; |
| 322 | const bool fUseHalfLeading; | 330 | const bool fUseHalfLeading; |
| 323 | const SkScalar fBaselineShift; | 331 | const SkScalar fBaselineShift; |
| 324 | SkScalar fCorrectAscent; | 332 | SkScalar fCorrectAscent; |
| @@ -335,6 +343,9 @@ private: | |||
| 335 | 343 | ||
| 336 | SkScalar fCompressionBaselineShift{0.0f}; | 344 | SkScalar fCompressionBaselineShift{0.0f}; |
| 337 | SkScalar fVerticalAlignShift{0.0f}; | 345 | SkScalar fVerticalAlignShift{0.0f}; |
| 346 | + SkScalar fMaxLineHeight{std::numeric_limits<float>::max()}; | ||
| 347 | + SkScalar fMinLineHeight{0.0f}; | ||
| 348 | + LineHeightStyle fLineHeightStyle{LineHeightStyle::kFontSize}; | ||
| 338 | 349 | ||
| 339 | }; | 350 | }; |
| 340 | 351 | ||
| @@ -52,6 +52,68 @@ TextStyle TextStyle::cloneForPlaceholder() { | |||
| 52 | return result; | 52 | return result; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | + | ||
| 56 | +bool TextStyle::equalsByTextShadow(const TextStyle& other) const { | ||
| 57 | + if (fTextShadows.size() != other.fTextShadows.size()) { | ||
| 58 | + return false; | ||
| 59 | + } | ||
| 60 | + for (size_t i = 0; i < fTextShadows.size(); ++i) { | ||
| 61 | + if (fTextShadows[i] != other.fTextShadows[i]) { | ||
| 62 | + return false; | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + return true; | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +bool TextStyle::equalsByFontFeatures(const TextStyle& other) const { | ||
| 69 | + if (fFontFeatures.size() != other.fFontFeatures.size()) { | ||
| 70 | + return false; | ||
| 71 | + } | ||
| 72 | + for (size_t i = 0; i < fFontFeatures.size(); ++i) { | ||
| 73 | + if (!(fFontFeatures[i] == other.fFontFeatures[i])) { | ||
| 74 | + return false; | ||
| 75 | + } | ||
| 76 | + } | ||
| 77 | + return true; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +bool TextStyle::equalsByShape(const TextStyle& other) const { | ||
| 81 | + return fFontStyle == other.fFontStyle && | ||
| 82 | + fLocale == other.fLocale && | ||
| 83 | + fFontFamilies == other.fFontFamilies && | ||
| 84 | + getCorrectFontSize() == other.getCorrectFontSize() && | ||
| 85 | + fHeightOverride == other.fHeightOverride && | ||
| 86 | + fHeight == other.fHeight && | ||
| 87 | + fHalfLeading == other.fHalfLeading && | ||
| 88 | + nearlyEqual(getTotalVerticalShift(), other.getTotalVerticalShift()) && | ||
| 89 | + fFontArguments == other.fFontArguments && | ||
| 90 | + fStyleId == other.fStyleId && | ||
| 91 | + fBackgroundRect == other.fBackgroundRect && | ||
| 92 | + nearlyEqual(fBaselineShift, other.fBaselineShift) && | ||
| 93 | + nearlyEqual(fMaxLineHeight, other.fMaxLineHeight) && | ||
| 94 | + nearlyEqual(fMinLineHeight, other.fMinLineHeight) && | ||
| 95 | + fLineHeightStyle == other.fLineHeightStyle && | ||
| 96 | + fBadgeType == other.fBadgeType; | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +bool TextStyle::equals(const TextStyle& other) const { | ||
| 100 | + if (fIsPlaceholder || other.fIsPlaceholder) { | ||
| 101 | + return false; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + return fColor == other.fColor && | ||
| 105 | + fDecoration == other.fDecoration && | ||
| 106 | + nearlyEqual(fLetterSpacing, other.fLetterSpacing) && | ||
| 107 | + nearlyEqual(fWordSpacing, other.fWordSpacing) && | ||
| 108 | + fHasForeground == other.fHasForeground && | ||
| 109 | + fForeground == other.fForeground && | ||
| 110 | + fHasBackground == other.fHasBackground && | ||
| 111 | + fBackground == other.fBackground && | ||
| 112 | + equalsByFontFeatures(other) && | ||
| 113 | + equalsByTextShadow(other) && | ||
| 114 | + equalsByShape(other); | ||
| 115 | +} | ||
| 116 | + | ||
| 55 | bool TextStyle::equals(const TextStyle& other) const { | 117 | bool TextStyle::equals(const TextStyle& other) const { |
| 56 | 118 | ||
| 57 | if (fIsPlaceholder || other.fIsPlaceholder) { | 119 | if (fIsPlaceholder || other.fIsPlaceholder) { |
| @@ -119,12 +181,9 @@ bool TextStyle::equals(const TextStyle& other) const { | |||
| 119 | if (fFontArguments != other.fFontArguments) { | 181 | if (fFontArguments != other.fFontArguments) { |
| 120 | return false; | 182 | return false; |
| 121 | } | 183 | } |
| 122 | - if (fStyleId != other.fStyleId || fBackgroundRect != other.fBackgroundRect) { | ||
| 123 | - return false; | ||
| 124 | - } | ||
| 125 | - | ||
| 126 | return true; | 184 | return true; |
| 127 | } | 185 | } |
| 186 | + | ||
| 128 | 187 | ||
| 129 | bool TextStyle::equalsByFonts(const TextStyle& that) const { | 188 | bool TextStyle::equalsByFonts(const TextStyle& that) const { |
| 130 | 189 | ||
| @@ -143,6 +202,33 @@ bool TextStyle::equalsByFonts(const TextStyle& that) const { | |||
| 143 | fBackgroundRect == that.fBackgroundRect; | 202 | fBackgroundRect == that.fBackgroundRect; |
| 144 | } | 203 | } |
| 145 | 204 | ||
| 205 | + | ||
| 206 | +bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) const { | ||
| 207 | + switch (styleType) { | ||
| 208 | + case kForeground: | ||
| 209 | + return (!fHasForeground && !other.fHasForeground && fColor == other.fColor) || | ||
| 210 | + ( fHasForeground && other.fHasForeground && fForeground == other.fForeground); | ||
| 211 | + case kBackground: | ||
| 212 | + return (!fHasBackground && !other.fHasBackground) || | ||
| 213 | + ( fHasBackground && other.fHasBackground && fBackground == other.fBackground); | ||
| 214 | + case kShadow: | ||
| 215 | + return equalsByTextShadow(other); | ||
| 216 | + case kDecorations: | ||
| 217 | + return this->fDecoration == other.fDecoration; | ||
| 218 | + case kLetterSpacing: | ||
| 219 | + return fLetterSpacing == other.fLetterSpacing; | ||
| 220 | + case kWordSpacing: | ||
| 221 | + return fWordSpacing == other.fWordSpacing; | ||
| 222 | + case kAllAttributes: | ||
| 223 | + return this->equals(other); | ||
| 224 | + case kFont: | ||
| 225 | + return equalsByShape(other); | ||
| 226 | + default: | ||
| 227 | + SkASSERT(false); | ||
| 228 | + return false; | ||
| 229 | + } | ||
| 230 | +} | ||
| 231 | + | ||
| 146 | bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) const { | 232 | bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) const { |
| 147 | switch (styleType) { | 233 | switch (styleType) { |
| 148 | case kForeground: | 234 | case kForeground: |
| @@ -182,17 +268,10 @@ bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) c | |||
| 182 | return fFontStyle == other.fFontStyle && | 268 | return fFontStyle == other.fFontStyle && |
| 183 | fLocale == other.fLocale && | 269 | fLocale == other.fLocale && |
| 184 | fFontFamilies == other.fFontFamilies && | 270 | fFontFamilies == other.fFontFamilies && |
| 185 | - | ||
| 186 | - getCorrectFontSize() == other.getCorrectFontSize() && | ||
| 187 | - fHeight == other.fHeight && | ||
| 188 | - fHalfLeading == other.fHalfLeading && | ||
| 189 | - getTotalVerticalShift() == other.getTotalVerticalShift() && | ||
| 190 | - | ||
| 191 | fFontSize == other.fFontSize && | 271 | fFontSize == other.fFontSize && |
| 192 | fHeight == other.fHeight && | 272 | fHeight == other.fHeight && |
| 193 | fHalfLeading == other.fHalfLeading && | 273 | fHalfLeading == other.fHalfLeading && |
| 194 | fBaselineShift == other.fBaselineShift && | 274 | fBaselineShift == other.fBaselineShift && |
| 195 | - | ||
| 196 | fFontArguments == other.fFontArguments && | 275 | fFontArguments == other.fFontArguments && |
| 197 | fStyleId == other.fStyleId && | 276 | fStyleId == other.fStyleId && |
| 198 | fBackgroundRect == other.fBackgroundRect; | 277 | fBackgroundRect == other.fBackgroundRect; |
| @@ -201,6 +280,7 @@ bool TextStyle::matchOneAttribute(StyleType styleType, const TextStyle& other) c | |||
| 201 | return false; | 280 | return false; |
| 202 | } | 281 | } |
| 203 | } | 282 | } |
| 283 | + | ||
| 204 | 284 | ||
| 205 | 285 | ||
| 206 | void TextStyle::getFontMetrics(SkFontMetrics* metrics) const { | 286 | void TextStyle::getFontMetrics(SkFontMetrics* metrics) const { |
| @@ -954,6 +954,8 @@ void TextWrapper::layoutLinesSimple(ParagraphImpl* parent, | |||
| 954 | auto disableFirstAscent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent; | 954 | auto disableFirstAscent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent; |
| 955 | auto disableLastDescent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent; | 955 | auto disableLastDescent = parent->paragraphStyle().getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent; |
| 956 | bool firstLine = true; // We only interested in fist line if we have to disable the first ascent | 956 | bool firstLine = true; // We only interested in fist line if we have to disable the first ascent |
| 957 | + SkScalar lineSpacing = parent->paragraphStyle().getLineSpacing(); | ||
| 958 | + bool needLineSpacing = lineSpacing > 0; | ||
| 957 | 959 | ||
| 958 | // Resolve balanced line widths | 960 | // Resolve balanced line widths |
| 959 | std::vector<SkScalar> balancedWidths; | 961 | std::vector<SkScalar> balancedWidths; |
| @@ -1049,6 +1051,7 @@ void TextWrapper::layoutLinesSimple(ParagraphImpl* parent, | |||
| 1049 | } | 1051 | } |
| 1050 | if (disableLastDescent && (lastLine || (startLine == end && !fHardLineBreak))) { | 1052 | if (disableLastDescent && (lastLine || (startLine == end && !fHardLineBreak))) { |
| 1051 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; | 1053 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; |
| 1054 | + needLineSpacing = false; | ||
| 1052 | } | 1055 | } |
| 1053 | 1056 | ||
| 1054 | if (parent->strutEnabled()) { | 1057 | if (parent->strutEnabled()) { |
| @@ -1057,6 +1060,9 @@ void TextWrapper::layoutLinesSimple(ParagraphImpl* parent, | |||
| 1057 | } | 1060 | } |
| 1058 | 1061 | ||
| 1059 | SkScalar lineHeight = fEndLine.metrics().height(); | 1062 | SkScalar lineHeight = fEndLine.metrics().height(); |
| 1063 | + if (needLineSpacing) { | ||
| 1064 | + lineHeight += lineSpacing; | ||
| 1065 | + } | ||
| 1060 | if (fHardLineBreak && !lastLine && parent->paragraphStyle().getParagraphSpacing() > 0) { | 1066 | if (fHardLineBreak && !lastLine && parent->paragraphStyle().getParagraphSpacing() > 0) { |
| 1061 | lineHeight += parent->paragraphStyle().getParagraphSpacing(); | 1067 | lineHeight += parent->paragraphStyle().getParagraphSpacing(); |
| 1062 | } | 1068 | } |
| @@ -1493,7 +1499,9 @@ void TextWrapper::initializeFormattingState(SkScalar maxWidth, const SkSpan<Clus | |||
| 1493 | style.getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent, | 1499 | style.getTextHeightBehavior() & TextHeightBehavior::kDisableFirstAscent, |
| 1494 | style.getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent, | 1500 | style.getTextHeightBehavior() & TextHeightBehavior::kDisableLastDescent, |
| 1495 | style.getMaxLines(), | 1501 | style.getMaxLines(), |
| 1496 | - style.effective_align()}; | 1502 | + style.effective_align(), |
| 1503 | + fParent->paragraphStyle().getLineSpacing() > 0, | ||
| 1504 | + fParent->paragraphStyle().getLineSpacing()}; | ||
| 1497 | 1505 | ||
| 1498 | fFirstLine = true; | 1506 | fFirstLine = true; |
| 1499 | fSoftLineMaxIntrinsicWidth = 0; | 1507 | fSoftLineMaxIntrinsicWidth = 0; |
| @@ -1516,14 +1524,13 @@ void TextWrapper::processLineStretches(SkScalar maxWidth, const AddLineToParagra | |||
| 1516 | } | 1524 | } |
| 1517 | 1525 | ||
| 1518 | void TextWrapper::finalizeTextLayout(const AddLineToParagraph& addLine) { | 1526 | void TextWrapper::finalizeTextLayout(const AddLineToParagraph& addLine) { |
| 1519 | - processRemainingClusters(); | ||
| 1520 | - addFinalLineBreakIfNeeded(addLine); | ||
| 1521 | - adjustFirstLastLineMetrics(); | ||
| 1522 | - | ||
| 1523 | if (fParent->paragraphStyle().getIsEndAddParagraphSpacing() && | 1527 | if (fParent->paragraphStyle().getIsEndAddParagraphSpacing() && |
| 1524 | fParent->paragraphStyle().getParagraphSpacing() > 0) { | 1528 | fParent->paragraphStyle().getParagraphSpacing() > 0) { |
| 1525 | fHeight += fParent->paragraphStyle().getParagraphSpacing(); | 1529 | fHeight += fParent->paragraphStyle().getParagraphSpacing(); |
| 1526 | } | 1530 | } |
| 1531 | + processRemainingClusters(); | ||
| 1532 | + addFinalLineBreakIfNeeded(addLine); | ||
| 1533 | + adjustFirstLastLineMetrics(); | ||
| 1527 | } | 1534 | } |
| 1528 | 1535 | ||
| 1529 | void TextWrapper::prepareLineForFormatting(TextStretch& line) { | 1536 | void TextWrapper::prepareLineForFormatting(TextStretch& line) { |
| @@ -1540,10 +1547,10 @@ void TextWrapper::formatCurrentLine(const AddLineToParagraph& addLine) { | |||
| 1540 | } | 1547 | } |
| 1541 | 1548 | ||
| 1542 | bool TextWrapper::determineIfEllipsisNeeded() { | 1549 | bool TextWrapper::determineIfEllipsisNeeded() { |
| 1543 | - bool lastLine = (fFormattingContext.hasEllipsis && fFormattingContext.unlimitedLines) || | 1550 | + fIsLastLine = (fFormattingContext.hasEllipsis && fFormattingContext.unlimitedLines) || |
| 1544 | fLineNumber >= fFormattingContext.maxLines; | 1551 | fLineNumber >= fFormattingContext.maxLines; |
| 1545 | bool needEllipsis = | 1552 | bool needEllipsis = |
| 1546 | - fFormattingContext.hasEllipsis && !fFormattingContext.endlessLine && lastLine; | 1553 | + fFormattingContext.hasEllipsis && !fFormattingContext.endlessLine && fIsLastLine; |
| 1547 | 1554 | ||
| 1548 | if (fParent->paragraphStyle().getEllipsisMod() == EllipsisModal::HEAD && | 1555 | if (fParent->paragraphStyle().getEllipsisMod() == EllipsisModal::HEAD && |
| 1549 | fFormattingContext.hasEllipsis) { | 1556 | fFormattingContext.hasEllipsis) { |
| @@ -1592,8 +1599,9 @@ void TextWrapper::adjustLineMetricsForFirstLastLine() { | |||
| 1592 | if (fFormattingContext.disableFirstAscent && fFirstLine) { | 1599 | if (fFormattingContext.disableFirstAscent && fFirstLine) { |
| 1593 | fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; | 1600 | fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; |
| 1594 | } | 1601 | } |
| 1595 | - if (fFormattingContext.disableLastDescent && isLastLine()) { | 1602 | + if (fFormattingContext.disableLastDescent && (fIsLastLine ||(fCurrentStartLine == fEnd && !fHardLineBreak))) { |
| 1596 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; | 1603 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; |
| 1604 | + fFormattingContext.needLineSpacing = false; | ||
| 1597 | } | 1605 | } |
| 1598 | } | 1606 | } |
| 1599 | 1607 | ||
| @@ -1638,6 +1646,9 @@ TextWrapper::LineTextRanges TextWrapper::calculateLineTextRanges() { | |||
| 1638 | 1646 | ||
| 1639 | SkScalar TextWrapper::calculateLineHeight() { | 1647 | SkScalar TextWrapper::calculateLineHeight() { |
| 1640 | SkScalar height = fEndLine.metrics().height(); | 1648 | SkScalar height = fEndLine.metrics().height(); |
| 1649 | + if (fFormattingContext.needLineSpacing) { | ||
| 1650 | + height += fFormattingContext.lineSpacing; | ||
| 1651 | + } | ||
| 1641 | if (fHardLineBreak && !isLastLine() && fParent->paragraphStyle().getParagraphSpacing() > 0) { | 1652 | if (fHardLineBreak && !isLastLine() && fParent->paragraphStyle().getParagraphSpacing() > 0) { |
| 1642 | height += fParent->paragraphStyle().getParagraphSpacing(); | 1653 | height += fParent->paragraphStyle().getParagraphSpacing(); |
| 1643 | } | 1654 | } |
| @@ -1767,7 +1778,7 @@ void TextWrapper::adjustMetricsForEmptyParagraph() { | |||
| 1767 | if (fFormattingContext.disableFirstAscent) { | 1778 | if (fFormattingContext.disableFirstAscent) { |
| 1768 | fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; | 1779 | fEndLine.metrics().fAscent = fEndLine.metrics().fRawAscent; |
| 1769 | } | 1780 | } |
| 1770 | - if (fFormattingContext.disableLastDescent && !fHardLineBreak) { | 1781 | + if (fFormattingContext.disableLastDescent && (fIsLastLine || (fEndLine.endCluster() == fEnd && !fHardLineBreak))) { |
| 1771 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; | 1782 | fEndLine.metrics().fDescent = fEndLine.metrics().fRawDescent; |
| 1772 | } | 1783 | } |
| 1773 | } | 1784 | } |
| @@ -260,6 +260,8 @@ private: | |||
| 260 | bool disableLastDescent{false}; | 260 | bool disableLastDescent{false}; |
| 261 | size_t maxLines{0}; | 261 | size_t maxLines{0}; |
| 262 | TextAlign align{TextAlign::kLeft}; | 262 | TextAlign align{TextAlign::kLeft}; |
| 263 | + bool needLineSpacing{false}; | ||
| 264 | + SkScalar lineSpacing{0.0f}; | ||
| 263 | }; | 265 | }; |
| 264 | 266 | ||
| 265 | struct LineTextRanges { | 267 | struct LineTextRanges { |
| @@ -402,6 +404,7 @@ private: | |||
| 402 | size_t fCurrentStartPos{0}; | 404 | size_t fCurrentStartPos{0}; |
| 403 | Cluster* fStart{nullptr}; | 405 | Cluster* fStart{nullptr}; |
| 404 | Cluster* fEnd{nullptr}; | 406 | Cluster* fEnd{nullptr}; |
| 407 | + bool fIsLastLine{false}; | ||
| 405 | 408 | ||
| 406 | }; | 409 | }; |
| 407 | } // namespace textlayout | 410 | } // namespace textlayout |
结构体拷贝以及快速重排版的地方没看到处理新增字段呢? ![]() ![]()
2d 层 ![]() ![]() | |||


nearlyEqual