diff --git a/gm/fontmgr.cpp b/gm/fontmgr.cpp index 8cb77c690..b0d92916a 100644 --- a/gm/fontmgr.cpp +++ b/gm/fontmgr.cpp @@ -28,7 +28,7 @@ public: protected: virtual SkString onShortName() { - return SkString("fontmgr"); + return SkString("fontmgr_iter"); } virtual SkISize onISize() { @@ -53,6 +53,7 @@ protected: (void)drawString(canvas, fname, 20, y, paint); SkScalar x = 220; + SkAutoTUnref set(fm->createStyleSet(i)); for (int j = 0; j < set->count(); ++j) { SkString sname; @@ -76,6 +77,102 @@ private: typedef GM INHERITED; }; +class FontMgrMatchGM : public skiagm::GM { + SkAutoTUnref fFM; + +public: + FontMgrMatchGM() : fFM(SkFontMgr::RefDefault()) { + SkGraphics::SetFontCacheLimit(16 * 1024 * 1024); + } + +protected: + virtual SkString onShortName() { + return SkString("fontmgr_match"); + } + + virtual SkISize onISize() { + return SkISize::Make(640, 1024); + } + + void iterateFamily(SkCanvas* canvas, const SkPaint& paint, + SkFontStyleSet* fset) { + SkPaint p(paint); + SkScalar y = 0; + + for (int j = 0; j < fset->count(); ++j) { + SkString sname; + SkFontStyle fs; + fset->getStyle(j, &fs, &sname); + + sname.appendf(" [%d %d]", fs.weight(), fs.width()); + + SkSafeUnref(p.setTypeface(fset->createTypeface(j))); + (void)drawString(canvas, sname, 0, y, p); + y += 24; + } + } + + void exploreFamily(SkCanvas* canvas, const SkPaint& paint, + SkFontStyleSet* fset) { + SkPaint p(paint); + SkScalar y = 0; + + for (int weight = 100; weight <= 900; weight += 200) { + for (int width = 1; width <= 9; width += 2) { + SkFontStyle fs(weight, width, SkFontStyle::kUpright_Slant); + SkTypeface* face = fset->matchStyle(fs); + if (face) { + SkString str; + str.printf("request [%d %d]", fs.weight(), fs.width()); + p.setTypeface(face)->unref(); + (void)drawString(canvas, str, 0, y, p); + y += 24; + } + } + } + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + SkPaint paint; + paint.setAntiAlias(true); + paint.setLCDRenderText(true); + paint.setSubpixelText(true); + paint.setTextSize(17); + + static const char* gNames[] = { + "Helvetica Neue", "Arial" + }; + + SkFontStyleSet* fset = NULL; + for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) { + fset = fFM->matchFamily(gNames[i]); + if (fset && fset->count() > 0) { + break; + } + } + + if (NULL == fset) { + return; + } + SkAutoUnref aur(fset); + + canvas->translate(20, 40); + this->exploreFamily(canvas, paint, fset); + canvas->translate(150, 0); + this->iterateFamily(canvas, paint, fset); + } + + virtual uint32_t onGetFlags() const SK_OVERRIDE { + // fontdescriptors (and therefore serialization) don't yet understand + // these new styles, so skip tests that exercise that for now. + return kSkipPicture_Flag | kSkipPipe_Flag; + } + +private: + typedef GM INHERITED; +}; + ////////////////////////////////////////////////////////////////////////////// DEF_GM( return SkNEW(FontMgrGM); ) +DEF_GM( return SkNEW(FontMgrMatchGM); ) diff --git a/include/ports/SkFontMgr.h b/include/ports/SkFontMgr.h index d4bd01ff8..3160f80ac 100644 --- a/include/ports/SkFontMgr.h +++ b/include/ports/SkFontMgr.h @@ -20,6 +20,9 @@ public: virtual int count() = 0; virtual void getStyle(int index, SkFontStyle*, SkString* style) = 0; virtual SkTypeface* createTypeface(int index) = 0; + virtual SkTypeface* matchStyle(const SkFontStyle& pattern) = 0; + + static SkFontStyleSet* CreateEmpty(); }; class SkFontMgr : public SkRefCnt { @@ -28,6 +31,8 @@ public: void getFamilyName(int index, SkString* familyName); SkFontStyleSet* createStyleSet(int index); + SkFontStyleSet* matchFamily(const char familyName[]); + /** * Find the closest matching typeface to the specified familyName and style * and return a ref to it. The caller must call unref() on the returned @@ -71,6 +76,8 @@ protected: virtual void onGetFamilyName(int index, SkString* familyName) = 0; virtual SkFontStyleSet* onCreateStyleSet(int index) = 0; + virtual SkFontStyleSet* onMatchFamily(const char familyName[]) = 0; + virtual SkTypeface* onMatchFamilyStyle(const char familyName[], const SkFontStyle&) = 0; virtual SkTypeface* onMatchFaceStyle(const SkTypeface*, diff --git a/src/core/SkFontHost.cpp b/src/core/SkFontHost.cpp index d784978eb..a3df04d79 100644 --- a/src/core/SkFontHost.cpp +++ b/src/core/SkFontHost.cpp @@ -50,8 +50,45 @@ void SkFontHost::SetSubpixelOrder(LCDOrder order) { /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// +#include "SkFontStyle.h" + +SkFontStyle::SkFontStyle() { + fUnion.fU32 = 0; + fUnion.fR.fWeight = kNormal_Weight; + fUnion.fR.fWidth = kNormal_Width; + fUnion.fR.fSlant = kUpright_Slant; +} + +SkFontStyle::SkFontStyle(int weight, int width, Slant slant) { + fUnion.fU32 = 0; + fUnion.fR.fWeight = SkPin32(weight, kThin_Weight, kBlack_Weight); + fUnion.fR.fWidth = SkPin32(width, kUltraCondensed_Width, kUltaExpanded_Width); + fUnion.fR.fSlant = SkPin32(slant, kUpright_Slant, kItalic_Slant); +} + #include "SkFontMgr.h" +class SkEmptyFontStyleSet : public SkFontStyleSet { +public: + virtual int count() SK_OVERRIDE { return 0; } + virtual void getStyle(int, SkFontStyle*, SkString*) SK_OVERRIDE { + SkASSERT(!"SkFontStyleSet::getStyle called on empty set"); + } + virtual SkTypeface* createTypeface(int index) SK_OVERRIDE { + SkASSERT(!"SkFontStyleSet::createTypeface called on empty set"); + return NULL; + } + virtual SkTypeface* matchStyle(const SkFontStyle&) SK_OVERRIDE { + return NULL; + } +}; + +SkFontStyleSet* SkFontStyleSet::CreateEmpty() { + return SkNEW(SkEmptyFontStyleSet); +} + +/////////////////////////////////////////////////////////////////////////////// + class SkEmptyFontMgr : public SkFontMgr { protected: virtual int onCountFamilies() SK_OVERRIDE { @@ -64,6 +101,10 @@ protected: SkASSERT(!"onCreateStyleSet called with bad index"); return NULL; } + virtual SkFontStyleSet* onMatchFamily(const char[]) SK_OVERRIDE { + return SkFontStyleSet::CreateEmpty(); + } + virtual SkTypeface* onMatchFamilyStyle(const char[], const SkFontStyle&) SK_OVERRIDE { return NULL; @@ -83,20 +124,6 @@ protected: } }; -SkFontStyle::SkFontStyle() { - fUnion.fU32 = 0; - fUnion.fR.fWeight = kNormal_Weight; - fUnion.fR.fWidth = kNormal_Width; - fUnion.fR.fSlant = kUpright_Slant; -} - -SkFontStyle::SkFontStyle(int weight, int width, Slant slant) { - fUnion.fU32 = 0; - fUnion.fR.fWeight = SkPin32(weight, kThin_Weight, kBlack_Weight); - fUnion.fR.fWidth = SkPin32(width, kUltraCondensed_Width, kUltaExpanded_Width); - fUnion.fR.fSlant = SkPin32(slant, kUpright_Slant, kItalic_Slant); -} - int SkFontMgr::countFamilies() { return this->onCountFamilies(); } @@ -109,6 +136,10 @@ SkFontStyleSet* SkFontMgr::createStyleSet(int index) { return this->onCreateStyleSet(index); } +SkFontStyleSet* SkFontMgr::matchFamily(const char familyName[]) { + return this->onMatchFamily(familyName); +} + SkTypeface* SkFontMgr::matchFamilyStyle(const char familyName[], const SkFontStyle& fs) { return this->onMatchFamilyStyle(familyName, fs); diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp index 79e4bde01..309dabc89 100755 --- a/src/ports/SkFontHost_mac.cpp +++ b/src/ports/SkFontHost_mac.cpp @@ -96,6 +96,10 @@ private: CFRef fCFRef; }; +static CFStringRef make_CFString(const char str[]) { + return CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8); +} + template class AutoCGTable : SkNoncopyable { public: AutoCGTable(CGFontRef font) @@ -498,8 +502,7 @@ static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theSty } // Create the font info - AutoCFRelease cfFontName( - CFStringCreateWithCString(NULL, familyName, kCFStringEncodingUTF8)); + AutoCFRelease cfFontName(make_CFString(familyName)); AutoCFRelease cfFontTraits( CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits)); @@ -1913,6 +1916,18 @@ static int unit_width_to_fontstyle(float unit) { return sk_float_round2int(value); } +static inline int sqr(int value) { + SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow + return value * value; +} + +// We normalize each axis (weight, width, italic) to be base-900 +static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) { + return sqr(a.weight() - b.weight()) + + sqr((a.width() - b.width()) * 100) + + sqr((a.isItalic() != b.isItalic()) * 900); +} + static SkFontStyle desc2fontstyle(CTFontDescriptorRef desc) { AutoCFRelease dict( (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc, @@ -2026,10 +2041,36 @@ public: return createFromDesc(fFamilyName, desc); } + virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE { + if (0 == fCount) { + return NULL; + } + return createFromDesc(fFamilyName, findMatchingDesc(pattern)); + } + private: CFArrayRef fArray; CFStringRef fFamilyName; int fCount; + + CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const { + int bestMetric = SK_MaxS32; + CTFontDescriptorRef bestDesc = NULL; + + for (int i = 0; i < fCount; ++i) { + CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i); + int metric = compute_metric(pattern, desc2fontstyle(desc)); + if (0 == metric) { + return desc; + } + if (metric < bestMetric) { + bestMetric = metric; + bestDesc = desc; + } + } + SkASSERT(bestDesc); + return bestDesc; + } }; class SkFontMgr_Mac : public SkFontMgr { @@ -2048,6 +2089,19 @@ class SkFontMgr_Mac : public SkFontMgr { } } + static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) { + AutoCFRelease cfAttr( + CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName); + + AutoCFRelease desc( + CTFontDescriptorCreateWithAttributes(cfAttr)); + return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc)); + } + public: SkFontMgr_Mac() : fCount(0), fNames(NULL) {} @@ -2075,20 +2129,14 @@ protected: if ((unsigned)index >= (unsigned)fCount) { return NULL; } - - AutoCFRelease cfAttr( - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - - CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, - this->stringAt(index)); - - AutoCFRelease desc( - CTFontDescriptorCreateWithAttributes(cfAttr)); - return SkNEW_ARGS(SkFontStyleSet_Mac, (this->stringAt(index), desc)); + return CreateSet(this->stringAt(index)); } - + + virtual SkFontStyleSet* onMatchFamily(const char familyName[]) SK_OVERRIDE { + AutoCFRelease cfName(make_CFString(familyName)); + return CreateSet(cfName); + } + virtual SkTypeface* onMatchFamilyStyle(const char familyName[], const SkFontStyle&) SK_OVERRIDE { return NULL;