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