gfx/thebes/gfxGraphiteShaper.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial