|
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "ScaledFontMac.h" |
|
7 #ifdef USE_SKIA |
|
8 #include "PathSkia.h" |
|
9 #include "skia/SkPaint.h" |
|
10 #include "skia/SkPath.h" |
|
11 #include "skia/SkTypeface_mac.h" |
|
12 #endif |
|
13 #include "DrawTargetCG.h" |
|
14 #include <vector> |
|
15 #include <dlfcn.h> |
|
16 |
|
17 // prototype for private API |
|
18 extern "C" { |
|
19 CGPathRef CGFontGetGlyphPath(CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph); |
|
20 }; |
|
21 |
|
22 |
|
23 namespace mozilla { |
|
24 namespace gfx { |
|
25 |
|
26 ScaledFontMac::CTFontDrawGlyphsFuncT* ScaledFontMac::CTFontDrawGlyphsPtr = nullptr; |
|
27 bool ScaledFontMac::sSymbolLookupDone = false; |
|
28 |
|
29 ScaledFontMac::ScaledFontMac(CGFontRef aFont, Float aSize) |
|
30 : ScaledFontBase(aSize) |
|
31 { |
|
32 if (!sSymbolLookupDone) { |
|
33 CTFontDrawGlyphsPtr = |
|
34 (CTFontDrawGlyphsFuncT*)dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs"); |
|
35 sSymbolLookupDone = true; |
|
36 } |
|
37 |
|
38 // XXX: should we be taking a reference |
|
39 mFont = CGFontRetain(aFont); |
|
40 if (CTFontDrawGlyphsPtr != nullptr) { |
|
41 // only create mCTFont if we're going to be using the CTFontDrawGlyphs API |
|
42 mCTFont = CTFontCreateWithGraphicsFont(aFont, aSize, nullptr, nullptr); |
|
43 } else { |
|
44 mCTFont = nullptr; |
|
45 } |
|
46 } |
|
47 |
|
48 ScaledFontMac::~ScaledFontMac() |
|
49 { |
|
50 if (mCTFont) { |
|
51 CFRelease(mCTFont); |
|
52 } |
|
53 CGFontRelease(mFont); |
|
54 } |
|
55 |
|
56 #ifdef USE_SKIA |
|
57 SkTypeface* ScaledFontMac::GetSkTypeface() |
|
58 { |
|
59 if (!mTypeface) { |
|
60 if (mCTFont) { |
|
61 mTypeface = SkCreateTypefaceFromCTFont(mCTFont); |
|
62 } else { |
|
63 CTFontRef fontFace = CTFontCreateWithGraphicsFont(mFont, mSize, nullptr, nullptr); |
|
64 mTypeface = SkCreateTypefaceFromCTFont(fontFace); |
|
65 CFRelease(fontFace); |
|
66 } |
|
67 } |
|
68 return mTypeface; |
|
69 } |
|
70 #endif |
|
71 |
|
72 // private API here are the public options on OS X |
|
73 // CTFontCreatePathForGlyph |
|
74 // ATSUGlyphGetCubicPaths |
|
75 // we've used this in cairo sucessfully for some time. |
|
76 // Note: cairo dlsyms it. We could do that but maybe it's |
|
77 // safe just to use? |
|
78 |
|
79 TemporaryRef<Path> |
|
80 ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) |
|
81 { |
|
82 if (aTarget->GetType() == BackendType::COREGRAPHICS || aTarget->GetType() == BackendType::COREGRAPHICS_ACCELERATED) { |
|
83 CGMutablePathRef path = CGPathCreateMutable(); |
|
84 |
|
85 for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { |
|
86 // XXX: we could probably fold both of these transforms together to avoid extra work |
|
87 CGAffineTransform flip = CGAffineTransformMakeScale(1, -1); |
|
88 CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex); |
|
89 |
|
90 CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize, |
|
91 aBuffer.mGlyphs[i].mPosition.x, |
|
92 aBuffer.mGlyphs[i].mPosition.y); |
|
93 CGPathAddPath(path, &matrix, glyphPath); |
|
94 CGPathRelease(glyphPath); |
|
95 } |
|
96 TemporaryRef<Path> ret = new PathCG(path, FillRule::FILL_WINDING); |
|
97 CGPathRelease(path); |
|
98 return ret; |
|
99 } else { |
|
100 return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget); |
|
101 } |
|
102 } |
|
103 |
|
104 void |
|
105 ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) |
|
106 { |
|
107 if (!(aBackendType == BackendType::COREGRAPHICS || aBackendType == BackendType::COREGRAPHICS_ACCELERATED)) { |
|
108 ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint); |
|
109 return; |
|
110 } |
|
111 |
|
112 PathBuilderCG *pathBuilderCG = |
|
113 static_cast<PathBuilderCG*>(aBuilder); |
|
114 // XXX: check builder type |
|
115 for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { |
|
116 // XXX: we could probably fold both of these transforms together to avoid extra work |
|
117 CGAffineTransform flip = CGAffineTransformMakeScale(1, -1); |
|
118 CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex); |
|
119 |
|
120 CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize, |
|
121 aBuffer.mGlyphs[i].mPosition.x, |
|
122 aBuffer.mGlyphs[i].mPosition.y); |
|
123 CGPathAddPath(pathBuilderCG->mCGPath, &matrix, glyphPath); |
|
124 CGPathRelease(glyphPath); |
|
125 } |
|
126 } |
|
127 |
|
128 uint32_t |
|
129 CalcTableChecksum(const uint32_t *tableStart, uint32_t length, bool skipChecksumAdjust = false) |
|
130 { |
|
131 uint32_t sum = 0L; |
|
132 const uint32_t *table = tableStart; |
|
133 const uint32_t *end = table+((length+3) & ~3) / sizeof(uint32_t); |
|
134 while (table < end) { |
|
135 if (skipChecksumAdjust && (table - tableStart) == 2) { |
|
136 table++; |
|
137 } else { |
|
138 sum += CFSwapInt32BigToHost(*table++); |
|
139 } |
|
140 } |
|
141 return sum; |
|
142 } |
|
143 |
|
144 struct TableRecord { |
|
145 uint32_t tag; |
|
146 uint32_t checkSum; |
|
147 uint32_t offset; |
|
148 uint32_t length; |
|
149 CFDataRef data; |
|
150 }; |
|
151 |
|
152 int maxPow2LessThan(int a) |
|
153 { |
|
154 int x = 1; |
|
155 int shift = 0; |
|
156 while ((x<<(shift+1)) < a) { |
|
157 shift++; |
|
158 } |
|
159 return shift; |
|
160 } |
|
161 |
|
162 struct writeBuf |
|
163 { |
|
164 writeBuf(int size) |
|
165 { |
|
166 this->data = new unsigned char [size]; |
|
167 this->offset = 0; |
|
168 } |
|
169 ~writeBuf() { |
|
170 delete this->data; |
|
171 } |
|
172 |
|
173 template <class T> |
|
174 void writeElement(T a) |
|
175 { |
|
176 *reinterpret_cast<T*>(&this->data[this->offset]) = a; |
|
177 this->offset += sizeof(T); |
|
178 } |
|
179 |
|
180 void writeMem(const void *data, unsigned long length) |
|
181 { |
|
182 memcpy(&this->data[this->offset], data, length); |
|
183 this->offset += length; |
|
184 } |
|
185 |
|
186 void align() |
|
187 { |
|
188 while (this->offset & 3) { |
|
189 this->data[this->offset] = 0; |
|
190 this->offset++; |
|
191 } |
|
192 } |
|
193 |
|
194 unsigned char *data; |
|
195 int offset; |
|
196 }; |
|
197 |
|
198 bool |
|
199 ScaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) |
|
200 { |
|
201 // We'll reconstruct a TTF font from the tables we can get from the CGFont |
|
202 CFArrayRef tags = CGFontCopyTableTags(mFont); |
|
203 CFIndex count = CFArrayGetCount(tags); |
|
204 |
|
205 TableRecord *records = new TableRecord[count]; |
|
206 uint32_t offset = 0; |
|
207 offset += sizeof(uint32_t)*3; |
|
208 offset += sizeof(uint32_t)*4*count; |
|
209 bool CFF = false; |
|
210 for (CFIndex i = 0; i<count; i++) { |
|
211 uint32_t tag = (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags, i); |
|
212 if (tag == 0x43464620) // 'CFF ' |
|
213 CFF = true; |
|
214 CFDataRef data = CGFontCopyTableForTag(mFont, tag); |
|
215 records[i].tag = tag; |
|
216 records[i].offset = offset; |
|
217 records[i].data = data; |
|
218 records[i].length = CFDataGetLength(data); |
|
219 bool skipChecksumAdjust = (tag == 0x68656164); // 'head' |
|
220 records[i].checkSum = CalcTableChecksum(reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data)), |
|
221 records[i].length, skipChecksumAdjust); |
|
222 offset += records[i].length; |
|
223 // 32 bit align the tables |
|
224 offset = (offset + 3) & ~3; |
|
225 } |
|
226 CFRelease(tags); |
|
227 |
|
228 struct writeBuf buf(offset); |
|
229 // write header/offset table |
|
230 if (CFF) { |
|
231 buf.writeElement(CFSwapInt32HostToBig(0x4f54544f)); |
|
232 } else { |
|
233 buf.writeElement(CFSwapInt32HostToBig(0x00010000)); |
|
234 } |
|
235 buf.writeElement(CFSwapInt16HostToBig(count)); |
|
236 buf.writeElement(CFSwapInt16HostToBig((1<<maxPow2LessThan(count))*16)); |
|
237 buf.writeElement(CFSwapInt16HostToBig(maxPow2LessThan(count))); |
|
238 buf.writeElement(CFSwapInt16HostToBig(count*16-((1<<maxPow2LessThan(count))*16))); |
|
239 |
|
240 // write table record entries |
|
241 for (CFIndex i = 0; i<count; i++) { |
|
242 buf.writeElement(CFSwapInt32HostToBig(records[i].tag)); |
|
243 buf.writeElement(CFSwapInt32HostToBig(records[i].checkSum)); |
|
244 buf.writeElement(CFSwapInt32HostToBig(records[i].offset)); |
|
245 buf.writeElement(CFSwapInt32HostToBig(records[i].length)); |
|
246 } |
|
247 |
|
248 // write tables |
|
249 int checkSumAdjustmentOffset = 0; |
|
250 for (CFIndex i = 0; i<count; i++) { |
|
251 if (records[i].tag == 0x68656164) { |
|
252 checkSumAdjustmentOffset = buf.offset + 2*4; |
|
253 } |
|
254 buf.writeMem(CFDataGetBytePtr(records[i].data), CFDataGetLength(records[i].data)); |
|
255 buf.align(); |
|
256 CFRelease(records[i].data); |
|
257 } |
|
258 delete[] records; |
|
259 |
|
260 // clear the checksumAdjust field before checksumming the whole font |
|
261 memset(&buf.data[checkSumAdjustmentOffset], 0, sizeof(uint32_t)); |
|
262 uint32_t fontChecksum = CFSwapInt32HostToBig(0xb1b0afba - CalcTableChecksum(reinterpret_cast<const uint32_t*>(buf.data), offset)); |
|
263 // set checkSumAdjust to the computed checksum |
|
264 memcpy(&buf.data[checkSumAdjustmentOffset], &fontChecksum, sizeof(fontChecksum)); |
|
265 |
|
266 // we always use an index of 0 |
|
267 aDataCallback(buf.data, buf.offset, 0, mSize, aBaton); |
|
268 |
|
269 return true; |
|
270 |
|
271 } |
|
272 |
|
273 } |
|
274 } |