Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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)
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
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
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)
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}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
uint16_t glyphs[5]
SkPoint pos
#define SkASSERT(cond)
Definition SkAssert.h:116
uint16_t SkGlyphID
Definition SkTypes.h:179
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
Definition SkTypes.h:167
Type::kYUV Type::kRGBA() int(0.7 *637)
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
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::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.
static sk_sp< SkUnicode > get_unicode()
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
SkScalar fAscent
distance to reserve above baseline, typically negative
SkScalar fDescent
distance to reserve below baseline, typically positive
float fY
y-axis value
constexpr float y() const
constexpr float x() const
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)