blob: be7c3c8fb2e0a729a04982021a9a7754ad8e2413 [file] [log] [blame]
Mike Reedb2bf28c2020-01-28 10:52:05 -05001/*
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
Mike Reedb2bf28c2020-01-28 10:52:05 -050010#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
Mike Reed04da7462020-01-28 16:14:14 -050021#include "include/ports/SkTypeface_mac.h"
Kevin Lubick46572b42023-01-18 13:11:06 -050022#include "include/private/base/SkTemplates.h"
Kevin Lubick1b3aa8b2023-01-19 14:03:31 -050023#include "src/base/SkUTF.h"
Adlai Hollerf54dbf72020-06-08 14:22:40 -040024#include "src/utils/mac/SkCGBase.h"
25#include "src/utils/mac/SkUniqueCFRef.h"
Mike Reed04da7462020-01-28 16:14:14 -050026
27#include <vector>
Martin Vejdarskicd2e1482020-11-07 13:18:04 +010028#include <utility>
Mike Reedb2bf28c2020-01-28 10:52:05 -050029
Herb Derbya183f792023-01-18 17:05:36 -050030using namespace skia_private;
31
Mike Reedb2bf28c2020-01-28 10:52:05 -050032class SkShaper_CoreText : public SkShaper {
33public:
34 SkShaper_CoreText() {}
35private:
36 void shape(const char* utf8, size_t utf8Bytes,
37 const SkFont& srcFont,
38 bool leftToRight,
39 SkScalar width,
40 RunHandler*) const override;
41
42 void shape(const char* utf8, size_t utf8Bytes,
43 FontRunIterator&,
44 BiDiRunIterator&,
45 ScriptRunIterator&,
46 LanguageRunIterator&,
47 SkScalar width,
48 RunHandler*) const override;
49
50 void shape(const char* utf8, size_t utf8Bytes,
51 FontRunIterator&,
52 BiDiRunIterator&,
53 ScriptRunIterator&,
54 LanguageRunIterator&,
55 const Feature*, size_t featureSize,
56 SkScalar width,
57 RunHandler*) const override;
58};
59
60std::unique_ptr<SkShaper> SkShaper::MakeCoreText() {
61 return std::make_unique<SkShaper_CoreText>();
62}
63
64void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
65 FontRunIterator& font,
66 BiDiRunIterator& bidi,
67 ScriptRunIterator&,
68 LanguageRunIterator&,
69 SkScalar width,
70 RunHandler* handler) const
71{
72 SkFont skfont;
73 if (!font.atEnd()) {
74 font.consume();
75 skfont = font.currentFont();
76 } else {
77 skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault()));
78 }
79 SkASSERT(skfont.getTypeface());
80 bool skbidi = 0;
81 if (!bidi.atEnd()) {
82 bidi.consume();
83 skbidi = (bidi.currentLevel() % 2) == 0;
84 }
85 return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
86}
87
88void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
89 FontRunIterator& font,
90 BiDiRunIterator& bidi,
91 ScriptRunIterator&,
92 LanguageRunIterator&,
93 const Feature*, size_t,
94 SkScalar width,
95 RunHandler* handler) const {
96 font.consume();
97 SkASSERT(font.currentFont().getTypeface());
98 bidi.consume();
99 return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
100 width, handler);
101}
102
Mike Reedb2bf28c2020-01-28 10:52:05 -0500103// CTFramesetter/CTFrame can do this, but require version 10.14
104class LineBreakIter {
105 CTTypesetterRef fTypesetter;
106 double fWidth;
107 CFIndex fStart;
108
109public:
110 LineBreakIter(CTTypesetterRef ts, SkScalar width) : fTypesetter(ts), fWidth(width) {
111 fStart = 0;
112 }
113
Ben Wagner8c9830b2020-08-04 13:54:03 -0400114 SkUniqueCFRef<CTLineRef> nextLine() {
115 CFRange stringRange {fStart, CTTypesetterSuggestLineBreak(fTypesetter, fStart, fWidth)};
116 if (stringRange.length == 0) {
Mike Reedb2bf28c2020-01-28 10:52:05 -0500117 return nullptr;
118 }
Ben Wagner8c9830b2020-08-04 13:54:03 -0400119 fStart += stringRange.length;
120 return SkUniqueCFRef<CTLineRef>(CTTypesetterCreateLine(fTypesetter, stringRange));
Mike Reedb2bf28c2020-01-28 10:52:05 -0500121 }
122};
123
Mike Reed04da7462020-01-28 16:14:14 -0500124static void dict_add_double(CFMutableDictionaryRef d, const void* name, double value) {
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400125 SkUniqueCFRef<CFNumberRef> number(
126 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
Mike Reed04da7462020-01-28 16:14:14 -0500127 CFDictionaryAddValue(d, name, number.get());
128}
129
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400130static SkUniqueCFRef<CTFontRef> create_ctfont_from_font(const SkFont& font) {
Mike Reeda2a0c8a2020-01-28 19:40:14 -0500131 auto typeface = font.getTypefaceOrDefault();
132 auto ctfont = SkTypeface_GetCTFontRef(typeface);
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400133 return SkUniqueCFRef<CTFontRef>(
134 CTFontCreateCopyWithAttributes(ctfont, font.getSize(), nullptr, nullptr));
Mike Reeda2a0c8a2020-01-28 19:40:14 -0500135}
136
137static SkFont run_to_font(CTRunRef run, const SkFont& orig) {
138 CFDictionaryRef attr = CTRunGetAttributes(run);
139 CTFontRef ct = (CTFontRef)CFDictionaryGetValue(attr, kCTFontAttributeName);
140 if (!ct) {
141 SkDebugf("no ctfont in Run Attributes\n");
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400142 CFShow(attr);
Mike Reeda2a0c8a2020-01-28 19:40:14 -0500143 return orig;
144 }
145 // Do I need to add a local cache, or allow the caller to manage this lookup?
146 SkFont font(orig);
147 font.setTypeface(SkMakeTypefaceFromCTFont(ct));
148 return font;
149}
150
Martin Vejdarskicd2e1482020-11-07 13:18:04 +0100151namespace {
152class UTF16ToUTF8IndicesMap {
153public:
154 /** Builds a UTF-16 to UTF-8 indices map; the text is not retained
155 * @return true if successful
156 */
157 bool setUTF8(const char* utf8, size_t size) {
158 SkASSERT(utf8 != nullptr);
159
160 if (!SkTFitsIn<int32_t>(size)) {
161 SkDEBUGF("UTF16ToUTF8IndicesMap: text too long");
162 return false;
163 }
164
165 auto utf16Size = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, size);
166 if (utf16Size < 0) {
167 SkDEBUGF("UTF16ToUTF8IndicesMap: Invalid utf8 input");
168 return false;
169 }
170
171 // utf16Size+1 to also store the size
172 fUtf16ToUtf8Indices = std::vector<size_t>(utf16Size + 1);
173 auto utf16 = fUtf16ToUtf8Indices.begin();
174 auto utf8Begin = utf8, utf8End = utf8 + size;
175 while (utf8Begin < utf8End) {
176 *utf16 = utf8Begin - utf8;
177 utf16 += SkUTF::ToUTF16(SkUTF::NextUTF8(&utf8Begin, utf8End), nullptr);
178 }
179 *utf16 = size;
180
181 return true;
182 }
183
184 size_t mapIndex(size_t index) const {
185 SkASSERT(index < fUtf16ToUtf8Indices.size());
186 return fUtf16ToUtf8Indices[index];
187 }
188
189 std::pair<size_t, size_t> mapRange(size_t start, size_t size) const {
190 auto utf8Start = mapIndex(start);
191 return {utf8Start, mapIndex(start + size) - utf8Start};
192 }
193private:
194 std::vector<size_t> fUtf16ToUtf8Indices;
195};
196} // namespace
197
Mike Reed04da7462020-01-28 16:14:14 -0500198// kCTTrackingAttributeName not available until 10.12
199const CFStringRef kCTTracking_AttributeName = CFSTR("CTTracking");
200
Mike Reedb2bf28c2020-01-28 10:52:05 -0500201void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
202 const SkFont& font,
203 bool /* leftToRight */,
204 SkScalar width,
205 RunHandler* handler) const {
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400206 SkUniqueCFRef<CFStringRef> textString(
207 CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)utf8, utf8Bytes,
208 kCFStringEncodingUTF8, false));
Mike Reedb2bf28c2020-01-28 10:52:05 -0500209
Martin Vejdarskicd2e1482020-11-07 13:18:04 +0100210 UTF16ToUTF8IndicesMap utf8IndicesMap;
211 if (!utf8IndicesMap.setUTF8(utf8, utf8Bytes)) {
212 return;
213 }
214
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400215 SkUniqueCFRef<CTFontRef> ctfont = create_ctfont_from_font(font);
Mike Reedb2bf28c2020-01-28 10:52:05 -0500216
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400217 SkUniqueCFRef<CFMutableDictionaryRef> attr(
Mike Reedb2bf28c2020-01-28 10:52:05 -0500218 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
219 &kCFTypeDictionaryKeyCallBacks,
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400220 &kCFTypeDictionaryValueCallBacks));
Mike Reeda2a0c8a2020-01-28 19:40:14 -0500221 CFDictionaryAddValue(attr.get(), kCTFontAttributeName, ctfont.get());
Mike Reed04da7462020-01-28 16:14:14 -0500222 if (false) {
223 // trying to see what these affect
224 dict_add_double(attr.get(), kCTTracking_AttributeName, 1);
225 dict_add_double(attr.get(), kCTKernAttributeName, 0.0);
226 }
Mike Reedb2bf28c2020-01-28 10:52:05 -0500227
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400228 SkUniqueCFRef<CFAttributedStringRef> attrString(
229 CFAttributedStringCreate(kCFAllocatorDefault, textString.get(), attr.get()));
Mike Reedb2bf28c2020-01-28 10:52:05 -0500230
Adlai Hollerf54dbf72020-06-08 14:22:40 -0400231 SkUniqueCFRef<CTTypesetterRef> typesetter(
232 CTTypesetterCreateWithAttributedString(attrString.get()));
Mike Reed04da7462020-01-28 16:14:14 -0500233
Mike Reedb2bf28c2020-01-28 10:52:05 -0500234 // We have to compute RunInfos in a loop, and then reuse them in a 2nd loop,
235 // so we store them in an array (we reuse the array's storage for each line).
Mike Reed0cbd5872020-05-05 10:59:43 -0400236 std::vector<SkFont> fontStorage;
Mike Reedb2bf28c2020-01-28 10:52:05 -0500237 std::vector<SkShaper::RunHandler::RunInfo> infos;
238
Mike Reed04da7462020-01-28 16:14:14 -0500239 LineBreakIter iter(typesetter.get(), width);
Ben Wagner8c9830b2020-08-04 13:54:03 -0400240 while (SkUniqueCFRef<CTLineRef> line = iter.nextLine()) {
241 CFArrayRef run_array = CTLineGetGlyphRuns(line.get());
Mike Reedb2bf28c2020-01-28 10:52:05 -0500242 CFIndex runCount = CFArrayGetCount(run_array);
243 if (runCount == 0) {
244 continue;
245 }
246 handler->beginLine();
Mike Reed0cbd5872020-05-05 10:59:43 -0400247 fontStorage.clear();
Martin Vejdarskicd2e1482020-11-07 13:18:04 +0100248 fontStorage.reserve(runCount); // ensure the refs won't get invalidated
Mike Reedb2bf28c2020-01-28 10:52:05 -0500249 infos.clear();
250 for (CFIndex j = 0; j < runCount; ++j) {
251 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
252 CFIndex runGlyphs = CTRunGetGlyphCount(run);
253
254 SkASSERT(sizeof(CGGlyph) == sizeof(uint16_t));
255
Herb Derbya183f792023-01-18 17:05:36 -0500256 AutoSTArray<4096, CGSize> advances(runGlyphs);
Ben Wagner83eed352021-03-05 11:26:35 -0500257 CTRunGetAdvances(run, {0, runGlyphs}, advances.data());
Mike Reedb2bf28c2020-01-28 10:52:05 -0500258 SkScalar adv = 0;
259 for (CFIndex k = 0; k < runGlyphs; ++k) {
260 adv += advances[k].width;
261 }
Mike Reed04da7462020-01-28 16:14:14 -0500262
Martin Vejdarskicd2e1482020-11-07 13:18:04 +0100263 CFRange cfRange = CTRunGetStringRange(run);
264 auto range = utf8IndicesMap.mapRange(cfRange.location, cfRange.length);
Mike Reedb2bf28c2020-01-28 10:52:05 -0500265
Mike Reed0cbd5872020-05-05 10:59:43 -0400266 fontStorage.push_back(run_to_font(run, font));
Mike Reedb2bf28c2020-01-28 10:52:05 -0500267 infos.push_back({
Mike Reed0cbd5872020-05-05 10:59:43 -0400268 fontStorage.back(), // info just stores a ref to the font
269 0, // need fBidiLevel
Mike Reed04da7462020-01-28 16:14:14 -0500270 {adv, 0},
Mike Reedb2bf28c2020-01-28 10:52:05 -0500271 (size_t)runGlyphs,
Martin Vejdarskicd2e1482020-11-07 13:18:04 +0100272 {range.first, range.second},
Mike Reedb2bf28c2020-01-28 10:52:05 -0500273 });
274 handler->runInfo(infos.back());
275 }
276 handler->commitRunInfo();
277
278 // Now loop through again and fill in the buffers
Martin Vejdarskicd2e1482020-11-07 13:18:04 +0100279 SkScalar lineAdvance = 0;
Mike Reedb2bf28c2020-01-28 10:52:05 -0500280 for (CFIndex j = 0; j < runCount; ++j) {
281 const auto& info = infos[j];
282 auto buffer = handler->runBuffer(info);
283
284 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
285 CFIndex runGlyphs = info.glyphCount;
286 SkASSERT(CTRunGetGlyphCount(run) == (CFIndex)info.glyphCount);
287
288 CTRunGetGlyphs(run, {0, runGlyphs}, buffer.glyphs);
Mike Reed04da7462020-01-28 16:14:14 -0500289
Herb Derbya183f792023-01-18 17:05:36 -0500290 AutoSTArray<4096, CGPoint> positions(runGlyphs);
Ben Wagner83eed352021-03-05 11:26:35 -0500291 CTRunGetPositions(run, {0, runGlyphs}, positions.data());
Herb Derbya183f792023-01-18 17:05:36 -0500292 AutoSTArray<4096, CFIndex> indices;
Mike Reedb2bf28c2020-01-28 10:52:05 -0500293 if (buffer.clusters) {
Ben Wagner83eed352021-03-05 11:26:35 -0500294 indices.reset(runGlyphs);
295 CTRunGetStringIndices(run, {0, runGlyphs}, indices.data());
Mike Reedb2bf28c2020-01-28 10:52:05 -0500296 }
297
298 for (CFIndex k = 0; k < runGlyphs; ++k) {
299 buffer.positions[k] = {
Martin Vejdarskicd2e1482020-11-07 13:18:04 +0100300 buffer.point.fX + SkScalarFromCGFloat(positions[k].x) - lineAdvance,
Mike Reedb2bf28c2020-01-28 10:52:05 -0500301 buffer.point.fY,
302 };
303 if (buffer.offsets) {
Mike Reedf4a90672020-01-29 13:00:50 -0500304 buffer.offsets[k] = {0, 0}; // offset relative to the origin for this glyph
Mike Reedb2bf28c2020-01-28 10:52:05 -0500305 }
306 if (buffer.clusters) {
Martin Vejdarskicd2e1482020-11-07 13:18:04 +0100307 buffer.clusters[k] = utf8IndicesMap.mapIndex(indices[k]);
Mike Reedb2bf28c2020-01-28 10:52:05 -0500308 }
309 }
Mike Reedb2bf28c2020-01-28 10:52:05 -0500310 handler->commitRunBuffer(info);
Martin Vejdarskicd2e1482020-11-07 13:18:04 +0100311 lineAdvance += info.fAdvance.fX;
Mike Reedb2bf28c2020-01-28 10:52:05 -0500312 }
313 handler->commitLine();
314 }
315}