add Table apis to SkFontHost
need to implement in _win backend



git-svn-id: http://skia.googlecode.com/svn/trunk@149 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/Makefile b/Makefile
index ddfecfc..84742d4 100644
--- a/Makefile
+++ b/Makefile
@@ -78,7 +78,7 @@
 
 # add any optional codecs for this app
 ifeq ($(SKIA_BUILD_FOR),mac)
-#    BENCH_SRCS += bench/TextBench.cpp
+    BENCH_SRCS += bench/TextBench.cpp
 else
     BENCH_SRCS += src/images/SkImageDecoder_libpng.cpp
 endif
diff --git a/bench/TextBench.cpp b/bench/TextBench.cpp
index 799a3c8..adbd962 100644
--- a/bench/TextBench.cpp
+++ b/bench/TextBench.cpp
@@ -1,10 +1,50 @@
 #include "SkBenchmark.h"
 #include "SkCanvas.h"
+#include "SkFontHost.h"
 #include "SkPaint.h"
 #include "SkRandom.h"
 #include "SkString.h"
 #include "SkTemplates.h"
 
+static void dump_font(const char name[], SkFontID fontID) {
+    SkDebugf("Font \"%s\" %x\n", name, fontID);
+    int count = SkFontHost::CountTables(fontID);
+    SkAutoTArray<SkFontTableTag> storage(count);
+    SkFontTableTag* tags = storage.get();
+    SkFontHost::GetTableTags(fontID, tags);
+    for (int i = 0; i < count; i++) {
+        uint32_t tag = tags[i];
+        uint8_t data[4];
+        size_t size = SkFontHost::GetTableSize(fontID, tag);
+        size_t bytes = SkFontHost::GetTableData(fontID, tag,
+                                                0, sizeof(data), data);
+        SkDebugf("   tag=%c%c%c%c size=%d bytes=%d %x %x %x %x\n",
+                 uint8_t(tag>>24), uint8_t(tag>>16), uint8_t(tag>>8), uint8_t(tag),
+                 size, bytes, data[0], data[1], data[2], data[3]);
+    }
+}
+
+static void test_tables() {
+    static bool gOnce;
+    if (gOnce) {
+        return;
+    }
+    gOnce = true;
+    
+    static const char* gNames[] = {
+        "Arial", "Times", "Courier"
+    };
+    
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++) {
+        SkTypeface* tf = SkTypeface::CreateFromName(gNames[i], SkTypeface::kNormal);
+        if (tf) {
+            SkFontID fontID = tf->uniqueID();
+            dump_font(gNames[i], fontID);
+            tf->unref();
+        }
+    }
+}
+
 /*  Some considerations for performance:
         short -vs- long strings (measuring overhead)
         tiny -vs- large pointsize (measure blit -vs- overhead)
@@ -23,6 +63,8 @@
     enum { N = 300 };
 public:
     TextBench(const char text[], int ps, bool linearText, bool posText) {
+        test_tables();
+        
         fText.set(text);
 
         fPaint.setAntiAlias(true);
diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h
index 942350f..03ebfc7 100644
--- a/include/core/SkFontHost.h
+++ b/include/core/SkFontHost.h
@@ -24,6 +24,9 @@
 class SkStream;
 class SkWStream;
 
+typedef uint32_t SkFontID;
+typedef uint32_t SkFontTableTag;
+
 /** \class SkFontHost
 
     This class is ported to each environment. It is responsible for bridging
@@ -91,13 +94,13 @@
         Returning false is similar to calling OpenStream with an invalid ID,
         which will return NULL in that case.
     */
-    static bool ValidFontID(uint32_t uniqueID);
+    static bool ValidFontID(SkFontID uniqueID);
     
     /** Return a new stream to read the font data, or null if the uniqueID does
         not match an existing typeface. .The caller must call stream->unref()
         when it is finished reading the data.
     */
-    static SkStream* OpenStream(uint32_t uniqueID);
+    static SkStream* OpenStream(SkFontID uniqueID);
 
     ///////////////////////////////////////////////////////////////////////////
 
@@ -124,10 +127,49 @@
         on. This process must be finite, and when the fonthost sees a
         font with no logical successor, it must return 0.
     */
-    static uint32_t NextLogicalFont(uint32_t fontID);
+    static uint32_t NextLogicalFont(SkFontID fontID);
 
     ///////////////////////////////////////////////////////////////////////////
+
+    /** Return the number of tables in the font
+     */
+    static int CountTables(SkFontID);
+
+    /** Copy into tags[] (allocated by the caller) the list of table tags in
+        the font, and return the number. This will be the same as CountTables()
+        or 0 if an error occured.
+     */
+    static int GetTableTags(SkFontID, SkFontTableTag[]);
+
+    /** Given a table tag, return the size of its contents, or 0 if not present
+     */
+    static size_t GetTableSize(SkFontID, SkFontTableTag);
     
