1/*
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
10#include "SkReadBuffer.h"
11#include "SkWriteBuffer.h"
12
13//
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)
112    , fBounds(bounds) {
113}
114
115SkTextBlob::~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
132uint32_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
143void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
144    int runCount = fRunCount;
145
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)) ||
209            !reader.readByteArray(buf->pos,
210                                  glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
211            return NULL;
212        }
213    }
214
215    return blobBuilder.build();
216}
217
218unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
219    // GlyphPositioning values are directly mapped to scalars-per-glyph.
220    SkASSERT(pos <= 2);
221    return pos;
222}
223
224SkTextBlob::RunIterator::RunIterator(const SkTextBlob* blob)
225    : fCurrentRun(RunRecord::First(blob))
226    , fRemainingRuns(blob->fRunCount) {
227    SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
228}
229
230bool SkTextBlob::RunIterator::done() const {
231    return fRemainingRuns <= 0;
232}
233
234void SkTextBlob::RunIterator::next() {
235    SkASSERT(!this->done());
236
237    if (!this->done()) {
238        SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
239        fCurrentRun = RunRecord::Next(fCurrentRun);
240        fRemainingRuns--;
241    }
242}
243
244uint32_t SkTextBlob::RunIterator::glyphCount() const {
245    SkASSERT(!this->done());
246    return fCurrentRun->glyphCount();
247}
248
249const uint16_t* SkTextBlob::RunIterator::glyphs() const {
250    SkASSERT(!this->done());
251    return fCurrentRun->glyphBuffer();
252}
253
254const SkScalar* SkTextBlob::RunIterator::pos() const {
255    SkASSERT(!this->done());
256    return fCurrentRun->posBuffer();
257}
258
259const SkPoint& SkTextBlob::RunIterator::offset() const {
260    SkASSERT(!this->done());
261    return fCurrentRun->offset();
262}
263
264SkTextBlob::GlyphPositioning SkTextBlob::RunIterator::positioning() const {
265    SkASSERT(!this->done());
266    return fCurrentRun->positioning();
267}
268
269void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const {
270    SkASSERT(!this->done());
271
272    const SkPaint& font = fCurrentRun->font();
273
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
297SkTextBlobBuilder::SkTextBlobBuilder()
298    : fStorageSize(0)
299    , fStorageUsed(0)
300    , fRunCount(0)
301    , fDeferredBounds(false)
302    , fLastRun(0) {
303    fBounds.setEmpty();
304}
305
306SkTextBlobBuilder::~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.
310        build()->unref();
311    }
312}
313
314void SkTextBlobBuilder::updateDeferredBounds() {
315    SkASSERT(!fDeferredBounds || fRunCount > 0);
316
317    if (!fDeferredBounds) {
318        return;
319    }
320
321    // FIXME: measure the current run & union bounds
322    fDeferredBounds = false;
323}
324
325void SkTextBlobBuilder::reserve(size_t size) {
326    // We don't currently pre-allocate, but maybe someday...
327    if (fStorageUsed + size <= fStorageSize) {
328        return;
329    }
330
331    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;
361    }
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
367    if (SkTextBlob::kFull_Positioning != positioning
368        && (SkTextBlob::kHorizontal_Positioning != positioning
369            || run->offset().y() != offset.y())) {
370        return false;
371    }
372
373    size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, positioning) -
374                       SkTextBlob::RunRecord::StorageSize(run->glyphCount(), positioning);
375    this->reserve(sizeDelta);
376
377    // 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;
393}
394
395void SkTextBlobBuilder::allocInternal(const SkPaint &font,
396                                      SkTextBlob::GlyphPositioning positioning,
397                                      int count, SkPoint offset, const SkRect* bounds) {
398    SkASSERT(count > 0);
399    SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
400
401    if (!this->mergeRun(font, positioning, count, offset)) {
402        updateDeferredBounds();
403
404        size_t runSize = SkTextBlob::RunRecord::StorageSize(count, positioning);
405        this->reserve(runSize);
406
407        SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
408        SkASSERT(fStorageUsed + runSize <= fStorageSize);
409
410        SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
411                                         SkTextBlob::RunRecord(count, offset, font, positioning);
412
413        fCurrentRunBuffer.glyphs = run->glyphBuffer();
414        fCurrentRunBuffer.pos = run->posBuffer();
415
416        fLastRun = fStorageUsed;
417        fStorageUsed += runSize;
418        fRunCount++;
419
420        SkASSERT(fStorageUsed <= fStorageSize);
421        run->validate(fStorage.get() + fStorageUsed);
422    }
423
424    if (!fDeferredBounds) {
425        if (bounds) {
426            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() {
458    SkASSERT((fRunCount > 0) == (NULL != fStorage.get()));
459
460    this->updateDeferredBounds();
461
462    if (0 == fRunCount) {
463        SkASSERT(NULL == fStorage.get());
464        fStorageUsed = sizeof(SkTextBlob);
465        fStorage.realloc(fStorageUsed);
466    }
467
468    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
489    return blob;
490}
491
492