WIP -- SkFont

BUG=skia:
R=bungeman@google.com, fmalita@chromium.org

Review URL: https://codereview.chromium.org/185293018

git-svn-id: http://skia.googlecode.com/svn/trunk@14090 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 9093368..df026a4 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -88,6 +88,7 @@
         '<(skia_src_path)/core/SkFloat.cpp',
         '<(skia_src_path)/core/SkFloat.h',
         '<(skia_src_path)/core/SkFloatBits.cpp',
+        '<(skia_src_path)/core/SkFont.cpp',
         '<(skia_src_path)/core/SkFontHost.cpp',
         '<(skia_src_path)/core/SkFontDescriptor.cpp',
         '<(skia_src_path)/core/SkFontDescriptor.h',
diff --git a/include/core/SkFont.h b/include/core/SkFont.h
new file mode 100644
index 0000000..4572228
--- /dev/null
+++ b/include/core/SkFont.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+
+class SkPaint;
+class SkTypeface;
+
+enum SkTextEncoding {
+    kUTF8_SkTextEncoding,
+    kUTF16_SkTextEncoding,
+    kUTF32_SkTextEncoding,
+    kGlyphID_SkTextEncoding,
+};
+
+/*
+ 1. The Hinting enum in SkPaint is gone entirely, absorbed into SkFont's flags.
+ 
+ 2. SkPaint Flags look like this today
+ 
+ enum Flags {
+     kAntiAlias_Flag       = 0x01,   //!< mask to enable antialiasing
+     kDither_Flag          = 0x04,   //!< mask to enable dithering
+     kUnderlineText_Flag   = 0x08,   //!< mask to enable underline text
+     kStrikeThruText_Flag  = 0x10,   //!< mask to enable strike-thru text
+     kFakeBoldText_Flag    = 0x20,   //!< mask to enable fake-bold text
+     kLinearText_Flag      = 0x40,   //!< mask to enable linear-text
+     kSubpixelText_Flag    = 0x80,   //!< mask to enable subpixel text positioning
+     kDevKernText_Flag     = 0x100,  //!< mask to enable device kerning text
+     kLCDRenderText_Flag   = 0x200,  //!< mask to enable subpixel glyph renderering
+     kEmbeddedBitmapText_Flag = 0x400, //!< mask to enable embedded bitmap strikes
+     kAutoHinting_Flag     = 0x800,  //!< mask to force Freetype's autohinter
+     kVerticalText_Flag    = 0x1000,
+     kGenA8FromLCD_Flag    = 0x2000, // hack for GDI -- do not use if you can help it
+ };
+
+ SkFont would absorb these:
+ 
+     kFakeBoldText_Flag    = 0x20,   //!< mask to enable fake-bold text
+     kLinearText_Flag      = 0x40,   //!< mask to enable linear-text
+     kSubpixelText_Flag    = 0x80,   //!< mask to enable subpixel text positioning
+     kDevKernText_Flag     = 0x100,  //!< mask to enable device kerning text
+     kLCDRenderText_Flag   = 0x200,  //!< mask to enable subpixel glyph renderering
+     kEmbeddedBitmapText_Flag = 0x400, //!< mask to enable embedded bitmap strikes
+     kAutoHinting_Flag     = 0x800,  //!< mask to force Freetype's autohinter
+     kVerticalText_Flag    = 0x1000,
+     kGenA8FromLCD_Flag    = 0x2000, // hack for GDI -- do not use if you can help it
+
+ leaving these still in paint
+
+     kAntiAlias_Flag       = 0x01,   //!< mask to enable antialiasing
+     kDither_Flag          = 0x04,   //!< mask to enable dithering
+     kUnderlineText_Flag   = 0x08,   //!< mask to enable underline text
+     kStrikeThruText_Flag  = 0x10,   //!< mask to enable strike-thru text
+
+ 3. Antialiasing
+ 
+    SkFont has a mask-type: BW, AA, LCD
+    SkPaint has antialias boolean
+ 
+    What to do if the font's mask-type disagrees with the paint?
+
+ */
+
+class SkFont : public SkRefCnt {
+public:
+    enum Flags {
+        /**
+         *  Use the system's automatic hinting mechanism to hint the typeface.
+         *  If both bytecode and auto hints are specified, attempt to use the bytecodes first.
+         *  If that fails (e.g. there are no codes), then attempt to autohint.
+         */
+        kEnableAutoHints_Flag       = 1 << 0,
+
+        /**
+         *  If the typeface contains explicit bytecodes for hinting, use them.
+         *  If both bytecode and auto hints are specified, attempt to use the bytecodes first;
+         *  if that fails (e.g. there are no codes), then attempt to autohint.
+         */
+        kEnableByteCodeHints_Flag   = 1 << 1,
+
+        /**
+         *  Use rounded metric values (e.g. advance).
+         *  If either auto or bytecode hinting was used, apply those results to the metrics of the
+         *  glyphs as well. If no hinting was applied, the metrics will just be rounded to the
+         *  nearest integer.
+         *
+         *  This applies to calls that return metrics (e.g. measureText) and to drawing the glyphs
+         *  (see SkCanvas drawText and drawPosText).
+         */
+        kUseNonlinearMetrics_Flag   = 1 << 2,
+
+        kVertical_Flag              = 1 << 3,
+        kEmbeddedBitmaps_Flag       = 1 << 4,
+        kGenA8FromLCD_Flag          = 1 << 5,
+        kEmbolden_Flag              = 1 << 6,
+        kDevKern_Flag               = 1 << 7,   // ifdef ANDROID ?
+    };
+
+    enum MaskType {
+        kBW_MaskType,
+        kA8_MaskType,
+        kLCD_MaskType,
+    };
+
+    static SkFont* Create(SkTypeface*, SkScalar size, MaskType, uint32_t flags);
+    static SkFont* Create(SkTypeface*, SkScalar size, SkScalar scaleX, SkScalar skewX,
+                          MaskType, uint32_t flags);
+
+    /**
+     *  Return a font with the same attributes of this font, but with the specified size.
+     *  If size is not supported (e.g. <= 0 or non-finite) NULL will be returned.
+     */
+    SkFont* cloneWithSize(SkScalar size) const;
+
+    SkTypeface* getTypeface() const { return fTypeface; }
+    SkScalar    getSize() const { return fSize; }
+    SkScalar    getScaleX() const { return fScaleX; }
+    SkScalar    getSkewX() const { return fSkewX; }
+    uint32_t    getFlags() const { return fFlags; }
+    MaskType    getMaskType() const { return (MaskType)fMaskType; }
+
+    int textToGlyphs(const void* text, size_t byteLength, SkTextEncoding,
+                     uint16_t glyphs[], int maxGlyphCount) const;
+
+    SkScalar measureText(const void* text, size_t byteLength, SkTextEncoding) const;
+
+    static SkFont* Testing_CreateFromPaint(const SkPaint&);
+
+private:
+    enum {
+        kAllFlags = 0xFF,
+    };
+    
+    SkFont(SkTypeface*, SkScalar size, SkScalar scaleX, SkScalar skewX, MaskType, uint32_t flags);
+    virtual ~SkFont();
+
+    SkTypeface* fTypeface;
+    SkScalar    fSize;
+    SkScalar    fScaleX;
+    SkScalar    fSkewX;
+    uint16_t    fFlags;
+    uint8_t     fMaskType;
+    uint8_t     fPad;
+};
+
diff --git a/src/core/SkFont.cpp b/src/core/SkFont.cpp
new file mode 100644
index 0000000..2c16186
--- /dev/null
+++ b/src/core/SkFont.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkFont.h"
+#include "SkTypeface.h"
+#include "SkUtils.h"
+
+static SkTypeface* ref_or_default(SkTypeface* face) {
+    return face ? SkRef(face) : SkTypeface::RefDefault();
+}
+
+SkFont::SkFont(SkTypeface* face, SkScalar size, SkScalar scaleX, SkScalar skewX, MaskType mt,
+               uint32_t flags)
+    : fTypeface(ref_or_default(face))
+    , fSize(size)
+    , fScaleX(scaleX)
+    , fSkewX(skewX)
+    , fFlags(flags)
+    , fMaskType(SkToU8(mt))
+    , fPad(0)
+{
+    SkASSERT(size > 0);
+    SkASSERT(scaleX > 0);
+    SkASSERT(SkScalarIsFinite(skewX));
+    SkASSERT(0 == (flags & ~kAllFlags));
+}
+
+SkFont* SkFont::Create(SkTypeface* face, SkScalar size, SkScalar scaleX, SkScalar skewX,
+                       MaskType mt, uint32_t flags) {
+    if (size <= 0 || !SkScalarIsFinite(size)) {
+        return NULL;
+    }
+    if (scaleX <= 0 || !SkScalarIsFinite(scaleX)) {
+        return NULL;
+    }
+    if (!SkScalarIsFinite(skewX)) {
+        return NULL;
+    }
+    flags &= kAllFlags;
+    return SkNEW_ARGS(SkFont, (face, size, scaleX, skewX, mt, flags));
+}
+
+SkFont* SkFont::Create(SkTypeface* face, SkScalar size, MaskType mt, uint32_t flags) {
+    return SkFont::Create(face, size, 1, 0, mt, flags);
+}
+
+SkFont* SkFont::cloneWithSize(SkScalar newSize) const {
+    return SkFont::Create(this->getTypeface(), newSize, this->getScaleX(), this->getSkewX(),
+                          this->getMaskType(), this->getFlags());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkFont::~SkFont() {
+    SkSafeUnref(fTypeface);
+}
+
+int SkFont::textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding,
+                         uint16_t glyphs[], int maxGlyphCount) const {
+    if (0 == byteLength) {
+        return 0;
+    }
+    
+    SkASSERT(text);
+
+    int count;
+    switch (encoding) {
+        case kUTF8_SkTextEncoding:
+            count = SkUTF8_CountUnichars((const char*)text, byteLength);
+            break;
+        case kUTF16_SkTextEncoding:
+            count = SkUTF16_CountUnichars((const uint16_t*)text, SkToInt(byteLength >> 1));
+            break;
+        case kUTF32_SkTextEncoding:
+            count = SkToInt(byteLength >> 2);
+            break;
+        case kGlyphID_SkTextEncoding:
+            count = SkToInt(byteLength >> 1);
+            break;
+    }
+    if (NULL == glyphs) {
+        return count;
+    }
+
+    if (kGlyphID_SkTextEncoding == encoding) {
+        memcpy(glyphs, text, count << 1);
+        return count;
+    }
+
+    // TODO: unify/eliminate SkTypeface::Encoding with SkTextEncoding
+    SkTypeface::Encoding typeface_encoding;
+    switch (encoding) {
+        case kUTF8_SkTextEncoding:
+            typeface_encoding = SkTypeface::kUTF8_Encoding;
+            break;
+        case kUTF16_SkTextEncoding:
+            typeface_encoding = SkTypeface::kUTF16_Encoding;
+            break;
+        case kUTF32_SkTextEncoding:
+            typeface_encoding = SkTypeface::kUTF32_Encoding;
+            break;
+        case kGlyphID_SkTextEncoding:
+            SkASSERT(0);    // can't get here
+    }
+
+    (void)fTypeface->charsToGlyphs(text, typeface_encoding, glyphs, count);
+    return count;
+}
+
+SkScalar SkFont::measureText(const void* text, size_t byteLength, SkTextEncoding encoding) const {
+    // TODO: need access to the cache
+    return -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkPaint.h"
+
+SkFont* SkFont::Testing_CreateFromPaint(const SkPaint& paint) {
+    uint32_t flags = 0;
+    if (paint.isVerticalText()) {
+        flags |= kVertical_Flag;
+    }
+    if (paint.isEmbeddedBitmapText()) {
+        flags |= kEmbeddedBitmaps_Flag;
+    }
+    if (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) {
+        flags |= kGenA8FromLCD_Flag;
+    }
+    if (paint.isFakeBoldText()) {
+        flags |= kEmbolden_Flag;
+    }
+
+    if (SkPaint::kFull_Hinting == paint.getHinting()) {
+        flags |= kEnableByteCodeHints_Flag;
+    }
+    if (paint.isAutohinted()) {
+        flags |= kEnableAutoHints_Flag;
+    }
+    if (paint.isSubpixelText() || paint.isLinearText()) {
+        // this is our default
+    } else {
+        flags |= kUseNonlinearMetrics_Flag;
+    }
+
+    MaskType maskType = SkFont::kBW_MaskType;
+    if (paint.isAntiAlias()) {
+        maskType = paint.isLCDRenderText() ? kLCD_MaskType : kA8_MaskType;
+    }
+
+    return Create(paint.getTypeface(),
+                  paint.getTextSize(), paint.getTextScaleX(), paint.getTextSkewX(),
+                  maskType, flags);
+}
diff --git a/tests/FontMgrTest.cpp b/tests/FontMgrTest.cpp
index 6e59364..69e2088 100644
--- a/tests/FontMgrTest.cpp
+++ b/tests/FontMgrTest.cpp
@@ -10,6 +10,44 @@
 #include "SkTypeface.h"
 #include "Test.h"
 
+#include "SkFont.h"
+#include "SkPaint.h"
+static void test_font(skiatest::Reporter* reporter) {
+    uint32_t flags = 0;
+    SkAutoTUnref<SkFont> font(SkFont::Create(NULL, 24, SkFont::kA8_MaskType, flags));
+    
+    REPORTER_ASSERT(reporter, NULL != font->getTypeface());
+    REPORTER_ASSERT(reporter, 24 == font->getSize());
+    REPORTER_ASSERT(reporter, 1 == font->getScaleX());
+    REPORTER_ASSERT(reporter, 0 == font->getSkewX());
+    REPORTER_ASSERT(reporter, SkFont::kA8_MaskType == font->getMaskType());
+
+    uint16_t glyphs[5];
+    sk_bzero(glyphs, sizeof(glyphs));
+
+    int count = font->textToGlyphs("Hello", 5, kUTF8_SkTextEncoding, glyphs, SK_ARRAY_COUNT(glyphs));
+
+    REPORTER_ASSERT(reporter, 5 == count);
+    for (int i = 0; i < count; ++i) {
+        REPORTER_ASSERT(reporter, 0 != glyphs[i]);
+    }
+    REPORTER_ASSERT(reporter, glyphs[0] != glyphs[1]); // 'h' != 'e'
+    REPORTER_ASSERT(reporter, glyphs[2] == glyphs[3]); // 'l' == 'l'
+    
+    SkAutoTUnref<SkFont> newFont(font->cloneWithSize(36));
+    REPORTER_ASSERT(reporter, newFont.get());
+    REPORTER_ASSERT(reporter, font->getTypeface() == newFont->getTypeface());
+    REPORTER_ASSERT(reporter, 36 == newFont->getSize());   // double check we haven't changed
+    REPORTER_ASSERT(reporter, 24 == font->getSize());   // double check we haven't changed
+
+    SkPaint paint;
+    paint.setTextSize(18);
+    font.reset(SkFont::Testing_CreateFromPaint(paint));
+    REPORTER_ASSERT(reporter, font.get());
+    REPORTER_ASSERT(reporter, font->getSize() == paint.getTextSize());
+    REPORTER_ASSERT(reporter, SkFont::kBW_MaskType == font->getMaskType());
+}
+
 /*
  *  If the font backend is going to "alias" some font names to other fonts
  *  (e.g. sans -> Arial) then we want to at least get the same typeface back
@@ -78,4 +116,5 @@
 DEF_TEST(FontMgr, reporter) {
     test_fontiter(reporter, FLAGS_verboseFontMgr);
     test_alias_names(reporter);
+    test_font(reporter);
 }