LCOV - code coverage report
Current view: top level - gfx/thebes - gfxGraphiteShaper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 208 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "gfxGraphiteShaper.h"
       7             : #include "nsString.h"
       8             : #include "gfxContext.h"
       9             : #include "gfxFontConstants.h"
      10             : #include "gfxTextRun.h"
      11             : 
      12             : #include "graphite2/Font.h"
      13             : #include "graphite2/Segment.h"
      14             : 
      15             : #include "harfbuzz/hb.h"
      16             : 
      17             : #define FloatToFixed(f) (65536 * (f))
      18             : #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
      19             : // Right shifts of negative (signed) integers are undefined, as are overflows
      20             : // when converting unsigned to negative signed integers.
      21             : // (If speed were an issue we could make some 2's complement assumptions.)
      22             : #define FixedToIntRound(f) ((f) > 0 ?  ((32768 + (f)) >> 16) \
      23             :                                     : -((32767 - (f)) >> 16))
      24             : 
      25             : using namespace mozilla; // for AutoSwap_* types
      26             : 
      27             : /*
      28             :  * Creation and destruction; on deletion, release any font tables we're holding
      29             :  */
      30             : 
      31           0 : gfxGraphiteShaper::gfxGraphiteShaper(gfxFont *aFont)
      32             :     : gfxFontShaper(aFont),
      33           0 :       mGrFace(mFont->GetFontEntry()->GetGrFace()),
      34           0 :       mGrFont(nullptr), mFallbackToSmallCaps(false)
      35             : {
      36           0 :     mCallbackData.mFont = aFont;
      37           0 : }
      38             : 
      39           0 : gfxGraphiteShaper::~gfxGraphiteShaper()
      40             : {
      41           0 :     if (mGrFont) {
      42           0 :         gr_font_destroy(mGrFont);
      43             :     }
      44           0 :     mFont->GetFontEntry()->ReleaseGrFace(mGrFace);
      45           0 : }
      46             : 
      47             : /*static*/ float
      48           0 : gfxGraphiteShaper::GrGetAdvance(const void* appFontHandle, uint16_t glyphid)
      49             : {
      50             :     const CallbackData *cb =
      51           0 :         static_cast<const CallbackData*>(appFontHandle);
      52           0 :     return FixedToFloat(cb->mFont->GetGlyphWidth(*cb->mDrawTarget, glyphid));
      53             : }
      54             : 
      55             : static inline uint32_t
      56           0 : MakeGraphiteLangTag(uint32_t aTag)
      57             : {
      58           0 :     uint32_t grLangTag = aTag;
      59             :     // replace trailing space-padding with NULs for graphite
      60           0 :     uint32_t mask = 0x000000FF;
      61           0 :     while ((grLangTag & mask) == ' ') {
      62           0 :         grLangTag &= ~mask;
      63           0 :         mask <<= 8;
      64             :     }
      65           0 :     return grLangTag;
      66             : }
      67             : 
      68             : struct GrFontFeatures {
      69             :     gr_face        *mFace;
      70             :     gr_feature_val *mFeatures;
      71             : };
      72             : 
      73             : static void
      74           0 : AddFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
      75             : {
      76           0 :     GrFontFeatures *f = static_cast<GrFontFeatures*>(aUserArg);
      77             : 
      78           0 :     const gr_feature_ref* fref = gr_face_find_fref(f->mFace, aTag);
      79           0 :     if (fref) {
      80           0 :         gr_fref_set_feature_value(fref, aValue, f->mFeatures);
      81             :     }
      82           0 : }
      83             : 
      84             : bool
      85           0 : gfxGraphiteShaper::ShapeText(DrawTarget      *aDrawTarget,
      86             :                              const char16_t *aText,
      87             :                              uint32_t         aOffset,
      88             :                              uint32_t         aLength,
      89             :                              Script           aScript,
      90             :                              bool             aVertical,
      91             :                              RoundingFlags    aRounding,
      92             :                              gfxShapedText   *aShapedText)
      93             : {
      94             :     // some font back-ends require this in order to get proper hinted metrics
      95           0 :     if (!mFont->SetupCairoFont(aDrawTarget)) {
      96           0 :         return false;
      97             :     }
      98             : 
      99           0 :     mCallbackData.mDrawTarget = aDrawTarget;
     100             : 
     101           0 :     const gfxFontStyle *style = mFont->GetStyle();
     102             : 
     103           0 :     if (!mGrFont) {
     104           0 :         if (!mGrFace) {
     105           0 :             return false;
     106             :         }
     107             : 
     108           0 :         if (mFont->ProvidesGlyphWidths()) {
     109             :             gr_font_ops ops = {
     110             :                 sizeof(gr_font_ops),
     111             :                 &GrGetAdvance,
     112             :                 nullptr // vertical text not yet implemented
     113           0 :             };
     114           0 :             mGrFont = gr_make_font_with_ops(mFont->GetAdjustedSize(),
     115           0 :                                             &mCallbackData, &ops, mGrFace);
     116             :         } else {
     117           0 :             mGrFont = gr_make_font(mFont->GetAdjustedSize(), mGrFace);
     118             :         }
     119             : 
     120           0 :         if (!mGrFont) {
     121           0 :             return false;
     122             :         }
     123             : 
     124             :         // determine whether petite-caps falls back to small-caps
     125           0 :         if (style->variantCaps != NS_FONT_VARIANT_CAPS_NORMAL) {
     126           0 :             switch (style->variantCaps) {
     127             :                 case NS_FONT_VARIANT_CAPS_ALLPETITE:
     128             :                 case NS_FONT_VARIANT_CAPS_PETITECAPS:
     129             :                     bool synLower, synUpper;
     130           0 :                     mFont->SupportsVariantCaps(aScript, style->variantCaps,
     131             :                                                mFallbackToSmallCaps, synLower,
     132           0 :                                                synUpper);
     133           0 :                     break;
     134             :                 default:
     135           0 :                     break;
     136             :             }
     137             :         }
     138             :     }
     139             : 
     140           0 :     gfxFontEntry *entry = mFont->GetFontEntry();
     141           0 :     uint32_t grLang = 0;
     142           0 :     if (style->languageOverride) {
     143           0 :         grLang = MakeGraphiteLangTag(style->languageOverride);
     144           0 :     } else if (entry->mLanguageOverride) {
     145           0 :         grLang = MakeGraphiteLangTag(entry->mLanguageOverride);
     146           0 :     } else if (style->explicitLanguage) {
     147           0 :         nsAutoCString langString;
     148           0 :         style->language->ToUTF8String(langString);
     149           0 :         grLang = GetGraphiteTagForLang(langString);
     150             :     }
     151           0 :     gr_feature_val *grFeatures = gr_face_featureval_for_lang(mGrFace, grLang);
     152             : 
     153             :     // insert any merged features into Graphite feature list
     154           0 :     GrFontFeatures f = {mGrFace, grFeatures};
     155           0 :     MergeFontFeatures(style,
     156           0 :                       mFont->GetFontEntry()->mFeatureSettings,
     157           0 :                       aShapedText->DisableLigatures(),
     158           0 :                       mFont->GetFontEntry()->FamilyName(),
     159           0 :                       mFallbackToSmallCaps,
     160             :                       AddFeature,
     161           0 :                       &f);
     162             : 
     163             :     // Graphite shaping doesn't map U+00a0 (nbsp) to space if it is missing
     164             :     // from the font, so check for that possibility. (Most fonts double-map
     165             :     // the space glyph to both 0x20 and 0xA0, so this won't often be needed;
     166             :     // so we don't copy the text until we know it's required.)
     167           0 :     nsAutoString transformed;
     168           0 :     const char16_t NO_BREAK_SPACE = 0x00a0;
     169           0 :     if (!entry->HasCharacter(NO_BREAK_SPACE)) {
     170           0 :         nsDependentSubstring src(aText, aLength);
     171           0 :         if (src.FindChar(NO_BREAK_SPACE) != kNotFound) {
     172           0 :             transformed = src;
     173           0 :             transformed.ReplaceChar(NO_BREAK_SPACE, ' ');
     174           0 :             aText = transformed.BeginReading();
     175             :         }
     176             :     }
     177             : 
     178             :     size_t numChars = gr_count_unicode_characters(gr_utf16,
     179           0 :                                                   aText, aText + aLength,
     180           0 :                                                   nullptr);
     181           0 :     gr_bidirtl grBidi = gr_bidirtl(aShapedText->IsRightToLeft()
     182           0 :                                    ? (gr_rtl | gr_nobidi) : gr_nobidi);
     183           0 :     gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures,
     184           0 :                                   gr_utf16, aText, numChars, grBidi);
     185             : 
     186           0 :     gr_featureval_destroy(grFeatures);
     187             : 
     188           0 :     if (!seg) {
     189           0 :         return false;
     190             :     }
     191             : 
     192             :     nsresult rv = SetGlyphsFromSegment(aShapedText, aOffset, aLength,
     193           0 :                                        aText, seg, aRounding);
     194             : 
     195           0 :     gr_seg_destroy(seg);
     196             : 
     197           0 :     return NS_SUCCEEDED(rv);
     198             : }
     199             : 
     200             : #define SMALL_GLYPH_RUN 256 // avoid heap allocation of per-glyph data arrays
     201             :                             // for short (typical) runs up to this length
     202             : 
     203             : struct Cluster {
     204             :     uint32_t baseChar; // in UTF16 code units, not Unicode character indices
     205             :     uint32_t baseGlyph;
     206             :     uint32_t nChars; // UTF16 code units
     207             :     uint32_t nGlyphs;
     208           0 :     Cluster() : baseChar(0), baseGlyph(0), nChars(0), nGlyphs(0) { }
     209             : };
     210             : 
     211             : nsresult
     212           0 : gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedText   *aShapedText,
     213             :                                         uint32_t         aOffset,
     214             :                                         uint32_t         aLength,
     215             :                                         const char16_t *aText,
     216             :                                         gr_segment      *aSegment,
     217             :                                         RoundingFlags    aRounding)
     218             : {
     219           0 :     int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
     220           0 :     bool rtl = aShapedText->IsRightToLeft();
     221             : 
     222           0 :     uint32_t glyphCount = gr_seg_n_slots(aSegment);
     223             : 
     224             :     // identify clusters; graphite may have reordered/expanded/ligated glyphs.
     225           0 :     AutoTArray<Cluster,SMALL_GLYPH_RUN> clusters;
     226           0 :     AutoTArray<uint16_t,SMALL_GLYPH_RUN> gids;
     227           0 :     AutoTArray<float,SMALL_GLYPH_RUN> xLocs;
     228           0 :     AutoTArray<float,SMALL_GLYPH_RUN> yLocs;
     229             : 
     230           0 :     if (!clusters.SetLength(aLength, fallible) ||
     231           0 :         !gids.SetLength(glyphCount, fallible) ||
     232           0 :         !xLocs.SetLength(glyphCount, fallible) ||
     233           0 :         !yLocs.SetLength(glyphCount, fallible))
     234             :     {
     235           0 :         return NS_ERROR_OUT_OF_MEMORY;
     236             :     }
     237             : 
     238             :     // walk through the glyph slots and check which original character
     239             :     // each is associated with
     240           0 :     uint32_t gIndex = 0; // glyph slot index
     241           0 :     uint32_t cIndex = 0; // current cluster index
     242           0 :     for (const gr_slot *slot = gr_seg_first_slot(aSegment);
     243           0 :          slot != nullptr;
     244             :          slot = gr_slot_next_in_segment(slot), gIndex++)
     245             :     {
     246             :         uint32_t before =
     247           0 :             gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_before(slot)));
     248             :         uint32_t after =
     249           0 :             gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_after(slot)));
     250           0 :         gids[gIndex] = gr_slot_gid(slot);
     251           0 :         xLocs[gIndex] = gr_slot_origin_X(slot);
     252           0 :         yLocs[gIndex] = gr_slot_origin_Y(slot);
     253             : 
     254             :         // if this glyph has a "before" character index that precedes the
     255             :         // current cluster's char index, we need to merge preceding
     256             :         // clusters until it gets included
     257           0 :         while (before < clusters[cIndex].baseChar && cIndex > 0) {
     258           0 :             clusters[cIndex-1].nChars += clusters[cIndex].nChars;
     259           0 :             clusters[cIndex-1].nGlyphs += clusters[cIndex].nGlyphs;
     260           0 :             --cIndex;
     261             :         }
     262             : 
     263             :         // if there's a gap between the current cluster's base character and
     264             :         // this glyph's, extend the cluster to include the intervening chars
     265           0 :         if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars &&
     266           0 :             before >= clusters[cIndex].baseChar + clusters[cIndex].nChars)
     267             :         {
     268           0 :             NS_ASSERTION(cIndex < aLength - 1, "cIndex at end of word");
     269           0 :             Cluster& c = clusters[cIndex + 1];
     270           0 :             c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars;
     271           0 :             c.nChars = before - c.baseChar;
     272           0 :             c.baseGlyph = gIndex;
     273           0 :             c.nGlyphs = 0;
     274           0 :             ++cIndex;
     275             :         }
     276             : 
     277             :         // increment cluster's glyph count to include current slot
     278           0 :         NS_ASSERTION(cIndex < aLength, "cIndex beyond word length");
     279           0 :         ++clusters[cIndex].nGlyphs;
     280             : 
     281             :         // bump |after| index if it falls in the middle of a surrogate pair
     282           0 :         if (NS_IS_HIGH_SURROGATE(aText[after]) && after < aLength - 1 &&
     283           0 :             NS_IS_LOW_SURROGATE(aText[after + 1])) {
     284           0 :             after++;
     285             :         }
     286             :         // extend cluster if necessary to reach the glyph's "after" index
     287           0 :         if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) {
     288           0 :             clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar;
     289             :         }
     290             :     }
     291             : 
     292             :     gfxShapedText::CompressedGlyph *charGlyphs =
     293           0 :         aShapedText->GetCharacterGlyphs() + aOffset;
     294             : 
     295           0 :     bool roundX = bool(aRounding & RoundingFlags::kRoundX);
     296           0 :     bool roundY = bool(aRounding & RoundingFlags::kRoundY);
     297             : 
     298             :     // now put glyphs into the textrun, one cluster at a time
     299           0 :     for (uint32_t i = 0; i <= cIndex; ++i) {
     300           0 :         const Cluster& c = clusters[i];
     301             : 
     302             :         float adv; // total advance of the cluster
     303           0 :         if (rtl) {
     304           0 :             if (i == 0) {
     305           0 :                 adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
     306             :             } else {
     307           0 :                 adv = xLocs[clusters[i-1].baseGlyph] - xLocs[c.baseGlyph];
     308             :             }
     309             :         } else {
     310           0 :             if (i == cIndex) {
     311           0 :                 adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
     312             :             } else {
     313           0 :                 adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph];
     314             :             }
     315             :         }
     316             : 
     317             :         // Check for default-ignorable char that didn't get filtered, combined,
     318             :         // etc by the shaping process, and skip it.
     319           0 :         uint32_t offs = c.baseChar;
     320           0 :         NS_ASSERTION(offs < aLength, "unexpected offset");
     321           0 :         if (c.nGlyphs == 1 && c.nChars == 1 &&
     322           0 :             aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) {
     323           0 :             continue;
     324             :         }
     325             : 
     326           0 :         uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits
     327           0 :                                      : NSToIntRound(adv * dev2appUnits);
     328           0 :         if (c.nGlyphs == 1 &&
     329           0 :             gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
     330           0 :             gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
     331           0 :             charGlyphs[offs].IsClusterStart() &&
     332           0 :             yLocs[c.baseGlyph] == 0)
     333             :         {
     334           0 :             charGlyphs[offs].SetSimpleGlyph(appAdvance, gids[c.baseGlyph]);
     335             :         } else {
     336             :             // not a one-to-one mapping with simple metrics: use DetailedGlyph
     337           0 :             AutoTArray<gfxShapedText::DetailedGlyph,8> details;
     338             :             float clusterLoc;
     339           0 :             for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
     340           0 :                 gfxShapedText::DetailedGlyph* d = details.AppendElement();
     341           0 :                 d->mGlyphID = gids[j];
     342           0 :                 d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits
     343           0 :                                      : -yLocs[j] * dev2appUnits;
     344           0 :                 if (j == c.baseGlyph) {
     345           0 :                     d->mXOffset = 0;
     346           0 :                     d->mAdvance = appAdvance;
     347           0 :                     clusterLoc = xLocs[j];
     348             :                 } else {
     349           0 :                     float dx = rtl ? (xLocs[j] - clusterLoc) :
     350           0 :                                      (xLocs[j] - clusterLoc - adv);
     351           0 :                     d->mXOffset = roundX ? NSToIntRound(dx) * dev2appUnits
     352           0 :                                          : dx * dev2appUnits;
     353           0 :                     d->mAdvance = 0;
     354             :                 }
     355             :             }
     356           0 :             gfxShapedText::CompressedGlyph g;
     357           0 :             g.SetComplex(charGlyphs[offs].IsClusterStart(),
     358           0 :                          true, details.Length());
     359           0 :             aShapedText->SetGlyphs(aOffset + offs, g, details.Elements());
     360             :         }
     361             : 
     362           0 :         for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
     363           0 :             NS_ASSERTION(j < aLength, "unexpected offset");
     364           0 :             gfxShapedText::CompressedGlyph &g = charGlyphs[j];
     365           0 :             NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
     366           0 :             g.SetComplex(g.IsClusterStart(), false, 0);
     367             :         }
     368             :     }
     369             : 
     370           0 :     return NS_OK;
     371             : }
     372             : 
     373             : #undef SMALL_GLYPH_RUN
     374             : 
     375             : // for language tag validation - include list of tags from the IANA registry
     376             : #include "gfxLanguageTagList.cpp"
     377             : 
     378             : nsTHashtable<nsUint32HashKey> *gfxGraphiteShaper::sLanguageTags;
     379             : 
     380             : /*static*/ uint32_t
     381           0 : gfxGraphiteShaper::GetGraphiteTagForLang(const nsCString& aLang)
     382             : {
     383           0 :     int len = aLang.Length();
     384           0 :     if (len < 2) {
     385           0 :         return 0;
     386             :     }
     387             : 
     388             :     // convert primary language subtag to a left-packed, NUL-padded integer
     389             :     // for the Graphite API
     390           0 :     uint32_t grLang = 0;
     391           0 :     for (int i = 0; i < 4; ++i) {
     392           0 :         grLang <<= 8;
     393           0 :         if (i < len) {
     394           0 :             uint8_t ch = aLang[i];
     395           0 :             if (ch == '-') {
     396             :                 // found end of primary language subtag, truncate here
     397           0 :                 len = i;
     398           0 :                 continue;
     399             :             }
     400           0 :             if (ch < 'a' || ch > 'z') {
     401             :                 // invalid character in tag, so ignore it completely
     402           0 :                 return 0;
     403             :             }
     404           0 :             grLang += ch;
     405             :         }
     406             :     }
     407             : 
     408             :     // valid tags must have length = 2 or 3
     409           0 :     if (len < 2 || len > 3) {
     410           0 :         return 0;
     411             :     }
     412             : 
     413           0 :     if (!sLanguageTags) {
     414             :         // store the registered IANA tags in a hash for convenient validation
     415           0 :         sLanguageTags = new nsTHashtable<nsUint32HashKey>(ArrayLength(sLanguageTagList));
     416           0 :         for (const uint32_t *tag = sLanguageTagList; *tag != 0; ++tag) {
     417           0 :             sLanguageTags->PutEntry(*tag);
     418             :         }
     419             :     }
     420             : 
     421             :     // only accept tags known in the IANA registry
     422           0 :     if (sLanguageTags->GetEntry(grLang)) {
     423           0 :         return grLang;
     424             :     }
     425             : 
     426           0 :     return 0;
     427             : }
     428             : 
     429             : /*static*/ void
     430           0 : gfxGraphiteShaper::Shutdown()
     431             : {
     432             : #ifdef NS_FREE_PERMANENT_DATA
     433           0 :     if (sLanguageTags) {
     434           0 :         sLanguageTags->Clear();
     435           0 :         delete sLanguageTags;
     436           0 :         sLanguageTags = nullptr;
     437             :     }
     438             : #endif
     439           0 : }

Generated by: LCOV version 1.13