Bug 1024804 p2 - scan scriptrun for characters lacking variant glyphs. r=jfkthame
authorJohn Daggett <jdaggett@mozilla.com>
Tue, 22 Jul 2014 09:02:48 +0900
changeset 195308 4cb44d54d48cdbb3096cc7ec28605ce6a2084261
parent 195307 7d18d41e8a090517c0e910edf41142ac41ec321c
child 195309 17d016b2af19c8dbd098f642f56931f542882fd3
push id46574
push userjdaggett@mozilla.com
push dateTue, 22 Jul 2014 00:05:11 +0000
treeherdermozilla-inbound@7bafd7701140 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1024804
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1024804 p2 - scan scriptrun for characters lacking variant glyphs. r=jfkthame
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxHarfBuzzShaper.cpp
gfx/thebes/gfxHarfBuzzShaper.h
layout/media/symbols.def.in
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -172,32 +172,43 @@ gfxFontEntry::gfxFontEntry(const nsAStri
     mHBFace(nullptr),
     mGrFace(nullptr),
     mGrFaceRefCnt(0)
 {
     memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
     memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
 }
 
+static PLDHashOperator
+DestroyHBSet(const uint32_t& aTag, hb_set_t*& aSet, void *aUserArg)
+{
+    hb_set_destroy(aSet);
+    return PL_DHASH_NEXT;
+}
+
 gfxFontEntry::~gfxFontEntry()
 {
     if (mCOLR) {
         hb_blob_destroy(mCOLR);
     }
 
     if (mCPAL) {
         hb_blob_destroy(mCPAL);
     }
 
     // For downloaded fonts, we need to tell the user font cache that this
     // entry is being deleted.
     if (!mIsProxy && IsUserFont() && !IsLocalUserFont()) {
         gfxUserFontSet::UserFontCache::ForgetFont(this);
     }
 
+    if (mFeatureInputs) {
+        mFeatureInputs->Enumerate(DestroyHBSet, nullptr);
+    }
+
     // By the time the entry is destroyed, all font instances that were
     // using it should already have been deleted, and so the HB and/or Gr
     // face objects should have been released.
     MOZ_ASSERT(!mHBFace);
     MOZ_ASSERT(!mGrFaceInitialized);
 }
 
 bool gfxFontEntry::IsSymbolFont() 
@@ -920,25 +931,18 @@ gfxFontEntry::SupportsOpenTypeFeature(in
         return result;
     }
 
     result = false;
 
     hb_face_t *face = GetHBFace();
 
     if (hb_ot_layout_has_substitution(face)) {
-        // Decide what harfbuzz script code will be used for shaping
-        hb_script_t hbScript;
-        if (aScript <= MOZ_SCRIPT_INHERITED) {
-            // For unresolved "common" or "inherited" runs, default to Latin
-            // for now. (Compare gfxHarfBuzzShaper.)
-            hbScript = HB_SCRIPT_LATIN;
-        } else {
-            hbScript = hb_script_t(GetScriptTagForCode(aScript));
-        }
+        hb_script_t hbScript =
+            gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
 
         // Get the OpenType tag(s) that match this script code
         hb_tag_t scriptTags[4] = {
             HB_TAG_NONE,
             HB_TAG_NONE,
             HB_TAG_NONE,
             HB_TAG_NONE
         };
@@ -972,16 +976,76 @@ gfxFontEntry::SupportsOpenTypeFeature(in
 
     hb_face_destroy(face);
 
     mSupportedFeatures->Put(scriptFeature, result);
 
     return result;
 }
 
+const hb_set_t*
+gfxFontEntry::InputsForOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag)
+{
+    if (!mFeatureInputs) {
+        mFeatureInputs = new nsDataHashtable<nsUint32HashKey,hb_set_t*>();
+    }
+
+    NS_ASSERTION(aFeatureTag == HB_TAG('s','u','p','s') ||
+                 aFeatureTag == HB_TAG('s','u','b','s'),
+                 "use of unknown feature tag");
+
+    uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
+    hb_set_t *inputGlyphs;
+    if (mFeatureInputs->Get(scriptFeature, &inputGlyphs)) {
+        return inputGlyphs;
+    }
+
+    inputGlyphs = hb_set_create();
+
+    hb_face_t *face = GetHBFace();
+
+    if (hb_ot_layout_has_substitution(face)) {
+        hb_script_t hbScript =
+            gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
+
+        // Get the OpenType tag(s) that match this script code
+        hb_tag_t scriptTags[4] = {
+            HB_TAG_NONE,
+            HB_TAG_NONE,
+            HB_TAG_NONE,
+            HB_TAG_NONE
+        };
+        hb_ot_tags_from_script(hbScript, &scriptTags[0], &scriptTags[1]);
+
+        // Replace the first remaining NONE with DEFAULT
+        hb_tag_t* scriptTag = &scriptTags[0];
+        while (*scriptTag != HB_TAG_NONE) {
+            ++scriptTag;
+        }
+        *scriptTag = HB_OT_TAG_DEFAULT_SCRIPT;
+
+        const hb_tag_t kGSUB = HB_TAG('G','S','U','B');
+        hb_tag_t features[2] = { aFeatureTag, HB_TAG_NONE };
+        hb_set_t *featurelookups = hb_set_create();
+        hb_ot_layout_collect_lookups(face, kGSUB, scriptTags, nullptr,
+                                     features, featurelookups);
+        hb_codepoint_t index = -1;
+        while (hb_set_next(featurelookups, &index)) {
+            hb_ot_layout_lookup_collect_glyphs(face, kGSUB, index,
+                                               nullptr, inputGlyphs,
+                                               nullptr, nullptr);
+        }
+    }
+
+    hb_face_destroy(face);
+
+    mFeatureInputs->Put(scriptFeature, inputGlyphs);
+    return inputGlyphs;
+}
+
 bool
 gfxFontEntry::SupportsGraphiteFeature(uint32_t aFeatureTag)
 {
     if (!mSupportedFeatures) {
         mSupportedFeatures = new nsDataHashtable<nsUint32HashKey,bool>();
     }
 
     // note: high-order three bytes *must* be unique for each feature
@@ -2857,30 +2921,87 @@ gfxFont::SupportsVariantCaps(int32_t aSc
 
     NS_ASSERTION(!(!ok && aFallbackToSmallCaps),
                  "if we found a usable fallback, that counts as ok");
 
     return ok;
 }
 
 bool
-gfxFont::SupportsSubSuperscript(int32_t aScript, uint32_t aSubSuperscript)
-{
-    bool ok = false;
-    switch (aSubSuperscript) {
-        case NS_FONT_VARIANT_POSITION_SUPER:
-            ok = SupportsFeature(aScript, HB_TAG('s','u','p','s'));
-            break;
-        case NS_FONT_VARIANT_POSITION_SUB:
-            ok = SupportsFeature(aScript, HB_TAG('s','u','b','s'));
-            break;
-        default:
-            break;
-    }
-    return ok;
+gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
+                                const uint8_t *aString,
+                                uint32_t aLength, int32_t aRunScript)
+{
+    NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString),
+                                         aLength);
+    return SupportsSubSuperscript(aSubSuperscript, unicodeString.get(),
+                                  aLength, aRunScript);
+}
+
+bool
+gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
+                                const char16_t *aString,
+                                uint32_t aLength, int32_t aRunScript)
+{
+    NS_ASSERTION(aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ||
+                 aSubSuperscript == NS_FONT_VARIANT_POSITION_SUB,
+                 "unknown value of font-variant-position");
+
+    uint32_t feature = aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ?
+                       HB_TAG('s','u','p','s') : HB_TAG('s','u','b','s');
+
+    if (!SupportsFeature(aRunScript, feature)) {
+        return false;
+    }
+
+    // xxx - for graphite, don't really know how to sniff lookups so bail
+    if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
+        return true;
+    }
+
+    if (!mHarfBuzzShaper) {
+        mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
+    }
+    gfxHarfBuzzShaper* shaper =
+        static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
+    if (!shaper->Initialize()) {
+        return false;
+    }
+
+    // get the hbset containing input glyphs for the feature
+    const hb_set_t *inputGlyphs = mFontEntry->InputsForOpenTypeFeature(aRunScript, feature);
+
+    // create an hbset containing default glyphs for the script run
+    hb_set_t *defaultGlyphsInRun = hb_set_create();
+
+    // for each character, get the glyph id
+    for (uint32_t i = 0; i < aLength; i++) {
+        uint32_t ch = aString[i];
+
+        if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
+                             NS_IS_LOW_SURROGATE(aString[i + 1])) {
+            i++;
+            ch = SURROGATE_TO_UCS4(ch, aString[i]);
+        }
+
+        if (ch == 0xa0) {
+            ch = ' ';
+        }
+
+        hb_codepoint_t gid = shaper->GetGlyph(ch, 0);
+        hb_set_add(defaultGlyphsInRun, gid);
+    }
+
+    // intersect with input glyphs, if size is not the same ==> fallback
+    uint32_t origSize = hb_set_get_population(defaultGlyphsInRun);
+    hb_set_intersect(defaultGlyphsInRun, inputGlyphs);
+    uint32_t intersectionSize = hb_set_get_population(defaultGlyphsInRun);
+    hb_set_destroy(defaultGlyphsInRun);
+
+    return origSize == intersectionSize;
 }
 
 bool
 gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn)
 {
     aFeatureOn = false;
 
     if (mStyle.featureSettings.IsEmpty() &&
@@ -5490,18 +5611,19 @@ gfxFontGroup::InitScriptRun(gfxContext *
             // shape with some variant feature that requires fallback handling
             bool petiteToSmallCaps = false;
             bool syntheticLower = false;
             bool syntheticUpper = false;
 
             if (mStyle.variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
                 (aTextRun->GetShapingState() ==
                      gfxTextRun::eShapingState_ForceFallbackFeature ||
-                 !matchedFont->SupportsSubSuperscript(aRunScript,
-                                                      mStyle.variantSubSuper)))
+                 !matchedFont->SupportsSubSuperscript(mStyle.variantSubSuper,
+                                                      aString, aLength,
+                                                      aRunScript)))
             {
                 // fallback for subscript/superscript variant glyphs
 
                 // if the feature was already used, abort and force
                 // fallback across the entire textrun
                 gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
 
                 if (ss == gfxTextRun::eShapingState_Normal) {
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -292,16 +292,20 @@ public:
     bool IgnoreGDEF() const { return mIgnoreGDEF; }
     bool IgnoreGSUB() const { return mIgnoreGSUB; }
 
     // whether a feature is supported by the font (limited to a small set
     // of features for which some form of fallback needs to be implemented)
     bool SupportsOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag);
     bool SupportsGraphiteFeature(uint32_t aFeatureTag);
 
+    // returns a set containing all input glyph ids for a given feature
+    const hb_set_t*
+    InputsForOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag);
+
     virtual bool IsSymbolFont();
 
     virtual bool HasFontTable(uint32_t aTableTag);
 
     inline bool HasGraphiteTables() {
         if (!mCheckedForGraphiteTables) {
             CheckForGraphiteTables();
             mCheckedForGraphiteTables = true;
@@ -582,16 +586,17 @@ public:
     nsAutoArrayPtr<uint8_t> mUVSData;
     nsAutoPtr<gfxUserFontData> mUserFontData;
     nsAutoPtr<gfxSVGGlyphs> mSVGGlyphs;
     // list of gfxFonts that are using SVG glyphs
     nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
     nsAutoPtr<gfxMathTable> mMathTable;
     nsTArray<gfxFontFeature> mFeatureSettings;
     nsAutoPtr<nsDataHashtable<nsUint32HashKey,bool>> mSupportedFeatures;
+    nsAutoPtr<nsDataHashtable<nsUint32HashKey,hb_set_t*>> mFeatureInputs;
     uint32_t         mLanguageOverride;
 
     // Color Layer font support
     hb_blob_t*       mCOLR;
     hb_blob_t*       mCPAL;
 
 protected:
     friend class gfxPlatformFontList;
@@ -1647,17 +1652,25 @@ public:
     // whether the font supports "real" small caps, petite caps etc.
     // aFallbackToSmallCaps true when petite caps should fallback to small caps
     bool SupportsVariantCaps(int32_t aScript, uint32_t aVariantCaps,
                              bool& aFallbackToSmallCaps,
                              bool& aSyntheticLowerToSmallCaps,
                              bool& aSyntheticUpperToSmallCaps);
 
     // whether the font supports subscript/superscript feature
-    bool SupportsSubSuperscript(int32_t aScript, uint32_t aSubSuperscript);
+    // for fallback, need to verify that all characters in the run
+    // have variant substitutions
+    bool SupportsSubSuperscript(uint32_t aSubSuperscript,
+                                const uint8_t *aString,
+                                uint32_t aLength, int32_t aRunScript);
+
+    bool SupportsSubSuperscript(uint32_t aSubSuperscript,
+                                const char16_t *aString,
+                                uint32_t aLength, int32_t aRunScript);
 
     // Subclasses may choose to look up glyph ids for characters.
     // If they do not override this, gfxHarfBuzzShaper will fetch the cmap
     // table and use that.
     virtual bool ProvidesGetGlyph() const {
         return false;
     }
     // Map unicode character to glyph ID.
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -998,23 +998,18 @@ gfxHarfBuzzShaper::ShapeText(gfxContext 
     bool isRightToLeft = aShapedText->IsRightToLeft();
     hb_buffer_t *buffer = hb_buffer_create();
     hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
     hb_buffer_set_direction(buffer, isRightToLeft ? HB_DIRECTION_RTL :
                                                     HB_DIRECTION_LTR);
     hb_script_t scriptTag;
     if (aShapedText->Flags() & gfxTextRunFactory::TEXT_USE_MATH_SCRIPT) {
         scriptTag = sMathScript;
-    } else if (aScript <= MOZ_SCRIPT_INHERITED) {
-        // For unresolved "common" or "inherited" runs, default to Latin for
-        // now.  (Should we somehow use the language or locale to try and infer
-        // a better default?)
-        scriptTag = HB_SCRIPT_LATIN;
     } else {
-        scriptTag = hb_script_t(GetScriptTagForCode(aScript));
+        scriptTag = GetHBScriptUsedForShaping(aScript);
     }
     hb_buffer_set_script(buffer, scriptTag);
 
     hb_language_t language;
     if (style->languageOverride) {
         language = hb_ot_tag_to_language(style->languageOverride);
     } else if (entry->mLanguageOverride) {
         language = hb_ot_tag_to_language(entry->mLanguageOverride);
--- a/gfx/thebes/gfxHarfBuzzShaper.h
+++ b/gfx/thebes/gfxHarfBuzzShaper.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_HARFBUZZSHAPER_H
 #define GFX_HARFBUZZSHAPER_H
 
 #include "gfxFont.h"
 
 #include "harfbuzz/hb.h"
+#include "nsUnicodeProperties.h"
 
 class gfxHarfBuzzShaper : public gfxFontShaper {
 public:
     gfxHarfBuzzShaper(gfxFont *aFont);
     virtual ~gfxHarfBuzzShaper();
 
     /*
      * For HarfBuzz font callback functions, font_data is a ptr to a
@@ -46,16 +47,31 @@ public:
     // get harfbuzz horizontal advance in 16.16 fixed point format.
     static hb_position_t
     HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
                        hb_codepoint_t glyph, void *user_data);
 
     hb_position_t GetHKerning(uint16_t aFirstGlyph,
                               uint16_t aSecondGlyph) const;
 
+    static hb_script_t
+    GetHBScriptUsedForShaping(int32_t aScript) {
+        // Decide what harfbuzz script code will be used for shaping
+        hb_script_t hbScript;
+        if (aScript <= MOZ_SCRIPT_INHERITED) {
+            // For unresolved "common" or "inherited" runs,
+            // default to Latin for now.
+            hbScript = HB_SCRIPT_LATIN;
+        } else {
+            hbScript =
+                hb_script_t(mozilla::unicode::GetScriptTagForCode(aScript));
+        }
+        return hbScript;
+    }
+
 protected:
     nsresult SetGlyphsFromRun(gfxContext      *aContext,
                               gfxShapedText   *aShapedText,
                               uint32_t         aOffset,
                               uint32_t         aLength,
                               const char16_t *aText,
                               hb_buffer_t     *aBuffer);
 
--- a/layout/media/symbols.def.in
+++ b/layout/media/symbols.def.in
@@ -583,17 +583,19 @@ hb_ot_layout_table_find_script
 hb_ot_layout_table_get_script_tags
 hb_ot_tag_to_language
 hb_ot_tag_to_script
 hb_ot_tags_from_script
 hb_set_add
 hb_set_clear
 hb_set_create
 hb_set_destroy
+hb_set_get_population
 hb_set_has
+hb_set_intersect
 hb_set_is_empty
 hb_set_next
 hb_shape
 hb_unicode_funcs_create
 hb_unicode_funcs_get_empty
 hb_unicode_funcs_set_combining_class_func
 hb_unicode_funcs_set_compose_func
 hb_unicode_funcs_set_decompose_func