separate mac fonthost into atsui (32bit, pre-10.6) and coretext (64bit, 10.6)
authorreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 19 May 2010 13:47:05 +0000 (13:47 +0000)
committerreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 19 May 2010 13:47:05 +0000 (13:47 +0000)
implementations.

code submitted by http://codereview.appspot.com/user/refnum

git-svn-id: http://skia.googlecode.com/svn/trunk@570 2bbb7eff-a529-9590-31e7-b0007b416f81

src/ports/SkFontHost_mac.cpp
src/ports/SkFontHost_mac_atsui.cpp [new file with mode: 0644]
src/ports/SkFontHost_mac_coretext.cpp [new file with mode: 0644]

index a0239e224c587fc2ab2f0bf140dd5bdca8fdd8d2..6adaf329b01e2c5491ea7c4d8282232d8002385f 100755 (executable)
  ** limitations under the License.
 */
 
-#include <Carbon/Carbon.h>
-#include "SkFontHost.h"
-#include "SkDescriptor.h"
-#include "SkEndian.h"
-#include "SkFloatingPoint.h"
-#include "SkPaint.h"
-#include "SkPoint.h"
 
-// Give 1MB font cache budget
-#define FONT_CACHE_MEMORY_BUDGET    (1024 * 1024)
-
-const char* gDefaultfont = "Arial"; // hard code for now
-static SkMutex      gFTMutex;
-
-static inline SkPoint F32PtToSkPoint(const Float32Point p) {
-    SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
-    return sp;
-}
-
-static inline uint32_t _rotl(uint32_t v, uint32_t r) {
-    return (v << r | v >> (32 - r));
-}
-
-class SkTypeface_Mac : public SkTypeface {
-public:
-    SkTypeface_Mac(SkTypeface::Style style, uint32_t id)
-        : SkTypeface(style, id) {}
-};
-
-#pragma mark -
-
-static uint32_t find_from_name(const char name[]) {
-    CFStringRef str = CFStringCreateWithCString(NULL, name,
-                                                kCFStringEncodingUTF8); 
-    uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault);
-    CFRelease(str);
-    return fontID;
-}
-
-static uint32_t find_default_fontID() {
-    static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" };
-
-    uint32_t fontID;
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) {
-        fontID = find_from_name(gDefaultNames[i]);
-        if (fontID) {
-            return fontID;
-        }
-    }
-    sk_throw();
-    return 0;
-}
-
-static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) {
-    uint32_t fontID = 0;
-    if (NULL != name) {
-        fontID = find_from_name(name);
-    }
-    if (0 == fontID) {
-        fontID = find_default_fontID();
-    }
-    // we lie (for now) and report that we found the exact style bits
-    return new SkTypeface_Mac(style, fontID);
-}
-
-#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 generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
-
-private:
-    ATSUTextLayout  fLayout;
-    ATSUStyle       fStyle;
-    CGColorSpaceRef fGrayColorSpace;
-    CGAffineTransform   fTransform;
-    
-    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);
-};
-
-void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
-    // we only support 2 levels of hinting
-    SkPaint::Hinting h = rec->getHinting();
-    if (SkPaint::kSlight_Hinting == h) {
-        h = SkPaint::kNo_Hinting;
-    } else if (SkPaint::kFull_Hinting == h) {
-        h = SkPaint::kNormal_Hinting;
-    }
-    rec->setHinting(h);
-
-    // we don't support LCD text
-    if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) {
-        rec->fMaskFormat = SkMask::kA8_Format;
-    }
-}
-
-SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
-    : SkScalerContext(desc), fLayout(0), fStyle(0)
-{
-    SkAutoMutexAcquire  ac(gFTMutex);
-    OSStatus err;
-    
-    err = ::ATSUCreateStyle(&fStyle);
-    SkASSERT(0 == err);
-            
-    SkMatrix    m;
-    fRec.getSingleMatrix(&m);
-    
-    fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]),
-                                       SkScalarToFloat(m[SkMatrix::kMSkewX]),
-                                       SkScalarToFloat(m[SkMatrix::kMSkewY]),
-                                       SkScalarToFloat(m[SkMatrix::kMScaleY]),
-                                       SkScalarToFloat(m[SkMatrix::kMTransX]),
-                                       SkScalarToFloat(m[SkMatrix::kMTransY]));
-                                       
-    ATSStyleRenderingOptions renderOpts = kATSStyleApplyAntiAliasing;
-    switch (fRec.getHinting()) {
-        case SkPaint::kNo_Hinting:
-        case SkPaint::kSlight_Hinting:
-            renderOpts |= kATSStyleNoHinting;
-            break;
-        case SkPaint::kNormal_Hinting:
-        case SkPaint::kFull_Hinting:
-            renderOpts |= kATSStyleApplyHints;
-            break;
-    }
-
-    ATSUFontID fontID = FMGetFontFromATSFontRef(fRec.fFontID);
-    // we put everything in the matrix, so our pt size is just 1.0
-    Fixed fixedSize = SK_Fixed1;
-    static const ATSUAttributeTag tags[] = {
-        kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag
-    };
-    static const ByteCount sizes[] = {
-        sizeof(fontID), sizeof(fixedSize), sizeof(fTransform), sizeof(renderOpts)
-    };
-    const ATSUAttributeValuePtr values[] = {
-        &fontID, &fixedSize, &fTransform, &renderOpts
-    };
-    err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags),
-                              tags, sizes, values);
-    SkASSERT(0 == err);
-
-    err = ::ATSUCreateTextLayout(&fLayout);
-    SkASSERT(0 == err);
-
-    fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
-}
-
-SkScalerContext_Mac::~SkScalerContext_Mac() {
-    ::CGColorSpaceRelease(fGrayColorSpace);
-    ::ATSUDisposeTextLayout(fLayout);
-    ::ATSUDisposeStyle(fStyle);
-}
-
-// man, we need to consider caching this, since it is just dependent on
-// fFontID, and not on any of the other settings like matrix or flags
-unsigned SkScalerContext_Mac::generateGlyphCount() const {
-    // The 'maxp' table stores the number of glyphs a offset 4, in 2 bytes
-    uint16_t numGlyphs;
-    if (SkFontHost::GetTableData(fRec.fFontID,
-                                 SkSetFourByteTag('m', 'a', 'x', 'p'),
-                                 4, 2, &numGlyphs) != 2) {
-        return 0xFFFF;
-    }
-    return SkEndian_SwapBE16(numGlyphs);
-}
-
-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;
-}
-
-static void set_glyph_metrics_on_error(SkGlyph* glyph) {
-    glyph->fRsbDelta = 0;
-    glyph->fLsbDelta = 0;
-    glyph->fWidth    = 0;
-    glyph->fHeight   = 0;
-    glyph->fTop      = 0;
-    glyph->fLeft     = 0;
-    glyph->fAdvanceX = 0;
-    glyph->fAdvanceY = 0;
-}
-
-void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
-    this->generateMetrics(glyph);
-}
-
-void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
-    GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount);
-    ATSGlyphScreenMetrics screenMetrics;
-    ATSGlyphIdealMetrics idealMetrics;
-
-    OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
-                                             &screenMetrics);
-    if (noErr != err) {
-        set_glyph_metrics_on_error(glyph);
-        return;
-    }
-    err = ATSUGlyphGetIdealMetrics(fStyle, 1, &glyphID, 0, &idealMetrics);
-    if (noErr != err) {
-        set_glyph_metrics_on_error(glyph);
-        return;
-    }
-
-    if (!fRec.fSubpixelPositioning) {
-        glyph->fAdvanceX = SkFloatToFixed(screenMetrics.deviceAdvance.x);
-        glyph->fAdvanceY = -SkFloatToFixed(screenMetrics.deviceAdvance.y);
-    } else {
-        glyph->fAdvanceX = SkFloatToFixed(idealMetrics.advance.x);
-        glyph->fAdvanceY = -SkFloatToFixed(idealMetrics.advance.y);
-    }
-
-    // specify an extra 1-pixel border, go tive CG room for its antialiasing
-    // i.e. without this, I was seeing some edges chopped off!
-    glyph->fWidth = screenMetrics.width + 2;
-    glyph->fHeight = screenMetrics.height + 2;
-    glyph->fLeft = sk_float_round2int(screenMetrics.topLeft.x) - 1;
-    glyph->fTop = -sk_float_round2int(screenMetrics.topLeft.y) - 1;
-}
-
-void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
-{
-    SkAutoMutexAcquire  ac(gFTMutex);
-    SkASSERT(fLayout);
-    
-    sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
-    CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage,
-                                              glyph.fWidth, glyph.fHeight, 8,
-                                              glyph.rowBytes(), fGrayColorSpace,
-                                              kCGImageAlphaNone);
-    if (!contextRef) {
-        SkASSERT(false);
-        return;
-    }
-    
-    ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
-    ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
-    
-    CGGlyph glyphID = glyph.getGlyphID(fBaseGlyphCount);
-    CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID);
-    CGContextSetFont(contextRef, fontRef);
-    CGContextSetFontSize(contextRef, 1);
-    CGContextSetTextMatrix(contextRef, fTransform);
-    CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft,
-                               glyph.fTop + glyph.fHeight, &glyphID, 1);
-    
-    ::CGContextRelease(contextRef);
-}
-
-#if 0
-static void convert_metrics(SkPaint::FontMetrics* dst,
-                            const ATSFontMetrics& src) {
-    dst->fTop     = -SkFloatToScalar(src.ascent);
-    dst->fAscent  = -SkFloatToScalar(src.ascent);
-    dst->fDescent = SkFloatToScalar(src.descent);
-    dst->fBottom  = SkFloatToScalar(src.descent);
-    dst->fLeading = SkFloatToScalar(src.leading);
-}
+/*
+ ** Mac Text API
+ **
+ **
+ ** Two text APIs are available on the Mac, ATSUI and CoreText.
+ **
+ ** ATSUI is available on all versions of Mac OS X, but is 32-bit only.
+ **
+ ** The replacement API, CoreText, supports both 32-bit and 64-bit builds
+ ** but is only available from Mac OS X 10.5 onwards.
+ **
+ ** To maintain support for Mac OS X 10.4, we default to ATSUI in 32-bit
+ ** builds unless SK_USE_CORETEXT is defined.
+*/
+#ifndef SK_USE_CORETEXT
+    #if TARGET_RT_64_BIT
+        #define SK_USE_CORETEXT                                     1
+    #else
+        #define SK_USE_CORETEXT                                     0
+    #endif
 #endif
 
