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
This commit is contained in:
parent
e89d3ec443
commit
feda2f90a4
@ -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;
|
||||
}
|
||||
#if SK_USE_CORETEXT
|
||||
#include "SkFontHost_mac_coretext.cpp"
|
||||
#else
|
||||
#include "SkFontHost_mac_atsui.cpp"
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
610
src/ports/SkFontHost_mac_atsui.cpp
Normal file
610
src/ports/SkFontHost_mac_atsui.cpp
Normal file
@ -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;
|
||||
}
|
||||
|
847
src/ports/SkFontHost_mac_coretext.cpp
Normal file
847
src/ports/SkFontHost_mac_coretext.cpp
Normal file
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user