1 // Copyright 2019 Google LLC. 2 #ifndef TextLine_DEFINED 3 #define TextLine_DEFINED 4 5 #include "include/core/SkPoint.h" 6 #include "include/core/SkRect.h" 7 #include "include/core/SkScalar.h" 8 #include "include/private/base/SkTArray.h" 9 #include "modules/skparagraph/include/DartTypes.h" 10 #include "modules/skparagraph/include/Metrics.h" 11 #include "modules/skparagraph/include/ParagraphPainter.h" 12 #ifdef ENABLE_TEXT_ENHANCE 13 #include "modules/skparagraph/include/RunBase.h" 14 #include "modules/skparagraph/include/TextLineBase.h" 15 #include "modules/skparagraph/include/ParagraphStyle.h" 16 #endif 17 #include "modules/skparagraph/include/TextStyle.h" 18 #include "modules/skparagraph/src/Run.h" 19 #include "src/base/SkBitmaskEnum.h" 20 21 #include <stddef.h> 22 #include <functional> 23 #include <memory> 24 #include <vector> 25 26 class SkString; 27 28 namespace skia { 29 namespace textlayout { 30 #ifdef ENABLE_TEXT_ENHANCE 31 const size_t BOTTOM_PADDING_FACTOR = 8; 32 #endif 33 34 class ParagraphImpl; 35 #ifdef ENABLE_TEXT_ENHANCE 36 struct DecorationContext { 37 SkScalar thickness{0.0f}; 38 SkScalar underlinePosition{0.0f}; 39 SkScalar textBlobTop{0.0f}; 40 SkScalar lineHeight{0.0f}; 41 }; 42 43 struct IterateRunsContext { 44 size_t runIndex{0}; 45 SkScalar width{0}; 46 SkScalar runOffset{0}; 47 SkScalar totalWidth{0}; 48 bool isAlreadyUseEllipsis{false}; 49 TextRange lineIntersection; 50 EllipsisModal ellipsisMode{EllipsisModal::NONE}; 51 }; 52 #endif 53 class TextLine { 54 public: 55 56 struct ClipContext { 57 const Run* run; 58 size_t pos; 59 size_t size; 60 SkScalar fTextShift; // Shifts the text inside the run so it's placed at the right position 61 SkRect clip; 62 SkScalar fExcludedTrailingSpaces; 63 bool clippingNeeded; 64 #ifdef ENABLE_TEXT_ENHANCE 65 bool fIsTrimTrailingSpaceWidth{false}; 66 SkScalar fTrailingSpaceWidth{0.0f}; 67 #endif 68 }; 69 70 #ifdef ENABLE_TEXT_ENHANCE 71 struct PathParameters { 72 const RSPath* recordPath{nullptr}; 73 SkScalar hOffset{0}; 74 SkScalar vOffset{0}; 75 } pathParameters; 76 #endif 77 78 enum TextAdjustment { 79 GlyphCluster = 0x01, // All text producing glyphs pointing to the same ClusterIndex 80 GlyphemeCluster = 0x02, // base glyph + all attached diacritics 81 Grapheme = 0x04, // Text adjusted to graphemes 82 GraphemeGluster = 0x05, // GlyphCluster & Grapheme 83 }; 84 85 #ifdef ENABLE_TEXT_ENHANCE 86 enum EllipsisReadStrategy { 87 DEFAULT = 0, // default 88 READ_REPLACED_WORD = 1, // read replaced word 89 READ_ELLIPSIS_WORD = 2, // read ellipsis word 90 }; 91 92 struct HighLevelInfo { 93 ClusterIndex clusterIndex{SIZE_MAX}; 94 bool isClusterPunct{false}; 95 SkScalar punctWidths{0.0f}; 96 SkScalar highLevelOffset{0.0f}; 97 }; 98 99 struct MiddleLevelInfo { 100 ClusterIndex clusterIndex{SIZE_MAX}; 101 bool isPrevClusterSpace{true}; 102 }; 103 104 struct ClusterLevelsIndices { 105 std::vector<HighLevelInfo> highLevelIndices; 106 std::vector<MiddleLevelInfo> middleLevelIndices; 107 std::vector<ClusterIndex> LowLevelIndices; 108 SkScalar middleLevelOffset{0.0f}; 109 SkScalar lowLevelOffset{0.0f}; 110 emptyClusterLevelsIndices111 bool empty() 112 { 113 return highLevelIndices.empty() && middleLevelIndices.empty() && LowLevelIndices.empty(); 114 } 115 }; 116 117 enum class ShiftLevel { 118 Undefined, 119 HighLevel, // Level 1 Label: Punctuation 120 MiddleLevel, // Level-2 label: WhitespaceBreak, between ideographic and non-ideographic characters 121 LowLevel // Level-3 label: Between ideographic characters 122 }; 123 #endif 124 125 TextLine() = default; 126 TextLine(const TextLine&) = delete; 127 TextLine& operator=(const TextLine&) = delete; 128 TextLine(TextLine&&) = default; 129 TextLine& operator=(TextLine&&) = default; 130 ~TextLine() = default; 131 132 TextLine(ParagraphImpl* owner, 133 SkVector offset, 134 SkVector advance, 135 BlockRange blocks, 136 TextRange textExcludingSpaces, 137 TextRange text, 138 TextRange textIncludingNewlines, 139 ClusterRange clusters, 140 ClusterRange clustersWithGhosts, 141 SkScalar widthWithSpaces, 142 InternalLineMetrics sizes); 143 trimmedText()144 TextRange trimmedText() const { return fTextExcludingSpaces; } textWithNewlines()145 TextRange textWithNewlines() const { return fTextIncludingNewlines; } text()146 TextRange text() const { return fText; } clusters()147 ClusterRange clusters() const { return fClusterRange; } clustersWithSpaces()148 ClusterRange clustersWithSpaces() const { return fGhostClusterRange; } ellipsis()149 Run* ellipsis() const { return fEllipsis.get(); } sizes()150 InternalLineMetrics sizes() const { return fSizes; } empty()151 bool empty() const { return fTextExcludingSpaces.empty(); } 152 spacesWidth()153 SkScalar spacesWidth() const { return fWidthWithSpaces - width(); } height()154 SkScalar height() const { return fAdvance.fY; } width()155 SkScalar width() const { 156 return fAdvance.fX + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0); 157 } widthWithoutEllipsis()158 SkScalar widthWithoutEllipsis() const { return fAdvance.fX; } 159 #ifdef ENABLE_TEXT_ENHANCE widthWithEllipsisSpaces()160 SkScalar widthWithEllipsisSpaces() const { 161 return fWidthWithSpaces + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0); 162 } 163 #endif 164 SkVector offset() const; 165 #ifdef ENABLE_TEXT_ENHANCE setLineOffsetX(SkScalar x)166 void setLineOffsetX(SkScalar x) { 167 fOffset.set(x, fOffset.y()); 168 } 169 #endif 170 alphabeticBaseline()171 SkScalar alphabeticBaseline() const { return fSizes.alphabeticBaseline(); } ideographicBaseline()172 SkScalar ideographicBaseline() const { return fSizes.ideographicBaseline(); } baseline()173 SkScalar baseline() const { return fSizes.baseline(); } 174 #ifdef ENABLE_TEXT_ENHANCE 175 void extendCoordinateRange(PositionWithAffinity& positionWithAffinity); 176 #endif 177 178 using RunVisitor = std::function<bool( 179 const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width)>; 180 181 #ifdef ENABLE_TEXT_ENHANCE 182 bool processEllipsisRun(IterateRunsContext& context, 183 EllipsisReadStrategy ellipsisReadStrategy, 184 const RunVisitor& visitor, 185 SkScalar& runWidthInLine) const; 186 bool processInsertedRun(const Run* run, 187 SkScalar& runOffset, 188 EllipsisReadStrategy ellipsisReadStrategy, 189 const RunVisitor& visitor, 190 SkScalar& runWidthInLine) const; 191 void iterateThroughVisualRuns(EllipsisReadStrategy ellipsisReadStrategy, 192 bool includingGhostSpaces, 193 const RunVisitor& runVisitor) const; 194 bool handleMiddleEllipsisMode(const Run* run, IterateRunsContext& context, 195 EllipsisReadStrategy& ellipsisReadStrategy, const RunVisitor& runVisitor) const; 196 #else 197 void iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisitor& runVisitor) const; 198 #endif 199 using RunStyleVisitor = std::function<void( 200 TextRange textRange, const TextStyle& style, const ClipContext& context)>; 201 SkScalar iterateThroughSingleRunByStyles(TextAdjustment textAdjustment, 202 const Run* run, 203 SkScalar runOffset, 204 TextRange textRange, 205 StyleType styleType, 206 const RunStyleVisitor& visitor) const; 207 208 using ClustersVisitor = std::function<bool(const Cluster* cluster, ClusterIndex index, bool ghost)>; 209 void iterateThroughClustersInGlyphsOrder(bool reverse, 210 bool includeGhosts, 211 const ClustersVisitor& visitor) const; 212 213 #ifdef ENABLE_TEXT_ENHANCE 214 void format(TextAlign align, SkScalar maxWidth, EllipsisModal ellipsisModal); 215 SkScalar autoSpacing(); 216 #else 217 void format(TextAlign align, SkScalar maxWidth); 218 #endif 219 void paint(ParagraphPainter* painter, SkScalar x, SkScalar y); 220 void visit(SkScalar x, SkScalar y); 221 void ensureTextBlobCachePopulated(); 222 223 void createEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr); 224 225 #ifdef ENABLE_TEXT_ENHANCE setParagraphImpl(ParagraphImpl * newpara)226 void setParagraphImpl(ParagraphImpl* newpara) { fOwner = newpara; } setBlockRange(const BlockRange & blockRange)227 void setBlockRange(const BlockRange& blockRange) { fBlockRange = blockRange; } 228 void countWord(int& wordCount, bool& inWord); 229 void ellipsisNotFitProcess(EllipsisModal ellipsisModal); 230 231 void createTailEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr, WordBreakType wordBreakType); 232 void handleTailEllipsisInEmptyLine(std::unique_ptr<Run>& ellipsisRun, const SkString& ellipsis, 233 SkScalar width, WordBreakType wordBreakType); 234 void TailEllipsisUpdateLine(Cluster& cluster, float width, size_t clusterIndex, WordBreakType wordBreakType); 235 void createHeadEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr); 236 void paint(ParagraphPainter* painter, const RSPath* path, SkScalar hOffset, SkScalar vOffset); 237 void createMiddleEllipsis(SkScalar maxWidth, const SkString& ellipsis); 238 void middleEllipsisUpdateLine(ClusterIndex& indexS, ClusterIndex& indexE, SkScalar width); 239 bool isLineHeightDominatedByRun(const Run& run); 240 SkScalar updateBlobShift(const Run& run, SkScalar verticalShift, bool isReset); 241 void updateBlobShift(const Run& run, SkScalar& verticalShift); 242 void resetBlobShift(const Run& run); 243 void shiftPlaceholderByVerticalAlignMode(Run& run, TextVerticalAlign VerticalAlignment); 244 void shiftTextByVerticalAlignment(Run& run, TextVerticalAlign VerticalAlignment); 245 void applyPlaceholderVerticalShift(); 246 void applyVerticalShift(); 247 void updateBlobAndRunShift(Run& run); 248 void refresh(); setLineAllRuns(skia_private::STArray<1,size_t,true> & runsInVisualOrder)249 void setLineAllRuns(skia_private::STArray<1, size_t, true>& runsInVisualOrder) { 250 fRunsInVisualOrder = std::move(runsInVisualOrder); 251 } setEllipsisRunIndex(size_t runIndex)252 void setEllipsisRunIndex(size_t runIndex) { fEllipsisIndex = runIndex; } 253 #endif 254 255 // For testing internal structures 256 void scanStyles(StyleType style, const RunStyleVisitor& visitor); 257 setMaxRunMetrics(const InternalLineMetrics & metrics)258 void setMaxRunMetrics(const InternalLineMetrics& metrics) { fMaxRunMetrics = metrics; } getMaxRunMetrics()259 InternalLineMetrics getMaxRunMetrics() const { return fMaxRunMetrics; } 260 261 bool isFirstLine() const; 262 bool isLastLine() const; 263 void getRectsForRange(TextRange textRange, 264 RectHeightStyle rectHeightStyle, 265 RectWidthStyle rectWidthStyle, 266 std::vector<TextBox>& boxes) const; 267 void getRectsForPlaceholders(std::vector<TextBox>& boxes); 268 PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx); 269 270 #ifdef ENABLE_TEXT_ENHANCE 271 TextLine::ClipContext getRunClipContextByRange( 272 const Run* run, TextRange textRange, TextLine::TextAdjustment textAdjustment, SkScalar textStartInLine) const; 273 #endif 274 ClipContext measureTextInsideOneRun(TextRange textRange, 275 const Run* run, 276 SkScalar runOffsetInLine, 277 SkScalar textOffsetInRunInLine, 278 bool includeGhostSpaces, 279 TextAdjustment textAdjustment) const; 280 281 LineMetrics getMetrics() const; 282 283 SkRect extendHeight(const ClipContext& context) const; 284 shiftVertically(SkScalar shift)285 void shiftVertically(SkScalar shift) { fOffset.fY += shift; } 286 setAscentStyle(LineMetricStyle style)287 void setAscentStyle(LineMetricStyle style) { fAscentStyle = style; } setDescentStyle(LineMetricStyle style)288 void setDescentStyle(LineMetricStyle style) { fDescentStyle = style; } 289 290 bool endsWithHardLineBreak() const; 291 292 std::unique_ptr<Run> shapeEllipsis(const SkString& ellipsis, const Cluster* cluster); 293 294 #ifdef ENABLE_TEXT_ENHANCE 295 std::vector<std::unique_ptr<RunBase>> getGlyphRuns() const; 296 double getTypographicBounds(double* ascent, double* descent, double* leading) const; 297 RSRect getImageBounds() const; 298 int32_t getStringIndexForPosition(SkPoint point) const; 299 size_t getGlyphCount() const; 300 bool endsWithOnlyHardBreak() const; 301 std::unique_ptr<Run> shapeString(const SkString& string, const Cluster* cluster); getLineAllRuns()302 skia_private::STArray<1, size_t, true> getLineAllRuns() const { return fRunsInVisualOrder; }; 303 TextLine CloneSelf(); getTextRangeReplacedByEllipsis()304 TextRange getTextRangeReplacedByEllipsis() const { return fTextRangeReplacedByEllipsis; } setTextBlobCachePopulated(const bool textBlobCachePopulated)305 void setTextBlobCachePopulated(const bool textBlobCachePopulated) { 306 fTextBlobCachePopulated = textBlobCachePopulated; 307 } 308 SkScalar usingAutoSpaceWidth(const Cluster* cluster) const; 309 std::unique_ptr<TextLineBase> createTruncatedLine(double width, EllipsisModal ellipsisMode, 310 const std::string& ellipsisStr); 311 312 double getTrailingSpaceWidth() const; 313 double getOffsetForStringIndex(int32_t index) const; 314 std::map<int32_t, double> getIndexAndOffsets(bool& isHardBreak) const; 315 double getAlignmentOffset(double alignmentFactor, double alignmentWidth) const; 316 SkRect generatePaintRegion(SkScalar x, SkScalar y); 317 void updateClusterOffsets(const Cluster* cluster, SkScalar shift, SkScalar prevShift); 318 void justifyUpdateRtlWidth(const SkScalar maxWidth, const SkScalar textLen); 319 void setBreakWithHyphen(bool breakWithHyphen); 320 bool getBreakWithHyphen() const; 321 void updateTextLinePaintAttributes(); 322 #endif 323 private: 324 #ifdef ENABLE_TEXT_ENHANCE 325 struct RoundRectAttr { 326 int styleId; 327 RectStyle roundRectStyle; 328 SkRect rect; 329 const Run* run; 330 RoundRectType fRoundRectType = RoundRectType::NONE; 331 }; 332 #endif 333 void justify(SkScalar maxWidth); 334 335 void buildTextBlob(TextRange textRange, const TextStyle& style, const ClipContext& context); 336 void paintBackground(ParagraphPainter* painter, 337 SkScalar x, 338 SkScalar y, 339 TextRange textRange, 340 const TextStyle& style, 341 const ClipContext& context) const; 342 void paintShadow(ParagraphPainter* painter, 343 SkScalar x, 344 SkScalar y, 345 TextRange textRange, 346 const TextStyle& style, 347 const ClipContext& context) const; 348 void paintDecorations(ParagraphPainter* painter, 349 SkScalar x, 350 SkScalar y, 351 TextRange textRange, 352 const TextStyle& style, 353 const ClipContext& context) const; 354 355 void shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift); 356 #ifdef ENABLE_TEXT_ENHANCE 357 void paintRoundRect(ParagraphPainter* painter, SkScalar x, SkScalar y) const; 358 void spacingCluster(const Cluster* cluster, SkScalar spacing, SkScalar prevSpacing); 359 bool hasBackgroundRect(const RoundRectAttr& attr); 360 void computeRoundRect(int& index, int& preIndex, std::vector<Run*>& groupRuns, Run* run); 361 void prepareRoundRect(); 362 SkScalar calculateThickness(const TextStyle& style, const ClipContext& context); 363 void measureTextWithSpacesAtTheEnd(ClipContext& context, bool includeGhostSpaces) const; 364 void computeNextPaintGlyphRange(ClipContext& context, const TextRange& lastGlyphRange, StyleType styleType) const; 365 SkRect computeShadowRect(SkScalar x, SkScalar y, const TextStyle& style, const ClipContext& context) const; 366 SkRect getAllShadowsRect(SkScalar x, SkScalar y) const; 367 #endif 368 369 ParagraphImpl* fOwner; 370 BlockRange fBlockRange; 371 TextRange fTextExcludingSpaces; 372 TextRange fText; 373 TextRange fTextIncludingNewlines; 374 ClusterRange fClusterRange; 375 ClusterRange fGhostClusterRange; 376 // Avoid the malloc/free in the common case of one run per line 377 skia_private::STArray<1, size_t, true> fRunsInVisualOrder; 378 SkVector fAdvance; // Text size 379 SkVector fOffset; // Text position 380 SkScalar fShift; // Let right 381 SkScalar fWidthWithSpaces; 382 std::unique_ptr<Run> fEllipsis; // In case the line ends with the ellipsis 383 InternalLineMetrics fSizes; // Line metrics as a max of all run metrics and struts 384 InternalLineMetrics fMaxRunMetrics; // No struts - need it for GetRectForRange(max height) 385 bool fHasBackground; 386 bool fHasShadows; 387 bool fHasDecorations; 388 #ifdef ENABLE_TEXT_ENHANCE 389 size_t fEllipsisIndex = EMPTY_INDEX; 390 TextRange fTextRangeReplacedByEllipsis; // text range replaced by ellipsis 391 bool fIsArcText; 392 bool fArcTextState; 393 bool fLastClipRunLtr; 394 #endif 395 396 LineMetricStyle fAscentStyle; 397 LineMetricStyle fDescentStyle; 398 399 struct TextBlobRecord { 400 void paint(ParagraphPainter* painter, SkScalar x, SkScalar y); 401 402 #ifdef ENABLE_TEXT_ENHANCE 403 void paint(ParagraphPainter* painter); 404 std::shared_ptr<RSTextBlob> fBlob; 405 size_t fVisitor_Size; 406 #else 407 sk_sp<SkTextBlob> fBlob; 408 #endif 409 SkPoint fOffset = SkPoint::Make(0.0f, 0.0f); 410 ParagraphPainter::SkPaintOrID fPaint; 411 SkRect fBounds = SkRect::MakeEmpty(); 412 bool fClippingNeeded = false; 413 SkRect fClipRect = SkRect::MakeEmpty(); 414 415 // Extra fields only used for the (experimental) visitor 416 const Run* fVisitor_Run; 417 size_t fVisitor_Pos; 418 }; 419 bool fTextBlobCachePopulated; 420 #ifdef ENABLE_TEXT_ENHANCE 421 DecorationContext fDecorationContext; 422 std::vector<RoundRectAttr> fRoundRectAttrs = {}; 423 bool fIsTextLineEllipsisHeadModal = false; 424 #endif 425 public: 426 std::vector<TextBlobRecord> fTextBlobCache; 427 #ifdef ENABLE_TEXT_ENHANCE 428 SkString fEllipsisString; 429 bool fBreakWithHyphen{false}; 430 std::unique_ptr<Run> fHyphenRun; 431 size_t fHyphenIndex = EMPTY_INDEX; 432 #endif 433 }; 434 } // namespace textlayout 435 } // namespace skia 436 437 namespace sknonstd { 438 template <> struct is_bitmask_enum<skia::textlayout::TextLine::TextAdjustment> : std::true_type {}; 439 } // namespace sknonstd 440 441 #endif // TextLine_DEFINED 442