blob: 3513edc745de472e94a8696fc0fad92f49d0c17c [file] [log] [blame]
reed@android.com0680d6c2008-12-19 19:46:15 +00001/*
reed@android.com8a1c16f2008-12-17 15:59:43 +00002 ** Copyright 2006, The Android Open Source Project
reed@android.com0680d6c2008-12-19 19:46:15 +00003 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 ** http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15*/
16
reed@android.comf13c6e12009-01-19 19:10:24 +000017#include <carbon/carbon.h>
reed@android.com0680d6c2008-12-19 19:46:15 +000018#include "SkFontHost.h"
19#include "SkDescriptor.h"
reed@android.com0bf64d42009-03-09 17:22:22 +000020#include "SkEndian.h"
reed@android.comf13c6e12009-01-19 19:10:24 +000021#include "SkPoint.h"
reed@android.com0680d6c2008-12-19 19:46:15 +000022
23// Give 1MB font cache budget
24#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
25
26const char* gDefaultfont = "Arial"; // hard code for now
27static SkMutex gFTMutex;
28
reed@android.com03ca3d12008-12-22 15:35:46 +000029static inline SkPoint F32PtToSkPoint(const Float32Point p) {
30 SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
reed@android.com0680d6c2008-12-19 19:46:15 +000031 return sp;
32}
33
reed@android.com03ca3d12008-12-22 15:35:46 +000034static inline uint32_t _rotl(uint32_t v, uint32_t r) {
reed@android.com0680d6c2008-12-19 19:46:15 +000035 return (v << r | v >> (32 - r));
36}
37
reed@android.com0680d6c2008-12-19 19:46:15 +000038class SkTypeface_Mac : public SkTypeface {
39public:
reed@android.comea446b92009-03-09 15:25:11 +000040 SkTypeface_Mac(SkTypeface::Style style, uint32_t id)
41 : SkTypeface(style, id) {}
reed@android.com0680d6c2008-12-19 19:46:15 +000042};
43
44#pragma mark -
45
reed@android.comea446b92009-03-09 15:25:11 +000046static uint32_t find_from_name(const char name[]) {
47 CFStringRef str = CFStringCreateWithCString(NULL, name,
48 kCFStringEncodingUTF8);
49 uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault);
50 CFRelease(str);
51 return fontID;
reed@android.com0680d6c2008-12-19 19:46:15 +000052}
53
reed@android.comea446b92009-03-09 15:25:11 +000054static uint32_t find_default_fontID() {
55 static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" };
reed@android.com0680d6c2008-12-19 19:46:15 +000056
reed@android.comea446b92009-03-09 15:25:11 +000057 uint32_t fontID;
58 for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) {
59 fontID = find_from_name(gDefaultNames[i]);
60 if (fontID) {
61 return fontID;
reed@android.com0680d6c2008-12-19 19:46:15 +000062 }
reed@android.com0680d6c2008-12-19 19:46:15 +000063 }
reed@android.comea446b92009-03-09 15:25:11 +000064 sk_throw();
65 return 0;
66}
67
68static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) {
69 uint32_t fontID = 0;
70 if (NULL != name) {
71 fontID = find_from_name(name);
72 }
73 if (0 == fontID) {
74 fontID = find_default_fontID();
75 }
76 // we lie (for now) and report that we found the exact style bits
77 return new SkTypeface_Mac(style, fontID);
reed@android.com0680d6c2008-12-19 19:46:15 +000078}
79
80#pragma mark -
81
82class SkScalerContext_Mac : public SkScalerContext {
83public:
84 SkScalerContext_Mac(const SkDescriptor* desc);
85 virtual ~SkScalerContext_Mac();
86
87protected:
88 virtual unsigned generateGlyphCount() const;
89 virtual uint16_t generateCharToGlyph(SkUnichar uni);
90 virtual void generateAdvance(SkGlyph* glyph);
91 virtual void generateMetrics(SkGlyph* glyph);
92 virtual void generateImage(const SkGlyph& glyph);
93 virtual void generatePath(const SkGlyph& glyph, SkPath* path);
reed@android.com0680d6c2008-12-19 19:46:15 +000094 virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
reed@android.com0680d6c2008-12-19 19:46:15 +000095
96private:
97 ATSUTextLayout fLayout;
98 ATSUStyle fStyle;
reed@android.com76aa34b2008-12-23 01:27:39 +000099 CGColorSpaceRef fGrayColorSpace;
reed@android.com0bf64d42009-03-09 17:22:22 +0000100 CGAffineTransform fTransform;
reed@android.com0680d6c2008-12-19 19:46:15 +0000101
102 static OSStatus MoveTo(const Float32Point *pt, void *cb);
103 static OSStatus Line(const Float32Point *pt, void *cb);
104 static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
105 static OSStatus Close(void *cb);
106};
107
108SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
reed@android.com0bf64d42009-03-09 17:22:22 +0000109 : SkScalerContext(desc), fLayout(0), fStyle(0)
110{
reed@android.com0680d6c2008-12-19 19:46:15 +0000111 SkAutoMutexAcquire ac(gFTMutex);
112 OSStatus err;
113
reed@android.comea446b92009-03-09 15:25:11 +0000114 err = ::ATSUCreateStyle(&fStyle);
115 SkASSERT(0 == err);
reed@android.com0bf64d42009-03-09 17:22:22 +0000116
117 SkMatrix m;
118 fRec.getSingleMatrix(&m);
119
120 fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]),
121 SkScalarToFloat(m[SkMatrix::kMSkewX]),
122 SkScalarToFloat(m[SkMatrix::kMSkewY]),
123 SkScalarToFloat(m[SkMatrix::kMScaleY]),
124 SkScalarToFloat(m[SkMatrix::kMTransX]),
125 SkScalarToFloat(m[SkMatrix::kMTransY]));
126
127
128 Fixed fixedSize = SK_Fixed1; //SkScalarToFixed(fRec.fTextSize);
129 static const ATSUAttributeTag tags[] = { kATSUSizeTag, kATSUFontMatrixTag };
130 static const ByteCount sizes[] = { sizeof(Fixed), sizeof(fTransform) };
131 const ATSUAttributeValuePtr values[] = { &fixedSize, &fTransform };
132 err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags),
133 tags, sizes, values);
reed@android.comea446b92009-03-09 15:25:11 +0000134 SkASSERT(0 == err);
reed@android.com0680d6c2008-12-19 19:46:15 +0000135
136 err = ::ATSUCreateTextLayout(&fLayout);
reed@android.comea446b92009-03-09 15:25:11 +0000137 SkASSERT(0 == err);
reed@android.com76aa34b2008-12-23 01:27:39 +0000138
139 fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
reed@android.com0680d6c2008-12-19 19:46:15 +0000140}
141
reed@android.comea446b92009-03-09 15:25:11 +0000142SkScalerContext_Mac::~SkScalerContext_Mac() {
reed@android.com76aa34b2008-12-23 01:27:39 +0000143 ::CGColorSpaceRelease(fGrayColorSpace);
reed@android.com0680d6c2008-12-19 19:46:15 +0000144 ::ATSUDisposeTextLayout(fLayout);
145 ::ATSUDisposeStyle(fStyle);
146}
147
reed@android.comea446b92009-03-09 15:25:11 +0000148unsigned SkScalerContext_Mac::generateGlyphCount() const {
reed@android.com0680d6c2008-12-19 19:46:15 +0000149 return 0xFFFF;
150}
151
152uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
153{
154 SkAutoMutexAcquire ac(gFTMutex);
155
156 OSStatus err;
157 UniChar achar = uni;
158 err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
159 err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
160
161 ATSLayoutRecord *layoutPtr;
162 ItemCount count;
163 ATSGlyphRef glyph;
164
165 err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
166 glyph = layoutPtr->glyphID;
167 ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
168 return glyph;
169}
170
reed@android.com76aa34b2008-12-23 01:27:39 +0000171static void set_glyph_metrics_on_error(SkGlyph* glyph) {
172 glyph->fRsbDelta = 0;
173 glyph->fLsbDelta = 0;
174 glyph->fWidth = 0;
175 glyph->fHeight = 0;
176 glyph->fTop = 0;
177 glyph->fLeft = 0;
178 glyph->fAdvanceX = 0;
179 glyph->fAdvanceY = 0;
180}
181
reed@android.com0680d6c2008-12-19 19:46:15 +0000182void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
183 this->generateMetrics(glyph);
184}
185
reed@android.com76aa34b2008-12-23 01:27:39 +0000186void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
187 GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount);
reed@android.com03ca3d12008-12-22 15:35:46 +0000188 ATSGlyphScreenMetrics metrics;
reed@android.com76aa34b2008-12-23 01:27:39 +0000189
190 OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
191 &metrics);
192 if (noErr != err) {
193 set_glyph_metrics_on_error(glyph);
194 } else {
reed@android.com0680d6c2008-12-19 19:46:15 +0000195 glyph->fAdvanceX = SkFloatToFixed(metrics.deviceAdvance.x);
reed@android.com03ca3d12008-12-22 15:35:46 +0000196 glyph->fAdvanceY = -SkFloatToFixed(metrics.deviceAdvance.y);
197 glyph->fWidth = metrics.width;
198 glyph->fHeight = metrics.height;
reed@android.com4df53b02008-12-22 22:12:55 +0000199 glyph->fLeft = sk_float_round2int(metrics.topLeft.x);
reed@android.com76aa34b2008-12-23 01:27:39 +0000200 glyph->fTop = -sk_float_round2int(metrics.topLeft.y);
reed@android.com0680d6c2008-12-19 19:46:15 +0000201 }
202}
203
reed@android.com0bf64d42009-03-09 17:22:22 +0000204static void convert_metrics(SkPaint::FontMetrics* dst,
205 const ATSFontMetrics& src) {
206 dst->fTop = -SkFloatToScalar(src.ascent);
207 dst->fAscent = -SkFloatToScalar(src.ascent);
208 dst->fDescent = SkFloatToScalar(src.descent);
209 dst->fBottom = SkFloatToScalar(src.descent);
210 dst->fLeading = SkFloatToScalar(src.leading);
211}
212
213static void* get_font_table(ATSFontRef fontID, uint32_t tag) {
214 ByteCount size;
215 OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size);
216 if (err) {
217 return NULL;
218 }
219 void* data = sk_malloc_throw(size);
220 err = ATSFontGetTable(fontID, tag, 0, size, data, &size);
221 if (err) {
222 sk_free(data);
223 data = NULL;
224 }
225 return data;
226}
227
228static int get_be16(const void* data, size_t offset) {
229 const char* ptr = reinterpret_cast<const char*>(data);
230 uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset);
231 int n = SkEndian_SwapBE16(value);
232 // now force it to be signed
233 return n << 16 >> 16;
234}
235
236#define SFNT_HEAD_UPEM_OFFSET 18
237#define SFNT_HEAD_YMIN_OFFSET 38
238#define SFNT_HEAD_YMAX_OFFSET 42
239#define SFNT_HEAD_STYLE_OFFSET 44
240
241#define SFNT_HHEA_ASCENT_OFFSET 4
242#define SFNT_HHEA_DESCENT_OFFSET 6
243#define SFNT_HHEA_LEADING_OFFSET 8
244
245static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) {
246 void* head = get_font_table(font, 'head');
247 if (NULL == head) {
248 return false;
249 }
250 void* hhea = get_font_table(font, 'hhea');
251 if (NULL == hhea) {
252 sk_free(head);
253 return false;
254 }
255
256 int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET);
257 int ys[5];
258
259 ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET);
260 ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET);
261 ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET);
262 ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET);
263 ys[4] = get_be16(hhea, SFNT_HHEA_LEADING_OFFSET);
264
265 // now do some cleanup, to ensure y[max,min] are really that
266 if (ys[0] > ys[1]) {
267 ys[0] = ys[1];
268 }
269 if (ys[3] < ys[2]) {
270 ys[3] = ys[2];
271 }
272
273 for (int i = 0; i < 5; i++) {
274 pts[i].set(0, SkIntToScalar(ys[i]) / upem);
275 }
276
277 sk_free(hhea);
278 sk_free(head);
279 return true;
280}
281
reed@android.com76aa34b2008-12-23 01:27:39 +0000282void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
283 SkPaint::FontMetrics* my) {
reed@android.com0bf64d42009-03-09 17:22:22 +0000284 SkPoint pts[5];
285
286 if (!init_vertical_metrics(fRec.fFontID, pts)) {
287 // these are not as accurate as init_vertical_metrics :(
288 ATSFontMetrics metrics;
289 ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault,
290 &metrics);
291 pts[0].set(0, -SkFloatToScalar(metrics.ascent));
292 pts[1].set(0, -SkFloatToScalar(metrics.ascent));
293 pts[2].set(0, -SkFloatToScalar(metrics.descent));
294 pts[3].set(0, -SkFloatToScalar(metrics.descent));
295 pts[4].set(0, SkFloatToScalar(metrics.leading)); //+ or -?
296 }
297
298 SkMatrix m;
299 fRec.getSingleMatrix(&m);
300 m.mapPoints(pts, 5);
301
302 if (mx) {
303 mx->fTop = pts[0].fX;
304 mx->fAscent = pts[1].fX;
305 mx->fDescent = pts[2].fX;
306 mx->fBottom = pts[3].fX;
307 mx->fLeading = pts[4].fX;
308 }
309 if (my) {
310 my->fTop = pts[0].fY;
311 my->fAscent = pts[1].fY;
312 my->fDescent = pts[2].fY;
313 my->fBottom = pts[3].fY;
314 my->fLeading = pts[4].fY;
315 }
reed@android.com0680d6c2008-12-19 19:46:15 +0000316}
317
318void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
319{
320 SkAutoMutexAcquire ac(gFTMutex);
reed@android.com0680d6c2008-12-19 19:46:15 +0000321 SkASSERT(fLayout);
reed@android.com0680d6c2008-12-19 19:46:15 +0000322
reed@android.com76aa34b2008-12-23 01:27:39 +0000323 bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
324 CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage,
325 glyph.fWidth, glyph.fHeight, 8,
326 glyph.rowBytes(), fGrayColorSpace,
327 kCGImageAlphaNone);
reed@android.com0680d6c2008-12-19 19:46:15 +0000328 if (!contextRef) {
329 SkASSERT(false);
330 return;
331 }
reed@android.com76aa34b2008-12-23 01:27:39 +0000332
reed@android.com0680d6c2008-12-19 19:46:15 +0000333 ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
reed@android.com0680d6c2008-12-19 19:46:15 +0000334 ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
335
reed@android.com60dfdbc2009-03-09 14:51:39 +0000336 CGGlyph glyphID = glyph.getGlyphID();
337 CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID);
338 CGContextSetFont(contextRef, fontRef);
reed@android.com0bf64d42009-03-09 17:22:22 +0000339 CGContextSetFontSize(contextRef, 1); //SkScalarToFloat(fRec.fTextSize));
340 CGContextSetTextMatrix(contextRef, fTransform);
reed@android.com60dfdbc2009-03-09 14:51:39 +0000341 CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft,
342 glyph.fTop + glyph.fHeight, &glyphID, 1);
reed@android.comea446b92009-03-09 15:25:11 +0000343
reed@android.com0680d6c2008-12-19 19:46:15 +0000344 ::CGContextRelease(contextRef);
345}
346
347void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
348{
349 SkAutoMutexAcquire ac(gFTMutex);
350 OSStatus err,result;
351
352 err = ::ATSUGlyphGetCubicPaths(
353 fStyle,glyph.fID,
354 &SkScalerContext_Mac::MoveTo,
355 &SkScalerContext_Mac::Line,
356 &SkScalerContext_Mac::Curve,
357 &SkScalerContext_Mac::Close,
358 path,&result);
359 SkASSERT(err == noErr);
360}
361
reed@android.com0680d6c2008-12-19 19:46:15 +0000362OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
363{
364 reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
365 return noErr;
366}
367
368OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
369{
370 reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
371 return noErr;
372}
373
reed@android.com03ca3d12008-12-22 15:35:46 +0000374OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1,
375 const Float32Point *pt2,
376 const Float32Point *pt3, void *cb)
reed@android.com0680d6c2008-12-19 19:46:15 +0000377{
reed@android.com03ca3d12008-12-22 15:35:46 +0000378 reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),
379 F32PtToSkPoint(*pt2),
380 F32PtToSkPoint(*pt3));
reed@android.com0680d6c2008-12-19 19:46:15 +0000381 return noErr;
382}
383
384OSStatus SkScalerContext_Mac::Close(void *cb)
385{
386 reinterpret_cast<SkPath*>(cb)->close();
387 return noErr;
388}
389
390#pragma mark -
391
392void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
393 SkASSERT(!"SkFontHost::Serialize unimplemented");
394}
395
396SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
397 SkASSERT(!"SkFontHost::Deserialize unimplemented");
398 return NULL;
399}
400
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000401SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
reed@android.comea446b92009-03-09 15:25:11 +0000402 return NULL;
reed@android.com0680d6c2008-12-19 19:46:15 +0000403}
404
reed@android.com03ca3d12008-12-22 15:35:46 +0000405SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
reed@android.comea446b92009-03-09 15:25:11 +0000406 return NULL;
reed@android.com03ca3d12008-12-22 15:35:46 +0000407}
408
reed@android.comea446b92009-03-09 15:25:11 +0000409SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
reed@android.com0680d6c2008-12-19 19:46:15 +0000410 return new SkScalerContext_Mac(desc);
411}
412
413SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)
414{
reed@android.comea446b92009-03-09 15:25:11 +0000415 SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
reed@android.com0680d6c2008-12-19 19:46:15 +0000416 SkDescriptor* desc = ad.getDesc();
reed@android.comea446b92009-03-09 15:25:11 +0000417
reed@android.com0680d6c2008-12-19 19:46:15 +0000418 desc->init();
419 SkScalerContext::Rec* newRec =
reed@android.comea446b92009-03-09 15:25:11 +0000420 (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
421 sizeof(rec), &rec);
422 newRec->fFontID = find_default_fontID();
reed@android.com0680d6c2008-12-19 19:46:15 +0000423 desc->computeChecksum();
reed@android.comea446b92009-03-09 15:25:11 +0000424
reed@android.com0680d6c2008-12-19 19:46:15 +0000425 return SkFontHost::CreateScalerContext(desc);
426}
427
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000428SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
429 const char familyName[], SkTypeface::Style style) {
reed@android.comea446b92009-03-09 15:25:11 +0000430 // todo: we don't know how to respect style bits
431 if (NULL == familyName && NULL != familyFace) {
432 familyFace->ref();
433 return const_cast<SkTypeface*>(familyFace);
434 } else {
435 return CreateTypeface_(familyName, style);
reed@android.com0680d6c2008-12-19 19:46:15 +0000436 }
reed@android.com0680d6c2008-12-19 19:46:15 +0000437}
438
439size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
440 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
441 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
442 else
443 return 0; // nothing to do
444}
445
446int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
447 return 0;
448}
449
450void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
451 tables[0] = NULL; // black gamma (e.g. exp=1.4)
452 tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
453}
454