use native mac fonthost
add 444 and (fake) 565 support in cg



git-svn-id: http://skia.googlecode.com/svn/trunk@41 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index af44cf5..d1e43358 100755
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -1,563 +1,563 @@
-/*

+/*
  ** Copyright 2006, The Android Open Source Project
- **

- ** Licensed under the Apache License, Version 2.0 (the "License"); 

- ** you may not use this file except in compliance with the License. 

- ** You may obtain a copy of the License at 

- **

- **     http://www.apache.org/licenses/LICENSE-2.0 

- **

- ** Unless required by applicable law or agreed to in writing, software 

- ** distributed under the License is distributed on an "AS IS" BASIS, 

- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

- ** See the License for the specific language governing permissions and 

- ** limitations under the License.

-*/

-

-#include "SkFontHost.h"

-#include "SkDescriptor.h"

-

-// Give 1MB font cache budget

-#define FONT_CACHE_MEMORY_BUDGET    (1024 * 1024)

-

-const char* gDefaultfont = "Arial"; // hard code for now

-static SkMutex      gFTMutex;

-

-inline SkPoint F32PtToSkPoint(const Float32Point p)

-{

-    SkPoint sp = { SkFloatToFixed(p.x),SkFloatToFixed(p.y) };

-    return sp;

-}

-

-static inline uint32_t _rotl(uint32_t v, uint32_t r) 

-{

-    return (v << r | v >> (32 - r));

-}

-

-// This will generate a unique ID based on the fontname + fontstyle

-// and also used by upper layer

-uint32_t FontFaceChecksum(const char *name,SkTypeface::Style style)

-{

-    if (!name) return style;

-    

-    char* q = (char*)name;

-

-    // From "Performance in Practice of String Hashing Functions"

-    // Ramakrishna & Zobel

-    const uint32_t L = 5;

-    const uint32_t R = 2;

-     

-    uint32_t h = 0x12345678;

-    while (*q) {

-        uint32_t ql = tolower(*q);

-        h ^= ((h << L) + (h >> R) + ql);

-        q ++;

-    }

-

-    // add style

-    h = _rotl(h, 3) ^ style;

-

-    return h;

-}

-

-#pragma mark -

-struct SkFaceRec {

-    SkFaceRec*      fNext;

-    uint32_t        fRefCnt;

-    ATSUFontID      fFontID;

-    ATSUStyle       fStyle;

-

-    SkFaceRec() : fFontID(0), fRefCnt(0), fStyle(NULL) {};

-

-    ~SkFaceRec() {

-        if (fStyle) {

-            ::ATSUDisposeStyle(fStyle);

-            fStyle = NULL;

-        }

-    }

-

-    uint32_t ref() {

-        return ++fRefCnt;

-    }

-};

-

-// Font Face list

-static SkFaceRec*   gFaceRecHead = NULL;

-

-static SkFaceRec* find_ft_face(const ATSUFontID fontID) {

-    SkFaceRec* rec = gFaceRecHead;

-    while (rec) {

-        if (rec->fFontID == fontID) {

-            return rec;

-        }

-        rec = rec->fNext;

-    }

-

-    return NULL;

-}

-

-static SkFaceRec* insert_ft_face(const ATSUFontID afontID, const ATSUStyle atsuStyle) {

-    SkFaceRec* rec = find_ft_face(afontID);

-    if (rec) {

-        return rec;  // found?

-    }

-

-    rec = SkNEW(SkFaceRec);

-    rec->fFontID = afontID;

-    rec->fStyle = atsuStyle;

-    rec->fNext = gFaceRecHead;

-    gFaceRecHead = rec;

-

-    return rec;

-}

-

-static void unref_ft_face(const ATSUFontID fontID) {

-

-    SkFaceRec* rec = gFaceRecHead;

-    SkFaceRec* prev = NULL;

-    while (rec) {

-        SkFaceRec* next = rec->fNext;

-        if (rec->fFontID == fontID) {

-            if (--rec->fRefCnt == 0) {

-                if (prev)

-                    prev->fNext = next;

-                else

-                    gFaceRecHead = next;

-

-                SkDELETE(rec);

-            }

-            return;

-        }

-        prev = rec;

-        rec = next;

-    }

-    SkASSERT("shouldn't get here, face not in list");

-}

-

-#pragma mark -

-

-// have to do this because SkTypeface::SkTypeface() is protected

-class SkTypeface_Mac : public SkTypeface {

-public:

-    SkTypeface_Mac(SkTypeface::Style style, uint32_t id) : SkTypeface(style, id) {}

-

-    ~SkTypeface_Mac() {}

-};

-

-#pragma mark -

-

