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