| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkTextBlob.h" | 8 #include "SkTextBlob.h" |
| 9 | 9 |
| 10 #include "SkReadBuffer.h" | 10 #include "SkReadBuffer.h" |
| 11 #include "SkWriteBuffer.h" | 11 #include "SkWriteBuffer.h" |
| 12 | 12 |
| 13 // | 13 SkTextBlob::SkTextBlob(uint16_t *glyphs, SkScalar *pos, const SkTArray<Run> *run
s, |
| 14 // Textblob data is laid out into externally-managed storage as follows: | 14 const SkRect& bounds) |
| 15 // | 15 : fGlyphBuffer(glyphs) |
| 16 // --------------------------------------------------------------------------
--- | 16 , fPosBuffer(pos) |
| 17 // | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[]
| ... | 17 , fRuns(runs) |
| 18 // --------------------------------------------------------------------------
--- | |
| 19 // | |
| 20 // Each run record describes a text blob run, and can be used to determine the
(implicit) | |
| 21 // location of the following record. | |
| 22 | |
| 23 SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;) | |
| 24 | |
| 25 class SkTextBlob::RunRecord { | |
| 26 public: | |
| 27 RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphP
ositioning pos) | |
| 28 : fCount(count) | |
| 29 , fOffset(offset) | |
| 30 , fFont(font) | |
| 31 , fPositioning(pos) { | |
| 32 SkDEBUGCODE(fMagic = kRunRecordMagic); | |
| 33 } | |
| 34 | |
| 35 uint32_t glyphCount() const { | |
| 36 return fCount; | |
| 37 } | |
| 38 | |
| 39 const SkPoint& offset() const { | |
| 40 return fOffset; | |
| 41 } | |
| 42 | |
| 43 const SkPaint& font() const { | |
| 44 return fFont; | |
| 45 } | |
| 46 | |
| 47 GlyphPositioning positioning() const { | |
| 48 return fPositioning; | |
| 49 } | |
| 50 | |
| 51 uint16_t* glyphBuffer() const { | |
| 52 // Glyph are stored immediately following the record. | |
| 53 return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1); | |
| 54 } | |
| 55 | |
| 56 SkScalar* posBuffer() const { | |
| 57 // Position scalars follow the (aligned) glyph buffer. | |
| 58 return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyp
hBuffer()) + | |
| 59 SkAlign4(fCount * sizeof(uint16_t))); | |
| 60 } | |
| 61 | |
| 62 static size_t StorageSize(int glyphCount, SkTextBlob::GlyphPositioning posit
ioning) { | |
| 63 // RunRecord object + (aligned) glyph buffer + position buffer | |
| 64 return SkAlignPtr(sizeof(SkTextBlob::RunRecord) | |
| 65 + SkAlign4(glyphCount* sizeof(uint16_t)) | |
| 66 + glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(positi
oning)); | |
| 67 } | |
| 68 | |
| 69 static const RunRecord* First(const SkTextBlob* blob) { | |
| 70 // The first record (if present) is stored following the blob object. | |
| 71 return reinterpret_cast<const RunRecord*>(blob + 1); | |
| 72 } | |
| 73 | |
| 74 static const RunRecord* Next(const RunRecord* run) { | |
| 75 return reinterpret_cast<const RunRecord*>(reinterpret_cast<const uint8_t
*>(run) | |
| 76 + StorageSize(run->glyphCount(), run->positioning())); | |
| 77 } | |
| 78 | |
| 79 void validate(uint8_t* storageTop) const { | |
| 80 SkASSERT(kRunRecordMagic == fMagic); | |
| 81 SkASSERT((uint8_t*)Next(this) <= storageTop); | |
| 82 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer()); | |
| 83 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(fPositioning) == (SkScal
ar*)Next(this)); | |
| 84 } | |
| 85 | |
| 86 private: | |
| 87 friend class SkTextBlobBuilder; | |
| 88 | |
| 89 void grow(uint32_t count) { | |
| 90 SkScalar* initialPosBuffer = posBuffer(); | |
| 91 uint32_t initialCount = fCount; | |
| 92 fCount += count; | |
| 93 | |
| 94 // Move the initial pos scalars to their new location. | |
| 95 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(fPos
itioning); | |
| 96 SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)Next(this)); | |
| 97 | |
| 98 // memmove, as the buffers may overlap | |
| 99 memmove(posBuffer(), initialPosBuffer, copySize); | |
| 100 } | |
| 101 | |
| 102 uint32_t fCount; | |
| 103 SkPoint fOffset; | |
| 104 SkPaint fFont; | |
| 105 GlyphPositioning fPositioning; | |
| 106 | |
| 107 SkDEBUGCODE(unsigned fMagic;) | |
| 108 }; | |
| 109 | |
| 110 SkTextBlob::SkTextBlob(int runCount, const SkRect& bounds) | |
| 111 : fRunCount(runCount) | |
| 112 , fBounds(bounds) { | 18 , fBounds(bounds) { |
| 113 } | 19 } |
| 114 | 20 |
| 115 SkTextBlob::~SkTextBlob() { | |
| 116 const RunRecord* run = RunRecord::First(this); | |
| 117 for (int i = 0; i < fRunCount; ++i) { | |
| 118 const RunRecord* nextRun = RunRecord::Next(run); | |
| 119 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);) | |
| 120 run->~RunRecord(); | |
| 121 run = nextRun; | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 void SkTextBlob::internal_dispose() const { | |
| 126 // SkTextBlobs use externally-managed storage. | |
| 127 this->internal_dispose_restore_refcnt_to_1(); | |
| 128 this->~SkTextBlob(); | |
| 129 sk_free(const_cast<SkTextBlob*>(this)); | |
| 130 } | |
| 131 | |
| 132 uint32_t SkTextBlob::uniqueID() const { | 21 uint32_t SkTextBlob::uniqueID() const { |
| 133 static int32_t gTextBlobGenerationID; // = 0; | 22 static int32_t gTextBlobGenerationID; // = 0; |
| 134 | 23 |
| 135 // loop in case our global wraps around, as we never want to return SK_Inval
idGenID | 24 // loop in case our global wraps around, as we never want to return SK_Inval
idGenID |
| 136 while (SK_InvalidGenID == fUniqueID) { | 25 while (SK_InvalidGenID == fUniqueID) { |
| 137 fUniqueID = sk_atomic_inc(&gTextBlobGenerationID) + 1; | 26 fUniqueID = sk_atomic_inc(&gTextBlobGenerationID) + 1; |
| 138 } | 27 } |
| 139 | 28 |
| 140 return fUniqueID; | 29 return fUniqueID; |
| 141 } | 30 } |
| 142 | 31 |
| 32 unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) { |
| 33 // GlyphPositioning values are directly mapped to scalars-per-glyph. |
| 34 SkASSERT(pos <= 2); |
| 35 return pos; |
| 36 } |
| 37 |
| 143 void SkTextBlob::flatten(SkWriteBuffer& buffer) const { | 38 void SkTextBlob::flatten(SkWriteBuffer& buffer) const { |
| 144 int runCount = fRunCount; | 39 int runCount = (NULL == fRuns.get()) ? 0 : fRuns->count(); |
| 145 | 40 |
| 146 buffer.write32(runCount); | 41 buffer.write32(runCount); |
| 147 buffer.writeRect(fBounds); | 42 buffer.writeRect(fBounds); |
| 148 | 43 |
| 149 SkPaint runPaint; | 44 SkPaint runPaint; |
| 150 RunIterator it(this); | 45 RunIterator it(this); |
| 151 while (!it.done()) { | 46 while (!it.done()) { |
| 152 SkASSERT(it.glyphCount() > 0); | 47 SkASSERT(it.glyphCount() > 0); |
| 153 | 48 |
| 154 buffer.write32(it.glyphCount()); | 49 buffer.write32(it.glyphCount()); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bound
s); | 94 buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bound
s); |
| 200 break; | 95 break; |
| 201 case kFull_Positioning: | 96 case kFull_Positioning: |
| 202 buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds); | 97 buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds); |
| 203 break; | 98 break; |
| 204 default: | 99 default: |
| 205 return NULL; | 100 return NULL; |
| 206 } | 101 } |
| 207 | 102 |
| 208 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) || | 103 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) || |
| 209 !reader.readByteArray(buf->pos, | 104 !reader.readByteArray(buf->pos, glyphCount * sizeof(SkScalar) * Scal
arsPerGlyph(pos))) { |
| 210 glyphCount * sizeof(SkScalar) * ScalarsPerGlyp
h(pos))) { | |
| 211 return NULL; | 105 return NULL; |
| 212 } | 106 } |
| 213 } | 107 } |
| 214 | 108 |
| 215 return blobBuilder.build(); | 109 return blobBuilder.build(); |
| 216 } | 110 } |
| 217 | 111 |
| 218 unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) { | |
| 219 // GlyphPositioning values are directly mapped to scalars-per-glyph. | |
| 220 SkASSERT(pos <= 2); | |
| 221 return pos; | |
| 222 } | |
| 223 | |
| 224 SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob) | 112 SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob) |
| 225 : fCurrentRun(RunRecord::First(blob)) | 113 : fBlob(blob) |
| 226 , fRemainingRuns(blob->fRunCount) { | 114 , fIndex(0) { |
| 227 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;) | 115 SkASSERT(blob); |
| 228 } | 116 } |
| 229 | 117 |
| 230 bool SkTextBlob::RunIterator::done() const { | 118 bool SkTextBlob::RunIterator::done() const { |
| 231 return fRemainingRuns <= 0; | 119 return NULL == fBlob->fRuns.get() || fIndex >= fBlob->fRuns->count(); |
| 232 } | 120 } |
| 233 | 121 |
| 234 void SkTextBlob::RunIterator::next() { | 122 void SkTextBlob::RunIterator::next() { |
| 235 SkASSERT(!this->done()); | 123 SkASSERT(!this->done()); |
| 236 | 124 fIndex++; |
| 237 if (!this->done()) { | |
| 238 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);) | |
| 239 fCurrentRun = RunRecord::Next(fCurrentRun); | |
| 240 fRemainingRuns--; | |
| 241 } | |
| 242 } | 125 } |
| 243 | 126 |
| 244 uint32_t SkTextBlob::RunIterator::glyphCount() const { | 127 uint32_t SkTextBlob::RunIterator::glyphCount() const { |
| 245 SkASSERT(!this->done()); | 128 SkASSERT(!this->done()); |
| 246 return fCurrentRun->glyphCount(); | 129 return (*fBlob->fRuns)[fIndex].count; |
| 247 } | 130 } |
| 248 | 131 |
| 249 const uint16_t* SkTextBlob::RunIterator::glyphs() const { | 132 const uint16_t* SkTextBlob::RunIterator::glyphs() const { |
| 250 SkASSERT(!this->done()); | 133 SkASSERT(!this->done()); |
| 251 return fCurrentRun->glyphBuffer(); | 134 return fBlob->fGlyphBuffer.get() + (*fBlob->fRuns)[fIndex].glyphStart; |
| 252 } | 135 } |
| 253 | 136 |
| 254 const SkScalar* SkTextBlob::RunIterator::pos() const { | 137 const SkScalar* SkTextBlob::RunIterator::pos() const { |
| 255 SkASSERT(!this->done()); | 138 SkASSERT(!this->done()); |
| 256 return fCurrentRun->posBuffer(); | 139 return fBlob->fPosBuffer.get() + (*fBlob->fRuns)[fIndex].posStart; |
| 257 } | 140 } |
| 258 | 141 |
| 259 const SkPoint& SkTextBlob::RunIterator::offset() const { | 142 const SkPoint& SkTextBlob::RunIterator::offset() const { |
| 260 SkASSERT(!this->done()); | 143 SkASSERT(!this->done()); |
| 261 return fCurrentRun->offset(); | 144 return (*fBlob->fRuns)[fIndex].offset; |
| 262 } | 145 } |
| 263 | 146 |
| 264 SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const { | 147 SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const { |
| 265 SkASSERT(!this->done()); | 148 SkASSERT(!this->done()); |
| 266 return fCurrentRun->positioning(); | 149 return (*fBlob->fRuns)[fIndex].positioning; |
| 267 } | 150 } |
| 268 | 151 |
| 269 void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const { | 152 void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const { |
| 270 SkASSERT(!this->done()); | 153 SkASSERT(!this->done()); |
| 271 | 154 |
| 272 const SkPaint& font = fCurrentRun->font(); | 155 const SkPaint& font = (*fBlob->fRuns)[fIndex].font; |
| 273 | 156 |
| 274 paint->setTypeface(font.getTypeface()); | 157 paint->setTypeface(font.getTypeface()); |
| 275 paint->setTextEncoding(font.getTextEncoding()); | 158 paint->setTextEncoding(font.getTextEncoding()); |
| 276 paint->setTextSize(font.getTextSize()); | 159 paint->setTextSize(font.getTextSize()); |
| 277 paint->setTextScaleX(font.getTextScaleX()); | 160 paint->setTextScaleX(font.getTextScaleX()); |
| 278 paint->setTextSkewX(font.getTextSkewX()); | 161 paint->setTextSkewX(font.getTextSkewX()); |
| 279 paint->setHinting(font.getHinting()); | 162 paint->setHinting(font.getHinting()); |
| 280 | 163 |
| 281 uint32_t flagsMask = SkPaint::kAntiAlias_Flag | 164 uint32_t flagsMask = SkPaint::kAntiAlias_Flag |
| 282 | SkPaint::kUnderlineText_Flag | 165 | SkPaint::kUnderlineText_Flag |
| 283 | SkPaint::kStrikeThruText_Flag | 166 | SkPaint::kStrikeThruText_Flag |
| 284 | SkPaint::kFakeBoldText_Flag | 167 | SkPaint::kFakeBoldText_Flag |
| 285 | SkPaint::kLinearText_Flag | 168 | SkPaint::kLinearText_Flag |
| 286 | SkPaint::kSubpixelText_Flag | 169 | SkPaint::kSubpixelText_Flag |
| 287 | SkPaint::kDevKernText_Flag | 170 | SkPaint::kDevKernText_Flag |
| 288 | SkPaint::kLCDRenderText_Flag | 171 | SkPaint::kLCDRenderText_Flag |
| 289 | SkPaint::kEmbeddedBitmapText_Flag | 172 | SkPaint::kEmbeddedBitmapText_Flag |
| 290 | SkPaint::kAutoHinting_Flag | 173 | SkPaint::kAutoHinting_Flag |
| 291 | SkPaint::kVerticalText_Flag | 174 | SkPaint::kVerticalText_Flag |
| 292 | SkPaint::kGenA8FromLCD_Flag | 175 | SkPaint::kGenA8FromLCD_Flag |
| 293 | SkPaint::kDistanceFieldTextTEMP_Flag; | 176 | SkPaint::kDistanceFieldTextTEMP_Flag; |
| 294 paint->setFlags((paint->getFlags() & ~flagsMask) | (font.getFlags() & flagsM
ask)); | 177 paint->setFlags((paint->getFlags() & ~flagsMask) | (font.getFlags() & flagsM
ask)); |
| 295 } | 178 } |
| 296 | 179 |
| 297 SkTextBlobBuilder::SkTextBlobBuilder() | 180 SkTextBlobBuilder::SkTextBlobBuilder(unsigned runs) |
| 298 : fStorageSize(0) | 181 : fRuns(NULL) |
| 299 , fStorageUsed(0) | 182 , fDeferredBounds(false) { |
| 300 , fRunCount(0) | 183 |
| 301 , fDeferredBounds(false) | 184 if (runs > 0) { |
| 302 , fLastRun(0) { | 185 // if the number of runs is known, size our run storage accordingly. |
| 186 fRuns = SkNEW(SkTArray<SkTextBlob::Run>(runs)); |
| 187 } |
| 303 fBounds.setEmpty(); | 188 fBounds.setEmpty(); |
| 304 } | 189 } |
| 305 | 190 |
| 306 SkTextBlobBuilder::~SkTextBlobBuilder() { | 191 SkTextBlobBuilder::~SkTextBlobBuilder() { |
| 307 if (NULL != fStorage.get()) { | 192 // unused runs |
| 308 // We are abandoning runs and must destruct the associated font data. | 193 SkDELETE(fRuns); |
| 309 // The easiest way to accomplish that is to use the blob destructor. | |
| 310 build()->unref(); | |
| 311 } | |
| 312 } | 194 } |
| 313 | 195 |
| 314 void SkTextBlobBuilder::updateDeferredBounds() { | 196 void SkTextBlobBuilder::updateDeferredBounds() { |
| 315 SkASSERT(!fDeferredBounds || fRunCount > 0); | 197 SkASSERT(!fDeferredBounds || (fRuns && !fRuns->empty())); |
| 316 | 198 |
| 317 if (!fDeferredBounds) { | 199 if (!fDeferredBounds) { |
| 318 return; | 200 return; |
| 319 } | 201 } |
| 320 | 202 |
| 321 // FIXME: measure the current run & union bounds | 203 // FIXME: measure the current run & union bounds |
| 322 fDeferredBounds = false; | 204 fDeferredBounds = false; |
| 323 } | 205 } |
| 324 | 206 |
| 325 void SkTextBlobBuilder::reserve(size_t size) { | 207 void SkTextBlobBuilder::ensureRun(const SkPaint& font, SkTextBlob::GlyphPosition
ing pos, |
| 326 // We don't currently pre-allocate, but maybe someday... | 208 const SkPoint& offset) { |
| 327 if (fStorageUsed + size <= fStorageSize) { | 209 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding()); |
| 328 return; | |
| 329 } | |
| 330 | 210 |
| 331 if (0 == fRunCount) { | 211 if (NULL == fRuns) { |
| 332 SkASSERT(NULL == fStorage.get()); | 212 fRuns = SkNEW(SkTArray<SkTextBlob::Run>()); |
| 333 SkASSERT(0 == fStorageSize); | |
| 334 SkASSERT(0 == fStorageUsed); | |
| 335 | |
| 336 // the first allocation also includes blob storage | |
| 337 fStorageUsed += sizeof(SkTextBlob); | |
| 338 } | |
| 339 | |
| 340 fStorageSize = fStorageUsed + size; | |
| 341 // FYI: This relies on everything we store being relocatable, particularly S
kPaint. | |
| 342 fStorage.realloc(fStorageSize); | |
| 343 } | |
| 344 | |
| 345 bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioni
ng positioning, | |
| 346 int count, SkPoint offset) { | |
| 347 if (0 == fLastRun) { | |
| 348 SkASSERT(0 == fRunCount); | |
| 349 return false; | |
| 350 } | |
| 351 | |
| 352 SkASSERT(fLastRun >= sizeof(SkTextBlob)); | |
| 353 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStora
ge.get() + | |
| 354 fLastR
un); | |
| 355 SkASSERT(run->glyphCount() > 0); | |
| 356 | |
| 357 if (run->positioning() != positioning | |
| 358 || run->font() != font | |
| 359 || (run->glyphCount() + count < run->glyphCount())) { | |
| 360 return false; | |
| 361 } | 213 } |
| 362 | 214 |
| 363 // we can merge same-font/same-positioning runs in the following cases: | 215 // we can merge same-font/same-positioning runs in the following cases: |
| 364 // * fully positioned run following another fully positioned run | 216 // * fully positioned run following another fully positioned run |
| 365 // * horizontally postioned run following another horizontally positioned
run with the same | 217 // * horizontally postioned run following another horizontally positioned
run with the same |
| 366 // y-offset | 218 // y-offset |
| 367 if (SkTextBlob::kFull_Positioning != positioning | 219 if (!fRuns->empty() |
| 368 && (SkTextBlob::kHorizontal_Positioning != positioning | 220 && fRuns->back().positioning == pos |
| 369 || run->offset().y() != offset.y())) { | 221 && fRuns->back().font == font |
| 370 return false; | 222 && (SkTextBlob::kFull_Positioning == fRuns->back().positioning |
| 223 || (SkTextBlob::kHorizontal_Positioning == fRuns->back().positioning |
| 224 && fRuns->back().offset.y() == offset.y()))){ |
| 225 return; |
| 371 } | 226 } |
| 372 | 227 |
| 373 size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + co
unt, positioning) - | 228 this->updateDeferredBounds(); |
| 374 SkTextBlob::RunRecord::StorageSize(run->glyphCount(), pos
itioning); | |
| 375 this->reserve(sizeDelta); | |
| 376 | 229 |
| 377 // reserve may have realloced | 230 // start a new run |
| 378 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); | 231 SkTextBlob::Run& newRun = fRuns->push_back(); |
| 379 uint32_t preMergeCount = run->glyphCount(); | 232 newRun.count = 0; |
| 380 run->grow(count); | 233 newRun.glyphStart = fGlyphBuffer.count(); |
| 381 | 234 newRun.posStart = fPosBuffer.count(); |
| 382 // Callers expect the buffers to point at the newly added slice, ant not at
the beginning. | 235 newRun.offset = offset; |
| 383 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount; | 236 newRun.font = font; |
| 384 fCurrentRunBuffer.pos = run->posBuffer() | 237 newRun.positioning = pos; |
| 385 + preMergeCount * SkTextBlob::ScalarsPerGlyph(position
ing); | |
| 386 | |
| 387 fStorageUsed += sizeDelta; | |
| 388 | |
| 389 SkASSERT(fStorageUsed <= fStorageSize); | |
| 390 run->validate(fStorage.get() + fStorageUsed); | |
| 391 | |
| 392 return true; | |
| 393 } | 238 } |
| 394 | 239 |
| 395 void SkTextBlobBuilder::allocInternal(const SkPaint &font, | 240 void SkTextBlobBuilder::allocInternal(const SkPaint &font, |
| 396 SkTextBlob::GlyphPositioning positioning, | 241 SkTextBlob::GlyphPositioning positioning, |
| 397 int count, SkPoint offset, const SkRect* b
ounds) { | 242 int count, SkPoint offset, const SkRect* b
ounds) { |
| 398 SkASSERT(count > 0); | 243 SkASSERT(count > 0); |
| 399 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding()); | |
| 400 | 244 |
| 401 if (!this->mergeRun(font, positioning, count, offset)) { | 245 this->ensureRun(font, positioning, offset); |
| 402 updateDeferredBounds(); | |
| 403 | 246 |
| 404 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning); | 247 unsigned posScalarsPerGlyph = SkTextBlob::ScalarsPerGlyph(positioning); |
| 405 this->reserve(runSize); | |
| 406 | 248 |
| 407 SkASSERT(fStorageUsed >= sizeof(SkTextBlob)); | 249 fGlyphBuffer.append(count); |
| 408 SkASSERT(fStorageUsed + runSize <= fStorageSize); | 250 fPosBuffer.append(count * posScalarsPerGlyph); |
| 409 | 251 |
| 410 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed) | 252 SkASSERT(fRuns && !fRuns->empty()); |
| 411 SkTextBlob::RunRecord(count, offset, fo
nt, positioning); | 253 SkTextBlob::Run& run = fRuns->back(); |
| 412 | 254 |
| 413 fCurrentRunBuffer.glyphs = run->glyphBuffer(); | 255 run.count += count; |
| 414 fCurrentRunBuffer.pos = run->posBuffer(); | |
| 415 | 256 |
| 416 fLastRun = fStorageUsed; | 257 // The current run might have been merged, so the start offset may point to
prev run data. |
| 417 fStorageUsed += runSize; | 258 // Start from the back (which always points to the end of the current run bu
ffers) instead. |
| 418 fRunCount++; | 259 fCurrentRunBuffer.glyphs = fGlyphBuffer.isEmpty() |
| 419 | 260 ? NULL : fGlyphBuffer.end() - count; |
| 420 SkASSERT(fStorageUsed <= fStorageSize); | 261 SkASSERT(NULL == fCurrentRunBuffer.glyphs || fCurrentRunBuffer.glyphs >= fGl
yphBuffer.begin()); |
| 421 run->validate(fStorage.get() + fStorageUsed); | 262 fCurrentRunBuffer.pos = fPosBuffer.isEmpty() |
| 422 } | 263 ? NULL : fPosBuffer.end() - count * posScalarsPerGlyph; |
| 264 SkASSERT(NULL == fCurrentRunBuffer.pos || fCurrentRunBuffer.pos >= fPosBuffe
r.begin()); |
| 423 | 265 |
| 424 if (!fDeferredBounds) { | 266 if (!fDeferredBounds) { |
| 425 if (bounds) { | 267 if (bounds) { |
| 426 fBounds.join(*bounds); | 268 fBounds.join(*bounds); |
| 427 } else { | 269 } else { |
| 428 fDeferredBounds = true; | 270 fDeferredBounds = true; |
| 429 } | 271 } |
| 430 } | 272 } |
| 431 } | 273 } |
| 432 | 274 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 448 } | 290 } |
| 449 | 291 |
| 450 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint
& font, int count, | 292 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint
& font, int count, |
| 451 const SkRect
*bounds) { | 293 const SkRect
*bounds) { |
| 452 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Mak
e(0, 0), bounds); | 294 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Mak
e(0, 0), bounds); |
| 453 | 295 |
| 454 return fCurrentRunBuffer; | 296 return fCurrentRunBuffer; |
| 455 } | 297 } |
| 456 | 298 |
| 457 const SkTextBlob* SkTextBlobBuilder::build() { | 299 const SkTextBlob* SkTextBlobBuilder::build() { |
| 458 SkASSERT((fRunCount > 0) == (NULL != fStorage.get())); | 300 const SkTextBlob* blob; |
| 459 | 301 |
| 460 this->updateDeferredBounds(); | 302 if (fGlyphBuffer.count() > 0) { |
| 303 // we have some glyphs, construct a real blob |
| 304 SkASSERT(fRuns && !fRuns->empty()); |
| 461 | 305 |
| 462 if (0 == fRunCount) { | 306 this->updateDeferredBounds(); |
| 463 SkASSERT(NULL == fStorage.get()); | 307 |
| 464 fStorage.realloc(sizeof(SkTextBlob)); | 308 // ownership of all buffers is transferred to the blob |
| 309 blob = SkNEW_ARGS(SkTextBlob, (fGlyphBuffer.detach(), |
| 310 fPosBuffer.detach(), |
| 311 fRuns, |
| 312 fBounds)); |
| 313 fRuns = NULL; |
| 314 fBounds.setEmpty(); |
| 315 } else { |
| 316 // empty blob |
| 317 SkASSERT(NULL == fRuns || fRuns->empty()); |
| 318 SkASSERT(fBounds.isEmpty()); |
| 319 |
| 320 blob = SkNEW_ARGS(SkTextBlob, (NULL, NULL, NULL, SkRect::MakeEmpty())); |
| 465 } | 321 } |
| 466 | 322 |
| 467 const SkTextBlob* blob = new (fStorage.detach()) SkTextBlob(fRunCount, fBoun
ds); | |
| 468 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;) | |
| 469 | |
| 470 fStorageUsed = 0; | |
| 471 fStorageSize = 0; | |
| 472 fRunCount = 0; | |
| 473 fLastRun = 0; | |
| 474 fBounds.setEmpty(); | |
| 475 | |
| 476 return blob; | 323 return blob; |
| 477 } | 324 } |
| 478 | 325 |
| OLD | NEW |