xref: /aosp_15_r20/external/skia/modules/skplaintexteditor/src/shape.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker // Copyright 2019 Google LLC.
2*c8dee2aaSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3*c8dee2aaSAndroid Build Coastguard Worker 
4*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skplaintexteditor/src/shape.h"
5*c8dee2aaSAndroid Build Coastguard Worker 
6*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFont.h"
7*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontMetrics.h"
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontMgr.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTextBlob.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTFitsIn.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skplaintexteditor/src/word_boundaries.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/include/SkShaper.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUTF.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTextBlobPriv.h"
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
22*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/include/SkShaper_harfbuzz.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skshaper/include/SkShaper_skunicode.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skunicode/include/SkUnicode.h"
25*c8dee2aaSAndroid Build Coastguard Worker #else
26*c8dee2aaSAndroid Build Coastguard Worker class SkUnicode;
27*c8dee2aaSAndroid Build Coastguard Worker #endif
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_UNICODE_ICU_IMPLEMENTATION)
30*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skunicode/include/SkUnicode_icu.h"
31*c8dee2aaSAndroid Build Coastguard Worker #endif
32*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION)
33*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skunicode/include/SkUnicode_libgrapheme.h"
34*c8dee2aaSAndroid Build Coastguard Worker #endif
35*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_UNICODE_ICU4X_IMPLEMENTATION)
36*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skunicode/include/SkUnicode_icu4x.h"
37*c8dee2aaSAndroid Build Coastguard Worker #endif
38*c8dee2aaSAndroid Build Coastguard Worker 
39*c8dee2aaSAndroid Build Coastguard Worker #include <cfloat>
40*c8dee2aaSAndroid Build Coastguard Worker #include <climits>
41*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
42*c8dee2aaSAndroid Build Coastguard Worker 
43*c8dee2aaSAndroid Build Coastguard Worker using namespace SkPlainTextEditor;
44*c8dee2aaSAndroid Build Coastguard Worker 
45*c8dee2aaSAndroid Build Coastguard Worker namespace {
46*c8dee2aaSAndroid Build Coastguard Worker class RunHandler final : public SkShaper::RunHandler {
47*c8dee2aaSAndroid Build Coastguard Worker public:
RunHandler(const char * utf8Text,size_t)48*c8dee2aaSAndroid Build Coastguard Worker     RunHandler(const char* utf8Text, size_t) : fUtf8Text(utf8Text) {}
49*c8dee2aaSAndroid Build Coastguard Worker     using RunCallback = void (*)(void* context,
50*c8dee2aaSAndroid Build Coastguard Worker                                  const char* utf8Text,
51*c8dee2aaSAndroid Build Coastguard Worker                                   size_t utf8TextBytes,
52*c8dee2aaSAndroid Build Coastguard Worker                                   size_t glyphCount,
53*c8dee2aaSAndroid Build Coastguard Worker                                   const SkGlyphID* glyphs,
54*c8dee2aaSAndroid Build Coastguard Worker                                   const SkPoint* positions,
55*c8dee2aaSAndroid Build Coastguard Worker                                   const uint32_t* clusters,
56*c8dee2aaSAndroid Build Coastguard Worker                                   const SkFont& font);
setRunCallback(RunCallback f,void * context)57*c8dee2aaSAndroid Build Coastguard Worker     void setRunCallback(RunCallback f, void* context) {
58*c8dee2aaSAndroid Build Coastguard Worker         fCallbackContext = context;
59*c8dee2aaSAndroid Build Coastguard Worker         fCallbackFunction = f;
60*c8dee2aaSAndroid Build Coastguard Worker     }
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkTextBlob> makeBlob();
endPoint() const63*c8dee2aaSAndroid Build Coastguard Worker     SkPoint endPoint() const { return fOffset; }
finalPosition() const64*c8dee2aaSAndroid Build Coastguard Worker     SkPoint finalPosition() const { return fCurrentPosition; }
65*c8dee2aaSAndroid Build Coastguard Worker 
66*c8dee2aaSAndroid Build Coastguard Worker     void beginLine() override;
67*c8dee2aaSAndroid Build Coastguard Worker     void runInfo(const RunInfo&) override;
68*c8dee2aaSAndroid Build Coastguard Worker     void commitRunInfo() override;
69*c8dee2aaSAndroid Build Coastguard Worker     SkShaper::RunHandler::Buffer runBuffer(const RunInfo&) override;
70*c8dee2aaSAndroid Build Coastguard Worker     void commitRunBuffer(const RunInfo&) override;
71*c8dee2aaSAndroid Build Coastguard Worker     void commitLine() override;
72*c8dee2aaSAndroid Build Coastguard Worker 
lineEndOffsets() const73*c8dee2aaSAndroid Build Coastguard Worker     const std::vector<size_t>& lineEndOffsets() const { return fLineEndOffsets; }
74*c8dee2aaSAndroid Build Coastguard Worker 
finalRect(const SkFont & font) const75*c8dee2aaSAndroid Build Coastguard Worker     SkRect finalRect(const SkFont& font) const {
76*c8dee2aaSAndroid Build Coastguard Worker         if (0 == fMaxRunAscent || 0 == fMaxRunDescent) {
77*c8dee2aaSAndroid Build Coastguard Worker             SkFontMetrics metrics;
78*c8dee2aaSAndroid Build Coastguard Worker             font.getMetrics(&metrics);
79*c8dee2aaSAndroid Build Coastguard Worker             return {fCurrentPosition.x(),
80*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentPosition.y(),
81*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentPosition.x() + font.getSize(),
82*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentPosition.y() + metrics.fDescent - metrics.fAscent};
83*c8dee2aaSAndroid Build Coastguard Worker         } else {
84*c8dee2aaSAndroid Build Coastguard Worker             return {fCurrentPosition.x(),
85*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentPosition.y() + fMaxRunAscent,
86*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentPosition.x() + font.getSize(),
87*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentPosition.y() + fMaxRunDescent};
88*c8dee2aaSAndroid Build Coastguard Worker         }
89*c8dee2aaSAndroid Build Coastguard Worker     }
90*c8dee2aaSAndroid Build Coastguard Worker 
91*c8dee2aaSAndroid Build Coastguard Worker 
92*c8dee2aaSAndroid Build Coastguard Worker private:
93*c8dee2aaSAndroid Build Coastguard Worker     SkTextBlobBuilder fBuilder;
94*c8dee2aaSAndroid Build Coastguard Worker     std::vector<size_t> fLineEndOffsets;
95*c8dee2aaSAndroid Build Coastguard Worker     const SkGlyphID* fCurrentGlyphs = nullptr;
96*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint* fCurrentPoints = nullptr;
97*c8dee2aaSAndroid Build Coastguard Worker     void* fCallbackContext = nullptr;
98*c8dee2aaSAndroid Build Coastguard Worker     RunCallback fCallbackFunction = nullptr;
99*c8dee2aaSAndroid Build Coastguard Worker     char const * const fUtf8Text;
100*c8dee2aaSAndroid Build Coastguard Worker     size_t fTextOffset = 0;
101*c8dee2aaSAndroid Build Coastguard Worker     uint32_t* fClusters = nullptr;
102*c8dee2aaSAndroid Build Coastguard Worker     int fClusterOffset = 0;
103*c8dee2aaSAndroid Build Coastguard Worker     int fGlyphCount = 0;
104*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fMaxRunAscent = 0;
105*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fMaxRunDescent = 0;
106*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fMaxRunLeading = 0;
107*c8dee2aaSAndroid Build Coastguard Worker     SkPoint fCurrentPosition = {0, 0};
108*c8dee2aaSAndroid Build Coastguard Worker     SkPoint fOffset = {0, 0};
109*c8dee2aaSAndroid Build Coastguard Worker };
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker // TODO(kjlubick,jlavrova) Remove these defines by having clients register something or somehow
112*c8dee2aaSAndroid Build Coastguard Worker // plumbing this all into the animation builder factories.
113*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
get_unicode()114*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkUnicode> get_unicode() {
115*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_UNICODE_ICU_IMPLEMENTATION)
116*c8dee2aaSAndroid Build Coastguard Worker     if (auto unicode = SkUnicodes::ICU::Make()) {
117*c8dee2aaSAndroid Build Coastguard Worker         return unicode;
118*c8dee2aaSAndroid Build Coastguard Worker     }
119*c8dee2aaSAndroid Build Coastguard Worker #endif  // defined(SK_UNICODE_ICU_IMPLEMENTATION)
120*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION)
121*c8dee2aaSAndroid Build Coastguard Worker     if (auto unicode = SkUnicodes::Libgrapheme::Make()) {
122*c8dee2aaSAndroid Build Coastguard Worker         return unicode;
123*c8dee2aaSAndroid Build Coastguard Worker     }
124*c8dee2aaSAndroid Build Coastguard Worker #endif
125*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_UNICODE_ICU4X_IMPLEMENTATION)
126*c8dee2aaSAndroid Build Coastguard Worker     if (auto unicode = SkUnicodes::ICU4X::Make()) {
127*c8dee2aaSAndroid Build Coastguard Worker         return unicode;
128*c8dee2aaSAndroid Build Coastguard Worker     }
129*c8dee2aaSAndroid Build Coastguard Worker #endif
130*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
131*c8dee2aaSAndroid Build Coastguard Worker }
132*c8dee2aaSAndroid Build Coastguard Worker #endif
133*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
134*c8dee2aaSAndroid Build Coastguard Worker 
beginLine()135*c8dee2aaSAndroid Build Coastguard Worker void RunHandler::beginLine() {
136*c8dee2aaSAndroid Build Coastguard Worker     fCurrentPosition = fOffset;
137*c8dee2aaSAndroid Build Coastguard Worker     fMaxRunAscent = 0;
138*c8dee2aaSAndroid Build Coastguard Worker     fMaxRunDescent = 0;
139*c8dee2aaSAndroid Build Coastguard Worker     fMaxRunLeading = 0;
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker 
runInfo(const SkShaper::RunHandler::RunInfo & info)142*c8dee2aaSAndroid Build Coastguard Worker void RunHandler::runInfo(const SkShaper::RunHandler::RunInfo& info) {
143*c8dee2aaSAndroid Build Coastguard Worker     SkFontMetrics metrics;
144*c8dee2aaSAndroid Build Coastguard Worker     info.fFont.getMetrics(&metrics);
145*c8dee2aaSAndroid Build Coastguard Worker     fMaxRunAscent = std::min(fMaxRunAscent, metrics.fAscent);
146*c8dee2aaSAndroid Build Coastguard Worker     fMaxRunDescent = std::max(fMaxRunDescent, metrics.fDescent);
147*c8dee2aaSAndroid Build Coastguard Worker     fMaxRunLeading = std::max(fMaxRunLeading, metrics.fLeading);
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker 
commitRunInfo()150*c8dee2aaSAndroid Build Coastguard Worker void RunHandler::commitRunInfo() {
151*c8dee2aaSAndroid Build Coastguard Worker     fCurrentPosition.fY -= fMaxRunAscent;
152*c8dee2aaSAndroid Build Coastguard Worker }
153*c8dee2aaSAndroid Build Coastguard Worker 
runBuffer(const RunInfo & info)154*c8dee2aaSAndroid Build Coastguard Worker SkShaper::RunHandler::Buffer RunHandler::runBuffer(const RunInfo& info) {
155*c8dee2aaSAndroid Build Coastguard Worker     int glyphCount = SkTFitsIn<int>(info.glyphCount) ? info.glyphCount : INT_MAX;
156*c8dee2aaSAndroid Build Coastguard Worker     int utf8RangeSize = SkTFitsIn<int>(info.utf8Range.size()) ? info.utf8Range.size() : INT_MAX;
157*c8dee2aaSAndroid Build Coastguard Worker 
158*c8dee2aaSAndroid Build Coastguard Worker     const auto& runBuffer = fBuilder.allocRunTextPos(info.fFont, glyphCount, utf8RangeSize);
159*c8dee2aaSAndroid Build Coastguard Worker     fCurrentGlyphs = runBuffer.glyphs;
160*c8dee2aaSAndroid Build Coastguard Worker     fCurrentPoints = runBuffer.points();
161*c8dee2aaSAndroid Build Coastguard Worker 
162*c8dee2aaSAndroid Build Coastguard Worker     if (runBuffer.utf8text && fUtf8Text) {
163*c8dee2aaSAndroid Build Coastguard Worker         memcpy(runBuffer.utf8text, fUtf8Text + info.utf8Range.begin(), utf8RangeSize);
164*c8dee2aaSAndroid Build Coastguard Worker     }
165*c8dee2aaSAndroid Build Coastguard Worker     fClusters = runBuffer.clusters;
166*c8dee2aaSAndroid Build Coastguard Worker     fGlyphCount = glyphCount;
167*c8dee2aaSAndroid Build Coastguard Worker     fClusterOffset = info.utf8Range.begin();
168*c8dee2aaSAndroid Build Coastguard Worker 
169*c8dee2aaSAndroid Build Coastguard Worker     return {runBuffer.glyphs,
170*c8dee2aaSAndroid Build Coastguard Worker             runBuffer.points(),
171*c8dee2aaSAndroid Build Coastguard Worker             nullptr,
172*c8dee2aaSAndroid Build Coastguard Worker             runBuffer.clusters,
173*c8dee2aaSAndroid Build Coastguard Worker             fCurrentPosition};
174*c8dee2aaSAndroid Build Coastguard Worker }
175*c8dee2aaSAndroid Build Coastguard Worker 
commitRunBuffer(const RunInfo & info)176*c8dee2aaSAndroid Build Coastguard Worker void RunHandler::commitRunBuffer(const RunInfo& info) {
177*c8dee2aaSAndroid Build Coastguard Worker     // for (size_t i = 0; i < info.glyphCount; ++i) {
178*c8dee2aaSAndroid Build Coastguard Worker     //     SkASSERT(fClusters[i] >= info.utf8Range.begin());
179*c8dee2aaSAndroid Build Coastguard Worker     //     // this fails for khmer example.
180*c8dee2aaSAndroid Build Coastguard Worker     //     SkASSERT(fClusters[i] <  info.utf8Range.end());
181*c8dee2aaSAndroid Build Coastguard Worker     // }
182*c8dee2aaSAndroid Build Coastguard Worker     if (fCallbackFunction) {
183*c8dee2aaSAndroid Build Coastguard Worker         fCallbackFunction(fCallbackContext,
184*c8dee2aaSAndroid Build Coastguard Worker                           fUtf8Text,
185*c8dee2aaSAndroid Build Coastguard Worker                           info.utf8Range.end(),
186*c8dee2aaSAndroid Build Coastguard Worker                           info.glyphCount,
187*c8dee2aaSAndroid Build Coastguard Worker                           fCurrentGlyphs,
188*c8dee2aaSAndroid Build Coastguard Worker                           fCurrentPoints,
189*c8dee2aaSAndroid Build Coastguard Worker                           fClusters,
190*c8dee2aaSAndroid Build Coastguard Worker                           info.fFont);
191*c8dee2aaSAndroid Build Coastguard Worker     }
192*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(0 <= fClusterOffset);
193*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < fGlyphCount; ++i) {
194*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fClusters[i] >= (unsigned)fClusterOffset);
195*c8dee2aaSAndroid Build Coastguard Worker         fClusters[i] -= fClusterOffset;
196*c8dee2aaSAndroid Build Coastguard Worker     }
197*c8dee2aaSAndroid Build Coastguard Worker     fCurrentPosition += info.fAdvance;
198*c8dee2aaSAndroid Build Coastguard Worker     fTextOffset = std::max(fTextOffset, info.utf8Range.end());
199*c8dee2aaSAndroid Build Coastguard Worker }
200*c8dee2aaSAndroid Build Coastguard Worker 
commitLine()201*c8dee2aaSAndroid Build Coastguard Worker void RunHandler::commitLine() {
202*c8dee2aaSAndroid Build Coastguard Worker     if (fLineEndOffsets.empty() || fTextOffset > fLineEndOffsets.back()) {
203*c8dee2aaSAndroid Build Coastguard Worker         // Ensure that fLineEndOffsets is monotonic.
204*c8dee2aaSAndroid Build Coastguard Worker         fLineEndOffsets.push_back(fTextOffset);
205*c8dee2aaSAndroid Build Coastguard Worker     }
206*c8dee2aaSAndroid Build Coastguard Worker     fOffset += { 0, fMaxRunDescent + fMaxRunLeading - fMaxRunAscent };
207*c8dee2aaSAndroid Build Coastguard Worker }
208*c8dee2aaSAndroid Build Coastguard Worker 
makeBlob()209*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkTextBlob> RunHandler::makeBlob() {
210*c8dee2aaSAndroid Build Coastguard Worker     return fBuilder.make();
211*c8dee2aaSAndroid Build Coastguard Worker }
212*c8dee2aaSAndroid Build Coastguard Worker 
selection_box(const SkFontMetrics & metrics,float advance,SkPoint pos)213*c8dee2aaSAndroid Build Coastguard Worker static SkRect selection_box(const SkFontMetrics& metrics,
214*c8dee2aaSAndroid Build Coastguard Worker                             float advance,
215*c8dee2aaSAndroid Build Coastguard Worker                             SkPoint pos) {
216*c8dee2aaSAndroid Build Coastguard Worker     if (fabsf(advance) < 1.0f) {
217*c8dee2aaSAndroid Build Coastguard Worker         advance = copysignf(1.0f, advance);
218*c8dee2aaSAndroid Build Coastguard Worker     }
219*c8dee2aaSAndroid Build Coastguard Worker     return SkRect{pos.x(),
220*c8dee2aaSAndroid Build Coastguard Worker                   pos.y() + metrics.fAscent,
221*c8dee2aaSAndroid Build Coastguard Worker                   pos.x() + advance,
222*c8dee2aaSAndroid Build Coastguard Worker                   pos.y() + metrics.fDescent}.makeSorted();
223*c8dee2aaSAndroid Build Coastguard Worker }
224*c8dee2aaSAndroid Build Coastguard Worker 
set_character_bounds(void * context,const char * utf8Text,size_t utf8TextBytes,size_t glyphCount,const SkGlyphID * glyphs,const SkPoint * positions,const uint32_t * clusters,const SkFont & font)225*c8dee2aaSAndroid Build Coastguard Worker static void set_character_bounds(void* context,
226*c8dee2aaSAndroid Build Coastguard Worker                                  const char* utf8Text,
227*c8dee2aaSAndroid Build Coastguard Worker                                  size_t utf8TextBytes,
228*c8dee2aaSAndroid Build Coastguard Worker                                  size_t glyphCount,
229*c8dee2aaSAndroid Build Coastguard Worker                                  const SkGlyphID* glyphs,
230*c8dee2aaSAndroid Build Coastguard Worker                                  const SkPoint* positions,
231*c8dee2aaSAndroid Build Coastguard Worker                                  const uint32_t* clusters,
232*c8dee2aaSAndroid Build Coastguard Worker                                  const SkFont& font)
233*c8dee2aaSAndroid Build Coastguard Worker {
234*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(context);
235*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(glyphCount > 0);
236*c8dee2aaSAndroid Build Coastguard Worker     SkRect* cursors = (SkRect*)context;
237*c8dee2aaSAndroid Build Coastguard Worker 
238*c8dee2aaSAndroid Build Coastguard Worker     SkFontMetrics metrics;
239*c8dee2aaSAndroid Build Coastguard Worker     font.getMetrics(&metrics);
240*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<float[]> advances(new float[glyphCount]);
241*c8dee2aaSAndroid Build Coastguard Worker     font.getWidths(glyphs, glyphCount, advances.get());
242*c8dee2aaSAndroid Build Coastguard Worker 
243*c8dee2aaSAndroid Build Coastguard Worker     // Loop over each cluster in this run.
244*c8dee2aaSAndroid Build Coastguard Worker     size_t clusterStart = 0;
245*c8dee2aaSAndroid Build Coastguard Worker     for (size_t glyphIndex = 0; glyphIndex < glyphCount; ++glyphIndex) {
246*c8dee2aaSAndroid Build Coastguard Worker         if (glyphIndex + 1 < glyphCount  // more glyphs
247*c8dee2aaSAndroid Build Coastguard Worker             && clusters[glyphIndex] == clusters[glyphIndex + 1]) {
248*c8dee2aaSAndroid Build Coastguard Worker             continue; // multi-glyph cluster
249*c8dee2aaSAndroid Build Coastguard Worker         }
250*c8dee2aaSAndroid Build Coastguard Worker         unsigned textBegin = clusters[glyphIndex];
251*c8dee2aaSAndroid Build Coastguard Worker         unsigned textEnd = utf8TextBytes;
252*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 0; i < glyphCount; ++i) {
253*c8dee2aaSAndroid Build Coastguard Worker             if (clusters[i] >= textEnd) {
254*c8dee2aaSAndroid Build Coastguard Worker                 textEnd = clusters[i] + 1;
255*c8dee2aaSAndroid Build Coastguard Worker             }
256*c8dee2aaSAndroid Build Coastguard Worker         }
257*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 0; i < glyphCount; ++i) {
258*c8dee2aaSAndroid Build Coastguard Worker             if (clusters[i] > textBegin && clusters[i] < textEnd) {
259*c8dee2aaSAndroid Build Coastguard Worker                 textEnd = clusters[i];
260*c8dee2aaSAndroid Build Coastguard Worker                 if (textEnd == textBegin + 1) { break; }
261*c8dee2aaSAndroid Build Coastguard Worker             }
262*c8dee2aaSAndroid Build Coastguard Worker         }
263*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(glyphIndex + 1 > clusterStart);
264*c8dee2aaSAndroid Build Coastguard Worker         unsigned clusterGlyphCount = glyphIndex + 1 - clusterStart;
265*c8dee2aaSAndroid Build Coastguard Worker         const SkPoint* clusterGlyphPositions = &positions[clusterStart];
266*c8dee2aaSAndroid Build Coastguard Worker         const float* clusterAdvances = &advances[clusterStart];
267*c8dee2aaSAndroid Build Coastguard Worker         clusterStart = glyphIndex + 1;  // for next loop
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker         SkRect clusterBox = selection_box(metrics, clusterAdvances[0], clusterGlyphPositions[0]);
270*c8dee2aaSAndroid Build Coastguard Worker         for (unsigned i = 1; i < clusterGlyphCount; ++i) { // multiple glyphs
271*c8dee2aaSAndroid Build Coastguard Worker             clusterBox.join(selection_box(metrics, clusterAdvances[i], clusterGlyphPositions[i]));
272*c8dee2aaSAndroid Build Coastguard Worker         }
273*c8dee2aaSAndroid Build Coastguard Worker         if (textBegin + 1 == textEnd) {  // single byte, fast path.
274*c8dee2aaSAndroid Build Coastguard Worker             cursors[textBegin] = clusterBox;
275*c8dee2aaSAndroid Build Coastguard Worker             continue;
276*c8dee2aaSAndroid Build Coastguard Worker         }
277*c8dee2aaSAndroid Build Coastguard Worker         int textCount = textEnd - textBegin;
278*c8dee2aaSAndroid Build Coastguard Worker         int codePointCount = SkUTF::CountUTF8(utf8Text + textBegin, textCount);
279*c8dee2aaSAndroid Build Coastguard Worker         if (codePointCount == 1) {  // single codepoint, fast path.
280*c8dee2aaSAndroid Build Coastguard Worker             cursors[textBegin] = clusterBox;
281*c8dee2aaSAndroid Build Coastguard Worker             continue;
282*c8dee2aaSAndroid Build Coastguard Worker         }
283*c8dee2aaSAndroid Build Coastguard Worker 
284*c8dee2aaSAndroid Build Coastguard Worker         float width = clusterBox.width() / codePointCount;
285*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(width > 0);
286*c8dee2aaSAndroid Build Coastguard Worker         const char* ptr = utf8Text + textBegin;
287*c8dee2aaSAndroid Build Coastguard Worker         const char* end = utf8Text + textEnd;
288*c8dee2aaSAndroid Build Coastguard Worker         float x = clusterBox.left();
289*c8dee2aaSAndroid Build Coastguard Worker         while (ptr < end) {  // for each codepoint in cluster
290*c8dee2aaSAndroid Build Coastguard Worker             const char* nextPtr = ptr;
291*c8dee2aaSAndroid Build Coastguard Worker             SkUTF::NextUTF8(&nextPtr, end);
292*c8dee2aaSAndroid Build Coastguard Worker             int firstIndex = ptr - utf8Text;
293*c8dee2aaSAndroid Build Coastguard Worker             float nextX = x + width;
294*c8dee2aaSAndroid Build Coastguard Worker             cursors[firstIndex] = SkRect{x, clusterBox.top(), nextX, clusterBox.bottom()};
295*c8dee2aaSAndroid Build Coastguard Worker             x = nextX;
296*c8dee2aaSAndroid Build Coastguard Worker             ptr = nextPtr;
297*c8dee2aaSAndroid Build Coastguard Worker         }
298*c8dee2aaSAndroid Build Coastguard Worker     }
299*c8dee2aaSAndroid Build Coastguard Worker }
300*c8dee2aaSAndroid Build Coastguard Worker 
Shape(const char * utf8Text,size_t textByteLen,const SkFont & font,sk_sp<SkFontMgr> fontMgr,const char * locale,float width)301*c8dee2aaSAndroid Build Coastguard Worker ShapeResult SkPlainTextEditor::Shape(const char* utf8Text,
302*c8dee2aaSAndroid Build Coastguard Worker                                      size_t textByteLen,
303*c8dee2aaSAndroid Build Coastguard Worker                                      const SkFont& font,
304*c8dee2aaSAndroid Build Coastguard Worker                                      sk_sp<SkFontMgr> fontMgr,
305*c8dee2aaSAndroid Build Coastguard Worker                                      const char* locale,
306*c8dee2aaSAndroid Build Coastguard Worker                                      float width)
307*c8dee2aaSAndroid Build Coastguard Worker {
308*c8dee2aaSAndroid Build Coastguard Worker     ShapeResult result;
309*c8dee2aaSAndroid Build Coastguard Worker     if (SkUTF::CountUTF8(utf8Text, textByteLen) < 0) {
310*c8dee2aaSAndroid Build Coastguard Worker         utf8Text = nullptr;
311*c8dee2aaSAndroid Build Coastguard Worker         textByteLen = 0;
312*c8dee2aaSAndroid Build Coastguard Worker     }
313*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkShaper> shaper = nullptr;
314*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
315*c8dee2aaSAndroid Build Coastguard Worker     auto unicode = get_unicode();
316*c8dee2aaSAndroid Build Coastguard Worker     shaper = SkShapers::HB::ShaperDrivenWrapper(unicode, fontMgr);
317*c8dee2aaSAndroid Build Coastguard Worker #else
318*c8dee2aaSAndroid Build Coastguard Worker     shaper = SkShapers::Primitive::PrimitiveText();
319*c8dee2aaSAndroid Build Coastguard Worker #endif
320*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(shaper);
321*c8dee2aaSAndroid Build Coastguard Worker 
322*c8dee2aaSAndroid Build Coastguard Worker     float height = font.getSpacing();
323*c8dee2aaSAndroid Build Coastguard Worker     RunHandler runHandler(utf8Text, textByteLen);
324*c8dee2aaSAndroid Build Coastguard Worker     if (textByteLen) {
325*c8dee2aaSAndroid Build Coastguard Worker         result.glyphBounds.resize(textByteLen);
326*c8dee2aaSAndroid Build Coastguard Worker         for (SkRect& c : result.glyphBounds) {
327*c8dee2aaSAndroid Build Coastguard Worker             c = SkRect{-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX};
328*c8dee2aaSAndroid Build Coastguard Worker         }
329*c8dee2aaSAndroid Build Coastguard Worker         runHandler.setRunCallback(set_character_bounds, result.glyphBounds.data());
330*c8dee2aaSAndroid Build Coastguard Worker 
331*c8dee2aaSAndroid Build Coastguard Worker         static constexpr uint8_t kBidiLevelLTR = 0;
332*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::BiDiRunIterator> bidi = nullptr;
333*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
334*c8dee2aaSAndroid Build Coastguard Worker         bidi = SkShapers::unicode::BidiRunIterator(
335*c8dee2aaSAndroid Build Coastguard Worker                 unicode, utf8Text, textByteLen, kBidiLevelLTR);
336*c8dee2aaSAndroid Build Coastguard Worker #endif
337*c8dee2aaSAndroid Build Coastguard Worker         if (!bidi) {
338*c8dee2aaSAndroid Build Coastguard Worker             bidi = std::make_unique<SkShaper::TrivialBiDiRunIterator>(kBidiLevelLTR, textByteLen);
339*c8dee2aaSAndroid Build Coastguard Worker         }
340*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(bidi);
341*c8dee2aaSAndroid Build Coastguard Worker 
342*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::LanguageRunIterator> language =
343*c8dee2aaSAndroid Build Coastguard Worker                 SkShaper::MakeStdLanguageRunIterator(utf8Text, textByteLen);
344*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(language);
345*c8dee2aaSAndroid Build Coastguard Worker 
346*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::ScriptRunIterator> script =
347*c8dee2aaSAndroid Build Coastguard Worker                 SkShaper::MakeScriptRunIterator(utf8Text, textByteLen, SkSetFourByteTag('Z','z','z','z'));
348*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(script);
349*c8dee2aaSAndroid Build Coastguard Worker 
350*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SkShaper::FontRunIterator> fontRuns =
351*c8dee2aaSAndroid Build Coastguard Worker                 SkShaper::MakeFontMgrRunIterator(utf8Text, textByteLen, font, fontMgr);
352*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fontRuns);
353*c8dee2aaSAndroid Build Coastguard Worker 
354*c8dee2aaSAndroid Build Coastguard Worker         shaper->shape(utf8Text,
355*c8dee2aaSAndroid Build Coastguard Worker                       textByteLen,
356*c8dee2aaSAndroid Build Coastguard Worker                       *fontRuns,
357*c8dee2aaSAndroid Build Coastguard Worker                       *bidi,
358*c8dee2aaSAndroid Build Coastguard Worker                       *script,
359*c8dee2aaSAndroid Build Coastguard Worker                       *language,
360*c8dee2aaSAndroid Build Coastguard Worker                       nullptr,
361*c8dee2aaSAndroid Build Coastguard Worker                       0,
362*c8dee2aaSAndroid Build Coastguard Worker                       width,
363*c8dee2aaSAndroid Build Coastguard Worker                       &runHandler);
364*c8dee2aaSAndroid Build Coastguard Worker         if (runHandler.lineEndOffsets().size() > 1) {
365*c8dee2aaSAndroid Build Coastguard Worker             result.lineBreakOffsets = runHandler.lineEndOffsets();
366*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(result.lineBreakOffsets.size() > 0);
367*c8dee2aaSAndroid Build Coastguard Worker             result.lineBreakOffsets.pop_back();
368*c8dee2aaSAndroid Build Coastguard Worker         }
369*c8dee2aaSAndroid Build Coastguard Worker         height = std::max(height, runHandler.endPoint().y());
370*c8dee2aaSAndroid Build Coastguard Worker         result.blob = runHandler.makeBlob();
371*c8dee2aaSAndroid Build Coastguard Worker     }
372*c8dee2aaSAndroid Build Coastguard Worker     result.glyphBounds.push_back(runHandler.finalRect(font));
373*c8dee2aaSAndroid Build Coastguard Worker     result.verticalAdvance = (int)ceilf(height);
374*c8dee2aaSAndroid Build Coastguard Worker     result.wordBreaks = GetUtf8WordBoundaries(utf8Text, textByteLen, locale);
375*c8dee2aaSAndroid Build Coastguard Worker     return result;
376*c8dee2aaSAndroid Build Coastguard Worker }
377