+    /** Copy the contents of a table into data (allocated by the caller). Note
+        that the contents of the table will be in their native endian order
+        (which for most truetype tables is big endian). If the table tag is
+        not found, or there is an error copying the data, then 0 is returned.
+        If this happens, it is possible that some or all of the memory pointed
+        to by data may have been written to, even though an error has occured.
+
+        @param fontID the font to copy the table from
+        @param tag  The table tag whose contents are to be copied
+        @param offset The offset in bytes into the table's contents where the
+                copy should start from.
+        @param length The number of bytes, starting at offset, of table data
+                to copy.
+        @param data storage address where the table contents are copied to
+        @return the number of bytes actually copied into data. If offset+length
+                exceeds the table's size, then only the bytes up to the table's
+                size are actually copied, and this is the value returned. If
+                offset > the table's size, or tag is not a valid table,
+                then 0 is returned.
+     */
+    static size_t GetTableData(SkFontID fontID, SkFontTableTag tag,
+                               size_t offset, size_t length, void* data);
+
+    ///////////////////////////////////////////////////////////////////////////
+
     /** Return the number of bytes (approx) that should be purged from the font
         cache. The input parameter is the cache's estimate of how much as been
         allocated by the cache so far.
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index b088c1e..06f1f31 100755
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -483,3 +483,91 @@
     tables[1] = NULL;   // white gamma (e.g. exp= 1/1.4)
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkSFNTHeader {
+    uint32_t    fVersion;
+    uint16_t    fNumTables;
+    uint16_t    fSearchRange;
+    uint16_t    fEntrySelector;
+    uint16_t    fRangeShift;
+};
+
+struct SkSFNTDirEntry {
+    uint32_t    fTag;
+    uint32_t    fChecksum;
+    uint32_t    fOffset;
+    uint32_t    fLength;
+};
+
+struct SfntHeader {
+    SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) {
+        ByteCount size;
+        if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) {
+            return;
+        }
+        
+        SkAutoMalloc storage(size);
+        SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get());
+        if (ATSFontGetTableDirectory(fontID, size, header, &size)) {
+            return;
+        }
+        
+        fCount = SkEndian_SwapBE16(header->fNumTables);
+        fData = header;
+        storage.detach();
+    }
+    
+    ~SfntHeader() {
+        sk_free(fData);
+    }
+    
+    int count() const { return fCount; }
+    const SkSFNTDirEntry* entries() const {
+        return reinterpret_cast<const SkSFNTDirEntry*>
+            (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader));
+    }
+    
+private:
+    int     fCount;
+    void*   fData;
+};
+        
+int SkFontHost::CountTables(SkFontID fontID) {
+    SfntHeader header(fontID, false);
+    return header.count();
+}
+
+int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
+    SfntHeader header(fontID, true);
+    int count = header.count();
+    const SkSFNTDirEntry* entry = header.entries();
+    for (int i = 0; i < count; i++) {
+        tags[i] = SkEndian_SwapBE32(entry[i].fTag);
+    }
+    return count;
+}
+
+size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
+    ByteCount size;
+    if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) {
+        return 0;
+    }
+    return size;
+}
+
+size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
+                                size_t offset, size_t length, void* data) {
+    ByteCount size;
+    if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) {
+        return 0;
+    }
+    if (offset >= size) {
+        return 0;
+    }
+    if (offset + length > size) {
+        length = size - offset;
+    }
+    return length;
+}
+
diff --git a/src/ports/SkFontHost_tables.cpp b/src/ports/SkFontHost_tables.cpp
new file mode 100644
index 0000000..f14e085
--- /dev/null
+++ b/src/ports/SkFontHost_tables.cpp
@@ -0,0 +1,173 @@
+#include "SkEndian.h"
+#include "SkFontHost.h"
+#include "SkStream.h"
+
+struct SkSFNTHeader {
+    uint32_t    fVersion;
+    uint16_t    fNumTables;
+    uint16_t    fSearchRange;
+    uint16_t    fEntrySelector;
+    uint16_t    fRangeShift;
+};
+
+struct SkTTCFHeader {
+    uint32_t    fTag;
+    uint32_t    fVersion;
+    uint32_t    fNumOffsets;
+    uint32_t    fOffset0;   // the first of N (fNumOffsets)
+};
+
+union SkSharedTTHeader {
+    SkSFNTHeader    fSingle;
+    SkTTCFHeader    fCollection;
+};
+
+struct SkSFNTDirEntry {
+    uint32_t    fTag;
+    uint32_t    fChecksum;
+    uint32_t    fOffset;
+    uint32_t    fLength;
+};
+
+static int count_tables(SkStream* stream, size_t* offsetToDir = NULL) {
+    SkSharedTTHeader shared;
+    if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) {
+        return 0;
+    }
+
+    uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag);
+    if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) {
+        if (shared.fCollection.fNumOffsets == 0) {
+            return 0;
+        }
+        size_t offset = SkEndian_SwapBE32(shared.fCollection.fOffset0);
+        stream->rewind();
+        if (stream->skip(offset) != offset) {
+            return 0;
+        }
+        if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) {
+            return 0;
+        }
+        if (offsetToDir) {
+            *offsetToDir = offset;
+        }
+    }
+    return SkEndian_SwapBE16(shared.fSingle.fNumTables);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SfntHeader {
+    SfntHeader() : fCount(0), fDir(NULL) {}
+    ~SfntHeader() { sk_free(fDir); }
+    
+    bool init(SkStream* stream) {
+        size_t offsetToDir;
+        fCount = count_tables(stream, &offsetToDir);
+        if (0 == fCount) {
+            return false;
+        }
+
+        stream->rewind();
+        if (stream->skip(offsetToDir) != offsetToDir) {
+            return false;
+        }
+
+        size_t size = fCount * sizeof(SkSFNTDirEntry);
+        fDir = reinterpret_cast<SkSFNTDirEntry*>(sk_malloc_throw(size));
+        return stream->read(fDir, size) == size;
+    }
+    
+    int             fCount;
+    SkSFNTDirEntry* fDir;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkFontHost::CountTables(SkFontID fontID) {
+    SkStream* stream = SkFontHost::OpenStream(fontID);
+    if (NULL == stream) {
+        return 0;
+    }
+
+    SkAutoUnref au(stream);
+    return count_tables(stream);
+}
+
+int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
+    SkStream* stream = SkFontHost::OpenStream(fontID);
+    if (NULL == stream) {
+        return 0;
+    }
+    
+    SkAutoUnref au(stream);
+    SfntHeader  header;
+    if (!header.init(stream)) {
+        return 0;
+    }
+    
+    for (int i = 0; i < header.fCount; i++) {
+        tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag);
+    }
+    return header.fCount;
+}
+
+size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
+    SkStream* stream = SkFontHost::OpenStream(fontID);
+    if (NULL == stream) {
+        return 0;
+    }
+    
+    SkAutoUnref au(stream);
+    SfntHeader  header;
+    if (!header.init(stream)) {
+        return 0;
+    }
+
+    for (int i = 0; i < header.fCount; i++) {
+        if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) {
+            return SkEndian_SwapBE32(header.fDir[i].fLength);
+        }
+    }
+    return 0;
+}
+
+size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
+                                size_t offset, size_t length, void* data) {
+    SkStream* stream = SkFontHost::OpenStream(fontID);
+    if (NULL == stream) {
+        return 0;
+    }
+    
+    SkAutoUnref au(stream);
+    SfntHeader  header;
+    if (!header.init(stream)) {
+        return 0;
+    }
+
+    for (int i = 0; i < header.fCount; i++) {
+        if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) {
+            size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset);
+            size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength);
+            // now sanity check the caller's offset/length
+            if (offset >= realLength) {
+                return 0;
+            }
+            if (offset + length > realLength) {
+                length = realLength - offset;
+            }
+            // skip the stream to the part of the table we want to copy from
+            stream->rewind();
+            size_t bytesToSkip = realOffset + offset;
+            if (stream->skip(bytesToSkip) != bytesToSkip) {
+                return 0;
+            }
+            if (stream->read(data, length) != length) {
+                return 0;
+            }
+            return length;
+        }
+    }
+    return 0;
+}
+
diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
index 7eea0ed..3a3a581 100644
--- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
+++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
@@ -167,6 +167,7 @@
 		0041CE310F00A12400695E8C /* SampleNinePatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleNinePatch.cpp; path = ../../samplecode/SampleNinePatch.cpp; sourceTree = SOURCE_ROOT; };
 		0041CE320F00A12400695E8C /* SampleOverflow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleOverflow.cpp; path = ../../samplecode/SampleOverflow.cpp; sourceTree = SOURCE_ROOT; };
 		0041CE340F00A12400695E8C /* SamplePatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SamplePatch.cpp; path = ../../samplecode/SamplePatch.cpp; sourceTree = SOURCE_ROOT; };
+		0053528A0F8C4DFF00EE34B6 /* SkFontHost_tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkFontHost_tables.cpp; path = ../../src/ports/SkFontHost_tables.cpp; sourceTree = SOURCE_ROOT; };
 		007A7CA40F01658C00A2D6EE /* SamplePicture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SamplePicture.cpp; path = ../../samplecode/SamplePicture.cpp; sourceTree = SOURCE_ROOT; };
 		007A7CA50F01658C00A2D6EE /* SamplePoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SamplePoints.cpp; path = ../../samplecode/SamplePoints.cpp; sourceTree = SOURCE_ROOT; };
 		007A7CA70F01658C00A2D6EE /* SampleRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleRegion.cpp; path = ../../samplecode/SampleRegion.cpp; sourceTree = SOURCE_ROOT; };
@@ -334,6 +335,7 @@
 				002884490EFAA35C0083E387 /* core.xcodeproj */,
 				002884B40EFAB69F0083E387 /* maccore.xcodeproj */,
 				00003C8C0EFC230E000FF73A /* effects.xcodeproj */,
+				0053528A0F8C4DFF00EE34B6 /* SkFontHost_tables.cpp */,
 			);
 			name = CICarbonSample;
 			sourceTree = "<group>";