-static SkTypeface* CreateTypeface_(const char *name, const SkTypeface::Style style) {

-

-    OSStatus err;

-    ATSUStyle atsuStyle;

-    ::ATSUCreateStyle(&atsuStyle);

-    if (name != NULL) {

-        static const ATSUAttributeTag fontTag = kATSUFontTag;

-        static const ByteCount fontTagSize =  sizeof(ATSUFontID);

-    

-        ATSUFontID fontID = 0;

-#if 1

-        err = ::ATSUFindFontFromName(

-                name,strlen(name),kFontNoNameCode,   /*  instead of regular, kFontFamilyName returns bold and/or italic sometimes, but why this works?? */

-                kFontMacintoshPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontID);

-#else    

-        CFStringRef cfontName = CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII); 

-        ATSFontRef fontRef = ::ATSFontFindFromName(cfontName,kATSOptionFlagsDefault);

-        fontID = ::FMGetFontFromATSFontRef(fontRef);

-        CFRelease(cfontName);

-#endif    

-        if (0 != fontID) {

-            const ATSUAttributeValuePtr values[] = { &fontID };

-            err = ::ATSUSetAttributes(atsuStyle,1,&fontTag,&fontTagSize,values);

-        }

-        else {

-        }

-    }

-    if (style != SkTypeface::kNormal) {

-        Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);

-        Boolean fontBold = ((style & SkTypeface::kBold) != 0);

-        const ATSUAttributeTag tags[2] =        { kATSUQDBoldfaceTag, kATSUQDItalicTag };

-        const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };

-        const ByteCount sizes[2] =                { sizeof(Boolean), sizeof(Boolean) };

-        err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);

-    }

-

-    uint32_t cs = FontFaceChecksum(name,style);

-    SkTypeface_Mac* ptypeface = new SkTypeface_Mac(style,cs);

-

-    if (NULL == ptypeface) {

-        SkASSERT(false);

-        return NULL;

-    }

-

-    SkFaceRec* rec = insert_ft_face(cs, atsuStyle);

-    SkASSERT(rec);

-

-    return ptypeface;

-}

-

-static SkTypeface* CreateTypeface_(const SkFaceRec* rec, const SkTypeface::Style style) {

-

-    OSStatus err;

-    ATSUStyle atsuStyle;

-    err = ::ATSUCreateAndCopyStyle(rec->fStyle, &atsuStyle);

-

-    Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);

-    Boolean fontBold = ((style & SkTypeface::kBold) != 0);

-    const ATSUAttributeTag tags[2] =        { kATSUQDBoldfaceTag, kATSUQDItalicTag };

-    const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };

-    const ByteCount sizes[2] =                { sizeof(Boolean), sizeof(Boolean) };

-    err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);

-

-    // get old font id and name

-    ATSUFontID fontID = 0;

-    ByteCount actual = 0;

-    err = ::ATSUGetAttribute(rec->fStyle,kATSUFontTag,sizeof(ATSUFontID),&fontID,&actual);

-

-    ByteCount actualLength = 0;

-    char *fontname = NULL;

-    err = ::ATSUFindFontName(fontID , kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,

-        kFontNoLanguageCode , 0 , NULL , &actualLength , NULL );

-    if ( err == noErr)

-    {

-        actualLength += 1 ;

-        fontname = (char*)malloc( actualLength );

-        err = ::ATSUFindFontName(fontID, kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,

-            kFontNoLanguageCode, actualLength, fontname , NULL, NULL);

-    }

-

-    SkTypeface_Mac* ptypeface = NULL;

-    if (fontname == NULL) {

-        ptypeface = new SkTypeface_Mac(style,rec->fFontID);

-        return ptypeface;

-    }

-    else {

-        uint32_t cs = FontFaceChecksum(fontname,style);

-        ptypeface = new SkTypeface_Mac(style, cs);

-

-        if (NULL == ptypeface) {

-            SkASSERT(false);

-            return NULL;

-        }

-

-        free(fontname);

-

-        insert_ft_face(cs,atsuStyle);

-    }

-    return ptypeface;

-}

-

-#pragma mark -

-

-class SkScalerContext_Mac : public SkScalerContext {

-public:

-    SkScalerContext_Mac(const SkDescriptor* desc);

-    virtual ~SkScalerContext_Mac();

-

-protected:

-    virtual unsigned generateGlyphCount() const;

-    virtual uint16_t generateCharToGlyph(SkUnichar uni);

-    virtual void generateAdvance(SkGlyph* glyph);

-    virtual void generateMetrics(SkGlyph* glyph);

-    virtual void generateImage(const SkGlyph& glyph);

-    virtual void generatePath(const SkGlyph& glyph, SkPath* path);

-    virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);

-    virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);

-    virtual SkDeviceContext getDC() { return NULL; } // not implemented on Mac

-

-private:

-    ATSUTextLayout  fLayout;

