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 | |
8 | #include "modules/skshaper/include/SkShaper.h" |
9 | |
10 | #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) |
11 | |
12 | #ifdef SK_BUILD_FOR_MAC |
13 | #import <ApplicationServices/ApplicationServices.h> |
14 | #endif |
15 | |
16 | #ifdef SK_BUILD_FOR_IOS |
17 | #include <CoreText/CoreText.h> |
18 | #include <CoreText/CTFontManager.h> |
19 | #include <CoreGraphics/CoreGraphics.h> |
20 | #include <CoreFoundation/CoreFoundation.h> |
21 | #endif |
22 | |
23 | #include "include/ports/SkTypeface_mac.h" |
24 | #include "src/core/SkArenaAlloc.h" |
25 | |
26 | #include <vector> |
27 | |
28 | class SkShaper_CoreText : public SkShaper { |
29 | public: |
30 | SkShaper_CoreText() {} |
31 | private: |
32 | void shape(const char* utf8, size_t utf8Bytes, |
33 | const SkFont& srcFont, |
34 | bool leftToRight, |
35 | SkScalar width, |
36 | RunHandler*) const override; |
37 | |
38 | void shape(const char* utf8, size_t utf8Bytes, |
39 | FontRunIterator&, |
40 | BiDiRunIterator&, |
41 | ScriptRunIterator&, |
42 | LanguageRunIterator&, |
43 | SkScalar width, |
44 | RunHandler*) const override; |
45 | |
46 | void shape(const char* utf8, size_t utf8Bytes, |
47 | FontRunIterator&, |
48 | BiDiRunIterator&, |
49 | ScriptRunIterator&, |
50 | LanguageRunIterator&, |
51 | const Feature*, size_t featureSize, |
52 | SkScalar width, |
53 | RunHandler*) const override; |
54 | }; |
55 | |
56 | std::unique_ptr<SkShaper> SkShaper::MakeCoreText() { |
57 | return std::make_unique<SkShaper_CoreText>(); |
58 | } |
59 | |
60 | void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes, |
61 | FontRunIterator& font, |
62 | BiDiRunIterator& bidi, |
63 | ScriptRunIterator&, |
64 | LanguageRunIterator&, |
65 | SkScalar width, |
66 | RunHandler* handler) const |
67 | { |
68 | SkFont skfont; |
69 | if (!font.atEnd()) { |
70 | font.consume(); |
71 | skfont = font.currentFont(); |
72 | } else { |
73 | skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault())); |
74 | } |
75 | SkASSERT(skfont.getTypeface()); |
76 | bool skbidi = 0; |
77 | if (!bidi.atEnd()) { |
78 | bidi.consume(); |
79 | skbidi = (bidi.currentLevel() % 2) == 0; |
80 | } |
81 | return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler); |
82 | } |
83 | |
84 | void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes, |
85 | FontRunIterator& font, |
86 | BiDiRunIterator& bidi, |
87 | ScriptRunIterator&, |
88 | LanguageRunIterator&, |
89 | const Feature*, size_t, |
90 | SkScalar width, |
91 | RunHandler* handler) const { |
92 | font.consume(); |
93 | SkASSERT(font.currentFont().getTypeface()); |
94 | bidi.consume(); |
95 | return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0, |
96 | width, handler); |
97 | } |
98 | |
99 | template <typename T> class AutoCF { |
100 | T fObj; |
101 | public: |
102 | AutoCF(T obj) : fObj(obj) {} |
103 | ~AutoCF() { CFRelease(fObj); } |
104 | |
105 | T get() const { return fObj; } |
106 | }; |
107 | |
108 | // CTFramesetter/CTFrame can do this, but require version 10.14 |
109 | class LineBreakIter { |
110 | CTTypesetterRef fTypesetter; |
111 | double fWidth; |
112 | CFIndex fStart; |
113 | |
114 | public: |
115 | LineBreakIter(CTTypesetterRef ts, SkScalar width) : fTypesetter(ts), fWidth(width) { |
116 | fStart = 0; |
117 | } |
118 | |
119 | CTLineRef nextLine() { |
120 | CFIndex count = CTTypesetterSuggestLineBreak(fTypesetter, fStart, fWidth); |
121 | if (count == 0) { |
122 | return nullptr; |
123 | } |
124 | auto line = CTTypesetterCreateLine(fTypesetter, {fStart, count}); |
125 | fStart += count; |
126 | return line; |
127 | } |
128 | }; |
129 | |
130 | static void dict_add_double(CFMutableDictionaryRef d, const void* name, double value) { |
131 | AutoCF<CFNumberRef> number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value); |
132 | CFDictionaryAddValue(d, name, number.get()); |
133 | } |
134 | |
135 | static void dump(CFDictionaryRef d) { |
136 | CFIndex count = CFDictionaryGetCount(d); |
137 | std::vector<const void*> keys(count); |
138 | std::vector<const void*> vals(count); |
139 | |
140 | CFDictionaryGetKeysAndValues(d, keys.data(), vals.data()); |
141 | |
142 | for (CFIndex i = 0; i < count; ++i) { |
143 | CFStringRef kstr = (CFStringRef)keys[i]; |
144 | const char* ckstr = CFStringGetCStringPtr(kstr, kCFStringEncodingUTF8); |
145 | SkDebugf("dict[%d] %s %p\n" , i, ckstr, vals[i]); |
146 | } |
147 | } |
148 | |
149 | static CTFontRef create_ctfont_from_font(const SkFont& font) { |
150 | auto typeface = font.getTypefaceOrDefault(); |
151 | auto ctfont = SkTypeface_GetCTFontRef(typeface); |
152 | return CTFontCreateCopyWithAttributes(ctfont, font.getSize(), nullptr, nullptr); |
153 | } |
154 | |
155 | static SkFont run_to_font(CTRunRef run, const SkFont& orig) { |
156 | CFDictionaryRef attr = CTRunGetAttributes(run); |
157 | CTFontRef ct = (CTFontRef)CFDictionaryGetValue(attr, kCTFontAttributeName); |
158 | if (!ct) { |
159 | SkDebugf("no ctfont in Run Attributes\n" ); |
160 | dump(attr); |
161 | return orig; |
162 | } |
163 | // Do I need to add a local cache, or allow the caller to manage this lookup? |
164 | SkFont font(orig); |
165 | font.setTypeface(SkMakeTypefaceFromCTFont(ct)); |
166 | return font; |
167 | } |
168 | |
169 | // kCTTrackingAttributeName not available until 10.12 |
170 | const CFStringRef kCTTracking_AttributeName = CFSTR("CTTracking" ); |
171 | |
172 | void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes, |
173 | const SkFont& font, |
174 | bool /* leftToRight */, |
175 | SkScalar width, |
176 | RunHandler* handler) const { |
177 | auto cgfloat_to_scalar = [](CGFloat x) { |
178 | SkScalar s; |
179 | if (sizeof(CGFloat) == sizeof(double)) { |
180 | s = SkDoubleToScalar(x); |
181 | } else { |
182 | s = x; |
183 | } |
184 | return s; |
185 | }; |
186 | |
187 | AutoCF<CFStringRef> textString = CFStringCreateWithBytes(nullptr, (const uint8_t*)utf8, utf8Bytes, |
188 | kCFStringEncodingUTF8, false); |
189 | |
190 | AutoCF<CTFontRef> ctfont = create_ctfont_from_font(font); |
191 | |
192 | AutoCF<CFMutableDictionaryRef> attr = |
193 | CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
194 | &kCFTypeDictionaryKeyCallBacks, |
195 | &kCFTypeDictionaryValueCallBacks); |
196 | CFDictionaryAddValue(attr.get(), kCTFontAttributeName, ctfont.get()); |
197 | if (false) { |
198 | // trying to see what these affect |
199 | dict_add_double(attr.get(), kCTTracking_AttributeName, 1); |
200 | dict_add_double(attr.get(), kCTKernAttributeName, 0.0); |
201 | } |
202 | |
203 | AutoCF<CFAttributedStringRef> attrString = |
204 | CFAttributedStringCreate(nullptr, textString.get(), attr.get()); |
205 | |
206 | AutoCF<CTTypesetterRef> typesetter = |
207 | CTTypesetterCreateWithAttributedStringAndOptions(attrString.get(), nullptr); |
208 | |
209 | SkSTArenaAlloc<4096> arena; |
210 | |
211 | // We have to compute RunInfos in a loop, and then reuse them in a 2nd loop, |
212 | // so we store them in an array (we reuse the array's storage for each line). |
213 | std::vector<SkShaper::RunHandler::RunInfo> infos; |
214 | |
215 | LineBreakIter iter(typesetter.get(), width); |
216 | while (CTLineRef line = iter.nextLine()) { |
217 | CFArrayRef run_array = CTLineGetGlyphRuns(line); |
218 | CFIndex runCount = CFArrayGetCount(run_array); |
219 | if (runCount == 0) { |
220 | continue; |
221 | } |
222 | handler->beginLine(); |
223 | infos.clear(); |
224 | for (CFIndex j = 0; j < runCount; ++j) { |
225 | CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j); |
226 | CFIndex runGlyphs = CTRunGetGlyphCount(run); |
227 | |
228 | SkASSERT(sizeof(CGGlyph) == sizeof(uint16_t)); |
229 | |
230 | CGSize* advances = arena.makeArrayDefault<CGSize>(runGlyphs); |
231 | CTRunGetAdvances(run, {0, runGlyphs}, advances); |
232 | SkScalar adv = 0; |
233 | for (CFIndex k = 0; k < runGlyphs; ++k) { |
234 | adv += advances[k].width; |
235 | } |
236 | arena.reset(); |
237 | |
238 | CFRange range = CTRunGetStringRange(run); |
239 | |
240 | SkFont run_font = run_to_font(run, font); |
241 | infos.push_back({ |
242 | run_font, |
243 | 0, // need fBidiLevel |
244 | {adv, 0}, |
245 | (size_t)runGlyphs, |
246 | {(size_t)range.location, (size_t)range.length}, |
247 | }); |
248 | handler->runInfo(infos.back()); |
249 | } |
250 | handler->commitRunInfo(); |
251 | |
252 | // Now loop through again and fill in the buffers |
253 | for (CFIndex j = 0; j < runCount; ++j) { |
254 | const auto& info = infos[j]; |
255 | auto buffer = handler->runBuffer(info); |
256 | |
257 | CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j); |
258 | CFIndex runGlyphs = info.glyphCount; |
259 | SkASSERT(CTRunGetGlyphCount(run) == (CFIndex)info.glyphCount); |
260 | |
261 | CTRunGetGlyphs(run, {0, runGlyphs}, buffer.glyphs); |
262 | |
263 | CGPoint* positions = arena.makeArrayDefault<CGPoint>(runGlyphs); |
264 | CTRunGetPositions(run, {0, runGlyphs}, positions); |
265 | CFIndex* indices = nullptr; |
266 | if (buffer.clusters) { |
267 | indices = arena.makeArrayDefault<CFIndex>(runGlyphs); |
268 | CTRunGetStringIndices(run, {0, runGlyphs}, indices); |
269 | } |
270 | |
271 | for (CFIndex k = 0; k < runGlyphs; ++k) { |
272 | buffer.positions[k] = { |
273 | buffer.point.fX + cgfloat_to_scalar(positions[k].x), |
274 | buffer.point.fY, |
275 | }; |
276 | if (buffer.offsets) { |
277 | buffer.offsets[k] = {0, 0}; // offset relative to the origin for this glyph |
278 | } |
279 | if (buffer.clusters) { |
280 | buffer.clusters[k] = indices[k]; |
281 | } |
282 | } |
283 | arena.reset(); |
284 | handler->commitRunBuffer(info); |
285 | } |
286 | handler->commitLine(); |
287 | } |
288 | } |
289 | |
290 | #else |
291 | std::unique_ptr<SkShaper> SkShaper::MakeCoreText() { |
292 | return nullptr; |
293 | } |
294 | #endif |
295 | |