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 #include "SkTextBlobRunIterator.h" 9 10 #include "SkReadBuffer.h" 11 #include "SkTypeface.h" 12 #include "SkWriteBuffer.h" 13 14 #if SK_SUPPORT_GPU 15 #include "text/GrTextBlobCache.h" 16 #endif 17 18 namespace { 19 20 // TODO(fmalita): replace with SkFont. 21 class RunFont : SkNoncopyable { 22 public: 23 RunFont(const SkPaint& paint) 24 : fSize(paint.getTextSize()) 25 , fScaleX(paint.getTextScaleX()) 26 , fTypeface(SkSafeRef(paint.getTypeface())) 27 , fSkewX(paint.getTextSkewX()) 28 , fAlign(paint.getTextAlign()) 29 , fHinting(paint.getHinting()) 30 , fFlags(paint.getFlags() & kFlagsMask) { } 31 32 void applyToPaint(SkPaint* paint) const { 33 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); 34 paint->setTypeface(fTypeface); 35 paint->setTextSize(fSize); 36 paint->setTextScaleX(fScaleX); 37 paint->setTextSkewX(fSkewX); 38 paint->setTextAlign(static_cast<SkPaint::Align>(fAlign)); 39 paint->setHinting(static_cast<SkPaint::Hinting>(fHinting)); 40 41 paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags); 42 } 43 44 bool operator==(const RunFont& other) const { 45 return fTypeface == other.fTypeface 46 && fSize == other.fSize 47 && fScaleX == other.fScaleX 48 && fSkewX == other.fSkewX 49 && fAlign == other.fAlign 50 && fHinting == other.fHinting 51 && fFlags == other.fFlags; 52 } 53 54 bool operator!=(const RunFont& other) const { 55 return !(*this == other); 56 } 57 58 uint32_t flags() const { return fFlags; } 59 60 private: 61 const static uint32_t kFlagsMask = 62 SkPaint::kAntiAlias_Flag | 63 SkPaint::kFakeBoldText_Flag | 64 SkPaint::kLinearText_Flag | 65 SkPaint::kSubpixelText_Flag | 66 SkPaint::kDevKernText_Flag | 67 SkPaint::kLCDRenderText_Flag | 68 SkPaint::kEmbeddedBitmapText_Flag | 69 SkPaint::kAutoHinting_Flag | 70 SkPaint::kVerticalText_Flag | 71 SkPaint::kGenA8FromLCD_Flag; 72 73 SkScalar fSize; 74 SkScalar fScaleX; 75 76 // Keep this sk_sp off the first position, to avoid interfering with SkNoncopyable 77 // empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694). 78 sk_sp<SkTypeface> fTypeface; 79 SkScalar fSkewX; 80 81 static_assert(SkPaint::kAlignCount < 4, "insufficient_align_bits"); 82 uint32_t fAlign : 2; 83 static_assert(SkPaint::kFull_Hinting < 4, "insufficient_hinting_bits"); 84 uint32_t fHinting : 2; 85 static_assert((kFlagsMask & 0xffff) == kFlagsMask, "insufficient_flags_bits"); 86 uint32_t fFlags : 16; 87 88 typedef SkNoncopyable INHERITED; 89 }; 90 91 struct RunFontStorageEquivalent { 92 SkScalar fSize, fScaleX; 93 void* fTypeface; 94 SkScalar fSkewX; 95 uint32_t fFlags; 96 }; 97 static_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed"); 98 99 } // anonymous namespace 100 101 // 102 // Textblob data is laid out into externally-managed storage as follows: 103 // 104 // ----------------------------------------------------------------------------- 105 // | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ... 106 // ----------------------------------------------------------------------------- 107 // 108 // Each run record describes a text blob run, and can be used to determine the (implicit) 109 // location of the following record. 110 // 111 // Extended Textblob runs have more data after the Pos[] array: 112 // 113 // ------------------------------------------------------------------------- 114 // ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ... 115 // ------------------------------------------------------------------------- 116 // 117 // To determine the length of the extended run data, the TextSize must be read. 118 // 119 // Extended Textblob runs may be mixed with non-extended runs. 120 121 SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;) 122 123 namespace { 124 struct RunRecordStorageEquivalent { 125 RunFont fFont; 126 SkPoint fOffset; 127 uint32_t fCount; 128 uint32_t fFlags; 129 SkDEBUGCODE(unsigned fMagic;) 130 }; 131 } 132 133 class SkTextBlob::RunRecord { 134 public: 135 RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos) 136 : fFont(font) 137 , fCount(count) 138 , fOffset(offset) 139 , fFlags(pos) { 140 SkASSERT(static_cast<unsigned>(pos) <= Flags::kPositioning_Mask); 141 142 SkDEBUGCODE(fMagic = kRunRecordMagic); 143 if (textSize > 0) { 144 fFlags |= kExtended_Flag; 145 *this->textSizePtr() = textSize; 146 } 147 } 148 149 uint32_t glyphCount() const { 150 return fCount; 151 } 152 153 const SkPoint& offset() const { 154 return fOffset; 155 } 156 157 const RunFont& font() const { 158 return fFont; 159 } 160 161 GlyphPositioning positioning() const { 162 return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask); 163 } 164 165 uint16_t* glyphBuffer() const { 166 static_assert(SkIsAlignPtr(sizeof(RunRecord)), ""); 167 // Glyphs are stored immediately following the record. 168 return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1); 169 } 170 171 SkScalar* posBuffer() const { 172 // Position scalars follow the (aligned) glyph buffer. 173 return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) + 174 SkAlign4(fCount * sizeof(uint16_t))); 175 } 176 177 uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; } 178 179 uint32_t* clusterBuffer() const { 180 // clusters follow the textSize. 181 return isExtended() ? 1 + this->textSizePtr() : nullptr; 182 } 183 184 char* textBuffer() const { 185 return isExtended() 186 ? reinterpret_cast<char*>(this->clusterBuffer() + fCount) 187 : nullptr; 188 } 189 190 static size_t StorageSize(int glyphCount, int textSize, 191 SkTextBlob::GlyphPositioning positioning) { 192 static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment"); 193 // RunRecord object + (aligned) glyph buffer + position buffer 194 size_t size = sizeof(SkTextBlob::RunRecord) 195 + SkAlign4(glyphCount* sizeof(uint16_t)) 196 + PosCount(glyphCount, positioning) * sizeof(SkScalar); 197 if (textSize > 0) { // Extended run. 198 size += sizeof(uint32_t) 199 + sizeof(uint32_t) * glyphCount 200 + textSize; 201 } 202 return SkAlignPtr(size); 203 } 204 205 static const RunRecord* First(const SkTextBlob* blob) { 206 // The first record (if present) is stored following the blob object. 207 return reinterpret_cast<const RunRecord*>(blob + 1); 208 } 209 210 static const RunRecord* Next(const RunRecord* run) { 211 return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run); 212 } 213 214 void validate(const uint8_t* storageTop) const { 215 SkASSERT(kRunRecordMagic == fMagic); 216 SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop); 217 218 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer()); 219 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning()) 220 <= (SkScalar*)NextUnchecked(this)); 221 if (isExtended()) { 222 SkASSERT(textSize() > 0); 223 SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this)); 224 SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this)); 225 SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this)); 226 } 227 static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent), 228 "runrecord_should_stay_packed"); 229 } 230 231 private: 232 friend class SkTextBlobBuilder; 233 234 enum Flags { 235 kPositioning_Mask = 0x03, // bits 0-1 reserved for positioning 236 kLast_Flag = 0x04, // set for the last blob run 237 kExtended_Flag = 0x08, // set for runs with text/cluster info 238 }; 239 240 static const RunRecord* NextUnchecked(const RunRecord* run) { 241 return reinterpret_cast<const RunRecord*>( 242 reinterpret_cast<const uint8_t*>(run) 243 + StorageSize(run->glyphCount(), run->textSize(), run->positioning())); 244 } 245 246 static size_t PosCount(int glyphCount, 247 SkTextBlob::GlyphPositioning positioning) { 248 return glyphCount * ScalarsPerGlyph(positioning); 249 } 250 251 uint32_t* textSizePtr() const { 252 // textSize follows the position buffer. 253 SkASSERT(isExtended()); 254 return (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning())]); 255 } 256 257 void grow(uint32_t count) { 258 SkScalar* initialPosBuffer = posBuffer(); 259 uint32_t initialCount = fCount; 260 fCount += count; 261 262 // Move the initial pos scalars to their new location. 263 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning()); 264 SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this)); 265 266 // memmove, as the buffers may overlap 267 memmove(posBuffer(), initialPosBuffer, copySize); 268 } 269 270 bool isExtended() const { 271 return fFlags & kExtended_Flag; 272 } 273 274 RunFont fFont; 275 uint32_t fCount; 276 SkPoint fOffset; 277 uint32_t fFlags; 278 279 SkDEBUGCODE(unsigned fMagic;) 280 }; 281 282 static int32_t gNextID = 1; 283 static int32_t next_id() { 284 int32_t id; 285 do { 286 id = sk_atomic_inc(&gNextID); 287 } while (id == SK_InvalidGenID); 288 return id; 289 } 290 291 SkTextBlob::SkTextBlob(const SkRect& bounds) 292 : fBounds(bounds) 293 , fUniqueID(next_id()) 294 , fAddedToCache(false) {} 295 296 SkTextBlob::~SkTextBlob() { 297 #if SK_SUPPORT_GPU 298 if (fAddedToCache.load()) { 299 GrTextBlobCache::PostPurgeBlobMessage(fUniqueID); 300 } 301 #endif 302 303 const auto* run = RunRecord::First(this); 304 do { 305 const auto* nextRun = RunRecord::Next(run); 306 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);) 307 run->~RunRecord(); 308 run = nextRun; 309 } while (run); 310 } 311 312 namespace { 313 union PositioningAndExtended { 314 int32_t intValue; 315 struct { 316 SkTextBlob::GlyphPositioning positioning; 317 bool extended; 318 uint16_t padding; 319 }; 320 }; 321 } // namespace 322 323 void SkTextBlob::flatten(SkWriteBuffer& buffer) const { 324 buffer.writeRect(fBounds); 325 326 SkPaint runPaint; 327 SkTextBlobRunIterator it(this); 328 while (!it.done()) { 329 SkASSERT(it.glyphCount() > 0); 330 331 buffer.write32(it.glyphCount()); 332 PositioningAndExtended pe; 333 pe.intValue = 0; 334 pe.positioning = it.positioning(); 335 SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat. 336 337 uint32_t textSize = it.textSize(); 338 pe.extended = textSize > 0; 339 buffer.write32(pe.intValue); 340 if (pe.extended) { 341 buffer.write32(textSize); 342 } 343 buffer.writePoint(it.offset()); 344 // This should go away when switching to SkFont 345 it.applyFontToPaint(&runPaint); 346 buffer.writePaint(runPaint); 347 348 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t)); 349 buffer.writeByteArray(it.pos(), 350 it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning())); 351 if (pe.extended) { 352 buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount()); 353 buffer.writeByteArray(it.text(), it.textSize()); 354 } 355 356 it.next(); 357 } 358 359 // Marker for the last run (0 is not a valid glyph count). 360 buffer.write32(0); 361 } 362 363 sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) { 364 const int runCount = reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version) 365 ? reader.read32() : std::numeric_limits<int>::max(); 366 if (runCount < 0) { 367 return nullptr; 368 } 369 370 SkRect bounds; 371 reader.readRect(&bounds); 372 373 SkTextBlobBuilder blobBuilder; 374 for (int i = 0; i < runCount; ++i) { 375 int glyphCount = reader.read32(); 376 if (glyphCount == 0 && 377 !reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)) { 378 // End-of-runs marker. 379 break; 380 } 381 382 PositioningAndExtended pe; 383 pe.intValue = reader.read32(); 384 GlyphPositioning pos = pe.positioning; 385 if (glyphCount <= 0 || pos > kFull_Positioning) { 386 return nullptr; 387 } 388 uint32_t textSize = pe.extended ? (uint32_t)reader.read32() : 0; 389 390 SkPoint offset; 391 reader.readPoint(&offset); 392 SkPaint font; 393 reader.readPaint(&font); 394 395 const SkTextBlobBuilder::RunBuffer* buf = nullptr; 396 switch (pos) { 397 case kDefault_Positioning: 398 buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(), 399 textSize, SkString(), &bounds); 400 break; 401 case kHorizontal_Positioning: 402 buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(), 403 textSize, SkString(), &bounds); 404 break; 405 case kFull_Positioning: 406 buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds); 407 break; 408 default: 409 return nullptr; 410 } 411 412 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) || 413 !reader.readByteArray(buf->pos, 414 glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) { 415 return nullptr; 416 } 417 418 if (pe.extended) { 419 if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t)) || 420 !reader.readByteArray(buf->utf8text, textSize)) { 421 return nullptr; 422 } 423 } 424 } 425 426 return blobBuilder.make(); 427 } 428 429 unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) { 430 // GlyphPositioning values are directly mapped to scalars-per-glyph. 431 SkASSERT(pos <= 2); 432 return pos; 433 } 434 435 SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob) 436 : fCurrentRun(SkTextBlob::RunRecord::First(blob)) { 437 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;) 438 } 439 440 bool SkTextBlobRunIterator::done() const { 441 return !fCurrentRun; 442 } 443 444 void SkTextBlobRunIterator::next() { 445 SkASSERT(!this->done()); 446 447 if (!this->done()) { 448 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);) 449 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun); 450 } 451 } 452 453 uint32_t SkTextBlobRunIterator::glyphCount() const { 454 SkASSERT(!this->done()); 455 return fCurrentRun->glyphCount(); 456 } 457 458 const uint16_t* SkTextBlobRunIterator::glyphs() const { 459 SkASSERT(!this->done()); 460 return fCurrentRun->glyphBuffer(); 461 } 462 463 const SkScalar* SkTextBlobRunIterator::pos() const { 464 SkASSERT(!this->done()); 465 return fCurrentRun->posBuffer(); 466 } 467 468 const SkPoint& SkTextBlobRunIterator::offset() const { 469 SkASSERT(!this->done()); 470 return fCurrentRun->offset(); 471 } 472 473 SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const { 474 SkASSERT(!this->done()); 475 return fCurrentRun->positioning(); 476 } 477 478 void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const { 479 SkASSERT(!this->done()); 480 481 fCurrentRun->font().applyToPaint(paint); 482 } 483 484 uint32_t* SkTextBlobRunIterator::clusters() const { 485 SkASSERT(!this->done()); 486 return fCurrentRun->clusterBuffer(); 487 } 488 uint32_t SkTextBlobRunIterator::textSize() const { 489 SkASSERT(!this->done()); 490 return fCurrentRun->textSize(); 491 } 492 char* SkTextBlobRunIterator::text() const { 493 SkASSERT(!this->done()); 494 return fCurrentRun->textBuffer(); 495 } 496 497 498 bool SkTextBlobRunIterator::isLCD() const { 499 return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag); 500 } 501 502 SkTextBlobBuilder::SkTextBlobBuilder() 503 : fStorageSize(0) 504 , fStorageUsed(0) 505 , fRunCount(0) 506 , fDeferredBounds(false) 507 , fLastRun(0) { 508 fBounds.setEmpty(); 509 } 510 511 SkTextBlobBuilder::~SkTextBlobBuilder() { 512 if (nullptr != fStorage.get()) { 513 // We are abandoning runs and must destruct the associated font data. 514 // The easiest way to accomplish that is to use the blob destructor. 515 this->make(); 516 } 517 } 518 519 SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) { 520 SkRect bounds; 521 SkPaint paint; 522 run.font().applyToPaint(&paint); 523 524 if (SkTextBlob::kDefault_Positioning == run.positioning()) { 525 paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds); 526 return bounds.makeOffset(run.offset().x(), run.offset().y()); 527 } 528 529 SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount()); 530 paint.getTextWidths(run.glyphBuffer(), 531 run.glyphCount() * sizeof(uint16_t), 532 NULL, 533 glyphBounds.get()); 534 535 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() || 536 SkTextBlob::kHorizontal_Positioning == run.positioning()); 537 // kFull_Positioning => [ x, y, x, y... ] 538 // kHorizontal_Positioning => [ x, x, x... ] 539 // (const y applied by runBounds.offset(run->offset()) later) 540 const SkScalar horizontalConstY = 0; 541 const SkScalar* glyphPosX = run.posBuffer(); 542 const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ? 543 glyphPosX + 1 : &horizontalConstY; 544 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning()); 545 const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ? 546 posXInc : 0; 547 548 bounds.setEmpty(); 549 for (unsigned i = 0; i < run.glyphCount(); ++i) { 550 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY)); 551 glyphPosX += posXInc; 552 glyphPosY += posYInc; 553 } 554 555 SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run)); 556 557 return bounds.makeOffset(run.offset().x(), run.offset().y()); 558 } 559 560 SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) { 561 SkASSERT(run.glyphCount() > 0); 562 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() || 563 SkTextBlob::kHorizontal_Positioning == run.positioning()); 564 565 SkPaint paint; 566 run.font().applyToPaint(&paint); 567 const SkRect fontBounds = paint.getFontBounds(); 568 if (fontBounds.isEmpty()) { 569 // Empty font bounds are likely a font bug. TightBounds has a better chance of 570 // producing useful results in this case. 571 return TightRunBounds(run); 572 } 573 574 // Compute the glyph position bbox. 575 SkRect bounds; 576 switch (run.positioning()) { 577 case SkTextBlob::kHorizontal_Positioning: { 578 const SkScalar* glyphPos = run.posBuffer(); 579 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); 580 581 SkScalar minX = *glyphPos; 582 SkScalar maxX = *glyphPos; 583 for (unsigned i = 1; i < run.glyphCount(); ++i) { 584 SkScalar x = glyphPos[i]; 585 minX = SkMinScalar(x, minX); 586 maxX = SkMaxScalar(x, maxX); 587 } 588 589 bounds.setLTRB(minX, 0, maxX, 0); 590 } break; 591 case SkTextBlob::kFull_Positioning: { 592 const SkPoint* glyphPosPts = reinterpret_cast<const SkPoint*>(run.posBuffer()); 593 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); 594 595 bounds.setBounds(glyphPosPts, run.glyphCount()); 596 } break; 597 default: 598 SkFAIL("unsupported positioning mode"); 599 } 600 601 // Expand by typeface glyph bounds. 602 bounds.fLeft += fontBounds.left(); 603 bounds.fTop += fontBounds.top(); 604 bounds.fRight += fontBounds.right(); 605 bounds.fBottom += fontBounds.bottom(); 606 607 // Offset by run position. 608 return bounds.makeOffset(run.offset().x(), run.offset().y()); 609 } 610 611 void SkTextBlobBuilder::updateDeferredBounds() { 612 SkASSERT(!fDeferredBounds || fRunCount > 0); 613 614 if (!fDeferredBounds) { 615 return; 616 } 617 618 SkASSERT(fLastRun >= sizeof(SkTextBlob)); 619 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + 620 fLastRun); 621 622 // FIXME: we should also use conservative bounds for kDefault_Positioning. 623 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ? 624 TightRunBounds(*run) : ConservativeRunBounds(*run); 625 fBounds.join(runBounds); 626 fDeferredBounds = false; 627 } 628 629 void SkTextBlobBuilder::reserve(size_t size) { 630 // We don't currently pre-allocate, but maybe someday... 631 if (fStorageUsed + size <= fStorageSize) { 632 return; 633 } 634 635 if (0 == fRunCount) { 636 SkASSERT(nullptr == fStorage.get()); 637 SkASSERT(0 == fStorageSize); 638 SkASSERT(0 == fStorageUsed); 639 640 // the first allocation also includes blob storage 641 fStorageUsed += sizeof(SkTextBlob); 642 } 643 644 fStorageSize = fStorageUsed + size; 645 // FYI: This relies on everything we store being relocatable, particularly SkPaint. 646 fStorage.realloc(fStorageSize); 647 } 648 649 bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning, 650 int count, SkPoint offset) { 651 if (0 == fLastRun) { 652 SkASSERT(0 == fRunCount); 653 return false; 654 } 655 656 SkASSERT(fLastRun >= sizeof(SkTextBlob)); 657 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + 658 fLastRun); 659 SkASSERT(run->glyphCount() > 0); 660 661 if (run->textSize() != 0) { 662 return false; 663 } 664 665 if (run->positioning() != positioning 666 || run->font() != font 667 || (run->glyphCount() + count < run->glyphCount())) { 668 return false; 669 } 670 671 // we can merge same-font/same-positioning runs in the following cases: 672 // * fully positioned run following another fully positioned run 673 // * horizontally postioned run following another horizontally positioned run with the same 674 // y-offset 675 if (SkTextBlob::kFull_Positioning != positioning 676 && (SkTextBlob::kHorizontal_Positioning != positioning 677 || run->offset().y() != offset.y())) { 678 return false; 679 } 680 681 size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning) - 682 SkTextBlob::RunRecord::StorageSize(run->glyphCount(), 0, positioning); 683 this->reserve(sizeDelta); 684 685 // reserve may have realloced 686 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); 687 uint32_t preMergeCount = run->glyphCount(); 688 run->grow(count); 689 690 // Callers expect the buffers to point at the newly added slice, ant not at the beginning. 691 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount; 692 fCurrentRunBuffer.pos = run->posBuffer() 693 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning); 694 695 fStorageUsed += sizeDelta; 696 697 SkASSERT(fStorageUsed <= fStorageSize); 698 run->validate(fStorage.get() + fStorageUsed); 699 700 return true; 701 } 702 703 void SkTextBlobBuilder::allocInternal(const SkPaint &font, 704 SkTextBlob::GlyphPositioning positioning, 705 int count, int textSize, SkPoint offset, const SkRect* bounds) { 706 SkASSERT(count > 0); 707 SkASSERT(textSize >= 0); 708 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding()); 709 if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) { 710 this->updateDeferredBounds(); 711 712 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning); 713 this->reserve(runSize); 714 715 SkASSERT(fStorageUsed >= sizeof(SkTextBlob)); 716 SkASSERT(fStorageUsed + runSize <= fStorageSize); 717 718 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed) 719 SkTextBlob::RunRecord(count, textSize, offset, font, positioning); 720 fCurrentRunBuffer.glyphs = run->glyphBuffer(); 721 fCurrentRunBuffer.pos = run->posBuffer(); 722 fCurrentRunBuffer.utf8text = run->textBuffer(); 723 fCurrentRunBuffer.clusters = run->clusterBuffer(); 724 725 fLastRun = fStorageUsed; 726 fStorageUsed += runSize; 727 fRunCount++; 728 729 SkASSERT(fStorageUsed <= fStorageSize); 730 run->validate(fStorage.get() + fStorageUsed); 731 } 732 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text); 733 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters); 734 if (!fDeferredBounds) { 735 if (bounds) { 736 fBounds.join(*bounds); 737 } else { 738 fDeferredBounds = true; 739 } 740 } 741 } 742 743 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkPaint& font, int count, 744 SkScalar x, SkScalar y, 745 int textByteCount, 746 SkString lang, 747 const SkRect* bounds) { 748 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, textByteCount, SkPoint::Make(x, y), bounds); 749 return fCurrentRunBuffer; 750 } 751 752 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkPaint& font, int count, 753 SkScalar y, 754 int textByteCount, 755 SkString lang, 756 const SkRect* bounds) { 757 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, textByteCount, SkPoint::Make(0, y), 758 bounds); 759 760 return fCurrentRunBuffer; 761 } 762 763 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count, 764 int textByteCount, 765 SkString lang, 766 const SkRect *bounds) { 767 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds); 768 769 return fCurrentRunBuffer; 770 } 771 772 sk_sp<SkTextBlob> SkTextBlobBuilder::make() { 773 if (!fRunCount) { 774 // We don't instantiate empty blobs. 775 SkASSERT(!fStorage.get()); 776 SkASSERT(fStorageUsed == 0); 777 SkASSERT(fStorageSize == 0); 778 SkASSERT(fLastRun == 0); 779 SkASSERT(fBounds.isEmpty()); 780 return nullptr; 781 } 782 783 this->updateDeferredBounds(); 784 785 // Tag the last run as such. 786 auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); 787 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag; 788 789 SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds); 790 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;) 791 792 SkDEBUGCODE( 793 size_t validateSize = sizeof(SkTextBlob); 794 for (const auto* run = SkTextBlob::RunRecord::First(blob); run; 795 run = SkTextBlob::RunRecord::Next(run)) { 796 validateSize += SkTextBlob::RunRecord::StorageSize( 797 run->fCount, run->textSize(), run->positioning()); 798 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed); 799 fRunCount--; 800 } 801 SkASSERT(validateSize == fStorageUsed); 802 SkASSERT(fRunCount == 0); 803 ) 804 805 fStorageUsed = 0; 806 fStorageSize = 0; 807 fRunCount = 0; 808 fLastRun = 0; 809 fBounds.setEmpty(); 810 811 return sk_sp<SkTextBlob>(blob); 812 } 813