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 |