Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(560)

Side by Side Diff: src/core/SkTextBlob.cpp

Issue 581173003: Souped-up SkTextBlob (reland) (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: updated assert + extra build-time validation Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « include/core/SkTextBlob.h ('k') | tests/TextBlobTest.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « include/core/SkTextBlob.h ('k') | tests/TextBlobTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698