blob: b815c263a8ddfcf659bc868d2b52d187cb946f10 [file] [log] [blame]
fmalita00d5c2c2014-08-21 08:53:26 -07001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkTextBlob.h"
9
fmalitab7425172014-08-26 07:56:44 -070010#include "SkReadBuffer.h"
11#include "SkWriteBuffer.h"
12
fmalita3c196de2014-09-20 05:40:22 -070013//
14// Textblob data is laid out into externally-managed storage as follows:
15//
16// -----------------------------------------------------------------------------
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
23SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
24
25class SkTextBlob::RunRecord {
26public:
27 RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning 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->glyphBuffer()) +
59 SkAlign4(fCount * sizeof(uint16_t)));
60 }
61
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));
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) <= (SkScalar*)Next(this));
84 }
85
86private:
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(fPositioning);
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
110SkTextBlob::SkTextBlob(int runCount, const SkRect& bounds)
111 : fRunCount(runCount)
fmalita00d5c2c2014-08-21 08:53:26 -0700112 , fBounds(bounds) {
113}
114
fmalita3c196de2014-09-20 05:40:22 -0700115SkTextBlob::~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
125void 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
fmalita00d5c2c2014-08-21 08:53:26 -0700132uint32_t SkTextBlob::uniqueID() const {
133 static int32_t gTextBlobGenerationID; // = 0;
134
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;
138 }
139
140 return fUniqueID;
141}
142
fmalitab7425172014-08-26 07:56:44 -0700143void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
fmalita3c196de2014-09-20 05:40:22 -0700144 int runCount = fRunCount;
fmalitab7425172014-08-26 07:56:44 -0700145
146 buffer.write32(runCount);
147 buffer.writeRect(fBounds);
148
149 SkPaint runPaint;
150 RunIterator it(this);
151 while (!it.done()) {
152 SkASSERT(it.glyphCount() > 0);
153
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);
160
161 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
162 buffer.writeByteArray(it.pos(),
163 it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
164
165 it.next();
166 SkDEBUGCODE(runCount--);
167 }
168 SkASSERT(0 == runCount);
169}
170
171const SkTextBlob* SkTextBlob::CreateFromBuffer(SkReadBuffer& reader) {
172 int runCount = reader.read32();
173 if (runCount < 0) {
174 return NULL;
175 }
176
177 SkRect bounds;
178 reader.readRect(&bounds);
179
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) {
185 return NULL;
186 }
187
188 SkPoint offset;
189 reader.readPoint(&offset);
190 SkPaint font;
191 reader.readPaint(&font);
192
193 const SkTextBlobBuilder::RunBuffer* buf = NULL;
194 switch (pos) {
195 case kDefault_Positioning:
196 buf = &blobBuilder.allocRun(font, glyphCount, offset.x(), offset.y(), &bounds);
197 break;
198 case kHorizontal_Positioning:
199 buf = &blobBuilder.allocRunPosH(font, glyphCount, offset.y(), &bounds);
200 break;
201 case kFull_Positioning:
202 buf = &blobBuilder.allocRunPos(font, glyphCount, &bounds);
203 break;
204 default:
205 return NULL;
206 }
207
208 if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
fmalita3c196de2014-09-20 05:40:22 -0700209 !reader.readByteArray(buf->pos,
210 glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
fmalitab7425172014-08-26 07:56:44 -0700211 return NULL;
212 }
213 }
214
215 return blobBuilder.build();
216}
217
fmalita3c196de2014-09-20 05:40:22 -0700218unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
219 // GlyphPositioning values are directly mapped to scalars-per-glyph.
220 SkASSERT(pos <= 2);
221 return pos;
222}
223
fmalita00d5c2c2014-08-21 08:53:26 -0700224SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob)
fmalita3c196de2014-09-20 05:40:22 -0700225 : fCurrentRun(RunRecord::First(blob))
226 , fRemainingRuns(blob->fRunCount) {
227 SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
fmalita00d5c2c2014-08-21 08:53:26 -0700228}
229
230bool SkTextBlob::RunIterator::done() const {
fmalita3c196de2014-09-20 05:40:22 -0700231 return fRemainingRuns <= 0;
fmalita00d5c2c2014-08-21 08:53:26 -0700232}
233
234void SkTextBlob::RunIterator::next() {
235 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700236
237 if (!this->done()) {
238 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
239 fCurrentRun = RunRecord::Next(fCurrentRun);
240 fRemainingRuns--;
241 }
fmalita00d5c2c2014-08-21 08:53:26 -0700242}
243
244uint32_t SkTextBlob::RunIterator::glyphCount() const {
245 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700246 return fCurrentRun->glyphCount();
fmalita00d5c2c2014-08-21 08:53:26 -0700247}
248
249const uint16_t* SkTextBlob::RunIterator::glyphs() const {
250 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700251 return fCurrentRun->glyphBuffer();
fmalita00d5c2c2014-08-21 08:53:26 -0700252}
253
254const SkScalar* SkTextBlob::RunIterator::pos() const {
255 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700256 return fCurrentRun->posBuffer();
fmalita00d5c2c2014-08-21 08:53:26 -0700257}
258
259const SkPoint& SkTextBlob::RunIterator::offset() const {
260 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700261 return fCurrentRun->offset();
fmalita00d5c2c2014-08-21 08:53:26 -0700262}
263
fmalita00d5c2c2014-08-21 08:53:26 -0700264SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const {
265 SkASSERT(!this->done());
fmalita3c196de2014-09-20 05:40:22 -0700266 return fCurrentRun->positioning();
fmalita00d5c2c2014-08-21 08:53:26 -0700267}
268
fmalita37ecbaf2014-08-22 09:01:19 -0700269void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const {
270 SkASSERT(!this->done());
271
fmalita3c196de2014-09-20 05:40:22 -0700272 const SkPaint& font = fCurrentRun->font();
fmalita37ecbaf2014-08-22 09:01:19 -0700273
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());
280
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));
295}
296
fmalita3c196de2014-09-20 05:40:22 -0700297SkTextBlobBuilder::SkTextBlobBuilder()
298 : fStorageSize(0)
299 , fStorageUsed(0)
300 , fRunCount(0)
301 , fDeferredBounds(false)
302 , fLastRun(0) {
fmalita00d5c2c2014-08-21 08:53:26 -0700303 fBounds.setEmpty();
304}
305
306SkTextBlobBuilder::~SkTextBlobBuilder() {
fmalita3c196de2014-09-20 05:40:22 -0700307 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.
310 build()->unref();
311 }
fmalita00d5c2c2014-08-21 08:53:26 -0700312}
313
314void SkTextBlobBuilder::updateDeferredBounds() {
fmalita3c196de2014-09-20 05:40:22 -0700315 SkASSERT(!fDeferredBounds || fRunCount > 0);
fmalita00d5c2c2014-08-21 08:53:26 -0700316
317 if (!fDeferredBounds) {
318 return;
319 }
320
321 // FIXME: measure the current run & union bounds
322 fDeferredBounds = false;
323}
324
fmalita3c196de2014-09-20 05:40:22 -0700325void SkTextBlobBuilder::reserve(size_t size) {
326 // We don't currently pre-allocate, but maybe someday...
327 if (fStorageUsed + size <= fStorageSize) {
328 return;
329 }
fmalita00d5c2c2014-08-21 08:53:26 -0700330
fmalita3c196de2014-09-20 05:40:22 -0700331 if (0 == fRunCount) {
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 SkPaint.
342 fStorage.realloc(fStorageSize);
343}
344
345bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning 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*>(fStorage.get() +
354 fLastRun);
355 SkASSERT(run->glyphCount() > 0);
356
357 if (run->positioning() != positioning
358 || run->font() != font
359 || (run->glyphCount() + count < run->glyphCount())) {
360 return false;
fmalita00d5c2c2014-08-21 08:53:26 -0700361 }
362
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
366 // y-offset
fmalita3c196de2014-09-20 05:40:22 -0700367 if (SkTextBlob::kFull_Positioning != positioning
368 && (SkTextBlob::kHorizontal_Positioning != positioning
369 || run->offset().y() != offset.y())) {
370 return false;
fmalita00d5c2c2014-08-21 08:53:26 -0700371 }
372
fmalita3c196de2014-09-20 05:40:22 -0700373 size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, positioning) -
374 SkTextBlob::RunRecord::StorageSize(run->glyphCount(), positioning);
375 this->reserve(sizeDelta);
fmalita00d5c2c2014-08-21 08:53:26 -0700376
fmalita3c196de2014-09-20 05:40:22 -0700377 // reserve may have realloced
378 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
379 uint32_t preMergeCount = run->glyphCount();
380 run->grow(count);
381
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);
386
387 fStorageUsed += sizeDelta;
388
389 SkASSERT(fStorageUsed <= fStorageSize);
390 run->validate(fStorage.get() + fStorageUsed);
391
392 return true;
fmalita00d5c2c2014-08-21 08:53:26 -0700393}
394
395void SkTextBlobBuilder::allocInternal(const SkPaint &font,
396 SkTextBlob::GlyphPositioning positioning,
397 int count, SkPoint offset, const SkRect* bounds) {
398 SkASSERT(count > 0);
fmalita3c196de2014-09-20 05:40:22 -0700399 SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
fmalita00d5c2c2014-08-21 08:53:26 -0700400
fmalita3c196de2014-09-20 05:40:22 -0700401 if (!this->mergeRun(font, positioning, count, offset)) {
402 updateDeferredBounds();
fmalita00d5c2c2014-08-21 08:53:26 -0700403
fmalita3c196de2014-09-20 05:40:22 -0700404 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning);
405 this->reserve(runSize);
fmalita00d5c2c2014-08-21 08:53:26 -0700406
fmalita3c196de2014-09-20 05:40:22 -0700407 SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
408 SkASSERT(fStorageUsed + runSize <= fStorageSize);
fmalita00d5c2c2014-08-21 08:53:26 -0700409
fmalita3c196de2014-09-20 05:40:22 -0700410 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
411 SkTextBlob::RunRecord(count, offset, font, positioning);
fmalita00d5c2c2014-08-21 08:53:26 -0700412
fmalita3c196de2014-09-20 05:40:22 -0700413 fCurrentRunBuffer.glyphs = run->glyphBuffer();
414 fCurrentRunBuffer.pos = run->posBuffer();
fmalita00d5c2c2014-08-21 08:53:26 -0700415
fmalita3c196de2014-09-20 05:40:22 -0700416 fLastRun = fStorageUsed;
417 fStorageUsed += runSize;
418 fRunCount++;
419
420 SkASSERT(fStorageUsed <= fStorageSize);
421 run->validate(fStorage.get() + fStorageUsed);
422 }
fmalita00d5c2c2014-08-21 08:53:26 -0700423
424 if (!fDeferredBounds) {
bsalomon49f085d2014-09-05 13:34:00 -0700425 if (bounds) {
fmalita00d5c2c2014-08-21 08:53:26 -0700426 fBounds.join(*bounds);
427 } else {
428 fDeferredBounds = true;
429 }
430 }
431}
432
433const 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);
437
438 return fCurrentRunBuffer;
439}
440
441const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunPosH(const SkPaint& font, int count,
442 SkScalar y,
443 const SkRect* bounds) {
444 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, SkPoint::Make(0, y),
445 bounds);
446
447 return fCurrentRunBuffer;
448}
449
450const 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);
453
454 return fCurrentRunBuffer;
455}
456
457const SkTextBlob* SkTextBlobBuilder::build() {
fmalita3c196de2014-09-20 05:40:22 -0700458 SkASSERT((fRunCount > 0) == (NULL != fStorage.get()));
fmalita00d5c2c2014-08-21 08:53:26 -0700459
fmalita3c196de2014-09-20 05:40:22 -0700460 this->updateDeferredBounds();
fmalita00d5c2c2014-08-21 08:53:26 -0700461
fmalita3c196de2014-09-20 05:40:22 -0700462 if (0 == fRunCount) {
463 SkASSERT(NULL == fStorage.get());
464 fStorageUsed = sizeof(SkTextBlob);
465 fStorage.realloc(fStorageUsed);
fmalita00d5c2c2014-08-21 08:53:26 -0700466 }
467
fmalita3c196de2014-09-20 05:40:22 -0700468 SkDEBUGCODE(
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);
476 }
477 SkASSERT(validateSize == fStorageUsed);
478 )
479
480 const SkTextBlob* blob = new (fStorage.detach()) SkTextBlob(fRunCount, fBounds);
481 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
482
483 fStorageUsed = 0;
484 fStorageSize = 0;
485 fRunCount = 0;
486 fLastRun = 0;
487 fBounds.setEmpty();
488
fmalita00d5c2c2014-08-21 08:53:26 -0700489 return blob;
490}
491