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) { |
| 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) |
18 , fBounds(bounds) { | 112 , fBounds(bounds) { |
19 } | 113 } |
20 | 114 |
| 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 |
21 uint32_t SkTextBlob::uniqueID() const { | 132 uint32_t SkTextBlob::uniqueID() const { |
22 static int32_t gTextBlobGenerationID; // = 0; | 133 static int32_t gTextBlobGenerationID; // = 0; |
23 | 134 |
24 // loop in case our global wraps around, as we never want to return SK_Inval
idGenID | 135 // loop in case our global wraps around, as we never want to return SK_Inval
idGenID |
25 while (SK_InvalidGenID == fUniqueID) { | 136 while (SK_InvalidGenID == fUniqueID) { |
26 fUniqueID = sk_atomic_inc(&gTextBlobGenerationID) + 1; | 137 fUniqueID = sk_atomic_inc(&gTextBlobGenerationID) + 1; |
27 } | 138 } |
28 | 139 |
29 return fUniqueID; | 140 return fUniqueID; |
30 } | 141 } |
31 | 142 |
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 { | 143 void SkTextBlob::flatten(SkWriteBuffer& buffer) const { |
39 int runCount = (NULL == fRuns.get()) ? 0 : fRuns->count(); | 144 int runCount = fRunCount; |
40 | 145 |
41 buffer.write32(runCount); | 146 buffer.write32(runCount); |
42 buffer.writeRect(fBounds); | 147 buffer.writeRect(fBounds); |
43 | 148 |
44 SkPaint runPaint; | 149 SkPaint runPaint; |
45 RunIterator it(this); | 150 RunIterator it(this); |
46 while (!it.done()) { | 151 while (!it.done()) { |
47 SkASSERT(it.glyphCount() > 0); | 152 SkASSERT(it.glyphCount() > 0); |
48 | 153 |
49 buffer.write32(it.glyphCount()); | 154 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); | 199 buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bound
s); |
95 break; | 200 break; |
96 case kFull_Positioning: | 201 case kFull_Positioning: |
97 buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds); | 202 buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds); |
98 break; | 203 break; |
99 default: | 204 default: |
100 return NULL; | 205 return NULL; |
101 } | 206 } |
102 | 207 |
103 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) || | 208 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) || |
104 !reader.readByteArray(buf->pos, glyphCount * sizeof(SkScalar) * Scal
arsPerGlyph(pos))) { | 209 !reader.readByteArray(buf->pos, |
| 210 glyphCount * sizeof(SkScalar) * ScalarsPerGlyp
h(pos))) { |
105 return NULL; | 211 return NULL; |
106 } | 212 } |
107 } | 213 } |
108 | 214 |
109 return blobBuilder.build(); | 215 return blobBuilder.build(); |
110 } | 216 } |
111 | 217 |
| 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 |
112 SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob) | 224 SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob) |
113 : fBlob(blob) | 225 : fCurrentRun(RunRecord::First(blob)) |
114 , fIndex(0) { | 226 , fRemainingRuns(blob->fRunCount) { |
115 SkASSERT(blob); | 227 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;) |
116 } | 228 } |
117 | 229 |
118 bool SkTextBlob::RunIterator::done() const { | 230 bool SkTextBlob::RunIterator::done() const { |
119 return NULL == fBlob->fRuns.get() || fIndex >= fBlob->fRuns->count(); | 231 return fRemainingRuns <= 0; |
120 } | 232 } |
121 | 233 |
122 void SkTextBlob::RunIterator::next() { | 234 void SkTextBlob::RunIterator::next() { |
123 SkASSERT(!this->done()); | 235 SkASSERT(!this->done()); |
124 fIndex++; | 236 |
| 237 if (!this->done()) { |
| 238 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);) |
| 239 fCurrentRun = RunRecord::Next(fCurrentRun); |
| 240 fRemainingRuns--; |
| 241 } |
125 } | 242 } |
126 | 243 |
127 uint32_t SkTextBlob::RunIterator::glyphCount() const { | 244 uint32_t SkTextBlob::RunIterator::glyphCount() const { |
128 SkASSERT(!this->done()); | 245 SkASSERT(!this->done()); |
129 return (*fBlob->fRuns)[fIndex].count; | 246 return fCurrentRun->glyphCount(); |
130 } | 247 } |
131 | 248 |
132 const uint16_t* SkTextBlob::RunIterator::glyphs() const { | 249 const uint16_t* SkTextBlob::RunIterator::glyphs() const { |
133 SkASSERT(!this->done()); | 250 SkASSERT(!this->done()); |
134 return fBlob->fGlyphBuffer.get() + (*fBlob->fRuns)[fIndex].glyphStart; | 251 return fCurrentRun->glyphBuffer(); |
135 } | 252 } |
136 | 253 |
137 const SkScalar* SkTextBlob::RunIterator::pos() const { | 254 const SkScalar* SkTextBlob::RunIterator::pos() const { |
138 SkASSERT(!this->done()); | 255 SkASSERT(!this->done()); |
139 return fBlob->fPosBuffer.get() + (*fBlob->fRuns)[fIndex].posStart; | 256 return fCurrentRun->posBuffer(); |
140 } | 257 } |
141 | 258 |
142 const SkPoint& SkTextBlob::RunIterator::offset() const { | 259 const SkPoint& SkTextBlob::RunIterator::offset() const { |
143 SkASSERT(!this->done()); | 260 SkASSERT(!this->done()); |
144 return (*fBlob->fRuns)[fIndex].offset; | 261 return fCurrentRun->offset(); |
145 } | 262 } |
146 | 263 |
147 SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const { | 264 SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const { |
148 SkASSERT(!this->done()); | 265 SkASSERT(!this->done()); |
149 return (*fBlob->fRuns)[fIndex].positioning; | 266 return fCurrentRun->positioning(); |
150 } | 267 } |
151 | 268 |
152 void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const { | 269 void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const { |
153 SkASSERT(!this->done()); | 270 SkASSERT(!this->done()); |
154 | 271 |
155 const SkPaint& font = (*fBlob->fRuns)[fIndex].font; | 272 const SkPaint& font = fCurrentRun->font(); |
156 | 273 |
157 paint->setTypeface(font.getTypeface()); | 274 paint->setTypeface(font.getTypeface()); |
158 paint->setTextEncoding(font.getTextEncoding()); | 275 paint->setTextEncoding(font.getTextEncoding()); |
159 paint->setTextSize(font.getTextSize()); | 276 paint->setTextSize(font.getTextSize()); |
160 paint->setTextScaleX(font.getTextScaleX()); | 277 paint->setTextScaleX(font.getTextScaleX()); |
161 paint->setTextSkewX(font.getTextSkewX()); | 278 paint->setTextSkewX(font.getTextSkewX()); |
162 paint->setHinting(font.getHinting()); | 279 paint->setHinting(font.getHinting()); |
163 | 280 |
164 uint32_t flagsMask = SkPaint::kAntiAlias_Flag | 281 uint32_t flagsMask = SkPaint::kAntiAlias_Flag |
165 | SkPaint::kUnderlineText_Flag | 282 | SkPaint::kUnderlineText_Flag |
166 | SkPaint::kStrikeThruText_Flag | 283 | SkPaint::kStrikeThruText_Flag |
167 | SkPaint::kFakeBoldText_Flag | 284 | SkPaint::kFakeBoldText_Flag |
168 | SkPaint::kLinearText_Flag | 285 | SkPaint::kLinearText_Flag |
169 | SkPaint::kSubpixelText_Flag | 286 | SkPaint::kSubpixelText_Flag |
170 | SkPaint::kDevKernText_Flag | 287 | SkPaint::kDevKernText_Flag |
171 | SkPaint::kLCDRenderText_Flag | 288 | SkPaint::kLCDRenderText_Flag |
172 | SkPaint::kEmbeddedBitmapText_Flag | 289 | SkPaint::kEmbeddedBitmapText_Flag |
173 | SkPaint::kAutoHinting_Flag | 290 | SkPaint::kAutoHinting_Flag |
174 | SkPaint::kVerticalText_Flag | 291 | SkPaint::kVerticalText_Flag |
175 | SkPaint::kGenA8FromLCD_Flag | 292 | SkPaint::kGenA8FromLCD_Flag |
176 | SkPaint::kDistanceFieldTextTEMP_Flag; | 293 | SkPaint::kDistanceFieldTextTEMP_Flag; |
177 paint->setFlags((paint->getFlags() & ~flagsMask) | (font.getFlags() & flagsM
ask)); | 294 paint->setFlags((paint->getFlags() & ~flagsMask) | (font.getFlags() & flagsM
ask)); |
178 } | 295 } |
179 | 296 |
180 SkTextBlobBuilder::SkTextBlobBuilder(unsigned runs) | 297 SkTextBlobBuilder::SkTextBlobBuilder() |
181 : fRuns(NULL) | 298 : fStorageSize(0) |
182 , fDeferredBounds(false) { | 299 , fStorageUsed(0) |
183 | 300 , fRunCount(0) |
184 if (runs > 0) { | 301 , fDeferredBounds(false) |
185 // if the number of runs is known, size our run storage accordingly. | 302 , fLastRun(0) { |
186 fRuns = SkNEW(SkTArray<SkTextBlob::Run>(runs)); | |
187 } | |
188 fBounds.setEmpty(); | 303 fBounds.setEmpty(); |
189 } | 304 } |
190 | 305 |
191 SkTextBlobBuilder::~SkTextBlobBuilder() { | 306 SkTextBlobBuilder::~SkTextBlobBuilder() { |
192 // unused runs | 307 if (NULL != fStorage.get()) { |
193 SkDELETE(fRuns); | 308 // We are abandoning runs and must destruct the associated font data. |
| 309 // The easiest way to accomplish that is to use the blob destructor. |
| 310 build()->unref(); |
| 311 } |
194 } | 312 } |
195 | 313 |
196 void SkTextBlobBuilder::updateDeferredBounds() { | 314 void SkTextBlobBuilder::updateDeferredBounds() { |
197 SkASSERT(!fDeferredBounds || (fRuns && !fRuns->empty())); | 315 SkASSERT(!fDeferredBounds || fRunCount > 0); |
198 | 316 |
199 if (!fDeferredBounds) { | 317 if (!fDeferredBounds) { |
200 return; | 318 return; |
201 } | 319 } |
202 | 320 |
203 // FIXME: measure the current run & union bounds | 321 // FIXME: measure the current run & union bounds |
204 fDeferredBounds = false; | 322 fDeferredBounds = false; |
205 } | 323 } |
206 | 324 |
207 void SkTextBlobBuilder::ensureRun(const SkPaint& font, SkTextBlob::GlyphPosition
ing pos, | 325 void SkTextBlobBuilder::reserve(size_t size) { |
208 const SkPoint& offset) { | 326 // We don't currently pre-allocate, but maybe someday... |
209 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding()); | 327 if (fStorageUsed + size <= fStorageSize) { |
| 328 return; |
| 329 } |
210 | 330 |
211 if (NULL == fRuns) { | 331 if (0 == fRunCount) { |
212 fRuns = SkNEW(SkTArray<SkTextBlob::Run>()); | 332 SkASSERT(NULL == fStorage.get()); |
| 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; |
213 } | 361 } |
214 | 362 |
215 // we can merge same-font/same-positioning runs in the following cases: | 363 // we can merge same-font/same-positioning runs in the following cases: |
216 // * fully positioned run following another fully positioned run | 364 // * fully positioned run following another fully positioned run |
217 // * horizontally postioned run following another horizontally positioned
run with the same | 365 // * horizontally postioned run following another horizontally positioned
run with the same |
218 // y-offset | 366 // y-offset |
219 if (!fRuns->empty() | 367 if (SkTextBlob::kFull_Positioning != positioning |
220 && fRuns->back().positioning == pos | 368 && (SkTextBlob::kHorizontal_Positioning != positioning |
221 && fRuns->back().font == font | 369 || run->offset().y() != offset.y())) { |
222 && (SkTextBlob::kFull_Positioning == fRuns->back().positioning | 370 return false; |
223 || (SkTextBlob::kHorizontal_Positioning == fRuns->back().positioning | |
224 && fRuns->back().offset.y() == offset.y()))){ | |
225 return; | |
226 } | 371 } |
227 | 372 |
228 this->updateDeferredBounds(); | 373 size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + co
unt, positioning) - |
| 374 SkTextBlob::RunRecord::StorageSize(run->glyphCount(), pos
itioning); |
| 375 this->reserve(sizeDelta); |
229 | 376 |
230 // start a new run | 377 // reserve may have realloced |
231 SkTextBlob::Run& newRun = fRuns->push_back(); | 378 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun); |
232 newRun.count = 0; | 379 uint32_t preMergeCount = run->glyphCount(); |
233 newRun.glyphStart = fGlyphBuffer.count(); | 380 run->grow(count); |
234 newRun.posStart = fPosBuffer.count(); | 381 |
235 newRun.offset = offset; | 382 // Callers expect the buffers to point at the newly added slice, ant not at
the beginning. |
236 newRun.font = font; | 383 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount; |
237 newRun.positioning = pos; | 384 fCurrentRunBuffer.pos = run->posBuffer() |
| 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; |
238 } | 393 } |
239 | 394 |
240 void SkTextBlobBuilder::allocInternal(const SkPaint &font, | 395 void SkTextBlobBuilder::allocInternal(const SkPaint &font, |
241 SkTextBlob::GlyphPositioning positioning, | 396 SkTextBlob::GlyphPositioning positioning, |
242 int count, SkPoint offset, const SkRect* b
ounds) { | 397 int count, SkPoint offset, const SkRect* b
ounds) { |
243 SkASSERT(count > 0); | 398 SkASSERT(count > 0); |
| 399 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding()); |
244 | 400 |
245 this->ensureRun(font, positioning, offset); | 401 if (!this->mergeRun(font, positioning, count, offset)) { |
| 402 updateDeferredBounds(); |
246 | 403 |
247 unsigned posScalarsPerGlyph = SkTextBlob::ScalarsPerGlyph(positioning); | 404 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning); |
| 405 this->reserve(runSize); |
248 | 406 |
249 fGlyphBuffer.append(count); | 407 SkASSERT(fStorageUsed >= sizeof(SkTextBlob)); |
250 fPosBuffer.append(count * posScalarsPerGlyph); | 408 SkASSERT(fStorageUsed + runSize <= fStorageSize); |
251 | 409 |
252 SkASSERT(fRuns && !fRuns->empty()); | 410 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed) |
253 SkTextBlob::Run& run = fRuns->back(); | 411 SkTextBlob::RunRecord(count, offset, fo
nt, positioning); |
254 | 412 |
255 run.count += count; | 413 fCurrentRunBuffer.glyphs = run->glyphBuffer(); |
| 414 fCurrentRunBuffer.pos = run->posBuffer(); |
256 | 415 |
257 // The current run might have been merged, so the start offset may point to
prev run data. | 416 fLastRun = fStorageUsed; |
258 // Start from the back (which always points to the end of the current run bu
ffers) instead. | 417 fStorageUsed += runSize; |
259 fCurrentRunBuffer.glyphs = fGlyphBuffer.isEmpty() | 418 fRunCount++; |
260 ? NULL : fGlyphBuffer.end() - count; | 419 |
261 SkASSERT(NULL == fCurrentRunBuffer.glyphs || fCurrentRunBuffer.glyphs >= fGl
yphBuffer.begin()); | 420 SkASSERT(fStorageUsed <= fStorageSize); |
262 fCurrentRunBuffer.pos = fPosBuffer.isEmpty() | 421 run->validate(fStorage.get() + fStorageUsed); |
263 ? NULL : fPosBuffer.end() - count * posScalarsPerGlyph; | 422 } |
264 SkASSERT(NULL == fCurrentRunBuffer.pos || fCurrentRunBuffer.pos >= fPosBuffe
r.begin()); | |
265 | 423 |
266 if (!fDeferredBounds) { | 424 if (!fDeferredBounds) { |
267 if (bounds) { | 425 if (bounds) { |
268 fBounds.join(*bounds); | 426 fBounds.join(*bounds); |
269 } else { | 427 } else { |
270 fDeferredBounds = true; | 428 fDeferredBounds = true; |
271 } | 429 } |
272 } | 430 } |
273 } | 431 } |
274 | 432 |
(...skipping 15 matching lines...) Expand all Loading... |
290 } | 448 } |
291 | 449 |
292 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint
& font, int count, | 450 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint
& font, int count, |
293 const SkRect
*bounds) { | 451 const SkRect
*bounds) { |
294 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Mak
e(0, 0), bounds); | 452 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Mak
e(0, 0), bounds); |
295 | 453 |
296 return fCurrentRunBuffer; | 454 return fCurrentRunBuffer; |
297 } | 455 } |
298 | 456 |
299 const SkTextBlob* SkTextBlobBuilder::build() { | 457 const SkTextBlob* SkTextBlobBuilder::build() { |
300 const SkTextBlob* blob; | 458 SkASSERT((fRunCount > 0) == (NULL != fStorage.get())); |
301 | 459 |
302 if (fGlyphBuffer.count() > 0) { | 460 this->updateDeferredBounds(); |
303 // we have some glyphs, construct a real blob | |
304 SkASSERT(fRuns && !fRuns->empty()); | |
305 | 461 |
306 this->updateDeferredBounds(); | 462 if (0 == fRunCount) { |
| 463 SkASSERT(NULL == fStorage.get()); |
| 464 fStorageUsed = sizeof(SkTextBlob); |
| 465 fStorage.realloc(fStorageUsed); |
| 466 } |
307 | 467 |
308 // ownership of all buffers is transferred to the blob | 468 SkDEBUGCODE( |
309 blob = SkNEW_ARGS(SkTextBlob, (fGlyphBuffer.detach(), | 469 size_t validateSize = sizeof(SkTextBlob); |
310 fPosBuffer.detach(), | 470 const SkTextBlob::RunRecord* run = |
311 fRuns, | 471 SkTextBlob::RunRecord::First(reinterpret_cast<const SkTextBlob*>(fSt
orage.get())); |
312 fBounds)); | 472 for (int i = 0; i < fRunCount; ++i) { |
313 fRuns = NULL; | 473 validateSize += SkTextBlob::RunRecord::StorageSize(run->fCount, run-
>fPositioning); |
314 fBounds.setEmpty(); | 474 run->validate(fStorage.get() + fStorageUsed); |
315 } else { | 475 run = SkTextBlob::RunRecord::Next(run); |
316 // empty blob | 476 } |
317 SkASSERT(NULL == fRuns || fRuns->empty()); | 477 SkASSERT(validateSize == fStorageUsed); |
318 SkASSERT(fBounds.isEmpty()); | 478 ) |
319 | 479 |
320 blob = SkNEW_ARGS(SkTextBlob, (NULL, NULL, NULL, SkRect::MakeEmpty())); | 480 const SkTextBlob* blob = new (fStorage.detach()) SkTextBlob(fRunCount, fBoun
ds); |
321 } | 481 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;) |
| 482 |
| 483 fStorageUsed = 0; |
| 484 fStorageSize = 0; |
| 485 fRunCount = 0; |
| 486 fLastRun = 0; |
| 487 fBounds.setEmpty(); |
322 | 488 |
323 return blob; | 489 return blob; |
324 } | 490 } |
325 | 491 |
OLD | NEW |