LCOV - code coverage report
Current view: directory - gfx/thebes - gfxGraphiteShaper.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 195 4 2.1 %
Date: 2012-06-02 Functions: 17 3 17.6 %

       1                 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla Graphite integration code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Mozilla Foundation.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   Jonathan Kew <jfkthame@gmail.com>
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "prtypes.h"
      39                 : #include "prmem.h"
      40                 : #include "nsString.h"
      41                 : #include "nsBidiUtils.h"
      42                 : #include "nsMathUtils.h"
      43                 : 
      44                 : #include "gfxTypes.h"
      45                 : 
      46                 : #include "gfxContext.h"
      47                 : #include "gfxPlatform.h"
      48                 : #include "gfxGraphiteShaper.h"
      49                 : #include "gfxFontUtils.h"
      50                 : 
      51                 : #include "graphite2/Font.h"
      52                 : #include "graphite2/Segment.h"
      53                 : 
      54                 : #include "harfbuzz/hb-blob.h"
      55                 : 
      56                 : #include "cairo.h"
      57                 : 
      58                 : #include "nsUnicodeRange.h"
      59                 : #include "nsCRT.h"
      60                 : 
      61                 : #define FloatToFixed(f) (65536 * (f))
      62                 : #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
      63                 : // Right shifts of negative (signed) integers are undefined, as are overflows
      64                 : // when converting unsigned to negative signed integers.
      65                 : // (If speed were an issue we could make some 2's complement assumptions.)
      66                 : #define FixedToIntRound(f) ((f) > 0 ?  ((32768 + (f)) >> 16) \
      67                 :                                     : -((32767 - (f)) >> 16))
      68                 : 
      69                 : using namespace mozilla; // for AutoSwap_* types
      70                 : 
      71                 : /*
      72                 :  * Creation and destruction; on deletion, release any font tables we're holding
      73                 :  */
      74                 : 
      75               0 : gfxGraphiteShaper::gfxGraphiteShaper(gfxFont *aFont)
      76                 :     : gfxFontShaper(aFont),
      77                 :       mGrFace(nsnull),
      78                 :       mGrFont(nsnull),
      79               0 :       mUseFontGlyphWidths(false)
      80                 : {
      81               0 :     mTables.Init();
      82               0 :     mCallbackData.mFont = aFont;
      83               0 :     mCallbackData.mShaper = this;
      84               0 : }
      85                 : 
      86                 : PLDHashOperator
      87               0 : ReleaseTableFunc(const PRUint32& /* aKey */,
      88                 :                  gfxGraphiteShaper::TableRec& aData,
      89                 :                  void* /* aUserArg */)
      90                 : {
      91               0 :     hb_blob_destroy(aData.mBlob);
      92               0 :     return PL_DHASH_REMOVE;
      93                 : }
      94                 : 
      95               0 : gfxGraphiteShaper::~gfxGraphiteShaper()
      96                 : {
      97               0 :     if (mGrFont) {
      98               0 :         gr_font_destroy(mGrFont);
      99                 :     }
     100               0 :     if (mGrFace) {
     101               0 :         gr_face_destroy(mGrFace);
     102                 :     }
     103               0 :     mTables.Enumerate(ReleaseTableFunc, nsnull);
     104               0 : }
     105                 : 
     106                 : static const void*
     107               0 : GrGetTable(const void* appFaceHandle, unsigned int name, size_t *len)
     108                 : {
     109                 :     const gfxGraphiteShaper::CallbackData *cb =
     110               0 :         static_cast<const gfxGraphiteShaper::CallbackData*>(appFaceHandle);
     111               0 :     return cb->mShaper->GetTable(name, len);
     112                 : }
     113                 : 
     114                 : const void*
     115               0 : gfxGraphiteShaper::GetTable(PRUint32 aTag, size_t *aLength)
     116                 : {
     117                 :     TableRec tableRec;
     118                 : 
     119               0 :     if (!mTables.Get(aTag, &tableRec)) {
     120               0 :         hb_blob_t *blob = mFont->GetFontTable(aTag);
     121               0 :         if (blob) {
     122                 :             // mFont->GetFontTable() gives us a reference to the blob.
     123                 :             // We will destroy (release) it in our destructor.
     124               0 :             tableRec.mBlob = blob;
     125               0 :             tableRec.mData = hb_blob_get_data(blob, &tableRec.mLength);
     126               0 :             mTables.Put(aTag, tableRec);
     127                 :         } else {
     128               0 :             return nsnull;
     129                 :         }
     130                 :     }
     131                 : 
     132               0 :     *aLength = tableRec.mLength;
     133               0 :     return tableRec.mData;
     134                 : }
     135                 : 
     136                 : static float
     137               0 : GrGetAdvance(const void* appFontHandle, gr_uint16 glyphid)
     138                 : {
     139                 :     const gfxGraphiteShaper::CallbackData *cb =
     140               0 :         static_cast<const gfxGraphiteShaper::CallbackData*>(appFontHandle);
     141               0 :     return FixedToFloat(cb->mFont->GetGlyphWidth(cb->mContext, glyphid));
     142                 : }
     143                 : 
     144                 : static inline PRUint32
     145               0 : MakeGraphiteLangTag(PRUint32 aTag)
     146                 : {
     147               0 :     PRUint32 grLangTag = aTag;
     148                 :     // replace trailing space-padding with NULs for graphite
     149               0 :     PRUint32 mask = 0x000000FF;
     150               0 :     while ((grLangTag & mask) == ' ') {
     151               0 :         grLangTag &= ~mask;
     152               0 :         mask <<= 8;
     153                 :     }
     154               0 :     return grLangTag;
     155                 : }
     156                 : 
     157                 : bool
     158               0 : gfxGraphiteShaper::ShapeWord(gfxContext      *aContext,
     159                 :                              gfxShapedWord   *aShapedWord,
     160                 :                              const PRUnichar *aText)
     161                 : {
     162                 :     // some font back-ends require this in order to get proper hinted metrics
     163               0 :     mFont->SetupCairoFont(aContext);
     164                 : 
     165               0 :     mCallbackData.mContext = aContext;
     166                 : 
     167               0 :     if (!mGrFont) {
     168               0 :         mGrFace = gr_make_face(&mCallbackData, GrGetTable, gr_face_default);
     169               0 :         if (!mGrFace) {
     170               0 :             return false;
     171                 :         }
     172                 :         mGrFont = mUseFontGlyphWidths ?
     173               0 :             gr_make_font_with_advance_fn(mFont->GetAdjustedSize(),
     174                 :                                          &mCallbackData, GrGetAdvance,
     175               0 :                                          mGrFace) :
     176               0 :             gr_make_font(mFont->GetAdjustedSize(), mGrFace);
     177               0 :         if (!mGrFont) {
     178               0 :             gr_face_destroy(mGrFace);
     179               0 :             mGrFace = nsnull;
     180               0 :             return false;
     181                 :         }
     182                 :     }
     183                 : 
     184               0 :     gfxFontEntry *entry = mFont->GetFontEntry();
     185               0 :     const gfxFontStyle *style = mFont->GetStyle();
     186               0 :     PRUint32 grLang = 0;
     187               0 :     if (style->languageOverride) {
     188               0 :         grLang = MakeGraphiteLangTag(style->languageOverride);
     189               0 :     } else if (entry->mLanguageOverride) {
     190               0 :         grLang = MakeGraphiteLangTag(entry->mLanguageOverride);
     191                 :     } else {
     192               0 :         nsCAutoString langString;
     193               0 :         style->language->ToUTF8String(langString);
     194               0 :         grLang = GetGraphiteTagForLang(langString);
     195                 :     }
     196               0 :     gr_feature_val *grFeatures = gr_face_featureval_for_lang(mGrFace, grLang);
     197                 : 
     198               0 :     if (aShapedWord->DisableLigatures()) {
     199                 :         const gr_feature_ref* fref =
     200               0 :             gr_face_find_fref(mGrFace, TRUETYPE_TAG('l','i','g','a'));
     201               0 :         if (fref) {
     202               0 :             gr_fref_set_feature_value(fref, 0, grFeatures);
     203                 :         }
     204                 :     }
     205                 : 
     206               0 :     const nsTArray<gfxFontFeature> *features = &style->featureSettings;
     207               0 :     if (features->IsEmpty()) {
     208               0 :         features = &entry->mFeatureSettings;
     209                 :     }
     210               0 :     for (PRUint32 i = 0; i < features->Length(); ++i) {
     211                 :         const gr_feature_ref* fref =
     212               0 :             gr_face_find_fref(mGrFace, (*features)[i].mTag);
     213               0 :         if (fref) {
     214               0 :             gr_fref_set_feature_value(fref, (*features)[i].mValue, grFeatures);
     215                 :         }
     216                 :     }
     217                 : 
     218                 :     gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures,
     219                 :                                   gr_utf16, aText, aShapedWord->Length(),
     220               0 :                                   aShapedWord->IsRightToLeft());
     221               0 :     if (features) {
     222               0 :         gr_featureval_destroy(grFeatures);
     223                 :     }
     224               0 :     if (!seg) {
     225               0 :         return false;
     226                 :     }
     227                 : 
     228               0 :     nsresult rv = SetGlyphsFromSegment(aShapedWord, seg);
     229                 : 
     230               0 :     gr_seg_destroy(seg);
     231                 : 
     232               0 :     return NS_SUCCEEDED(rv);
     233                 : }
     234                 : 
     235                 : #define SMALL_GLYPH_RUN 256 // avoid heap allocation of per-glyph data arrays
     236                 :                             // for short (typical) runs up to this length
     237                 : 
     238               0 : struct Cluster {
     239                 :     PRUint32 baseChar;
     240                 :     PRUint32 baseGlyph;
     241                 :     PRUint32 nChars;
     242                 :     PRUint32 nGlyphs;
     243               0 :     Cluster() : baseChar(0), baseGlyph(0), nChars(0), nGlyphs(0) { }
     244                 : };
     245                 : 
     246                 : nsresult
     247               0 : gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
     248                 :                                         gr_segment *aSegment)
     249                 : {
     250               0 :     PRInt32 dev2appUnits = aShapedWord->AppUnitsPerDevUnit();
     251               0 :     bool rtl = aShapedWord->IsRightToLeft();
     252                 : 
     253               0 :     PRUint32 glyphCount = gr_seg_n_slots(aSegment);
     254                 : 
     255                 :     // identify clusters; graphite may have reordered/expanded/ligated glyphs.
     256               0 :     nsAutoTArray<Cluster,SMALL_GLYPH_RUN> clusters;
     257               0 :     nsAutoTArray<PRUint16,SMALL_GLYPH_RUN> gids;
     258               0 :     nsAutoTArray<float,SMALL_GLYPH_RUN> xLocs;
     259               0 :     nsAutoTArray<float,SMALL_GLYPH_RUN> yLocs;
     260                 : 
     261               0 :     if (!clusters.SetLength(aShapedWord->Length()) ||
     262               0 :         !gids.SetLength(glyphCount) ||
     263               0 :         !xLocs.SetLength(glyphCount) ||
     264               0 :         !yLocs.SetLength(glyphCount))
     265                 :     {
     266               0 :         return NS_ERROR_OUT_OF_MEMORY;
     267                 :     }
     268                 : 
     269                 :     // walk through the glyph slots and check which original character
     270                 :     // each is associated with
     271               0 :     PRUint32 gIndex = 0; // glyph slot index
     272               0 :     PRUint32 cIndex = 0; // current cluster index
     273               0 :     for (const gr_slot *slot = gr_seg_first_slot(aSegment);
     274                 :          slot != nsnull;
     275                 :          slot = gr_slot_next_in_segment(slot), gIndex++)
     276                 :     {
     277               0 :         PRUint32 before = gr_slot_before(slot);
     278               0 :         PRUint32 after = gr_slot_after(slot);
     279               0 :         gids[gIndex] = gr_slot_gid(slot);
     280               0 :         xLocs[gIndex] = gr_slot_origin_X(slot);
     281               0 :         yLocs[gIndex] = gr_slot_origin_Y(slot);
     282                 : 
     283                 :         // if this glyph has a "before" character index that precedes the
     284                 :         // current cluster's char index, we need to merge preceding
     285                 :         // clusters until it gets included
     286               0 :         while (before < clusters[cIndex].baseChar && cIndex > 0) {
     287               0 :             clusters[cIndex-1].nChars += clusters[cIndex].nChars;
     288               0 :             clusters[cIndex-1].nGlyphs += clusters[cIndex].nGlyphs;
     289               0 :             --cIndex;
     290                 :         }
     291                 : 
     292                 :         // if there's a gap between the current cluster's base character and
     293                 :         // this glyph's, extend the cluster to include the intervening chars
     294               0 :         if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars &&
     295               0 :             before >= clusters[cIndex].baseChar + clusters[cIndex].nChars)
     296                 :         {
     297               0 :             NS_ASSERTION(cIndex < aShapedWord->Length() - 1, "cIndex at end of word");
     298               0 :             Cluster& c = clusters[cIndex + 1];
     299               0 :             c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars;
     300               0 :             c.nChars = before - c.baseChar;
     301               0 :             c.baseGlyph = gIndex;
     302               0 :             c.nGlyphs = 0;
     303               0 :             ++cIndex;
     304                 :         }
     305                 : 
     306                 :         // increment cluster's glyph count to include current slot
     307               0 :         NS_ASSERTION(cIndex < aShapedWord->Length(), "cIndex beyond word length");
     308               0 :         ++clusters[cIndex].nGlyphs;
     309                 : 
     310                 :         // extend cluster if necessary to reach the glyph's "after" index
     311               0 :         if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) {
     312               0 :             clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar;
     313                 :         }
     314                 :     }
     315                 : 
     316                 :     // now put glyphs into the textrun, one cluster at a time
     317               0 :     for (PRUint32 i = 0; i <= cIndex; ++i) {
     318               0 :         const Cluster& c = clusters[i];
     319                 : 
     320                 :         float adv; // total advance of the cluster
     321               0 :         if (rtl) {
     322               0 :             if (i == 0) {
     323               0 :                 adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
     324                 :             } else {
     325               0 :                 adv = xLocs[clusters[i-1].baseGlyph] - xLocs[c.baseGlyph];
     326                 :             }
     327                 :         } else {
     328               0 :             if (i == cIndex) {
     329               0 :                 adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
     330                 :             } else {
     331               0 :                 adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph];
     332                 :             }
     333                 :         }
     334                 : 
     335                 :         // Check for default-ignorable char that didn't get filtered, combined,
     336                 :         // etc by the shaping process, and skip it.
     337               0 :         PRUint32 offs = gr_cinfo_base(gr_seg_cinfo(aSegment, c.baseChar));
     338               0 :         NS_ASSERTION(offs >= c.baseChar && offs < aShapedWord->Length(),
     339                 :                      "unexpected offset");
     340               0 :         if (c.nGlyphs == 1 && c.nChars == 1 &&
     341               0 :             aShapedWord->FilterIfIgnorable(offs))
     342                 :         {
     343               0 :             continue;
     344                 :         }
     345                 : 
     346               0 :         PRUint32 appAdvance = adv * dev2appUnits;
     347               0 :         if (c.nGlyphs == 1 &&
     348               0 :             gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
     349               0 :             gfxShapedWord::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
     350               0 :             yLocs[c.baseGlyph] == 0)
     351                 :         {
     352               0 :             gfxShapedWord::CompressedGlyph g;
     353                 :             aShapedWord->SetSimpleGlyph(offs,
     354                 :                                         g.SetSimpleGlyph(appAdvance,
     355               0 :                                                          gids[c.baseGlyph]));
     356                 :         } else {
     357                 :             // not a one-to-one mapping with simple metrics: use DetailedGlyph
     358               0 :             nsAutoTArray<gfxShapedWord::DetailedGlyph,8> details;
     359                 :             float clusterLoc;
     360               0 :             for (PRUint32 j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
     361               0 :                 gfxShapedWord::DetailedGlyph* d = details.AppendElement();
     362               0 :                 d->mGlyphID = gids[j];
     363               0 :                 d->mYOffset = -yLocs[j] * dev2appUnits;
     364               0 :                 if (j == c.baseGlyph) {
     365               0 :                     d->mXOffset = 0;
     366               0 :                     d->mAdvance = appAdvance;
     367               0 :                     clusterLoc = xLocs[j];
     368                 :                 } else {
     369               0 :                     d->mXOffset = (xLocs[j] - clusterLoc - adv) * dev2appUnits;
     370               0 :                     d->mAdvance = 0;
     371                 :                 }
     372                 :             }
     373               0 :             gfxShapedWord::CompressedGlyph g;
     374               0 :             g.SetComplex(aShapedWord->IsClusterStart(offs),
     375               0 :                          true, details.Length());
     376               0 :             aShapedWord->SetGlyphs(offs, g, details.Elements());
     377                 :         }
     378                 : 
     379               0 :         for (PRUint32 j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
     380               0 :             offs = gr_cinfo_base(gr_seg_cinfo(aSegment, j));
     381               0 :             NS_ASSERTION(offs >= j && offs < aShapedWord->Length(),
     382                 :                          "unexpected offset");
     383               0 :             gfxShapedWord::CompressedGlyph g;
     384               0 :             g.SetComplex(aShapedWord->IsClusterStart(offs), false, 0);
     385               0 :             aShapedWord->SetGlyphs(offs, g, nsnull);
     386                 :         }
     387                 :     }
     388                 : 
     389               0 :     return NS_OK;
     390                 : }
     391                 : 
     392                 : // for language tag validation - include list of tags from the IANA registry
     393                 : #include "gfxLanguageTagList.cpp"
     394                 : 
     395            1464 : nsTHashtable<nsUint32HashKey> gfxGraphiteShaper::sLanguageTags;
     396                 : 
     397                 : /*static*/ PRUint32
     398               0 : gfxGraphiteShaper::GetGraphiteTagForLang(const nsCString& aLang)
     399                 : {
     400               0 :     int len = aLang.Length();
     401               0 :     if (len < 2) {
     402               0 :         return 0;
     403                 :     }
     404                 : 
     405                 :     // convert primary language subtag to a left-packed, NUL-padded integer
     406                 :     // for the Graphite API
     407               0 :     PRUint32 grLang = 0;
     408               0 :     for (int i = 0; i < 4; ++i) {
     409               0 :         grLang <<= 8;
     410               0 :         if (i < len) {
     411               0 :             PRUint8 ch = aLang[i];
     412               0 :             if (ch == '-') {
     413                 :                 // found end of primary language subtag, truncate here
     414               0 :                 len = i;
     415               0 :                 continue;
     416                 :             }
     417               0 :             if (ch < 'a' || ch > 'z') {
     418                 :                 // invalid character in tag, so ignore it completely
     419               0 :                 return 0;
     420                 :             }
     421               0 :             grLang += ch;
     422                 :         }
     423                 :     }
     424                 : 
     425                 :     // valid tags must have length = 2 or 3
     426               0 :     if (len < 2 || len > 3) {
     427               0 :         return 0;
     428                 :     }
     429                 : 
     430               0 :     if (!sLanguageTags.IsInitialized()) {
     431                 :         // store the registered IANA tags in a hash for convenient validation
     432               0 :         sLanguageTags.Init(ArrayLength(sLanguageTagList));
     433               0 :         for (const PRUint32 *tag = sLanguageTagList; *tag != 0; ++tag) {
     434               0 :             sLanguageTags.PutEntry(*tag);
     435                 :         }
     436                 :     }
     437                 : 
     438                 :     // only accept tags known in the IANA registry
     439               0 :     if (sLanguageTags.GetEntry(grLang)) {
     440               0 :         return grLang;
     441                 :     }
     442                 : 
     443               0 :     return 0;
     444                 : }
     445                 : 
     446                 : /*static*/ void
     447               4 : gfxGraphiteShaper::Shutdown()
     448                 : {
     449                 : #ifdef NS_FREE_PERMANENT_DATA
     450               4 :     if (sLanguageTags.IsInitialized()) {
     451               0 :         sLanguageTags.Clear();
     452                 :     }
     453                 : #endif
     454            4396 : }

Generated by: LCOV version 1.7