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