2 * Copyright 2014 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkTextBlob.h"
10 #include "SkReadBuffer.h"
11 #include "SkWriteBuffer.h"
14 // Textblob data is laid out into externally-managed storage as follows:
16 // -----------------------------------------------------------------------------
17 // | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
18 // -----------------------------------------------------------------------------
20 // Each run record describes a text blob run, and can be used to determine the (implicit)
21 // location of the following record.
23 SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
25 class SkTextBlob::RunRecord {
27 RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
32 SkDEBUGCODE(fMagic = kRunRecordMagic);
35 uint32_t glyphCount() const {
39 const SkPoint& offset() const {
43 const SkPaint& font() const {
47 GlyphPositioning positioning() const {
51 uint16_t* glyphBuffer() const {
52 // Glyph are stored immediately following the record.
53 return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
56 SkScalar* posBuffer() const {
57 // Position scalars follow the (aligned) glyph buffer.
58 return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
59 SkAlign4(fCount * sizeof(uint16_t)));
62 static size_t StorageSize(int glyphCount, SkTextBlob::GlyphPositioning positioning) {
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(positioning));
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);
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()));
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) <= (SkScalar*)Next(this));
87 friend class SkTextBlobBuilder;
89 void grow(uint32_t count) {
90 SkScalar* initialPosBuffer = posBuffer();
91 uint32_t initialCount = fCount;
94 // Move the initial pos scalars to their new location.
95 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(fPositioning);
96 SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)Next(this));
98 // memmove, as the buffers may overlap
99 memmove(posBuffer(), initialPosBuffer, copySize);
105 GlyphPositioning fPositioning;
107 SkDEBUGCODE(unsigned fMagic;)
110 SkTextBlob::SkTextBlob(int runCount, const SkRect& bounds)
111 : fRunCount(runCount)
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);)
125 void SkTextBlob::internal_dispose() const {
126 // SkTextBlobs use externally-managed storage.
127 this->internal_dispose_restore_refcnt_to_1();
129 sk_free(const_cast<SkTextBlob*>(this));
132 uint32_t SkTextBlob::uniqueID() const {
133 static int32_t gTextBlobGenerationID; // = 0;
135 // loop in case our global wraps around, as we never want to return SK_InvalidGenID
136 while (SK_InvalidGenID == fUniqueID) {
137 fUniqueID = sk_atomic_inc(&gTextBlobGenerationID) + 1;
143 void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
144 int runCount = fRunCount;
146 buffer.write32(runCount);
147 buffer.writeRect(fBounds);
150 RunIterator it(this);
152 SkASSERT(it.glyphCount() > 0);
154 buffer.write32(it.glyphCount());
155 buffer.write32(it.positioning());
156 buffer.writePoint(it.offset());
157 // This should go away when switching to SkFont
158 it.applyFontToPaint(&runPaint);
159 buffer.writePaint(runPaint);
161 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
162 buffer.writeByteArray(it.pos(),
163 it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
166 SkDEBUGCODE(runCount--);
168 SkASSERT(0 == runCount);
171 const SkTextBlob* SkTextBlob::CreateFromBuffer(SkReadBuffer& reader) {
172 int runCount = reader.read32();
178 reader.readRect(&bounds);
180 SkTextBlobBuilder blobBuilder;
181 for (int i = 0; i < runCount; ++i) {
182 int glyphCount = reader.read32();
183 GlyphPositioning pos = static_cast<GlyphPositioning>(reader.read32());
184 if (glyphCount <= 0 || pos > kFull_Positioning) {
189 reader.readPoint(&offset);
191 reader.readPaint(&font);
193 const SkTextBlobBuilder::RunBuffer* buf = NULL;
195 case kDefault_Positioning:
196 buf = &blobBuilder.allocRun(font, glyphCount, offset.x(), offset.y(), &bounds);
198 case kHorizontal_Positioning:
199 buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bounds);
201 case kFull_Positioning:
202 buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds);
208 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
209 !reader.readByteArray(buf->pos,
210 glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
215 return blobBuilder.build();
218 unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
219 // GlyphPositioning values are directly mapped to scalars-per-glyph.
224 SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob)
225 : fCurrentRun(RunRecord::First(blob))
226 , fRemainingRuns(blob->fRunCount) {
227 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
230 bool SkTextBlob::RunIterator::done() const {
231 return fRemainingRuns <= 0;
234 void SkTextBlob::RunIterator::next() {
235 SkASSERT(!this->done());
238 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
239 fCurrentRun = RunRecord::Next(fCurrentRun);
244 uint32_t SkTextBlob::RunIterator::glyphCount() const {
245 SkASSERT(!this->done());
246 return fCurrentRun->glyphCount();
249 const uint16_t* SkTextBlob::RunIterator::glyphs() const {
250 SkASSERT(!this->done());
251 return fCurrentRun->glyphBuffer();
254 const SkScalar* SkTextBlob::RunIterator::pos() const {
255 SkASSERT(!this->done());
256 return fCurrentRun->posBuffer();
259 const SkPoint& SkTextBlob::RunIterator::offset() const {
260 SkASSERT(!this->done());
261 return fCurrentRun->offset();
264 SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const {
265 SkASSERT(!this->done());
266 return fCurrentRun->positioning();
269 void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const {
270 SkASSERT(!this->done());
272 const SkPaint& font = fCurrentRun->font();
274 paint->setTypeface(font.getTypeface());
275 paint->setTextEncoding(font.getTextEncoding());
276 paint->setTextSize(font.getTextSize());
277 paint->setTextScaleX(font.getTextScaleX());
278 paint->setTextSkewX(font.getTextSkewX());
279 paint->setHinting(font.getHinting());
281 uint32_t flagsMask = SkPaint::kAntiAlias_Flag
282 | SkPaint::kUnderlineText_Flag
283 | SkPaint::kStrikeThruText_Flag
284 | SkPaint::kFakeBoldText_Flag
285 | SkPaint::kLinearText_Flag
286 | SkPaint::kSubpixelText_Flag
287 | SkPaint::kDevKernText_Flag
288 | SkPaint::kLCDRenderText_Flag
289 | SkPaint::kEmbeddedBitmapText_Flag
290 | SkPaint::kAutoHinting_Flag
291 | SkPaint::kVerticalText_Flag
292 | SkPaint::kGenA8FromLCD_Flag
293 | SkPaint::kDistanceFieldTextTEMP_Flag;
294 paint->setFlags((paint->getFlags() & ~flagsMask) | (font.getFlags() & flagsMask));
297 SkTextBlobBuilder::SkTextBlobBuilder()
301 , fDeferredBounds(false)
306 SkTextBlobBuilder::~SkTextBlobBuilder() {
307 if (NULL != fStorage.get()) {
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.
314 void SkTextBlobBuilder::updateDeferredBounds() {
315 SkASSERT(!fDeferredBounds || fRunCount > 0);
317 if (!fDeferredBounds) {
321 // FIXME: measure the current run & union bounds
322 fDeferredBounds = false;
325 void SkTextBlobBuilder::reserve(size_t size) {
326 // We don't currently pre-allocate, but maybe someday...
327 if (fStorageUsed + size <= fStorageSize) {
331 if (0 == fRunCount) {
332 SkASSERT(NULL == fStorage.get());
333 SkASSERT(0 == fStorageSize);
334 SkASSERT(0 == fStorageUsed);
336 // the first allocation also includes blob storage
337 fStorageUsed += sizeof(SkTextBlob);
340 fStorageSize = fStorageUsed + size;
341 // FYI: This relies on everything we store being relocatable, particularly SkPaint.
342 fStorage.realloc(fStorageSize);
345 bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
346 int count, SkPoint offset) {
348 SkASSERT(0 == fRunCount);
352 SkASSERT(fLastRun >= sizeof(SkTextBlob));
353 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
355 SkASSERT(run->glyphCount() > 0);
357 if (run->positioning() != positioning
358 || run->font() != font
359 || (run->glyphCount() + count < run->glyphCount())) {
363 // we can merge same-font/same-positioning runs in the following cases:
364 // * fully positioned run following another fully positioned run
365 // * horizontally postioned run following another horizontally positioned run with the same
367 if (SkTextBlob::kFull_Positioning != positioning
368 && (SkTextBlob::kHorizontal_Positioning != positioning
369 || run->offset().y() != offset.y())) {
373 size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, positioning) -
374 SkTextBlob::RunRecord::StorageSize(run->glyphCount(), positioning);
375 this->reserve(sizeDelta);
377 // reserve may have realloced
378 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
379 uint32_t preMergeCount = run->glyphCount();
382 // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
383 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
384 fCurrentRunBuffer.pos = run->posBuffer()
385 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
387 fStorageUsed += sizeDelta;
389 SkASSERT(fStorageUsed <= fStorageSize);
390 run->validate(fStorage.get() + fStorageUsed);
395 void SkTextBlobBuilder::allocInternal(const SkPaint &font,
396 SkTextBlob::GlyphPositioning positioning,
397 int count, SkPoint offset, const SkRect* bounds) {
399 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
401 if (!this->mergeRun(font, positioning, count, offset)) {
402 updateDeferredBounds();
404 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning);
405 this->reserve(runSize);
407 SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
408 SkASSERT(fStorageUsed + runSize <= fStorageSize);
410 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
411 SkTextBlob::RunRecord(count, offset, font, positioning);
413 fCurrentRunBuffer.glyphs = run->glyphBuffer();
414 fCurrentRunBuffer.pos = run->posBuffer();
416 fLastRun = fStorageUsed;
417 fStorageUsed += runSize;
420 SkASSERT(fStorageUsed <= fStorageSize);
421 run->validate(fStorage.get() + fStorageUsed);
424 if (!fDeferredBounds) {
426 fBounds.join(*bounds);
428 fDeferredBounds = true;
433 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRun(const SkPaint& font, int count,
434 SkScalar x, SkScalar y,
435 const SkRect* bounds) {
436 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, SkPoint::Make(x, y), bounds);
438 return fCurrentRunBuffer;
441 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkPaint& font, int count,
443 const SkRect* bounds) {
444 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, SkPoint::Make(0, y),
447 return fCurrentRunBuffer;
450 const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPos(const SkPaint& font, int count,
451 const SkRect *bounds) {
452 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, SkPoint::Make(0, 0), bounds);
454 return fCurrentRunBuffer;
457 const SkTextBlob* SkTextBlobBuilder::build() {
458 SkASSERT((fRunCount > 0) == (NULL != fStorage.get()));
460 this->updateDeferredBounds();
462 if (0 == fRunCount) {
463 SkASSERT(NULL == fStorage.get());
464 fStorageUsed = sizeof(SkTextBlob);
465 fStorage.realloc(fStorageUsed);
469 size_t validateSize = sizeof(SkTextBlob);
470 const SkTextBlob::RunRecord* run =
471 SkTextBlob::RunRecord::First(reinterpret_cast<const SkTextBlob*>(fStorage.get()));
472 for (int i = 0; i < fRunCount; ++i) {
473 validateSize += SkTextBlob::RunRecord::StorageSize(run->fCount, run->fPositioning);
474 run->validate(fStorage.get() + fStorageUsed);
475 run = SkTextBlob::RunRecord::Next(run);
477 SkASSERT(validateSize == fStorageUsed);
480 const SkTextBlob* blob = new (fStorage.detach()) SkTextBlob(fRunCount, fBounds);
481 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)