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 #ifdef ENABLE_DRAWING_ADAPTER
25 namespace SkiaRsText {
26 #endif
27 class SkShaperPrimitive : public SkShaper {
28 public:
SkShaperPrimitive()29 SkShaperPrimitive() {}
30 private:
31 #if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
32 void shape(const char* utf8, size_t utf8Bytes,
33 #ifdef ENABLE_TEXT_ENHANCE
34 const RSFont& srcFont,
35 #else
36 const SkFont& srcFont,
37 #endif
38 bool leftToRight,
39 SkScalar width,
40 RunHandler*) const override;
41
42 void shape(const char* utf8, size_t utf8Bytes,
43 FontRunIterator&,
44 BiDiRunIterator&,
45 ScriptRunIterator&,
46 LanguageRunIterator&,
47 SkScalar width,
48 RunHandler*) const override;
49 #endif
50
51 void shape(const char* utf8, size_t utf8Bytes,
52 FontRunIterator&,
53 BiDiRunIterator&,
54 ScriptRunIterator&,
55 LanguageRunIterator&,
56 const Feature*, size_t featureSize,
57 SkScalar width,
58 RunHandler*) const override;
59
60 #ifdef ENABLE_TEXT_ENHANCE
61 void shapeProcessUtf(const char* utf8, size_t utf8Bytes, SkScalar width, RunHandler* handler,
62 RSFont font, std::unique_ptr<SkGlyphID[]>& glyphs, std::unique_ptr<SkScalar[]>& advances) const;
63 #endif
64 };
65
is_breaking_whitespace(SkUnichar c)66 static inline bool is_breaking_whitespace(SkUnichar c) {
67 switch (c) {
68 case 0x0020: // SPACE
69 //case 0x00A0: // NO-BREAK SPACE
70 case 0x1680: // OGHAM SPACE MARK
71 case 0x180E: // MONGOLIAN VOWEL SEPARATOR
72 case 0x2000: // EN QUAD
73 case 0x2001: // EM QUAD
74 case 0x2002: // EN SPACE (nut)
75 case 0x2003: // EM SPACE (mutton)
76 case 0x2004: // THREE-PER-EM SPACE (thick space)
77 case 0x2005: // FOUR-PER-EM SPACE (mid space)
78 case 0x2006: // SIX-PER-EM SPACE
79 case 0x2007: // FIGURE SPACE
80 case 0x2008: // PUNCTUATION SPACE
81 case 0x2009: // THIN SPACE
82 case 0x200A: // HAIR SPACE
83 case 0x200B: // ZERO WIDTH SPACE
84 case 0x202F: // NARROW NO-BREAK SPACE
85 case 0x205F: // MEDIUM MATHEMATICAL SPACE
86 case 0x3000: // IDEOGRAPHIC SPACE
87 //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
88 return true;
89 default:
90 return false;
91 }
92 }
93
linebreak(const char text[],const char stop[],const RSFont & font,SkScalar width,SkScalar * advance,size_t * trailing)94 static size_t linebreak(const char text[], const char stop[],
95 #ifdef ENABLE_TEXT_ENHANCE
96 const RSFont& font, SkScalar width,
97 #else
98 const SkFont& font, SkScalar width,
99 #endif
100 SkScalar* advance,
101 size_t* trailing)
102 {
103 SkScalar accumulatedWidth = 0;
104 int glyphIndex = 0;
105 const char* start = text;
106 const char* wordStart = text;
107 bool prevWS = true;
108 *trailing = 0;
109
110 while (text < stop) {
111 const char* prevText = text;
112 SkUnichar uni = SkUTF::NextUTF8(&text, stop);
113 accumulatedWidth += advance[glyphIndex++];
114 bool currWS = is_breaking_whitespace(uni);
115
116 if (!currWS && prevWS) {
117 wordStart = prevText;
118 }
119 prevWS = currWS;
120
121 if (width < accumulatedWidth) {
122 bool consumeWhitespace = false;
123 if (currWS) {
124 // previous fit, put this and following whitespace in trailing
125 if (prevText == start) {
126 // don't put this in trailing if it's the first thing
127 prevText = text;
128 }
129 consumeWhitespace = true;
130 } else if (wordStart != start) {
131 // backup to the last whitespace that fit
132 text = wordStart;
133 } else if (prevText > start) {
134 // backup to just before the glyph that didn't fit
135 text = prevText;
136 } else {
137 // let it overflow, put any following whitespace in trailing
138 prevText = text;
139 consumeWhitespace = true;
140 }
141 if (consumeWhitespace) {
142 const char* next = text;
143 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
144 text = next;
145 }
146 if (trailing) {
147 *trailing = text - prevText;
148 }
149 }
150 break;
151 }
152 }
153
154 return text - start;
155 }
156
157 #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) const158 void SkShaperPrimitive::shape(const char* utf8,
159 size_t utf8Bytes,
160 FontRunIterator& font,
161 BiDiRunIterator& bidi,
162 ScriptRunIterator& script,
163 LanguageRunIterator& lang,
164 SkScalar width,
165 RunHandler* handler) const {
166 return this->shape(utf8, utf8Bytes, font, bidi, script, lang, nullptr, 0, width, handler);
167 }
168
shape(const char * utf8,size_t utf8Bytes,const RSFont & font,bool leftToRight,SkScalar width,RunHandler * handler) const169 void SkShaperPrimitive::shape(const char* utf8,
170 size_t utf8Bytes,
171 #ifdef ENABLE_TEXT_ENHANCE
172 const RSFont& font,
173 #else
174 const SkFont& font,
175 #endif
176 bool leftToRight,
177 SkScalar width,
178 RunHandler* handler) const {
179 std::unique_ptr<FontRunIterator> fontRuns(
180 MakeFontMgrRunIterator(utf8, utf8Bytes, font, nullptr));
181 if (!fontRuns) {
182 return;
183 }
184 // bidi, script, and lang are all unused so we can construct them with empty data.
185 TrivialBiDiRunIterator bidi{0, 0};
186 TrivialScriptRunIterator script{0, 0};
187 TrivialLanguageRunIterator lang{nullptr, 0};
188 return this->shape(utf8, utf8Bytes, *fontRuns, bidi, script, lang, nullptr, 0, width, handler);
189 }
190 #endif
191
192 #ifdef ENABLE_TEXT_ENHANCE
shapeProcessUtf(const char * utf8,size_t utf8Bytes,SkScalar width,RunHandler * handler,RSFont font,std::unique_ptr<SkGlyphID[]> & glyphs,std::unique_ptr<SkScalar[]> & advances) const193 void SkShaperPrimitive::shapeProcessUtf(const char* utf8, size_t utf8Bytes, SkScalar width, RunHandler* handler,
194 RSFont font, std::unique_ptr<SkGlyphID[]>& glyphs, std::unique_ptr<SkScalar[]>& advances) const {
195 size_t glyphOffset = 0;
196 size_t utf8Offset = 0;
197 do {
198 size_t bytesCollapsed;
199 size_t bytesConsumed = linebreak(utf8, utf8 + utf8Bytes, font, width,
200 advances.get() + glyphOffset, &bytesCollapsed);
201 size_t bytesVisible = bytesConsumed - bytesCollapsed;
202
203 size_t numGlyphs = SkUTF::CountUTF8(utf8, bytesVisible);
204 const RunHandler::RunInfo info = {
205 font,
206 0,
207 { font.MeasureText(utf8, bytesVisible, RSDrawing::TextEncoding::UTF8), 0 },
208 numGlyphs,
209 RunHandler::Range(utf8Offset, bytesVisible)
210 };
211 handler->beginLine();
212 if (info.glyphCount) {
213 handler->runInfo(info);
214 }
215 handler->commitRunInfo();
216 if (info.glyphCount) {
217 const auto buffer = handler->runBuffer(info);
218
219 memcpy(buffer.glyphs, glyphs.get() + glyphOffset, info.glyphCount * sizeof(SkGlyphID));
220 SkPoint position = buffer.point;
221 for (size_t i = 0; i < info.glyphCount; ++i) {
222 buffer.positions[i] = position;
223 position.fX += advances[i + glyphOffset];
224 }
225 if (buffer.clusters) {
226 const char* txtPtr = utf8;
227 for (size_t i = 0; i < info.glyphCount; ++i) {
228 // Each character maps to exactly one glyph.
229 buffer.clusters[i] = SkToU32(txtPtr - utf8 + utf8Offset);
230 SkUTF::NextUTF8(&txtPtr, utf8 + utf8Bytes);
231 }
232 }
233 handler->commitRunBuffer(info);
234 }
235 handler->commitLine();
236
237 glyphOffset += SkUTF::CountUTF8(utf8, bytesConsumed);
238 utf8Offset += bytesConsumed;
239 utf8 += bytesConsumed;
240 utf8Bytes -= bytesConsumed;
241 } while (0 < utf8Bytes);
242 }
243 #endif
244
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & fontRuns,BiDiRunIterator &,ScriptRunIterator &,LanguageRunIterator &,const Feature *,size_t,SkScalar width,RunHandler * handler) const245 void SkShaperPrimitive::shape(const char* utf8,
246 size_t utf8Bytes,
247 FontRunIterator& fontRuns,
248 BiDiRunIterator&,
249 ScriptRunIterator&,
250 LanguageRunIterator&,
251 const Feature*,
252 size_t,
253 SkScalar width,
254 RunHandler* handler) const {
255 #ifdef ENABLE_TEXT_ENHANCE
256 RSFont font;
257 if (!fontRuns.atEnd()) {
258 fontRuns.consume();
259 font = fontRuns.currentFont();
260 }
261 SkASSERT(font.getTypeface());
262
263 int glyphCount = font.CountText(utf8, utf8Bytes, RSDrawing::TextEncoding::UTF8);
264 if (glyphCount < 0) {
265 return;
266 }
267
268 std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
269 font.TextToGlyphs(utf8, utf8Bytes, RSDrawing::TextEncoding::UTF8, glyphs.get(), glyphCount);
270
271 std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
272 font.GetWidths(glyphs.get(), glyphCount, advances.get(), nullptr);
273
274 shapeProcessUtf(utf8, utf8Bytes, width, handler, font, glyphs, advances);
275 }
276 #else
277 SkFont font;
278 if (!fontRuns.atEnd()) {
279 fontRuns.consume();
280 font = fontRuns.currentFont();
281 }
282 SkASSERT(font.getTypeface());
283
284 int glyphCount = font.countText(utf8, utf8Bytes, SkTextEncoding::kUTF8);
285 if (glyphCount < 0) {
286 return;
287 }
288
289 std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
290 font.textToGlyphs(utf8, utf8Bytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
291
292 std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
293 font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
294
295 size_t glyphOffset = 0;
296 size_t utf8Offset = 0;
297 do {
298 size_t bytesCollapsed;
299 size_t bytesConsumed = linebreak(utf8, utf8 + utf8Bytes, font, width,
300 advances.get() + glyphOffset, &bytesCollapsed);
301 size_t bytesVisible = bytesConsumed - bytesCollapsed;
302
303 size_t numGlyphs = SkUTF::CountUTF8(utf8, bytesVisible);
304 const RunHandler::RunInfo info = {
305 font,
306 0,
307 { font.measureText(utf8, bytesVisible, SkTextEncoding::kUTF8), 0 },
308 numGlyphs,
309 RunHandler::Range(utf8Offset, bytesVisible)
310 };
311 handler->beginLine();
312 if (info.glyphCount) {
313 handler->runInfo(info);
314 }
315 handler->commitRunInfo();
316 if (info.glyphCount) {
317 const auto buffer = handler->runBuffer(info);
318
319 memcpy(buffer.glyphs, glyphs.get() + glyphOffset, info.glyphCount * sizeof(SkGlyphID));
320 SkPoint position = buffer.point;
321 for (size_t i = 0; i < info.glyphCount; ++i) {
322 buffer.positions[i] = position;
323 position.fX += advances[i + glyphOffset];
324 }
325 if (buffer.clusters) {
326 const char* txtPtr = utf8;
327 for (size_t i = 0; i < info.glyphCount; ++i) {
328 // Each character maps to exactly one glyph.
329 buffer.clusters[i] = SkToU32(txtPtr - utf8 + utf8Offset);
330 SkUTF::NextUTF8(&txtPtr, utf8 + utf8Bytes);
331 }
332 }
333 handler->commitRunBuffer(info);
334 }
335 handler->commitLine();
336
337 glyphOffset += SkUTF::CountUTF8(utf8, bytesConsumed);
338 utf8Offset += bytesConsumed;
339 utf8 += bytesConsumed;
340 utf8Bytes -= bytesConsumed;
341 } while (0 < utf8Bytes);
342 }
343 #endif
344
345 #if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
MakePrimitive()346 std::unique_ptr<SkShaper> SkShaper::MakePrimitive() { return SkShapers::Primitive::PrimitiveText(); }
347 #endif
348
349 namespace SkShapers::Primitive {
PrimitiveText()350 std::unique_ptr<SkShaper> PrimitiveText() { return std::make_unique<SkShaperPrimitive>(); }
351 } // namespace SkShapers
352 #ifdef ENABLE_DRAWING_ADAPTER
353 } // namespace SkiaRsText
354 #endif // ENABLE_DRAWING_ADAPTER
355