1 /* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkTextBlob_DEFINED 9 #define SkTextBlob_DEFINED 10 11 #include "include/core/SkFont.h" 12 #include "include/core/SkPaint.h" 13 #include "include/core/SkRefCnt.h" 14 #include "include/core/SkString.h" 15 #include "include/private/SkTemplates.h" 16 17 #include <atomic> 18 #include <string> 19 20 struct SkRSXform; 21 struct SkSerialProcs; 22 struct SkDeserialProcs; 23 24 /** \class SkTextBlob 25 SkTextBlob combines multiple text runs into an immutable container. Each text 26 run consists of glyphs, SkPaint, and position. Only parts of SkPaint related to 27 fonts and text rendering are used by run. 28 */ 29 class SK_API SkTextBlob final : public SkNVRefCnt<SkTextBlob> { 30 private: 31 class RunRecord; 32 33 public: 34 35 /** Returns conservative bounding box. Uses SkPaint associated with each glyph to 36 determine glyph bounds, and unions all bounds. Returned bounds may be 37 larger than the bounds of all glyphs in runs. 38 39 @return conservative bounding box 40 */ bounds()41 const SkRect& bounds() const { return fBounds; } 42 43 /** Returns a non-zero value unique among all text blobs. 44 45 @return identifier for SkTextBlob 46 */ uniqueID()47 uint32_t uniqueID() const { return fUniqueID; } 48 49 /** Returns the number of intervals that intersect bounds. 50 bounds describes a pair of lines parallel to the text advance. 51 The return count is zero or a multiple of two, and is at most twice the number of glyphs in 52 the the blob. 53 54 Pass nullptr for intervals to determine the size of the interval array. 55 56 Runs within the blob that contain SkRSXform are ignored when computing intercepts. 57 58 @param bounds lower and upper line parallel to the advance 59 @param intervals returned intersections; may be nullptr 60 @param paint specifies stroking, SkPathEffect that affects the result; may be nullptr 61 @return number of intersections; may be zero 62 */ 63 int getIntercepts(const SkScalar bounds[2], SkScalar intervals[], 64 const SkPaint* paint = nullptr) const; 65 66 /** Creates SkTextBlob with a single run. 67 68 font contains attributes used to define the run text. 69 70 When encoding is SkTextEncoding::kUTF8, SkTextEncoding::kUTF16, or 71 SkTextEncoding::kUTF32, this function uses the default 72 character-to-glyph mapping from the SkTypeface in font. It does not 73 perform typeface fallback for characters not found in the SkTypeface. 74 It does not perform kerning or other complex shaping; glyphs are 75 positioned based on their default advances. 76 77 @param text character code points or glyphs drawn 78 @param byteLength byte length of text array 79 @param font text size, typeface, text scale, and so on, used to draw 80 @param encoding text encoding used in the text array 81 @return SkTextBlob constructed from one run 82 */ 83 static sk_sp<SkTextBlob> MakeFromText(const void* text, size_t byteLength, const SkFont& font, 84 SkTextEncoding encoding = SkTextEncoding::kUTF8); 85 86 /** Creates SkTextBlob with a single run. string meaning depends on SkTextEncoding; 87 by default, string is encoded as UTF-8. 88 89 font contains attributes used to define the run text. 90 91 When encoding is SkTextEncoding::kUTF8, SkTextEncoding::kUTF16, or 92 SkTextEncoding::kUTF32, this function uses the default 93 character-to-glyph mapping from the SkTypeface in font. It does not 94 perform typeface fallback for characters not found in the SkTypeface. 95 It does not perform kerning or other complex shaping; glyphs are 96 positioned based on their default advances. 97 98 @param string character code points or glyphs drawn 99 @param font text size, typeface, text scale, and so on, used to draw 100 @param encoding text encoding used in the text array 101 @return SkTextBlob constructed from one run 102 */ 103 static sk_sp<SkTextBlob> MakeFromString(const char* string, const SkFont& font, 104 SkTextEncoding encoding = SkTextEncoding::kUTF8) { 105 if (!string) { 106 return nullptr; 107 } 108 return MakeFromText(string, strlen(string), font, encoding); 109 } 110 111 /** Returns a textblob built from a single run of text with x-positions and a single y value. 112 This is equivalent to using SkTextBlobBuilder and calling allocRunPosH(). 113 Returns nullptr if byteLength is zero. 114 115 @param text character code points or glyphs drawn (based on encoding) 116 @param byteLength byte length of text array 117 @param xpos array of x-positions, must contain values for all of the character points. 118 @param constY shared y-position for each character point, to be paired with each xpos. 119 @param font SkFont used for this run 120 @param encoding specifies the encoding of the text array. 121 @return new textblob or nullptr 122 */ 123 static sk_sp<SkTextBlob> MakeFromPosTextH(const void* text, size_t byteLength, 124 const SkScalar xpos[], SkScalar constY, const SkFont& font, 125 SkTextEncoding encoding = SkTextEncoding::kUTF8); 126 127 /** Returns a textblob built from a single run of text with positions. 128 This is equivalent to using SkTextBlobBuilder and calling allocRunPos(). 129 Returns nullptr if byteLength is zero. 130 131 @param text character code points or glyphs drawn (based on encoding) 132 @param byteLength byte length of text array 133 @param pos array of positions, must contain values for all of the character points. 134 @param font SkFont used for this run 135 @param encoding specifies the encoding of the text array. 136 @return new textblob or nullptr 137 */ 138 static sk_sp<SkTextBlob> MakeFromPosText(const void* text, size_t byteLength, 139 const SkPoint pos[], const SkFont& font, 140 SkTextEncoding encoding = SkTextEncoding::kUTF8); 141 142 static sk_sp<SkTextBlob> MakeFromRSXform(const void* text, size_t byteLength, 143 const SkRSXform xform[], const SkFont& font, 144 SkTextEncoding encoding = SkTextEncoding::kUTF8); 145 146 /** Writes data to allow later reconstruction of SkTextBlob. memory points to storage 147 to receive the encoded data, and memory_size describes the size of storage. 148 Returns bytes used if provided storage is large enough to hold all data; 149 otherwise, returns zero. 150 151 procs.fTypefaceProc permits supplying a custom function to encode SkTypeface. 152 If procs.fTypefaceProc is nullptr, default encoding is used. procs.fTypefaceCtx 153 may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc 154 is called with a pointer to SkTypeface and user context. 155 156 @param procs custom serial data encoders; may be nullptr 157 @param memory storage for data 158 @param memory_size size of storage 159 @return bytes written, or zero if required storage is larger than memory_size 160 161 example: https://fiddle.skia.org/c/@TextBlob_serialize 162 */ 163 size_t serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const; 164 165 /** Returns storage containing SkData describing SkTextBlob, using optional custom 166 encoders. 167 168 procs.fTypefaceProc permits supplying a custom function to encode SkTypeface. 169 If procs.fTypefaceProc is nullptr, default encoding is used. procs.fTypefaceCtx 170 may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc 171 is called with a pointer to SkTypeface and user context. 172 173 @param procs custom serial data encoders; may be nullptr 174 @return storage containing serialized SkTextBlob 175 176 example: https://fiddle.skia.org/c/@TextBlob_serialize_2 177 */ 178 sk_sp<SkData> serialize(const SkSerialProcs& procs) const; 179 180 /** Recreates SkTextBlob that was serialized into data. Returns constructed SkTextBlob 181 if successful; otherwise, returns nullptr. Fails if size is smaller than 182 required data length, or if data does not permit constructing valid SkTextBlob. 183 184 procs.fTypefaceProc permits supplying a custom function to decode SkTypeface. 185 If procs.fTypefaceProc is nullptr, default decoding is used. procs.fTypefaceCtx 186 may be used to provide user context to procs.fTypefaceProc; procs.fTypefaceProc 187 is called with a pointer to SkTypeface data, data byte length, and user context. 188 189 @param data pointer for serial data 190 @param size size of data 191 @param procs custom serial data decoders; may be nullptr 192 @return SkTextBlob constructed from data in memory 193 */ 194 static sk_sp<SkTextBlob> Deserialize(const void* data, size_t size, 195 const SkDeserialProcs& procs); 196 197 class SK_API Iter { 198 public: 199 struct Run { 200 SkTypeface* fTypeface; 201 int fGlyphCount; 202 const uint16_t* fGlyphIndices; 203 #ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED 204 const uint32_t* fClusterIndex_forTest; 205 int fUtf8Size_forTest; 206 const char* fUtf8_forTest; 207 #endif 208 }; 209 210 Iter(const SkTextBlob&); 211 212 /** 213 * Returns true for each "run" inside the textblob, setting the Run fields (if not null). 214 * If this returns false, there are no more runs, and the Run parameter will be ignored. 215 */ 216 bool next(Run*); 217 218 // Experimental, DO NO USE, will change/go-away 219 struct ExperimentalRun { 220 SkFont font; 221 int count; 222 const uint16_t* glyphs; 223 const SkPoint* positions; 224 }; 225 bool experimentalNext(ExperimentalRun*); 226 227 private: 228 const RunRecord* fRunRecord; 229 }; 230 231 /** Writes text representation of SkTextBlob to string. 232 233 @param desc the string storing a description of parameters. 234 @param depth the number of tabs preceding each line. 235 */ 236 void dump(std::string& desc, int depth) const; 237 private: 238 friend class SkNVRefCnt<SkTextBlob>; 239 240 enum GlyphPositioning : uint8_t; 241 242 explicit SkTextBlob(const SkRect& bounds); 243 244 ~SkTextBlob(); 245 246 // Memory for objects of this class is created with sk_malloc rather than operator new and must 247 // be freed with sk_free. 248 void operator delete(void* p); 249 void* operator new(size_t); 250 void* operator new(size_t, void* p); 251 252 static unsigned ScalarsPerGlyph(GlyphPositioning pos); 253 254 // Call when this blob is part of the key to a cache entry. This allows the cache 255 // to know automatically those entries can be purged when this SkTextBlob is deleted. notifyAddedToCache(uint32_t cacheID)256 void notifyAddedToCache(uint32_t cacheID) const { 257 fCacheID.store(cacheID); 258 } 259 260 friend class SkGlyphRunList; 261 friend class GrTextBlobCache; 262 friend class SkTextBlobBuilder; 263 friend class SkTextBlobPriv; 264 friend class SkTextBlobRunIterator; 265 266 const SkRect fBounds; 267 const uint32_t fUniqueID; 268 mutable std::atomic<uint32_t> fCacheID; 269 270 SkDEBUGCODE(size_t fStorageSize;) 271 272 // The actual payload resides in externally-managed storage, following the object. 273 // (see the .cpp for more details) 274 275 using INHERITED = SkRefCnt; 276 }; 277 278 /** \class SkTextBlobBuilder 279 Helper class for constructing SkTextBlob. 280 */ 281 class SK_API SkTextBlobBuilder { 282 public: 283 284 /** Constructs empty SkTextBlobBuilder. By default, SkTextBlobBuilder has no runs. 285 286 @return empty SkTextBlobBuilder 287 288 example: https://fiddle.skia.org/c/@TextBlobBuilder_empty_constructor 289 */ 290 SkTextBlobBuilder(); 291 292 /** Deletes data allocated internally by SkTextBlobBuilder. 293 */ 294 ~SkTextBlobBuilder(); 295 296 /** Returns SkTextBlob built from runs of glyphs added by builder. Returned 297 SkTextBlob is immutable; it may be copied, but its contents may not be altered. 298 Returns nullptr if no runs of glyphs were added by builder. 299 300 Resets SkTextBlobBuilder to its initial empty state, allowing it to be 301 reused to build a new set of runs. 302 303 @return SkTextBlob or nullptr 304 305 example: https://fiddle.skia.org/c/@TextBlobBuilder_make 306 */ 307 sk_sp<SkTextBlob> make(); 308 309 /** \struct SkTextBlobBuilder::RunBuffer 310 RunBuffer supplies storage for glyphs and positions within a run. 311 312 A run is a sequence of glyphs sharing font metrics and positioning. 313 Each run may position its glyphs in one of three ways: 314 by specifying where the first glyph is drawn, and allowing font metrics to 315 determine the advance to subsequent glyphs; by specifying a baseline, and 316 the position on that baseline for each glyph in run; or by providing SkPoint 317 array, one per glyph. 318 */ 319 struct RunBuffer { 320 SkGlyphID* glyphs; //!< storage for glyph indexes in run 321 SkScalar* pos; //!< storage for glyph positions in run 322 char* utf8text; //!< storage for text UTF-8 code units in run 323 uint32_t* clusters; //!< storage for glyph clusters (index of UTF-8 code unit) 324 325 // Helpers, since the "pos" field can be different types (always some number of floats). pointsRunBuffer326 SkPoint* points() const { return reinterpret_cast<SkPoint*>(pos); } xformsRunBuffer327 SkRSXform* xforms() const { return reinterpret_cast<SkRSXform*>(pos); } 328 }; 329 330 /** Returns run with storage for glyphs. Caller must write count glyphs to 331 RunBuffer::glyphs before next call to SkTextBlobBuilder. 332 333 RunBuffer::pos, RunBuffer::utf8text, and RunBuffer::clusters should be ignored. 334 335 Glyphs share metrics in font. 336 337 Glyphs are positioned on a baseline at (x, y), using font metrics to 338 determine their relative placement. 339 340 bounds defines an optional bounding box, used to suppress drawing when SkTextBlob 341 bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds 342 is computed from (x, y) and RunBuffer::glyphs metrics. 343 344 @param font SkFont used for this run 345 @param count number of glyphs 346 @param x horizontal offset within the blob 347 @param y vertical offset within the blob 348 @param bounds optional run bounding box 349 @return writable glyph buffer 350 */ 351 const RunBuffer& allocRun(const SkFont& font, int count, SkScalar x, SkScalar y, 352 const SkRect* bounds = nullptr); 353 354 /** Returns run with storage for glyphs and positions along baseline. Caller must 355 write count glyphs to RunBuffer::glyphs and count scalars to RunBuffer::pos 356 before next call to SkTextBlobBuilder. 357 358 RunBuffer::utf8text and RunBuffer::clusters should be ignored. 359 360 Glyphs share metrics in font. 361 362 Glyphs are positioned on a baseline at y, using x-axis positions written by 363 caller to RunBuffer::pos. 364 365 bounds defines an optional bounding box, used to suppress drawing when SkTextBlob 366 bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds 367 is computed from y, RunBuffer::pos, and RunBuffer::glyphs metrics. 368 369 @param font SkFont used for this run 370 @param count number of glyphs 371 @param y vertical offset within the blob 372 @param bounds optional run bounding box 373 @return writable glyph buffer and x-axis position buffer 374 */ 375 const RunBuffer& allocRunPosH(const SkFont& font, int count, SkScalar y, 376 const SkRect* bounds = nullptr); 377 378 /** Returns run with storage for glyphs and SkPoint positions. Caller must 379 write count glyphs to RunBuffer::glyphs and count SkPoint to RunBuffer::pos 380 before next call to SkTextBlobBuilder. 381 382 RunBuffer::utf8text and RunBuffer::clusters should be ignored. 383 384 Glyphs share metrics in font. 385 386 Glyphs are positioned using SkPoint written by caller to RunBuffer::pos, using 387 two scalar values for each SkPoint. 388 389 bounds defines an optional bounding box, used to suppress drawing when SkTextBlob 390 bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds 391 is computed from RunBuffer::pos, and RunBuffer::glyphs metrics. 392 393 @param font SkFont used for this run 394 @param count number of glyphs 395 @param bounds optional run bounding box 396 @return writable glyph buffer and SkPoint buffer 397 */ 398 const RunBuffer& allocRunPos(const SkFont& font, int count, 399 const SkRect* bounds = nullptr); 400 401 // RunBuffer.pos points to SkRSXform array 402 const RunBuffer& allocRunRSXform(const SkFont& font, int count); 403 404 /** Returns run with storage for glyphs, text, and clusters. Caller must 405 write count glyphs to RunBuffer::glyphs, textByteCount UTF-8 code units 406 into RunBuffer::utf8text, and count monotonic indexes into utf8text 407 into RunBuffer::clusters before next call to SkTextBlobBuilder. 408 409 RunBuffer::pos should be ignored. 410 411 Glyphs share metrics in font. 412 413 Glyphs are positioned on a baseline at (x, y), using font metrics to 414 determine their relative placement. 415 416 bounds defines an optional bounding box, used to suppress drawing when SkTextBlob 417 bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds 418 is computed from (x, y) and RunBuffer::glyphs metrics. 419 420 @param font SkFont used for this run 421 @param count number of glyphs 422 @param x horizontal offset within the blob 423 @param y vertical offset within the blob 424 @param textByteCount number of UTF-8 code units 425 @param bounds optional run bounding box 426 @return writable glyph buffer, text buffer, and cluster buffer 427 */ 428 const RunBuffer& allocRunText(const SkFont& font, int count, SkScalar x, SkScalar y, 429 int textByteCount, const SkRect* bounds = nullptr); 430 431 /** Returns run with storage for glyphs, positions along baseline, text, 432 and clusters. Caller must write count glyphs to RunBuffer::glyphs, 433 count scalars to RunBuffer::pos, textByteCount UTF-8 code units into 434 RunBuffer::utf8text, and count monotonic indexes into utf8text into 435 RunBuffer::clusters before next call to SkTextBlobBuilder. 436 437 Glyphs share metrics in font. 438 439 Glyphs are positioned on a baseline at y, using x-axis positions written by 440 caller to RunBuffer::pos. 441 442 bounds defines an optional bounding box, used to suppress drawing when SkTextBlob 443 bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds 444 is computed from y, RunBuffer::pos, and RunBuffer::glyphs metrics. 445 446 @param font SkFont used for this run 447 @param count number of glyphs 448 @param y vertical offset within the blob 449 @param textByteCount number of UTF-8 code units 450 @param bounds optional run bounding box 451 @return writable glyph buffer, x-axis position buffer, text buffer, and cluster buffer 452 */ 453 const RunBuffer& allocRunTextPosH(const SkFont& font, int count, SkScalar y, int textByteCount, 454 const SkRect* bounds = nullptr); 455 456 /** Returns run with storage for glyphs, SkPoint positions, text, and 457 clusters. Caller must write count glyphs to RunBuffer::glyphs, count 458 SkPoint to RunBuffer::pos, textByteCount UTF-8 code units into 459 RunBuffer::utf8text, and count monotonic indexes into utf8text into 460 RunBuffer::clusters before next call to SkTextBlobBuilder. 461 462 Glyphs share metrics in font. 463 464 Glyphs are positioned using SkPoint written by caller to RunBuffer::pos, using 465 two scalar values for each SkPoint. 466 467 bounds defines an optional bounding box, used to suppress drawing when SkTextBlob 468 bounds does not intersect SkSurface bounds. If bounds is nullptr, SkTextBlob bounds 469 is computed from RunBuffer::pos, and RunBuffer::glyphs metrics. 470 471 @param font SkFont used for this run 472 @param count number of glyphs 473 @param textByteCount number of UTF-8 code units 474 @param bounds optional run bounding box 475 @return writable glyph buffer, SkPoint buffer, text buffer, and cluster buffer 476 */ 477 const RunBuffer& allocRunTextPos(const SkFont& font, int count, int textByteCount, 478 const SkRect* bounds = nullptr); 479 480 // RunBuffer.pos points to SkRSXform array 481 const RunBuffer& allocRunTextRSXform(const SkFont& font, int count, int textByteCount, 482 const SkRect* bounds = nullptr); 483 484 private: 485 void reserve(size_t size); 486 void allocInternal(const SkFont& font, SkTextBlob::GlyphPositioning positioning, 487 int count, int textBytes, SkPoint offset, const SkRect* bounds); 488 bool mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning, 489 uint32_t count, SkPoint offset); 490 void updateDeferredBounds(); 491 492 static SkRect ConservativeRunBounds(const SkTextBlob::RunRecord&); 493 static SkRect TightRunBounds(const SkTextBlob::RunRecord&); 494 495 friend class SkTextBlobPriv; 496 friend class SkTextBlobBuilderPriv; 497 498 SkAutoTMalloc<uint8_t> fStorage; 499 size_t fStorageSize; 500 size_t fStorageUsed; 501 502 SkRect fBounds; 503 int fRunCount; 504 bool fDeferredBounds; 505 size_t fLastRun; // index into fStorage 506 507 RunBuffer fCurrentRunBuffer; 508 }; 509 510 /** function GetGlyphIDforTextBlob: 511 Obtain the path of the first glyph from SkTextBlob blob 512 */ 513 SK_API void GetGlyphIDforTextBlob(const SkTextBlob* blob, std::vector<SkGlyphID>& glyphIds); 514 515 SK_API SkPath GetPathforTextBlob(const SkGlyphID& glyphId, const SkTextBlob* blob); 516 517 SK_API void GetPointsForTextBlob(const SkTextBlob* blob, std::vector<SkPoint>& points); 518 519 #endif // SkTextBlob_DEFINED 520