-static void* get_font_table(ATSFontRef fontID, uint32_t tag) {
-    ByteCount size;
-    OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size);
-    if (err) {
-        return NULL;
-    }
-    void* data = sk_malloc_throw(size);
-    err = ATSFontGetTable(fontID, tag, 0, size, data, &size);
-    if (err) {
-        sk_free(data);
-        data = NULL;
-    }
-    return data;
-}
-
-static int get_be16(const void* data, size_t offset) {
-    const char* ptr = reinterpret_cast<const char*>(data);
-    uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset);
-    int n = SkEndian_SwapBE16(value);
-    // now force it to be signed
-    return n << 16 >> 16;
-}
-
-#define SFNT_HEAD_UPEM_OFFSET       18
-#define SFNT_HEAD_YMIN_OFFSET       38
-#define SFNT_HEAD_YMAX_OFFSET       42
-#define SFNT_HEAD_STYLE_OFFSET      44
-
-#define SFNT_HHEA_ASCENT_OFFSET     4
-#define SFNT_HHEA_DESCENT_OFFSET    6
-#define SFNT_HHEA_LEADING_OFFSET    8
-
-static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) {
-    void* head = get_font_table(font, 'head');
-    if (NULL == head) {
-        return false;
-    }
-    void* hhea = get_font_table(font, 'hhea');
-    if (NULL == hhea) {
-        sk_free(head);
-        return false;
-    }
-
-    int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET);
-    int ys[5];
-
-    ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET);
-    ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET);
-    ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET);
-    ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET);
-    ys[4] =  get_be16(hhea, SFNT_HHEA_LEADING_OFFSET);
-
-    // now do some cleanup, to ensure y[max,min] are really that
-    if (ys[0] > ys[1]) {
-        ys[0] = ys[1];
-    }
-    if (ys[3] < ys[2]) {
-        ys[3] = ys[2];
-    }
-
-    for (int i = 0; i < 5; i++) {
-        pts[i].set(0, SkIntToScalar(ys[i]) / upem);
-    }
-    
-    sk_free(hhea);
-    sk_free(head);
-    return true;
-}
-
-void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
-                                              SkPaint::FontMetrics* my) {
-    SkPoint pts[5];
-    
-    if (!init_vertical_metrics(fRec.fFontID, pts)) {
-        // these are not as accurate as init_vertical_metrics :(
-        ATSFontMetrics metrics;
-        ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault,
-                                  &metrics);
-        pts[0].set(0, -SkFloatToScalar(metrics.ascent));
-        pts[1].set(0, -SkFloatToScalar(metrics.ascent));
-        pts[2].set(0, -SkFloatToScalar(metrics.descent));
-        pts[3].set(0, -SkFloatToScalar(metrics.descent));
-        pts[4].set(0, SkFloatToScalar(metrics.leading));    //+ or -?
-    }
-    
-    SkMatrix m;
-    fRec.getSingleMatrix(&m);
-    m.mapPoints(pts, 5);
-
-    if (mx) {
-        mx->fTop = pts[0].fX;
-        mx->fAscent = pts[1].fX;
-        mx->fDescent = pts[2].fX;
-        mx->fBottom = pts[3].fX;
-        mx->fLeading = pts[4].fX;
-        // FIXME:
-        mx->fAvgCharWidth = 0;
-        mx->fXMin = 0;
-        mx->fXMax = 0;
-        mx->fXHeight = 0;
-    }
-    if (my) {
-        my->fTop = pts[0].fY;
-        my->fAscent = pts[1].fY;
-        my->fDescent = pts[2].fY;
-        my->fBottom = pts[3].fY;
-        my->fLeading = pts[4].fY;
-        // FIXME:
-        my->fAvgCharWidth = 0;
-        my->fXMin = 0;
-        my->fXMax = 0;
-        my->fXHeight = 0;
-    }
-}
-
-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);
-}
-
-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::CreateTypefaceFromStream(SkStream* stream) {
-    return NULL;
-}
-
-SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
-    return NULL;
-}
-
-SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
-    return new SkScalerContext_Mac(desc);
-}
-
-uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
-    uint32_t newFontID = find_default_fontID();
-    if (newFontID == fontID) {
-        newFontID = 0;
-    }
-    return newFontID;
-}
-
-SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
-                            const char familyName[],
-                            const void* data, size_t bytelength,
-                            SkTypeface::Style style) {
-    // todo: we don't know how to respect style bits
-    if (NULL == familyName && NULL != familyFace) {
-        familyFace->ref();
-        return const_cast<SkTypeface*>(familyFace);
-    } else {
-        return CreateTypeface_(familyName, style);
-    }
-}
-
-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)
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct SkSFNTHeader {
-    uint32_t    fVersion;
-    uint16_t    fNumTables;
-    uint16_t    fSearchRange;
-    uint16_t    fEntrySelector;
-    uint16_t    fRangeShift;
-};
-
-struct SkSFNTDirEntry {
-    uint32_t    fTag;
-    uint32_t    fChecksum;
-    uint32_t    fOffset;
-    uint32_t    fLength;
-};
-
-struct SfntHeader {
-    SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) {
-        ByteCount size;
-        if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) {
-            return;
-        }
-        
-        SkAutoMalloc storage(size);
-        SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get());
-        if (ATSFontGetTableDirectory(fontID, size, header, &size)) {
-            return;
-        }
-        
-        fCount = SkEndian_SwapBE16(header->fNumTables);
-        fData = header;
-        storage.detach();
-    }
-    
-    ~SfntHeader() {
-        sk_free(fData);
-    }
-    
-    int count() const { return fCount; }
-    const SkSFNTDirEntry* entries() const {
-        return reinterpret_cast<const SkSFNTDirEntry*>
-            (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader));
-    }
-    
-private:
-    int     fCount;
-    void*   fData;
-};
-        
-int SkFontHost::CountTables(SkFontID fontID) {
-    SfntHeader header(fontID, false);
-    return header.count();
-}
-
-int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
-    SfntHeader header(fontID, true);
-    int count = header.count();
-    const SkSFNTDirEntry* entry = header.entries();
-    for (int i = 0; i < count; i++) {
-        tags[i] = SkEndian_SwapBE32(entry[i].fTag);
-    }
-    return count;
-}
-
-size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
-    ByteCount size;
-    if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) {
-        return 0;
-    }
-    return size;
-}
+#if SK_USE_CORETEXT
+    #include "SkFontHost_mac_coretext.cpp"
+#else
+    #include "SkFontHost_mac_atsui.cpp"
+#endif
 
