Flutter Engine
The Flutter Engine
shape.cpp
Go to the documentation of this file.
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
5
18#include "src/base/SkUTF.h"
20
21#if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && defined(SK_SHAPER_UNICODE_AVAILABLE)
25#else
26class SkUnicode;
27#endif
28
29#if defined(SK_UNICODE_ICU_IMPLEMENTATION)
31#endif
32#if defined(SK_UNICODE_LIBGRAPHEME_IMPLEMENTATION)
34#endif
35#if defined(SK_UNICODE_ICU4X_IMPLEMENTATION)
37#endif
38
39#include <cfloat>
40#include <climits>
41#include <cstring>
42
43using namespace SkPlainTextEditor;
44
45namespace {
46class RunHandler final : public SkShaper::RunHandler {
47public:
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);
57 void setRunCallback(RunCallback f, void* context) {
58 fCallbackContext = context;
59 fCallbackFunction = f;
60 }
61
62 sk_sp<SkTextBlob> makeBlob();
63 SkPoint endPoint() const { return fOffset; }
64 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
73 const std::vector<size_t>& lineEndOffsets() const { return fLineEndOffsets; }
74
75 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
92private:
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)
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)
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
135void RunHandler::beginLine() {
136 fCurrentPosition = fOffset;
137 fMaxRunAscent = 0;
138 fMaxRunDescent = 0;
139 fMaxRunLeading = 0;
140}
141
142void 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
150void RunHandler::commitRunInfo() {
151 fCurrentPosition.fY -= fMaxRunAscent;
152}
153
154SkShaper::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
176void 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
201void 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
209sk_sp<SkTextBlob> RunHandler::makeBlob() {
210 return fBuilder.make();
211}
212
213static 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
225static 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
302 size_t textByteLen,
303 const SkFont& font,
305 const char* locale,
306 float width)
307{
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();
317#else
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)
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'));
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}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
sk_sp< SkUnicode > get_unicode()
uint16_t SkGlyphID
Definition: SkTypes.h:179
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
Definition: SkTypes.h:167
Definition: SkFont.h:35
static std::unique_ptr< ScriptRunIterator > MakeScriptRunIterator(const char *utf8, size_t utf8Bytes, SkFourByteTag script)
Definition: SkShaper.cpp:68
static std::unique_ptr< FontRunIterator > MakeFontMgrRunIterator(const char *utf8, size_t utf8Bytes, const SkFont &font, sk_sp< SkFontMgr > fallback)
Definition: SkShaper.cpp:187
static std::unique_ptr< LanguageRunIterator > MakeStdLanguageRunIterator(const char *utf8, size_t utf8Bytes)
Definition: SkShaper.cpp:204
sk_sp< SkFontMgr > fontMgr
Definition: examples.cpp:32
float SkScalar
Definition: extension.cpp:12
glong glong end
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
double x
ShapeResult Shape(const char *ut8text, size_t textByteLen, const SkFont &font, sk_sp< SkFontMgr > fontMgr, const char *locale, float width)
Definition: shape.cpp:301
SKSHAPER_API std::unique_ptr< SkShaper > ShaperDrivenWrapper(sk_sp< SkUnicode > unicode, sk_sp< SkFontMgr > fallback)
SKSHAPER_API std::unique_ptr< SkShaper > PrimitiveText()
SKSHAPER_API std::unique_ptr< SkShaper::BiDiRunIterator > BidiRunIterator(sk_sp< SkUnicode > unicode, const char *utf8, size_t utf8Bytes, uint8_t bidiLevel)
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
Definition: SkUTF.cpp:118
SK_SPI int CountUTF8(const char *utf8, size_t byteLength)
Definition: SkUTF.cpp:47
SKUNICODE_API sk_sp< SkUnicode > Make()
SKUNICODE_API sk_sp< SkUnicode > Make()
SKUNICODE_API sk_sp< SkUnicode > Make()
font
Font Metadata and Metrics.
int32_t height
int32_t width
static SkRect selection_box(const SkFontMetrics &metrics, float advance, SkPoint pos)
Definition: shape.cpp:213
static void 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)
Definition: shape.cpp:225
SkScalar fLeading
distance to add between lines, typically positive or zero
Definition: SkFontMetrics.h:57
SkScalar fAscent
distance to reserve above baseline, typically negative
Definition: SkFontMetrics.h:54
SkScalar fDescent
distance to reserve below baseline, typically positive
Definition: SkFontMetrics.h:55
float fY
y-axis value
Definition: SkPoint_impl.h:165
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741
constexpr float width() const
Definition: SkRect.h:762
void join(const SkRect &r)
Definition: SkRect.cpp:126
constexpr float bottom() const
Definition: SkRect.h:755
std::vector< bool > GetUtf8WordBoundaries(const char *begin, size_t byteCount, const char *locale)