add matcher to fontstyleset
Review URL: https://codereview.chromium.org/13312002

git-svn-id: http://skia.googlecode.com/svn/trunk@8444 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/fontmgr.cpp b/gm/fontmgr.cpp
index 8cb77c6..b0d9291 100644
--- a/gm/fontmgr.cpp
+++ b/gm/fontmgr.cpp
@@ -28,7 +28,7 @@
 
 protected:
     virtual SkString onShortName() {
-        return SkString("fontmgr");
+        return SkString("fontmgr_iter");
     }
 
     virtual SkISize onISize() {
@@ -53,6 +53,7 @@
             (void)drawString(canvas, fname, 20, y, paint);
 
             SkScalar x = 220;
+
             SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i));
             for (int j = 0; j < set->count(); ++j) {
                 SkString sname;
@@ -76,6 +77,102 @@
     typedef GM INHERITED;
 };
 
+class FontMgrMatchGM : public skiagm::GM {
+    SkAutoTUnref<SkFontMgr> 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 d4bd01f..3160f80 100644
--- a/include/ports/SkFontMgr.h
+++ b/include/ports/SkFontMgr.h
@@ -20,6 +20,9 @@
     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 @@
     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 @@
     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 d784978..a3df04d 100644
--- a/src/core/SkFontHost.cpp
+++ b/src/core/SkFontHost.cpp
@@ -50,8 +50,45 @@
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
+#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 @@
         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 @@
     }
 };
 
-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 @@
     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 79e4bde..309dabc 100755
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -96,6 +96,10 @@
     CFRef fCFRef;
 };
 
+static CFStringRef make_CFString(const char str[]) {
+    return CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
+}
+
 template<typename T> class AutoCGTable : SkNoncopyable {
 public:
     AutoCGTable(CGFontRef font)
@@ -498,8 +502,7 @@
     }
 
     // Create the font info
-    AutoCFRelease<CFStringRef> cfFontName(
-            CFStringCreateWithCString(NULL, familyName, kCFStringEncodingUTF8));
+    AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
 
     AutoCFRelease<CFNumberRef> cfFontTraits(
             CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
@@ -1913,6 +1916,18 @@
     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<CFDictionaryRef> dict(
         (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc,
@@ -2026,10 +2041,36 @@
         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 @@
         }
     }
 
+    static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
+        AutoCFRelease<CFMutableDictionaryRef> cfAttr(
+                 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                           &kCFTypeDictionaryKeyCallBacks,
+                                           &kCFTypeDictionaryValueCallBacks));
+        
+        CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
+        
+        AutoCFRelease<CTFontDescriptorRef> desc(
+                                CTFontDescriptorCreateWithAttributes(cfAttr));
+        return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc));
+    }
+    
 public:
     SkFontMgr_Mac() : fCount(0), fNames(NULL) {}
 
@@ -2075,20 +2129,14 @@
         if ((unsigned)index >= (unsigned)fCount) {
             return NULL;
         }
-
-        AutoCFRelease<CFMutableDictionaryRef> cfAttr(
-                 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                           &kCFTypeDictionaryKeyCallBacks,
-                                           &kCFTypeDictionaryValueCallBacks));
-
-        CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute,
-                             this->stringAt(index));
-
-        AutoCFRelease<CTFontDescriptorRef> 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<CFStringRef> cfName(make_CFString(familyName));
+        return CreateSet(cfName);
+    }
+    
     virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
                                            const SkFontStyle&) SK_OVERRIDE {
         return NULL;