aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ports
diff options
context:
space:
mode:
authorGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-05-19 13:47:05 +0000
committerGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-05-19 13:47:05 +0000
commitfeda2f90a4ad9625e14d8cb02a90b9644d803dd4 (patch)
treec44f2f0b45097e83a3f14d9aba6a88fb3c7c73d0 /src/ports
parente89d3ec443563a77d0cf29f08c0d034b2a93ec18 (diff)
separate mac fonthost into atsui (32bit, pre-10.6) and coretext (64bit, 10.6)
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
Diffstat (limited to 'src/ports')
-rwxr-xr-xsrc/ports/SkFontHost_mac.cpp614
-rw-r--r--src/ports/SkFontHost_mac_atsui.cpp610
-rw-r--r--src/ports/SkFontHost_mac_coretext.cpp847
3 files changed, 1482 insertions, 589 deletions
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index a0239e224c..6adaf329b0 100755
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -14,597 +14,33 @@
** 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
index 0000000000..a0239e224c
--- /dev/null
+++ b/src/ports/SkFontHost_mac_atsui.cpp
@@ -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
index 0000000000..e54fb78b4b
--- /dev/null
+++ b/src/ports/SkFontHost_mac_coretext.cpp
@@ -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);
+}
+
+
+
+