Line data Source code
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 "SkTextBlobRunIterator.h"
9 :
10 : #include "SkReadBuffer.h"
11 : #include "SkTypeface.h"
12 : #include "SkWriteBuffer.h"
13 :
14 : #if SK_SUPPORT_GPU
15 : #include "text/GrTextBlobCache.h"
16 : #endif
17 :
18 : namespace {
19 :
20 : // TODO(fmalita): replace with SkFont.
21 0 : class RunFont : SkNoncopyable {
22 : public:
23 0 : RunFont(const SkPaint& paint)
24 0 : : fSize(paint.getTextSize())
25 0 : , fScaleX(paint.getTextScaleX())
26 : , fTypeface(SkSafeRef(paint.getTypeface()))
27 0 : , fSkewX(paint.getTextSkewX())
28 0 : , fAlign(paint.getTextAlign())
29 0 : , fHinting(paint.getHinting())
30 0 : , fFlags(paint.getFlags() & kFlagsMask) { }
31 :
32 0 : void applyToPaint(SkPaint* paint) const {
33 0 : paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
34 0 : paint->setTypeface(fTypeface);
35 0 : paint->setTextSize(fSize);
36 0 : paint->setTextScaleX(fScaleX);
37 0 : paint->setTextSkewX(fSkewX);
38 0 : paint->setTextAlign(static_cast<SkPaint::Align>(fAlign));
39 0 : paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
40 :
41 0 : paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
42 0 : }
43 :
44 0 : bool operator==(const RunFont& other) const {
45 0 : return fTypeface == other.fTypeface
46 0 : && fSize == other.fSize
47 0 : && fScaleX == other.fScaleX
48 0 : && fSkewX == other.fSkewX
49 0 : && fAlign == other.fAlign
50 0 : && fHinting == other.fHinting
51 0 : && fFlags == other.fFlags;
52 : }
53 :
54 0 : bool operator!=(const RunFont& other) const {
55 0 : return !(*this == other);
56 : }
57 :
58 0 : uint32_t flags() const { return fFlags; }
59 :
60 : private:
61 : const static uint32_t kFlagsMask =
62 : SkPaint::kAntiAlias_Flag |
63 : SkPaint::kFakeBoldText_Flag |
64 : SkPaint::kLinearText_Flag |
65 : SkPaint::kSubpixelText_Flag |
66 : SkPaint::kDevKernText_Flag |
67 : SkPaint::kLCDRenderText_Flag |
68 : SkPaint::kEmbeddedBitmapText_Flag |
69 : SkPaint::kAutoHinting_Flag |
70 : SkPaint::kVerticalText_Flag |
71 : SkPaint::kGenA8FromLCD_Flag;
72 :
73 : SkScalar fSize;
74 : SkScalar fScaleX;
75 :
76 : // Keep this sk_sp off the first position, to avoid interfering with SkNoncopyable
77 : // empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694).
78 : sk_sp<SkTypeface> fTypeface;
79 : SkScalar fSkewX;
80 :
81 : static_assert(SkPaint::kAlignCount < 4, "insufficient_align_bits");
82 : uint32_t fAlign : 2;
83 : static_assert(SkPaint::kFull_Hinting < 4, "insufficient_hinting_bits");
84 : uint32_t fHinting : 2;
85 : static_assert((kFlagsMask & 0xffff) == kFlagsMask, "insufficient_flags_bits");
86 : uint32_t fFlags : 16;
87 :
88 : typedef SkNoncopyable INHERITED;
89 : };
90 :
91 : struct RunFontStorageEquivalent {
92 : SkScalar fSize, fScaleX;
93 : void* fTypeface;
94 : SkScalar fSkewX;
95 : uint32_t fFlags;
96 : };
97 : static_assert(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
98 :
99 : } // anonymous namespace
100 :
101 : //
102 : // Textblob data is laid out into externally-managed storage as follows:
103 : //
104 : // -----------------------------------------------------------------------------
105 : // | SkTextBlob | RunRecord | Glyphs[] | Pos[] | RunRecord | Glyphs[] | Pos[] | ...
106 : // -----------------------------------------------------------------------------
107 : //
108 : // Each run record describes a text blob run, and can be used to determine the (implicit)
109 : // location of the following record.
110 : //
111 : // Extended Textblob runs have more data after the Pos[] array:
112 : //
113 : // -------------------------------------------------------------------------
114 : // ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ...
115 : // -------------------------------------------------------------------------
116 : //
117 : // To determine the length of the extended run data, the TextSize must be read.
118 : //
119 : // Extended Textblob runs may be mixed with non-extended runs.
120 :
121 : SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
122 :
123 : namespace {
124 : struct RunRecordStorageEquivalent {
125 : RunFont fFont;
126 : SkPoint fOffset;
127 : uint32_t fCount;
128 : uint32_t fFlags;
129 : SkDEBUGCODE(unsigned fMagic;)
130 : };
131 : }
132 :
133 0 : class SkTextBlob::RunRecord {
134 : public:
135 0 : RunRecord(uint32_t count, uint32_t textSize, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
136 0 : : fFont(font)
137 : , fCount(count)
138 : , fOffset(offset)
139 0 : , fFlags(pos) {
140 0 : SkASSERT(static_cast<unsigned>(pos) <= Flags::kPositioning_Mask);
141 :
142 0 : SkDEBUGCODE(fMagic = kRunRecordMagic);
143 0 : if (textSize > 0) {
144 0 : fFlags |= kExtended_Flag;
145 0 : *this->textSizePtr() = textSize;
146 : }
147 0 : }
148 :
149 0 : uint32_t glyphCount() const {
150 0 : return fCount;
151 : }
152 :
153 0 : const SkPoint& offset() const {
154 0 : return fOffset;
155 : }
156 :
157 0 : const RunFont& font() const {
158 0 : return fFont;
159 : }
160 :
161 0 : GlyphPositioning positioning() const {
162 0 : return static_cast<GlyphPositioning>(fFlags & kPositioning_Mask);
163 : }
164 :
165 0 : uint16_t* glyphBuffer() const {
166 : static_assert(SkIsAlignPtr(sizeof(RunRecord)), "");
167 : // Glyphs are stored immediately following the record.
168 0 : return reinterpret_cast<uint16_t*>(const_cast<RunRecord*>(this) + 1);
169 : }
170 :
171 0 : SkScalar* posBuffer() const {
172 : // Position scalars follow the (aligned) glyph buffer.
173 0 : return reinterpret_cast<SkScalar*>(reinterpret_cast<uint8_t*>(this->glyphBuffer()) +
174 0 : SkAlign4(fCount * sizeof(uint16_t)));
175 : }
176 :
177 0 : uint32_t textSize() const { return isExtended() ? *this->textSizePtr() : 0; }
178 :
179 0 : uint32_t* clusterBuffer() const {
180 : // clusters follow the textSize.
181 0 : return isExtended() ? 1 + this->textSizePtr() : nullptr;
182 : }
183 :
184 0 : char* textBuffer() const {
185 0 : return isExtended()
186 0 : ? reinterpret_cast<char*>(this->clusterBuffer() + fCount)
187 0 : : nullptr;
188 : }
189 :
190 0 : static size_t StorageSize(int glyphCount, int textSize,
191 : SkTextBlob::GlyphPositioning positioning) {
192 : static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
193 : // RunRecord object + (aligned) glyph buffer + position buffer
194 : size_t size = sizeof(SkTextBlob::RunRecord)
195 0 : + SkAlign4(glyphCount* sizeof(uint16_t))
196 0 : + PosCount(glyphCount, positioning) * sizeof(SkScalar);
197 0 : if (textSize > 0) { // Extended run.
198 0 : size += sizeof(uint32_t)
199 : + sizeof(uint32_t) * glyphCount
200 0 : + textSize;
201 : }
202 0 : return SkAlignPtr(size);
203 : }
204 :
205 0 : static const RunRecord* First(const SkTextBlob* blob) {
206 : // The first record (if present) is stored following the blob object.
207 0 : return reinterpret_cast<const RunRecord*>(blob + 1);
208 : }
209 :
210 0 : static const RunRecord* Next(const RunRecord* run) {
211 0 : return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
212 : }
213 :
214 0 : void validate(const uint8_t* storageTop) const {
215 0 : SkASSERT(kRunRecordMagic == fMagic);
216 0 : SkASSERT((uint8_t*)NextUnchecked(this) <= storageTop);
217 :
218 0 : SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
219 0 : SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
220 : <= (SkScalar*)NextUnchecked(this));
221 0 : if (isExtended()) {
222 0 : SkASSERT(textSize() > 0);
223 0 : SkASSERT(textSizePtr() < (uint32_t*)NextUnchecked(this));
224 0 : SkASSERT(clusterBuffer() < (uint32_t*)NextUnchecked(this));
225 0 : SkASSERT(textBuffer() + textSize() <= (char*)NextUnchecked(this));
226 : }
227 : static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
228 : "runrecord_should_stay_packed");
229 0 : }
230 :
231 : private:
232 : friend class SkTextBlobBuilder;
233 :
234 : enum Flags {
235 : kPositioning_Mask = 0x03, // bits 0-1 reserved for positioning
236 : kLast_Flag = 0x04, // set for the last blob run
237 : kExtended_Flag = 0x08, // set for runs with text/cluster info
238 : };
239 :
240 0 : static const RunRecord* NextUnchecked(const RunRecord* run) {
241 : return reinterpret_cast<const RunRecord*>(
242 : reinterpret_cast<const uint8_t*>(run)
243 0 : + StorageSize(run->glyphCount(), run->textSize(), run->positioning()));
244 : }
245 :
246 0 : static size_t PosCount(int glyphCount,
247 : SkTextBlob::GlyphPositioning positioning) {
248 0 : return glyphCount * ScalarsPerGlyph(positioning);
249 : }
250 :
251 0 : uint32_t* textSizePtr() const {
252 : // textSize follows the position buffer.
253 0 : SkASSERT(isExtended());
254 0 : return (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning())]);
255 : }
256 :
257 0 : void grow(uint32_t count) {
258 0 : SkScalar* initialPosBuffer = posBuffer();
259 0 : uint32_t initialCount = fCount;
260 0 : fCount += count;
261 :
262 : // Move the initial pos scalars to their new location.
263 0 : size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
264 0 : SkASSERT((uint8_t*)posBuffer() + copySize <= (uint8_t*)NextUnchecked(this));
265 :
266 : // memmove, as the buffers may overlap
267 0 : memmove(posBuffer(), initialPosBuffer, copySize);
268 0 : }
269 :
270 0 : bool isExtended() const {
271 0 : return fFlags & kExtended_Flag;
272 : }
273 :
274 : RunFont fFont;
275 : uint32_t fCount;
276 : SkPoint fOffset;
277 : uint32_t fFlags;
278 :
279 : SkDEBUGCODE(unsigned fMagic;)
280 : };
281 :
282 : static int32_t gNextID = 1;
283 0 : static int32_t next_id() {
284 : int32_t id;
285 0 : do {
286 0 : id = sk_atomic_inc(&gNextID);
287 0 : } while (id == SK_InvalidGenID);
288 0 : return id;
289 : }
290 :
291 0 : SkTextBlob::SkTextBlob(const SkRect& bounds)
292 : : fBounds(bounds)
293 0 : , fUniqueID(next_id())
294 0 : , fAddedToCache(false) {}
295 :
296 0 : SkTextBlob::~SkTextBlob() {
297 : #if SK_SUPPORT_GPU
298 0 : if (fAddedToCache.load()) {
299 0 : GrTextBlobCache::PostPurgeBlobMessage(fUniqueID);
300 : }
301 : #endif
302 :
303 0 : const auto* run = RunRecord::First(this);
304 0 : do {
305 0 : const auto* nextRun = RunRecord::Next(run);
306 0 : SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
307 0 : run->~RunRecord();
308 0 : run = nextRun;
309 0 : } while (run);
310 0 : }
311 :
312 : namespace {
313 : union PositioningAndExtended {
314 : int32_t intValue;
315 : struct {
316 : SkTextBlob::GlyphPositioning positioning;
317 : bool extended;
318 : uint16_t padding;
319 : };
320 : };
321 : } // namespace
322 :
323 0 : void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
324 0 : buffer.writeRect(fBounds);
325 :
326 0 : SkPaint runPaint;
327 0 : SkTextBlobRunIterator it(this);
328 0 : while (!it.done()) {
329 0 : SkASSERT(it.glyphCount() > 0);
330 :
331 0 : buffer.write32(it.glyphCount());
332 : PositioningAndExtended pe;
333 0 : pe.intValue = 0;
334 0 : pe.positioning = it.positioning();
335 0 : SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat.
336 :
337 0 : uint32_t textSize = it.textSize();
338 0 : pe.extended = textSize > 0;
339 0 : buffer.write32(pe.intValue);
340 0 : if (pe.extended) {
341 0 : buffer.write32(textSize);
342 : }
343 0 : buffer.writePoint(it.offset());
344 : // This should go away when switching to SkFont
345 0 : it.applyFontToPaint(&runPaint);
346 0 : buffer.writePaint(runPaint);
347 :
348 0 : buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
349 0 : buffer.writeByteArray(it.pos(),
350 0 : it.glyphCount() * sizeof(SkScalar) * ScalarsPerGlyph(it.positioning()));
351 0 : if (pe.extended) {
352 0 : buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
353 0 : buffer.writeByteArray(it.text(), it.textSize());
354 : }
355 :
356 0 : it.next();
357 : }
358 :
359 : // Marker for the last run (0 is not a valid glyph count).
360 0 : buffer.write32(0);
361 0 : }
362 :
363 0 : sk_sp<SkTextBlob> SkTextBlob::MakeFromBuffer(SkReadBuffer& reader) {
364 0 : const int runCount = reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)
365 0 : ? reader.read32() : std::numeric_limits<int>::max();
366 0 : if (runCount < 0) {
367 0 : return nullptr;
368 : }
369 :
370 : SkRect bounds;
371 0 : reader.readRect(&bounds);
372 :
373 0 : SkTextBlobBuilder blobBuilder;
374 0 : for (int i = 0; i < runCount; ++i) {
375 0 : int glyphCount = reader.read32();
376 0 : if (glyphCount == 0 &&
377 0 : !reader.isVersionLT(SkReadBuffer::kTextBlobImplicitRunCount_Version)) {
378 : // End-of-runs marker.
379 0 : break;
380 : }
381 :
382 : PositioningAndExtended pe;
383 0 : pe.intValue = reader.read32();
384 0 : GlyphPositioning pos = pe.positioning;
385 0 : if (glyphCount <= 0 || pos > kFull_Positioning) {
386 0 : return nullptr;
387 : }
388 0 : uint32_t textSize = pe.extended ? (uint32_t)reader.read32() : 0;
389 :
390 : SkPoint offset;
391 0 : reader.readPoint(&offset);
392 0 : SkPaint font;
393 0 : reader.readPaint(&font);
394 :
395 0 : const SkTextBlobBuilder::RunBuffer* buf = nullptr;
396 0 : switch (pos) {
397 : case kDefault_Positioning:
398 : buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
399 0 : textSize, SkString(), &bounds);
400 0 : break;
401 : case kHorizontal_Positioning:
402 : buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
403 0 : textSize, SkString(), &bounds);
404 0 : break;
405 : case kFull_Positioning:
406 0 : buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, SkString(), &bounds);
407 0 : break;
408 : default:
409 0 : return nullptr;
410 : }
411 :
412 0 : if (!reader.readByteArray(buf->glyphs, glyphCount * sizeof(uint16_t)) ||
413 0 : !reader.readByteArray(buf->pos,
414 0 : glyphCount * sizeof(SkScalar) * ScalarsPerGlyph(pos))) {
415 0 : return nullptr;
416 : }
417 :
418 0 : if (pe.extended) {
419 0 : if (!reader.readByteArray(buf->clusters, glyphCount * sizeof(uint32_t)) ||
420 0 : !reader.readByteArray(buf->utf8text, textSize)) {
421 0 : return nullptr;
422 : }
423 : }
424 : }
425 :
426 0 : return blobBuilder.make();
427 : }
428 :
429 0 : unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
430 : // GlyphPositioning values are directly mapped to scalars-per-glyph.
431 0 : SkASSERT(pos <= 2);
432 0 : return pos;
433 : }
434 :
435 0 : SkTextBlobRunIterator::SkTextBlobRunIterator(const SkTextBlob* blob)
436 0 : : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
437 0 : SkDEBUGCODE(fStorageTop = (uint8_t*)blob + blob->fStorageSize;)
438 0 : }
439 :
440 0 : bool SkTextBlobRunIterator::done() const {
441 0 : return !fCurrentRun;
442 : }
443 :
444 0 : void SkTextBlobRunIterator::next() {
445 0 : SkASSERT(!this->done());
446 :
447 0 : if (!this->done()) {
448 0 : SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
449 0 : fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
450 : }
451 0 : }
452 :
453 0 : uint32_t SkTextBlobRunIterator::glyphCount() const {
454 0 : SkASSERT(!this->done());
455 0 : return fCurrentRun->glyphCount();
456 : }
457 :
458 0 : const uint16_t* SkTextBlobRunIterator::glyphs() const {
459 0 : SkASSERT(!this->done());
460 0 : return fCurrentRun->glyphBuffer();
461 : }
462 :
463 0 : const SkScalar* SkTextBlobRunIterator::pos() const {
464 0 : SkASSERT(!this->done());
465 0 : return fCurrentRun->posBuffer();
466 : }
467 :
468 0 : const SkPoint& SkTextBlobRunIterator::offset() const {
469 0 : SkASSERT(!this->done());
470 0 : return fCurrentRun->offset();
471 : }
472 :
473 0 : SkTextBlob::GlyphPositioning SkTextBlobRunIterator::positioning() const {
474 0 : SkASSERT(!this->done());
475 0 : return fCurrentRun->positioning();
476 : }
477 :
478 0 : void SkTextBlobRunIterator::applyFontToPaint(SkPaint* paint) const {
479 0 : SkASSERT(!this->done());
480 :
481 0 : fCurrentRun->font().applyToPaint(paint);
482 0 : }
483 :
484 0 : uint32_t* SkTextBlobRunIterator::clusters() const {
485 0 : SkASSERT(!this->done());
486 0 : return fCurrentRun->clusterBuffer();
487 : }
488 0 : uint32_t SkTextBlobRunIterator::textSize() const {
489 0 : SkASSERT(!this->done());
490 0 : return fCurrentRun->textSize();
491 : }
492 0 : char* SkTextBlobRunIterator::text() const {
493 0 : SkASSERT(!this->done());
494 0 : return fCurrentRun->textBuffer();
495 : }
496 :
497 :
498 0 : bool SkTextBlobRunIterator::isLCD() const {
499 0 : return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
500 : }
501 :
502 0 : SkTextBlobBuilder::SkTextBlobBuilder()
503 : : fStorageSize(0)
504 : , fStorageUsed(0)
505 : , fRunCount(0)
506 : , fDeferredBounds(false)
507 0 : , fLastRun(0) {
508 0 : fBounds.setEmpty();
509 0 : }
510 :
511 0 : SkTextBlobBuilder::~SkTextBlobBuilder() {
512 0 : if (nullptr != fStorage.get()) {
513 : // We are abandoning runs and must destruct the associated font data.
514 : // The easiest way to accomplish that is to use the blob destructor.
515 0 : this->make();
516 : }
517 0 : }
518 :
519 0 : SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
520 : SkRect bounds;
521 0 : SkPaint paint;
522 0 : run.font().applyToPaint(&paint);
523 :
524 0 : if (SkTextBlob::kDefault_Positioning == run.positioning()) {
525 0 : paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
526 0 : return bounds.makeOffset(run.offset().x(), run.offset().y());
527 : }
528 :
529 0 : SkAutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
530 0 : paint.getTextWidths(run.glyphBuffer(),
531 0 : run.glyphCount() * sizeof(uint16_t),
532 : NULL,
533 0 : glyphBounds.get());
534 :
535 0 : SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
536 : SkTextBlob::kHorizontal_Positioning == run.positioning());
537 : // kFull_Positioning => [ x, y, x, y... ]
538 : // kHorizontal_Positioning => [ x, x, x... ]
539 : // (const y applied by runBounds.offset(run->offset()) later)
540 0 : const SkScalar horizontalConstY = 0;
541 0 : const SkScalar* glyphPosX = run.posBuffer();
542 0 : const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
543 0 : glyphPosX + 1 : &horizontalConstY;
544 0 : const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
545 0 : const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
546 0 : posXInc : 0;
547 :
548 0 : bounds.setEmpty();
549 0 : for (unsigned i = 0; i < run.glyphCount(); ++i) {
550 0 : bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
551 0 : glyphPosX += posXInc;
552 0 : glyphPosY += posYInc;
553 : }
554 :
555 0 : SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
556 :
557 0 : return bounds.makeOffset(run.offset().x(), run.offset().y());
558 : }
559 :
560 0 : SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
561 0 : SkASSERT(run.glyphCount() > 0);
562 0 : SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
563 : SkTextBlob::kHorizontal_Positioning == run.positioning());
564 :
565 0 : SkPaint paint;
566 0 : run.font().applyToPaint(&paint);
567 0 : const SkRect fontBounds = paint.getFontBounds();
568 0 : if (fontBounds.isEmpty()) {
569 : // Empty font bounds are likely a font bug. TightBounds has a better chance of
570 : // producing useful results in this case.
571 0 : return TightRunBounds(run);
572 : }
573 :
574 : // Compute the glyph position bbox.
575 : SkRect bounds;
576 0 : switch (run.positioning()) {
577 : case SkTextBlob::kHorizontal_Positioning: {
578 0 : const SkScalar* glyphPos = run.posBuffer();
579 0 : SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
580 :
581 0 : SkScalar minX = *glyphPos;
582 0 : SkScalar maxX = *glyphPos;
583 0 : for (unsigned i = 1; i < run.glyphCount(); ++i) {
584 0 : SkScalar x = glyphPos[i];
585 0 : minX = SkMinScalar(x, minX);
586 0 : maxX = SkMaxScalar(x, maxX);
587 : }
588 :
589 0 : bounds.setLTRB(minX, 0, maxX, 0);
590 0 : } break;
591 : case SkTextBlob::kFull_Positioning: {
592 0 : const SkPoint* glyphPosPts = reinterpret_cast<const SkPoint*>(run.posBuffer());
593 0 : SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
594 :
595 0 : bounds.setBounds(glyphPosPts, run.glyphCount());
596 0 : } break;
597 : default:
598 0 : SkFAIL("unsupported positioning mode");
599 : }
600 :
601 : // Expand by typeface glyph bounds.
602 0 : bounds.fLeft += fontBounds.left();
603 0 : bounds.fTop += fontBounds.top();
604 0 : bounds.fRight += fontBounds.right();
605 0 : bounds.fBottom += fontBounds.bottom();
606 :
607 : // Offset by run position.
608 0 : return bounds.makeOffset(run.offset().x(), run.offset().y());
609 : }
610 :
611 0 : void SkTextBlobBuilder::updateDeferredBounds() {
612 0 : SkASSERT(!fDeferredBounds || fRunCount > 0);
613 :
614 0 : if (!fDeferredBounds) {
615 0 : return;
616 : }
617 :
618 0 : SkASSERT(fLastRun >= sizeof(SkTextBlob));
619 0 : SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
620 0 : fLastRun);
621 :
622 : // FIXME: we should also use conservative bounds for kDefault_Positioning.
623 0 : SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
624 0 : TightRunBounds(*run) : ConservativeRunBounds(*run);
625 0 : fBounds.join(runBounds);
626 0 : fDeferredBounds = false;
627 : }
628 :
629 0 : void SkTextBlobBuilder::reserve(size_t size) {
630 : // We don't currently pre-allocate, but maybe someday...
631 0 : if (fStorageUsed + size <= fStorageSize) {
632 0 : return;
633 : }
634 :
635 0 : if (0 == fRunCount) {
636 0 : SkASSERT(nullptr == fStorage.get());
637 0 : SkASSERT(0 == fStorageSize);
638 0 : SkASSERT(0 == fStorageUsed);
639 :
640 : // the first allocation also includes blob storage
641 0 : fStorageUsed += sizeof(SkTextBlob);
642 : }
643 :
644 0 : fStorageSize = fStorageUsed + size;
645 : // FYI: This relies on everything we store being relocatable, particularly SkPaint.
646 0 : fStorage.realloc(fStorageSize);
647 : }
648 :
649 0 : bool SkTextBlobBuilder::mergeRun(const SkPaint &font, SkTextBlob::GlyphPositioning positioning,
650 : int count, SkPoint offset) {
651 0 : if (0 == fLastRun) {
652 0 : SkASSERT(0 == fRunCount);
653 0 : return false;
654 : }
655 :
656 0 : SkASSERT(fLastRun >= sizeof(SkTextBlob));
657 0 : SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
658 0 : fLastRun);
659 0 : SkASSERT(run->glyphCount() > 0);
660 :
661 0 : if (run->textSize() != 0) {
662 0 : return false;
663 : }
664 :
665 0 : if (run->positioning() != positioning
666 0 : || run->font() != font
667 0 : || (run->glyphCount() + count < run->glyphCount())) {
668 0 : return false;
669 : }
670 :
671 : // we can merge same-font/same-positioning runs in the following cases:
672 : // * fully positioned run following another fully positioned run
673 : // * horizontally postioned run following another horizontally positioned run with the same
674 : // y-offset
675 0 : if (SkTextBlob::kFull_Positioning != positioning
676 0 : && (SkTextBlob::kHorizontal_Positioning != positioning
677 0 : || run->offset().y() != offset.y())) {
678 0 : return false;
679 : }
680 :
681 0 : size_t sizeDelta = SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning) -
682 0 : SkTextBlob::RunRecord::StorageSize(run->glyphCount(), 0, positioning);
683 0 : this->reserve(sizeDelta);
684 :
685 : // reserve may have realloced
686 0 : run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
687 0 : uint32_t preMergeCount = run->glyphCount();
688 0 : run->grow(count);
689 :
690 : // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
691 0 : fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
692 0 : fCurrentRunBuffer.pos = run->posBuffer()
693 0 : + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
694 :
695 0 : fStorageUsed += sizeDelta;
696 :
697 0 : SkASSERT(fStorageUsed <= fStorageSize);
698 0 : run->validate(fStorage.get() + fStorageUsed);
699 :
700 0 : return true;
701 : }
702 :
703 0 : void SkTextBlobBuilder::allocInternal(const SkPaint &font,
704 : SkTextBlob::GlyphPositioning positioning,
705 : int count, int textSize, SkPoint offset, const SkRect* bounds) {
706 0 : SkASSERT(count > 0);
707 0 : SkASSERT(textSize >= 0);
708 0 : SkASSERT(SkPaint::kGlyphID_TextEncoding == font.getTextEncoding());
709 0 : if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
710 0 : this->updateDeferredBounds();
711 :
712 0 : size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning);
713 0 : this->reserve(runSize);
714 :
715 0 : SkASSERT(fStorageUsed >= sizeof(SkTextBlob));
716 0 : SkASSERT(fStorageUsed + runSize <= fStorageSize);
717 :
718 0 : SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
719 0 : SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
720 0 : fCurrentRunBuffer.glyphs = run->glyphBuffer();
721 0 : fCurrentRunBuffer.pos = run->posBuffer();
722 0 : fCurrentRunBuffer.utf8text = run->textBuffer();
723 0 : fCurrentRunBuffer.clusters = run->clusterBuffer();
724 :
725 0 : fLastRun = fStorageUsed;
726 0 : fStorageUsed += runSize;
727 0 : fRunCount++;
728 :
729 0 : SkASSERT(fStorageUsed <= fStorageSize);
730 0 : run->validate(fStorage.get() + fStorageUsed);
731 : }
732 0 : SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
733 0 : SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
734 0 : if (!fDeferredBounds) {
735 0 : if (bounds) {
736 0 : fBounds.join(*bounds);
737 : } else {
738 0 : fDeferredBounds = true;
739 : }
740 : }
741 0 : }
742 :
743 0 : const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunText(const SkPaint& font, int count,
744 : SkScalar x, SkScalar y,
745 : int textByteCount,
746 : SkString lang,
747 : const SkRect* bounds) {
748 0 : this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, textByteCount, SkPoint::Make(x, y), bounds);
749 0 : return fCurrentRunBuffer;
750 : }
751 :
752 0 : const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPosH(const SkPaint& font, int count,
753 : SkScalar y,
754 : int textByteCount,
755 : SkString lang,
756 : const SkRect* bounds) {
757 0 : this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, textByteCount, SkPoint::Make(0, y),
758 0 : bounds);
759 :
760 0 : return fCurrentRunBuffer;
761 : }
762 :
763 0 : const SkTextBlobBuilder::RunBuffer& SkTextBlobBuilder::allocRunTextPos(const SkPaint& font, int count,
764 : int textByteCount,
765 : SkString lang,
766 : const SkRect *bounds) {
767 0 : this->allocInternal(font, SkTextBlob::kFull_Positioning, count, textByteCount, SkPoint::Make(0, 0), bounds);
768 :
769 0 : return fCurrentRunBuffer;
770 : }
771 :
772 0 : sk_sp<SkTextBlob> SkTextBlobBuilder::make() {
773 0 : if (!fRunCount) {
774 : // We don't instantiate empty blobs.
775 0 : SkASSERT(!fStorage.get());
776 0 : SkASSERT(fStorageUsed == 0);
777 0 : SkASSERT(fStorageSize == 0);
778 0 : SkASSERT(fLastRun == 0);
779 0 : SkASSERT(fBounds.isEmpty());
780 0 : return nullptr;
781 : }
782 :
783 0 : this->updateDeferredBounds();
784 :
785 : // Tag the last run as such.
786 0 : auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
787 0 : lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
788 :
789 0 : SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
790 0 : SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
791 :
792 0 : SkDEBUGCODE(
793 : size_t validateSize = sizeof(SkTextBlob);
794 : for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
795 : run = SkTextBlob::RunRecord::Next(run)) {
796 : validateSize += SkTextBlob::RunRecord::StorageSize(
797 : run->fCount, run->textSize(), run->positioning());
798 : run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
799 : fRunCount--;
800 : }
801 : SkASSERT(validateSize == fStorageUsed);
802 : SkASSERT(fRunCount == 0);
803 : )
804 :
805 0 : fStorageUsed = 0;
806 0 : fStorageSize = 0;
807 0 : fRunCount = 0;
808 0 : fLastRun = 0;
809 0 : fBounds.setEmpty();
810 :
811 0 : return sk_sp<SkTextBlob>(blob);
812 : }
|