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 "include/core/SkRSXform.h" 9#include "include/core/SkTextBlob.h" 10#include "include/core/SkTypeface.h" 11#include "src/core/SkFontPriv.h" 12#include "src/core/SkGlyphRun.h" 13#include "src/core/SkPaintPriv.h" 14#include "src/core/SkReadBuffer.h" 15#include "src/core/SkSafeMath.h" 16#include "src/core/SkStrikeCache.h" 17#include "src/core/SkStrikeSpec.h" 18#include "src/core/SkTextBlobPriv.h" 19#include "src/core/SkWriteBuffer.h" 20 21#include <atomic> 22#include <limits> 23#include <new> 24 25#if SK_SUPPORT_GPU 26#include "src/gpu/text/GrTextBlobCache.h" 27#endif 28 29namespace { 30struct RunFontStorageEquivalent { 31 SkScalar fSize, fScaleX; 32 void* fTypeface; 33 SkScalar fSkewX; 34 uint32_t fFlags; 35}; 36static_assert(sizeof(SkFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed"); 37} // namespace 38 39size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize, 40 SkTextBlob::GlyphPositioning positioning, 41 SkSafeMath* safe) { 42 static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment"); 43 44 auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)), 45 posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar)); 46 47 // RunRecord object + (aligned) glyph buffer + position buffer 48 auto size = sizeof(SkTextBlob::RunRecord); 49 size = safe->add(size, safe->alignUp(glyphSize, 4)); 50 size = safe->add(size, posSize); 51 52 if (textSize) { // Extended run. 53 size = safe->add(size, sizeof(uint32_t)); 54 size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t))); 55 size = safe->add(size, textSize); 56 } 57 58 return safe->alignUp(size, sizeof(void*)); 59} 60 61const SkTextBlob::RunRecord* SkTextBlob::RunRecord::First(const SkTextBlob* blob) { 62 // The first record (if present) is stored following the blob object. 63 // (aligned up to make the RunRecord aligned too) 64 return reinterpret_cast<const RunRecord*>(SkAlignPtr((uintptr_t)(blob + 1))); 65} 66 67const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(const RunRecord* run) { 68 return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run); 69} 70 71namespace { 72struct RunRecordStorageEquivalent { 73 SkFont fFont; 74 SkPoint fOffset; 75 uint32_t fCount; 76 uint32_t fFlags; 77 SkDEBUGCODE(unsigned fMagic;) 78}; 79} // namespace 80 81void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const { 82 SkASSERT(kRunRecordMagic == fMagic); 83 SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop); 84 85 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer()); 86 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning()) 87 <= (SkScalar*)NextUnchecked(this)); 88 if (isExtended()) { 89 SkASSERT(textSize() > 0); 90 SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this)); 91 SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this)); 92 SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this)); 93 } 94 static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent), 95 "runrecord_should_stay_packed"); 96} 97 98const SkTextBlob::RunRecord* SkTextBlob::RunRecord::NextUnchecked(const RunRecord* run) { 99 SkSafeMath safe; 100 auto res = reinterpret_cast<const RunRecord*>( 101 reinterpret_cast<const uint8_t*>(run) 102 + StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe)); 103 SkASSERT(safe); 104 return res; 105} 106 107size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount, 108 SkTextBlob::GlyphPositioning positioning, 109 SkSafeMath* safe) { 110 return safe->mul(glyphCount, ScalarsPerGlyph(positioning)); 111} 112 113uint32_t* SkTextBlob::RunRecord::textSizePtr() const { 114 // textSize follows the position buffer. 115 SkASSERT(isExtended()); 116 SkSafeMath safe; 117 auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]); 118 SkASSERT(safe); 119 return res; 120} 121 122void SkTextBlob::RunRecord::grow(uint32_t count) { 123 SkScalar* initialPosBuffer = posBuffer(); 124 uint32_t initialCount = fCount; 125 fCount += count; 126 127 // Move the initial pos scalars to their new location. 128 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning()); 129 SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this)); 130 131 // memmove, as the buffers may overlap 132 memmove(posBuffer(), initialPosBuffer, copySize); 133} 134 135static int32_t next_id() { 136 static std::atomic<int32_t> nextID{1}; 137 int32_t id; 138 do { 139 id = nextID.fetch_add(1, std::memory_order_relaxed); 140 } while (id == SK_InvalidGenID); 141 return id; 142} 143 144SkTextBlob::SkTextBlob(const SkRect& bounds) 145 : fBounds(bounds) 146 , fUniqueID(next_id()) 147 , fCacheID(SK_InvalidUniqueID) {} 148 149SkTextBlob::~SkTextBlob() { 150#if SK_SUPPORT_GPU 151 if (SK_InvalidUniqueID != fCacheID.load()) { 152 GrTextBlobCache::PostPurgeBlobMessage(fUniqueID, fCacheID); 153 } 154#endif 155 156 const auto* run = RunRecord::First(this); 157 do { 158 const auto* nextRun = RunRecord::Next(run); 159 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);) 160 run->~RunRecord(); 161 run = nextRun; 162 } while (run); 163} 164 165namespace { 166 167union PositioningAndExtended { 168 int32_t intValue; 169 struct { 170 uint8_t positioning; 171 uint8_t extended; 172 uint16_t padding; 173 }; 174}; 175 176static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), ""); 177 178} // namespace 179 180enum SkTextBlob::GlyphPositioning : uint8_t { 181 kDefault_Positioning = 0, // Default glyph advances -- zero scalars per glyph. 182 kHorizontal_Positioning = 1, // Horizontal positioning -- one scalar per glyph. 183 kFull_Positioning = 2, // Point positioning -- two scalars per glyph. 184 kRSXform_Positioning = 3, // RSXform positioning -- four scalars per glyph. 185}; 186 187unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) { 188 const uint8_t gScalarsPerPositioning[] = { 189 0, // kDefault_Positioning 190 1, // kHorizontal_Positioning 191 2, // kFull_Positioning 192 4, // kRSXform_Positioning 193 }; 194 SkASSERT((unsigned)pos <= 3); 195 return gScalarsPerPositioning[pos]; 196} 197 198void SkTextBlob::operator delete(void* p) { 199 sk_free(p); 200} 201 202void* SkTextBlob::operator new(size_t) { 203 SK_ABORT("All blobs are created by placement new."); 204} 205 206void* SkTextBlob::operator new(size_t, void* p) { 207 return p; 208} 209 210SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob) 211 : fCurrentRun(SkTextBlob::RunRecord::First(blob)) { 212 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;) 213} 214 215void SkTextBlobRunIterator::next() { 216 SkASSERT(!this->done()); 217 218 if (!this->done()) { 219 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);) 220 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun); 221 } 222} 223 224SkTextBlobRunIterator::GlyphPositioning SkTextBlobRunIterator::positioning() const { 225 SkASSERT(!this->done()); 226 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kDefault_Positioning) == 227 kDefault_Positioning, ""); 228 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kHorizontal_Positioning) == 229 kHorizontal_Positioning, ""); 230 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kFull_Positioning) == 231 kFull_Positioning, ""); 232 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kRSXform_Positioning) == 233 kRSXform_Positioning, ""); 234 235 return SkTo<GlyphPositioning>(fCurrentRun->positioning()); 236} 237 238unsigned SkTextBlobRunIterator::scalarsPerGlyph() const { 239 return SkTextBlob::ScalarsPerGlyph(fCurrentRun->positioning()); 240} 241 242bool SkTextBlobRunIterator::isLCD() const { 243 return fCurrentRun->font().getEdging() == SkFont::Edging::kSubpixelAntiAlias; 244} 245 246SkTextBlobBuilder::SkTextBlobBuilder() 247 : fStorageSize(0) 248 , fStorageUsed(0) 249 , fRunCount(0) 250 , fDeferredBounds(false) 251 , fLastRun(0) { 252 fBounds.setEmpty(); 253} 254 255SkTextBlobBuilder::~SkTextBlobBuilder() { 256 if (nullptr != fStorage.get()) { 257 // We are abandoning runs and must destruct the associated font data. 258 // The easiest way to accomplish that is to use the blob destructor. 259 this->make(); 260 } 261} 262 263static SkRect map_quad_to_rect(const SkRSXform& xform, const SkRect& rect) { 264 return SkMatrix().setRSXform(xform).mapRect(rect); 265} 266 267SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) { 268 const SkFont& font = run.font(); 269 SkRect bounds; 270 271 if (SkTextBlob::kDefault_Positioning == run.positioning()) { 272 font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), 273 SkTextEncoding::kGlyphID, &bounds); 274 return bounds.makeOffset(run.offset().x(), run.offset().y()); 275 } 276 277 SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount()); 278 font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr); 279 280 if (SkTextBlob::kRSXform_Positioning == run.positioning()) { 281 bounds.setEmpty(); 282 const SkRSXform* xform = run.xformBuffer(); 283 SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); 284 for (unsigned i = 0; i < run.glyphCount(); ++i) { 285 bounds.join(map_quad_to_rect(xform[i], glyphBounds[i])); 286 } 287 } else { 288 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() || 289 SkTextBlob::kHorizontal_Positioning == run.positioning()); 290 // kFull_Positioning => [ x, y, x, y... ] 291 // kHorizontal_Positioning => [ x, x, x... ] 292 // (const y applied by runBounds.offset(run->offset()) later) 293 const SkScalar horizontalConstY = 0; 294 const SkScalar* glyphPosX = run.posBuffer(); 295 const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ? 296 glyphPosX + 1 : &horizontalConstY; 297 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning()); 298 const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ? 299 posXInc : 0; 300 301 bounds.setEmpty(); 302 for (unsigned i = 0; i < run.glyphCount(); ++i) { 303 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY)); 304 glyphPosX += posXInc; 305 glyphPosY += posYInc; 306 } 307 308 SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run)); 309 } 310 return bounds.makeOffset(run.offset().x(), run.offset().y()); 311} 312 313SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) { 314 SkASSERT(run.glyphCount() > 0); 315 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() || 316 SkTextBlob::kHorizontal_Positioning == run.positioning() || 317 SkTextBlob::kRSXform_Positioning == run.positioning()); 318 319 const SkRect fontBounds = SkFontPriv::GetFontBounds(run.font()); 320 if (fontBounds.isEmpty()) { 321 // Empty font bounds are likely a font bug. TightBounds has a better chance of 322 // producing useful results in this case. 323 return TightRunBounds(run); 324 } 325 326 // Compute the glyph position bbox. 327 SkRect bounds; 328 switch (run.positioning()) { 329 case SkTextBlob::kHorizontal_Positioning: { 330 const SkScalar* glyphPos = run.posBuffer(); 331 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); 332 333 SkScalar minX = *glyphPos; 334 SkScalar maxX = *glyphPos; 335 for (unsigned i = 1; i < run.glyphCount(); ++i) { 336 SkScalar x = glyphPos[i]; 337 minX = std::min(x, minX); 338 maxX = std::max(x, maxX); 339 } 340 341 bounds.setLTRB(minX, 0, maxX, 0); 342 } break; 343 case SkTextBlob::kFull_Positioning: { 344 const SkPoint* glyphPosPts = run.pointBuffer(); 345 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); 346 347 bounds.setBounds(glyphPosPts, run.glyphCount()); 348 } break; 349 case SkTextBlob::kRSXform_Positioning: { 350 const SkRSXform* xform = run.xformBuffer(); 351 SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); 352 bounds.setEmpty(); 353 for (unsigned i = 0; i < run.glyphCount(); ++i) { 354 bounds.join(map_quad_to_rect(xform[i], fontBounds)); 355 } 356 } break; 357 default: 358 SK_ABORT("unsupported positioning mode"); 359 } 360 361 if (run.positioning() != SkTextBlob::kRSXform_Positioning) { 362 // Expand by typeface glyph bounds. 363 bounds.fLeft += fontBounds.left(); 364 bounds.fTop += fontBounds.top(); 365 bounds.fRight += fontBounds.right(); 366 bounds.fBottom += fontBounds.bottom(); 367 } 368 369 // Offset by run position. 370 return bounds.makeOffset(run.offset().x(), run.offset().y()); 371} 372 373void SkTextBlobBuilder::updateDeferredBounds() { 374 SkASSERT(!fDeferredBounds || fRunCount > 0); 375 376 if (!fDeferredBounds) { 377 return; 378 } 379 380 SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob))); 381 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + 382 fLastRun); 383 384 // FIXME: we should also use conservative bounds for kDefault_Positioning. 385 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ? 386 TightRunBounds(*run) : ConservativeRunBounds(*run); 387 fBounds.join(runBounds); 388 fDeferredBounds = false; 389} 390 391void SkTextBlobBuilder::reserve(size_t size) { 392 SkSafeMath safe; 393 394 // We don't currently pre-allocate, but maybe someday... 395 if (safe.add(fStorageUsed, size) <= fStorageSize && safe) { 396 return; 397 } 398 399 if (0 == fRunCount) { 400 SkASSERT(nullptr == fStorage.get()); 401 SkASSERT(0 == fStorageSize); 402 SkASSERT(0 == fStorageUsed); 403 404 // the first allocation also includes blob storage 405 // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned. 406 fStorageUsed = SkAlignPtr(sizeof(SkTextBlob)); 407 } 408 409 fStorageSize = safe.add(fStorageUsed, size); 410 411 // FYI: This relies on everything we store being relocatable, particularly SkPaint. 412 // Also, this is counting on the underlying realloc to throw when passed max(). 413 fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max()); 414} 415 416bool SkTextBlobBuilder::mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning, 417 uint32_t count, SkPoint offset) { 418 if (0 == fLastRun) { 419 SkASSERT(0 == fRunCount); 420 return false; 421 } 422 423 SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob))); 424 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + 425 fLastRun); 426 SkASSERT(run->glyphCount() > 0); 427 428 if (run->textSize() != 0) { 429 return false; 430 } 431 432 if (run->positioning() != positioning 433 || run->font() != font 434 || (run->glyphCount() + count < run->glyphCount())) { 435 return false; 436 } 437 438 // we can merge same-font/same-positioning runs in the following cases: 439 // * fully positioned run following another fully positioned run 440 // * horizontally postioned run following another horizontally positioned run with the same 441 // y-offset 442 if (SkTextBlob::kFull_Positioning != positioning 443 && (SkTextBlob::kHorizontal_Positioning != positioning 444 || run->offset().y() != offset.y())) { 445 return false; 446 } 447 448 SkSafeMath safe; 449 size_t sizeDelta = 450 SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) - 451 SkTextBlob::RunRecord::StorageSize(run->glyphCount() , 0, positioning, &safe); 452 if (!safe) { 453 return false; 454 } 455 456 this->reserve(sizeDelta); 457 458 // reserve may have realloced 459 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); 460 uint32_t preMergeCount = run->glyphCount(); 461 run->grow(count); 462 463 // Callers expect the buffers to point at the newly added slice, ant not at the beginning. 464 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount; 465 fCurrentRunBuffer.pos = run->posBuffer() 466 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning); 467 468 fStorageUsed += sizeDelta; 469 470 SkASSERT(fStorageUsed <= fStorageSize); 471 run->validate(fStorage.get() + fStorageUsed); 472 473 return true; 474} 475 476void SkTextBlobBuilder::allocInternal(const SkFont& font, 477 SkTextBlob::GlyphPositioning positioning, 478 int count, int textSize, SkPoint offset, 479 const SkRect* bounds) { 480 if (count <= 0 || textSize < 0) { 481 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr }; 482 return; 483 } 484 485 if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) { 486 this->updateDeferredBounds(); 487 488 SkSafeMath safe; 489 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe); 490 if (!safe) { 491 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr }; 492 return; 493 } 494 495 this->reserve(runSize); 496 497 SkASSERT(fStorageUsed >= SkAlignPtr(sizeof(SkTextBlob))); 498 SkASSERT(fStorageUsed + runSize <= fStorageSize); 499 500 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed) 501 SkTextBlob::RunRecord(count, textSize, offset, font, positioning); 502 fCurrentRunBuffer.glyphs = run->glyphBuffer(); 503 fCurrentRunBuffer.pos = run->posBuffer(); 504 fCurrentRunBuffer.utf8text = run->textBuffer(); 505 fCurrentRunBuffer.clusters = run->clusterBuffer(); 506 507 fLastRun = fStorageUsed; 508 fStorageUsed += runSize; 509 fRunCount++; 510 511 SkASSERT(fStorageUsed <= fStorageSize); 512 run->validate(fStorage.get() + fStorageUsed); 513 } 514 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text); 515 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters); 516 if (!fDeferredBounds) { 517 if (bounds) { 518 fBounds.join(*bounds); 519 } else { 520 fDeferredBounds = true; 521 } 522 } 523} 524 525// SkFont versions 526 527const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkFont& font, int count, 528 SkScalar x, SkScalar y, 529 const SkRect* bounds) { 530 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, 0, {x, y}, bounds); 531 return fCurrentRunBuffer; 532} 533 534const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkFont& font, int count, 535 SkScalar y, 536 const SkRect* bounds) { 537 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, 0, {0, y}, bounds); 538 return fCurrentRunBuffer; 539} 540 541const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkFont& font, int count, 542 const SkRect* bounds) { 543 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, 0, {0, 0}, bounds); 544 return fCurrentRunBuffer; 545} 546 547const SkTextBlobBuilder::RunBuffer& 548SkTextBlobBuilder::allocRunRSXform(const SkFont& font, int count) { 549 this->allocInternal(font, SkTextBlob::kRSXform_Positioning, count, 0, {0, 0}, nullptr); 550 return fCurrentRunBuffer; 551} 552 553const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkFont& font, int count, 554 SkScalar x, SkScalar y, 555 int textByteCount, 556 const SkRect* bounds) { 557 this->allocInternal(font, 558 SkTextBlob::kDefault_Positioning, 559 count, 560 textByteCount, 561 SkPoint::Make(x, y), 562 bounds); 563 return fCurrentRunBuffer; 564} 565 566const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkFont& font, 567 int count, 568 SkScalar y, 569 int textByteCount, 570 const SkRect* bounds) { 571 this->allocInternal(font, 572 SkTextBlob::kHorizontal_Positioning, 573 count, 574 textByteCount, 575 SkPoint::Make(0, y), 576 bounds); 577 return fCurrentRunBuffer; 578} 579 580const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkFont& font, 581 int count, 582 int textByteCount, 583 const SkRect *bounds) { 584 this->allocInternal(font, 585 SkTextBlob::kFull_Positioning, 586 count, textByteCount, 587 SkPoint::Make(0, 0), 588 bounds); 589 return fCurrentRunBuffer; 590} 591 592const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextRSXform(const SkFont& font, 593 int count, 594 int textByteCount, 595 const SkRect *bounds) { 596 this->allocInternal(font, 597 SkTextBlob::kRSXform_Positioning, 598 count, 599 textByteCount, 600 {0, 0}, 601 bounds); 602 return fCurrentRunBuffer; 603} 604 605sk_sp<SkTextBlob> SkTextBlobBuilder::make() { 606 if (!fRunCount) { 607 // We don't instantiate empty blobs. 608 SkASSERT(!fStorage.get()); 609 SkASSERT(fStorageUsed == 0); 610 SkASSERT(fStorageSize == 0); 611 SkASSERT(fLastRun == 0); 612 SkASSERT(fBounds.isEmpty()); 613 return nullptr; 614 } 615 616 this->updateDeferredBounds(); 617 618 // Tag the last run as such. 619 auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); 620 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag; 621 622 SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds); 623 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;) 624 625 SkDEBUGCODE( 626 SkSafeMath safe; 627 size_t validateSize = SkAlignPtr(sizeof(SkTextBlob)); 628 for (const auto* run = SkTextBlob::RunRecord::First(blob); run; 629 run = SkTextBlob::RunRecord::Next(run)) { 630 validateSize += SkTextBlob::RunRecord::StorageSize( 631 run->fCount, run->textSize(), run->positioning(), &safe); 632 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed); 633 fRunCount--; 634 } 635 SkASSERT(validateSize == fStorageUsed); 636 SkASSERT(fRunCount == 0); 637 SkASSERT(safe); 638 ) 639 640 fStorageUsed = 0; 641 fStorageSize = 0; 642 fRunCount = 0; 643 fLastRun = 0; 644 fBounds.setEmpty(); 645 646 return sk_sp<SkTextBlob>(blob); 647} 648 649/////////////////////////////////////////////////////////////////////////////////////////////////// 650 651void SkTextBlobPriv::Flatten(const SkTextBlob& blob, SkWriteBuffer& buffer) { 652 // seems like we could skip this, and just recompute bounds in unflatten, but 653 // some cc_unittests fail if we remove this... 654 buffer.writeRect(blob.bounds()); 655 656 SkTextBlobRunIterator it(&blob); 657 while (!it.done()) { 658 SkASSERT(it.glyphCount() > 0); 659 660 buffer.write32(it.glyphCount()); 661 PositioningAndExtended pe; 662 pe.intValue = 0; 663 pe.positioning = it.positioning(); 664 SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat. 665 666 uint32_t textSize = it.textSize(); 667 pe.extended = textSize > 0; 668 buffer.write32(pe.intValue); 669 if (pe.extended) { 670 buffer.write32(textSize); 671 } 672 buffer.writePoint(it.offset()); 673 674 SkFontPriv::Flatten(it.font(), buffer); 675 676 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t)); 677 buffer.writeByteArray(it.pos(), 678 it.glyphCount() * sizeof(SkScalar) * 679 SkTextBlob::ScalarsPerGlyph( 680 SkTo<SkTextBlob::GlyphPositioning>(it.positioning()))); 681 if (pe.extended) { 682 buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount()); 683 buffer.writeByteArray(it.text(), it.textSize()); 684 } 685 686 it.next(); 687 } 688 689 // Marker for the last run (0 is not a valid glyph count). 690 buffer.write32(0); 691} 692 693sk_sp<SkTextBlob> SkTextBlobPriv::MakeFromBuffer(SkReadBuffer& reader) { 694 SkRect bounds; 695 reader.readRect(&bounds); 696 697 SkTextBlobBuilder blobBuilder; 698 SkSafeMath safe; 699 for (;;) { 700 int glyphCount = reader.read32(); 701 if (glyphCount == 0) { 702 // End-of-runs marker. 703 break; 704 } 705 706 PositioningAndExtended pe; 707 pe.intValue = reader.read32(); 708 const auto pos = SkTo<SkTextBlob::GlyphPositioning>(pe.positioning); 709 if (glyphCount <= 0 || pos > SkTextBlob::kRSXform_Positioning) { 710 return nullptr; 711 } 712 int textSize = pe.extended ? reader.read32() : 0; 713 if (textSize < 0) { 714 return nullptr; 715 } 716 717 SkPoint offset; 718 reader.readPoint(&offset); 719 SkFont font; 720 SkFontPriv::Unflatten(&font, reader); 721 722 // Compute the expected size of the buffer and ensure we have enough to deserialize 723 // a run before allocating it. 724 const size_t glyphSize = safe.mul(glyphCount, sizeof(uint16_t)), 725 posSize = 726 safe.mul(glyphCount, safe.mul(sizeof(SkScalar), 727 SkTextBlob::ScalarsPerGlyph(pos))), 728 clusterSize = pe.extended ? safe.mul(glyphCount, sizeof(uint32_t)) : 0; 729 const size_t totalSize = 730 safe.add(safe.add(glyphSize, posSize), safe.add(clusterSize, textSize)); 731 732 if (!reader.isValid() || !safe || totalSize > reader.available()) { 733 return nullptr; 734 } 735 736 const SkTextBlobBuilder::RunBuffer* buf = nullptr; 737 switch (pos) { 738 case SkTextBlob::kDefault_Positioning: 739 buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(), 740 textSize, &bounds); 741 break; 742 case SkTextBlob::kHorizontal_Positioning: 743 buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(), 744 textSize, &bounds); 745 break; 746 case SkTextBlob::kFull_Positioning: 747 buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, &bounds); 748 break; 749 case SkTextBlob::kRSXform_Positioning: 750 buf = &blobBuilder.allocRunTextRSXform(font, glyphCount, textSize, &bounds); 751 break; 752 } 753 754 if (!buf->glyphs || 755 !buf->pos || 756 (pe.extended && (!buf->clusters || !buf->utf8text))) { 757 return nullptr; 758 } 759 760 if (!reader.readByteArray(buf->glyphs, glyphSize) || 761 !reader.readByteArray(buf->pos, posSize)) { 762 return nullptr; 763 } 764 765 if (pe.extended) { 766 if (!reader.readByteArray(buf->clusters, clusterSize) || 767 !reader.readByteArray(buf->utf8text, textSize)) { 768 return nullptr; 769 } 770 } 771 } 772 773 return blobBuilder.make(); 774} 775 776sk_sp<SkTextBlob> SkTextBlob::MakeFromText(const void* text, size_t byteLength, const SkFont& font, 777 SkTextEncoding encoding) { 778 // Note: we deliberately promote this to fully positioned blobs, since we'd have to pay the 779 // same cost down stream (i.e. computing bounds), so its cheaper to pay the cost once now. 780 const int count = font.countText(text, byteLength, encoding); 781 if (count < 1) { 782 return nullptr; 783 } 784 SkTextBlobBuilder builder; 785 auto buffer = builder.allocRunPos(font, count); 786 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); 787 font.getPos(buffer.glyphs, count, buffer.points(), {0, 0}); 788 return builder.make(); 789} 790 791sk_sp<SkTextBlob> SkTextBlob::MakeFromPosText(const void* text, size_t byteLength, 792 const SkPoint pos[], const SkFont& font, 793 SkTextEncoding encoding) { 794 const int count = font.countText(text, byteLength, encoding); 795 if (count < 1) { 796 return nullptr; 797 } 798 SkTextBlobBuilder builder; 799 auto buffer = builder.allocRunPos(font, count); 800 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); 801 memcpy(buffer.points(), pos, count * sizeof(SkPoint)); 802 return builder.make(); 803} 804 805sk_sp<SkTextBlob> SkTextBlob::MakeFromPosTextH(const void* text, size_t byteLength, 806 const SkScalar xpos[], SkScalar constY, 807 const SkFont& font, SkTextEncoding encoding) { 808 const int count = font.countText(text, byteLength, encoding); 809 if (count < 1) { 810 return nullptr; 811 } 812 SkTextBlobBuilder builder; 813 auto buffer = builder.allocRunPosH(font, count, constY); 814 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); 815 memcpy(buffer.pos, xpos, count * sizeof(SkScalar)); 816 return builder.make(); 817} 818 819sk_sp<SkTextBlob> SkTextBlob::MakeFromRSXform(const void* text, size_t byteLength, 820 const SkRSXform xform[], const SkFont& font, 821 SkTextEncoding encoding) { 822 const int count = font.countText(text, byteLength, encoding); 823 if (count < 1) { 824 return nullptr; 825 } 826 SkTextBlobBuilder builder; 827 auto buffer = builder.allocRunRSXform(font, count); 828 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); 829 memcpy(buffer.xforms(), xform, count * sizeof(SkRSXform)); 830 return builder.make(); 831} 832 833sk_sp<SkData> SkTextBlob::serialize(const SkSerialProcs& procs) const { 834 SkBinaryWriteBuffer buffer; 835 buffer.setSerialProcs(procs); 836 SkTextBlobPriv::Flatten(*this, buffer); 837 838 size_t total = buffer.bytesWritten(); 839 sk_sp<SkData> data = SkData::MakeUninitialized(total); 840 buffer.writeToMemory(data->writable_data()); 841 return data; 842} 843 844sk_sp<SkTextBlob> SkTextBlob::Deserialize(const void* data, size_t length, 845 const SkDeserialProcs& procs) { 846 SkReadBuffer buffer(data, length); 847 buffer.setDeserialProcs(procs); 848 return SkTextBlobPriv::MakeFromBuffer(buffer); 849} 850 851void SkTextBlob::dump(std::string& desc, int depth) const { 852 std::string split(depth, '\t'); 853 desc += split + "\n SkTextBlob:{ \n"; 854 fBounds.dump(desc, depth + 1); 855 desc += split + "\t fUniqueID:" + std::to_string(fUniqueID) + "\n"; 856 desc += split + "}\n"; 857} 858 859/////////////////////////////////////////////////////////////////////////////////////////////////// 860 861size_t SkTextBlob::serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const { 862 SkBinaryWriteBuffer buffer(memory, memory_size); 863 buffer.setSerialProcs(procs); 864 SkTextBlobPriv::Flatten(*this, buffer); 865 return buffer.usingInitialStorage() ? buffer.bytesWritten() : 0u; 866} 867 868/////////////////////////////////////////////////////////////////////////////////////////////////// 869 870namespace { 871int get_glyph_run_intercepts(const SkGlyphRun& glyphRun, 872 const SkPaint& paint, 873 const SkScalar bounds[2], 874 SkScalar intervals[], 875 int* intervalCount) { 876 SkScalar scale = SK_Scalar1; 877 SkPaint interceptPaint{paint}; 878 SkFont interceptFont{glyphRun.font()}; 879 880 interceptPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup 881 882 // can't use our canonical size if we need to apply path effects 883 if (interceptPaint.getPathEffect() == nullptr) { 884 // If the wrong size is going to be used, don't hint anything. 885 interceptFont.setHinting(SkFontHinting::kNone); 886 interceptFont.setSubpixel(true); 887 scale = interceptFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths; 888 interceptFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths)); 889 // Note: fScale can be zero here (even if it wasn't before the divide). It can also 890 // be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior, 891 // since downstream we will check for the resulting coordinates being non-finite anyway. 892 // Thus we don't need to check for zero here. 893 if (interceptPaint.getStrokeWidth() > 0 894 && interceptPaint.getStyle() != SkPaint::kFill_Style) { 895 interceptPaint.setStrokeWidth( 896 sk_ieee_float_divide(interceptPaint.getStrokeWidth(), scale)); 897 } 898 } 899 900 interceptPaint.setStyle(SkPaint::kFill_Style); 901 interceptPaint.setPathEffect(nullptr); 902 903 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint); 904 SkBulkGlyphMetricsAndPaths metricsAndPaths{strikeSpec}; 905 906 const SkPoint* posCursor = glyphRun.positions().begin(); 907 for (const SkGlyph* glyph : metricsAndPaths.glyphs(glyphRun.glyphsIDs())) { 908 SkPoint pos = *posCursor++; 909 910 if (glyph->path() != nullptr) { 911 // The typeface is scaled, so un-scale the bounds to be in the space of the typeface. 912 // Also ensure the bounds are properly offset by the vertical positioning of the glyph. 913 SkScalar scaledBounds[2] = { 914 (bounds[0] - pos.y()) / scale, 915 (bounds[1] - pos.y()) / scale 916 }; 917 metricsAndPaths.findIntercepts( 918 scaledBounds, scale, pos.x(), glyph, intervals, intervalCount); 919 } 920 } 921 return *intervalCount; 922} 923} // namespace 924 925int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[], 926 const SkPaint* paint) const { 927 SkTLazy<SkPaint> defaultPaint; 928 if (paint == nullptr) { 929 defaultPaint.init(); 930 paint = defaultPaint.get(); 931 } 932 933 SkGlyphRunBuilder builder; 934 auto glyphRunList = builder.blobToGlyphRunList(*this, {0, 0}); 935 936 int intervalCount = 0; 937 for (const SkGlyphRun& glyphRun : glyphRunList) { 938 // Ignore RSXForm runs. 939 if (glyphRun.scaledRotations().empty()) { 940 intervalCount = get_glyph_run_intercepts( 941 glyphRun, *paint, bounds, intervals, &intervalCount); 942 } 943 } 944 945 return intervalCount; 946} 947 948std::vector<SkScalar> SkFont::getIntercepts(const SkGlyphID glyphs[], int count, 949 const SkPoint positions[], 950 SkScalar top, SkScalar bottom, 951 const SkPaint* paintPtr) const { 952 if (count <= 0) { 953 return std::vector<SkScalar>(); 954 } 955 956 const SkPaint paint(paintPtr ? *paintPtr : SkPaint()); 957 const SkScalar bounds[] = {top, bottom}; 958 const SkGlyphRun run(*this, 959 {positions, size_t(count)}, {glyphs, size_t(count)}, 960 {nullptr, 0}, {nullptr, 0}, {nullptr, 0}); 961 962 std::vector<SkScalar> result; 963 result.resize(count * 2); // worst case allocation 964 int intervalCount = 0; 965 intervalCount = get_glyph_run_intercepts(run, paint, bounds, result.data(), &intervalCount); 966 result.resize(intervalCount); 967 return result; 968} 969 970//////// 971 972SkTextBlob::Iter::Iter(const SkTextBlob& blob) { 973 fRunRecord = RunRecord::First(&blob); 974} 975 976bool SkTextBlob::Iter::next(Run* rec) { 977 if (fRunRecord) { 978 if (rec) { 979 rec->fTypeface = fRunRecord->font().getTypeface(); 980 rec->fGlyphCount = fRunRecord->glyphCount(); 981 rec->fGlyphIndices = fRunRecord->glyphBuffer(); 982#ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED 983 rec->fClusterIndex_forTest = fRunRecord->clusterBuffer(); 984 rec->fUtf8Size_forTest = fRunRecord->textSize(); 985 rec->fUtf8_forTest = fRunRecord->textBuffer(); 986#endif 987 } 988 if (fRunRecord->isLastRun()) { 989 fRunRecord = nullptr; 990 } else { 991 fRunRecord = RunRecord::Next(fRunRecord); 992 } 993 return true; 994 } 995 return false; 996} 997 998bool SkTextBlob::Iter::experimentalNext(ExperimentalRun* rec) { 999 if (fRunRecord) { 1000 if (rec) { 1001 rec->font = fRunRecord->font(); 1002 rec->count = fRunRecord->glyphCount(); 1003 rec->glyphs = fRunRecord->glyphBuffer(); 1004 rec->positions = fRunRecord->pointBuffer(); 1005 } 1006 if (fRunRecord->isLastRun()) { 1007 fRunRecord = nullptr; 1008 } else { 1009 fRunRecord = RunRecord::Next(fRunRecord); 1010 } 1011 return true; 1012 } 1013 return false; 1014} 1015 1016void GetGlyphIDforTextBlob(const SkTextBlob* blob, std::vector<SkGlyphID>& glyphIds) 1017{ 1018 if (blob == nullptr) { 1019 return; 1020 } 1021 SkTextBlobRunIterator it(blob); 1022 if (!it.done()) { 1023 size_t runSize = it.glyphCount(); 1024 auto glyphIDs = it.glyphs(); 1025 for (size_t i = 0; i < runSize; ++i) { 1026 glyphIds.push_back(glyphIDs[i]); 1027 } 1028 } 1029} 1030 1031SkPath GetPathforTextBlob(const SkGlyphID& glyphId, const SkTextBlob* blob) 1032{ 1033 SkPath path; 1034 if (blob == nullptr) { 1035 return path; 1036 } 1037 SkTextBlobRunIterator it(blob); 1038 if (!it.done()) { 1039 SkFont font = it.font(); 1040 font.getPath(glyphId, &path); 1041 } 1042 return path; 1043} 1044 1045void GetPointsForTextBlob(const SkTextBlob* blob, std::vector<SkPoint>& points) 1046{ 1047 if (blob == nullptr) { 1048 return; 1049 } 1050 SkTextBlobRunIterator run(blob); 1051 if (!run.done()) { 1052 const auto glyphCount = run.glyphCount(); 1053 switch (run.positioning()) { 1054 case SkTextBlobRunIterator::kFull_Positioning: { 1055 for (auto i = 0; i < glyphCount; i++) { 1056 const SkPoint* glyphPoints = run.points(); 1057 const auto* point = glyphPoints + i; 1058 points.push_back(*point); 1059 } 1060 break; 1061 } 1062 default: 1063 break; 1064 } 1065 run.next(); 1066 } 1067} 1068