Flutter Engine
The Flutter Engine
SkTextBlob.cpp
Go to the documentation of this file.
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
9
10#include "include/core/SkData.h"
22#include "src/base/SkSafeMath.h"
23#include "src/base/SkTLazy.h"
24#include "src/core/SkFontPriv.h"
25#include "src/core/SkGlyph.h"
30#include "src/text/GlyphRun.h"
31
32#include <algorithm>
33#include <atomic>
34#include <limits>
35#include <new>
36#include <vector>
37
38using namespace skia_private;
39
40namespace {
41struct RunFontStorageEquivalent {
42 SkScalar fSize, fScaleX;
43 void* fTypeface;
44 SkScalar fSkewX;
45 uint32_t fFlags;
46};
47static_assert(sizeof(SkFont) == sizeof(RunFontStorageEquivalent), "runfont_should_stay_packed");
48} // namespace
49
50size_t SkTextBlob::RunRecord::StorageSize(uint32_t glyphCount, uint32_t textSize,
52 SkSafeMath* safe) {
53 static_assert(SkIsAlign4(sizeof(SkScalar)), "SkScalar size alignment");
54
55 auto glyphSize = safe->mul(glyphCount, sizeof(uint16_t)),
56 posSize = safe->mul(PosCount(glyphCount, positioning, safe), sizeof(SkScalar));
57
58 // RunRecord object + (aligned) glyph buffer + position buffer
59 auto size = sizeof(SkTextBlob::RunRecord);
60 size = safe->add(size, safe->alignUp(glyphSize, 4));
61 size = safe->add(size, posSize);
62
63 if (textSize) { // Extended run.
64 size = safe->add(size, sizeof(uint32_t));
65 size = safe->add(size, safe->mul(glyphCount, sizeof(uint32_t)));
66 size = safe->add(size, textSize);
67 }
68
69 return safe->alignUp(size, sizeof(void*));
70}
71
72const SkTextBlob::RunRecord* SkTextBlob::RunRecord::First(const SkTextBlob* blob) {
73 // The first record (if present) is stored following the blob object.
74 // (aligned up to make the RunRecord aligned too)
75 return reinterpret_cast<const RunRecord*>(SkAlignPtr((uintptr_t)(blob + 1)));
76}
77
78const SkTextBlob::RunRecord* SkTextBlob::RunRecord::Next(const RunRecord* run) {
79 return SkToBool(run->fFlags & kLast_Flag) ? nullptr : NextUnchecked(run);
80}
81
82namespace {
83struct RunRecordStorageEquivalent {
84 SkFont fFont;
85 SkPoint fOffset;
86 uint32_t fCount;
87 uint32_t fFlags;
88 SkDEBUGCODE(unsigned fMagic;)
89};
90} // namespace
91
92void SkTextBlob::RunRecord::validate(const uint8_t* storageTop) const {
93 SkASSERT(kRunRecordMagic == fMagic);
94 SkASSERT((const uint8_t*)NextUnchecked(this) <= storageTop);
95
96 SkASSERT(glyphBuffer() + fCount <= (uint16_t*)posBuffer());
97 SkASSERT(posBuffer() + fCount * ScalarsPerGlyph(positioning())
98 <= (const SkScalar*)NextUnchecked(this));
99 if (isExtended()) {
100 SkASSERT(textSize() > 0);
101 SkASSERT(textSizePtr() < (const uint32_t*)NextUnchecked(this));
102 SkASSERT(clusterBuffer() < (const uint32_t*)NextUnchecked(this));
103 SkASSERT(textBuffer() + textSize() <= (const char*)NextUnchecked(this));
104 }
105 static_assert(sizeof(SkTextBlob::RunRecord) == sizeof(RunRecordStorageEquivalent),
106 "runrecord_should_stay_packed");
107}
108
109const SkTextBlob::RunRecord* SkTextBlob::RunRecord::NextUnchecked(const RunRecord* run) {
110 SkSafeMath safe;
111 auto res = reinterpret_cast<const RunRecord*>(
112 reinterpret_cast<const uint8_t*>(run)
113 + StorageSize(run->glyphCount(), run->textSize(), run->positioning(), &safe));
114 SkASSERT(safe);
115 return res;
116}
117
118size_t SkTextBlob::RunRecord::PosCount(uint32_t glyphCount,
120 SkSafeMath* safe) {
121 return safe->mul(glyphCount, ScalarsPerGlyph(positioning));
122}
123
124uint32_t* SkTextBlob::RunRecord::textSizePtr() const {
125 // textSize follows the position buffer.
126 SkASSERT(isExtended());
127 SkSafeMath safe;
128 auto res = (uint32_t*)(&this->posBuffer()[PosCount(fCount, positioning(), &safe)]);
129 SkASSERT(safe);
130 return res;
131}
132
133void SkTextBlob::RunRecord::grow(uint32_t count) {
134 SkScalar* initialPosBuffer = posBuffer();
135 uint32_t initialCount = fCount;
136 fCount += count;
137
138 // Move the initial pos scalars to their new location.
139 size_t copySize = initialCount * sizeof(SkScalar) * ScalarsPerGlyph(positioning());
140 SkASSERT((uint8_t*)posBuffer() + copySize <= (const uint8_t*)NextUnchecked(this));
141
142 // memmove, as the buffers may overlap
143 memmove(posBuffer(), initialPosBuffer, copySize);
144}
145
146static uint32_t next_id() {
147 static std::atomic<uint32_t> nextID{1};
148 uint32_t id;
149 do {
150 id = nextID.fetch_add(1, std::memory_order_relaxed);
151 } while (id == SK_InvalidGenID);
152 return id;
153}
154
155SkTextBlob::SkTextBlob(const SkRect& bounds)
156 : fBounds(bounds)
157 , fUniqueID(next_id())
158 , fCacheID(SK_InvalidUniqueID)
159 , fPurgeDelegate(nullptr) {}
160
161SkTextBlob::~SkTextBlob() {
162 if (SK_InvalidUniqueID != fCacheID.load()) {
163 PurgeDelegate f = fPurgeDelegate.load();
164 SkASSERT(f);
165 f(fUniqueID, fCacheID);
166 }
167
168 const auto* run = RunRecord::First(this);
169 do {
170 const auto* nextRun = RunRecord::Next(run);
171 SkDEBUGCODE(run->validate((uint8_t*)this + fStorageSize);)
172 run->~RunRecord();
173 run = nextRun;
174 } while (run);
175}
176
177namespace {
178
179union PositioningAndExtended {
180 int32_t intValue;
181 struct {
182 uint8_t positioning;
183 uint8_t extended;
184 uint16_t padding;
185 };
186};
187
188static_assert(sizeof(PositioningAndExtended) == sizeof(int32_t), "");
189
190} // namespace
191
193 kDefault_Positioning = 0, // Default glyph advances -- zero scalars per glyph.
194 kHorizontal_Positioning = 1, // Horizontal positioning -- one scalar per glyph.
195 kFull_Positioning = 2, // Point positioning -- two scalars per glyph.
196 kRSXform_Positioning = 3, // RSXform positioning -- four scalars per glyph.
197};
198
199unsigned SkTextBlob::ScalarsPerGlyph(GlyphPositioning pos) {
200 const uint8_t gScalarsPerPositioning[] = {
201 0, // kDefault_Positioning
202 1, // kHorizontal_Positioning
203 2, // kFull_Positioning
204 4, // kRSXform_Positioning
205 };
206 SkASSERT((unsigned)pos <= 3);
207 return gScalarsPerPositioning[pos];
208}
209
210void SkTextBlob::operator delete(void* p) {
211 sk_free(p);
212}
213
214void* SkTextBlob::operator new(size_t) {
215 SK_ABORT("All blobs are created by placement new.");
216}
217
218void* SkTextBlob::operator new(size_t, void* p) {
219 return p;
220}
221
223 : fCurrentRun(SkTextBlob::RunRecord::First(blob)) {
224 SkDEBUGCODE(fStorageTop = (const uint8_t*)blob + blob->fStorageSize;)
225}
226
228 SkASSERT(!this->done());
229
230 if (!this->done()) {
231 SkDEBUGCODE(fCurrentRun->validate(fStorageTop);)
232 fCurrentRun = SkTextBlob::RunRecord::Next(fCurrentRun);
233 }
234}
235
237 SkASSERT(!this->done());
238 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kDefault_Positioning) ==
240 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kHorizontal_Positioning) ==
242 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kFull_Positioning) ==
244 static_assert(static_cast<GlyphPositioning>(SkTextBlob::kRSXform_Positioning) ==
246
247 return SkTo<GlyphPositioning>(fCurrentRun->positioning());
248}
249
251 return SkTextBlob::ScalarsPerGlyph(fCurrentRun->positioning());
252}
253
255 return fCurrentRun->font().getEdging() == SkFont::Edging::kSubpixelAntiAlias;
256}
257
259 : fStorageSize(0)
260 , fStorageUsed(0)
261 , fRunCount(0)
262 , fDeferredBounds(false)
263 , fLastRun(0) {
264 fBounds.setEmpty();
265}
266
268 if (nullptr != fStorage.get()) {
269 // We are abandoning runs and must destruct the associated font data.
270 // The easiest way to accomplish that is to use the blob destructor.
271 this->make();
272 }
273}
274
275static SkRect map_quad_to_rect(const SkRSXform& xform, const SkRect& rect) {
276 return SkMatrix().setRSXform(xform).mapRect(rect);
277}
278
279SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) {
280 const SkFont& font = run.font();
282
283 if (SkTextBlob::kDefault_Positioning == run.positioning()) {
284 font.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t),
286 return bounds.makeOffset(run.offset().x(), run.offset().y());
287 }
288
289 AutoSTArray<16, SkRect> glyphBounds(run.glyphCount());
290 font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr);
291
292 if (SkTextBlob::kRSXform_Positioning == run.positioning()) {
293 bounds.setEmpty();
294 const SkRSXform* xform = run.xformBuffer();
295 SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
296 for (unsigned i = 0; i < run.glyphCount(); ++i) {
297 bounds.join(map_quad_to_rect(xform[i], glyphBounds[i]));
298 }
299 } else {
300 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
302 // kFull_Positioning => [ x, y, x, y... ]
303 // kHorizontal_Positioning => [ x, x, x... ]
304 // (const y applied by runBounds.offset(run->offset()) later)
305 const SkScalar horizontalConstY = 0;
306 const SkScalar* glyphPosX = run.posBuffer();
307 const SkScalar* glyphPosY = (run.positioning() == SkTextBlob::kFull_Positioning) ?
308 glyphPosX + 1 : &horizontalConstY;
309 const unsigned posXInc = SkTextBlob::ScalarsPerGlyph(run.positioning());
310 const unsigned posYInc = (run.positioning() == SkTextBlob::kFull_Positioning) ?
311 posXInc : 0;
312
313 bounds.setEmpty();
314 for (unsigned i = 0; i < run.glyphCount(); ++i) {
315 bounds.join(glyphBounds[i].makeOffset(*glyphPosX, *glyphPosY));
316 glyphPosX += posXInc;
317 glyphPosY += posYInc;
318 }
319
320 SkASSERT((void*)glyphPosX <= SkTextBlob::RunRecord::Next(&run));
321 }
322 return bounds.makeOffset(run.offset().x(), run.offset().y());
323}
324
325SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run) {
326 SkASSERT(run.glyphCount() > 0);
327 SkASSERT(SkTextBlob::kFull_Positioning == run.positioning() ||
328 SkTextBlob::kHorizontal_Positioning == run.positioning() ||
329 SkTextBlob::kRSXform_Positioning == run.positioning());
330
331 const SkRect fontBounds = SkFontPriv::GetFontBounds(run.font());
332 if (fontBounds.isEmpty()) {
333 // Empty font bounds are likely a font bug. TightBounds has a better chance of
334 // producing useful results in this case.
335 return TightRunBounds(run);
336 }
337
338 // Compute the glyph position bbox.
340 switch (run.positioning()) {
342 const SkScalar* glyphPos = run.posBuffer();
343 SkASSERT((void*)(glyphPos + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
344
345 SkScalar minX = *glyphPos;
346 SkScalar maxX = *glyphPos;
347 for (unsigned i = 1; i < run.glyphCount(); ++i) {
348 SkScalar x = glyphPos[i];
349 minX = std::min(x, minX);
350 maxX = std::max(x, maxX);
351 }
352
353 bounds.setLTRB(minX, 0, maxX, 0);
354 } break;
356 const SkPoint* glyphPosPts = run.pointBuffer();
357 SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
358
359 bounds.setBounds(glyphPosPts, run.glyphCount());
360 } break;
362 const SkRSXform* xform = run.xformBuffer();
363 SkASSERT((void*)(xform + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run));
364 bounds.setEmpty();
365 for (unsigned i = 0; i < run.glyphCount(); ++i) {
366 bounds.join(map_quad_to_rect(xform[i], fontBounds));
367 }
368 } break;
369 default:
370 SK_ABORT("unsupported positioning mode");
371 }
372
373 if (run.positioning() != SkTextBlob::kRSXform_Positioning) {
374 // Expand by typeface glyph bounds.
375 bounds.fLeft += fontBounds.left();
376 bounds.fTop += fontBounds.top();
377 bounds.fRight += fontBounds.right();
378 bounds.fBottom += fontBounds.bottom();
379 }
380
381 // Offset by run position.
382 return bounds.makeOffset(run.offset().x(), run.offset().y());
383}
384
385void SkTextBlobBuilder::updateDeferredBounds() {
386 SkASSERT(!fDeferredBounds || fRunCount > 0);
387
388 if (!fDeferredBounds) {
389 return;
390 }
391
392 SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
393 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
394 fLastRun);
395
396 // FIXME: we should also use conservative bounds for kDefault_Positioning.
397 SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
398 TightRunBounds(*run) : ConservativeRunBounds(*run);
399 fBounds.join(runBounds);
400 fDeferredBounds = false;
401}
402
403void SkTextBlobBuilder::reserve(size_t size) {
404 SkSafeMath safe;
405
406 // We don't currently pre-allocate, but maybe someday...
407 if (safe.add(fStorageUsed, size) <= fStorageSize && safe) {
408 return;
409 }
410
411 if (0 == fRunCount) {
412 SkASSERT(nullptr == fStorage.get());
413 SkASSERT(0 == fStorageSize);
414 SkASSERT(0 == fStorageUsed);
415
416 // the first allocation also includes blob storage
417 // aligned up to a pointer alignment so SkTextBlob::RunRecords after it stay aligned.
418 fStorageUsed = SkAlignPtr(sizeof(SkTextBlob));
419 }
420
421 fStorageSize = safe.add(fStorageUsed, size);
422
423 // FYI: This relies on everything we store being relocatable, particularly SkPaint.
424 // Also, this is counting on the underlying realloc to throw when passed max().
425 fStorage.realloc(safe ? fStorageSize : std::numeric_limits<size_t>::max());
426}
427
428bool SkTextBlobBuilder::mergeRun(const SkFont& font, SkTextBlob::GlyphPositioning positioning,
429 uint32_t count, SkPoint offset) {
430 if (0 == fLastRun) {
431 SkASSERT(0 == fRunCount);
432 return false;
433 }
434
435 SkASSERT(fLastRun >= SkAlignPtr(sizeof(SkTextBlob)));
436 SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
437 fLastRun);
438 SkASSERT(run->glyphCount() > 0);
439
440 if (run->textSize() != 0) {
441 return false;
442 }
443
444 if (run->positioning() != positioning
445 || run->font() != font
446 || (run->glyphCount() + count < run->glyphCount())) {
447 return false;
448 }
449
450 // we can merge same-font/same-positioning runs in the following cases:
451 // * fully positioned run following another fully positioned run
452 // * horizontally postioned run following another horizontally positioned run with the same
453 // y-offset
454 if (SkTextBlob::kFull_Positioning != positioning
455 && (SkTextBlob::kHorizontal_Positioning != positioning
456 || run->offset().y() != offset.y())) {
457 return false;
458 }
459
460 SkSafeMath safe;
461 size_t sizeDelta =
462 SkTextBlob::RunRecord::StorageSize(run->glyphCount() + count, 0, positioning, &safe) -
463 SkTextBlob::RunRecord::StorageSize(run->glyphCount() , 0, positioning, &safe);
464 if (!safe) {
465 return false;
466 }
467
468 this->reserve(sizeDelta);
469
470 // reserve may have realloced
471 run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
472 uint32_t preMergeCount = run->glyphCount();
473 run->grow(count);
474
475 // Callers expect the buffers to point at the newly added slice, ant not at the beginning.
476 fCurrentRunBuffer.glyphs = run->glyphBuffer() + preMergeCount;
477 fCurrentRunBuffer.pos = run->posBuffer()
478 + preMergeCount * SkTextBlob::ScalarsPerGlyph(positioning);
479
480 fStorageUsed += sizeDelta;
481
482 SkASSERT(fStorageUsed <= fStorageSize);
483 run->validate(fStorage.get() + fStorageUsed);
484
485 return true;
486}
487
488void SkTextBlobBuilder::allocInternal(const SkFont& font,
490 int count, int textSize, SkPoint offset,
491 const SkRect* bounds) {
492 if (count <= 0 || textSize < 0) {
493 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
494 return;
495 }
496
497 if (textSize != 0 || !this->mergeRun(font, positioning, count, offset)) {
498 this->updateDeferredBounds();
499
500 SkSafeMath safe;
501 size_t runSize = SkTextBlob::RunRecord::StorageSize(count, textSize, positioning, &safe);
502 if (!safe) {
503 fCurrentRunBuffer = { nullptr, nullptr, nullptr, nullptr };
504 return;
505 }
506
507 this->reserve(runSize);
508
509 SkASSERT(fStorageUsed >= SkAlignPtr(sizeof(SkTextBlob)));
510 SkASSERT(fStorageUsed + runSize <= fStorageSize);
511
512 SkTextBlob::RunRecord* run = new (fStorage.get() + fStorageUsed)
513 SkTextBlob::RunRecord(count, textSize, offset, font, positioning);
514 fCurrentRunBuffer.glyphs = run->glyphBuffer();
515 fCurrentRunBuffer.pos = run->posBuffer();
516 fCurrentRunBuffer.utf8text = run->textBuffer();
517 fCurrentRunBuffer.clusters = run->clusterBuffer();
518
519 fLastRun = fStorageUsed;
520 fStorageUsed += runSize;
521 fRunCount++;
522
523 SkASSERT(fStorageUsed <= fStorageSize);
524 run->validate(fStorage.get() + fStorageUsed);
525 }
526 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.utf8text);
527 SkASSERT(textSize > 0 || nullptr == fCurrentRunBuffer.clusters);
528 if (!fDeferredBounds) {
529 if (bounds) {
530 fBounds.join(*bounds);
531 } else {
532 fDeferredBounds = true;
533 }
534 }
535}
536
537// SkFont versions
538
541 const SkRect* bounds) {
542 this->allocInternal(font, SkTextBlob::kDefault_Positioning, count, 0, {x, y}, bounds);
543 return fCurrentRunBuffer;
544}
545
547 SkScalar y,
548 const SkRect* bounds) {
549 this->allocInternal(font, SkTextBlob::kHorizontal_Positioning, count, 0, {0, y}, bounds);
550 return fCurrentRunBuffer;
551}
552
554 const SkRect* bounds) {
555 this->allocInternal(font, SkTextBlob::kFull_Positioning, count, 0, {0, 0}, bounds);
556 return fCurrentRunBuffer;
557}
558
561 this->allocInternal(font, SkTextBlob::kRSXform_Positioning, count, 0, {0, 0}, nullptr);
562 return fCurrentRunBuffer;
563}
564
567 int textByteCount,
568 const SkRect* bounds) {
569 this->allocInternal(font,
571 count,
572 textByteCount,
573 SkPoint::Make(x, y),
574 bounds);
575 return fCurrentRunBuffer;
576}
577
579 int count,
580 SkScalar y,
581 int textByteCount,
582 const SkRect* bounds) {
583 this->allocInternal(font,
585 count,
586 textByteCount,
587 SkPoint::Make(0, y),
588 bounds);
589 return fCurrentRunBuffer;
590}
591
593 int count,
594 int textByteCount,
595 const SkRect *bounds) {
596 this->allocInternal(font,
598 count, textByteCount,
599 SkPoint::Make(0, 0),
600 bounds);
601 return fCurrentRunBuffer;
602}
603
605 int count,
606 int textByteCount,
607 const SkRect *bounds) {
608 this->allocInternal(font,
610 count,
611 textByteCount,
612 {0, 0},
613 bounds);
614 return fCurrentRunBuffer;
615}
616
618 if (!fRunCount) {
619 // We don't instantiate empty blobs.
620 SkASSERT(!fStorage.get());
621 SkASSERT(fStorageUsed == 0);
622 SkASSERT(fStorageSize == 0);
623 SkASSERT(fLastRun == 0);
624 SkASSERT(fBounds.isEmpty());
625 return nullptr;
626 }
627
628 this->updateDeferredBounds();
629
630 // Tag the last run as such.
631 auto* lastRun = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() + fLastRun);
632 lastRun->fFlags |= SkTextBlob::RunRecord::kLast_Flag;
633
634 SkTextBlob* blob = new (fStorage.release()) SkTextBlob(fBounds);
635 SkDEBUGCODE(const_cast<SkTextBlob*>(blob)->fStorageSize = fStorageSize;)
636
638 SkSafeMath safe;
639 size_t validateSize = SkAlignPtr(sizeof(SkTextBlob));
640 for (const auto* run = SkTextBlob::RunRecord::First(blob); run;
641 run = SkTextBlob::RunRecord::Next(run)) {
642 validateSize += SkTextBlob::RunRecord::StorageSize(
643 run->fCount, run->textSize(), run->positioning(), &safe);
644 run->validate(reinterpret_cast<const uint8_t*>(blob) + fStorageUsed);
645 fRunCount--;
646 }
647 SkASSERT(validateSize == fStorageUsed);
648 SkASSERT(fRunCount == 0);
649 SkASSERT(safe);
650 )
651
652 fStorageUsed = 0;
653 fStorageSize = 0;
654 fRunCount = 0;
655 fLastRun = 0;
656 fBounds.setEmpty();
657
658 return sk_sp<SkTextBlob>(blob);
659}
660
661///////////////////////////////////////////////////////////////////////////////////////////////////
662
664 // seems like we could skip this, and just recompute bounds in unflatten, but
665 // some cc_unittests fail if we remove this...
666 buffer.writeRect(blob.bounds());
667
668 SkTextBlobRunIterator it(&blob);
669 while (!it.done()) {
670 SkASSERT(it.glyphCount() > 0);
671
672 buffer.write32(it.glyphCount());
673 PositioningAndExtended pe;
674 pe.intValue = 0;
675 pe.positioning = it.positioning();
676 SkASSERT((int32_t)it.positioning() == pe.intValue); // backwards compat.
677
678 uint32_t textSize = it.textSize();
679 pe.extended = textSize > 0;
680 buffer.write32(pe.intValue);
681 if (pe.extended) {
682 buffer.write32(textSize);
683 }
684 buffer.writePoint(it.offset());
685
687
688 buffer.writeByteArray(it.glyphs(), it.glyphCount() * sizeof(uint16_t));
689 buffer.writeByteArray(it.pos(),
690 it.glyphCount() * sizeof(SkScalar) *
691 SkTextBlob::ScalarsPerGlyph(
692 SkTo<SkTextBlob::GlyphPositioning>(it.positioning())));
693 if (pe.extended) {
694 buffer.writeByteArray(it.clusters(), sizeof(uint32_t) * it.glyphCount());
695 buffer.writeByteArray(it.text(), it.textSize());
696 }
697
698 it.next();
699 }
700
701 // Marker for the last run (0 is not a valid glyph count).
702 buffer.write32(0);
703}
704
707 reader.readRect(&bounds);
708
709 SkTextBlobBuilder blobBuilder;
710 SkSafeMath safe;
711 for (;;) {
712 int glyphCount = reader.read32();
713 if (glyphCount == 0) {
714 // End-of-runs marker.
715 break;
716 }
717
718 PositioningAndExtended pe;
719 pe.intValue = reader.read32();
720 const auto pos = SkTo<SkTextBlob::GlyphPositioning>(pe.positioning);
721 if (glyphCount <= 0 || pos > SkTextBlob::kRSXform_Positioning) {
722 return nullptr;
723 }
724 int textSize = pe.extended ? reader.read32() : 0;
725 if (textSize < 0) {
726 return nullptr;
727 }
728
730 reader.readPoint(&offset);
731 SkFont font;
732 SkFontPriv::Unflatten(&font, reader);
733
734 // Compute the expected size of the buffer and ensure we have enough to deserialize
735 // a run before allocating it.
736 const size_t glyphSize = safe.mul(glyphCount, sizeof(uint16_t)),
737 posSize =
738 safe.mul(glyphCount, safe.mul(sizeof(SkScalar),
739 SkTextBlob::ScalarsPerGlyph(pos))),
740 clusterSize = pe.extended ? safe.mul(glyphCount, sizeof(uint32_t)) : 0;
741 const size_t totalSize =
742 safe.add(safe.add(glyphSize, posSize), safe.add(clusterSize, textSize));
743
744 if (!reader.isValid() || !safe || totalSize > reader.available()) {
745 return nullptr;
746 }
747
748 const SkTextBlobBuilder::RunBuffer* buf = nullptr;
749 switch (pos) {
751 buf = &blobBuilder.allocRunText(font, glyphCount, offset.x(), offset.y(),
752 textSize, &bounds);
753 break;
755 buf = &blobBuilder.allocRunTextPosH(font, glyphCount, offset.y(),
756 textSize, &bounds);
757 break;
759 buf = &blobBuilder.allocRunTextPos(font, glyphCount, textSize, &bounds);
760 break;
762 buf = &blobBuilder.allocRunTextRSXform(font, glyphCount, textSize, &bounds);
763 break;
764 }
765
766 if (!buf->glyphs ||
767 !buf->pos ||
768 (pe.extended && (!buf->clusters || !buf->utf8text))) {
769 return nullptr;
770 }
771
772 if (!reader.readByteArray(buf->glyphs, glyphSize) ||
773 !reader.readByteArray(buf->pos, posSize)) {
774 return nullptr;
775 }
776
777 if (pe.extended) {
778 if (!reader.readByteArray(buf->clusters, clusterSize) ||
779 !reader.readByteArray(buf->utf8text, textSize)) {
780 return nullptr;
781 }
782 }
783 }
784
785 return blobBuilder.make();
786}
787
788sk_sp<SkTextBlob> SkTextBlob::MakeFromText(const void* text, size_t byteLength, const SkFont& font,
789 SkTextEncoding encoding) {
790 // Note: we deliberately promote this to fully positioned blobs, since we'd have to pay the
791 // same cost down stream (i.e. computing bounds), so its cheaper to pay the cost once now.
792 const int count = font.countText(text, byteLength, encoding);
793 if (count < 1) {
794 return nullptr;
795 }
797 auto buffer = builder.allocRunPos(font, count);
798 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
799 font.getPos(buffer.glyphs, count, buffer.points(), {0, 0});
800 return builder.make();
801}
802
804 const SkPoint pos[], const SkFont& font,
805 SkTextEncoding encoding) {
806 const int count = font.countText(text, byteLength, encoding);
807 if (count < 1) {
808 return nullptr;
809 }
811 auto buffer = builder.allocRunPos(font, count);
812 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
813 memcpy(buffer.points(), pos, count * sizeof(SkPoint));
814 return builder.make();
815}
816
818 const SkScalar xpos[], SkScalar constY,
819 const SkFont& font, SkTextEncoding encoding) {
820 const int count = font.countText(text, byteLength, encoding);
821 if (count < 1) {
822 return nullptr;
823 }
825 auto buffer = builder.allocRunPosH(font, count, constY);
826 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
827 memcpy(buffer.pos, xpos, count * sizeof(SkScalar));
828 return builder.make();
829}
830
832 const SkRSXform xform[], const SkFont& font,
833 SkTextEncoding encoding) {
834 const int count = font.countText(text, byteLength, encoding);
835 if (count < 1) {
836 return nullptr;
837 }
839 auto buffer = builder.allocRunRSXform(font, count);
840 font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count);
841 memcpy(buffer.xforms(), xform, count * sizeof(SkRSXform));
842 return builder.make();
843}
844
848
849 size_t total = buffer.bytesWritten();
851 buffer.writeToMemory(data->writable_data());
852 return data;
853}
854
856 const SkDeserialProcs& procs) {
858 buffer.setDeserialProcs(procs);
860}
861
862///////////////////////////////////////////////////////////////////////////////////////////////////
863
864size_t SkTextBlob::serialize(const SkSerialProcs& procs, void* memory, size_t memory_size) const {
865 SkBinaryWriteBuffer buffer(memory, memory_size, procs);
867 return buffer.usingInitialStorage() ? buffer.bytesWritten() : 0u;
868}
869
870///////////////////////////////////////////////////////////////////////////////////////////////////
871
872namespace {
873int get_glyph_run_intercepts(const sktext::GlyphRun& glyphRun,
874 const SkPaint& paint,
875 const SkScalar bounds[2],
876 SkScalar intervals[],
877 int* intervalCount) {
879 SkPaint interceptPaint{paint};
880 SkFont interceptFont{glyphRun.font()};
881
882 interceptPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
883
884 // can't use our canonical size if we need to apply path effects
885 if (interceptPaint.getPathEffect() == nullptr) {
886 // If the wrong size is going to be used, don't hint anything.
887 interceptFont.setHinting(SkFontHinting::kNone);
888 interceptFont.setSubpixel(true);
889 scale = interceptFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths;
891 // Note: fScale can be zero here (even if it wasn't before the divide). It can also
892 // be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior,
893 // since downstream we will check for the resulting coordinates being non-finite anyway.
894 // Thus we don't need to check for zero here.
895 if (interceptPaint.getStrokeWidth() > 0
896 && interceptPaint.getStyle() != SkPaint::kFill_Style) {
897 interceptPaint.setStrokeWidth(
898 sk_ieee_float_divide(interceptPaint.getStrokeWidth(), scale));
899 }
900 }
901
902 interceptPaint.setStyle(SkPaint::kFill_Style);
903 interceptPaint.setPathEffect(nullptr);
904
905 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint);
906 SkBulkGlyphMetricsAndPaths metricsAndPaths{strikeSpec};
907
908 const SkPoint* posCursor = glyphRun.positions().begin();
909 for (const SkGlyph* glyph : metricsAndPaths.glyphs(glyphRun.glyphsIDs())) {
910 SkPoint pos = *posCursor++;
911
912 if (glyph->path() != nullptr) {
913 // The typeface is scaled, so un-scale the bounds to be in the space of the typeface.
914 // Also ensure the bounds are properly offset by the vertical positioning of the glyph.
915 SkScalar scaledBounds[2] = {
916 (bounds[0] - pos.y()) / scale,
917 (bounds[1] - pos.y()) / scale
918 };
919 metricsAndPaths.findIntercepts(
920 scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
921 }
922 }
923 return *intervalCount;
924}
925} // namespace
926
928 const SkPaint* paint) const {
929 SkTLazy<SkPaint> defaultPaint;
930 if (paint == nullptr) {
931 defaultPaint.init();
932 paint = defaultPaint.get();
933 }
934
936 auto glyphRunList = builder.blobToGlyphRunList(*this, {0, 0});
937
938 int intervalCount = 0;
939 for (const sktext::GlyphRun& glyphRun : glyphRunList) {
940 // Ignore RSXForm runs.
941 if (glyphRun.scaledRotations().empty()) {
942 intervalCount = get_glyph_run_intercepts(
943 glyphRun, *paint, bounds, intervals, &intervalCount);
944 }
945 }
946
947 return intervalCount;
948}
949
950std::vector<SkScalar> SkFont::getIntercepts(const SkGlyphID glyphs[], int count,
951 const SkPoint positions[],
952 SkScalar top, SkScalar bottom,
953 const SkPaint* paintPtr) const {
954 if (count <= 0) {
955 return std::vector<SkScalar>();
956 }
957
958 const SkPaint paint(paintPtr ? *paintPtr : SkPaint());
959 const SkScalar bounds[] = {top, bottom};
960 const sktext::GlyphRun run(*this,
961 {positions, size_t(count)}, {glyphs, size_t(count)},
962 {nullptr, 0}, {nullptr, 0}, {nullptr, 0});
963
964 std::vector<SkScalar> result;
965 result.resize(count * 2); // worst case allocation
966 int intervalCount = 0;
967 intervalCount = get_glyph_run_intercepts(run, paint, bounds, result.data(), &intervalCount);
968 result.resize(intervalCount);
969 return result;
970}
971
972////////
973
975 fRunRecord = RunRecord::First(&blob);
976}
977
979 if (fRunRecord) {
980 if (rec) {
981 rec->fTypeface = fRunRecord->font().getTypeface();
982 rec->fGlyphCount = fRunRecord->glyphCount();
983 rec->fGlyphIndices = fRunRecord->glyphBuffer();
984#ifdef SK_UNTIL_CRBUG_1187654_IS_FIXED
985 rec->fClusterIndex_forTest = fRunRecord->clusterBuffer();
986 rec->fUtf8Size_forTest = fRunRecord->textSize();
987 rec->fUtf8_forTest = fRunRecord->textBuffer();
988#endif
989 }
990 if (fRunRecord->isLastRun()) {
991 fRunRecord = nullptr;
992 } else {
993 fRunRecord = RunRecord::Next(fRunRecord);
994 }
995 return true;
996 }
997 return false;
998}
999
1001 if (fRunRecord) {
1002 if (rec) {
1003 rec->font = fRunRecord->font();
1004 rec->count = fRunRecord->glyphCount();
1005 rec->glyphs = fRunRecord->glyphBuffer();
1006 rec->positions = fRunRecord->pointBuffer();
1007 }
1008 if (fRunRecord->isLastRun()) {
1009 fRunRecord = nullptr;
1010 } else {
1011 fRunRecord = RunRecord::Next(fRunRecord);
1012 }
1013 return true;
1014 }
1015 return false;
1016}
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
int count
Definition: FontMgrTest.cpp:50
SkPoint pos
const SkRect fBounds
uint16_t fFlags
Definition: ShapeLayer.cpp:106
static constexpr bool SkIsAlign4(T x)
Definition: SkAlign.h:20
static constexpr T SkAlignPtr(T x)
Definition: SkAlign.h:23
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
static constexpr float sk_ieee_float_divide(float numer, float denom)
@ kNone
glyph outlines unchanged
SkTextEncoding
Definition: SkFontTypes.h:11
@ kGlyphID
uses two byte words to represent glyph indices
SK_API void sk_free(void *)
#define SK_Scalar1
Definition: SkScalar.h:18
#define SkIntToScalar(x)
Definition: SkScalar.h:57
static SkRect map_quad_to_rect(const SkRSXform &xform, const SkRect &rect)
Definition: SkTextBlob.cpp:275
static uint32_t next_id()
Definition: SkTextBlob.cpp:146
@ kHorizontal_Positioning
Definition: SkTextBlob.cpp:194
@ kDefault_Positioning
Definition: SkTextBlob.cpp:193
@ kRSXform_Positioning
Definition: SkTextBlob.cpp:196
@ kFull_Positioning
Definition: SkTextBlob.cpp:195
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
uint16_t SkGlyphID
Definition: SkTypes.h:179
static constexpr uint32_t SK_InvalidUniqueID
Definition: SkTypes.h:196
static constexpr uint32_t SK_InvalidGenID
Definition: SkTypes.h:192
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition: SkData.cpp:116
static void Flatten(const SkFont &, SkWriteBuffer &buffer)
static constexpr int kCanonicalTextSizeForPaths
Definition: SkFontPriv.h:36
static bool Unflatten(SkFont *, SkReadBuffer &buffer)
static SkRect GetFontBounds(const SkFont &)
Definition: SkFont.cpp:354
Definition: SkFont.h:35
std::vector< SkScalar > getIntercepts(const SkGlyphID glyphs[], int count, const SkPoint pos[], SkScalar top, SkScalar bottom, const SkPaint *=nullptr) const
Definition: SkTextBlob.cpp:950
void setHinting(SkFontHinting hintingLevel)
Definition: SkFont.cpp:125
@ kSubpixelAntiAlias
glyph positioned in pixel using transparency
SkMatrix & setRSXform(const SkRSXform &rsxForm)
Definition: SkMatrix.cpp:420
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
void readRect(SkRect *rect)
void readPoint(SkPoint *point)
bool readByteArray(void *value, size_t size)
bool isValid() const
Definition: SkReadBuffer.h:208
int32_t read32()
size_t available() const
Definition: SkReadBuffer.h:82
size_t add(size_t x, size_t y)
Definition: SkSafeMath.h:33
size_t alignUp(size_t x, size_t alignment)
Definition: SkSafeMath.h:54
size_t mul(size_t x, size_t y)
Definition: SkSafeMath.h:29
static SkStrikeSpec MakeWithNoDevice(const SkFont &font, const SkPaint *paint=nullptr)
T * init(Args &&... args)
Definition: SkTLazy.h:45
T * get()
Definition: SkTLazy.h:83
const RunBuffer & allocRunRSXform(const SkFont &font, int count)
Definition: SkTextBlob.cpp:560
const RunBuffer & allocRunTextPosH(const SkFont &font, int count, SkScalar y, int textByteCount, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:578
const RunBuffer & allocRunPosH(const SkFont &font, int count, SkScalar y, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:546
const RunBuffer & allocRunText(const SkFont &font, int count, SkScalar x, SkScalar y, int textByteCount, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:565
const RunBuffer & allocRun(const SkFont &font, int count, SkScalar x, SkScalar y, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:539
const RunBuffer & allocRunTextRSXform(const SkFont &font, int count, int textByteCount, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:604
sk_sp< SkTextBlob > make()
Definition: SkTextBlob.cpp:617
const RunBuffer & allocRunTextPos(const SkFont &font, int count, int textByteCount, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:592
const RunBuffer & allocRunPos(const SkFont &font, int count, const SkRect *bounds=nullptr)
Definition: SkTextBlob.cpp:553
static void Flatten(const SkTextBlob &, SkWriteBuffer &)
Definition: SkTextBlob.cpp:663
static sk_sp< SkTextBlob > MakeFromBuffer(SkReadBuffer &)
Definition: SkTextBlob.cpp:705
uint32_t * clusters() const
SkTextBlobRunIterator(const SkTextBlob *blob)
Definition: SkTextBlob.cpp:222
GlyphPositioning positioning() const
Definition: SkTextBlob.cpp:236
const SkPoint & offset() const
const SkScalar * pos() const
unsigned scalarsPerGlyph() const
Definition: SkTextBlob.cpp:250
const uint16_t * glyphs() const
uint32_t glyphCount() const
uint32_t textSize() const
const SkFont & font() const
bool next(Run *)
Definition: SkTextBlob.cpp:978
bool experimentalNext(ExperimentalRun *)
Iter(const SkTextBlob &)
Definition: SkTextBlob.cpp:974
int getIntercepts(const SkScalar bounds[2], SkScalar intervals[], const SkPaint *paint=nullptr) const
Definition: SkTextBlob.cpp:927
static sk_sp< SkTextBlob > MakeFromPosTextH(const void *text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.cpp:817
const SkRect & bounds() const
Definition: SkTextBlob.h:53
static sk_sp< SkTextBlob > MakeFromText(const void *text, size_t byteLength, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.cpp:788
static sk_sp< SkTextBlob > MakeFromPosText(const void *text, size_t byteLength, const SkPoint pos[], const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.cpp:803
static sk_sp< SkTextBlob > Deserialize(const void *data, size_t size, const SkDeserialProcs &procs)
Definition: SkTextBlob.cpp:855
static sk_sp< SkTextBlob > MakeFromRSXform(const void *text, size_t byteLength, const SkRSXform xform[], const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.cpp:831
size_t serialize(const SkSerialProcs &procs, void *memory, size_t memory_size) const
Definition: SkTextBlob.cpp:864
void realloc(size_t count)
Definition: SkTemplates.h:291
SkSpan< const SkVector > scaledRotations() const
Definition: GlyphRun.h:54
const SkFont & font() const
Definition: GlyphRun.h:51
SkSpan< const SkGlyphID > glyphsIDs() const
Definition: GlyphRun.h:49
SkSpan< const SkPoint > positions() const
Definition: GlyphRun.h:48
const Paint & paint
Definition: color_source.cc:38
float SkScalar
Definition: extension.cpp:12
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
size_t length
std::u16string text
double y
double x
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
font
Font Metadata and Metrics.
Definition: run.py:1
def run(cmd)
Definition: run.py:14
const Scalar scale
SeparatedVector2 offset
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741
constexpr float right() const
Definition: SkRect.h:748
bool isEmpty() const
Definition: SkRect.h:693
void join(const SkRect &r)
Definition: SkRect.cpp:126
constexpr float bottom() const
Definition: SkRect.h:755
void setEmpty()
Definition: SkRect.h:842
SkScalar * pos
storage for glyph positions in run
Definition: SkTextBlob.h:330
char * utf8text
storage for text UTF-8 code units in run
Definition: SkTextBlob.h:331
SkGlyphID * glyphs
storage for glyph indexes in run
Definition: SkTextBlob.h:329
uint32_t * clusters
storage for glyph clusters (index of UTF-8 code unit)
Definition: SkTextBlob.h:332
SkTypeface * fTypeface
Definition: SkTextBlob.h:212
const uint16_t * fGlyphIndices
Definition: SkTextBlob.h:214
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
const uintptr_t id