<commit_before>/*
 ** 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 "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);
};

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.fHints) {
        case kNo_Hints:
            renderOpts |= kATSStyleNoHinting;
            break;
        case kSubpixel_Hints:
            // hmmm, need to support subpixel... from path?
            renderOpts |= kATSStyleNoHinting;
            break;
        case kNormal_Hints:
            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 (kNormal_Hints == fRec.fHints) {
        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,<commit_msg>Mac: fix SkFontHost_mac.cpp to work with the new hinting interface.<commit_after>/*
 ** 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 "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);
};

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,
{'fraction_non_alphanumeric': 0.10118043844856661, 'fraction_numerical': 0.011804384485666104, 'mean_word_length': 3.005177847816299, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7433734', 'n_tokens_mistral': 5886, 'n_tokens_neox': 5608, 'n_words': 1283}