diff options
author | 2010-05-19 13:47:05 +0000 | |
---|---|---|
committer | 2010-05-19 13:47:05 +0000 | |
commit | feda2f90a4ad9625e14d8cb02a90b9644d803dd4 (patch) | |
tree | c44f2f0b45097e83a3f14d9aba6a88fb3c7c73d0 /src/ports | |
parent | e89d3ec443563a77d0cf29f08c0d034b2a93ec18 (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-x | src/ports/SkFontHost_mac.cpp | 614 | ||||
-rw-r--r-- | src/ports/SkFontHost_mac_atsui.cpp | 610 | ||||
-rw-r--r-- | src/ports/SkFontHost_mac_coretext.cpp | 847 |
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); +} + + + + |