1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include <Carbon/Carbon.h>
11#include "SkFontHost.h"
12#include "SkDescriptor.h"
13#include "SkEndian.h"
14#include "SkFloatingPoint.h"
15#include "SkPaint.h"
16#include "SkPoint.h"
17
18const char* gDefaultfont = "Arial"; // hard code for now
19SK_DECLARE_STATIC_MUTEX(gFTMutex);
20
21static inline SkPoint F32PtToSkPoint(const Float32Point p) {
22    SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
23    return sp;
24}
25
26static inline uint32_t _rotl(uint32_t v, uint32_t r) {
27    return (v << r | v >> (32 - r));
28}
29
30class SkTypeface_Mac : public SkTypeface {
31public:
32    SkTypeface_Mac(SkTypeface::Style style, uint32_t id)
33        : SkTypeface(style, id) {}
34};
35
36#pragma mark -
37
38static uint32_t find_from_name(const char name[]) {
39    CFStringRef str = CFStringCreateWithCString(NULL, name,
40                                                kCFStringEncodingUTF8);
41    uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault);
42    CFRelease(str);
43    return fontID;
44}
45
46static uint32_t find_default_fontID() {
47    static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" };
48
49    uint32_t fontID;
50    for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) {
51        fontID = find_from_name(gDefaultNames[i]);
52        if (fontID) {
53            return fontID;
54        }
55    }
56    sk_throw();
57    return 0;
58}
59
60static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) {
61    uint32_t fontID = 0;
62    if (NULL != name) {
63        fontID = find_from_name(name);
64    }
65    if (0 == fontID) {
66        fontID = find_default_fontID();
67    }
68    // we lie (for now) and report that we found the exact style bits
69    return new SkTypeface_Mac(style, fontID);
70}
71
72#pragma mark -
73
74class SkScalerContext_Mac : public SkScalerContext {
75public:
76    SkScalerContext_Mac(const SkDescriptor* desc);
77    virtual ~SkScalerContext_Mac();
78
79protected:
80    virtual unsigned generateGlyphCount();
81    virtual uint16_t generateCharToGlyph(SkUnichar uni);
82    virtual void generateAdvance(SkGlyph* glyph);
83    virtual void generateMetrics(SkGlyph* glyph);
84    virtual void generateImage(const SkGlyph& glyph);
85    virtual void generatePath(const SkGlyph& glyph, SkPath* path);
86    virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
87
88private:
89    ATSUTextLayout  fLayout;
90    ATSUStyle       fStyle;
91    CGColorSpaceRef fGrayColorSpace;
92    CGAffineTransform   fTransform;
93
94    static OSStatus MoveTo(const Float32Point *pt, void *cb);
95    static OSStatus Line(const Float32Point *pt, void *cb);
96    static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
97    static OSStatus Close(void *cb);
98};
99
100void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
101    // we only support 2 levels of hinting
102    SkPaint::Hinting h = rec->getHinting();
103    if (SkPaint::kSlight_Hinting == h) {
104        h = SkPaint::kNo_Hinting;
105    } else if (SkPaint::kFull_Hinting == h) {
106        h = SkPaint::kNormal_Hinting;
107    }
108    rec->setHinting(h);
109
110    // we don't support LCD text
111    if (SkMask::kLCD16_Format == rec->fMaskFormat ||
112        SkMask::kLCD32_Format == rec->fMaskFormat) {
113        rec->fMaskFormat = SkMask::kA8_Format;
114    }
115}
116
117SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
118    : SkScalerContext(desc), fLayout(0), fStyle(0)
119{
120    SkAutoMutexAcquire  ac(gFTMutex);
121    OSStatus err;
122
123    err = ::ATSUCreateStyle(&fStyle);
124    SkASSERT(0 == err);
125
126    SkMatrix    m;
127    fRec.getSingleMatrix(&m);
128
129    fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]),
130                                       SkScalarToFloat(m[SkMatrix::kMSkewX]),
131                                       SkScalarToFloat(m[SkMatrix::kMSkewY]),
132                                       SkScalarToFloat(m[SkMatrix::kMScaleY]),
133                                       SkScalarToFloat(m[SkMatrix::kMTransX]),
134                                       SkScalarToFloat(m[SkMatrix::kMTransY]));
135
136    ATSStyleRenderingOptions renderOpts = kATSStyleApplyAntiAliasing;
137    switch (fRec.getHinting()) {
138        case SkPaint::kNo_Hinting:
139        case SkPaint::kSlight_Hinting:
140            renderOpts |= kATSStyleNoHinting;
141            break;
142        case SkPaint::kNormal_Hinting:
143        case SkPaint::kFull_Hinting:
144            renderOpts |= kATSStyleApplyHints;
145            break;
146    }
147
148    ATSUFontID fontID = FMGetFontFromATSFontRef(fRec.fFontID);
149    // we put everything in the matrix, so our pt size is just 1.0
150    Fixed fixedSize = SK_Fixed1;
151    static const ATSUAttributeTag tags[] = {
152        kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag
153    };
154    static const ByteCount sizes[] = {
155        sizeof(fontID), sizeof(fixedSize), sizeof(fTransform), sizeof(renderOpts)
156    };
157    const ATSUAttributeValuePtr values[] = {
158        &fontID, &fixedSize, &fTransform, &renderOpts
159    };
160    err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags),
161                              tags, sizes, values);
162    SkASSERT(0 == err);
163
164    err = ::ATSUCreateTextLayout(&fLayout);
165    SkASSERT(0 == err);
166
167    fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
168}
169
170SkScalerContext_Mac::~SkScalerContext_Mac() {
171    ::CGColorSpaceRelease(fGrayColorSpace);
172    ::ATSUDisposeTextLayout(fLayout);
173    ::ATSUDisposeStyle(fStyle);
174}
175
176// man, we need to consider caching this, since it is just dependent on
177// fFontID, and not on any of the other settings like matrix or flags
178unsigned SkScalerContext_Mac::generateGlyphCount() {
179    // The 'maxp' table stores the number of glyphs a offset 4, in 2 bytes
180    uint16_t numGlyphs;
181    if (SkFontHost::GetTableData(fRec.fFontID,
182                                 SkSetFourByteTag('m', 'a', 'x', 'p'),
183                                 4, 2, &numGlyphs) != 2) {
184        return 0xFFFF;
185    }
186    return SkEndian_SwapBE16(numGlyphs);
187}
188
189uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
190{
191    SkAutoMutexAcquire  ac(gFTMutex);
192
193    OSStatus err;
194    UniChar achar = uni;
195    err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
196    err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
197
198    ATSLayoutRecord *layoutPtr;
199    ItemCount count;
200    ATSGlyphRef glyph;
201
202    err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
203    glyph = layoutPtr->glyphID;
204    ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
205    return glyph;
206}
207
208static void set_glyph_metrics_on_error(SkGlyph* glyph) {
209    glyph->fRsbDelta = 0;
210    glyph->fLsbDelta = 0;
211    glyph->fWidth    = 0;
212    glyph->fHeight   = 0;
213    glyph->fTop      = 0;
214    glyph->fLeft     = 0;
215    glyph->fAdvanceX = 0;
216    glyph->fAdvanceY = 0;
217}
218
219void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
220    this->generateMetrics(glyph);
221}
222
223void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
224    GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount);
225    ATSGlyphScreenMetrics screenMetrics;
226    ATSGlyphIdealMetrics idealMetrics;
227
228    OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
229                                             &screenMetrics);
230    if (noErr != err) {
231        set_glyph_metrics_on_error(glyph);
232        return;
233    }
234    err = ATSUGlyphGetIdealMetrics(fStyle, 1, &glyphID, 0, &idealMetrics);
235    if (noErr != err) {
236        set_glyph_metrics_on_error(glyph);
237        return;
238    }
239
240    if ((fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) == 0) {
241        glyph->fAdvanceX = SkFloatToFixed(screenMetrics.deviceAdvance.x);
242        glyph->fAdvanceY = -SkFloatToFixed(screenMetrics.deviceAdvance.y);
243    } else {
244        glyph->fAdvanceX = SkFloatToFixed(idealMetrics.advance.x);
245        glyph->fAdvanceY = -SkFloatToFixed(idealMetrics.advance.y);
246    }
247
248    // specify an extra 1-pixel border, go tive CG room for its antialiasing
249    // i.e. without this, I was seeing some edges chopped off!
250    glyph->fWidth = screenMetrics.width + 2;
251    glyph->fHeight = screenMetrics.height + 2;
252    glyph->fLeft = sk_float_round2int(screenMetrics.topLeft.x) - 1;
253    glyph->fTop = -sk_float_round2int(screenMetrics.topLeft.y) - 1;
254}
255
256void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
257{
258    SkAutoMutexAcquire  ac(gFTMutex);
259    SkASSERT(fLayout);
260
261    sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
262    CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage,
263                                              glyph.fWidth, glyph.fHeight, 8,
264                                              glyph.rowBytes(), fGrayColorSpace,
265                                              kCGImageAlphaNone);
266    if (!contextRef) {
267        SkASSERT(false);
268        return;
269    }
270
271    ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
272    ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
273
274    CGGlyph glyphID = glyph.getGlyphID(fBaseGlyphCount);
275    CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID);
276    CGContextSetFont(contextRef, fontRef);
277    CGContextSetFontSize(contextRef, 1);
278    CGContextSetTextMatrix(contextRef, fTransform);
279    CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft,
280                               glyph.fTop + glyph.fHeight, &glyphID, 1);
281
282    ::CGContextRelease(contextRef);
283}
284
285#if 0
286static void convert_metrics(SkPaint::FontMetrics* dst,
287                            const ATSFontMetrics& src) {
288    dst->fTop     = -SkFloatToScalar(src.ascent);
289    dst->fAscent  = -SkFloatToScalar(src.ascent);
290    dst->fDescent = SkFloatToScalar(src.descent);
291    dst->fBottom  = SkFloatToScalar(src.descent);
292    dst->fLeading = SkFloatToScalar(src.leading);
293}
294#endif
295
296static void* get_font_table(ATSFontRef fontID, uint32_t tag) {
297    ByteCount size;
298    OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size);
299    if (err) {
300        return NULL;
301    }
302    void* data = sk_malloc_throw(size);
303    err = ATSFontGetTable(fontID, tag, 0, size, data, &size);
304    if (err) {
305        sk_free(data);
306        data = NULL;
307    }
308    return data;
309}
310
311static int get_be16(const void* data, size_t offset) {
312    const char* ptr = reinterpret_cast<const char*>(data);
313    uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset);
314    int n = SkEndian_SwapBE16(value);
315    // now force it to be signed
316    return n << 16 >> 16;
317}
318
319#define SFNT_HEAD_UPEM_OFFSET       18
320#define SFNT_HEAD_YMIN_OFFSET       38
321#define SFNT_HEAD_YMAX_OFFSET       42
322#define SFNT_HEAD_STYLE_OFFSET      44
323
324#define SFNT_HHEA_ASCENT_OFFSET     4
325#define SFNT_HHEA_DESCENT_OFFSET    6
326#define SFNT_HHEA_LEADING_OFFSET    8
327
328static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) {
329    void* head = get_font_table(font, 'head');
330    if (NULL == head) {
331        return false;
332    }
333    void* hhea = get_font_table(font, 'hhea');
334    if (NULL == hhea) {
335        sk_free(head);
336        return false;
337    }
338
339    int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET);
340    int ys[5];
341
342    ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET);
343    ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET);
344    ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET);
345    ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET);
346    ys[4] =  get_be16(hhea, SFNT_HHEA_LEADING_OFFSET);
347
348    // now do some cleanup, to ensure y[max,min] are really that
349    if (ys[0] > ys[1]) {
350        ys[0] = ys[1];
351    }
352    if (ys[3] < ys[2]) {
353        ys[3] = ys[2];
354    }
355
356    for (int i = 0; i < 5; i++) {
357        pts[i].set(0, SkIntToScalar(ys[i]) / upem);
358    }
359
360    sk_free(hhea);
361    sk_free(head);
362    return true;
363}
364
365void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
366                                              SkPaint::FontMetrics* my) {
367    SkPoint pts[5];
368
369    if (!init_vertical_metrics(fRec.fFontID, pts)) {
370        // these are not as accurate as init_vertical_metrics :(
371        ATSFontMetrics metrics;
372        ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault,
373                                  &metrics);
374        pts[0].set(0, -SkFloatToScalar(metrics.ascent));
375        pts[1].set(0, -SkFloatToScalar(metrics.ascent));
376        pts[2].set(0, -SkFloatToScalar(metrics.descent));
377        pts[3].set(0, -SkFloatToScalar(metrics.descent));
378        pts[4].set(0, SkFloatToScalar(metrics.leading));    //+ or -?
379    }
380
381    SkMatrix m;
382    fRec.getSingleMatrix(&m);
383    m.mapPoints(pts, 5);
384
385    if (mx) {
386        mx->fTop = pts[0].fX;
387        mx->fAscent = pts[1].fX;
388        mx->fDescent = pts[2].fX;
389        mx->fBottom = pts[3].fX;
390        mx->fLeading = pts[4].fX;
391        // FIXME:
392        mx->fAvgCharWidth = 0;
393        mx->fXMin = 0;
394        mx->fXMax = 0;
395        mx->fXHeight = 0;
396    }
397    if (my) {
398        my->fTop = pts[0].fY;
399        my->fAscent = pts[1].fY;
400        my->fDescent = pts[2].fY;
401        my->fBottom = pts[3].fY;
402        my->fLeading = pts[4].fY;
403        // FIXME:
404        my->fAvgCharWidth = 0;
405        my->fXMin = 0;
406        my->fXMax = 0;
407        my->fXHeight = 0;
408    }
409}
410
411void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
412{
413    SkAutoMutexAcquire  ac(gFTMutex);
414    OSStatus err,result;
415
416    err = ::ATSUGlyphGetCubicPaths(
417            fStyle,glyph.fID,
418            &SkScalerContext_Mac::MoveTo,
419            &SkScalerContext_Mac::Line,
420            &SkScalerContext_Mac::Curve,
421            &SkScalerContext_Mac::Close,
422            path,&result);
423    SkASSERT(err == noErr);
424}
425
426OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
427{
428    reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
429    return noErr;
430}
431
432OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
433{
434    reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
435    return noErr;
436}
437
438OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1,
439                                    const Float32Point *pt2,
440                                    const Float32Point *pt3, void *cb)
441{
442    reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),
443                                           F32PtToSkPoint(*pt2),
444                                           F32PtToSkPoint(*pt3));
445    return noErr;
446}
447
448OSStatus SkScalerContext_Mac::Close(void *cb)
449{
450    reinterpret_cast<SkPath*>(cb)->close();
451    return noErr;
452}
453
454#pragma mark -
455
456void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
457    SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
458}
459
460SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
461    SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
462    return NULL;
463}
464
465SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
466    return NULL;
467}
468
469SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
470    return NULL;
471}
472
473// static
474SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
475        uint32_t fontID,
476        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
477        const uint32_t* glyphIDs,
478        uint32_t glyphIDsCount) {
479    SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
480    return NULL;
481}
482
483SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
484    return new SkScalerContext_Mac(desc);
485}
486
487SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
488    uint32_t newFontID = find_default_fontID();
489    if (newFontID == currFontID) {
490        newFontID = 0;
491    }
492    return newFontID;
493}
494
495SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
496                            const char familyName[],
497                            const void* data, size_t bytelength,
498                            SkTypeface::Style style) {
499    // todo: we don't know how to respect style bits
500    if (NULL == familyName && NULL != familyFace) {
501        familyFace->ref();
502        return const_cast<SkTypeface*>(familyFace);
503    } else {
504        return CreateTypeface_(familyName, style);
505    }
506}
507
508///////////////////////////////////////////////////////////////////////////////
509
510struct SkSFNTHeader {
511    uint32_t    fVersion;
512    uint16_t    fNumTables;
513    uint16_t    fSearchRange;
514    uint16_t    fEntrySelector;
515    uint16_t    fRangeShift;
516};
517
518struct SkSFNTDirEntry {
519    uint32_t    fTag;
520    uint32_t    fChecksum;
521    uint32_t    fOffset;
522    uint32_t    fLength;
523};
524
525struct SfntHeader {
526    SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) {
527        ByteCount size;
528        if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) {
529            return;
530        }
531
532        SkAutoMalloc storage(size);
533        SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get());
534        if (ATSFontGetTableDirectory(fontID, size, header, &size)) {
535            return;
536        }
537
538        fCount = SkEndian_SwapBE16(header->fNumTables);
539        fData = header;
540        storage.detach();
541    }
542
543    ~SfntHeader() {
544        sk_free(fData);
545    }
546
547    int count() const { return fCount; }
548    const SkSFNTDirEntry* entries() const {
549        return reinterpret_cast<const SkSFNTDirEntry*>
550            (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader));
551    }
552
553private:
554    int     fCount;
555    void*   fData;
556};
557
558int SkFontHost::CountTables(SkFontID fontID) {
559    SfntHeader header(fontID, false);
560    return header.count();
561}
562
563int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
564    SfntHeader header(fontID, true);
565    int count = header.count();
566    const SkSFNTDirEntry* entry = header.entries();
567    for (int i = 0; i < count; i++) {
568        tags[i] = SkEndian_SwapBE32(entry[i].fTag);
569    }
570    return count;
571}
572
573size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
574    ByteCount size;
575    if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) {
576        return 0;
577    }
578    return size;
579}
580
581size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
582                                size_t offset, size_t length, void* data) {
583    ByteCount size;
584    if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) {
585        return 0;
586    }
587    if (offset >= size) {
588        return 0;
589    }
590    if (offset + length > size) {
591        length = size - offset;
592    }
593    return length;
594}
595