xref: /aosp_15_r20/external/skia/modules/skottie/src/text/TextShaper.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2019 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/include/TextShaper.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontMetrics.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontMgr.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontTypes.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFourByteTag.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypeface.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTPin.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/include/SkShaper.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/include/SkShaper_factory.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skunicode/include/SkUnicode.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTLazy.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUTF.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkFontPriv.h"
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
33*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
34*c8dee2aaSAndroid Build Coastguard Worker #include <numeric>
35*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
36*c8dee2aaSAndroid Build Coastguard Worker 
37*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SK_DISABLE_LEGACY_SHAPER_FACTORY)
38*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/utils/FactoryHelpers.h"
39*c8dee2aaSAndroid Build Coastguard Worker #endif
40*c8dee2aaSAndroid Build Coastguard Worker 
41*c8dee2aaSAndroid Build Coastguard Worker class SkPaint;
42*c8dee2aaSAndroid Build Coastguard Worker 
43*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
44*c8dee2aaSAndroid Build Coastguard Worker 
45*c8dee2aaSAndroid Build Coastguard Worker namespace skottie {
46*c8dee2aaSAndroid Build Coastguard Worker namespace {
is_whitespace(char c)47*c8dee2aaSAndroid Build Coastguard Worker static bool is_whitespace(char c) {
48*c8dee2aaSAndroid Build Coastguard Worker     // TODO: we've been getting away with this simple heuristic,
49*c8dee2aaSAndroid Build Coastguard Worker     // but ideally we should use SkUicode::isWhiteSpace().
50*c8dee2aaSAndroid Build Coastguard Worker     return c == ' ' || c == '\t' || c == '\r' || c == '\n';
51*c8dee2aaSAndroid Build Coastguard Worker }
52*c8dee2aaSAndroid Build Coastguard Worker 
53*c8dee2aaSAndroid Build Coastguard Worker // Helper for interfacing with SkShaper: buffers shaper-fed runs and performs
54*c8dee2aaSAndroid Build Coastguard Worker // per-line position adjustments (for external line breaking, horizontal alignment, etc).
55*c8dee2aaSAndroid Build Coastguard Worker class ResultBuilder final : public SkShaper::RunHandler {
56*c8dee2aaSAndroid Build Coastguard Worker public:
ResultBuilder(const Shaper::TextDesc & desc,const SkRect & box,const sk_sp<SkFontMgr> & fontmgr,const sk_sp<SkShapers::Factory> & shapingFactory)57*c8dee2aaSAndroid Build Coastguard Worker     ResultBuilder(const Shaper::TextDesc& desc, const SkRect& box, const sk_sp<SkFontMgr>& fontmgr,
58*c8dee2aaSAndroid Build Coastguard Worker                   const sk_sp<SkShapers::Factory>& shapingFactory)
59*c8dee2aaSAndroid Build Coastguard Worker             : fDesc(desc)
60*c8dee2aaSAndroid Build Coastguard Worker             , fBox(box)
61*c8dee2aaSAndroid Build Coastguard Worker             , fHAlignFactor(HAlignFactor(fDesc.fHAlign))
62*c8dee2aaSAndroid Build Coastguard Worker             , fFont(fDesc.fTypeface, fDesc.fTextSize)
63*c8dee2aaSAndroid Build Coastguard Worker             , fFontMgr(fontmgr)
64*c8dee2aaSAndroid Build Coastguard Worker             , fShapingFactory(shapingFactory) {
65*c8dee2aaSAndroid Build Coastguard Worker         // If the shaper callback returns null, fallback to the primitive shaper.
66*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fShapingFactory);
67*c8dee2aaSAndroid Build Coastguard Worker         fShaper = fShapingFactory->makeShaper(fFontMgr);
68*c8dee2aaSAndroid Build Coastguard Worker         if (!fShaper) {
69*c8dee2aaSAndroid Build Coastguard Worker             fShaper = SkShapers::Primitive::PrimitiveText();
70*c8dee2aaSAndroid Build Coastguard Worker             fShapingFactory = SkShapers::Primitive::Factory();
71*c8dee2aaSAndroid Build Coastguard Worker         }
72*c8dee2aaSAndroid Build Coastguard Worker         fFont.setHinting(SkFontHinting::kNone);
73*c8dee2aaSAndroid Build Coastguard Worker         fFont.setSubpixel(true);
74*c8dee2aaSAndroid Build Coastguard Worker         fFont.setLinearMetrics(true);
75*c8dee2aaSAndroid Build Coastguard Worker         fFont.setBaselineSnap(false);
76*c8dee2aaSAndroid Build Coastguard Worker         fFont.setEdging(SkFont::Edging::kAntiAlias);
77*c8dee2aaSAndroid Build Coastguard Worker     }
78*c8dee2aaSAndroid Build Coastguard Worker 
beginLine()79*c8dee2aaSAndroid Build Coastguard Worker     void beginLine() override {
80*c8dee2aaSAndroid Build Coastguard Worker         fLineGlyphs.reset(0);
81*c8dee2aaSAndroid Build Coastguard Worker         fLinePos.reset(0);
82*c8dee2aaSAndroid Build Coastguard Worker         fLineClusters.reset(0);
83*c8dee2aaSAndroid Build Coastguard Worker         fLineRuns.clear();
84*c8dee2aaSAndroid Build Coastguard Worker         fLineGlyphCount = 0;
85*c8dee2aaSAndroid Build Coastguard Worker 
86*c8dee2aaSAndroid Build Coastguard Worker         fCurrentPosition = fOffset;
87*c8dee2aaSAndroid Build Coastguard Worker         fPendingLineAdvance  = { 0, 0 };
88*c8dee2aaSAndroid Build Coastguard Worker 
89*c8dee2aaSAndroid Build Coastguard Worker         fLastLineDescent = 0;
90*c8dee2aaSAndroid Build Coastguard Worker     }
91*c8dee2aaSAndroid Build Coastguard Worker 
runInfo(const RunInfo & info)92*c8dee2aaSAndroid Build Coastguard Worker     void runInfo(const RunInfo& info) override {
93*c8dee2aaSAndroid Build Coastguard Worker         fPendingLineAdvance += info.fAdvance;
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker         SkFontMetrics metrics;
96*c8dee2aaSAndroid Build Coastguard Worker         info.fFont.getMetrics(&metrics);
97*c8dee2aaSAndroid Build Coastguard Worker         if (!fLineCount) {
98*c8dee2aaSAndroid Build Coastguard Worker             fFirstLineAscent = std::min(fFirstLineAscent, metrics.fAscent);
99*c8dee2aaSAndroid Build Coastguard Worker         }
100*c8dee2aaSAndroid Build Coastguard Worker         fLastLineDescent = std::max(fLastLineDescent, metrics.fDescent);
101*c8dee2aaSAndroid Build Coastguard Worker     }
102*c8dee2aaSAndroid Build Coastguard Worker 
commitRunInfo()103*c8dee2aaSAndroid Build Coastguard Worker     void commitRunInfo() override {}
104*c8dee2aaSAndroid Build Coastguard Worker 
runBuffer(const RunInfo & info)105*c8dee2aaSAndroid Build Coastguard Worker     Buffer runBuffer(const RunInfo& info) override {
106*c8dee2aaSAndroid Build Coastguard Worker         const auto run_start_index = fLineGlyphCount;
107*c8dee2aaSAndroid Build Coastguard Worker         fLineGlyphCount += info.glyphCount;
108*c8dee2aaSAndroid Build Coastguard Worker 
109*c8dee2aaSAndroid Build Coastguard Worker         fLineGlyphs.realloc(fLineGlyphCount);
110*c8dee2aaSAndroid Build Coastguard Worker         fLinePos.realloc(fLineGlyphCount);
111*c8dee2aaSAndroid Build Coastguard Worker         fLineClusters.realloc(fLineGlyphCount);
112*c8dee2aaSAndroid Build Coastguard Worker         fLineRuns.push_back({info.fFont, info.glyphCount});
113*c8dee2aaSAndroid Build Coastguard Worker 
114*c8dee2aaSAndroid Build Coastguard Worker         SkVector alignmentOffset { fHAlignFactor * (fPendingLineAdvance.x() - fBox.width()), 0 };
115*c8dee2aaSAndroid Build Coastguard Worker 
116*c8dee2aaSAndroid Build Coastguard Worker         return {
117*c8dee2aaSAndroid Build Coastguard Worker             fLineGlyphs.get()   + run_start_index,
118*c8dee2aaSAndroid Build Coastguard Worker             fLinePos.get()      + run_start_index,
119*c8dee2aaSAndroid Build Coastguard Worker             nullptr,
120*c8dee2aaSAndroid Build Coastguard Worker             fLineClusters.get() + run_start_index,
121*c8dee2aaSAndroid Build Coastguard Worker             fCurrentPosition + alignmentOffset
122*c8dee2aaSAndroid Build Coastguard Worker         };
123*c8dee2aaSAndroid Build Coastguard Worker     }
124*c8dee2aaSAndroid Build Coastguard Worker 
commitRunBuffer(const RunInfo & info)125*c8dee2aaSAndroid Build Coastguard Worker     void commitRunBuffer(const RunInfo& info) override {
126*c8dee2aaSAndroid Build Coastguard Worker         fCurrentPosition += info.fAdvance;
127*c8dee2aaSAndroid Build Coastguard Worker     }
128*c8dee2aaSAndroid Build Coastguard Worker 
commitLine()129*c8dee2aaSAndroid Build Coastguard Worker     void commitLine() override {
130*c8dee2aaSAndroid Build Coastguard Worker         fOffset.fY += fDesc.fLineHeight;
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker         // Observed AE handling of whitespace, for alignment purposes:
133*c8dee2aaSAndroid Build Coastguard Worker         //
134*c8dee2aaSAndroid Build Coastguard Worker         //   - leading whitespace contributes to alignment
135*c8dee2aaSAndroid Build Coastguard Worker         //   - trailing whitespace is ignored
136*c8dee2aaSAndroid Build Coastguard Worker         //   - auto line breaking retains all separating whitespace on the first line (no artificial
137*c8dee2aaSAndroid Build Coastguard Worker         //     leading WS is created).
138*c8dee2aaSAndroid Build Coastguard Worker         auto adjust_trailing_whitespace = [this]() {
139*c8dee2aaSAndroid Build Coastguard Worker             // For left-alignment, trailing WS doesn't make any difference.
140*c8dee2aaSAndroid Build Coastguard Worker             if (fLineRuns.empty() || fDesc.fHAlign == SkTextUtils::Align::kLeft_Align) {
141*c8dee2aaSAndroid Build Coastguard Worker                 return;
142*c8dee2aaSAndroid Build Coastguard Worker             }
143*c8dee2aaSAndroid Build Coastguard Worker 
144*c8dee2aaSAndroid Build Coastguard Worker             // Technically, trailing whitespace could span multiple runs, but realistically,
145*c8dee2aaSAndroid Build Coastguard Worker             // SkShaper has no reason to split it.  Hence we're only checking the last run.
146*c8dee2aaSAndroid Build Coastguard Worker             size_t ws_count = 0;
147*c8dee2aaSAndroid Build Coastguard Worker             for (size_t i = 0; i < fLineRuns.back().fSize; ++i) {
148*c8dee2aaSAndroid Build Coastguard Worker                 if (is_whitespace(fUTF8[fLineClusters[SkToInt(fLineGlyphCount - i - 1)]])) {
149*c8dee2aaSAndroid Build Coastguard Worker                     ++ws_count;
150*c8dee2aaSAndroid Build Coastguard Worker                 } else {
151*c8dee2aaSAndroid Build Coastguard Worker                     break;
152*c8dee2aaSAndroid Build Coastguard Worker                 }
153*c8dee2aaSAndroid Build Coastguard Worker             }
154*c8dee2aaSAndroid Build Coastguard Worker 
155*c8dee2aaSAndroid Build Coastguard Worker             // No trailing whitespace.
156*c8dee2aaSAndroid Build Coastguard Worker             if (!ws_count) {
157*c8dee2aaSAndroid Build Coastguard Worker                 return;
158*c8dee2aaSAndroid Build Coastguard Worker             }
159*c8dee2aaSAndroid Build Coastguard Worker 
160*c8dee2aaSAndroid Build Coastguard Worker             // Compute the cumulative whitespace advance.
161*c8dee2aaSAndroid Build Coastguard Worker             fAdvanceBuffer.resize(ws_count);
162*c8dee2aaSAndroid Build Coastguard Worker             fLineRuns.back().fFont.getWidths(fLineGlyphs.data() + fLineGlyphCount - ws_count,
163*c8dee2aaSAndroid Build Coastguard Worker                                              SkToInt(ws_count), fAdvanceBuffer.data(), nullptr);
164*c8dee2aaSAndroid Build Coastguard Worker 
165*c8dee2aaSAndroid Build Coastguard Worker             const auto ws_advance = std::accumulate(fAdvanceBuffer.begin(),
166*c8dee2aaSAndroid Build Coastguard Worker                                                     fAdvanceBuffer.end(),
167*c8dee2aaSAndroid Build Coastguard Worker                                                     0.0f);
168*c8dee2aaSAndroid Build Coastguard Worker 
169*c8dee2aaSAndroid Build Coastguard Worker             // Offset needed to compensate for whitespace.
170*c8dee2aaSAndroid Build Coastguard Worker             const auto offset = ws_advance*-fHAlignFactor;
171*c8dee2aaSAndroid Build Coastguard Worker 
172*c8dee2aaSAndroid Build Coastguard Worker             // Shift the whole line horizontally by the computed offset.
173*c8dee2aaSAndroid Build Coastguard Worker             std::transform(fLinePos.data(),
174*c8dee2aaSAndroid Build Coastguard Worker                            fLinePos.data() + fLineGlyphCount,
175*c8dee2aaSAndroid Build Coastguard Worker                            fLinePos.data(),
176*c8dee2aaSAndroid Build Coastguard Worker                            [&offset](SkPoint pos) { return SkPoint{pos.fX + offset, pos.fY}; });
177*c8dee2aaSAndroid Build Coastguard Worker         };
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker         adjust_trailing_whitespace();
180*c8dee2aaSAndroid Build Coastguard Worker 
181*c8dee2aaSAndroid Build Coastguard Worker         const auto commit_proc = (fDesc.fFlags & Shaper::Flags::kFragmentGlyphs)
182*c8dee2aaSAndroid Build Coastguard Worker             ? &ResultBuilder::commitFragementedRun
183*c8dee2aaSAndroid Build Coastguard Worker             : &ResultBuilder::commitConsolidatedRun;
184*c8dee2aaSAndroid Build Coastguard Worker 
185*c8dee2aaSAndroid Build Coastguard Worker         size_t run_offset = 0;
186*c8dee2aaSAndroid Build Coastguard Worker         for (const auto& rec : fLineRuns) {
187*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(run_offset < fLineGlyphCount);
188*c8dee2aaSAndroid Build Coastguard Worker             (this->*commit_proc)(rec,
189*c8dee2aaSAndroid Build Coastguard Worker                         fLineGlyphs.get()   + run_offset,
190*c8dee2aaSAndroid Build Coastguard Worker                         fLinePos.get()      + run_offset,
191*c8dee2aaSAndroid Build Coastguard Worker                         fLineClusters.get() + run_offset,
192*c8dee2aaSAndroid Build Coastguard Worker                         fLineCount);
193*c8dee2aaSAndroid Build Coastguard Worker             run_offset += rec.fSize;
194*c8dee2aaSAndroid Build Coastguard Worker         }
195*c8dee2aaSAndroid Build Coastguard Worker 
196*c8dee2aaSAndroid Build Coastguard Worker         fLineCount++;
197*c8dee2aaSAndroid Build Coastguard Worker     }
198*c8dee2aaSAndroid Build Coastguard Worker 
finalize(SkSize * shaped_size)199*c8dee2aaSAndroid Build Coastguard Worker     Shaper::Result finalize(SkSize* shaped_size) {
200*c8dee2aaSAndroid Build Coastguard Worker         if (!(fDesc.fFlags & Shaper::Flags::kFragmentGlyphs)) {
201*c8dee2aaSAndroid Build Coastguard Worker             // All glyphs (if any) are pending in a single fragment.
202*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(fResult.fFragments.size() <= 1);
203*c8dee2aaSAndroid Build Coastguard Worker         }
204*c8dee2aaSAndroid Build Coastguard Worker 
205*c8dee2aaSAndroid Build Coastguard Worker         const auto ascent = this->ascent();
206*c8dee2aaSAndroid Build Coastguard Worker 
207*c8dee2aaSAndroid Build Coastguard Worker         // For visual VAlign modes, we use a hybrid extent box computed as the union of
208*c8dee2aaSAndroid Build Coastguard Worker         // actual visual bounds and the vertical typographical extent.
209*c8dee2aaSAndroid Build Coastguard Worker         //
210*c8dee2aaSAndroid Build Coastguard Worker         // This ensures that
211*c8dee2aaSAndroid Build Coastguard Worker         //
212*c8dee2aaSAndroid Build Coastguard Worker         //   a) text doesn't visually overflow the alignment boundaries
213*c8dee2aaSAndroid Build Coastguard Worker         //
214*c8dee2aaSAndroid Build Coastguard Worker         //   b) leading/trailing empty lines are still taken into account for alignment purposes
215*c8dee2aaSAndroid Build Coastguard Worker 
216*c8dee2aaSAndroid Build Coastguard Worker         auto extent_box = [&](bool include_typographical_extent) {
217*c8dee2aaSAndroid Build Coastguard Worker             auto box = fResult.computeVisualBounds();
218*c8dee2aaSAndroid Build Coastguard Worker 
219*c8dee2aaSAndroid Build Coastguard Worker             if (include_typographical_extent) {
220*c8dee2aaSAndroid Build Coastguard Worker                 // Hybrid visual alignment mode, based on typographical extent.
221*c8dee2aaSAndroid Build Coastguard Worker 
222*c8dee2aaSAndroid Build Coastguard Worker                 // By default, first line is vertically-aligned on a baseline of 0.
223*c8dee2aaSAndroid Build Coastguard Worker                 // The typographical height considered for vertical alignment is the distance
224*c8dee2aaSAndroid Build Coastguard Worker                 // between the first line top (ascent) to the last line bottom (descent).
225*c8dee2aaSAndroid Build Coastguard Worker                 const auto typographical_top    = fBox.fTop + ascent,
226*c8dee2aaSAndroid Build Coastguard Worker                            typographical_bottom = fBox.fTop + fLastLineDescent +
227*c8dee2aaSAndroid Build Coastguard Worker                                           fDesc.fLineHeight*(fLineCount > 0 ? fLineCount - 1 : 0ul);
228*c8dee2aaSAndroid Build Coastguard Worker 
229*c8dee2aaSAndroid Build Coastguard Worker                 box.fTop    = std::min(box.fTop,    typographical_top);
230*c8dee2aaSAndroid Build Coastguard Worker                 box.fBottom = std::max(box.fBottom, typographical_bottom);
231*c8dee2aaSAndroid Build Coastguard Worker             }
232*c8dee2aaSAndroid Build Coastguard Worker 
233*c8dee2aaSAndroid Build Coastguard Worker             return box;
234*c8dee2aaSAndroid Build Coastguard Worker         };
235*c8dee2aaSAndroid Build Coastguard Worker 
236*c8dee2aaSAndroid Build Coastguard Worker         // Only compute the extent box when needed.
237*c8dee2aaSAndroid Build Coastguard Worker         SkTLazy<SkRect> ebox;
238*c8dee2aaSAndroid Build Coastguard Worker 
239*c8dee2aaSAndroid Build Coastguard Worker         // Vertical adjustments.
240*c8dee2aaSAndroid Build Coastguard Worker         float v_offset = -fDesc.fLineShift;
241*c8dee2aaSAndroid Build Coastguard Worker 
242*c8dee2aaSAndroid Build Coastguard Worker         switch (fDesc.fVAlign) {
243*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::VAlign::kTop:
244*c8dee2aaSAndroid Build Coastguard Worker             v_offset -= ascent;
245*c8dee2aaSAndroid Build Coastguard Worker             break;
246*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::VAlign::kTopBaseline:
247*c8dee2aaSAndroid Build Coastguard Worker             // Default behavior.
248*c8dee2aaSAndroid Build Coastguard Worker             break;
249*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::VAlign::kHybridTop:
250*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::VAlign::kVisualTop:
251*c8dee2aaSAndroid Build Coastguard Worker             ebox.init(extent_box(fDesc.fVAlign == Shaper::VAlign::kHybridTop));
252*c8dee2aaSAndroid Build Coastguard Worker             v_offset += fBox.fTop - ebox->fTop;
253*c8dee2aaSAndroid Build Coastguard Worker             break;
254*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::VAlign::kHybridCenter:
255*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::VAlign::kVisualCenter:
256*c8dee2aaSAndroid Build Coastguard Worker             ebox.init(extent_box(fDesc.fVAlign == Shaper::VAlign::kHybridCenter));
257*c8dee2aaSAndroid Build Coastguard Worker             v_offset += fBox.centerY() - ebox->centerY();
258*c8dee2aaSAndroid Build Coastguard Worker             break;
259*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::VAlign::kHybridBottom:
260*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::VAlign::kVisualBottom:
261*c8dee2aaSAndroid Build Coastguard Worker             ebox.init(extent_box(fDesc.fVAlign == Shaper::VAlign::kHybridBottom));
262*c8dee2aaSAndroid Build Coastguard Worker             v_offset += fBox.fBottom - ebox->fBottom;
263*c8dee2aaSAndroid Build Coastguard Worker             break;
264*c8dee2aaSAndroid Build Coastguard Worker         }
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker         if (shaped_size) {
267*c8dee2aaSAndroid Build Coastguard Worker             if (!ebox.isValid()) {
268*c8dee2aaSAndroid Build Coastguard Worker                 ebox.init(extent_box(true));
269*c8dee2aaSAndroid Build Coastguard Worker             }
270*c8dee2aaSAndroid Build Coastguard Worker             *shaped_size = SkSize::Make(ebox->width(), ebox->height());
271*c8dee2aaSAndroid Build Coastguard Worker         }
272*c8dee2aaSAndroid Build Coastguard Worker 
273*c8dee2aaSAndroid Build Coastguard Worker         if (v_offset) {
274*c8dee2aaSAndroid Build Coastguard Worker             for (auto& fragment : fResult.fFragments) {
275*c8dee2aaSAndroid Build Coastguard Worker                 fragment.fOrigin.fY += v_offset;
276*c8dee2aaSAndroid Build Coastguard Worker             }
277*c8dee2aaSAndroid Build Coastguard Worker         }
278*c8dee2aaSAndroid Build Coastguard Worker 
279*c8dee2aaSAndroid Build Coastguard Worker         return std::move(fResult);
280*c8dee2aaSAndroid Build Coastguard Worker     }
281*c8dee2aaSAndroid Build Coastguard Worker 
shapeLine(const char * start,const char * end,size_t utf8_offset)282*c8dee2aaSAndroid Build Coastguard Worker     void shapeLine(const char* start, const char* end, size_t utf8_offset) {
283*c8dee2aaSAndroid Build Coastguard Worker         if (!fShaper) {
284*c8dee2aaSAndroid Build Coastguard Worker             return;
285*c8dee2aaSAndroid Build Coastguard Worker         }
286*c8dee2aaSAndroid Build Coastguard Worker 
287*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(start <= end);
288*c8dee2aaSAndroid Build Coastguard Worker         if (start == end) {
289*c8dee2aaSAndroid Build Coastguard Worker             // SkShaper doesn't care for empty lines.
290*c8dee2aaSAndroid Build Coastguard Worker             this->beginLine();
291*c8dee2aaSAndroid Build Coastguard Worker             this->commitLine();
292*c8dee2aaSAndroid Build Coastguard Worker 
293*c8dee2aaSAndroid Build Coastguard Worker             // The calls above perform bookkeeping, but they do not add any fragments (since there
294*c8dee2aaSAndroid Build Coastguard Worker             // are no runs to commit).
295*c8dee2aaSAndroid Build Coastguard Worker             //
296*c8dee2aaSAndroid Build Coastguard Worker             // Certain Skottie features (line-based range selectors) do require accurate indexing
297*c8dee2aaSAndroid Build Coastguard Worker             // information even for empty lines though -- so we inject empty fragments solely for
298*c8dee2aaSAndroid Build Coastguard Worker             // line index tracking.
299*c8dee2aaSAndroid Build Coastguard Worker             //
300*c8dee2aaSAndroid Build Coastguard Worker             // Note: we don't add empty fragments in consolidated mode because 1) consolidated mode
301*c8dee2aaSAndroid Build Coastguard Worker             // assumes there is a single result fragment and 2) kFragmentGlyphs is always enabled
302*c8dee2aaSAndroid Build Coastguard Worker             // for cases where line index tracking is relevant.
303*c8dee2aaSAndroid Build Coastguard Worker             //
304*c8dee2aaSAndroid Build Coastguard Worker             // TODO(fmalita): investigate whether it makes sense to move this special case down
305*c8dee2aaSAndroid Build Coastguard Worker             // to commitFragmentedRun().
306*c8dee2aaSAndroid Build Coastguard Worker             if (fDesc.fFlags & Shaper::Flags::kFragmentGlyphs) {
307*c8dee2aaSAndroid Build Coastguard Worker                 fResult.fFragments.push_back({
308*c8dee2aaSAndroid Build Coastguard Worker                     Shaper::ShapedGlyphs(),
309*c8dee2aaSAndroid Build Coastguard Worker                     {fBox.x(),fBox.y()},
310*c8dee2aaSAndroid Build Coastguard Worker                     0, 0,
311*c8dee2aaSAndroid Build Coastguard Worker                     fLineCount - 1,
312*c8dee2aaSAndroid Build Coastguard Worker                     false
313*c8dee2aaSAndroid Build Coastguard Worker                 });
314*c8dee2aaSAndroid Build Coastguard Worker             }
315*c8dee2aaSAndroid Build Coastguard Worker 
316*c8dee2aaSAndroid Build Coastguard Worker             return;
317*c8dee2aaSAndroid Build Coastguard Worker         }
318*c8dee2aaSAndroid Build Coastguard Worker 
319*c8dee2aaSAndroid Build Coastguard Worker         // In default paragraph mode (VAlign::kTop), AE clips out lines when the baseline
320*c8dee2aaSAndroid Build Coastguard Worker         // goes below the box lower edge.
321*c8dee2aaSAndroid Build Coastguard Worker         if (fDesc.fVAlign == Shaper::VAlign::kTop) {
322*c8dee2aaSAndroid Build Coastguard Worker             // fOffset is relative to the first line baseline.
323*c8dee2aaSAndroid Build Coastguard Worker             const auto max_offset = fBox.height() + this->ascent(); // NB: ascent is negative
324*c8dee2aaSAndroid Build Coastguard Worker             if (fOffset.y() > max_offset) {
325*c8dee2aaSAndroid Build Coastguard Worker                 return;
326*c8dee2aaSAndroid Build Coastguard Worker             }
327*c8dee2aaSAndroid Build Coastguard Worker         }
328*c8dee2aaSAndroid Build Coastguard Worker 
329*c8dee2aaSAndroid Build Coastguard Worker         const auto shape_width  = fDesc.fLinebreak == Shaper::LinebreakPolicy::kExplicit
330*c8dee2aaSAndroid Build Coastguard Worker                                     ? SK_ScalarMax
331*c8dee2aaSAndroid Build Coastguard Worker                                     : fBox.width();
332*c8dee2aaSAndroid Build Coastguard Worker         const auto shape_ltr    = fDesc.fDirection == Shaper::Direction::kLTR;
333*c8dee2aaSAndroid Build Coastguard Worker         const size_t utf8_bytes = SkToSizeT(end - start);
334*c8dee2aaSAndroid Build Coastguard Worker 
335*c8dee2aaSAndroid Build Coastguard Worker         static constexpr uint8_t kBidiLevelLTR = 0,
336*c8dee2aaSAndroid Build Coastguard Worker                                  kBidiLevelRTL = 1;
337*c8dee2aaSAndroid Build Coastguard Worker         const auto lang_iter = fDesc.fLocale
338*c8dee2aaSAndroid Build Coastguard Worker                 ? std::make_unique<SkShaper::TrivialLanguageRunIterator>(fDesc.fLocale, utf8_bytes)
339*c8dee2aaSAndroid Build Coastguard Worker                 : SkShaper::MakeStdLanguageRunIterator(start, utf8_bytes);
340*c8dee2aaSAndroid Build Coastguard Worker #if defined(SKOTTIE_TRIVIAL_FONTRUN_ITER)
341*c8dee2aaSAndroid Build Coastguard Worker         // Chrome Linux/CrOS does not have a fallback-capable fontmgr, and crashes if fallback is
342*c8dee2aaSAndroid Build Coastguard Worker         // triggered.  Using a TrivialFontRunIterator avoids the issue (https://crbug.com/1520148).
343*c8dee2aaSAndroid Build Coastguard Worker         const auto font_iter = std::make_unique<SkShaper::TrivialFontRunIterator>(fFont,
344*c8dee2aaSAndroid Build Coastguard Worker                                                                                   utf8_bytes);
345*c8dee2aaSAndroid Build Coastguard Worker #else
346*c8dee2aaSAndroid Build Coastguard Worker         const auto font_iter = SkShaper::MakeFontMgrRunIterator(
347*c8dee2aaSAndroid Build Coastguard Worker                                     start, utf8_bytes, fFont,
348*c8dee2aaSAndroid Build Coastguard Worker                                     fFontMgr ? fFontMgr : SkFontMgr::RefEmpty(), // used as fallback
349*c8dee2aaSAndroid Build Coastguard Worker                                     fDesc.fFontFamily,
350*c8dee2aaSAndroid Build Coastguard Worker                                     fFont.getTypeface()->fontStyle(),
351*c8dee2aaSAndroid Build Coastguard Worker                                     lang_iter.get());
352*c8dee2aaSAndroid Build Coastguard Worker #endif
353*c8dee2aaSAndroid Build Coastguard Worker 
354*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::BiDiRunIterator> bidi_iter =
355*c8dee2aaSAndroid Build Coastguard Worker                 fShapingFactory->makeBidiRunIterator(start, utf8_bytes,
356*c8dee2aaSAndroid Build Coastguard Worker                                                   shape_ltr ? kBidiLevelLTR : kBidiLevelRTL);
357*c8dee2aaSAndroid Build Coastguard Worker         if (!bidi_iter) {
358*c8dee2aaSAndroid Build Coastguard Worker             bidi_iter = std::make_unique<SkShaper::TrivialBiDiRunIterator>(
359*c8dee2aaSAndroid Build Coastguard Worker                     shape_ltr ? kBidiLevelLTR : kBidiLevelRTL, utf8_bytes);
360*c8dee2aaSAndroid Build Coastguard Worker         }
361*c8dee2aaSAndroid Build Coastguard Worker 
362*c8dee2aaSAndroid Build Coastguard Worker         constexpr SkFourByteTag unknownScript = SkSetFourByteTag('Z', 'z', 'z', 'z');
363*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::ScriptRunIterator> scpt_iter =
364*c8dee2aaSAndroid Build Coastguard Worker                 fShapingFactory->makeScriptRunIterator(start, utf8_bytes, unknownScript);
365*c8dee2aaSAndroid Build Coastguard Worker         if (!scpt_iter) {
366*c8dee2aaSAndroid Build Coastguard Worker             scpt_iter = std::make_unique<SkShaper::TrivialScriptRunIterator>(unknownScript, utf8_bytes);
367*c8dee2aaSAndroid Build Coastguard Worker         }
368*c8dee2aaSAndroid Build Coastguard Worker 
369*c8dee2aaSAndroid Build Coastguard Worker         if (!font_iter || !bidi_iter || !scpt_iter || !lang_iter) {
370*c8dee2aaSAndroid Build Coastguard Worker             return;
371*c8dee2aaSAndroid Build Coastguard Worker         }
372*c8dee2aaSAndroid Build Coastguard Worker 
373*c8dee2aaSAndroid Build Coastguard Worker         fUTF8 = start;
374*c8dee2aaSAndroid Build Coastguard Worker         fUTF8Offset = utf8_offset;
375*c8dee2aaSAndroid Build Coastguard Worker         fShaper->shape(start,
376*c8dee2aaSAndroid Build Coastguard Worker                        utf8_bytes,
377*c8dee2aaSAndroid Build Coastguard Worker                        *font_iter,
378*c8dee2aaSAndroid Build Coastguard Worker                        *bidi_iter,
379*c8dee2aaSAndroid Build Coastguard Worker                        *scpt_iter,
380*c8dee2aaSAndroid Build Coastguard Worker                        *lang_iter,
381*c8dee2aaSAndroid Build Coastguard Worker                        nullptr,
382*c8dee2aaSAndroid Build Coastguard Worker                        0,
383*c8dee2aaSAndroid Build Coastguard Worker                        shape_width,
384*c8dee2aaSAndroid Build Coastguard Worker                        this);
385*c8dee2aaSAndroid Build Coastguard Worker         fUTF8 = nullptr;
386*c8dee2aaSAndroid Build Coastguard Worker     }
387*c8dee2aaSAndroid Build Coastguard Worker 
388*c8dee2aaSAndroid Build Coastguard Worker private:
commitFragementedRun(const skottie::Shaper::RunRec & run,const SkGlyphID * glyphs,const SkPoint * pos,const uint32_t * clusters,uint32_t line_index)389*c8dee2aaSAndroid Build Coastguard Worker     void commitFragementedRun(const skottie::Shaper::RunRec& run,
390*c8dee2aaSAndroid Build Coastguard Worker                               const SkGlyphID* glyphs,
391*c8dee2aaSAndroid Build Coastguard Worker                               const SkPoint* pos,
392*c8dee2aaSAndroid Build Coastguard Worker                               const uint32_t* clusters,
393*c8dee2aaSAndroid Build Coastguard Worker                               uint32_t line_index) {
394*c8dee2aaSAndroid Build Coastguard Worker         float ascent = 0;
395*c8dee2aaSAndroid Build Coastguard Worker 
396*c8dee2aaSAndroid Build Coastguard Worker         if (fDesc.fFlags & Shaper::Flags::kTrackFragmentAdvanceAscent) {
397*c8dee2aaSAndroid Build Coastguard Worker             SkFontMetrics metrics;
398*c8dee2aaSAndroid Build Coastguard Worker             run.fFont.getMetrics(&metrics);
399*c8dee2aaSAndroid Build Coastguard Worker             ascent = metrics.fAscent;
400*c8dee2aaSAndroid Build Coastguard Worker 
401*c8dee2aaSAndroid Build Coastguard Worker             // Note: we use per-glyph advances for anchoring, but it's unclear whether this
402*c8dee2aaSAndroid Build Coastguard Worker             // is exactly the same as AE.  E.g. are 'acute' glyphs anchored separately for fonts
403*c8dee2aaSAndroid Build Coastguard Worker             // in which they're distinct?
404*c8dee2aaSAndroid Build Coastguard Worker             fAdvanceBuffer.resize(run.fSize);
405*c8dee2aaSAndroid Build Coastguard Worker             fFont.getWidths(glyphs, SkToInt(run.fSize), fAdvanceBuffer.data());
406*c8dee2aaSAndroid Build Coastguard Worker         }
407*c8dee2aaSAndroid Build Coastguard Worker 
408*c8dee2aaSAndroid Build Coastguard Worker         // In fragmented mode we immediately push the glyphs to fResult,
409*c8dee2aaSAndroid Build Coastguard Worker         // one fragment per glyph.  Glyph positioning is externalized
410*c8dee2aaSAndroid Build Coastguard Worker         // (positions returned in Fragment::fPos).
411*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 0; i < run.fSize; ++i) {
412*c8dee2aaSAndroid Build Coastguard Worker             const auto advance = (fDesc.fFlags & Shaper::Flags::kTrackFragmentAdvanceAscent)
413*c8dee2aaSAndroid Build Coastguard Worker                     ? fAdvanceBuffer[SkToInt(i)]
414*c8dee2aaSAndroid Build Coastguard Worker                     : 0.0f;
415*c8dee2aaSAndroid Build Coastguard Worker 
416*c8dee2aaSAndroid Build Coastguard Worker             fResult.fFragments.push_back({
417*c8dee2aaSAndroid Build Coastguard Worker                 {
418*c8dee2aaSAndroid Build Coastguard Worker                     { {run.fFont, 1} },
419*c8dee2aaSAndroid Build Coastguard Worker                     { glyphs[i] },
420*c8dee2aaSAndroid Build Coastguard Worker                     { {0,0} },
421*c8dee2aaSAndroid Build Coastguard Worker                     fDesc.fFlags & Shaper::kClusters
422*c8dee2aaSAndroid Build Coastguard Worker                         ? std::vector<size_t>{ fUTF8Offset + clusters[i] }
423*c8dee2aaSAndroid Build Coastguard Worker                         : std::vector<size_t>({}),
424*c8dee2aaSAndroid Build Coastguard Worker                 },
425*c8dee2aaSAndroid Build Coastguard Worker                 { fBox.x() + pos[i].fX, fBox.y() + pos[i].fY },
426*c8dee2aaSAndroid Build Coastguard Worker                 advance, ascent,
427*c8dee2aaSAndroid Build Coastguard Worker                 line_index, is_whitespace(fUTF8[clusters[i]])
428*c8dee2aaSAndroid Build Coastguard Worker             });
429*c8dee2aaSAndroid Build Coastguard Worker 
430*c8dee2aaSAndroid Build Coastguard Worker             // Note: we only check the first code point in the cluster for whitespace.
431*c8dee2aaSAndroid Build Coastguard Worker             // It's unclear whether thers's a saner approach.
432*c8dee2aaSAndroid Build Coastguard Worker             fResult.fMissingGlyphCount += (glyphs[i] == kMissingGlyphID);
433*c8dee2aaSAndroid Build Coastguard Worker         }
434*c8dee2aaSAndroid Build Coastguard Worker     }
435*c8dee2aaSAndroid Build Coastguard Worker 
commitConsolidatedRun(const skottie::Shaper::RunRec & run,const SkGlyphID * glyphs,const SkPoint * pos,const uint32_t * clusters,uint32_t)436*c8dee2aaSAndroid Build Coastguard Worker     void commitConsolidatedRun(const skottie::Shaper::RunRec& run,
437*c8dee2aaSAndroid Build Coastguard Worker                                const SkGlyphID* glyphs,
438*c8dee2aaSAndroid Build Coastguard Worker                                const SkPoint* pos,
439*c8dee2aaSAndroid Build Coastguard Worker                                const uint32_t* clusters,
440*c8dee2aaSAndroid Build Coastguard Worker                                uint32_t) {
441*c8dee2aaSAndroid Build Coastguard Worker         // In consolidated mode we just accumulate glyphs to a single fragment in ResultBuilder.
442*c8dee2aaSAndroid Build Coastguard Worker         // Glyph positions are baked in the fragment runs (Fragment::fPos only reflects the
443*c8dee2aaSAndroid Build Coastguard Worker         // box origin).
444*c8dee2aaSAndroid Build Coastguard Worker 
445*c8dee2aaSAndroid Build Coastguard Worker         if (fResult.fFragments.empty()) {
446*c8dee2aaSAndroid Build Coastguard Worker             fResult.fFragments.push_back({{{}, {}, {}, {}}, {fBox.x(), fBox.y()}, 0, 0, 0, false});
447*c8dee2aaSAndroid Build Coastguard Worker         }
448*c8dee2aaSAndroid Build Coastguard Worker 
449*c8dee2aaSAndroid Build Coastguard Worker         auto& current_glyphs = fResult.fFragments.back().fGlyphs;
450*c8dee2aaSAndroid Build Coastguard Worker         current_glyphs.fRuns.push_back(run);
451*c8dee2aaSAndroid Build Coastguard Worker         current_glyphs.fGlyphIDs.insert(current_glyphs.fGlyphIDs.end(), glyphs, glyphs + run.fSize);
452*c8dee2aaSAndroid Build Coastguard Worker         current_glyphs.fGlyphPos.insert(current_glyphs.fGlyphPos.end(), pos   , pos    + run.fSize);
453*c8dee2aaSAndroid Build Coastguard Worker 
454*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 0; i < run.fSize; ++i) {
455*c8dee2aaSAndroid Build Coastguard Worker             fResult.fMissingGlyphCount += (glyphs[i] == kMissingGlyphID);
456*c8dee2aaSAndroid Build Coastguard Worker         }
457*c8dee2aaSAndroid Build Coastguard Worker 
458*c8dee2aaSAndroid Build Coastguard Worker         if (fDesc.fFlags & Shaper::kClusters) {
459*c8dee2aaSAndroid Build Coastguard Worker             current_glyphs.fClusters.reserve(current_glyphs.fClusters.size() + run.fSize);
460*c8dee2aaSAndroid Build Coastguard Worker             for (size_t i = 0; i < run.fSize; ++i) {
461*c8dee2aaSAndroid Build Coastguard Worker                 current_glyphs.fClusters.push_back(fUTF8Offset + clusters[i]);
462*c8dee2aaSAndroid Build Coastguard Worker             }
463*c8dee2aaSAndroid Build Coastguard Worker         }
464*c8dee2aaSAndroid Build Coastguard Worker     }
465*c8dee2aaSAndroid Build Coastguard Worker 
HAlignFactor(SkTextUtils::Align align)466*c8dee2aaSAndroid Build Coastguard Worker     static float HAlignFactor(SkTextUtils::Align align) {
467*c8dee2aaSAndroid Build Coastguard Worker         switch (align) {
468*c8dee2aaSAndroid Build Coastguard Worker         case SkTextUtils::kLeft_Align:   return  0.0f;
469*c8dee2aaSAndroid Build Coastguard Worker         case SkTextUtils::kCenter_Align: return -0.5f;
470*c8dee2aaSAndroid Build Coastguard Worker         case SkTextUtils::kRight_Align:  return -1.0f;
471*c8dee2aaSAndroid Build Coastguard Worker         }
472*c8dee2aaSAndroid Build Coastguard Worker         return 0.0f; // go home, msvc...
473*c8dee2aaSAndroid Build Coastguard Worker     }
474*c8dee2aaSAndroid Build Coastguard Worker 
ascent() const475*c8dee2aaSAndroid Build Coastguard Worker     SkScalar ascent() const {
476*c8dee2aaSAndroid Build Coastguard Worker         // Use the explicit ascent, when specified.
477*c8dee2aaSAndroid Build Coastguard Worker         // Note: ascent values are negative (relative to the baseline).
478*c8dee2aaSAndroid Build Coastguard Worker         return fDesc.fAscent ? fDesc.fAscent : fFirstLineAscent;
479*c8dee2aaSAndroid Build Coastguard Worker     }
480*c8dee2aaSAndroid Build Coastguard Worker 
481*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr SkGlyphID kMissingGlyphID = 0;
482*c8dee2aaSAndroid Build Coastguard Worker 
483*c8dee2aaSAndroid Build Coastguard Worker     const Shaper::TextDesc&   fDesc;
484*c8dee2aaSAndroid Build Coastguard Worker     const SkRect&             fBox;
485*c8dee2aaSAndroid Build Coastguard Worker     const float               fHAlignFactor;
486*c8dee2aaSAndroid Build Coastguard Worker 
487*c8dee2aaSAndroid Build Coastguard Worker     SkFont                          fFont;
488*c8dee2aaSAndroid Build Coastguard Worker     const sk_sp<SkFontMgr>          fFontMgr;
489*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkShaper>       fShaper;
490*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkShapers::Factory>       fShapingFactory;
491*c8dee2aaSAndroid Build Coastguard Worker 
492*c8dee2aaSAndroid Build Coastguard Worker     AutoSTMalloc<64, SkGlyphID>          fLineGlyphs;
493*c8dee2aaSAndroid Build Coastguard Worker     AutoSTMalloc<64, SkPoint>            fLinePos;
494*c8dee2aaSAndroid Build Coastguard Worker     AutoSTMalloc<64, uint32_t>           fLineClusters;
495*c8dee2aaSAndroid Build Coastguard Worker     STArray<16, skottie::Shaper::RunRec> fLineRuns;
496*c8dee2aaSAndroid Build Coastguard Worker     size_t                                 fLineGlyphCount = 0;
497*c8dee2aaSAndroid Build Coastguard Worker 
498*c8dee2aaSAndroid Build Coastguard Worker     STArray<64, float, true> fAdvanceBuffer;
499*c8dee2aaSAndroid Build Coastguard Worker 
500*c8dee2aaSAndroid Build Coastguard Worker     SkPoint  fCurrentPosition{ 0, 0 };
501*c8dee2aaSAndroid Build Coastguard Worker     SkPoint  fOffset{ 0, 0 };
502*c8dee2aaSAndroid Build Coastguard Worker     SkVector fPendingLineAdvance{ 0, 0 };
503*c8dee2aaSAndroid Build Coastguard Worker     uint32_t fLineCount = 0;
504*c8dee2aaSAndroid Build Coastguard Worker     float    fFirstLineAscent = 0,
505*c8dee2aaSAndroid Build Coastguard Worker              fLastLineDescent = 0;
506*c8dee2aaSAndroid Build Coastguard Worker 
507*c8dee2aaSAndroid Build Coastguard Worker     const char* fUTF8       = nullptr; // only valid during shapeLine() calls
508*c8dee2aaSAndroid Build Coastguard Worker     size_t      fUTF8Offset = 0;       // current line offset within the original string
509*c8dee2aaSAndroid Build Coastguard Worker 
510*c8dee2aaSAndroid Build Coastguard Worker     Shaper::Result fResult;
511*c8dee2aaSAndroid Build Coastguard Worker };
512*c8dee2aaSAndroid Build Coastguard Worker 
ShapeImpl(const SkString & txt,const Shaper::TextDesc & desc,const SkRect & box,const sk_sp<SkFontMgr> & fontmgr,const sk_sp<SkShapers::Factory> & shapingFactory,SkSize * shaped_size)513*c8dee2aaSAndroid Build Coastguard Worker Shaper::Result ShapeImpl(const SkString& txt, const Shaper::TextDesc& desc,
514*c8dee2aaSAndroid Build Coastguard Worker                          const SkRect& box, const sk_sp<SkFontMgr>& fontmgr,
515*c8dee2aaSAndroid Build Coastguard Worker                          const sk_sp<SkShapers::Factory>& shapingFactory,
516*c8dee2aaSAndroid Build Coastguard Worker                          SkSize* shaped_size) {
517*c8dee2aaSAndroid Build Coastguard Worker     const auto& is_line_break = [](SkUnichar uch) {
518*c8dee2aaSAndroid Build Coastguard Worker         // TODO: other explicit breaks?
519*c8dee2aaSAndroid Build Coastguard Worker         return uch == '\r';
520*c8dee2aaSAndroid Build Coastguard Worker     };
521*c8dee2aaSAndroid Build Coastguard Worker 
522*c8dee2aaSAndroid Build Coastguard Worker     const char* ptr        = txt.c_str();
523*c8dee2aaSAndroid Build Coastguard Worker     const char* line_start = ptr;
524*c8dee2aaSAndroid Build Coastguard Worker     const char* begin      = ptr;
525*c8dee2aaSAndroid Build Coastguard Worker     const char* end        = ptr + txt.size();
526*c8dee2aaSAndroid Build Coastguard Worker 
527*c8dee2aaSAndroid Build Coastguard Worker     ResultBuilder rbuilder(desc, box, fontmgr, shapingFactory);
528*c8dee2aaSAndroid Build Coastguard Worker     while (ptr < end) {
529*c8dee2aaSAndroid Build Coastguard Worker         if (is_line_break(SkUTF::NextUTF8(&ptr, end))) {
530*c8dee2aaSAndroid Build Coastguard Worker             rbuilder.shapeLine(line_start, ptr - 1, SkToSizeT(line_start - begin));
531*c8dee2aaSAndroid Build Coastguard Worker             line_start = ptr;
532*c8dee2aaSAndroid Build Coastguard Worker         }
533*c8dee2aaSAndroid Build Coastguard Worker     }
534*c8dee2aaSAndroid Build Coastguard Worker     rbuilder.shapeLine(line_start, ptr, SkToSizeT(line_start - begin));
535*c8dee2aaSAndroid Build Coastguard Worker 
536*c8dee2aaSAndroid Build Coastguard Worker     return rbuilder.finalize(shaped_size);
537*c8dee2aaSAndroid Build Coastguard Worker }
538*c8dee2aaSAndroid Build Coastguard Worker 
result_fits(const Shaper::Result & res,const SkSize & res_size,const SkRect & box,const Shaper::TextDesc & desc)539*c8dee2aaSAndroid Build Coastguard Worker bool result_fits(const Shaper::Result& res, const SkSize& res_size,
540*c8dee2aaSAndroid Build Coastguard Worker                  const SkRect& box, const Shaper::TextDesc& desc) {
541*c8dee2aaSAndroid Build Coastguard Worker     // optional max line count constraint
542*c8dee2aaSAndroid Build Coastguard Worker     if (desc.fMaxLines) {
543*c8dee2aaSAndroid Build Coastguard Worker         const auto line_count = res.fFragments.empty()
544*c8dee2aaSAndroid Build Coastguard Worker                 ? 0
545*c8dee2aaSAndroid Build Coastguard Worker                 : res.fFragments.back().fLineIndex + 1;
546*c8dee2aaSAndroid Build Coastguard Worker         if (line_count > desc.fMaxLines) {
547*c8dee2aaSAndroid Build Coastguard Worker             return false;
548*c8dee2aaSAndroid Build Coastguard Worker         }
549*c8dee2aaSAndroid Build Coastguard Worker     }
550*c8dee2aaSAndroid Build Coastguard Worker 
551*c8dee2aaSAndroid Build Coastguard Worker     // geometric constraint
552*c8dee2aaSAndroid Build Coastguard Worker     return res_size.width() <= box.width() && res_size.height() <= box.height();
553*c8dee2aaSAndroid Build Coastguard Worker }
554*c8dee2aaSAndroid Build Coastguard Worker 
ShapeToFit(const SkString & txt,const Shaper::TextDesc & orig_desc,const SkRect & box,const sk_sp<SkFontMgr> & fontmgr,const sk_sp<SkShapers::Factory> & shapingFactory)555*c8dee2aaSAndroid Build Coastguard Worker Shaper::Result ShapeToFit(const SkString& txt, const Shaper::TextDesc& orig_desc,
556*c8dee2aaSAndroid Build Coastguard Worker                           const SkRect& box, const sk_sp<SkFontMgr>& fontmgr,
557*c8dee2aaSAndroid Build Coastguard Worker                           const sk_sp<SkShapers::Factory>& shapingFactory) {
558*c8dee2aaSAndroid Build Coastguard Worker     Shaper::Result best_result;
559*c8dee2aaSAndroid Build Coastguard Worker 
560*c8dee2aaSAndroid Build Coastguard Worker     if (box.isEmpty() || orig_desc.fTextSize <= 0) {
561*c8dee2aaSAndroid Build Coastguard Worker         return best_result;
562*c8dee2aaSAndroid Build Coastguard Worker     }
563*c8dee2aaSAndroid Build Coastguard Worker 
564*c8dee2aaSAndroid Build Coastguard Worker     auto desc = orig_desc;
565*c8dee2aaSAndroid Build Coastguard Worker 
566*c8dee2aaSAndroid Build Coastguard Worker     const auto min_scale = std::max(desc.fMinTextSize / desc.fTextSize, 0.0f),
567*c8dee2aaSAndroid Build Coastguard Worker                max_scale = std::max(desc.fMaxTextSize / desc.fTextSize, min_scale);
568*c8dee2aaSAndroid Build Coastguard Worker 
569*c8dee2aaSAndroid Build Coastguard Worker     float in_scale = min_scale,                          // maximum scale that fits inside
570*c8dee2aaSAndroid Build Coastguard Worker          out_scale = max_scale,                          // minimum scale that doesn't fit
571*c8dee2aaSAndroid Build Coastguard Worker          try_scale = SkTPin(1.0f, min_scale, max_scale); // current probe
572*c8dee2aaSAndroid Build Coastguard Worker 
573*c8dee2aaSAndroid Build Coastguard Worker     // Perform a binary search for the best vertical fit (SkShaper already handles
574*c8dee2aaSAndroid Build Coastguard Worker     // horizontal fitting), starting with the specified text size.
575*c8dee2aaSAndroid Build Coastguard Worker     //
576*c8dee2aaSAndroid Build Coastguard Worker     // This hybrid loop handles both the binary search (when in/out extremes are known), and an
577*c8dee2aaSAndroid Build Coastguard Worker     // exponential search for the extremes.
578*c8dee2aaSAndroid Build Coastguard Worker     static constexpr size_t kMaxIter = 16;
579*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < kMaxIter; ++i) {
580*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(try_scale >= in_scale && try_scale <= out_scale);
581*c8dee2aaSAndroid Build Coastguard Worker         desc.fTextSize   = try_scale * orig_desc.fTextSize;
582*c8dee2aaSAndroid Build Coastguard Worker         desc.fLineHeight = try_scale * orig_desc.fLineHeight;
583*c8dee2aaSAndroid Build Coastguard Worker         desc.fLineShift  = try_scale * orig_desc.fLineShift;
584*c8dee2aaSAndroid Build Coastguard Worker         desc.fAscent     = try_scale * orig_desc.fAscent;
585*c8dee2aaSAndroid Build Coastguard Worker 
586*c8dee2aaSAndroid Build Coastguard Worker         SkSize res_size = {0, 0};
587*c8dee2aaSAndroid Build Coastguard Worker         auto res = ShapeImpl(txt, desc, box, fontmgr, shapingFactory, &res_size);
588*c8dee2aaSAndroid Build Coastguard Worker 
589*c8dee2aaSAndroid Build Coastguard Worker         const auto prev_scale = try_scale;
590*c8dee2aaSAndroid Build Coastguard Worker         if (!result_fits(res, res_size, box, desc)) {
591*c8dee2aaSAndroid Build Coastguard Worker             out_scale = try_scale;
592*c8dee2aaSAndroid Build Coastguard Worker             try_scale = (in_scale == min_scale)
593*c8dee2aaSAndroid Build Coastguard Worker                     // initial in_scale not found yet - search exponentially
594*c8dee2aaSAndroid Build Coastguard Worker                     ? std::max(min_scale, try_scale * 0.5f)
595*c8dee2aaSAndroid Build Coastguard Worker                     // in_scale found - binary search
596*c8dee2aaSAndroid Build Coastguard Worker                     : (in_scale + out_scale) * 0.5f;
597*c8dee2aaSAndroid Build Coastguard Worker         } else {
598*c8dee2aaSAndroid Build Coastguard Worker             // It fits - so it's a candidate.
599*c8dee2aaSAndroid Build Coastguard Worker             best_result = std::move(res);
600*c8dee2aaSAndroid Build Coastguard Worker             best_result.fScale = try_scale;
601*c8dee2aaSAndroid Build Coastguard Worker 
602*c8dee2aaSAndroid Build Coastguard Worker             in_scale = try_scale;
603*c8dee2aaSAndroid Build Coastguard Worker             try_scale = (out_scale == max_scale)
604*c8dee2aaSAndroid Build Coastguard Worker                     // initial out_scale not found yet - search exponentially
605*c8dee2aaSAndroid Build Coastguard Worker                     ? std::min(max_scale, try_scale * 2)
606*c8dee2aaSAndroid Build Coastguard Worker                     // out_scale found - binary search
607*c8dee2aaSAndroid Build Coastguard Worker                     : (in_scale + out_scale) * 0.5f;
608*c8dee2aaSAndroid Build Coastguard Worker         }
609*c8dee2aaSAndroid Build Coastguard Worker 
610*c8dee2aaSAndroid Build Coastguard Worker         if (try_scale == prev_scale) {
611*c8dee2aaSAndroid Build Coastguard Worker             // no more progress
612*c8dee2aaSAndroid Build Coastguard Worker             break;
613*c8dee2aaSAndroid Build Coastguard Worker         }
614*c8dee2aaSAndroid Build Coastguard Worker     }
615*c8dee2aaSAndroid Build Coastguard Worker 
616*c8dee2aaSAndroid Build Coastguard Worker     return best_result;
617*c8dee2aaSAndroid Build Coastguard Worker }
618*c8dee2aaSAndroid Build Coastguard Worker 
619*c8dee2aaSAndroid Build Coastguard Worker 
620*c8dee2aaSAndroid Build Coastguard Worker // Applies capitalization rules.
621*c8dee2aaSAndroid Build Coastguard Worker class AdjustedText {
622*c8dee2aaSAndroid Build Coastguard Worker public:
AdjustedText(const SkString & txt,const Shaper::TextDesc & desc,SkUnicode * unicode)623*c8dee2aaSAndroid Build Coastguard Worker     AdjustedText(const SkString& txt, const Shaper::TextDesc& desc, SkUnicode* unicode)
624*c8dee2aaSAndroid Build Coastguard Worker         : fText(txt) {
625*c8dee2aaSAndroid Build Coastguard Worker         switch (desc.fCapitalization) {
626*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::Capitalization::kNone:
627*c8dee2aaSAndroid Build Coastguard Worker             break;
628*c8dee2aaSAndroid Build Coastguard Worker         case Shaper::Capitalization::kUpperCase:
629*c8dee2aaSAndroid Build Coastguard Worker             if (unicode) {
630*c8dee2aaSAndroid Build Coastguard Worker                 *fText.writable() = unicode->toUpper(*fText);
631*c8dee2aaSAndroid Build Coastguard Worker             }
632*c8dee2aaSAndroid Build Coastguard Worker             break;
633*c8dee2aaSAndroid Build Coastguard Worker         }
634*c8dee2aaSAndroid Build Coastguard Worker     }
635*c8dee2aaSAndroid Build Coastguard Worker 
operator const SkString&() const636*c8dee2aaSAndroid Build Coastguard Worker     operator const SkString&() const { return *fText; }
637*c8dee2aaSAndroid Build Coastguard Worker 
638*c8dee2aaSAndroid Build Coastguard Worker private:
639*c8dee2aaSAndroid Build Coastguard Worker     SkTCopyOnFirstWrite<SkString> fText;
640*c8dee2aaSAndroid Build Coastguard Worker };
641*c8dee2aaSAndroid Build Coastguard Worker 
642*c8dee2aaSAndroid Build Coastguard Worker } // namespace
643*c8dee2aaSAndroid Build Coastguard Worker 
Shape(const SkString & text,const TextDesc & desc,const SkPoint & point,const sk_sp<SkFontMgr> & fontmgr,const sk_sp<SkShapers::Factory> & shapingFactory)644*c8dee2aaSAndroid Build Coastguard Worker Shaper::Result Shaper::Shape(const SkString& text, const TextDesc& desc, const SkPoint& point,
645*c8dee2aaSAndroid Build Coastguard Worker                              const sk_sp<SkFontMgr>& fontmgr, const sk_sp<SkShapers::Factory>& shapingFactory) {
646*c8dee2aaSAndroid Build Coastguard Worker     const AdjustedText adjText(text, desc, shapingFactory->getUnicode());
647*c8dee2aaSAndroid Build Coastguard Worker 
648*c8dee2aaSAndroid Build Coastguard Worker     return (desc.fResize == ResizePolicy::kScaleToFit ||
649*c8dee2aaSAndroid Build Coastguard Worker             desc.fResize == ResizePolicy::kDownscaleToFit) // makes no sense in point mode
650*c8dee2aaSAndroid Build Coastguard Worker             ? Result()
651*c8dee2aaSAndroid Build Coastguard Worker             : ShapeImpl(adjText, desc, SkRect::MakeEmpty().makeOffset(point.x(), point.y()),
652*c8dee2aaSAndroid Build Coastguard Worker                         fontmgr, shapingFactory, nullptr);
653*c8dee2aaSAndroid Build Coastguard Worker }
654*c8dee2aaSAndroid Build Coastguard Worker 
Shape(const SkString & text,const TextDesc & desc,const SkRect & box,const sk_sp<SkFontMgr> & fontmgr,const sk_sp<SkShapers::Factory> & shapingFactory)655*c8dee2aaSAndroid Build Coastguard Worker Shaper::Result Shaper::Shape(const SkString& text, const TextDesc& desc, const SkRect& box,
656*c8dee2aaSAndroid Build Coastguard Worker                              const sk_sp<SkFontMgr>& fontmgr, const sk_sp<SkShapers::Factory>& shapingFactory) {
657*c8dee2aaSAndroid Build Coastguard Worker     const AdjustedText adjText(text, desc, shapingFactory->getUnicode());
658*c8dee2aaSAndroid Build Coastguard Worker 
659*c8dee2aaSAndroid Build Coastguard Worker     switch(desc.fResize) {
660*c8dee2aaSAndroid Build Coastguard Worker     case ResizePolicy::kNone:
661*c8dee2aaSAndroid Build Coastguard Worker         return ShapeImpl(adjText, desc, box, fontmgr, shapingFactory, nullptr);
662*c8dee2aaSAndroid Build Coastguard Worker     case ResizePolicy::kScaleToFit:
663*c8dee2aaSAndroid Build Coastguard Worker         return ShapeToFit(adjText, desc, box, fontmgr, shapingFactory);
664*c8dee2aaSAndroid Build Coastguard Worker     case ResizePolicy::kDownscaleToFit: {
665*c8dee2aaSAndroid Build Coastguard Worker         SkSize size;
666*c8dee2aaSAndroid Build Coastguard Worker         auto result = ShapeImpl(adjText, desc, box, fontmgr, shapingFactory, &size);
667*c8dee2aaSAndroid Build Coastguard Worker 
668*c8dee2aaSAndroid Build Coastguard Worker         return result_fits(result, size, box, desc)
669*c8dee2aaSAndroid Build Coastguard Worker                 ? result
670*c8dee2aaSAndroid Build Coastguard Worker                 : ShapeToFit(adjText, desc, box, fontmgr, shapingFactory);
671*c8dee2aaSAndroid Build Coastguard Worker     }
672*c8dee2aaSAndroid Build Coastguard Worker     }
673*c8dee2aaSAndroid Build Coastguard Worker 
674*c8dee2aaSAndroid Build Coastguard Worker     SkUNREACHABLE;
675*c8dee2aaSAndroid Build Coastguard Worker }
676*c8dee2aaSAndroid Build Coastguard Worker 
computeBounds(BoundsType btype) const677*c8dee2aaSAndroid Build Coastguard Worker SkRect Shaper::ShapedGlyphs::computeBounds(BoundsType btype) const {
678*c8dee2aaSAndroid Build Coastguard Worker     auto bounds = SkRect::MakeEmpty();
679*c8dee2aaSAndroid Build Coastguard Worker 
680*c8dee2aaSAndroid Build Coastguard Worker     AutoSTArray<16, SkRect> glyphBounds;
681*c8dee2aaSAndroid Build Coastguard Worker 
682*c8dee2aaSAndroid Build Coastguard Worker     size_t offset = 0;
683*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& run : fRuns) {
684*c8dee2aaSAndroid Build Coastguard Worker         SkRect font_bounds;
685*c8dee2aaSAndroid Build Coastguard Worker         if (btype == BoundsType::kConservative) {
686*c8dee2aaSAndroid Build Coastguard Worker             font_bounds = SkFontPriv::GetFontBounds(run.fFont);
687*c8dee2aaSAndroid Build Coastguard Worker 
688*c8dee2aaSAndroid Build Coastguard Worker             // Empty font bounds is likely a font bug -- fall back to tight bounds.
689*c8dee2aaSAndroid Build Coastguard Worker             if (font_bounds.isEmpty()) {
690*c8dee2aaSAndroid Build Coastguard Worker                 btype = BoundsType::kTight;
691*c8dee2aaSAndroid Build Coastguard Worker             }
692*c8dee2aaSAndroid Build Coastguard Worker         }
693*c8dee2aaSAndroid Build Coastguard Worker 
694*c8dee2aaSAndroid Build Coastguard Worker         switch (btype) {
695*c8dee2aaSAndroid Build Coastguard Worker         case BoundsType::kConservative: {
696*c8dee2aaSAndroid Build Coastguard Worker             SkRect run_bounds;
697*c8dee2aaSAndroid Build Coastguard Worker             run_bounds.setBounds(fGlyphPos.data() + offset, SkToInt(run.fSize));
698*c8dee2aaSAndroid Build Coastguard Worker             run_bounds.fLeft   += font_bounds.left();
699*c8dee2aaSAndroid Build Coastguard Worker             run_bounds.fTop    += font_bounds.top();
700*c8dee2aaSAndroid Build Coastguard Worker             run_bounds.fRight  += font_bounds.right();
701*c8dee2aaSAndroid Build Coastguard Worker             run_bounds.fBottom += font_bounds.bottom();
702*c8dee2aaSAndroid Build Coastguard Worker 
703*c8dee2aaSAndroid Build Coastguard Worker             bounds.join(run_bounds);
704*c8dee2aaSAndroid Build Coastguard Worker         } break;
705*c8dee2aaSAndroid Build Coastguard Worker         case BoundsType::kTight: {
706*c8dee2aaSAndroid Build Coastguard Worker             glyphBounds.reset(SkToInt(run.fSize));
707*c8dee2aaSAndroid Build Coastguard Worker             run.fFont.getBounds(fGlyphIDs.data() + offset,
708*c8dee2aaSAndroid Build Coastguard Worker                                 SkToInt(run.fSize), glyphBounds.data(), nullptr);
709*c8dee2aaSAndroid Build Coastguard Worker 
710*c8dee2aaSAndroid Build Coastguard Worker             for (size_t i = 0; i < run.fSize; ++i) {
711*c8dee2aaSAndroid Build Coastguard Worker                 bounds.join(glyphBounds[SkToInt(i)].makeOffset(fGlyphPos[offset + i]));
712*c8dee2aaSAndroid Build Coastguard Worker             }
713*c8dee2aaSAndroid Build Coastguard Worker         } break;
714*c8dee2aaSAndroid Build Coastguard Worker         }
715*c8dee2aaSAndroid Build Coastguard Worker 
716*c8dee2aaSAndroid Build Coastguard Worker         offset += run.fSize;
717*c8dee2aaSAndroid Build Coastguard Worker     }
718*c8dee2aaSAndroid Build Coastguard Worker 
719*c8dee2aaSAndroid Build Coastguard Worker     return bounds;
720*c8dee2aaSAndroid Build Coastguard Worker }
721*c8dee2aaSAndroid Build Coastguard Worker 
draw(SkCanvas * canvas,const SkPoint & origin,const SkPaint & paint) const722*c8dee2aaSAndroid Build Coastguard Worker void Shaper::ShapedGlyphs::draw(SkCanvas* canvas,
723*c8dee2aaSAndroid Build Coastguard Worker                                 const SkPoint& origin,
724*c8dee2aaSAndroid Build Coastguard Worker                                 const SkPaint& paint) const {
725*c8dee2aaSAndroid Build Coastguard Worker     size_t offset = 0;
726*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& run : fRuns) {
727*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawGlyphs(SkToInt(run.fSize),
728*c8dee2aaSAndroid Build Coastguard Worker                            fGlyphIDs.data() + offset,
729*c8dee2aaSAndroid Build Coastguard Worker                            fGlyphPos.data() + offset,
730*c8dee2aaSAndroid Build Coastguard Worker                            origin,
731*c8dee2aaSAndroid Build Coastguard Worker                            run.fFont,
732*c8dee2aaSAndroid Build Coastguard Worker                            paint);
733*c8dee2aaSAndroid Build Coastguard Worker         offset += run.fSize;
734*c8dee2aaSAndroid Build Coastguard Worker     }
735*c8dee2aaSAndroid Build Coastguard Worker }
736*c8dee2aaSAndroid Build Coastguard Worker 
computeVisualBounds() const737*c8dee2aaSAndroid Build Coastguard Worker SkRect Shaper::Result::computeVisualBounds() const {
738*c8dee2aaSAndroid Build Coastguard Worker     auto bounds = SkRect::MakeEmpty();
739*c8dee2aaSAndroid Build Coastguard Worker 
740*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& frag: fFragments) {
741*c8dee2aaSAndroid Build Coastguard Worker         bounds.join(frag.fGlyphs.computeBounds(ShapedGlyphs::BoundsType::kTight)
742*c8dee2aaSAndroid Build Coastguard Worker                                 .makeOffset(frag.fOrigin));
743*c8dee2aaSAndroid Build Coastguard Worker     }
744*c8dee2aaSAndroid Build Coastguard Worker 
745*c8dee2aaSAndroid Build Coastguard Worker     return bounds;
746*c8dee2aaSAndroid Build Coastguard Worker }
747*c8dee2aaSAndroid Build Coastguard Worker 
748*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SK_DISABLE_LEGACY_SHAPER_FACTORY)
Shape(const SkString & text,const TextDesc & desc,const SkPoint & point,const sk_sp<SkFontMgr> & fontmgr)749*c8dee2aaSAndroid Build Coastguard Worker Shaper::Result Shaper::Shape(const SkString& text, const TextDesc& desc, const SkPoint& point,
750*c8dee2aaSAndroid Build Coastguard Worker              const sk_sp<SkFontMgr>& fontmgr) {
751*c8dee2aaSAndroid Build Coastguard Worker     return Shaper::Shape(text, desc, point, fontmgr, SkShapers::BestAvailable());
752*c8dee2aaSAndroid Build Coastguard Worker }
753*c8dee2aaSAndroid Build Coastguard Worker 
Shape(const SkString & text,const TextDesc & desc,const SkRect & box,const sk_sp<SkFontMgr> & fontmgr)754*c8dee2aaSAndroid Build Coastguard Worker Shaper::Result Shaper::Shape(const SkString& text, const TextDesc& desc, const SkRect& box,
755*c8dee2aaSAndroid Build Coastguard Worker              const sk_sp<SkFontMgr>& fontmgr) {
756*c8dee2aaSAndroid Build Coastguard Worker     return Shaper::Shape(text, desc, box, fontmgr, SkShapers::BestAvailable());
757*c8dee2aaSAndroid Build Coastguard Worker }
758*c8dee2aaSAndroid Build Coastguard Worker 
759*c8dee2aaSAndroid Build Coastguard Worker #endif
760*c8dee2aaSAndroid Build Coastguard Worker 
761*c8dee2aaSAndroid Build Coastguard Worker } // namespace skottie
762