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
8 #include "include/core/SkFontMetrics.h"
9 #include "include/core/SkStream.h"
10 #include "include/core/SkTypeface.h"
11 #include "include/private/SkTo.h"
12 #include "modules/skshaper/include/SkShaper.h"
13 #include "src/utils/SkUTF.h"
14
15 #ifdef USE_SKIA_TXT
16 namespace SkiaRsText{
17 #endif
18
19 class SkShaperPrimitive : public SkShaper {
20 public:
SkShaperPrimitive()21 SkShaperPrimitive() {}
22 private:
23 void shape(const char* utf8, size_t utf8Bytes,
24 #ifdef USE_SKIA_TXT
25 const RSFont& srcFont,
26 #else
27 const SkFont& srcFont,
28 #endif
29 bool leftToRight,
30 SkScalar width,
31 RunHandler*) const override;
32
33 void shape(const char* utf8, size_t utf8Bytes,
34 FontRunIterator&,
35 BiDiRunIterator&,
36 ScriptRunIterator&,
37 LanguageRunIterator&,
38 SkScalar width,
39 RunHandler*) const override;
40
41 void shape(const char* utf8, size_t utf8Bytes,
42 FontRunIterator&,
43 BiDiRunIterator&,
44 ScriptRunIterator&,
45 LanguageRunIterator&,
46 const Feature*, size_t featureSize,
47 SkScalar width,
48 RunHandler*) const override;
49 };
50
MakePrimitive()51 std::unique_ptr<SkShaper> SkShaper::MakePrimitive() {
52 return std::make_unique<SkShaperPrimitive>();
53 }
54
is_breaking_whitespace(SkUnichar c)55 static inline bool is_breaking_whitespace(SkUnichar c) {
56 switch (c) {
57 case 0x0020: // SPACE
58 //case 0x00A0: // NO-BREAK SPACE
59 case 0x1680: // OGHAM SPACE MARK
60 case 0x180E: // MONGOLIAN VOWEL SEPARATOR
61 case 0x2000: // EN QUAD
62 case 0x2001: // EM QUAD
63 case 0x2002: // EN SPACE (nut)
64 case 0x2003: // EM SPACE (mutton)
65 case 0x2004: // THREE-PER-EM SPACE (thick space)
66 case 0x2005: // FOUR-PER-EM SPACE (mid space)
67 case 0x2006: // SIX-PER-EM SPACE
68 case 0x2007: // FIGURE SPACE
69 case 0x2008: // PUNCTUATION SPACE
70 case 0x2009: // THIN SPACE
71 case 0x200A: // HAIR SPACE
72 case 0x200B: // ZERO WIDTH SPACE
73 case 0x202F: // NARROW NO-BREAK SPACE
74 case 0x205F: // MEDIUM MATHEMATICAL SPACE
75 case 0x3000: // IDEOGRAPHIC SPACE
76 //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
77 return true;
78 default:
79 return false;
80 }
81 }
82
linebreak(const char text[], const char stop[], const RSFont& font, SkScalar width, SkScalar* advance, size_t* trailing)83 static size_t linebreak(const char text[], const char stop[],
84 #ifdef USE_SKIA_TXT
85 const RSFont& font, SkScalar width,
86 #else
87 const SkFont& font, SkScalar width,
88 #endif
89 SkScalar* advance,
90 size_t* trailing)
91 {
92 SkScalar accumulatedWidth = 0;
93 int glyphIndex = 0;
94 const char* start = text;
95 const char* wordStart = text;
96 bool prevWS = true;
97 *trailing = 0;
98
99 while (text < stop) {
100 const char* prevText = text;
101 SkUnichar uni = SkUTF::NextUTF8(&text, stop);
102 accumulatedWidth += advance[glyphIndex++];
103 bool currWS = is_breaking_whitespace(uni);
104
105 if (!currWS && prevWS) {
106 wordStart = prevText;
107 }
108 prevWS = currWS;
109
110 if (width < accumulatedWidth) {
111 bool consumeWhitespace = false;
112 if (currWS) {
113 // previous fit, put this and following whitespace in trailing
114 if (prevText == start) {
115 // don't put this in trailing if it's the first thing
116 prevText = text;
117 }
118 consumeWhitespace = true;
119 } else if (wordStart != start) {
120 // backup to the last whitespace that fit
121 text = wordStart;
122 } else if (prevText > start) {
123 // backup to just before the glyph that didn't fit
124 text = prevText;
125 } else {
126 // let it overflow, put any following whitespace in trailing
127 prevText = text;
128 consumeWhitespace = true;
129 }
130 if (consumeWhitespace) {
131 const char* next = text;
132 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
133 text = next;
134 }
135 if (trailing) {
136 *trailing = text - prevText;
137 }
138 }
139 break;
140 }
141 }
142
143 return text - start;
144 }
145
shape(const char* utf8, size_t utf8Bytes, FontRunIterator& font, BiDiRunIterator& bidi, ScriptRunIterator&, LanguageRunIterator&, SkScalar width, RunHandler* handler) const146 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
147 FontRunIterator& font,
148 BiDiRunIterator& bidi,
149 ScriptRunIterator&,
150 LanguageRunIterator&,
151 SkScalar width,
152 RunHandler* handler) const
153 {
154 #ifdef USE_SKIA_TXT
155 RSFont skfont;
156 #else
157 SkFont skfont;
158 #endif
159 if (!font.atEnd()) {
160 font.consume();
161 skfont = font.currentFont();
162 } else {
163 #ifdef USE_SKIA_TXT
164 skfont.SetTypeface(RSTypeface::MakeDefault());
165 #else
166 skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault()));
167 #endif
168 }
169 SkASSERT(skfont.getTypeface());
170 bool skbidi = 0;
171 if (!bidi.atEnd()) {
172 bidi.consume();
173 skbidi = (bidi.currentLevel() % 2) == 0;
174 }
175 return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
176 }
177
shape(const char* utf8, size_t utf8Bytes, FontRunIterator& font, BiDiRunIterator& bidi, ScriptRunIterator&, LanguageRunIterator&, const Feature*, size_t, SkScalar width, RunHandler* handler) const178 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
179 FontRunIterator& font,
180 BiDiRunIterator& bidi,
181 ScriptRunIterator&,
182 LanguageRunIterator&,
183 const Feature*, size_t,
184 SkScalar width,
185 RunHandler* handler) const {
186 font.consume();
187 SkASSERT(font.currentFont().getTypeface());
188 bidi.consume();
189 return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
190 width, handler);
191 }
192
shape(const char* utf8, size_t utf8Bytes, const RSFont& font, bool leftToRight, SkScalar width, RunHandler* handler) const193 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
194 #ifdef USE_SKIA_TXT
195 const RSFont& font,
196 #else
197 const SkFont& font,
198 #endif
199 bool leftToRight,
200 SkScalar width,
201 RunHandler* handler) const {
202 sk_ignore_unused_variable(leftToRight);
203
204 #ifdef USE_SKIA_TXT
205 int glyphCount = font.CountText(utf8, utf8Bytes, RSDrawing::TextEncoding::UTF8);
206 #else
207 int glyphCount = font.countText(utf8, utf8Bytes, SkTextEncoding::kUTF8);
208 #endif
209
210 if (glyphCount < 0) {
211 return;
212 }
213
214 std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
215 #ifdef USE_SKIA_TXT
216 font.TextToGlyphs(utf8, utf8Bytes, RSDrawing::TextEncoding::UTF8, glyphs.get(), glyphCount);
217 #else
218 font.textToGlyphs(utf8, utf8Bytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
219 #endif
220
221 std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
222 #ifdef USE_SKIA_TXT
223 font.GetWidths(glyphs.get(), glyphCount, advances.get(), nullptr);
224 #else
225 font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
226 #endif
227
228
229 size_t glyphOffset = 0;
230 size_t utf8Offset = 0;
231 do {
232 size_t bytesCollapsed;
233 size_t bytesConsumed = linebreak(utf8, utf8 + utf8Bytes, font, width,
234 advances.get() + glyphOffset, &bytesCollapsed);
235 size_t bytesVisible = bytesConsumed - bytesCollapsed;
236
237 size_t numGlyphs = SkUTF::CountUTF8(utf8, bytesVisible);
238 const RunHandler::RunInfo info = {
239 font,
240 0,
241 #ifdef USE_SKIA_TXT
242 { font.MeasureText(utf8, bytesVisible, RSDrawing::TextEncoding::UTF8), 0 },
243 #else
244 { font.measureText(utf8, bytesVisible, SkTextEncoding::kUTF8), 0 },
245 #endif
246 numGlyphs,
247 RunHandler::Range(utf8Offset, bytesVisible)
248 };
249 handler->beginLine();
250 if (info.glyphCount) {
251 handler->runInfo(info);
252 }
253 handler->commitRunInfo();
254 if (info.glyphCount) {
255 const auto buffer = handler->runBuffer(info);
256
257 memcpy(buffer.glyphs, glyphs.get() + glyphOffset, info.glyphCount * sizeof(SkGlyphID));
258 SkPoint position = buffer.point;
259 for (size_t i = 0; i < info.glyphCount; ++i) {
260 buffer.positions[i] = position;
261 position.fX += advances[i + glyphOffset];
262 }
263 if (buffer.clusters) {
264 const char* txtPtr = utf8;
265 for (size_t i = 0; i < info.glyphCount; ++i) {
266 // Each character maps to exactly one glyph.
267 buffer.clusters[i] = SkToU32(txtPtr - utf8 + utf8Offset);
268 SkUTF::NextUTF8(&txtPtr, utf8 + utf8Bytes);
269 }
270 }
271 handler->commitRunBuffer(info);
272 }
273 handler->commitLine();
274
275 glyphOffset += SkUTF::CountUTF8(utf8, bytesConsumed);
276 utf8Offset += bytesConsumed;
277 utf8 += bytesConsumed;
278 utf8Bytes -= bytesConsumed;
279 } while (0 < utf8Bytes);
280
281 return;
282 }
283 #ifdef USE_SKIA_TXT
284 }
285 #endif
286