-    ATSUStyle       fStyle;

-    

-    static OSStatus MoveTo(const Float32Point *pt, void *cb);

-    static OSStatus Line(const Float32Point *pt, void *cb);

-    static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);

-    static OSStatus Close(void *cb);

-};

-

-SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)

-    : SkScalerContext(desc), fLayout(0), fStyle(0)

-{

-    SkAutoMutexAcquire  ac(gFTMutex);

-    OSStatus err;

-    

-    SkFaceRec* rec = find_ft_face(fRec.fFontID);

-    if (rec) {

-        rec->ref();

-        err = ::ATSUCreateAndCopyStyle(rec->fStyle, &fStyle);

-    }

-    else {

-        SkASSERT(false);

-        // create a default

-        err = ::ATSUCreateStyle(&fStyle);

-    }

-

-    uint32_t size = SkFixedFloor(fRec.fTextSize);

-    Fixed fixedSize = IntToFixed(size);

-    static const ATSUAttributeTag sizeTag = kATSUSizeTag;

-    static const ByteCount sizeTagSize = sizeof(Fixed);

-    const ATSUAttributeValuePtr values[] = { &fixedSize };

-    err = ::ATSUSetAttributes(fStyle,1,&sizeTag,&sizeTagSize,values);

-

-    err = ::ATSUCreateTextLayout(&fLayout);

-}

-

-SkScalerContext_Mac::~SkScalerContext_Mac()

-{

-    unref_ft_face(fRec.fFontID);

-

-    ::ATSUDisposeTextLayout(fLayout);

-    ::ATSUDisposeStyle(fStyle);

-}

-

-unsigned SkScalerContext_Mac::generateGlyphCount() const

-{

-    return 0xFFFF;

-}

-

-uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)

-{

-    SkAutoMutexAcquire  ac(gFTMutex);

-    

-    OSStatus err;

-    UniChar achar = uni;

-    err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);

-    err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);

-        

-    ATSLayoutRecord *layoutPtr;

-    ItemCount count;

-    ATSGlyphRef glyph;

-    

-    err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);

-    glyph = layoutPtr->glyphID;

-    ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);

-    return glyph;

-}

-

-void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {

-    this->generateMetrics(glyph);

-}

-

-void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph)

-{

-    GlyphID glyphID = glyph->fID;

-    ATSGlyphScreenMetrics metrics= { 0 };

-    

-    glyph->fRsbDelta = 0;

-    glyph->fLsbDelta = 0;

-    

-    OSStatus err = ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);

-    if (err == noErr) {

-        glyph->fAdvanceX = SkFloatToFixed(metrics.deviceAdvance.x);

-        glyph->fAdvanceY = SkFloatToFixed(metrics.deviceAdvance.y);

-        //glyph->fWidth = metrics.width;

-        //glyph->fHeight = metrics.height;

-        glyph->fWidth = metrics.width + ceil(metrics.sideBearing.x - metrics.otherSideBearing.x);

-        glyph->fHeight = metrics.height + ceil(metrics.sideBearing.y - metrics.otherSideBearing.y) + 1;

-

-        glyph->fTop = -metrics.topLeft.y;

-        glyph->fLeft = metrics.topLeft.x;

-    }

-}

-

-void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {

-    //SkASSERT(false);

-    if (mx)

-        memset(mx, 0, sizeof(SkPaint::FontMetrics));

-    if (my)

-        memset(my, 0, sizeof(SkPaint::FontMetrics));

-    return;

-}

-

-void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)

-{

-    SkAutoMutexAcquire  ac(gFTMutex);

-    

-    GlyphID glyphID = glyph.fID;

-    ATSGlyphScreenMetrics metrics= { 0 };

-    

-    SkASSERT(fLayout);

-    OSStatus err = ::ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);

-

-//    uint32_t w = metrics.width;

-//    uint32_t h = metrics.height;

-//    uint32_t pitch = (w + 3) & ~0x3;

-//    if (pitch != glyph.rowBytes()) {

-//        SkASSERT(false); // it's different from previously cacluated in generateMetrics(), so the size of glyph.fImage buffer is incorrect!

-//    }

-    

-    CGColorSpaceRef greyColorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);

-    CGContextRef contextRef = ::CGBitmapContextCreate((uint8_t*)glyph.fImage, glyph.fWidth, glyph.fHeight, 8, glyph.rowBytes(), greyColorSpace, kCGImageAlphaNone);

-    if (!contextRef) {

-        SkASSERT(false);

-        return;

-    }

-        

-    ::CGContextSetFillColorSpace(contextRef, greyColorSpace); 

-    ::CGContextSetStrokeColorSpace(contextRef, greyColorSpace); 

-                

-    ::CGContextSetGrayFillColor(contextRef, 0.0, 1.0);

