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