1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxGraphiteShaper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,405 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "gfxGraphiteShaper.h" 1.10 +#include "nsString.h" 1.11 +#include "gfxContext.h" 1.12 + 1.13 +#include "graphite2/Font.h" 1.14 +#include "graphite2/Segment.h" 1.15 + 1.16 +#include "harfbuzz/hb.h" 1.17 + 1.18 +#define FloatToFixed(f) (65536 * (f)) 1.19 +#define FixedToFloat(f) ((f) * (1.0 / 65536.0)) 1.20 +// Right shifts of negative (signed) integers are undefined, as are overflows 1.21 +// when converting unsigned to negative signed integers. 1.22 +// (If speed were an issue we could make some 2's complement assumptions.) 1.23 +#define FixedToIntRound(f) ((f) > 0 ? ((32768 + (f)) >> 16) \ 1.24 + : -((32767 - (f)) >> 16)) 1.25 + 1.26 +using namespace mozilla; // for AutoSwap_* types 1.27 + 1.28 +/* 1.29 + * Creation and destruction; on deletion, release any font tables we're holding 1.30 + */ 1.31 + 1.32 +gfxGraphiteShaper::gfxGraphiteShaper(gfxFont *aFont) 1.33 + : gfxFontShaper(aFont), 1.34 + mGrFace(mFont->GetFontEntry()->GetGrFace()), 1.35 + mGrFont(nullptr) 1.36 +{ 1.37 + mCallbackData.mFont = aFont; 1.38 + mCallbackData.mShaper = this; 1.39 +} 1.40 + 1.41 +gfxGraphiteShaper::~gfxGraphiteShaper() 1.42 +{ 1.43 + if (mGrFont) { 1.44 + gr_font_destroy(mGrFont); 1.45 + } 1.46 + mFont->GetFontEntry()->ReleaseGrFace(mGrFace); 1.47 +} 1.48 + 1.49 +/*static*/ float 1.50 +gfxGraphiteShaper::GrGetAdvance(const void* appFontHandle, uint16_t glyphid) 1.51 +{ 1.52 + const CallbackData *cb = 1.53 + static_cast<const CallbackData*>(appFontHandle); 1.54 + return FixedToFloat(cb->mFont->GetGlyphWidth(cb->mContext, glyphid)); 1.55 +} 1.56 + 1.57 +static inline uint32_t 1.58 +MakeGraphiteLangTag(uint32_t aTag) 1.59 +{ 1.60 + uint32_t grLangTag = aTag; 1.61 + // replace trailing space-padding with NULs for graphite 1.62 + uint32_t mask = 0x000000FF; 1.63 + while ((grLangTag & mask) == ' ') { 1.64 + grLangTag &= ~mask; 1.65 + mask <<= 8; 1.66 + } 1.67 + return grLangTag; 1.68 +} 1.69 + 1.70 +struct GrFontFeatures { 1.71 + gr_face *mFace; 1.72 + gr_feature_val *mFeatures; 1.73 +}; 1.74 + 1.75 +static PLDHashOperator 1.76 +AddFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg) 1.77 +{ 1.78 + GrFontFeatures *f = static_cast<GrFontFeatures*>(aUserArg); 1.79 + 1.80 + const gr_feature_ref* fref = gr_face_find_fref(f->mFace, aTag); 1.81 + if (fref) { 1.82 + gr_fref_set_feature_value(fref, aValue, f->mFeatures); 1.83 + } 1.84 + return PL_DHASH_NEXT; 1.85 +} 1.86 + 1.87 +bool 1.88 +gfxGraphiteShaper::ShapeText(gfxContext *aContext, 1.89 + const char16_t *aText, 1.90 + uint32_t aOffset, 1.91 + uint32_t aLength, 1.92 + int32_t aScript, 1.93 + gfxShapedText *aShapedText) 1.94 +{ 1.95 + // some font back-ends require this in order to get proper hinted metrics 1.96 + if (!mFont->SetupCairoFont(aContext)) { 1.97 + return false; 1.98 + } 1.99 + 1.100 + mCallbackData.mContext = aContext; 1.101 + 1.102 + if (!mGrFont) { 1.103 + if (!mGrFace) { 1.104 + return false; 1.105 + } 1.106 + 1.107 + if (mFont->ProvidesGlyphWidths()) { 1.108 + gr_font_ops ops = { 1.109 + sizeof(gr_font_ops), 1.110 + &GrGetAdvance, 1.111 + nullptr // vertical text not yet implemented 1.112 + }; 1.113 + mGrFont = gr_make_font_with_ops(mFont->GetAdjustedSize(), 1.114 + &mCallbackData, &ops, mGrFace); 1.115 + } else { 1.116 + mGrFont = gr_make_font(mFont->GetAdjustedSize(), mGrFace); 1.117 + } 1.118 + 1.119 + if (!mGrFont) { 1.120 + return false; 1.121 + } 1.122 + } 1.123 + 1.124 + gfxFontEntry *entry = mFont->GetFontEntry(); 1.125 + const gfxFontStyle *style = mFont->GetStyle(); 1.126 + uint32_t grLang = 0; 1.127 + if (style->languageOverride) { 1.128 + grLang = MakeGraphiteLangTag(style->languageOverride); 1.129 + } else if (entry->mLanguageOverride) { 1.130 + grLang = MakeGraphiteLangTag(entry->mLanguageOverride); 1.131 + } else { 1.132 + nsAutoCString langString; 1.133 + style->language->ToUTF8String(langString); 1.134 + grLang = GetGraphiteTagForLang(langString); 1.135 + } 1.136 + gr_feature_val *grFeatures = gr_face_featureval_for_lang(mGrFace, grLang); 1.137 + 1.138 + nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures; 1.139 + 1.140 + // if style contains font-specific features 1.141 + if (MergeFontFeatures(style, 1.142 + mFont->GetFontEntry()->mFeatureSettings, 1.143 + aShapedText->DisableLigatures(), 1.144 + mFont->GetFontEntry()->FamilyName(), 1.145 + mergedFeatures)) 1.146 + { 1.147 + // enumerate result and insert into Graphite feature list 1.148 + GrFontFeatures f = {mGrFace, grFeatures}; 1.149 + mergedFeatures.Enumerate(AddFeature, &f); 1.150 + } 1.151 + 1.152 + size_t numChars = gr_count_unicode_characters(gr_utf16, 1.153 + aText, aText + aLength, 1.154 + nullptr); 1.155 + gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures, 1.156 + gr_utf16, aText, numChars, 1.157 + aShapedText->IsRightToLeft()); 1.158 + 1.159 + gr_featureval_destroy(grFeatures); 1.160 + 1.161 + if (!seg) { 1.162 + return false; 1.163 + } 1.164 + 1.165 + nsresult rv = SetGlyphsFromSegment(aContext, aShapedText, aOffset, aLength, 1.166 + aText, seg); 1.167 + 1.168 + gr_seg_destroy(seg); 1.169 + 1.170 + return NS_SUCCEEDED(rv); 1.171 +} 1.172 + 1.173 +#define SMALL_GLYPH_RUN 256 // avoid heap allocation of per-glyph data arrays 1.174 + // for short (typical) runs up to this length 1.175 + 1.176 +struct Cluster { 1.177 + uint32_t baseChar; // in UTF16 code units, not Unicode character indices 1.178 + uint32_t baseGlyph; 1.179 + uint32_t nChars; // UTF16 code units 1.180 + uint32_t nGlyphs; 1.181 + Cluster() : baseChar(0), baseGlyph(0), nChars(0), nGlyphs(0) { } 1.182 +}; 1.183 + 1.184 +nsresult 1.185 +gfxGraphiteShaper::SetGlyphsFromSegment(gfxContext *aContext, 1.186 + gfxShapedText *aShapedText, 1.187 + uint32_t aOffset, 1.188 + uint32_t aLength, 1.189 + const char16_t *aText, 1.190 + gr_segment *aSegment) 1.191 +{ 1.192 + int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit(); 1.193 + bool rtl = aShapedText->IsRightToLeft(); 1.194 + 1.195 + uint32_t glyphCount = gr_seg_n_slots(aSegment); 1.196 + 1.197 + // identify clusters; graphite may have reordered/expanded/ligated glyphs. 1.198 + AutoFallibleTArray<Cluster,SMALL_GLYPH_RUN> clusters; 1.199 + AutoFallibleTArray<uint16_t,SMALL_GLYPH_RUN> gids; 1.200 + AutoFallibleTArray<float,SMALL_GLYPH_RUN> xLocs; 1.201 + AutoFallibleTArray<float,SMALL_GLYPH_RUN> yLocs; 1.202 + 1.203 + if (!clusters.SetLength(aLength) || 1.204 + !gids.SetLength(glyphCount) || 1.205 + !xLocs.SetLength(glyphCount) || 1.206 + !yLocs.SetLength(glyphCount)) 1.207 + { 1.208 + return NS_ERROR_OUT_OF_MEMORY; 1.209 + } 1.210 + 1.211 + // walk through the glyph slots and check which original character 1.212 + // each is associated with 1.213 + uint32_t gIndex = 0; // glyph slot index 1.214 + uint32_t cIndex = 0; // current cluster index 1.215 + for (const gr_slot *slot = gr_seg_first_slot(aSegment); 1.216 + slot != nullptr; 1.217 + slot = gr_slot_next_in_segment(slot), gIndex++) 1.218 + { 1.219 + uint32_t before = 1.220 + gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_before(slot))); 1.221 + uint32_t after = 1.222 + gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_after(slot))); 1.223 + gids[gIndex] = gr_slot_gid(slot); 1.224 + xLocs[gIndex] = gr_slot_origin_X(slot); 1.225 + yLocs[gIndex] = gr_slot_origin_Y(slot); 1.226 + 1.227 + // if this glyph has a "before" character index that precedes the 1.228 + // current cluster's char index, we need to merge preceding 1.229 + // clusters until it gets included 1.230 + while (before < clusters[cIndex].baseChar && cIndex > 0) { 1.231 + clusters[cIndex-1].nChars += clusters[cIndex].nChars; 1.232 + clusters[cIndex-1].nGlyphs += clusters[cIndex].nGlyphs; 1.233 + --cIndex; 1.234 + } 1.235 + 1.236 + // if there's a gap between the current cluster's base character and 1.237 + // this glyph's, extend the cluster to include the intervening chars 1.238 + if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars && 1.239 + before >= clusters[cIndex].baseChar + clusters[cIndex].nChars) 1.240 + { 1.241 + NS_ASSERTION(cIndex < aLength - 1, "cIndex at end of word"); 1.242 + Cluster& c = clusters[cIndex + 1]; 1.243 + c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars; 1.244 + c.nChars = before - c.baseChar; 1.245 + c.baseGlyph = gIndex; 1.246 + c.nGlyphs = 0; 1.247 + ++cIndex; 1.248 + } 1.249 + 1.250 + // increment cluster's glyph count to include current slot 1.251 + NS_ASSERTION(cIndex < aLength, "cIndex beyond word length"); 1.252 + ++clusters[cIndex].nGlyphs; 1.253 + 1.254 + // extend cluster if necessary to reach the glyph's "after" index 1.255 + if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) { 1.256 + clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar; 1.257 + } 1.258 + } 1.259 + 1.260 + bool roundX; 1.261 + bool roundY; 1.262 + aContext->GetRoundOffsetsToPixels(&roundX, &roundY); 1.263 + 1.264 + gfxShapedText::CompressedGlyph *charGlyphs = 1.265 + aShapedText->GetCharacterGlyphs() + aOffset; 1.266 + 1.267 + // now put glyphs into the textrun, one cluster at a time 1.268 + for (uint32_t i = 0; i <= cIndex; ++i) { 1.269 + const Cluster& c = clusters[i]; 1.270 + 1.271 + float adv; // total advance of the cluster 1.272 + if (rtl) { 1.273 + if (i == 0) { 1.274 + adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph]; 1.275 + } else { 1.276 + adv = xLocs[clusters[i-1].baseGlyph] - xLocs[c.baseGlyph]; 1.277 + } 1.278 + } else { 1.279 + if (i == cIndex) { 1.280 + adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph]; 1.281 + } else { 1.282 + adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph]; 1.283 + } 1.284 + } 1.285 + 1.286 + // Check for default-ignorable char that didn't get filtered, combined, 1.287 + // etc by the shaping process, and skip it. 1.288 + uint32_t offs = c.baseChar; 1.289 + NS_ASSERTION(offs < aLength, "unexpected offset"); 1.290 + if (c.nGlyphs == 1 && c.nChars == 1 && 1.291 + aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) { 1.292 + continue; 1.293 + } 1.294 + 1.295 + uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits : 1.296 + NSToIntRound(adv * dev2appUnits); 1.297 + if (c.nGlyphs == 1 && 1.298 + gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) && 1.299 + gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) && 1.300 + charGlyphs[offs].IsClusterStart() && 1.301 + yLocs[c.baseGlyph] == 0) 1.302 + { 1.303 + charGlyphs[offs].SetSimpleGlyph(appAdvance, gids[c.baseGlyph]); 1.304 + } else { 1.305 + // not a one-to-one mapping with simple metrics: use DetailedGlyph 1.306 + nsAutoTArray<gfxShapedText::DetailedGlyph,8> details; 1.307 + float clusterLoc; 1.308 + for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) { 1.309 + gfxShapedText::DetailedGlyph* d = details.AppendElement(); 1.310 + d->mGlyphID = gids[j]; 1.311 + d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits : 1.312 + -yLocs[j] * dev2appUnits; 1.313 + if (j == c.baseGlyph) { 1.314 + d->mXOffset = 0; 1.315 + d->mAdvance = appAdvance; 1.316 + clusterLoc = xLocs[j]; 1.317 + } else { 1.318 + float dx = rtl ? (xLocs[j] - clusterLoc) : 1.319 + (xLocs[j] - clusterLoc - adv); 1.320 + d->mXOffset = roundX ? NSToIntRound(dx) * dev2appUnits : 1.321 + dx * dev2appUnits; 1.322 + d->mAdvance = 0; 1.323 + } 1.324 + } 1.325 + gfxShapedText::CompressedGlyph g; 1.326 + g.SetComplex(charGlyphs[offs].IsClusterStart(), 1.327 + true, details.Length()); 1.328 + aShapedText->SetGlyphs(aOffset + offs, g, details.Elements()); 1.329 + } 1.330 + 1.331 + for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) { 1.332 + NS_ASSERTION(j < aLength, "unexpected offset"); 1.333 + gfxShapedText::CompressedGlyph &g = charGlyphs[j]; 1.334 + NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph"); 1.335 + g.SetComplex(g.IsClusterStart(), false, 0); 1.336 + } 1.337 + } 1.338 + 1.339 + return NS_OK; 1.340 +} 1.341 + 1.342 +#undef SMALL_GLYPH_RUN 1.343 + 1.344 +// for language tag validation - include list of tags from the IANA registry 1.345 +#include "gfxLanguageTagList.cpp" 1.346 + 1.347 +nsTHashtable<nsUint32HashKey> *gfxGraphiteShaper::sLanguageTags; 1.348 + 1.349 +/*static*/ uint32_t 1.350 +gfxGraphiteShaper::GetGraphiteTagForLang(const nsCString& aLang) 1.351 +{ 1.352 + int len = aLang.Length(); 1.353 + if (len < 2) { 1.354 + return 0; 1.355 + } 1.356 + 1.357 + // convert primary language subtag to a left-packed, NUL-padded integer 1.358 + // for the Graphite API 1.359 + uint32_t grLang = 0; 1.360 + for (int i = 0; i < 4; ++i) { 1.361 + grLang <<= 8; 1.362 + if (i < len) { 1.363 + uint8_t ch = aLang[i]; 1.364 + if (ch == '-') { 1.365 + // found end of primary language subtag, truncate here 1.366 + len = i; 1.367 + continue; 1.368 + } 1.369 + if (ch < 'a' || ch > 'z') { 1.370 + // invalid character in tag, so ignore it completely 1.371 + return 0; 1.372 + } 1.373 + grLang += ch; 1.374 + } 1.375 + } 1.376 + 1.377 + // valid tags must have length = 2 or 3 1.378 + if (len < 2 || len > 3) { 1.379 + return 0; 1.380 + } 1.381 + 1.382 + if (!sLanguageTags) { 1.383 + // store the registered IANA tags in a hash for convenient validation 1.384 + sLanguageTags = new nsTHashtable<nsUint32HashKey>(ArrayLength(sLanguageTagList)); 1.385 + for (const uint32_t *tag = sLanguageTagList; *tag != 0; ++tag) { 1.386 + sLanguageTags->PutEntry(*tag); 1.387 + } 1.388 + } 1.389 + 1.390 + // only accept tags known in the IANA registry 1.391 + if (sLanguageTags->GetEntry(grLang)) { 1.392 + return grLang; 1.393 + } 1.394 + 1.395 + return 0; 1.396 +} 1.397 + 1.398 +/*static*/ void 1.399 +gfxGraphiteShaper::Shutdown() 1.400 +{ 1.401 +#ifdef NS_FREE_PERMANENT_DATA 1.402 + if (sLanguageTags) { 1.403 + sLanguageTags->Clear(); 1.404 + delete sLanguageTags; 1.405 + sLanguageTags = nullptr; 1.406 + } 1.407 +#endif 1.408 +}