aboutsummaryrefslogtreecommitdiffstats
path: root/src/ports/SkFontHost_mac.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ports/SkFontHost_mac.cpp')
-rwxr-xr-xsrc/ports/SkFontHost_mac.cpp579
1 files changed, 579 insertions, 0 deletions
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
new file mode 100755
index 0000000..4f51dea
--- /dev/null
+++ b/src/ports/SkFontHost_mac.cpp
@@ -0,0 +1,579 @@
+/*
+ ** 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;
+
+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::CreateTypeface(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);
+}
+
+
+ /** 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)
+}
+