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