xref: /aosp_15_r20/external/skia/modules/skshaper/src/SkShaper_primitive.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "include/core/SkFont.h"
8 #include "include/core/SkFontTypes.h"
9 #include "include/core/SkPoint.h"
10 #include "include/core/SkScalar.h"
11 #include "include/core/SkTypes.h"
12 #include "include/private/base/SkTo.h"
13 #include "modules/skshaper/include/SkShaper.h"
14 #include "src/base/SkUTF.h"
15 
16 #if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
17 #include "include/core/SkFontMgr.h"
18 #endif
19 
20 #include <cstdint>
21 #include <cstring>
22 #include <memory>
23 
24 class SkShaperPrimitive : public SkShaper {
25 public:
SkShaperPrimitive()26     SkShaperPrimitive() {}
27 private:
28 #if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
29     void shape(const char* utf8, size_t utf8Bytes,
30                const SkFont& srcFont,
31                bool leftToRight,
32                SkScalar width,
33                RunHandler*) const override;
34 
35     void shape(const char* utf8, size_t utf8Bytes,
36                FontRunIterator&,
37                BiDiRunIterator&,
38                ScriptRunIterator&,
39                LanguageRunIterator&,
40                SkScalar width,
41                RunHandler*) const override;
42 #endif
43 
44     void shape(const char* utf8, size_t utf8Bytes,
45                FontRunIterator&,
46                BiDiRunIterator&,
47                ScriptRunIterator&,
48                LanguageRunIterator&,
49                const Feature*, size_t featureSize,
50                SkScalar width,
51                RunHandler*) const override;
52 };
53 
is_breaking_whitespace(SkUnichar c)54 static inline bool is_breaking_whitespace(SkUnichar c) {
55     switch (c) {
56         case 0x0020: // SPACE
57         //case 0x00A0: // NO-BREAK SPACE
58         case 0x1680: // OGHAM SPACE MARK
59         case 0x180E: // MONGOLIAN VOWEL SEPARATOR
60         case 0x2000: // EN QUAD
61         case 0x2001: // EM QUAD
62         case 0x2002: // EN SPACE (nut)
63         case 0x2003: // EM SPACE (mutton)
64         case 0x2004: // THREE-PER-EM SPACE (thick space)
65         case 0x2005: // FOUR-PER-EM SPACE (mid space)
66         case 0x2006: // SIX-PER-EM SPACE
67         case 0x2007: // FIGURE SPACE
68         case 0x2008: // PUNCTUATION SPACE
69         case 0x2009: // THIN SPACE
70         case 0x200A: // HAIR SPACE
71         case 0x200B: // ZERO WIDTH SPACE
72         case 0x202F: // NARROW NO-BREAK SPACE
73         case 0x205F: // MEDIUM MATHEMATICAL SPACE
74         case 0x3000: // IDEOGRAPHIC SPACE
75         //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
76             return true;
77         default:
78             return false;
79     }
80 }
81 
linebreak(const char text[],const char stop[],const SkFont & font,SkScalar width,SkScalar * advance,size_t * trailing)82 static size_t linebreak(const char text[], const char stop[],
83                         const SkFont& font, SkScalar width,
84                         SkScalar* advance,
85                         size_t* trailing)
86 {
87     SkScalar accumulatedWidth = 0;
88     int glyphIndex = 0;
89     const char* start = text;
90     const char* wordStart = text;
91     bool prevWS = true;
92     *trailing = 0;
93 
94     while (text < stop) {
95         const char* prevText = text;
96         SkUnichar uni = SkUTF::NextUTF8(&text, stop);
97         accumulatedWidth += advance[glyphIndex++];
98         bool currWS = is_breaking_whitespace(uni);
99 
100         if (!currWS && prevWS) {
101             wordStart = prevText;
102         }
103         prevWS = currWS;
104 
105         if (width < accumulatedWidth) {
106             bool consumeWhitespace = false;
107             if (currWS) {
108                 // previous fit, put this and following whitespace in trailing
109                 if (prevText == start) {
110                     // don't put this in trailing if it's the first thing
111                     prevText = text;
112                 }
113                 consumeWhitespace = true;
114             } else if (wordStart != start) {
115                 // backup to the last whitespace that fit
116                 text = wordStart;
117             } else if (prevText > start) {
118                 // backup to just before the glyph that didn't fit
119                 text = prevText;
120             } else {
121                 // let it overflow, put any following whitespace in trailing
122                 prevText = text;
123                 consumeWhitespace = true;
124             }
125             if (consumeWhitespace) {
126                 const char* next = text;
127                 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
128                     text = next;
129                 }
130                 if (trailing) {
131                     *trailing = text - prevText;
132                 }
133             }
134             break;
135         }
136     }
137 
138     return text - start;
139 }
140 
141 #if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & font,BiDiRunIterator & bidi,ScriptRunIterator & script,LanguageRunIterator & lang,SkScalar width,RunHandler * handler) const142 void SkShaperPrimitive::shape(const char* utf8,
143                               size_t utf8Bytes,
144                               FontRunIterator& font,
145                               BiDiRunIterator& bidi,
146                               ScriptRunIterator& script,
147                               LanguageRunIterator& lang,
148                               SkScalar width,
149                               RunHandler* handler) const {
150     return this->shape(utf8, utf8Bytes, font, bidi, script, lang, nullptr, 0, width, handler);
151 }
152 
shape(const char * utf8,size_t utf8Bytes,const SkFont & font,bool leftToRight,SkScalar width,RunHandler * handler) const153 void SkShaperPrimitive::shape(const char* utf8,
154                               size_t utf8Bytes,
155                               const SkFont& font,
156                               bool leftToRight,
157                               SkScalar width,
158                               RunHandler* handler) const {
159     std::unique_ptr<FontRunIterator> fontRuns(
160             MakeFontMgrRunIterator(utf8, utf8Bytes, font, nullptr));
161     if (!fontRuns) {
162         return;
163     }
164     // bidi, script, and lang are all unused so we can construct them with empty data.
165     TrivialBiDiRunIterator bidi{0, 0};
166     TrivialScriptRunIterator script{0, 0};
167     TrivialLanguageRunIterator lang{nullptr, 0};
168     return this->shape(utf8, utf8Bytes, *fontRuns, bidi, script, lang, nullptr, 0, width, handler);
169 }
170 #endif
171 
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & fontRuns,BiDiRunIterator &,ScriptRunIterator &,LanguageRunIterator &,const Feature *,size_t,SkScalar width,RunHandler * handler) const172 void SkShaperPrimitive::shape(const char* utf8,
173                               size_t utf8Bytes,
174                               FontRunIterator& fontRuns,
175                               BiDiRunIterator&,
176                               ScriptRunIterator&,
177                               LanguageRunIterator&,
178                               const Feature*,
179                               size_t,
180                               SkScalar width,
181                               RunHandler* handler) const {
182     SkFont font;
183     if (!fontRuns.atEnd()) {
184         fontRuns.consume();
185         font = fontRuns.currentFont();
186     }
187     SkASSERT(font.getTypeface());
188 
189     int glyphCount = font.countText(utf8, utf8Bytes, SkTextEncoding::kUTF8);
190     if (glyphCount < 0) {
191         return;
192     }
193 
194     std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
195     font.textToGlyphs(utf8, utf8Bytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
196 
197     std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
198     font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
199 
200     size_t glyphOffset = 0;
201     size_t utf8Offset = 0;
202     do {
203         size_t bytesCollapsed;
204         size_t bytesConsumed = linebreak(utf8, utf8 + utf8Bytes, font, width,
205                                          advances.get() + glyphOffset, &bytesCollapsed);
206         size_t bytesVisible = bytesConsumed - bytesCollapsed;
207 
208         size_t numGlyphs = SkUTF::CountUTF8(utf8, bytesVisible);
209         const RunHandler::RunInfo info = {
210             font,
211             0,
212             { font.measureText(utf8, bytesVisible, SkTextEncoding::kUTF8), 0 },
213             numGlyphs,
214             RunHandler::Range(utf8Offset, bytesVisible)
215         };
216         handler->beginLine();
217         if (info.glyphCount) {
218             handler->runInfo(info);
219         }
220         handler->commitRunInfo();
221         if (info.glyphCount) {
222             const auto buffer = handler->runBuffer(info);
223 
224             memcpy(buffer.glyphs, glyphs.get() + glyphOffset, info.glyphCount * sizeof(SkGlyphID));
225             SkPoint position = buffer.point;
226             for (size_t i = 0; i < info.glyphCount; ++i) {
227                 buffer.positions[i] = position;
228                 position.fX += advances[i + glyphOffset];
229             }
230             if (buffer.clusters) {
231                 const char* txtPtr = utf8;
232                 for (size_t i = 0; i < info.glyphCount; ++i) {
233                     // Each character maps to exactly one glyph.
234                     buffer.clusters[i] = SkToU32(txtPtr - utf8 + utf8Offset);
235                     SkUTF::NextUTF8(&txtPtr, utf8 + utf8Bytes);
236                 }
237             }
238             handler->commitRunBuffer(info);
239         }
240         handler->commitLine();
241 
242         glyphOffset += SkUTF::CountUTF8(utf8, bytesConsumed);
243         utf8Offset += bytesConsumed;
244         utf8 += bytesConsumed;
245         utf8Bytes -= bytesConsumed;
246     } while (0 < utf8Bytes);
247 }
248 
249 #if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
MakePrimitive()250 std::unique_ptr<SkShaper> SkShaper::MakePrimitive() { return SkShapers::Primitive::PrimitiveText(); }
251 #endif
252 
253 namespace SkShapers::Primitive {
PrimitiveText()254 std::unique_ptr<SkShaper> PrimitiveText() { return std::make_unique<SkShaperPrimitive>(); }
255 }  // namespace SkShapers
256