-    ::CGContextFillRect(contextRef, ::CGRectMake(0, 0, glyph.fWidth, glyph.fHeight));

-                

-    ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);

-    ::CGContextSetGrayStrokeColor(contextRef, 1.0, 1.0);

-    ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);

-    

-    ATSUAttributeTag tag = kATSUCGContextTag;

-    ByteCount size = sizeof(CGContextRef);

-    ATSUAttributeValuePtr value = &contextRef;

-    err = ::ATSUSetLayoutControls(fLayout,1,&tag,&size,&value);

-    err = ::ATSUDrawText(fLayout,kATSUFromTextBeginning,kATSUToTextEnd,FloatToFixed(-metrics.topLeft.x),FloatToFixed(glyph.fHeight-metrics.topLeft.y));

-    ::CGContextRelease(contextRef);

-}

-

-void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)

-{

-    SkAutoMutexAcquire  ac(gFTMutex);

-    OSStatus err,result;

-    

-    err = ::ATSUGlyphGetCubicPaths(

-            fStyle,glyph.fID,

-            &SkScalerContext_Mac::MoveTo,

-            &SkScalerContext_Mac::Line,

-            &SkScalerContext_Mac::Curve,

-            &SkScalerContext_Mac::Close,

-            path,&result);

-    SkASSERT(err == noErr);

-}

-

-void SkScalerContext_Mac::generateLineHeight(SkPoint* ascent, SkPoint* descent)

-{

-    ATSUTextMeasurement     textAscent, textDescent;

-    ByteCount actual = 0;

-    OSStatus err = ::ATSUGetAttribute(fStyle,kATSULineAscentTag,sizeof(ATSUTextMeasurement),&textAscent,&actual);

-    ascent->set(0,textAscent);

-    err = ::ATSUGetAttribute(fStyle,kATSULineDescentTag,sizeof(ATSUTextMeasurement),&textDescent,&actual);

-    descent->set(0,textDescent);

-}

-

-OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)

-{

-    reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));

-    return noErr;

-}

-

-OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)

-{

-    reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));

-    return noErr;

-}

-

-OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb)

-{

-    reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),F32PtToSkPoint(*pt2),F32PtToSkPoint(*pt3));

-    return noErr;

-}

-

-OSStatus SkScalerContext_Mac::Close(void *cb)

-{

-    reinterpret_cast<SkPath*>(cb)->close();

-    return noErr;

-}

-

-#pragma mark -

-

-void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {

-    SkASSERT(!"SkFontHost::Serialize unimplemented");

-}

-

-SkTypeface* SkFontHost::Deserialize(SkStream* stream) {

-    SkASSERT(!"SkFontHost::Deserialize unimplemented");

-    return NULL;

-}

-

-SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {

-

-    //Should not be used on Mac, keep linker happy

-    SkASSERT(false);

-    return CreateTypeface_(gDefaultfont,SkTypeface::kNormal);

-}

-

-SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc)

-{

-    return new SkScalerContext_Mac(desc);

-}

-

-SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)

-{

-    SkAutoDescriptor    ad(sizeof(rec) + sizeof(gDefaultfont) + SkDescriptor::ComputeOverhead(2));

-    SkDescriptor*       desc = ad.getDesc();

-

-    desc->init();

-    SkScalerContext::Rec* newRec =

-        (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);

-

-    CreateTypeface_(gDefaultfont,SkTypeface::kNormal);

-    newRec->fFontID = FontFaceChecksum(gDefaultfont,SkTypeface::kNormal);

-    desc->computeChecksum();

-

-    return SkFontHost::CreateScalerContext(desc);

-}

-

-

-    /** Return the closest matching typeface given either an existing family

-        (specified by a typeface in that family) or by a familyName, and a

-        requested style.

-        1) If familyFace is null, use famillyName.

-        2) If famillyName is null, use familyFace.

-        3) If both are null, return the default font that best matches style

-        This MUST not return NULL.

-    */

-

-SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {

-    

-    SkAutoMutexAcquire  ac(gFTMutex);

-    

-    // clip to legal style bits

-    style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);

-    

-    SkTypeface* tf = NULL;

-    

-    if (NULL == familyFace && NULL == familyName) {

-        tf = CreateTypeface_(gDefaultfont,style);

-    }

-    else {        

-        if (NULL != familyFace) {

-            uint32_t id = familyFace->uniqueID();

-            SkFaceRec* rec = find_ft_face(id);

-            if (!rec) {

-                SkASSERT(false);

-                tf = CreateTypeface_(gDefaultfont,style);

-            }

-            else {

-                tf = CreateTypeface_(rec,style);

-            }

-        }

-        else {

-            tf = CreateTypeface_(familyName,style);

-        }

-    }

