SkPDF: maybe save some RAM by making the bitsets smaller

Wrap SkBitSet in a thin SkPDFGlyphUse class that manages new mapping.

Change-Id: Id97d42b8961f49c93fd45fdefad69d0aa9e273c5
Reviewed-on: https://skia-review.googlesource.com/c/163882
Auto-Submit: Hal Canary <halcanary@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index a7b3662..d797338 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -277,9 +277,7 @@
 SkPDFFont::SkPDFFont(SkPDFFont::Info info)
     : SkPDFDict("Font")
     , fTypeface(std::move(info.fTypeface))
-    , fGlyphUsage(info.fLastGlyphID + 1)  // TODO(halcanary): Adjust mapping?
-    , fFirstGlyphID(info.fFirstGlyphID)
-    , fLastGlyphID(info.fLastGlyphID)
+    , fGlyphUsage(info.fFirstGlyphID, info.fLastGlyphID)
     , fFontType(info.fFontType) {
     SkASSERT(fTypeface);
 }
@@ -349,7 +347,7 @@
 
 static sk_sp<SkPDFStream> get_subset_font_stream(
         std::unique_ptr<SkStreamAsset> fontAsset,
-        const SkBitSet& glyphUsage,
+        const SkPDFGlyphUse& glyphUsage,
         const char* fontName,
         int ttcIndex) {
     // Generate glyph id array in format needed by sfntly.
@@ -425,6 +423,7 @@
                 #ifdef SK_PDF_USE_SFNTLY
                 if (!SkToBool(metrics.fFlags &
                               SkAdvancedTypefaceMetrics::kNotSubsettable_FontFlag)) {
+                    SkASSERT(this->firstGlyphID() == 1);
                     sk_sp<SkPDFStream> subsetStream = get_subset_font_stream(
                             std::move(fontAsset), this->glyphUsage(),
                             metrics.fFontName.c_str(), ttcIndex);
@@ -696,7 +695,7 @@
 static void add_type3_font_info(SkPDFCanon* canon,
                                 SkPDFDict* font,
                                 SkTypeface* typeface,
-                                const SkBitSet& subset,
+                                const SkPDFGlyphUse& subset,
                                 SkGlyphID firstGlyphID,
                                 SkGlyphID lastGlyphID) {
     const SkAdvancedTypefaceMetrics* metrics = SkPDFFont::GetMetrics(typeface, canon);
@@ -860,6 +859,6 @@
 
 void SkPDFFont::drop() {
     fTypeface = nullptr;
-    fGlyphUsage = SkBitSet(0);
+    fGlyphUsage = SkPDFGlyphUse();
     this->SkPDFDict::drop();
 }
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
index 5efcdaf..28957b0 100644
--- a/src/pdf/SkPDFFont.h
+++ b/src/pdf/SkPDFFont.h
@@ -10,8 +10,8 @@
 #define SkPDFFont_DEFINED
 
 #include "SkAdvancedTypefaceMetrics.h"
-#include "SkBitSet.h"
 #include "SkPDFCanon.h"
+#include "SkPDFGlyphUse.h"
 #include "SkPDFTypes.h"
 #include "SkStrikeCache.h"
 #include "SkTypeface.h"
@@ -55,7 +55,7 @@
     /** Return true if this font has an encoding for the passed glyph id.
      */
     bool hasGlyph(SkGlyphID gid) {
-        return (gid >= fFirstGlyphID && gid <= fLastGlyphID) || gid == 0;
+        return (gid >= this->firstGlyphID() && gid <= this->lastGlyphID()) || gid == 0;
     }
 
     /** Convert the input glyph ID into the font encoding.  */
@@ -63,9 +63,9 @@
         if (this->multiByteGlyphs() || gid == 0) {
             return gid;
         }
-        SkASSERT(gid >= fFirstGlyphID && gid <= fLastGlyphID);
-        SkASSERT(fFirstGlyphID > 0);
-        return gid - fFirstGlyphID + 1;
+        SkASSERT(gid >= this->firstGlyphID() && gid <= this->lastGlyphID());
+        SkASSERT(this->firstGlyphID() > 0);
+        return gid - this->firstGlyphID() + 1;
     }
 
     void noteGlyphUsage(SkGlyphID glyph) {
@@ -117,21 +117,19 @@
     };
     SkPDFFont(Info);
 
-    SkGlyphID firstGlyphID() const { return fFirstGlyphID; }
-    SkGlyphID lastGlyphID() const { return fLastGlyphID; }
-    const SkBitSet& glyphUsage() const { return fGlyphUsage; }
+    SkGlyphID firstGlyphID() const { return fGlyphUsage.firstNonZero(); }
+    SkGlyphID lastGlyphID() const { return fGlyphUsage.lastGlyph(); }
+    const SkPDFGlyphUse& glyphUsage() const { return fGlyphUsage; }
     sk_sp<SkTypeface> refTypeface() const { return fTypeface; }
 
     void drop() override;
 
 private:
     sk_sp<SkTypeface> fTypeface;
-    SkBitSet fGlyphUsage;
+    SkPDFGlyphUse fGlyphUsage;
 
     // The glyph IDs accessible with this font.  For Type1 (non CID) fonts,
     // this will be a subset if the font has more than 255 glyphs.
-    const SkGlyphID fFirstGlyphID;
-    const SkGlyphID fLastGlyphID;
     const SkAdvancedTypefaceMetrics::FontType fFontType;
 
     typedef SkPDFDict INHERITED;
diff --git a/src/pdf/SkPDFGlyphUse.h b/src/pdf/SkPDFGlyphUse.h
new file mode 100644
index 0000000..5ee76e8
--- /dev/null
+++ b/src/pdf/SkPDFGlyphUse.h
@@ -0,0 +1,49 @@
+// Copyright 2018 Google LLC.
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+#ifndef SkPDFGlyphUse_DEFINED
+#define SkPDFGlyphUse_DEFINED
+
+#include "SkBitSet.h"
+#include "SkTypes.h"
+
+class SkPDFGlyphUse {
+public:
+    SkPDFGlyphUse() : fBitSet(0) {}
+    SkPDFGlyphUse(SkGlyphID firstNonZero, SkGlyphID lastGlyph)
+        : fBitSet((int)lastGlyph - firstNonZero + 2)
+        , fFirstNonZero(firstNonZero)
+        , fLastGlyph(lastGlyph) { SkASSERT(firstNonZero >= 1); }
+    ~SkPDFGlyphUse() = default;
+    SkPDFGlyphUse(SkPDFGlyphUse&&) = default;
+    SkPDFGlyphUse& operator=(SkPDFGlyphUse&&) = default;
+
+    SkGlyphID firstNonZero() const { return fFirstNonZero; }
+    SkGlyphID lastGlyph() const { return fLastGlyph; }
+    void set(SkGlyphID gid) { fBitSet.set(this->toCode(gid)); }
+    bool has(SkGlyphID gid) const { return fBitSet.has(this->toCode(gid)); }
+
+    template<typename FN>
+    void getSetValues(FN f) const {
+        if (fFirstNonZero == 1) {
+            return fBitSet.getSetValues(std::move(f));
+        }
+        uint16_t offset = fFirstNonZero - 1;
+        fBitSet.getSetValues([&f, offset](unsigned v) { f(v == 0 ? v : v + offset); });
+    }
+
+private:
+    SkBitSet fBitSet;
+    SkGlyphID fFirstNonZero = 0;
+    SkGlyphID fLastGlyph = 0;
+
+    uint16_t toCode(SkGlyphID gid) const {
+        if (gid == 0 || fFirstNonZero == 1) {
+            return gid;
+        }
+        SkASSERT(gid >= fFirstNonZero && gid <= fLastGlyph);
+        return gid - fFirstNonZero + 1;
+    }
+    SkPDFGlyphUse(const SkPDFGlyphUse&) = delete;
+    SkPDFGlyphUse& operator=(const SkPDFGlyphUse&) = delete;
+};
+#endif  // SkPDFGlyphUse_DEFINED
diff --git a/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp b/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp
index d5785d3..1090e36 100644
--- a/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp
+++ b/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp
@@ -7,7 +7,7 @@
 
 #include "SkPDFMakeCIDGlyphWidthsArray.h"
 
-#include "SkBitSet.h"
+#include "SkPDFGlyphUse.h"
 #include "SkGlyphCache.h"
 #include "SkPaint.h"
 #include "SkTo.h"
@@ -144,7 +144,7 @@
 // TODO(halcanary): this function is complex enough to need its logic
 // tested with unit tests.
 sk_sp<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(SkGlyphCache* cache,
-                                               const SkBitSet* subset,
+                                               const SkPDFGlyphUse* subset,
                                                uint16_t emSize,
                                                int16_t* defaultAdvance) {
     // Assuming that on average, the ASCII representation of an advance plus
diff --git a/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h b/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h
index 4fc0072..bfa4a38 100644
--- a/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h
+++ b/src/pdf/SkPDFMakeCIDGlyphWidthsArray.h
@@ -9,14 +9,14 @@
 
 #include "SkPDFTypes.h"
 
-class SkBitSet;
 class SkGlyphCache;
+class SkPDFGlyphUse;
 
 /* PDF 32000-1:2008, page 270: "The array's elements have a variable
    format that can specify individual widths for consecutive CIDs or
    one width for a range of CIDs". */
 sk_sp<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(SkGlyphCache* cache,
-                                               const SkBitSet* subset,
+                                               const SkPDFGlyphUse* subset,
                                                uint16_t emSize,
                                                int16_t* defaultWidth);
 
diff --git a/src/pdf/SkPDFMakeToUnicodeCmap.cpp b/src/pdf/SkPDFMakeToUnicodeCmap.cpp
index f4789a8..a329547 100644
--- a/src/pdf/SkPDFMakeToUnicodeCmap.cpp
+++ b/src/pdf/SkPDFMakeToUnicodeCmap.cpp
@@ -150,7 +150,7 @@
 // one of them), the possible savings by aggressive optimization is 416KB
 // pre-compressed and does not provide enough motivation for implementation.
 void SkPDFAppendCmapSections(const SkUnichar* glyphToUnicode,
-                             const SkBitSet* subset,
+                             const SkPDFGlyphUse* subset,
                              SkDynamicMemoryWStream* cmap,
                              bool multiByteGlyphs,
                              SkGlyphID firstGlyphID,
@@ -168,8 +168,8 @@
     const int limit = (int)lastGlyphID + 1 - glyphOffset;
 
     for (int i = firstGlyphID - glyphOffset; i < limit + 1; ++i) {
-        bool inSubset = i < limit &&
-                        (subset == nullptr || subset->has(i + glyphOffset));
+        SkGlyphID gid = i + glyphOffset;
+        bool inSubset = i < limit && (subset == nullptr || subset->has(gid));
         if (!rangeEmpty) {
             // PDF spec requires bfrange not changing the higher byte,
             // e.g. <1035> <10FF> <2222> is ok, but
@@ -178,7 +178,7 @@
                 i == currentRangeEntry.fEnd + 1 &&
                 i >> 8 == currentRangeEntry.fStart >> 8 &&
                 i < limit &&
-                glyphToUnicode[i + glyphOffset] ==
+                glyphToUnicode[gid] ==
                     currentRangeEntry.fUnicode + i - currentRangeEntry.fStart;
             if (!inSubset || !inRange) {
                 if (currentRangeEntry.fEnd > currentRangeEntry.fStart) {
@@ -193,7 +193,7 @@
             currentRangeEntry.fEnd = i;
             if (rangeEmpty) {
               currentRangeEntry.fStart = i;
-              currentRangeEntry.fUnicode = glyphToUnicode[i + glyphOffset];
+              currentRangeEntry.fUnicode = glyphToUnicode[gid];
               rangeEmpty = false;
             }
         }
@@ -207,7 +207,7 @@
 
 sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
         const SkUnichar* glyphToUnicode,
-        const SkBitSet* subset,
+        const SkPDFGlyphUse* subset,
         bool multiByteGlyphs,
         SkGlyphID firstGlyphID,
         SkGlyphID lastGlyphID) {
diff --git a/src/pdf/SkPDFMakeToUnicodeCmap.h b/src/pdf/SkPDFMakeToUnicodeCmap.h
index c7eaed9..f52862f 100644
--- a/src/pdf/SkPDFMakeToUnicodeCmap.h
+++ b/src/pdf/SkPDFMakeToUnicodeCmap.h
@@ -12,14 +12,14 @@
 
 sk_sp<SkPDFStream> SkPDFMakeToUnicodeCmap(
         const SkUnichar* glyphToUnicode,
-        const SkBitSet* subset,
+        const SkPDFGlyphUse* subset,
         bool multiByteGlyphs,
         SkGlyphID firstGlyphID,
         SkGlyphID lastGlyphID);
 
 // Exposed for unit testing.
 void SkPDFAppendCmapSections(const SkUnichar* glyphToUnicode,
-                             const SkBitSet* subset,
+                             const SkPDFGlyphUse* subset,
                              SkDynamicMemoryWStream* cmap,
                              bool multiByteGlyphs,
                              SkGlyphID firstGlyphID,
diff --git a/tests/PDFGlyphsToUnicodeTest.cpp b/tests/PDFGlyphsToUnicodeTest.cpp
index 2aa52f8..71d75dd 100644
--- a/tests/PDFGlyphsToUnicodeTest.cpp
+++ b/tests/PDFGlyphsToUnicodeTest.cpp
@@ -15,7 +15,7 @@
 #include "SkStream.h"
 #include "SkTo.h"
 
-static const int kMaximumGlyphCount = UINT16_MAX + 1;
+static constexpr SkGlyphID kMaximumGlyphIndex = UINT16_MAX;
 
 static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
                           const char* buffer, size_t len) {
@@ -37,7 +37,7 @@
 DEF_TEST(SkPDF_ToUnicode, reporter) {
     SkTDArray<SkUnichar> glyphToUnicode;
     SkTDArray<uint16_t> glyphsInSubset;
-    SkBitSet subset(kMaximumGlyphCount);
+    SkPDFGlyphUse subset(1, kMaximumGlyphIndex);
 
     glyphToUnicode.push_back(0);  // 0
     glyphToUnicode.push_back(0);  // 1
@@ -155,7 +155,7 @@
 
     glyphToUnicode.reset();
     glyphsInSubset.reset();
-    SkBitSet subset2(kMaximumGlyphCount);
+    SkPDFGlyphUse subset2(1, kMaximumGlyphIndex);
 
     // Test mapping:
     //           I  n  s  t  a  l