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