-

-    if (NULL == tf) {

-        tf = CreateTypeface_(gDefaultfont,style);

-    }

-    return tf;

-

-}

-

-size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {

-    if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)

-        return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;

-    else

-        return 0;   // nothing to do

-}

-

-int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {

-    return 0;

-}

-

-void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {

-    tables[0] = NULL;   // black gamma (e.g. exp=1.4)

-    tables[1] = NULL;   // white gamma (e.g. exp= 1/1.4)

-}

-

+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License"); 
+ ** you may not use this file except in compliance with the License. 
+ ** You may obtain a copy of the License at 
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0 
+ **
+ ** Unless required by applicable law or agreed to in writing, software 
+ ** distributed under the License is distributed on an "AS IS" BASIS, 
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ ** See the License for the specific language governing permissions and 
+ ** limitations under the License.
+*/
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+
+// Give 1MB font cache budget
+#define FONT_CACHE_MEMORY_BUDGET    (1024 * 1024)
+
+const char* gDefaultfont = "Arial"; // hard code for now
+static SkMutex      gFTMutex;
+
+inline SkPoint F32PtToSkPoint(const Float32Point p)
+{
+    SkPoint sp = { SkFloatToFixed(p.x),SkFloatToFixed(p.y) };
+    return sp;
+}
+
+static inline uint32_t _rotl(uint32_t v, uint32_t r) 
+{
+    return (v << r | v >> (32 - r));
+}
+
+// This will generate a unique ID based on the fontname + fontstyle
+// and also used by upper layer
+uint32_t FontFaceChecksum(const char *name,SkTypeface::Style style)
+{
+    if (!name) return style;
+    
+    char* q = (char*)name;
+
+    // From "Performance in Practice of String Hashing Functions"
+    // Ramakrishna & Zobel
+    const uint32_t L = 5;
+    const uint32_t R = 2;
+     
+    uint32_t h = 0x12345678;
+    while (*q) {
+        uint32_t ql = tolower(*q);
+        h ^= ((h << L) + (h >> R) + ql);
+        q ++;
+    }
+
+    // add style
+    h = _rotl(h, 3) ^ style;
+
+    return h;
+}
+
+#pragma mark -
+struct SkFaceRec {
+    SkFaceRec*      fNext;
+    uint32_t        fRefCnt;
+    ATSUFontID      fFontID;
+    ATSUStyle       fStyle;
+
+    SkFaceRec() : fFontID(0), fRefCnt(0), fStyle(NULL) {};
+
+    ~SkFaceRec() {
+        if (fStyle) {
+            ::ATSUDisposeStyle(fStyle);
+            fStyle = NULL;
+        }
+    }
+
+    uint32_t ref() {
+        return ++fRefCnt;
+    }
+};
+
+// Font Face list
+static SkFaceRec*   gFaceRecHead = NULL;
+
+static SkFaceRec* find_ft_face(const ATSUFontID fontID) {
+    SkFaceRec* rec = gFaceRecHead;
+    while (rec) {
+        if (rec->fFontID == fontID) {
+            return rec;
+        }
+        rec = rec->fNext;
+    }
+
+    return NULL;
+}
+
+static SkFaceRec* insert_ft_face(const ATSUFontID afontID, const ATSUStyle atsuStyle) {
+    SkFaceRec* rec = find_ft_face(afontID);
+    if (rec) {
+        return rec;  // found?
+    }
+
+    rec = SkNEW(SkFaceRec);
+    rec->fFontID = afontID;
+    rec->fStyle = atsuStyle;
+    rec->fNext = gFaceRecHead;
+    gFaceRecHead = rec;
+
+    return rec;
+}
+
+static void unref_ft_face(const ATSUFontID fontID) {
+
+    SkFaceRec* rec = gFaceRecHead;
+    SkFaceRec* prev = NULL;
+    while (rec) {
+        SkFaceRec* next = rec->fNext;
+        if (rec->fFontID == fontID) {
+            if (--rec->fRefCnt == 0) {
+                if (prev)
+                    prev->fNext = next;
+                else
+                    gFaceRecHead = next;
+
+                SkDELETE(rec);
+            }
+            return;
+        }
+        prev = rec;
+        rec = next;
+    }
+    SkASSERT("shouldn't get here, face not in list");
+}
+
+#pragma mark -
+
+// have to do this because SkTypeface::SkTypeface() is protected
+class SkTypeface_Mac : public SkTypeface {
+public:
+    SkTypeface_Mac(SkTypeface::Style style, uint32_t id) : SkTypeface(style, id) {}
+
+    ~SkTypeface_Mac() {}
+};
+
+#pragma mark -
+
+static SkTypeface* CreateTypeface_(const char *name, const SkTypeface::Style style) {
+
+    OSStatus err;
+    ATSUStyle atsuStyle;
+    ::ATSUCreateStyle(&atsuStyle);
+    if (name != NULL) {
+        static const ATSUAttributeTag fontTag = kATSUFontTag;
+        static const ByteCount fontTagSize =  sizeof(ATSUFontID);
+    
+        ATSUFontID fontID = 0;
+#if 1
+        err = ::ATSUFindFontFromName(
+                name,strlen(name),kFontNoNameCode,   /*  instead of regular, kFontFamilyName returns bold and/or italic sometimes, but why this works?? */
+                kFontMacintoshPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontID);
+#else    
+        CFStringRef cfontName = CFStringCreateWithCString(NULL, name, kCFStringEncodingASCII); 
+        ATSFontRef fontRef = ::ATSFontFindFromName(cfontName,kATSOptionFlagsDefault);
+        fontID = ::FMGetFontFromATSFontRef(fontRef);
+        CFRelease(cfontName);
+#endif    
+        if (0 != fontID) {
+            const ATSUAttributeValuePtr values[] = { &fontID };
+            err = ::ATSUSetAttributes(atsuStyle,1,&fontTag,&fontTagSize,values);
+        }
+        else {
+        }
+    }
+    if (style != SkTypeface::kNormal) {
+        Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);
+        Boolean fontBold = ((style & SkTypeface::kBold) != 0);
+        const ATSUAttributeTag tags[2] =        { kATSUQDBoldfaceTag, kATSUQDItalicTag };
+        const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };
+        const ByteCount sizes[2] =                { sizeof(Boolean), sizeof(Boolean) };
+        err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);
+    }
+
+    uint32_t cs = FontFaceChecksum(name,style);
+    SkTypeface_Mac* ptypeface = new SkTypeface_Mac(style,cs);
+
+    if (NULL == ptypeface) {
+        SkASSERT(false);
+        return NULL;
+    }
+
+    SkFaceRec* rec = insert_ft_face(cs, atsuStyle);
+    SkASSERT(rec);
+
+    return ptypeface;
+}
+
+static SkTypeface* CreateTypeface_(const SkFaceRec* rec, const SkTypeface::Style style) {
+
+    OSStatus err;
+    ATSUStyle atsuStyle;
+    err = ::ATSUCreateAndCopyStyle(rec->fStyle, &atsuStyle);
+
+    Boolean fontItalic = ((style & SkTypeface::kItalic) != 0);
+    Boolean fontBold = ((style & SkTypeface::kBold) != 0);
+    const ATSUAttributeTag tags[2] =        { kATSUQDBoldfaceTag, kATSUQDItalicTag };
+    const ATSUAttributeValuePtr values[2] = { &fontBold, &fontItalic };
+    const ByteCount sizes[2] =                { sizeof(Boolean), sizeof(Boolean) };
+    err = ::ATSUSetAttributes(atsuStyle,2,tags,sizes,values);
+
+    // get old font id and name
+    ATSUFontID fontID = 0;
+    ByteCount actual = 0;
+    err = ::ATSUGetAttribute(rec->fStyle,kATSUFontTag,sizeof(ATSUFontID),&fontID,&actual);
+
+    ByteCount actualLength = 0;
+    char *fontname = NULL;
+    err = ::ATSUFindFontName(fontID , kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,
+        kFontNoLanguageCode , 0 , NULL , &actualLength , NULL );
+    if ( err == noErr)
+    {
+        actualLength += 1 ;
+        fontname = (char*)malloc( actualLength );
+        err = ::ATSUFindFontName(fontID, kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode,
+            kFontNoLanguageCode, actualLength, fontname , NULL, NULL);
+    }
+
+    SkTypeface_Mac* ptypeface = NULL;
+    if (fontname == NULL) {
+        ptypeface = new SkTypeface_Mac(style,rec->fFontID);
+        return ptypeface;
+    }
+    else {
+        uint32_t cs = FontFaceChecksum(fontname,style);
+        ptypeface = new SkTypeface_Mac(style, cs);
+
+        if (NULL == ptypeface) {
+            SkASSERT(false);
+            return NULL;
+        }
+
+        free(fontname);
+
+        insert_ft_face(cs,atsuStyle);
+    }
+    return ptypeface;
+}
+
+#pragma mark -
+
+class SkScalerContext_Mac : public SkScalerContext {
+public:
+    SkScalerContext_Mac(const SkDescriptor* desc);
+    virtual ~SkScalerContext_Mac();
+
+protected:
+    virtual unsigned generateGlyphCount() const;
+    virtual uint16_t generateCharToGlyph(SkUnichar uni);
+    virtual void generateAdvance(SkGlyph* glyph);
+    virtual void generateMetrics(SkGlyph* glyph);
+    virtual void generateImage(const SkGlyph& glyph);
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path);
+    virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+//    virtual SkDeviceContext getDC() { return NULL; } // not implemented on Mac
+
+private:
+    ATSUTextLayout  fLayout;
+    ATSUStyle       fStyle;
+    
+    static OSStatus MoveTo(const Float32Point *pt, void *cb);
+    static OSStatus Line(const Float32Point *pt, void *cb);
+    static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
+    static OSStatus Close(void *cb);
+};
+
+SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
+    : SkScalerContext(desc), fLayout(0), fStyle(0)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+    OSStatus err;
+    
+    SkFaceRec* rec = find_ft_face(fRec.fFontID);
+    if (rec) {
+        rec->ref();
+        err = ::ATSUCreateAndCopyStyle(rec->fStyle, &fStyle);
+    }
+    else {
+        SkASSERT(false);
+        // create a default
+        err = ::ATSUCreateStyle(&fStyle);
+    }
+
+    uint32_t size = SkScalarFloor(fRec.fTextSize);
+    Fixed fixedSize = IntToFixed(size);
+    static const ATSUAttributeTag sizeTag = kATSUSizeTag;
+    static const ByteCount sizeTagSize = sizeof(Fixed);
+    const ATSUAttributeValuePtr values[] = { &fixedSize };
+    err = ::ATSUSetAttributes(fStyle,1,&sizeTag,&sizeTagSize,values);
+
+    err = ::ATSUCreateTextLayout(&fLayout);
+}
+
+SkScalerContext_Mac::~SkScalerContext_Mac()
+{
+    unref_ft_face(fRec.fFontID);
+
+    ::ATSUDisposeTextLayout(fLayout);
+    ::ATSUDisposeStyle(fStyle);
+}
+
+unsigned SkScalerContext_Mac::generateGlyphCount() const
+{
+    return 0xFFFF;
+}
+
+uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+    
+    OSStatus err;
+    UniChar achar = uni;
+    err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
+    err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
+        
+    ATSLayoutRecord *layoutPtr;
+    ItemCount count;
+    ATSGlyphRef glyph;
+    
+    err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
+    glyph = layoutPtr->glyphID;
+    ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
+    return glyph;
+}
+
+void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
+    this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph)
+{
+    GlyphID glyphID = glyph->fID;
+    ATSGlyphScreenMetrics metrics= { 0 };
+    
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+    
+    OSStatus err = ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);
+    if (err == noErr) {
+        glyph->fAdvanceX = SkFloatToFixed(metrics.deviceAdvance.x);
+        glyph->fAdvanceY = SkFloatToFixed(metrics.deviceAdvance.y);
+        //glyph->fWidth = metrics.width;
+        //glyph->fHeight = metrics.height;
+        glyph->fWidth = metrics.width + ceil(metrics.sideBearing.x - metrics.otherSideBearing.x);
+        glyph->fHeight = metrics.height + ceil(metrics.sideBearing.y - metrics.otherSideBearing.y) + 1;
+
+        glyph->fTop = -metrics.topLeft.y;
+        glyph->fLeft = metrics.topLeft.x;
+    }
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
+    //SkASSERT(false);
+    if (mx)
+        memset(mx, 0, sizeof(SkPaint::FontMetrics));
+    if (my)
+        memset(my, 0, sizeof(SkPaint::FontMetrics));
+    return;
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+    
+    GlyphID glyphID = glyph.fID;
+    ATSGlyphScreenMetrics metrics= { 0 };
+    
+    SkASSERT(fLayout);
+    OSStatus err = ::ATSUGlyphGetScreenMetrics(fStyle,1,&glyphID,0,true,true,&metrics);
+
+//    uint32_t w = metrics.width;
+//    uint32_t h = metrics.height;
+//    uint32_t pitch = (w + 3) & ~0x3;
+//    if (pitch != glyph.rowBytes()) {
+//        SkASSERT(false); // it's different from previously cacluated in generateMetrics(), so the size of glyph.fImage buffer is incorrect!
+//    }
+    
+    CGColorSpaceRef greyColorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
+    CGContextRef contextRef = ::CGBitmapContextCreate((uint8_t*)glyph.fImage, glyph.fWidth, glyph.fHeight, 8, glyph.rowBytes(), greyColorSpace, kCGImageAlphaNone);
+    if (!contextRef) {
+        SkASSERT(false);
+        return;
+    }
+        
+    ::CGContextSetFillColorSpace(contextRef, greyColorSpace); 
+    ::CGContextSetStrokeColorSpace(contextRef, greyColorSpace); 
+                
+    ::CGContextSetGrayFillColor(contextRef, 0.0, 1.0);
+    ::CGContextFillRect(contextRef, ::CGRectMake(0, 0, glyph.fWidth, glyph.fHeight));
+                
+    ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
+    ::CGContextSetGrayStrokeColor(contextRef, 1.0, 1.0);
+    ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
+    
+    ATSUAttributeTag tag = kATSUCGContextTag;
+    ByteCount size = sizeof(CGContextRef);
+    ATSUAttributeValuePtr value = &contextRef;
+    err = ::ATSUSetLayoutControls(fLayout,1,&tag,&size,&value);
+    err = ::ATSUDrawText(fLayout,kATSUFromTextBeginning,kATSUToTextEnd,FloatToFixed(-metrics.topLeft.x),FloatToFixed(glyph.fHeight-metrics.topLeft.y));
+    ::CGContextRelease(contextRef);
+}
+
+void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+    OSStatus err,result;
+    
+    err = ::ATSUGlyphGetCubicPaths(
+            fStyle,glyph.fID,
+            &SkScalerContext_Mac::MoveTo,
+            &SkScalerContext_Mac::Line,
+            &SkScalerContext_Mac::Curve,
+            &SkScalerContext_Mac::Close,
+            path,&result);
+    SkASSERT(err == noErr);
+}
+
+void SkScalerContext_Mac::generateLineHeight(SkPoint* ascent, SkPoint* descent)
+{
+    ATSUTextMeasurement     textAscent, textDescent;
+    ByteCount actual = 0;
+    OSStatus err = ::ATSUGetAttribute(fStyle,kATSULineAscentTag,sizeof(ATSUTextMeasurement),&textAscent,&actual);
+    ascent->set(0,textAscent);
+    err = ::ATSUGetAttribute(fStyle,kATSULineDescentTag,sizeof(ATSUTextMeasurement),&textDescent,&actual);
+    descent->set(0,textDescent);
+}
+
+OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
+{
+    reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
+    return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
+{
+    reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
+    return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb)
+{
+    reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),F32PtToSkPoint(*pt2),F32PtToSkPoint(*pt3));
+    return noErr;
+}
+
+OSStatus SkScalerContext_Mac::Close(void *cb)
+{
+    reinterpret_cast<SkPath*>(cb)->close();
+    return noErr;
+}
+
+#pragma mark -
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+    SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkASSERT(!"SkFontHost::Deserialize unimplemented");
+    return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypeface(SkStream* stream) {
+
+    //Should not be used on Mac, keep linker happy
+    SkASSERT(false);
+    return CreateTypeface_(gDefaultfont,SkTypeface::kNormal);
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc)
+{
+    return new SkScalerContext_Mac(desc);
+}
+
+SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)
+{
+    SkAutoDescriptor    ad(sizeof(rec) + sizeof(gDefaultfont) + SkDescriptor::ComputeOverhead(2));
+    SkDescriptor*       desc = ad.getDesc();
+
+    desc->init();
+    SkScalerContext::Rec* newRec =
+        (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+    CreateTypeface_(gDefaultfont,SkTypeface::kNormal);
+    newRec->fFontID = FontFaceChecksum(gDefaultfont,SkTypeface::kNormal);
+    desc->computeChecksum();
+
+    return SkFontHost::CreateScalerContext(desc);
+}
+
+
+    /** Return the closest matching typeface given either an existing family
+        (specified by a typeface in that family) or by a familyName, and a
+        requested style.
+        1) If familyFace is null, use famillyName.
+        2) If famillyName is null, use familyFace.
+        3) If both are null, return the default font that best matches style
+        This MUST not return NULL.
+    */
+
+SkTypeface* SkFontHost::FindTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) {
+    
+    SkAutoMutexAcquire  ac(gFTMutex);
+    
+    // clip to legal style bits
+    style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+    
+    SkTypeface* tf = NULL;
+    
+    if (NULL == familyFace && NULL == familyName) {
+        tf = CreateTypeface_(gDefaultfont,style);
+    }
+    else {        
+        if (NULL != familyFace) {
+            uint32_t id = familyFace->uniqueID();
+            SkFaceRec* rec = find_ft_face(id);
+            if (!rec) {
+                SkASSERT(false);
+                tf = CreateTypeface_(gDefaultfont,style);
+            }
+            else {
+                tf = CreateTypeface_(rec,style);
+            }
+        }
+        else {
+            tf = CreateTypeface_(familyName,style);
+        }
+    }
+
+    if (NULL == tf) {
+        tf = CreateTypeface_(gDefaultfont,style);
+    }
+    return tf;
+
+}
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
+    if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
+        return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
+    else
+        return 0;   // nothing to do
+}
+
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
+    return 0;
+}
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
+    tables[0] = NULL;   // black gamma (e.g. exp=1.4)
+    tables[1] = NULL;   // white gamma (e.g. exp= 1/1.4)
+}
+