Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkShaper_coretext.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 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
9
10#ifdef SK_BUILD_FOR_MAC
11#import <ApplicationServices/ApplicationServices.h>
12#endif
13
14#ifdef SK_BUILD_FOR_IOS
15#include <CoreText/CoreText.h>
16#include <CoreText/CTFontManager.h>
17#include <CoreGraphics/CoreGraphics.h>
18#include <CoreFoundation/CoreFoundation.h>
19#endif
20
23#include "src/base/SkUTF.h"
24#include "src/core/SkFontPriv.h"
27
28#include <vector>
29#include <utility>
30
31using namespace skia_private;
32
34public:
36private:
37#if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
38 void shape(const char* utf8, size_t utf8Bytes,
39 const SkFont& srcFont,
40 bool leftToRight,
42 RunHandler*) const override;
43
44 void shape(const char* utf8, size_t utf8Bytes,
45 FontRunIterator&,
46 BiDiRunIterator&,
47 ScriptRunIterator&,
48 LanguageRunIterator&,
50 RunHandler*) const override;
51#endif
52
53 void shape(const char* utf8, size_t utf8Bytes,
54 FontRunIterator&,
55 BiDiRunIterator&,
56 ScriptRunIterator&,
57 LanguageRunIterator&,
58 const Feature*, size_t featureSize,
60 RunHandler*) const override;
61};
62
63// CTFramesetter/CTFrame can do this, but require version 10.14
65 CTTypesetterRef fTypesetter;
66 double fWidth;
67 CFIndex fStart;
68
69public:
70 LineBreakIter(CTTypesetterRef ts, SkScalar width) : fTypesetter(ts), fWidth(width) {
71 fStart = 0;
72 }
73
74 SkUniqueCFRef<CTLineRef> nextLine() {
75 CFRange stringRange {fStart, CTTypesetterSuggestLineBreak(fTypesetter, fStart, fWidth)};
76 if (stringRange.length == 0) {
77 return nullptr;
78 }
79 fStart += stringRange.length;
80 return SkUniqueCFRef<CTLineRef>(CTTypesetterCreateLine(fTypesetter, stringRange));
81 }
82};
83
84[[maybe_unused]] static void dict_add_double(CFMutableDictionaryRef d, const void* name, double value) {
85 SkUniqueCFRef<CFNumberRef> number(
86 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
87 CFDictionaryAddValue(d, name, number.get());
88}
89
90static SkUniqueCFRef<CTFontRef> create_ctfont_from_font(const SkFont& font) {
91 auto typeface = font.getTypeface();
92 auto ctfont = SkTypeface_GetCTFontRef(typeface);
93 return SkUniqueCFRef<CTFontRef>(
94 CTFontCreateCopyWithAttributes(ctfont, font.getSize(), nullptr, nullptr));
95}
96
97static SkFont run_to_font(CTRunRef run, const SkFont& orig) {
98 CFDictionaryRef attr = CTRunGetAttributes(run);
99 CTFontRef ct = (CTFontRef)CFDictionaryGetValue(attr, kCTFontAttributeName);
100 if (!ct) {
101 SkDebugf("no ctfont in Run Attributes\n");
102 CFShow(attr);
103 return orig;
104 }
105 // Do I need to add a local cache, or allow the caller to manage this lookup?
106 SkFont font(orig);
107 font.setTypeface(SkMakeTypefaceFromCTFont(ct));
108 return font;
109}
110
111namespace {
112class UTF16ToUTF8IndicesMap {
113public:
114 /** Builds a UTF-16 to UTF-8 indices map; the text is not retained
115 * @return true if successful
116 */
117 bool setUTF8(const char* utf8, size_t size) {
118 SkASSERT(utf8 != nullptr);
119
120 if (!SkTFitsIn<int32_t>(size)) {
121 SkDEBUGF("UTF16ToUTF8IndicesMap: text too long");
122 return false;
123 }
124
125 auto utf16Size = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, size);
126 if (utf16Size < 0) {
127 SkDEBUGF("UTF16ToUTF8IndicesMap: Invalid utf8 input");
128 return false;
129 }
130
131 // utf16Size+1 to also store the size
132 fUtf16ToUtf8Indices = std::vector<size_t>(utf16Size + 1);
133 auto utf16 = fUtf16ToUtf8Indices.begin();
134 auto utf8Begin = utf8, utf8End = utf8 + size;
135 while (utf8Begin < utf8End) {
136 *utf16 = utf8Begin - utf8;
137 utf16 += SkUTF::ToUTF16(SkUTF::NextUTF8(&utf8Begin, utf8End), nullptr);
138 }
139 *utf16 = size;
140
141 return true;
142 }
143
144 size_t mapIndex(size_t index) const {
145 SkASSERT(index < fUtf16ToUtf8Indices.size());
146 return fUtf16ToUtf8Indices[index];
147 }
148
149 std::pair<size_t, size_t> mapRange(size_t start, size_t size) const {
150 auto utf8Start = mapIndex(start);
151 return {utf8Start, mapIndex(start + size) - utf8Start};
152 }
153private:
154 std::vector<size_t> fUtf16ToUtf8Indices;
155};
156} // namespace
157
158// kCTTrackingAttributeName not available until 10.12
159const CFStringRef kCTTracking_AttributeName = CFSTR("CTTracking");
160
161#if !defined(SK_DISABLE_LEGACY_SKSHAPER_FUNCTIONS)
162void SkShaper_CoreText::shape(const char* utf8,
163 size_t utf8Bytes,
164 FontRunIterator& font,
165 BiDiRunIterator& bidi,
166 ScriptRunIterator& script,
169 RunHandler* handler) const {
170 return this->shape(utf8, utf8Bytes, font, bidi, script, lang, nullptr, 0, width, handler);
171}
172
173void SkShaper_CoreText::shape(const char* utf8,
174 size_t utf8Bytes,
175 const SkFont& font,
176 bool,
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
192void SkShaper_CoreText::shape(const char* utf8,
193 size_t utf8Bytes,
194 FontRunIterator& fontRuns,
198 const Feature*,
199 size_t,
201 RunHandler* handler) const {
202 SkFont font;
203 if (!fontRuns.atEnd()) {
204 fontRuns.consume();
205 font = fontRuns.currentFont();
206 }
207 SkASSERT(font.getTypeface());
208
209 SkUniqueCFRef<CFStringRef> textString(
210 CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)utf8, utf8Bytes,
211 kCFStringEncodingUTF8, false));
212
213 UTF16ToUTF8IndicesMap utf8IndicesMap;
214 if (!utf8IndicesMap.setUTF8(utf8, utf8Bytes)) {
215 return;
216 }
217
218 SkUniqueCFRef<CTFontRef> ctfont = create_ctfont_from_font(font);
219
220 SkUniqueCFRef<CFMutableDictionaryRef> attr(
221 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
222 &kCFTypeDictionaryKeyCallBacks,
223 &kCFTypeDictionaryValueCallBacks));
224 CFDictionaryAddValue(attr.get(), kCTFontAttributeName, ctfont.get());
225 if ((false)) {
226 // trying to see what these affect
228 dict_add_double(attr.get(), kCTKernAttributeName, 0.0);
229 }
230
231 SkUniqueCFRef<CFAttributedStringRef> attrString(
232 CFAttributedStringCreate(kCFAllocatorDefault, textString.get(), attr.get()));
233
234 SkUniqueCFRef<CTTypesetterRef> typesetter(
235 CTTypesetterCreateWithAttributedString(attrString.get()));
236
237 // We have to compute RunInfos in a loop, and then reuse them in a 2nd loop,
238 // so we store them in an array (we reuse the array's storage for each line).
239 std::vector<SkFont> fontStorage;
240 std::vector<SkShaper::RunHandler::RunInfo> infos;
241
242 LineBreakIter iter(typesetter.get(), width);
243 while (SkUniqueCFRef<CTLineRef> line = iter.nextLine()) {
244 CFArrayRef run_array = CTLineGetGlyphRuns(line.get());
245 CFIndex runCount = CFArrayGetCount(run_array);
246 if (runCount == 0) {
247 continue;
248 }
249 handler->beginLine();
250 fontStorage.clear();
251 fontStorage.reserve(runCount); // ensure the refs won't get invalidated
252 infos.clear();
253 for (CFIndex j = 0; j < runCount; ++j) {
254 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
255 CFIndex runGlyphs = CTRunGetGlyphCount(run);
256
257 SkASSERT(sizeof(CGGlyph) == sizeof(uint16_t));
258
259 AutoSTArray<4096, CGSize> advances(runGlyphs);
260 CTRunGetAdvances(run, {0, runGlyphs}, advances.data());
261 SkScalar adv = 0;
262 for (CFIndex k = 0; k < runGlyphs; ++k) {
263 adv += advances[k].width;
264 }
265
266 CFRange cfRange = CTRunGetStringRange(run);
267 auto range = utf8IndicesMap.mapRange(cfRange.location, cfRange.length);
268
269 fontStorage.push_back(run_to_font(run, font));
270 infos.push_back({
271 fontStorage.back(), // info just stores a ref to the font
272 0, // need fBidiLevel
273 {adv, 0},
274 (size_t)runGlyphs,
275 {range.first, range.second},
276 });
277 handler->runInfo(infos.back());
278 }
279 handler->commitRunInfo();
280
281 // Now loop through again and fill in the buffers
282 SkScalar lineAdvance = 0;
283 for (CFIndex j = 0; j < runCount; ++j) {
284 const auto& info = infos[j];
285 auto buffer = handler->runBuffer(info);
286
287 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
288 CFIndex runGlyphs = info.glyphCount;
289 SkASSERT(CTRunGetGlyphCount(run) == (CFIndex)info.glyphCount);
290
291 CTRunGetGlyphs(run, {0, runGlyphs}, buffer.glyphs);
292
293 AutoSTArray<4096, CGPoint> positions(runGlyphs);
294 CTRunGetPositions(run, {0, runGlyphs}, positions.data());
296 if (buffer.clusters) {
297 indices.reset(runGlyphs);
298 CTRunGetStringIndices(run, {0, runGlyphs}, indices.data());
299 }
300
301 for (CFIndex k = 0; k < runGlyphs; ++k) {
302 buffer.positions[k] = {
303 buffer.point.fX + SkScalarFromCGFloat(positions[k].x) - lineAdvance,
304 buffer.point.fY,
305 };
306 if (buffer.offsets) {
307 buffer.offsets[k] = {0, 0}; // offset relative to the origin for this glyph
308 }
309 if (buffer.clusters) {
310 buffer.clusters[k] = utf8IndicesMap.mapIndex(indices[k]);
311 }
312 }
313 handler->commitRunBuffer(info);
314 lineAdvance += info.fAdvance.fX;
315 }
316 handler->commitLine();
317 }
318}
319
320namespace SkShapers::CT {
321std::unique_ptr<SkShaper> CoreText() { return std::make_unique<SkShaper_CoreText>(); }
322} // namespace SkShapers::CT
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
#define SkASSERT(cond)
Definition SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SkDEBUGF(...)
Definition SkDebug.h:24
static void dict_add_double(CFMutableDictionaryRef d, const void *name, double value)
static SkUniqueCFRef< CTFontRef > create_ctfont_from_font(const SkFont &font)
const CFStringRef kCTTracking_AttributeName
static SkFont run_to_font(CTRunRef run, const SkFont &orig)
LineBreakIter(CTTypesetterRef ts, SkScalar width)
SkUniqueCFRef< CTLineRef > nextLine()
virtual const SkFont & currentFont() const =0
virtual void commitLine()=0
Definition shape.cpp:201
virtual void commitRunBuffer(const RunInfo &)=0
Definition shape.cpp:176
virtual void commitRunInfo()=0
Definition shape.cpp:150
virtual void runInfo(const RunInfo &)=0
Definition shape.cpp:142
virtual void beginLine()=0
Definition shape.cpp:135
virtual Buffer runBuffer(const RunInfo &)=0
Definition shape.cpp:154
virtual bool atEnd() const =0
virtual void consume()=0
void shape(const char *utf8, size_t utf8Bytes, const SkFont &srcFont, bool leftToRight, SkScalar width, RunHandler *) const override
static std::unique_ptr< FontRunIterator > MakeFontMgrRunIterator(const char *utf8, size_t utf8Bytes, const SkFont &font, sk_sp< SkFontMgr > fallback)
Definition SkShaper.cpp:187
const T * data() const
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
float SkScalar
Definition extension.cpp:12
static const uint8_t buffer[]
uint8_t value
const char * name
Definition fuchsia.cc:50
double x
SKSHAPER_API std::unique_ptr< SkShaper > CoreText()
SK_SPI int UTF8ToUTF16(uint16_t dst[], int dstCapacity, const char src[], size_t srcByteLength)
Definition SkUTF.cpp:259
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
Definition SkUTF.cpp:118
SK_SPI size_t ToUTF16(SkUnichar uni, uint16_t utf16[2]=nullptr)
Definition SkUTF.cpp:243
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
Definition run.py:1
int32_t width