SkTextBlob.cpp revision 2af858354d913397a6c316ef46a5d52d686e10ab
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 "SkTextBlob.h" 9 10#include "SkReadBuffer.h" 11#include "SkTypeface.h" 12#include "SkWriteBuffer.h" 13 14// 15// Textblob data is laid out into externally-managed storage as follows: 16// 17// ----------------------------------------------------------------------------- 18// | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ... 19// ----------------------------------------------------------------------------- 20// 21// Each run record describes a text blob run, and can be used to determine the (implicit) 22// location of the following record. 23 24SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;) 25 26class SkTextBlob::RunRecord { 27public: 28 RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos) 29 : fCount(count) 30 , fOffset(offset) 31 , fFont(font) 32 , fPositioning(pos) { 33 SkDEBUGCODE(fMagic = kRunRecordMagic); 34 } 35 36 uint32_t glyphCount() const { 37 return fCount; 38 } 39 40 const SkPoint& offset() const { 41 return fOffset; 42 } 43 44 const SkPaint& font() const { 45 return fFont; 46 } 47 48 GlyphPositioning positioning() const { 49 return fPositioning; 50 } 51 52 uint16_t* glyphBuffer() const { 53 // Glyph are stored immediately following the record. 54 return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1); 55 } 56 57 SkScalar* posBuffer() const { 58 // Position scalars follow the (aligned) glyph buffer. 59 return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) + 60 SkAlign4(fCount * sizeof(uint16_t))); 61 } 62 63 static size_t StorageSize(int glyphCount, SkTextBlob::GlyphPositioning positioning) { 64 // RunRecord object + (aligned) glyph buffer + position buffer 65 return SkAlignPtr(sizeof(SkTextBlob::RunRecord) 66 + SkAlign4(glyphCount* sizeof(uint16_t)) 67 + glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning)); 68 } 69 70 static const RunRecord* First(const SkTextBlob* blob) { 71 // The first record (if present) is stored following the blob object. 72 return reinterpret_cast<const RunRecord*>(blob + 1); 73 } 74 75 static const RunRecord* Next(const RunRecord* run) { 76 return reinterpret_cast<const RunRecord*>(reinterpret_cast<const uint8_t*>(run) 77 + StorageSize(run->glyphCount(), run->positioning())); 78 } 79 80 void validate(uint8_t* storageTop) const { 81 SkASSERT(kRunRecordMagic == fMagic); 82 SkASSERT((uint8_t*)Next(this) <= storageTop); 83 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer()); 84 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(fPositioning) <= (SkScalar*)Next(this)); 85 } 86 87private: 88 friend class SkTextBlobBuilder; 89 90 void grow(uint32_t count) { 91 SkScalar* initialPosBuffer = posBuffer(); 92 uint32_t initialCount = fCount; 93 fCount += count; 94 95 // Move the initial pos scalars to their new location. 96 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(fPositioning); 97 SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)Next(this)); 98 99 // memmove, as the buffers may overlap 100 memmove(posBuffer(), initialPosBuffer, copySize); 101 } 102 103 uint32_t fCount; 104 SkPoint fOffset; 105 SkPaint fFont; 106 GlyphPositioning fPositioning; 107 108 SkDEBUGCODE(unsigned fMagic;) 109}; 110 111static int32_t gNextID = 1; 112static int32_t next_id() { 113 int32_t id; 114 do { 115 id = sk_atomic_inc(&gNextID); 116 } while (id == SK_InvalidGenID); 117 return id; 118} 119 120SkTextBlob::SkTextBlob(int runCount, const SkRect& bounds) 121 : fRunCount(runCount) 122 , fBounds(bounds) 123 , fUniqueID(next_id()) { 124} 125 126SkTextBlob::~SkTextBlob() { 127 const RunRecord* run = RunRecord::First(this); 128 for (int i = 0; i < fRunCount; ++i) { 129 const RunRecord* nextRun = RunRecord::Next(run); 130 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);) 131 run->~RunRecord(); 132 run = nextRun; 133 } 134} 135 136void SkTextBlob::flatten(SkWriteBuffer& buffer) const { 137 int runCount = fRunCount; 138 139 buffer.write32(runCount); 140 buffer.writeRect(fBounds); 141 142 SkPaint runPaint; 143 RunIterator it(this); 144 while (!it.done()) { 145 SkASSERT(it.glyphCount() > 0); 146 147 buffer.write32(it.glyphCount()); 148 buffer.write32(it.positioning()); 149 buffer.writePoint(it.offset()); 150 // This should go away when switching to SkFont 151 it.applyFontToPaint(&runPaint); 152 buffer.writePaint(runPaint); 153 154 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t)); 155 buffer.writeByteArray(it.pos(), 156 it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning())); 157 158 it.next(); 159 SkDEBUGCODE(runCount--); 160 } 161 SkASSERT(0 == runCount); 162} 163 164const SkTextBlob* SkTextBlob::CreateFromBuffer(SkReadBuffer& reader) { 165 int runCount = reader.read32(); 166 if (runCount < 0) { 167 return NULL; 168 } 169 170 SkRect bounds; 171 reader.readRect(&bounds); 172 173 SkTextBlobBuilder blobBuilder; 174 for (int i = 0; i < runCount; ++i) { 175 int glyphCount = reader.read32(); 176 GlyphPositioning pos = static_cast<GlyphPositioning>(reader.read32()); 177 if (glyphCount <= 0 || pos > kFull_Positioning) { 178 return NULL; 179 } 180 181 SkPoint offset; 182 reader.readPoint(&offset); 183 SkPaint font; 184 reader.readPaint(&font); 185 186 const SkTextBlobBuilder::RunBuffer* buf = NULL; 187 switch (pos) { 188 case kDefault_Positioning: 189 buf = &blobBuilder.allocRun(font, glyphCount, offset.x(), offset.y(), &bounds); 190 break; 191 case kHorizontal_Positioning: 192 buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bounds); 193 break; 194 case kFull_Positioning: 195 buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds); 196 break; 197 default: 198 return NULL; 199 } 200 201 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) || 202 !reader.readByteArray(buf->pos, 203 glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) { 204 return NULL; 205 } 206 } 207 208 return blobBuilder.build(); 209} 210 211unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) { 212 // GlyphPositioning values are directly mapped to scalars-per-glyph. 213 SkASSERT(pos <= 2); 214 return pos; 215} 216 217SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob) 218 : fCurrentRun(RunRecord::First(blob)) 219 , fRemainingRuns(blob->fRunCount) { 220 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;) 221} 222 223bool SkTextBlob::RunIterator::done() const { 224 return fRemainingRuns <= 0; 225} 226 227void SkTextBlob::RunIterator::next() { 228 SkASSERT(!this->done()); 229 230 if (!this->done()) { 231 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);) 232 fCurrentRun = RunRecord::Next(fCurrentRun); 233 fRemainingRuns--; 234 } 235} 236 237uint32_t SkTextBlob::RunIterator::glyphCount() const { 238 SkASSERT(!this->done()); 239 return fCurrentRun->glyphCount(); 240} 241 242const uint16_t* SkTextBlob::RunIterator::glyphs() const { 243 SkASSERT(!this->done()); 244 return fCurrentRun->glyphBuffer(); 245} 246 247const SkScalar* SkTextBlob::RunIterator::pos() const { 248 SkASSERT(!this->done()); 249 return fCurrentRun->posBuffer(); 250} 251 252const SkPoint& SkTextBlob::RunIterator::offset() const { 253 SkASSERT(!this->done()); 254 return fCurrentRun->offset(); 255} 256 257SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const { 258 SkASSERT(!this->done()); 259 return fCurrentRun->positioning(); 260} 261 262void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const { 263 SkASSERT(!this->done()); 264 265 const SkPaint& font = fCurrentRun->font(); 266 267 paint->setTypeface(font.getTypeface()); 268 paint->setTextEncoding(font.getTextEncoding()); 269 paint->setTextSize(font.getTextSize()); 270 paint->setTextScaleX(font.getTextScaleX()); 271 paint->setTextSkewX(font.getTextSkewX()); 272 paint->setHinting(font.getHinting()); 273 274 uint32_t flagsMask = SkPaint::kAntiAlias_Flag 275 | SkPaint::kUnderlineText_Flag 276 | SkPaint::kStrikeThruText_Flag 277 | SkPaint::kFakeBoldText_Flag 278 | SkPaint::kLinearText_Flag 279 | SkPaint::kSubpixelText_Flag 280 | SkPaint::kDevKernText_Flag 281 | SkPaint::kLCDRenderText_Flag 282 | SkPaint::kEmbeddedBitmapText_Flag 283 | SkPaint::kAutoHinting_Flag 284 | SkPaint::kVerticalText_Flag 285 | SkPaint::kGenA8FromLCD_Flag 286 | SkPaint::kDistanceFieldTextTEMP_Flag; 287 paint->setFlags((paint->getFlags() & ~flagsMask) | (font.getFlags() & flagsMask)); 288} 289 290SkTextBlobBuilder::SkTextBlobBuilder() 291 : fStorageSize(0) 292 , fStorageUsed(0) 293 , fRunCount(0) 294 , fDeferredBounds(false) 295 , fLastRun(0) { 296 fBounds.setEmpty(); 297} 298 299SkTextBlobBuilder::~SkTextBlobBuilder() { 300 if (NULL != fStorage.get()) { 301 // We are abandoning runs and must destruct the associated font data. 302 // The easiest way to accomplish that is to use the blob destructor. 303 build()->unref(); 304 } 305} 306 307SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) { 308 SkASSERT(SkTextBlob::kDefault_Positioning == run.positioning()); 309 310 SkRect bounds; 311 run.font().measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds); 312 313 return bounds.makeOffset(run.offset().x(), run.offset().y()); 314} 315 316SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) { 317 SkASSERT(run.glyphCount() > 0); 318 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() || 319 SkTextBlob::kHorizontal_Positioning == run.positioning()); 320 321 // First, compute the glyph position bbox. 322 SkRect bounds; 323 switch (run.positioning()) { 324 case SkTextBlob::kHorizontal_Positioning: { 325 const SkScalar* glyphPos = run.posBuffer(); 326 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); 327 328 SkScalar minX = *glyphPos; 329 SkScalar maxX = *glyphPos; 330 for (unsigned i = 1; i < run.glyphCount(); ++i) { 331 SkScalar x = glyphPos[i]; 332 minX = SkMinScalar(x, minX); 333 maxX = SkMaxScalar(x, maxX); 334 } 335 336 bounds.setLTRB(minX, 0, maxX, 0); 337 } break; 338 case SkTextBlob::kFull_Positioning: { 339 const SkPoint* glyphPosPts = reinterpret_cast<const SkPoint*>(run.posBuffer()); 340 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); 341 342 bounds.setBounds(glyphPosPts, run.glyphCount()); 343 } break; 344 default: 345 SkFAIL("unsupported positioning mode"); 346 } 347 348 // Expand by typeface glyph bounds. 349 const SkRect fontBounds = run.font().getFontBounds(); 350 bounds.fLeft += fontBounds.left(); 351 bounds.fTop += fontBounds.top(); 352 bounds.fRight += fontBounds.right(); 353 bounds.fBottom += fontBounds.bottom(); 354 355 // Offset by run position. 356 return bounds.makeOffset(run.offset().x(), run.offset().y()); 357} 358 359void SkTextBlobBuilder::updateDeferredBounds() { 360 SkASSERT(!fDeferredBounds || fRunCount > 0); 361 362 if (!fDeferredBounds) { 363 return; 364 } 365 366 SkASSERT(fLastRun >= sizeof(SkTextBlob)); 367 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + 368 fLastRun); 369 SkASSERT(SkPaint::kGlyphID_TextEncoding == run->font().getTextEncoding()); 370 371 // FIXME: we should also use conservative bounds for kDefault_Positioning. 372 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ? 373 TightRunBounds(*run) : ConservativeRunBounds(*run); 374 fBounds.join(runBounds); 375 fDeferredBounds = false; 376} 377 378void SkTextBlobBuilder::reserve(size_t size) { 379 // We don't currently pre-allocate, but maybe someday... 380 if (fStorageUsed + size <= fStorageSize) { 381 return; 382 } 383 384 if (0 == fRunCount) { 385 SkASSERT(NULL == fStorage.get()); 386 SkASSERT(0 == fStorageSize); 387 SkASSERT(0 == fStorageUsed); 388 389 // the first allocation also includes blob storage 390 fStorageUsed += sizeof(SkTextBlob); 391 } 392 393 fStorageSize = fStorageUsed + size; 394 // FYI: This relies on everything we store being relocatable, particularly SkPaint. 395 fStorage.realloc(fStorageSize); 396} 397 398bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning, 399 int count, SkPoint offset) { 400 if (0 == fLastRun) { 401 SkASSERT(0 == fRunCount); 402 return false; 403 } 404 405 SkASSERT(fLastRun >= sizeof(SkTextBlob)); 406 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + 407 fLastRun); 408 SkASSERT(run->glyphCount() > 0); 409 410 if (run->positioning() != positioning 411 || run->font() != font 412 || (run->glyphCount() + count < run->glyphCount())) { 413 return false; 414 } 415 416 // we can merge same-font/same-positioning runs in the following cases: 417 // * fully positioned run following another fully positioned run 418 // * horizontally postioned run following another horizontally positioned run with the same 419 // y-offset 420 if (SkTextBlob::kFull_Positioning != positioning 421 && (SkTextBlob::kHorizontal_Positioning != positioning 422 || run->offset().y() != offset.y())) { 423 return false; 424 } 425 426 size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, positioning) - 427 SkTextBlob::RunRecord::StorageSize(run->glyphCount(), positioning); 428 this->reserve(sizeDelta); 429 430 // reserve may have realloced 431 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); 432 uint32_t preMergeCount = run->glyphCount(); 433 run->grow(count); 434 435 // Callers expect the buffers to point at the newly added slice, ant not at the beginning. 436 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount; 437 fCurrentRunBuffer.pos = run->posBuffer() 438 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning); 439 440 fStorageUsed += sizeDelta; 441 442 SkASSERT(fStorageUsed <= fStorageSize); 443 run->validate(fStorage.get() + fStorageUsed); 444 445 return true; 446} 447 448void SkTextBlobBuilder::allocInternal(const SkPaint &font, 449 SkTextBlob::GlyphPositioning positioning, 450 int count, SkPoint offset, const SkRect* bounds) { 451 SkASSERT(count > 0); 452 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding()); 453 454 if (!this->mergeRun(font, positioning, count, offset)) { 455 this->updateDeferredBounds(); 456 457 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning); 458 this->reserve(runSize); 459 460 SkASSERT(fStorageUsed >= sizeof(SkTextBlob)); 461 SkASSERT(fStorageUsed + runSize <= fStorageSize); 462 463 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed) 464 SkTextBlob::RunRecord(count, offset, font, positioning); 465 466 fCurrentRunBuffer.glyphs = run->glyphBuffer(); 467 fCurrentRunBuffer.pos = run->posBuffer(); 468 469 fLastRun = fStorageUsed; 470 fStorageUsed += runSize; 471 fRunCount++; 472 473 SkASSERT(fStorageUsed <= fStorageSize); 474 run->validate(fStorage.get() + fStorageUsed); 475 } 476 477 if (!fDeferredBounds) { 478 if (bounds) { 479 fBounds.join(*bounds); 480 } else { 481 fDeferredBounds = true; 482 } 483 } 484} 485 486const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkPaint& font, int count, 487 SkScalar x, SkScalar y, 488 const SkRect* bounds) { 489 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, SkPoint::Make(x, y), bounds); 490 491 return fCurrentRunBuffer; 492} 493 494const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkPaint& font, int count, 495 SkScalar y, 496 const SkRect* bounds) { 497 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, SkPoint::Make(0, y), 498 bounds); 499 500 return fCurrentRunBuffer; 501} 502 503const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint& font, int count, 504 const SkRect *bounds) { 505 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Make(0, 0), bounds); 506 507 return fCurrentRunBuffer; 508} 509 510const SkTextBlob* SkTextBlobBuilder::build() { 511 SkASSERT((fRunCount > 0) == (NULL != fStorage.get())); 512 513 this->updateDeferredBounds(); 514 515 if (0 == fRunCount) { 516 SkASSERT(NULL == fStorage.get()); 517 fStorageUsed = sizeof(SkTextBlob); 518 fStorage.realloc(fStorageUsed); 519 } 520 521 SkDEBUGCODE( 522 size_t validateSize = sizeof(SkTextBlob); 523 const SkTextBlob::RunRecord* run = 524 SkTextBlob::RunRecord::First(reinterpret_cast<const SkTextBlob*>(fStorage.get())); 525 for (int i = 0; i < fRunCount; ++i) { 526 validateSize += SkTextBlob::RunRecord::StorageSize(run->fCount, run->fPositioning); 527 run->validate(fStorage.get() + fStorageUsed); 528 run = SkTextBlob::RunRecord::Next(run); 529 } 530 SkASSERT(validateSize == fStorageUsed); 531 ) 532 533 const SkTextBlob* blob = new (fStorage.detach()) SkTextBlob(fRunCount, fBounds); 534 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;) 535 536 fStorageUsed = 0; 537 fStorageSize = 0; 538 fRunCount = 0; 539 fLastRun = 0; 540 fBounds.setEmpty(); 541 542 return blob; 543} 544 545