36#if !defined(SK_DISABLE_LEGACY_SHAPER_FACTORY)
49 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n';
60 , fHAlignFactor(HAlignFactor(fDesc.fHAlign))
61 , fFont(fDesc.fTypeface, fDesc.fTextSize)
63 , fShapingFactory(shapingFactory) {
66 fShaper = fShapingFactory->
makeShaper(fFontMgr);
78 void beginLine()
override {
81 fLineClusters.reset(0);
85 fCurrentPosition = fOffset;
86 fPendingLineAdvance = { 0, 0 };
91 void runInfo(
const RunInfo&
info)
override {
92 fPendingLineAdvance +=
info.fAdvance;
95 info.fFont.getMetrics(&metrics);
102 void commitRunInfo()
override {}
104 Buffer runBuffer(
const RunInfo&
info)
override {
105 const auto run_start_index = fLineGlyphCount;
106 fLineGlyphCount +=
info.glyphCount;
108 fLineGlyphs.realloc(fLineGlyphCount);
109 fLinePos.realloc(fLineGlyphCount);
110 fLineClusters.realloc(fLineGlyphCount);
111 fLineRuns.push_back({
info.fFont,
info.glyphCount});
113 SkVector alignmentOffset { fHAlignFactor * (fPendingLineAdvance.
x() - fBox.
width()), 0 };
116 fLineGlyphs.get() + run_start_index,
117 fLinePos.get() + run_start_index,
119 fLineClusters.get() + run_start_index,
120 fCurrentPosition + alignmentOffset
124 void commitRunBuffer(
const RunInfo&
info)
override {
125 fCurrentPosition +=
info.fAdvance;
128 void commitLine()
override {
129 fOffset.fY += fDesc.fLineHeight;
137 auto adjust_trailing_whitespace = [
this]() {
146 for (
size_t i = 0;
i < fLineRuns.back().fSize; ++
i) {
160 fAdvanceBuffer.resize(ws_count);
161 fLineRuns.back().fFont.getWidths(fLineGlyphs.data() + fLineGlyphCount - ws_count,
162 SkToInt(ws_count), fAdvanceBuffer.data(),
nullptr);
164 const auto ws_advance = std::accumulate(fAdvanceBuffer.begin(),
165 fAdvanceBuffer.end(),
169 const auto offset = ws_advance*-fHAlignFactor;
173 fLinePos.data() + fLineGlyphCount,
178 adjust_trailing_whitespace();
180 const auto commit_proc = (fDesc.fFlags & Shaper::Flags::kFragmentGlyphs)
181 ? &ResultBuilder::commitFragementedRun
182 : &ResultBuilder::commitConsolidatedRun;
184 size_t run_offset = 0;
185 for (
const auto& rec : fLineRuns) {
186 SkASSERT(run_offset < fLineGlyphCount);
187 (this->*commit_proc)(rec,
188 fLineGlyphs.get() + run_offset,
189 fLinePos.get() + run_offset,
190 fLineClusters.get() + run_offset,
192 run_offset += rec.fSize;
198 Shaper::Result finalize(
SkSize* shaped_size) {
199 if (!(fDesc.
fFlags & Shaper::Flags::kFragmentGlyphs)) {
215 auto extent_box = [&](
bool include_typographical_extent) {
216 auto box =
fResult.computeVisualBounds();
218 if (include_typographical_extent) {
224 const auto typographical_top = fBox.
fTop +
ascent,
225 typographical_bottom = fBox.
fTop + fLastLineDescent +
226 fDesc.
fLineHeight*(fLineCount > 0 ? fLineCount - 1 : 0ul);
267 ebox.
init(extent_box(
true));
273 for (
auto& fragment :
fResult.fFragments) {
274 fragment.fOrigin.fY += v_offset;
281 void shapeLine(
const char* start,
const char* end,
size_t utf8_offset) {
305 if (fDesc.
fFlags & Shaper::Flags::kFragmentGlyphs) {
307 Shaper::ShapedGlyphs(),
323 if (fOffset.
y() > max_offset) {
332 const size_t utf8_bytes =
SkToSizeT(end - start);
334 static constexpr uint8_t kBidiLevelLTR = 0,
336 const auto lang_iter = fDesc.
fLocale
337 ? std::make_unique<SkShaper::TrivialLanguageRunIterator>(fDesc.
fLocale, utf8_bytes)
339#if defined(SKOTTIE_TRIVIAL_FONTRUN_ITER)
342 const auto font_iter = std::make_unique<SkShaper::TrivialFontRunIterator>(fFont,
346 start, utf8_bytes, fFont,
353 std::unique_ptr<SkShaper::BiDiRunIterator> bidi_iter =
355 shape_ltr ? kBidiLevelLTR : kBidiLevelRTL);
357 bidi_iter = std::make_unique<SkShaper::TrivialBiDiRunIterator>(
358 shape_ltr ? kBidiLevelLTR : kBidiLevelRTL, utf8_bytes);
362 std::unique_ptr<SkShaper::ScriptRunIterator> scpt_iter =
365 scpt_iter = std::make_unique<SkShaper::TrivialScriptRunIterator>(unknownScript, utf8_bytes);
368 if (!font_iter || !bidi_iter || !scpt_iter || !lang_iter) {
373 fUTF8Offset = utf8_offset;
374 fShaper->shape(start,
391 const uint32_t* clusters,
392 uint32_t line_index) {
395 if (fDesc.
fFlags & Shaper::Flags::kTrackFragmentAdvanceAscent) {
397 run.fFont.getMetrics(&metrics);
403 fAdvanceBuffer.resize(
run.fSize);
410 for (
size_t i = 0;
i <
run.fSize; ++
i) {
411 const auto advance = (fDesc.
fFlags & Shaper::Flags::kTrackFragmentAdvanceAscent)
421 ? std::vector<size_t>{ fUTF8Offset + clusters[
i] }
422 : std::vector<size_t>({}),
438 const uint32_t* clusters,
444 if (
fResult.fFragments.empty()) {
445 fResult.fFragments.push_back({{{}, {}, {}, {}}, {fBox.
x(), fBox.
y()}, 0, 0, 0,
false});
448 auto& current_glyphs =
fResult.fFragments.back().fGlyphs;
449 current_glyphs.fRuns.push_back(
run);
450 current_glyphs.fGlyphIDs.insert(current_glyphs.fGlyphIDs.end(),
glyphs,
glyphs +
run.fSize);
451 current_glyphs.fGlyphPos.insert(current_glyphs.fGlyphPos.end(),
pos ,
pos +
run.fSize);
453 for (
size_t i = 0;
i <
run.fSize; ++
i) {
458 current_glyphs.fClusters.reserve(current_glyphs.fClusters.size() +
run.fSize);
459 for (
size_t i = 0;
i <
run.fSize; ++
i) {
460 current_glyphs.fClusters.push_back(fUTF8Offset + clusters[
i]);
480 inline static constexpr SkGlyphID kMissingGlyphID = 0;
482 const Shaper::TextDesc& fDesc;
484 const float fHAlignFactor;
488 std::unique_ptr<SkShaper> fShaper;
495 size_t fLineGlyphCount = 0;
499 SkPoint fCurrentPosition{ 0, 0 };
501 SkVector fPendingLineAdvance{ 0, 0 };
502 uint32_t fLineCount = 0;
503 float fFirstLineAscent = 0,
504 fLastLineDescent = 0;
506 const char* fUTF8 =
nullptr;
507 size_t fUTF8Offset = 0;
512Shaper::Result ShapeImpl(
const SkString&
txt,
const Shaper::TextDesc&
desc,
516 const auto& is_line_break = [](
SkUnichar uch) {
521 const char* ptr =
txt.c_str();
522 const char* line_start = ptr;
523 const char*
begin = ptr;
524 const char*
end = ptr +
txt.size();
526 ResultBuilder rbuilder(
desc, box, fontmgr, shapingFactory);
529 rbuilder.shapeLine(line_start, ptr - 1,
SkToSizeT(line_start -
begin));
533 rbuilder.shapeLine(line_start, ptr,
SkToSizeT(line_start -
begin));
535 return rbuilder.finalize(shaped_size);
538bool result_fits(
const Shaper::Result& res,
const SkSize& res_size,
539 const SkRect& box,
const Shaper::TextDesc&
desc) {
541 if (
desc.fMaxLines) {
542 const auto line_count = res.fFragments.empty()
544 : res.fFragments.back().fLineIndex + 1;
545 if (line_count >
desc.fMaxLines) {
554Shaper::Result ShapeToFit(
const SkString&
txt,
const Shaper::TextDesc& orig_desc,
557 Shaper::Result best_result;
559 if (box.
isEmpty() || orig_desc.fTextSize <= 0) {
563 auto desc = orig_desc;
568 float in_scale = min_scale,
569 out_scale = max_scale,
570 try_scale =
SkTPin(1.0f, min_scale, max_scale);
577 static constexpr size_t kMaxIter = 16;
578 for (
size_t i = 0;
i < kMaxIter; ++
i) {
579 SkASSERT(try_scale >= in_scale && try_scale <= out_scale);
580 desc.fTextSize = try_scale * orig_desc.fTextSize;
581 desc.fLineHeight = try_scale * orig_desc.fLineHeight;
582 desc.fLineShift = try_scale * orig_desc.fLineShift;
583 desc.fAscent = try_scale * orig_desc.fAscent;
586 auto res = ShapeImpl(
txt,
desc, box, fontmgr, shapingFactory, &res_size);
588 const auto prev_scale = try_scale;
589 if (!result_fits(res, res_size, box,
desc)) {
590 out_scale = try_scale;
591 try_scale = (in_scale == min_scale)
593 ?
std::max(min_scale, try_scale * 0.5f)
595 : (in_scale + out_scale) * 0.5f;
598 best_result = std::move(res);
599 best_result.fScale = try_scale;
601 in_scale = try_scale;
602 try_scale = (out_scale == max_scale)
604 ?
std::min(max_scale, try_scale * 2)
606 : (in_scale + out_scale) * 0.5f;
609 if (try_scale == prev_scale) {
624 switch (
desc.fCapitalization) {
629 *fText.writable() =
unicode->toUpper(*fText);
635 operator const SkString&()
const {
return *fText; }
647 return (
desc.fResize == ResizePolicy::kScaleToFit ||
648 desc.fResize == ResizePolicy::kDownscaleToFit)
651 fontmgr, shapingFactory,
nullptr);
658 switch(
desc.fResize) {
660 return ShapeImpl(adjText,
desc, box, fontmgr, shapingFactory,
nullptr);
661 case ResizePolicy::kScaleToFit:
662 return ShapeToFit(adjText,
desc, box, fontmgr, shapingFactory);
663 case ResizePolicy::kDownscaleToFit: {
665 auto result = ShapeImpl(adjText,
desc, box, fontmgr, shapingFactory, &
size);
669 : ShapeToFit(adjText,
desc, box, fontmgr, shapingFactory);
682 for (
const auto&
run : fRuns) {
684 if (btype == BoundsType::kConservative) {
689 btype = BoundsType::kTight;
694 case BoundsType::kConservative: {
698 run_bounds.
fTop += font_bounds.
top();
704 case BoundsType::kTight: {
706 run.fFont.getBounds(fGlyphIDs.data() +
offset,
709 for (
size_t i = 0;
i <
run.fSize; ++
i) {
725 for (
const auto&
run : fRuns) {
727 fGlyphIDs.data() +
offset,
728 fGlyphPos.data() +
offset,
736SkRect Shaper::Result::computeVisualBounds()
const {
739 for (
const auto& frag: fFragments) {
740 bounds.join(frag.fGlyphs.computeBounds(ShapedGlyphs::BoundsType::kTight)
741 .makeOffset(frag.fOrigin));
747#if !defined(SK_DISABLE_LEGACY_SHAPER_FACTORY)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
static bool is_whitespace(char c)
@ kNone
glyph outlines unchanged
LoopControlFlowInfo fResult
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
constexpr size_t SkToSizeT(S x)
constexpr int SkToInt(S x)
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[], const uint32_t clusters[], int textByteCount, const char utf8text[], SkPoint origin, const SkFont &font, const SkPaint &paint)
static sk_sp< SkFontMgr > RefEmpty()
static SkRect GetFontBounds(const SkFont &)
void setSubpixel(bool subpixel)
void setLinearMetrics(bool linearMetrics)
SkTypeface * getTypeface() const
void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[], SkRect bounds[]) const
void setBaselineSnap(bool baselineSnap)
void setEdging(Edging edging)
void setHinting(SkFontHinting hintingLevel)
@ kAntiAlias
may have transparent pixels on glyph edges
static std::unique_ptr< FontRunIterator > MakeFontMgrRunIterator(const char *utf8, size_t utf8Bytes, const SkFont &font, sk_sp< SkFontMgr > fallback)
virtual std::unique_ptr< SkShaper::BiDiRunIterator > makeBidiRunIterator(const char *utf8, size_t utf8Bytes, uint8_t bidiLevel)=0
virtual std::unique_ptr< SkShaper > makeShaper(sk_sp< SkFontMgr > fallback)=0
virtual SkUnicode * getUnicode()=0
virtual std::unique_ptr< SkShaper::ScriptRunIterator > makeScriptRunIterator(const char *utf8, size_t utf8Bytes, SkFourByteTag script)=0
T * init(Args &&... args)
SkFontStyle fontStyle() const
static const char * begin(const StringSlice &s)
static float max(float r, float g, float b)
static float min(float r, float g, float b)
Optional< SkRect > bounds
SKSHAPER_API sk_sp< Factory > Factory()
SKSHAPER_API std::unique_ptr< SkShaper > PrimitiveText()
sk_sp< Factory > BestAvailable()
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
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
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
SkScalar fAscent
distance to reserve above baseline, typically negative
SkScalar fDescent
distance to reserve below baseline, typically positive
constexpr float y() const
constexpr float x() const
static constexpr SkRect MakeEmpty()
SkScalar fBottom
larger y-axis bounds
constexpr float left() const
constexpr float top() const
SkScalar fLeft
smaller x-axis bounds
constexpr float x() const
constexpr float y() const
SkScalar fRight
larger x-axis bounds
constexpr float height() const
constexpr float right() const
void setBounds(const SkPoint pts[], int count)
constexpr float centerY() const
constexpr float width() const
constexpr float bottom() const
SkScalar fTop
smaller y-axis bounds
static constexpr SkSize Make(SkScalar w, SkScalar h)
LinebreakPolicy fLinebreak