-size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
-                                size_t offset, size_t length, void* data) {
-    ByteCount size;
-    if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) {
-        return 0;
-    }
-    if (offset >= size) {
-        return 0;
-    }
-    if (offset + length > size) {
-        length = size - offset;
-    }
-    return length;
-}
 
diff --git a/src/ports/SkFontHost_mac_atsui.cpp b/src/ports/SkFontHost_mac_atsui.cpp
new file mode 100644 (file)
index 0000000..a0239e2
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ ** 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 <Carbon/Carbon.h>
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkEndian.h"
+#include "SkFloatingPoint.h"
+#include "SkPaint.h"
+#include "SkPoint.h"
+
+// Give 1MB font cache budget
+#define FONT_CACHE_MEMORY_BUDGET    (1024 * 1024)
+
+const char* gDefaultfont = "Arial"; // hard code for now
+static SkMutex      gFTMutex;
+
+static inline SkPoint F32PtToSkPoint(const Float32Point p) {
+    SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
+    return sp;
+}
+
+static inline uint32_t _rotl(uint32_t v, uint32_t r) {
+    return (v << r | v >> (32 - r));
+}
+
+class SkTypeface_Mac : public SkTypeface {
+public:
+    SkTypeface_Mac(SkTypeface::Style style, uint32_t id)
+        : SkTypeface(style, id) {}
+};
+
+#pragma mark -
+
+static uint32_t find_from_name(const char name[]) {
+    CFStringRef str = CFStringCreateWithCString(NULL, name,
+                                                kCFStringEncodingUTF8); 
+    uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault);
+    CFRelease(str);
+    return fontID;
+}
+
+static uint32_t find_default_fontID() {
+    static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" };
+
+    uint32_t fontID;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) {
+        fontID = find_from_name(gDefaultNames[i]);
+        if (fontID) {
+            return fontID;
+        }
+    }
+    sk_throw();
+    return 0;
+}
+
+static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) {
+    uint32_t fontID = 0;
+    if (NULL != name) {
+        fontID = find_from_name(name);
+    }
+    if (0 == fontID) {
+        fontID = find_default_fontID();
+    }
+    // we lie (for now) and report that we found the exact style bits
+    return new SkTypeface_Mac(style, fontID);
+}
+
+#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 generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+
+private:
+    ATSUTextLayout  fLayout;
+    ATSUStyle       fStyle;
+    CGColorSpaceRef fGrayColorSpace;
+    CGAffineTransform   fTransform;
+    
+    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);
+};
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+    // we only support 2 levels of hinting
+    SkPaint::Hinting h = rec->getHinting();
+    if (SkPaint::kSlight_Hinting == h) {
+        h = SkPaint::kNo_Hinting;
+    } else if (SkPaint::kFull_Hinting == h) {
+        h = SkPaint::kNormal_Hinting;
+    }
+    rec->setHinting(h);
+
+    // we don't support LCD text
+    if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) {
+        rec->fMaskFormat = SkMask::kA8_Format;
+    }
+}
+
+SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
+    : SkScalerContext(desc), fLayout(0), fStyle(0)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+    OSStatus err;
+    
+    err = ::ATSUCreateStyle(&fStyle);
+    SkASSERT(0 == err);
+            
+    SkMatrix    m;
+    fRec.getSingleMatrix(&m);
+    
+    fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]),
+                                       SkScalarToFloat(m[SkMatrix::kMSkewX]),
+                                       SkScalarToFloat(m[SkMatrix::kMSkewY]),
+                                       SkScalarToFloat(m[SkMatrix::kMScaleY]),
+                                       SkScalarToFloat(m[SkMatrix::kMTransX]),
+                                       SkScalarToFloat(m[SkMatrix::kMTransY]));
+                                       
+    ATSStyleRenderingOptions renderOpts = kATSStyleApplyAntiAliasing;
+    switch (fRec.getHinting()) {
+        case SkPaint::kNo_Hinting:
+        case SkPaint::kSlight_Hinting:
+            renderOpts |= kATSStyleNoHinting;
+            break;
+        case SkPaint::kNormal_Hinting:
+        case SkPaint::kFull_Hinting:
+            renderOpts |= kATSStyleApplyHints;
+            break;
+    }
+
+    ATSUFontID fontID = FMGetFontFromATSFontRef(fRec.fFontID);
+    // we put everything in the matrix, so our pt size is just 1.0
+    Fixed fixedSize = SK_Fixed1;
+    static const ATSUAttributeTag tags[] = {
+        kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag
+    };
+    static const ByteCount sizes[] = {
+        sizeof(fontID), sizeof(fixedSize), sizeof(fTransform), sizeof(renderOpts)
+    };
+    const ATSUAttributeValuePtr values[] = {
+        &fontID, &fixedSize, &fTransform, &renderOpts
+    };
+    err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags),
+                              tags, sizes, values);
+    SkASSERT(0 == err);
+
+    err = ::ATSUCreateTextLayout(&fLayout);
+    SkASSERT(0 == err);
+
+    fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
+}
+
+SkScalerContext_Mac::~SkScalerContext_Mac() {
+    ::CGColorSpaceRelease(fGrayColorSpace);
+    ::ATSUDisposeTextLayout(fLayout);
+    ::ATSUDisposeStyle(fStyle);
+}
+
+// man, we need to consider caching this, since it is just dependent on
+// fFontID, and not on any of the other settings like matrix or flags
+unsigned SkScalerContext_Mac::generateGlyphCount() const {
+    // The 'maxp' table stores the number of glyphs a offset 4, in 2 bytes
+    uint16_t numGlyphs;
+    if (SkFontHost::GetTableData(fRec.fFontID,
+                                 SkSetFourByteTag('m', 'a', 'x', 'p'),
+                                 4, 2, &numGlyphs) != 2) {
+        return 0xFFFF;
+    }
+    return SkEndian_SwapBE16(numGlyphs);
+}
+
+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;
+}
+
+static void set_glyph_metrics_on_error(SkGlyph* glyph) {
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+    glyph->fWidth    = 0;
+    glyph->fHeight   = 0;
+    glyph->fTop      = 0;
+    glyph->fLeft     = 0;
+    glyph->fAdvanceX = 0;
+    glyph->fAdvanceY = 0;
+}
+
+void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
+    this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
+    GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount);
+    ATSGlyphScreenMetrics screenMetrics;
+    ATSGlyphIdealMetrics idealMetrics;
+
+    OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
+                                             &screenMetrics);
+    if (noErr != err) {
+        set_glyph_metrics_on_error(glyph);
+        return;
+    }
+    err = ATSUGlyphGetIdealMetrics(fStyle, 1, &glyphID, 0, &idealMetrics);
+    if (noErr != err) {
+        set_glyph_metrics_on_error(glyph);
+        return;
+    }
+
+    if (!fRec.fSubpixelPositioning) {
+        glyph->fAdvanceX = SkFloatToFixed(screenMetrics.deviceAdvance.x);
+        glyph->fAdvanceY = -SkFloatToFixed(screenMetrics.deviceAdvance.y);
+    } else {
+        glyph->fAdvanceX = SkFloatToFixed(idealMetrics.advance.x);
+        glyph->fAdvanceY = -SkFloatToFixed(idealMetrics.advance.y);
+    }
+
+    // specify an extra 1-pixel border, go tive CG room for its antialiasing
+    // i.e. without this, I was seeing some edges chopped off!
+    glyph->fWidth = screenMetrics.width + 2;
+    glyph->fHeight = screenMetrics.height + 2;
+    glyph->fLeft = sk_float_round2int(screenMetrics.topLeft.x) - 1;
+    glyph->fTop = -sk_float_round2int(screenMetrics.topLeft.y) - 1;
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
+{
+    SkAutoMutexAcquire  ac(gFTMutex);
+    SkASSERT(fLayout);
+    
+    sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
+    CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage,
+                                              glyph.fWidth, glyph.fHeight, 8,
+                                              glyph.rowBytes(), fGrayColorSpace,
+                                              kCGImageAlphaNone);
+    if (!contextRef) {
+        SkASSERT(false);
+        return;
+    }
+    
+    ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
+    ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
+    
+    CGGlyph glyphID = glyph.getGlyphID(fBaseGlyphCount);
+    CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID);
+    CGContextSetFont(contextRef, fontRef);
+    CGContextSetFontSize(contextRef, 1);
+    CGContextSetTextMatrix(contextRef, fTransform);
+    CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft,
+                               glyph.fTop + glyph.fHeight, &glyphID, 1);
+    
+    ::CGContextRelease(contextRef);
+}
+
+#if 0
+static void convert_metrics(SkPaint::FontMetrics* dst,
+                            const ATSFontMetrics& src) {
+    dst->fTop     = -SkFloatToScalar(src.ascent);
+    dst->fAscent  = -SkFloatToScalar(src.ascent);
+    dst->fDescent = SkFloatToScalar(src.descent);
+    dst->fBottom  = SkFloatToScalar(src.descent);
+    dst->fLeading = SkFloatToScalar(src.leading);
+}
+#endif
+
+static void* get_font_table(ATSFontRef fontID, uint32_t tag) {
+    ByteCount size;
+    OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size);
+    if (err) {
+        return NULL;
+    }
+    void* data = sk_malloc_throw(size);
+    err = ATSFontGetTable(fontID, tag, 0, size, data, &size);
+    if (err) {
+        sk_free(data);
+        data = NULL;
+    }
+    return data;
+}
+
+static int get_be16(const void* data, size_t offset) {
+    const char* ptr = reinterpret_cast<const char*>(data);
+    uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset);
+    int n = SkEndian_SwapBE16(value);
+    // now force it to be signed
+    return n << 16 >> 16;
+}
+
+#define SFNT_HEAD_UPEM_OFFSET       18
+#define SFNT_HEAD_YMIN_OFFSET       38
+#define SFNT_HEAD_YMAX_OFFSET       42
+#define SFNT_HEAD_STYLE_OFFSET      44
+
+#define SFNT_HHEA_ASCENT_OFFSET     4
+#define SFNT_HHEA_DESCENT_OFFSET    6
+#define SFNT_HHEA_LEADING_OFFSET    8
+
+static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) {
+    void* head = get_font_table(font, 'head');
+    if (NULL == head) {
+        return false;
+    }
+    void* hhea = get_font_table(font, 'hhea');
+    if (NULL == hhea) {
+        sk_free(head);
+        return false;
+    }
+
+    int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET);
+    int ys[5];
+
+    ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET);
+    ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET);
+    ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET);
+    ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET);
+    ys[4] =  get_be16(hhea, SFNT_HHEA_LEADING_OFFSET);
+
+    // now do some cleanup, to ensure y[max,min] are really that
+    if (ys[0] > ys[1]) {
+        ys[0] = ys[1];
+    }
+    if (ys[3] < ys[2]) {
+        ys[3] = ys[2];
+    }
+
+    for (int i = 0; i < 5; i++) {
+        pts[i].set(0, SkIntToScalar(ys[i]) / upem);
+    }
+    
+    sk_free(hhea);
+    sk_free(head);
+    return true;
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
+                                              SkPaint::FontMetrics* my) {
+    SkPoint pts[5];
+    
+    if (!init_vertical_metrics(fRec.fFontID, pts)) {
+        // these are not as accurate as init_vertical_metrics :(
+        ATSFontMetrics metrics;
+        ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault,
+                                  &metrics);
+        pts[0].set(0, -SkFloatToScalar(metrics.ascent));
+        pts[1].set(0, -SkFloatToScalar(metrics.ascent));
+        pts[2].set(0, -SkFloatToScalar(metrics.descent));
+        pts[3].set(0, -SkFloatToScalar(metrics.descent));
+        pts[4].set(0, SkFloatToScalar(metrics.leading));    //+ or -?
+    }
+    
+    SkMatrix m;
+    fRec.getSingleMatrix(&m);
+    m.mapPoints(pts, 5);
+
+    if (mx) {
+        mx->fTop = pts[0].fX;
+        mx->fAscent = pts[1].fX;
+        mx->fDescent = pts[2].fX;
+        mx->fBottom = pts[3].fX;
+        mx->fLeading = pts[4].fX;
+        // FIXME:
+        mx->fAvgCharWidth = 0;
+        mx->fXMin = 0;
+        mx->fXMax = 0;
+        mx->fXHeight = 0;
+    }
+    if (my) {
+        my->fTop = pts[0].fY;
+        my->fAscent = pts[1].fY;
+        my->fDescent = pts[2].fY;
+        my->fBottom = pts[3].fY;
+        my->fLeading = pts[4].fY;
+        // FIXME:
+        my->fAvgCharWidth = 0;
+        my->fXMin = 0;
+        my->fXMax = 0;
+        my->fXHeight = 0;
+    }
+}
+
+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);
+}
+
+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::CreateTypefaceFromStream(SkStream* stream) {
+    return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    return NULL;
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    return new SkScalerContext_Mac(desc);
+}
+
+uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
+    uint32_t newFontID = find_default_fontID();
+    if (newFontID == fontID) {
+        newFontID = 0;
+    }
+    return newFontID;
+}
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                            const char familyName[],
+                            const void* data, size_t bytelength,
+                            SkTypeface::Style style) {
+    // todo: we don't know how to respect style bits
+    if (NULL == familyName && NULL != familyFace) {
+        familyFace->ref();
+        return const_cast<SkTypeface*>(familyFace);
+    } else {
+        return CreateTypeface_(familyName, style);
+    }
+}
+
+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)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkSFNTHeader {
+    uint32_t    fVersion;
+    uint16_t    fNumTables;
+    uint16_t    fSearchRange;
+    uint16_t    fEntrySelector;
+    uint16_t    fRangeShift;
+};
+
+struct SkSFNTDirEntry {
+    uint32_t    fTag;
+    uint32_t    fChecksum;
+    uint32_t    fOffset;
+    uint32_t    fLength;
+};
+
+struct SfntHeader {
+    SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) {
+        ByteCount size;
+        if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) {
+            return;
+        }
+        
+        SkAutoMalloc storage(size);
+        SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get());
+        if (ATSFontGetTableDirectory(fontID, size, header, &size)) {
+            return;
+        }
+        
+        fCount = SkEndian_SwapBE16(header->fNumTables);
+        fData = header;
+        storage.detach();
+    }
+    
+    ~SfntHeader() {
+        sk_free(fData);
+    }
+    
+    int count() const { return fCount; }
+    const SkSFNTDirEntry* entries() const {
+        return reinterpret_cast<const SkSFNTDirEntry*>
+            (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader));
+    }
+    
+private:
+    int     fCount;
+    void*   fData;
+};
+        
+int SkFontHost::CountTables(SkFontID fontID) {
+    SfntHeader header(fontID, false);
+    return header.count();
+}
+
+int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
+    SfntHeader header(fontID, true);
+    int count = header.count();
+    const SkSFNTDirEntry* entry = header.entries();
+    for (int i = 0; i < count; i++) {
+        tags[i] = SkEndian_SwapBE32(entry[i].fTag);
+    }
+    return count;
+}
+
+size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
+    ByteCount size;
+    if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) {
+        return 0;
+    }
+    return size;
+}
+
+size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
+                                size_t offset, size_t length, void* data) {
+    ByteCount size;
+    if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) {
+        return 0;
+    }
+    if (offset >= size) {
+        return 0;
+    }
+    if (offset + length > size) {
+        length = size - offset;
+    }
+    return length;
+}
+
diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp
new file mode 100644 (file)
index 0000000..e54fb78
--- /dev/null
@@ -0,0 +1,847 @@
+/*
+ ** 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 <vector>
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkString.h"
+#include "SkPaint.h"
+
+
+
+
+
+//============================================================================
+//      Constants
+//----------------------------------------------------------------------------
+static const SkFontID kSkInvalidFontID                              = 0;
+
+static const size_t FONT_CACHE_MEMORY_BUDGET                        = 1024 * 1024;
+static const char  *FONT_DEFAULT_NAME                               = "Lucida Sans";
+
+
+
+
+
+//============================================================================
+//      Types
+//----------------------------------------------------------------------------
+// Native font info
+typedef struct {
+    SkString                name;
+    SkTypeface::Style       style;
+    SkFontID                fontID;
+    CTFontRef               fontRef;
+} SkNativeFontInfo;
+
+typedef std::vector<SkNativeFontInfo>                               SkNativeFontInfoList;
+typedef SkNativeFontInfoList::iterator                              SkNativeFontInfoListIterator;
+typedef SkNativeFontInfoList::const_iterator                        SkNativeFontInfoListConstIterator;
+
+
+
+
+
+//============================================================================
+//      Macros
+//----------------------------------------------------------------------------
+// Release a CFTypeRef
+#ifndef CFSafeRelease
+#define CFSafeRelease(_object)                                      \
+    do                                                              \
+        {                                                           \
+        if ((_object) != NULL)                                      \
+            {                                                       \
+            CFRelease((CFTypeRef) (_object));                       \
+            (_object) = NULL;                                       \
+            }                                                       \
+        }                                                           \
+    while (false)
+#endif
+
+
+
+
+
+//============================================================================
+//      SkNativeFontCache
+//----------------------------------------------------------------------------
+#pragma mark -
+class SkNativeFontCache {
+public:
+                                        SkNativeFontCache(void);
+    virtual                            ~SkNativeFontCache(void);
+
+
+    // Is a font ID valid?
+    bool                                IsValid(SkFontID fontID);
+    
+    
+    // Get a font
+    CTFontRef                           GetFont(SkFontID fontID);
+    SkNativeFontInfo                    GetFontInfo(const SkString &theName, SkTypeface::Style theStyle);
+    
+    
+    // Create a font
+    SkNativeFontInfo                    CreateFont(const SkString &theName, SkTypeface::Style theStyle);
+
+
+    // Get the font table
+    static SkNativeFontCache           *Get(void);
+
+
+private:
+    CTFontRef                           CreateNativeFont(const SkString &name, SkTypeface::Style style);
+
+
+private:
+    SkNativeFontInfoList                mFonts;
+    SkMutex                             mMutex;
+};
+
+SkNativeFontCache::SkNativeFontCache(void)
+{   SkAutoMutexAcquire      acquireLock(mMutex);
+    SkNativeFontInfo        fontInfo;
+
+
+    // Initialise ourselves
+    //
+    // SkTypeface uses a uint32_t to identify fonts, however CoreText font references
+    // are opaque pointers.
+    //
+    // To support 64-bit builds, we need a separate index to look up a 64-bit font
+    // reference from its 32-bit SkFontID. As an ID of 0 is reserved, we insert a
+    // dummy entry into the cache so we can use the array index as the font ID.
+    //
+    // This could be simplified if SkFontID was changed to a intptr_t, and SkTypeface
+    // returned an SkFontID from uniqueID().
+    fontInfo.name    = SkString("__SkNativeFontCache__");
+    fontInfo.style   = SkTypeface::kNormal;
+    fontInfo.fontID  = kSkInvalidFontID;
+    fontInfo.fontRef = NULL;
+        
+    mFonts.push_back(fontInfo);
+}
+
+SkNativeFontCache::~SkNativeFontCache(void)
+{   SkAutoMutexAcquire                  acquireLock(mMutex);
+    SkNativeFontInfoListIterator        theIter;
+
+
+    // Clean up
+    for (theIter = mFonts.begin(); theIter != mFonts.end(); theIter++)
+        CFSafeRelease(theIter->fontRef);
+}
+
+bool SkNativeFontCache::IsValid(SkFontID fontID)
+{   SkAutoMutexAcquire  acquireLock(mMutex);
+    bool                isValid;
+
+
+    // Check the ID
+    isValid = (fontID >= 1 && fontID < mFonts.size());
+    return(isValid);
+}
+
+CTFontRef SkNativeFontCache::GetFont(SkFontID fontID)
+{   SkAutoMutexAcquire  acquireLock(mMutex);
+
+
+    // Validate our parameters
+    SkASSERT(fontID >= 1 && fontID < mFonts.size());
+
+
+    // Get the font
+    return(mFonts.at(fontID).fontRef);
+}
+
+SkNativeFontInfo SkNativeFontCache::GetFontInfo(const SkString &theName, SkTypeface::Style theStyle)
+{   SkAutoMutexAcquire              acquireLock(mMutex);
+    SkNativeFontInfo                fontInfo;
+    SkNativeFontInfoListIterator    theIter;
+
+
+    // Validate our parameters
+    SkASSERT(!theName.isEmpty());
+
+
+    // Get the state we need
+    fontInfo.style   = SkTypeface::kNormal;
+    fontInfo.fontID  = kSkInvalidFontID;
+    fontInfo.fontRef = NULL;
+
+
+    // Get the font
+    for (theIter = mFonts.begin(); theIter != mFonts.end(); theIter++)
+        {
+        if (theIter->name == theName && theIter->style == theStyle)
+            return(*theIter);
+        }
+    
+    return(fontInfo);
+}
+
+SkNativeFontInfo SkNativeFontCache::CreateFont(const SkString &theName, SkTypeface::Style theStyle)
+{   SkAutoMutexAcquire      acquireLock(mMutex);
+    SkNativeFontInfo        fontInfo;
+
+
+    // Validate our parameters
+    SkASSERT(!theName.isEmpty());
+
+
+    // Create the font
+    fontInfo.name    = theName;
+    fontInfo.style   = theStyle;
+    fontInfo.fontID  = mFonts.size();
+    fontInfo.fontRef = CreateNativeFont(theName, theStyle);
+
+    mFonts.push_back(fontInfo);
+    return(fontInfo);
+}
+
+SkNativeFontCache *SkNativeFontCache::Get(void)
+{   static SkNativeFontCache    sInstance;
+
+
+    // Get the instance
+    //
+    // We use a local static for well-defined static initialisation order.
+    return(&sInstance);
+}
+
+///////////////////////////////////////////////////////////////////////////
+CTFontRef SkNativeFontCache::CreateNativeFont(const SkString &theName, SkTypeface::Style theStyle)
+{   CFMutableDictionaryRef      cfAttributes, cfTraits;
+    CFNumberRef                 cfFontTraits;
+    CTFontSymbolicTraits        ctFontTraits;
+    CTFontDescriptorRef         ctFontDesc;
+    CFStringRef                 cfFontName;
+    CTFontRef                   ctFont;
+
+
+    // Get the state we need
+    ctFontDesc   = NULL;
+    ctFont       = NULL;
+    ctFontTraits = 0;
+
+    if (theStyle & SkTypeface::kBold)
+        ctFontTraits |= kCTFontBoldTrait;
+
+    if (theStyle & SkTypeface::kItalic)
+        ctFontTraits |= kCTFontItalicTrait;
+
+
+    // Create the font info
+    cfFontName   = CFStringCreateWithCString(NULL, theName.c_str(), kCFStringEncodingUTF8);
+    cfFontTraits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits);
+    cfAttributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    cfTraits     = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+
+    // Create the font
+    //
+    // Fonts are scaled using the Sk matrix, so we always request a font of size 1.
+    if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL)
+        {
+        CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
+
+        CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
+        CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute,     cfTraits);
+
+        ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
+        if (ctFontDesc != NULL)
+            ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 1.0, NULL);
+
+        }
+
+
+    // Clean up
+    CFSafeRelease(cfFontName);
+    CFSafeRelease(cfFontTraits);
+    CFSafeRelease(cfAttributes);
+    CFSafeRelease(cfTraits);
+    CFSafeRelease(ctFontDesc);
+
+    return(ctFont);
+}
+
+
+
+
+
+//============================================================================
+//      SkTypeface_Mac
+//----------------------------------------------------------------------------
+#pragma mark -
+class SkTypeface_Mac : public SkTypeface {
+public:
+                                        SkTypeface_Mac(SkTypeface::Style style, uint32_t fontID);
+};
+
+
+SkTypeface_Mac::SkTypeface_Mac(SkTypeface::Style style, uint32_t fontID)
+        : SkTypeface(style, fontID)
+{
+}
+
+
+
+
+
+//============================================================================
+//      SkScalerContext_Mac
+//----------------------------------------------------------------------------
+#pragma mark -
+class SkScalerContext_Mac : public SkScalerContext {
+public:
+                                        SkScalerContext_Mac(const SkDescriptor* desc);
+    virtual                            ~SkScalerContext_Mac(void);
+
+
+protected:
+    unsigned                            generateGlyphCount(void) const;
+    uint16_t                            generateCharToGlyph(SkUnichar uni);
+    void                                generateAdvance(SkGlyph* glyph);
+    void                                generateMetrics(SkGlyph* glyph);
+    void                                generateImage(const SkGlyph& glyph);
+    void                                generatePath( const SkGlyph& glyph, SkPath* path);
+    void                                generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+
+
+private:
+    static void                         CTPathElement(void *info, const CGPathElement *element);
+
+
+private:
+    CGColorSpaceRef                     mColorSpace;
+    CGAffineTransform                   mTransform;
+
+    CTFontRef                           mFont;
+    uint16_t                            mGlyphCount;
+};
+
+SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
+        : SkScalerContext(desc)
+{   CFIndex             numGlyphs;
+    CTFontRef           ctFont;
+    SkMatrix            skMatrix;
+
+
+
+    // Get the state we need
+    fRec.getSingleMatrix(&skMatrix);
+
+    ctFont    = SkNativeFontCache::Get()->GetFont(fRec.fFontID);
+    numGlyphs = CTFontGetGlyphCount(ctFont);
+    SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
+
+
+    // Initialise ourselves
+    mColorSpace = CGColorSpaceCreateDeviceGray();
+    mTransform  = CGAffineTransformMake(SkScalarToFloat(skMatrix[SkMatrix::kMScaleX]),
+                                        SkScalarToFloat(skMatrix[SkMatrix::kMSkewX]),
+                                        SkScalarToFloat(skMatrix[SkMatrix::kMSkewY]),
+                                        SkScalarToFloat(skMatrix[SkMatrix::kMScaleY]),
+                                        SkScalarToFloat(skMatrix[SkMatrix::kMTransX]),
+                                        SkScalarToFloat(skMatrix[SkMatrix::kMTransY]));
+
+    mFont       = CTFontCreateCopyWithAttributes(ctFont, 0.0, &mTransform, NULL);
+    mGlyphCount = (uint16_t) numGlyphs;
+}
+
+SkScalerContext_Mac::~SkScalerContext_Mac(void)
+{
+
+    // Clean up
+    CFSafeRelease(mColorSpace);
+    CFSafeRelease(mFont);
+}
+
+unsigned SkScalerContext_Mac::generateGlyphCount(void) const
+{
+    return(mGlyphCount);
+}
+
+uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
+{   CGGlyph     cgGlyph;
+    UniChar     theChar;
+
+
+    // Validate our parameters and state
+    SkASSERT(uni             <= 0x0000FFFF);
+    SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
+
+
+    // Get the glyph
+    theChar = (UniChar) uni;
+
+    if (!CTFontGetGlyphsForCharacters(mFont, &theChar, &cgGlyph, 1))
+        cgGlyph = 0;
+
+    return(cgGlyph);
+}
+
+void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph)
+{
+    this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph)
+{   CGSize      theAdvance;
+    CGRect      theBounds;
+    CGGlyph     cgGlyph;
+
+
+
+    // Get the state we need
+    cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
+
+    CTFontGetBoundingRectsForGlyphs(mFont, kCTFontDefaultOrientation, &cgGlyph, &theBounds,  1);
+    CTFontGetAdvancesForGlyphs(     mFont, kCTFontDefaultOrientation, &cgGlyph, &theAdvance, 1);
+
+
+
+    // Adjust the bounds
+    //
+    // CTFontGetBoundingRectsForGlyphs ignores the font transform, so we need
+    // to transform the bounding box ourselves.
+    //
+    // The bounds are also expanded by 1 pixel, to give CG room for anti-aliasing.
+    theBounds = CGRectApplyAffineTransform(theBounds, mTransform);
+    theBounds = CGRectInset(theBounds, -1, -1);
+
+
+
+    // Get the metrics
+    glyph->zeroMetrics();
+    glyph->fAdvanceX =  SkFloatToFixed(theAdvance.width);
+    glyph->fAdvanceY = -SkFloatToFixed(theAdvance.height);
+    glyph->fWidth    =  sk_float_round2int(theBounds.size.width);
+    glyph->fHeight   =  sk_float_round2int(theBounds.size.height);
+    glyph->fTop      = -sk_float_round2int(CGRectGetMaxY(theBounds));
+    glyph->fLeft     =  sk_float_round2int(CGRectGetMinX(theBounds));
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
+{   CGContextRef        cgContext;
+    CGGlyph             cgGlyph;
+    CGFontRef           cgFont;
+
+
+    // Get the state we need
+    sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
+
+    cgGlyph   = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
+    cgFont    = CTFontCopyGraphicsFont(mFont, NULL);
+    cgContext = CGBitmapContextCreate(  glyph.fImage, glyph.fWidth, glyph.fHeight, 8,
+                                        glyph.rowBytes(), mColorSpace, kCGImageAlphaNone);
+
+
+    // Draw the glyph
+    if (cgFont != NULL && cgContext != NULL)
+        {
+        CGContextSetGrayFillColor(  cgContext, 1.0, 1.0);
+        CGContextSetTextDrawingMode(cgContext, kCGTextFill);
+        CGContextSetFont(           cgContext, cgFont);
+        CGContextSetFontSize(       cgContext, 1.0);
+        CGContextSetTextMatrix(     cgContext, mTransform);
+        CGContextShowGlyphsAtPoint( cgContext, -glyph.fLeft, glyph.fTop + glyph.fHeight, &cgGlyph, 1);
+        }
+
+
+    // Clean up
+    CFSafeRelease(cgFont);
+    CFSafeRelease(cgContext);
+}
+
+void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
+{   CGGlyph     cgGlyph;
+    CGPathRef   cgPath;
+
+
+    // Get the state we need
+    cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
+    cgPath  = CTFontCreatePathForGlyph(mFont, cgGlyph, NULL);
+
+
+    // Get the path
+    path->reset();
+
+    if (cgPath != NULL)
+        CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
+
+    CFSafeRelease(cgPath);
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my)
+{   SkPaint::FontMetrics        theMetrics;
+    CGRect                      theBounds;
+
+
+    // Get the state we need
+    theBounds = CTFontGetBoundingBox(mFont);
+
+
+    // Get the metrics
+    theMetrics.fTop          = -CGRectGetMaxY(theBounds);
+    theMetrics.fAscent       = -CTFontGetAscent(mFont);
+    theMetrics.fDescent      =  CTFontGetDescent(mFont);
+    theMetrics.fBottom       = -CGRectGetMinY(theBounds);
+    theMetrics.fLeading      =  CTFontGetLeading(mFont);
+    theMetrics.fAvgCharWidth =  CGRectGetWidth(theBounds);
+    theMetrics.fXMin         =  CGRectGetMinX(theBounds);
+    theMetrics.fXMax         =  CGRectGetMaxX(theBounds);
+    theMetrics.fXHeight      =  CTFontGetXHeight(mFont);
+
+
+    // Return the metrics
+    SkASSERT(theMetrics.fTop          <= 0.0);
+    SkASSERT(theMetrics.fAscent       <= 0.0);
+    SkASSERT(theMetrics.fDescent      >= 0.0);
+    SkASSERT(theMetrics.fBottom       >= 0.0);
+    SkASSERT(theMetrics.fLeading      >= 0.0);
+    SkASSERT(theMetrics.fAvgCharWidth >= 0.0);
+    SkASSERT(theMetrics.fXMin         <= 0.0);
+    SkASSERT(theMetrics.fXMax         >  0.0);
+    SkASSERT(theMetrics.fXHeight      >= 0.0);
+
+    if (mx != NULL)
+        *mx = theMetrics;
+    
+    if (my != NULL)
+        *my = theMetrics;
+}
+
+void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element)
+{   SkPath      *skPath = (SkPath *) info;
+
+
+    // Process the path element
+    switch (element->type) {
+        case kCGPathElementMoveToPoint:
+            skPath->moveTo( element->points[0].x, -element->points[0].y);
+            break;
+
+        case kCGPathElementAddLineToPoint:
+            skPath->lineTo( element->points[0].x, -element->points[0].y);
+            break;
+
+        case kCGPathElementAddQuadCurveToPoint:
+            skPath->quadTo( element->points[0].x, -element->points[0].y,
+                            element->points[1].x, -element->points[1].y);
+            break;
+
+        case kCGPathElementAddCurveToPoint:
+            skPath->cubicTo(element->points[0].x, -element->points[0].y,
+                            element->points[1].x, -element->points[1].y,
+                            element->points[2].x, -element->points[2].y);
+            break;
+
+        case kCGPathElementCloseSubpath:
+            skPath->close();
+            break;
+        
+        default:
+            SkASSERT("Unknown path element!");
+            break;
+        }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////
+#pragma mark -
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                            const char familyName[],
+                            const void* data, size_t bytelength,
+                            SkTypeface::Style style)
+{   SkTypeface              *theTypeface;
+    SkNativeFontCache       *fontTable;
+    SkNativeFontInfo        fontInfo;
+    SkString                fontName;
+
+
+    // Get the state we need
+    fontName  = SkString(familyName);
+    fontTable = SkNativeFontCache::Get();
+
+
+    // Clone an existing typeface
+    if (familyName == NULL && familyFace != NULL)
+        {
+        familyFace->ref();
+        return(const_cast<SkTypeface*>(familyFace));
+        }
+
+
+    // Get the native font
+    fontInfo = fontTable->GetFontInfo(fontName, style);
+    if (fontInfo.fontID == kSkInvalidFontID)
+        fontInfo = fontTable->CreateFont(fontName, style);
+
+
+    // Create the typeface
+    theTypeface = new SkTypeface_Mac(fontInfo.style, fontInfo.fontID);
+    return(theTypeface);
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream)
+{
+    SkASSERT(!"SkFontHost::CreateTypefaceFromStream unimplemented");
+    return(NULL);
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[])
+{
+    SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented");
+    return(NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool SkFontHost::ValidFontID(SkFontID uniqueID)
+{
+
+    // Check the font ID
+    return(SkNativeFontCache::Get()->IsValid(uniqueID));
+}
+
+SkStream* SkFontHost::OpenStream(SkFontID uniqueID)
+{
+    SkASSERT(!"SkFontHost::OpenStream unimplemented");
+    return(NULL);
+}
+
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index)
+{
+    SkASSERT(!"SkFontHost::GetFileName unimplemented");
+    return(0);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream)
+{
+    SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream)
+{
+    SkASSERT(!"SkFontHost::Deserialize unimplemented");
+    return(NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc)
+{
+    return new SkScalerContext_Mac(desc);
+}
+
+uint32_t SkFontHost::NextLogicalFont(uint32_t fontID)
+{   SkTypeface      *typeFace;
+    uint32_t        newFontID;
+
+
+    // Get the state we need
+    newFontID = kSkInvalidFontID;
+    typeFace  = CreateTypeface(NULL, FONT_DEFAULT_NAME, NULL, 0, SkTypeface::kNormal);
+
+    if (typeFace == NULL)
+        return(0);
+
+
+    // Get the next font
+    //
+    // When we're passed in the default font, we've reached the end.
+    newFontID = typeFace->uniqueID();
+    if (newFontID == fontID)
+        newFontID = 0;
+
+
+    // Clean up
+    typeFace->unref();
+
+    return(newFontID);
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec)
+{
+    // we only support 2 levels of hinting
+    SkPaint::Hinting h = rec->getHinting();
+    if (SkPaint::kSlight_Hinting == h) {
+        h = SkPaint::kNo_Hinting;
+    } else if (SkPaint::kFull_Hinting == h) {
+        h = SkPaint::kNormal_Hinting;
+    }
+    rec->setHinting(h);
+
+    // we don't support LCD text
+    if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) {
+        rec->fMaskFormat = SkMask::kA8_Format;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+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)
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::SetSubpixelOrientation(SkFontHost::LCDOrientation orientation)
+{
+    SkASSERT(!"SkFontHost::SetSubpixelOrientation unimplemented");
+}
+
+SkFontHost::LCDOrientation SkFontHost::GetSubpixelOrientation(void)
+{
+    SkASSERT(!"SkFontHost::GetSubpixelOrientation unimplemented");
+    return kHorizontal_LCDOrientation;
+}
+
+void SkFontHost::SetSubpixelOrder(SkFontHost::LCDOrder order)
+{
+    SkASSERT(!"SkFontHost::SetSubpixelOrder unimplemented");
+}
+
+SkFontHost::LCDOrder SkFontHost::GetSubpixelOrder(void)
+{
+    SkASSERT(!"SkFontHost::GetSubpixelOrder unimplemented");
+    return kRGB_LCDOrder;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+int SkFontHost::CountTables(SkFontID fontID)
+{   int             numTables;
+    CFArrayRef      cfArray;
+    CTFontRef       ctFont;
+
+
+    // Get the state we need
+    ctFont    = SkNativeFontCache::Get()->GetFont(fontID);
+    cfArray   = CTFontCopyAvailableTables(ctFont, kCTFontTableOptionNoOptions);
+    numTables = 0;
+
+
+    // Get the table count
+    if (cfArray != NULL)
+        {
+        numTables = CFArrayGetCount(cfArray);
+        CFSafeRelease(cfArray);
+        }
+
+    return(numTables);
+}
+
+int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[])
+{   int             n, numTables;
+    CFArrayRef      cfArray;
+    CTFontRef       ctFont;
+
+
+    // Get the state we need
+    ctFont    = SkNativeFontCache::Get()->GetFont(fontID);
+    cfArray   = CTFontCopyAvailableTables(ctFont, kCTFontTableOptionNoOptions);
+    numTables = 0;
+
+
+    // Get the table tags
+    if (cfArray != NULL)
+        {
+        numTables = CFArrayGetCount(cfArray);
+        for (n = 0; n < numTables; n++)
+            tags[n] = (SkFontTableTag) ((uintptr_t) CFArrayGetValueAtIndex(cfArray, n));
+
+        CFSafeRelease(cfArray);
+        }
+
+    return(numTables);
+}
+
+size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag)
+{   size_t      theSize;
+    CTFontRef   ctFont;
+    CFDataRef   cfData;
+
+
+    // Get the state we need
+    ctFont  = SkNativeFontCache::Get()->GetFont(fontID);
+    cfData  = CTFontCopyTable(ctFont, (CTFontTableTag) tag, kCTFontTableOptionNoOptions);
+    theSize = 0;
+
+
+    // Get the data size
+    if (cfData != NULL)
+        {
+        theSize = CFDataGetLength(cfData);
+        CFSafeRelease(cfData);
+        }
+    
+    return(theSize);
+}
+
+size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
+                                size_t offset, size_t length, void* data)
+{   size_t          theSize;
+    CTFontRef       ctFont;
+    CFDataRef       cfData;
+
+
+    // Get the state we need
+    ctFont  = SkNativeFontCache::Get()->GetFont(fontID);
+    cfData  = CTFontCopyTable(ctFont, (CTFontTableTag) tag, kCTFontTableOptionNoOptions);
+    theSize = 0;
+
+
+    // Get the data
+    if (cfData != NULL)
+        theSize = CFDataGetLength(cfData);
+
+    if (offset >= theSize)
+        return 0;
+
+    if ((offset + length) > theSize)
+        length = theSize - offset;
+
+    memcpy(data, CFDataGetBytePtr(cfData) + offset, length);
+    return(length);
+}
+
+
+
+