blob: f0e3a933727c8185c686bacaa0beb1bfd6422571 [file] [log] [blame]
/*
** 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 "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));
}
// 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;
CGColorSpaceRef fGrayColorSpace;
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);
fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
}
SkScalerContext_Mac::~SkScalerContext_Mac()
{
::CGColorSpaceRelease(fGrayColorSpace);
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;
}
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 metrics;
OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
&metrics);
if (noErr != err) {
set_glyph_metrics_on_error(glyph);
} else {
glyph->fAdvanceX = SkFloatToFixed(metrics.deviceAdvance.x);
glyph->fAdvanceY = -SkFloatToFixed(metrics.deviceAdvance.y);
glyph->fWidth = metrics.width;
glyph->fHeight = metrics.height;
glyph->fLeft = sk_float_round2int(metrics.topLeft.x);
glyph->fTop = -sk_float_round2int(metrics.topLeft.y);
}
}
void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
SkPaint::FontMetrics* my) {
#if 0
OSStatus ATSFontGetVerticalMetrics (
ATSFontRef iFont,
ATSOptionFlags iOptions,
ATSFontMetrics *oMetrics
);
#endif
//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);
SkASSERT(fLayout);
OSStatus err;
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);
ATSUAttributeTag tag = kATSUCGContextTag;
ByteCount size = sizeof(CGContextRef);
ATSUAttributeValuePtr value = &contextRef;
err = ::ATSUSetLayoutControls(fLayout, 1, &tag, &size, &value);
SkASSERT(!err);
err = ::ATSUDrawText(fLayout, kATSUFromTextBeginning, kATSUToTextEnd,
SkIntToFixed(-glyph.fLeft),
SkIntToFixed(glyph.fTop + glyph.fHeight));
SkASSERT(!err);
::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::CreateTypefaceFromStream(SkStream* stream) {
//Should not be used on Mac, keep linker happy
SkASSERT(false);
return CreateTypeface_(gDefaultfont,SkTypeface::kNormal);
}
SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
// TODO
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);
}
SkTypeface* SkFontHost::CreateTypeface(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)
}