Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 "gfxFont.h"
7
8
#include "mozilla/BinarySearch.h"
9
#include "mozilla/DebugOnly.h"
10
#include "mozilla/FontPropertyTypes.h"
11
#include "mozilla/gfx/2D.h"
12
#include "mozilla/IntegerRange.h"
13
#include "mozilla/MathAlgorithms.h"
14
#include "mozilla/StaticPrefs_gfx.h"
15
#include "mozilla/SVGContextPaint.h"
16
17
#include "mozilla/Logging.h"
18
19
#include "nsITimer.h"
20
21
#include "gfxGlyphExtents.h"
22
#include "gfxPlatform.h"
23
#include "gfxTextRun.h"
24
#include "nsGkAtoms.h"
25
26
#include "gfxTypes.h"
27
#include "gfxContext.h"
28
#include "gfxFontMissingGlyphs.h"
29
#include "gfxGraphiteShaper.h"
30
#include "gfxHarfBuzzShaper.h"
31
#include "gfxUserFontSet.h"
32
#include "nsSpecialCasingData.h"
33
#include "nsTextRunTransformations.h"
34
#include "nsUGenCategory.h"
35
#include "nsUnicodeProperties.h"
36
#include "nsStyleConsts.h"
37
#include "mozilla/AppUnits.h"
38
#include "mozilla/Likely.h"
39
#include "mozilla/MemoryReporting.h"
40
#include "mozilla/Preferences.h"
41
#include "mozilla/Services.h"
42
#include "mozilla/Telemetry.h"
43
#include "gfxMathTable.h"
44
#include "gfxSVGGlyphs.h"
45
#include "gfx2DGlue.h"
46
#include "TextDrawTarget.h"
47
48
#include "ThebesRLBox.h"
49
50
#include "GreekCasing.h"
51
52
#include "cairo.h"
53
#ifdef XP_WIN
54
# include "cairo-win32.h"
55
# include "gfxWindowsPlatform.h"
56
#endif
57
58
#include "harfbuzz/hb.h"
59
#include "harfbuzz/hb-ot.h"
60
61
#include <algorithm>
62
#include <limits>
63
#include <cmath>
64
65
using namespace mozilla;
66
using namespace mozilla::gfx;
67
using namespace mozilla::unicode;
68
using mozilla::services::GetObserverService;
69
70
gfxFontCache* gfxFontCache::gGlobalCache = nullptr;
71
72
#ifdef DEBUG_roc
73
# define DEBUG_TEXT_RUN_STORAGE_METRICS
74
#endif
75
76
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
77
uint32_t gTextRunStorageHighWaterMark = 0;
78
uint32_t gTextRunStorage = 0;
79
uint32_t gFontCount = 0;
80
uint32_t gGlyphExtentsCount = 0;
81
uint32_t gGlyphExtentsWidthsTotalSize = 0;
82
uint32_t gGlyphExtentsSetupEagerSimple = 0;
83
uint32_t gGlyphExtentsSetupEagerTight = 0;
84
uint32_t gGlyphExtentsSetupLazyTight = 0;
85
uint32_t gGlyphExtentsSetupFallBackToTight = 0;
86
#endif
87
88
#define LOG_FONTINIT(args) \
89
MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
90
#define LOG_FONTINIT_ENABLED() \
91
MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
92
93
/*
94
* gfxFontCache - global cache of gfxFont instances.
95
* Expires unused fonts after a short interval;
96
* notifies fonts to age their cached shaped-word records;
97
* observes memory-pressure notification and tells fonts to clear their
98
* shaped-word caches to free up memory.
99
*/
100
101
MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
102
103
NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
104
105
/*virtual*/
106
gfxTextRunFactory::~gfxTextRunFactory() {
107
// Should not be dropped by stylo
108
MOZ_ASSERT(NS_IsMainThread());
109
}
110
111
NS_IMETHODIMP
112
gfxFontCache::MemoryReporter::CollectReports(
113
nsIHandleReportCallback* aHandleReport, nsISupports* aData,
114
bool aAnonymize) {
115
FontCacheSizes sizes;
116
117
gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
118
&sizes);
119
120
MOZ_COLLECT_REPORT("explicit/gfx/font-cache", KIND_HEAP, UNITS_BYTES,
121
sizes.mFontInstances,
122
"Memory used for active font instances.");
123
124
MOZ_COLLECT_REPORT("explicit/gfx/font-shaped-words", KIND_HEAP, UNITS_BYTES,
125
sizes.mShapedWords,
126
"Memory used to cache shaped glyph data.");
127
128
return NS_OK;
129
}
130
131
NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver)
132
133
NS_IMETHODIMP
134
gfxFontCache::Observer::Observe(nsISupports* aSubject, const char* aTopic,
135
const char16_t* someData) {
136
if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
137
gfxFontCache* fontCache = gfxFontCache::GetCache();
138
if (fontCache) {
139
fontCache->FlushShapedWordCaches();
140
}
141
} else {
142
MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
143
}
144
return NS_OK;
145
}
146
147
nsresult gfxFontCache::Init() {
148
NS_ASSERTION(!gGlobalCache, "Where did this come from?");
149
gGlobalCache =
150
new gfxFontCache(SystemGroup::EventTargetFor(TaskCategory::Other));
151
if (!gGlobalCache) {
152
return NS_ERROR_OUT_OF_MEMORY;
153
}
154
RegisterStrongMemoryReporter(new MemoryReporter());
155
return NS_OK;
156
}
157
158
void gfxFontCache::Shutdown() {
159
delete gGlobalCache;
160
gGlobalCache = nullptr;
161
162
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
163
printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
164
printf("Total number of fonts=%d\n", gFontCount);
165
printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
166
int(gGlyphExtentsCount * sizeof(gfxGlyphExtents)));
167
printf("Total glyph extents width-storage size allocated=%d\n",
168
gGlyphExtentsWidthsTotalSize);
169
printf("Number of simple glyph extents eagerly requested=%d\n",
170
gGlyphExtentsSetupEagerSimple);
171
printf("Number of tight glyph extents eagerly requested=%d\n",
172
gGlyphExtentsSetupEagerTight);
173
printf("Number of tight glyph extents lazily requested=%d\n",
174
gGlyphExtentsSetupLazyTight);
175
printf("Number of simple glyph extent setups that fell back to tight=%d\n",
176
gGlyphExtentsSetupFallBackToTight);
177
#endif
178
}
179
180
gfxFontCache::gfxFontCache(nsIEventTarget* aEventTarget)
181
: gfxFontCacheExpirationTracker(aEventTarget) {
182
nsCOMPtr<nsIObserverService> obs = GetObserverService();
183
if (obs) {
184
obs->AddObserver(new Observer, "memory-pressure", false);
185
}
186
187
#ifdef EARLY_BETA_OR_EARLIER
188
// Currently disabled for release builds, due to unexplained crashes
189
// during expiration; see bug 717175 & 894798.
190
// Bug 1548661: enabled for early beta, to see what crash-stats shows.
191
nsIEventTarget* target = nullptr;
192
if (XRE_IsContentProcess() && NS_IsMainThread()) {
193
target = aEventTarget;
194
}
195
NS_NewTimerWithFuncCallback(getter_AddRefs(mWordCacheExpirationTimer),
196
WordCacheExpirationTimerCallback, this,
197
SHAPED_WORD_TIMEOUT_SECONDS * 1000,
198
nsITimer::TYPE_REPEATING_SLACK,
199
"gfxFontCache::gfxFontCache", target);
200
#endif
201
}
202
203
gfxFontCache::~gfxFontCache() {
204
// Ensure the user font cache releases its references to font entries,
205
// so they aren't kept alive after the font instances and font-list
206
// have been shut down.
207
gfxUserFontSet::UserFontCache::Shutdown();
208
209
if (mWordCacheExpirationTimer) {
210
mWordCacheExpirationTimer->Cancel();
211
mWordCacheExpirationTimer = nullptr;
212
}
213
214
// Expire everything that has a zero refcount, so we don't leak them.
215
AgeAllGenerations();
216
// All fonts should be gone.
217
NS_WARNING_ASSERTION(mFonts.Count() == 0,
218
"Fonts still alive while shutting down gfxFontCache");
219
// Note that we have to delete everything through the expiration
220
// tracker, since there might be fonts not in the hashtable but in
221
// the tracker.
222
}
223
224
bool gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const {
225
const gfxCharacterMap* fontUnicodeRangeMap = mFont->GetUnicodeRangeMap();
226
return aKey->mFontEntry == mFont->GetFontEntry() &&
227
aKey->mStyle->Equals(*mFont->GetStyle()) &&
228
((!aKey->mUnicodeRangeMap && !fontUnicodeRangeMap) ||
229
(aKey->mUnicodeRangeMap && fontUnicodeRangeMap &&
230
aKey->mUnicodeRangeMap->Equals(fontUnicodeRangeMap)));
231
}
232
233
gfxFont* gfxFontCache::Lookup(const gfxFontEntry* aFontEntry,
234
const gfxFontStyle* aStyle,
235
const gfxCharacterMap* aUnicodeRangeMap) {
236
Key key(aFontEntry, aStyle, aUnicodeRangeMap);
237
HashEntry* entry = mFonts.GetEntry(key);
238
239
Telemetry::Accumulate(Telemetry::FONT_CACHE_HIT, entry != nullptr);
240
if (!entry) return nullptr;
241
242
return entry->mFont;
243
}
244
245
void gfxFontCache::AddNew(gfxFont* aFont) {
246
Key key(aFont->GetFontEntry(), aFont->GetStyle(),
247
aFont->GetUnicodeRangeMap());
248
HashEntry* entry = mFonts.PutEntry(key);
249
if (!entry) return;
250
gfxFont* oldFont = entry->mFont;
251
entry->mFont = aFont;
252
// Assert that we can find the entry we just put in (this fails if the key
253
// has a NaN float value in it, e.g. 'sizeAdjust').
254
MOZ_ASSERT(entry == mFonts.GetEntry(key));
255
// If someone's asked us to replace an existing font entry, then that's a
256
// bit weird, but let it happen, and expire the old font if it's not used.
257
if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
258
// if oldFont == aFont, recount should be > 0,
259
// so we shouldn't be here.
260
NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
261
NotifyExpired(oldFont);
262
}
263
}
264
265
void gfxFontCache::NotifyReleased(gfxFont* aFont) {
266
nsresult rv = AddObject(aFont);
267
if (NS_FAILED(rv)) {
268
// We couldn't track it for some reason. Kill it now.
269
DestroyFont(aFont);
270
}
271
// Note that we might have fonts that aren't in the hashtable, perhaps because
272
// of OOM adding to the hashtable or because someone did an AddNew where
273
// we already had a font. These fonts are added to the expiration tracker
274
// anyway, even though Lookup can't resurrect them. Eventually they will
275
// expire and be deleted.
276
}
277
278
void gfxFontCache::NotifyExpired(gfxFont* aFont) {
279
aFont->ClearCachedWords();
280
RemoveObject(aFont);
281
DestroyFont(aFont);
282
}
283
284
void gfxFontCache::DestroyFont(gfxFont* aFont) {
285
Key key(aFont->GetFontEntry(), aFont->GetStyle(),
286
aFont->GetUnicodeRangeMap());
287
HashEntry* entry = mFonts.GetEntry(key);
288
if (entry && entry->mFont == aFont) {
289
mFonts.RemoveEntry(entry);
290
}
291
NS_ASSERTION(aFont->GetRefCount() == 0,
292
"Destroying with non-zero ref count!");
293
delete aFont;
294
}
295
296
/*static*/
297
void gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer,
298
void* aCache) {
299
gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
300
for (auto it = cache->mFonts.Iter(); !it.Done(); it.Next()) {
301
it.Get()->mFont->AgeCachedWords();
302
}
303
}
304
305
void gfxFontCache::FlushShapedWordCaches() {
306
for (auto it = mFonts.Iter(); !it.Done(); it.Next()) {
307
it.Get()->mFont->ClearCachedWords();
308
}
309
}
310
311
void gfxFontCache::NotifyGlyphsChanged() {
312
for (auto it = mFonts.Iter(); !it.Done(); it.Next()) {
313
it.Get()->mFont->NotifyGlyphsChanged();
314
}
315
}
316
317
void gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
318
FontCacheSizes* aSizes) const {
319
// TODO: add the overhead of the expiration tracker (generation arrays)
320
321
aSizes->mFontInstances += mFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
322
for (auto iter = mFonts.ConstIter(); !iter.Done(); iter.Next()) {
323
iter.Get()->mFont->AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
324
}
325
}
326
327
void gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
328
FontCacheSizes* aSizes) const {
329
aSizes->mFontInstances += aMallocSizeOf(this);
330
AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
331
}
332
333
#define MAX_SSXX_VALUE 99
334
#define MAX_CVXX_VALUE 99
335
336
static void LookupAlternateValues(const gfxFontFeatureValueSet& aFeatureLookup,
337
const nsACString& aFamily,
338
const StyleVariantAlternates& aAlternates,
339
nsTArray<gfxFontFeature>& aFontFeatures) {
340
using Tag = StyleVariantAlternates::Tag;
341
342
// historical-forms gets handled in nsFont::AddFontFeaturesToStyle.
343
if (aAlternates.IsHistoricalForms()) {
344
return;
345
}
346
347
gfxFontFeature feature;
348
if (aAlternates.IsCharacterVariant()) {
349
for (auto& ident : aAlternates.AsCharacterVariant().AsSpan()) {
350
Span<const uint32_t> values = aFeatureLookup.GetFontFeatureValuesFor(
351
aFamily, NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT,
352
ident.AsAtom());
353
// nothing defined, skip
354
if (values.IsEmpty()) {
355
continue;
356
}
357
NS_ASSERTION(values.Length() <= 2,
358
"too many values allowed for character-variant");
359
// character-variant(12 3) ==> 'cv12' = 3
360
uint32_t nn = values[0];
361
// ignore values greater than 99
362
if (nn == 0 || nn > MAX_CVXX_VALUE) {
363
continue;
364
}
365
feature.mValue = values.Length() > 1 ? values[1] : 1;
366
feature.mTag = HB_TAG('c', 'v', ('0' + nn / 10), ('0' + nn % 10));
367
aFontFeatures.AppendElement(feature);
368
}
369
return;
370
}
371
372
if (aAlternates.IsStyleset()) {
373
for (auto& ident : aAlternates.AsStyleset().AsSpan()) {
374
Span<const uint32_t> values = aFeatureLookup.GetFontFeatureValuesFor(
375
aFamily, NS_FONT_VARIANT_ALTERNATES_STYLESET, ident.AsAtom());
376
377
// styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1
378
feature.mValue = 1;
379
for (uint32_t nn : values) {
380
if (nn == 0 || nn > MAX_SSXX_VALUE) {
381
continue;
382
}
383
feature.mTag = HB_TAG('s', 's', ('0' + nn / 10), ('0' + nn % 10));
384
aFontFeatures.AppendElement(feature);
385
}
386
}
387
return;
388
}
389
390
uint32_t constant = 0;
391
nsAtom* name = nullptr;
392
switch (aAlternates.tag) {
393
case Tag::Swash:
394
constant = NS_FONT_VARIANT_ALTERNATES_SWASH;
395
name = aAlternates.AsSwash().AsAtom();
396
break;
397
case Tag::Stylistic:
398
constant = NS_FONT_VARIANT_ALTERNATES_STYLISTIC;
399
name = aAlternates.AsStylistic().AsAtom();
400
break;
401
case Tag::Ornaments:
402
constant = NS_FONT_VARIANT_ALTERNATES_ORNAMENTS;
403
name = aAlternates.AsOrnaments().AsAtom();
404
break;
405
case Tag::Annotation:
406
constant = NS_FONT_VARIANT_ALTERNATES_ANNOTATION;
407
name = aAlternates.AsAnnotation().AsAtom();
408
break;
409
default:
410
MOZ_ASSERT_UNREACHABLE("Unknown font-variant-alternates value!");
411
return;
412
}
413
414
Span<const uint32_t> values =
415
aFeatureLookup.GetFontFeatureValuesFor(aFamily, constant, name);
416
if (values.IsEmpty()) {
417
return;
418
}
419
MOZ_ASSERT(values.Length() == 1,
420
"too many values for font-specific font-variant-alternates");
421
422
feature.mValue = values[0];
423
switch (aAlternates.tag) {
424
case Tag::Swash: // swsh, cswh
425
feature.mTag = HB_TAG('s', 'w', 's', 'h');
426
aFontFeatures.AppendElement(feature);
427
feature.mTag = HB_TAG('c', 's', 'w', 'h');
428
break;
429
case Tag::Stylistic: // salt
430
feature.mTag = HB_TAG('s', 'a', 'l', 't');
431
break;
432
case Tag::Ornaments: // ornm
433
feature.mTag = HB_TAG('o', 'r', 'n', 'm');
434
break;
435
case Tag::Annotation: // nalt
436
feature.mTag = HB_TAG('n', 'a', 'l', 't');
437
break;
438
default:
439
MOZ_ASSERT_UNREACHABLE("how?");
440
return;
441
}
442
aFontFeatures.AppendElement(feature);
443
}
444
445
/* static */
446
void gfxFontShaper::MergeFontFeatures(
447
const gfxFontStyle* aStyle, const nsTArray<gfxFontFeature>& aFontFeatures,
448
bool aDisableLigatures, const nsACString& aFamilyName, bool aAddSmallCaps,
449
void (*aHandleFeature)(const uint32_t&, uint32_t&, void*),
450
void* aHandleFeatureData) {
451
const nsTArray<gfxFontFeature>& styleRuleFeatures = aStyle->featureSettings;
452
453
// Bail immediately if nothing to do, which is the common case.
454
if (styleRuleFeatures.IsEmpty() && aFontFeatures.IsEmpty() &&
455
!aDisableLigatures &&
456
aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL &&
457
aStyle->variantSubSuper == NS_FONT_VARIANT_POSITION_NORMAL &&
458
aStyle->variantAlternates.IsEmpty()) {
459
return;
460
}
461
462
nsDataHashtable<nsUint32HashKey, uint32_t> mergedFeatures;
463
464
// add feature values from font
465
for (const gfxFontFeature& feature : aFontFeatures) {
466
mergedFeatures.Put(feature.mTag, feature.mValue);
467
}
468
469
// font-variant-caps - handled here due to the need for fallback handling
470
// petite caps cases can fallback to appropriate smallcaps
471
uint32_t variantCaps = aStyle->variantCaps;
472
switch (variantCaps) {
473
case NS_FONT_VARIANT_CAPS_NORMAL:
474
break;
475
476
case NS_FONT_VARIANT_CAPS_ALLSMALL:
477
mergedFeatures.Put(HB_TAG('c', '2', 's', 'c'), 1);
478
// fall through to the small-caps case
479
[[fallthrough]];
480
481
case NS_FONT_VARIANT_CAPS_SMALLCAPS:
482
mergedFeatures.Put(HB_TAG('s', 'm', 'c', 'p'), 1);
483
break;
484
485
case NS_FONT_VARIANT_CAPS_ALLPETITE:
486
mergedFeatures.Put(aAddSmallCaps ? HB_TAG('c', '2', 's', 'c')
487
: HB_TAG('c', '2', 'p', 'c'),
488
1);
489
// fall through to the petite-caps case
490
[[fallthrough]];
491
492
case NS_FONT_VARIANT_CAPS_PETITECAPS:
493
mergedFeatures.Put(aAddSmallCaps ? HB_TAG('s', 'm', 'c', 'p')
494
: HB_TAG('p', 'c', 'a', 'p'),
495
1);
496
break;
497
498
case NS_FONT_VARIANT_CAPS_TITLING:
499
mergedFeatures.Put(HB_TAG('t', 'i', 't', 'l'), 1);
500
break;
501
502
case NS_FONT_VARIANT_CAPS_UNICASE:
503
mergedFeatures.Put(HB_TAG('u', 'n', 'i', 'c'), 1);
504
break;
505
506
default:
507
MOZ_ASSERT_UNREACHABLE("Unexpected variantCaps");
508
break;
509
}
510
511
// font-variant-position - handled here due to the need for fallback
512
switch (aStyle->variantSubSuper) {
513
case NS_FONT_VARIANT_POSITION_NORMAL:
514
break;
515
case NS_FONT_VARIANT_POSITION_SUPER:
516
mergedFeatures.Put(HB_TAG('s', 'u', 'p', 's'), 1);
517
break;
518
case NS_FONT_VARIANT_POSITION_SUB:
519
mergedFeatures.Put(HB_TAG('s', 'u', 'b', 's'), 1);
520
break;
521
default:
522
MOZ_ASSERT_UNREACHABLE("Unexpected variantSubSuper");
523
break;
524
}
525
526
// add font-specific feature values from style rules
527
if (aStyle->featureValueLookup && !aStyle->variantAlternates.IsEmpty()) {
528
AutoTArray<gfxFontFeature, 4> featureList;
529
530
// insert list of alternate feature settings
531
for (auto& alternate : aStyle->variantAlternates.AsSpan()) {
532
LookupAlternateValues(*aStyle->featureValueLookup, aFamilyName, alternate,
533
featureList);
534
}
535
536
for (const gfxFontFeature& feature : featureList) {
537
mergedFeatures.Put(feature.mTag, feature.mValue);
538
}
539
}
540
541
// Add features that are already resolved to tags & values in the style.
542
if (styleRuleFeatures.IsEmpty()) {
543
// Disable common ligatures if non-zero letter-spacing is in effect.
544
if (aDisableLigatures) {
545
mergedFeatures.Put(HB_TAG('l', 'i', 'g', 'a'), 0);
546
mergedFeatures.Put(HB_TAG('c', 'l', 'i', 'g'), 0);
547
}
548
} else {
549
for (const gfxFontFeature& feature : styleRuleFeatures) {
550
// A dummy feature (0,0) is used as a sentinel to separate features
551
// originating from font-variant-* or other high-level properties from
552
// those directly specified as font-feature-settings. The high-level
553
// features may be overridden by aDisableLigatures, while low-level
554
// features specified directly as tags will come last and therefore
555
// take precedence over everything else.
556
if (feature.mTag) {
557
mergedFeatures.Put(feature.mTag, feature.mValue);
558
} else if (aDisableLigatures) {
559
// Handle ligature-disabling setting at the boundary between high-
560
// and low-level features.
561
mergedFeatures.Put(HB_TAG('l', 'i', 'g', 'a'), 0);
562
mergedFeatures.Put(HB_TAG('c', 'l', 'i', 'g'), 0);
563
}
564
}
565
}
566
567
if (mergedFeatures.Count() != 0) {
568
for (auto iter = mergedFeatures.Iter(); !iter.Done(); iter.Next()) {
569
aHandleFeature(iter.Key(), iter.Data(), aHandleFeatureData);
570
}
571
}
572
}
573
574
void gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
575
const char16_t* aString,
576
uint32_t aLength) {
577
CompressedGlyph* glyphs = GetCharacterGlyphs() + aOffset;
578
579
CompressedGlyph extendCluster = CompressedGlyph::MakeComplex(false, true, 0);
580
581
ClusterIterator iter(aString, aLength);
582
583
// the ClusterIterator won't be able to tell us if the string
584
// _begins_ with a cluster-extender, so we handle that here
585
if (aLength) {
586
uint32_t ch = *aString;
587
if (aLength > 1 && NS_IS_SURROGATE_PAIR(ch, aString[1])) {
588
ch = SURROGATE_TO_UCS4(ch, aString[1]);
589
}
590
if (IsClusterExtender(ch)) {
591
*glyphs = extendCluster;
592
}
593
}
594
595
while (!iter.AtEnd()) {
596
if (*iter == char16_t(' ')) {
597
glyphs->SetIsSpace();
598
}
599
// advance iter to the next cluster-start (or end of text)
600
iter.Next();
601
// step past the first char of the cluster
602
aString++;
603
glyphs++;
604
// mark all the rest as cluster-continuations
605
while (aString < iter) {
606
*glyphs = extendCluster;
607
glyphs++;
608
aString++;
609
}
610
}
611
}
612
613
void gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
614
const uint8_t* aString,
615
uint32_t aLength) {
616
CompressedGlyph* glyphs = GetCharacterGlyphs() + aOffset;
617
const uint8_t* limit = aString + aLength;
618
619
while (aString < limit) {
620
if (*aString == uint8_t(' ')) {
621
glyphs->SetIsSpace();
622
}
623
aString++;
624
glyphs++;
625
}
626
}
627
628
gfxShapedText::DetailedGlyph* gfxShapedText::AllocateDetailedGlyphs(
629
uint32_t aIndex, uint32_t aCount) {
630
NS_ASSERTION(aIndex < GetLength(), "Index out of range");
631
632
if (!mDetailedGlyphs) {
633
mDetailedGlyphs = MakeUnique<DetailedGlyphStore>();
634
}
635
636
return mDetailedGlyphs->Allocate(aIndex, aCount);
637
}
638
639
void gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
640
const DetailedGlyph* aGlyphs) {
641
NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
642
NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
643
"First character can't be a ligature continuation!");
644
645
uint32_t glyphCount = aGlyph.GetGlyphCount();
646
if (glyphCount > 0) {
647
DetailedGlyph* details = AllocateDetailedGlyphs(aIndex, glyphCount);
648
memcpy(details, aGlyphs, sizeof(DetailedGlyph) * glyphCount);
649
}
650
GetCharacterGlyphs()[aIndex] = aGlyph;
651
}
652
653
#define ZWNJ 0x200C
654
#define ZWJ 0x200D
655
static inline bool IsIgnorable(uint32_t aChar) {
656
return (IsDefaultIgnorable(aChar)) || aChar == ZWNJ || aChar == ZWJ;
657
}
658
659
void gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar,
660
gfxFont* aFont) {
661
uint8_t category = GetGeneralCategory(aChar);
662
if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
663
category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
664
GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
665
}
666
667
DetailedGlyph* details = AllocateDetailedGlyphs(aIndex, 1);
668
669
details->mGlyphID = aChar;
670
if (IsIgnorable(aChar)) {
671
// Setting advance width to zero will prevent drawing the hexbox
672
details->mAdvance = 0;
673
} else {
674
gfxFloat width =
675
std::max(aFont->GetMetrics(nsFontMetrics::eHorizontal).aveCharWidth,
676
gfxFloat(gfxFontMissingGlyphs::GetDesiredMinWidth(
677
aChar, mAppUnitsPerDevUnit)));
678
details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
679
}
680
GetCharacterGlyphs()[aIndex].SetMissing(1);
681
}
682
683
bool gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh) {
684
if (IsIgnorable(aCh)) {
685
// There are a few default-ignorables of Letter category (currently,
686
// just the Hangul filler characters) that we'd better not discard
687
// if they're followed by additional characters in the same cluster.
688
// Some fonts use them to carry the width of a whole cluster of
689
// combining jamos; see bug 1238243.
690
if (GetGenCategory(aCh) == nsUGenCategory::kLetter &&
691
aIndex + 1 < GetLength() &&
692
!GetCharacterGlyphs()[aIndex + 1].IsClusterStart()) {
693
return false;
694
}
695
DetailedGlyph* details = AllocateDetailedGlyphs(aIndex, 1);
696
details->mGlyphID = aCh;
697
details->mAdvance = 0;
698
GetCharacterGlyphs()[aIndex].SetMissing(1);
699
return true;
700
}
701
return false;
702
}
703
704
void gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
705
uint32_t aOffset,
706
uint32_t aLength) {
707
uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
708
CompressedGlyph* charGlyphs = GetCharacterGlyphs();
709
for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
710
CompressedGlyph* glyphData = charGlyphs + i;
711
if (glyphData->IsSimpleGlyph()) {
712
// simple glyphs ==> just add the advance
713
int32_t advance = glyphData->GetSimpleAdvance();
714
if (advance > 0) {
715
advance += synAppUnitOffset;
716
if (CompressedGlyph::IsSimpleAdvance(advance)) {
717
glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
718
} else {
719
// rare case, tested by making this the default
720
uint32_t glyphIndex = glyphData->GetSimpleGlyph();
721
glyphData->SetComplex(true, true, 1);
722
DetailedGlyph detail = {glyphIndex, advance, gfx::Point()};
723
SetGlyphs(i, *glyphData, &detail);
724
}
725
}
726
} else {
727
// complex glyphs ==> add offset at cluster/ligature boundaries
728
uint32_t detailedLength = glyphData->GetGlyphCount();
729
if (detailedLength) {
730
DetailedGlyph* details = GetDetailedGlyphs(i);
731
if (!details) {
732
continue;
733
}
734
if (IsRightToLeft()) {
735
if (details[0].mAdvance > 0) {
736
details[0].mAdvance += synAppUnitOffset;
737
}
738
} else {
739
if (details[detailedLength - 1].mAdvance > 0) {
740
details[detailedLength - 1].mAdvance += synAppUnitOffset;
741
}
742
}
743
}
744
}
745
}
746
}
747
748
float gfxFont::AngleForSyntheticOblique() const {
749
// If the style doesn't call for italic/oblique, or if the face already
750
// provides it, no synthetic style should be added.
751
if (mStyle.style == FontSlantStyle::Normal() || !mStyle.allowSyntheticStyle ||
752
!mFontEntry->IsUpright()) {
753
return 0.0f;
754
}
755
756
// If style calls for italic, and face doesn't support it, use default
757
// oblique angle as a simulation.
758
if (mStyle.style.IsItalic()) {
759
return mFontEntry->SupportsItalic() ? 0.0f : FontSlantStyle::kDefaultAngle;
760
}
761
762
// Default or custom oblique angle
763
return mStyle.style.ObliqueAngle();
764
}
765
766
float gfxFont::SkewForSyntheticOblique() const {
767
// Precomputed value of tan(kDefaultAngle), the default italic/oblique slant;
768
// avoids calling tan() at runtime except for custom oblique values.
769
static const float kTanDefaultAngle =
770
tan(FontSlantStyle::kDefaultAngle * (M_PI / 180.0));
771
772
float angle = AngleForSyntheticOblique();
773
if (angle == 0.0f) {
774
return 0.0f;
775
} else if (angle == FontSlantStyle::kDefaultAngle) {
776
return kTanDefaultAngle;
777
} else {
778
return tan(angle * (M_PI / 180.0));
779
}
780
}
781
782
void gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther,
783
bool aOtherIsOnLeft) {
784
mAscent = std::max(mAscent, aOther.mAscent);
785
mDescent = std::max(mDescent, aOther.mDescent);
786
if (aOtherIsOnLeft) {
787
mBoundingBox = (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0))
788
.Union(aOther.mBoundingBox);
789
} else {
790
mBoundingBox =
791
mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
792
}
793
mAdvanceWidth += aOther.mAdvanceWidth;
794
}
795
796
gfxFont::gfxFont(const RefPtr<UnscaledFont>& aUnscaledFont,
797
gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle,
798
AntialiasOption anAAOption)
799
: mFontEntry(aFontEntry),
800
mUnscaledFont(aUnscaledFont),
801
mStyle(*aFontStyle),
802
mAdjustedSize(0.0),
803
mFUnitsConvFactor(-1.0f), // negative to indicate "not yet initialized"
804
mAntialiasOption(anAAOption),
805
mIsValid(true),
806
mApplySyntheticBold(false),
807
mKerningEnabled(false),
808
mMathInitialized(false) {
809
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
810
++gFontCount;
811
#endif
812
813
if (MOZ_UNLIKELY(StaticPrefs::gfx_text_disable_aa_AtStartup())) {
814
mAntialiasOption = kAntialiasNone;
815
}
816
817
// Turn off AA for Ahem for testing purposes when requested.
818
if (MOZ_UNLIKELY(StaticPrefs::gfx_font_rendering_ahem_antialias_none() &&
819
mFontEntry->FamilyName().EqualsLiteral("Ahem"))) {
820
mAntialiasOption = kAntialiasNone;
821
}
822
823
mKerningSet = HasFeatureSet(HB_TAG('k', 'e', 'r', 'n'), mKerningEnabled);
824
}
825
826
gfxFont::~gfxFont() {
827
mFontEntry->NotifyFontDestroyed(this);
828
829
if (mGlyphChangeObservers) {
830
for (auto it = mGlyphChangeObservers->Iter(); !it.Done(); it.Next()) {
831
it.Get()->GetKey()->ForgetFont();
832
}
833
}
834
}
835
836
// Work out whether cairo will snap inter-glyph spacing to pixels.
837
//
838
// Layout does not align text to pixel boundaries, so, with font drawing
839
// backends that snap glyph positions to pixels, it is important that
840
// inter-glyph spacing within words is always an integer number of pixels.
841
// This ensures that the drawing backend snaps all of the word's glyphs in the
842
// same direction and so inter-glyph spacing remains the same.
843
//
844
gfxFont::RoundingFlags gfxFont::GetRoundOffsetsToPixels(
845
DrawTarget* aDrawTarget) {
846
// Could do something fancy here for ScaleFactors of
847
// AxisAlignedTransforms, but we leave things simple.
848
// Not much point rounding if a matrix will mess things up anyway.
849
// Also check if the font already knows hint metrics is off...
850
if (aDrawTarget->GetTransform().HasNonTranslation() || !ShouldHintMetrics()) {
851
return RoundingFlags(0);
852
}
853
854
cairo_t* cr = static_cast<cairo_t*>(
855
aDrawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
856
if (cr) {
857
cairo_surface_t* target = cairo_get_target(cr);
858
859
// Check whether the cairo surface's font options hint metrics.
860
cairo_font_options_t* fontOptions = cairo_font_options_create();
861
cairo_surface_get_font_options(target, fontOptions);
862
cairo_hint_metrics_t hintMetrics =
863
cairo_font_options_get_hint_metrics(fontOptions);
864
cairo_font_options_destroy(fontOptions);
865
866
switch (hintMetrics) {
867
case CAIRO_HINT_METRICS_OFF:
868
return RoundingFlags(0);
869
case CAIRO_HINT_METRICS_ON:
870
return RoundingFlags::kRoundX | RoundingFlags::kRoundY;
871
default:
872
break;
873
}
874
}
875
876
if (ShouldRoundXOffset(cr)) {
877
return RoundingFlags::kRoundX | RoundingFlags::kRoundY;
878
} else {
879
return RoundingFlags::kRoundY;
880
}
881
}
882
883
gfxFloat gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID) {
884
if (ProvidesGlyphWidths()) {
885
return GetGlyphWidth(aGID) / 65536.0;
886
}
887
if (mFUnitsConvFactor < 0.0f) {
888
GetMetrics(nsFontMetrics::eHorizontal);
889
}
890
NS_ASSERTION(mFUnitsConvFactor >= 0.0f,
891
"missing font unit conversion factor");
892
if (!mHarfBuzzShaper) {
893
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
894
}
895
gfxHarfBuzzShaper* shaper =
896
static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
897
if (!shaper->Initialize()) {
898
return 0;
899
}
900
return shaper->GetGlyphHAdvance(aGID) / 65536.0;
901
}
902
903
static void CollectLookupsByFeature(hb_face_t* aFace, hb_tag_t aTableTag,
904
uint32_t aFeatureIndex,
905
hb_set_t* aLookups) {
906
uint32_t lookups[32];
907
uint32_t i, len, offset;
908
909
offset = 0;
910
do {
911
len = ArrayLength(lookups);
912
hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex, offset,
913
&len, lookups);
914
for (i = 0; i < len; i++) {
915
hb_set_add(aLookups, lookups[i]);
916
}
917
offset += len;
918
} while (len == ArrayLength(lookups));
919
}
920
921
static void CollectLookupsByLanguage(
922
hb_face_t* aFace, hb_tag_t aTableTag,
923
const nsTHashtable<nsUint32HashKey>& aSpecificFeatures,
924
hb_set_t* aOtherLookups, hb_set_t* aSpecificFeatureLookups,
925
uint32_t aScriptIndex, uint32_t aLangIndex) {
926
uint32_t reqFeatureIndex;
927
if (hb_ot_layout_language_get_required_feature_index(
928
aFace, aTableTag, aScriptIndex, aLangIndex, &reqFeatureIndex)) {
929
CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex, aOtherLookups);
930
}
931
932
uint32_t featureIndexes[32];
933
uint32_t i, len, offset;
934
935
offset = 0;
936
do {
937
len = ArrayLength(featureIndexes);
938
hb_ot_layout_language_get_feature_indexes(aFace, aTableTag, aScriptIndex,
939
aLangIndex, offset, &len,
940
featureIndexes);
941
942
for (i = 0; i < len; i++) {
943
uint32_t featureIndex = featureIndexes[i];
944
945
// get the feature tag
946
hb_tag_t featureTag;
947
uint32_t tagLen = 1;
948
hb_ot_layout_language_get_feature_tags(aFace, aTableTag, aScriptIndex,
949
aLangIndex, offset + i, &tagLen,
950
&featureTag);
951
952
// collect lookups
953
hb_set_t* lookups = aSpecificFeatures.GetEntry(featureTag)
954
? aSpecificFeatureLookups
955
: aOtherLookups;
956
CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
957
}
958
offset += len;
959
} while (len == ArrayLength(featureIndexes));
960
}
961
962
static bool HasLookupRuleWithGlyphByScript(
963
hb_face_t* aFace, hb_tag_t aTableTag, hb_tag_t aScriptTag,
964
uint32_t aScriptIndex, uint16_t aGlyph,
965
const nsTHashtable<nsUint32HashKey>& aDefaultFeatures,
966
bool& aHasDefaultFeatureWithGlyph) {
967
uint32_t numLangs, lang;
968
hb_set_t* defaultFeatureLookups = hb_set_create();
969
hb_set_t* nonDefaultFeatureLookups = hb_set_create();
970
971
// default lang
972
CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
973
nonDefaultFeatureLookups, defaultFeatureLookups,
974
aScriptIndex, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
975
976
// iterate over langs
977
numLangs = hb_ot_layout_script_get_language_tags(
978
aFace, aTableTag, aScriptIndex, 0, nullptr, nullptr);
979
for (lang = 0; lang < numLangs; lang++) {
980
CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
981
nonDefaultFeatureLookups, defaultFeatureLookups,
982
aScriptIndex, lang);
983
}
984
985
// look for the glyph among default feature lookups
986
aHasDefaultFeatureWithGlyph = false;
987
hb_set_t* glyphs = hb_set_create();
988
hb_codepoint_t index = -1;
989
while (hb_set_next(defaultFeatureLookups, &index)) {
990
hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
991
glyphs, nullptr);
992
if (hb_set_has(glyphs, aGlyph)) {
993
aHasDefaultFeatureWithGlyph = true;
994
break;
995
}
996
}
997
998
// look for the glyph among non-default feature lookups
999
// if no default feature lookups contained spaces
1000
bool hasNonDefaultFeatureWithGlyph = false;
1001
if (!aHasDefaultFeatureWithGlyph) {
1002
hb_set_clear(glyphs);
1003
index = -1;
1004
while (hb_set_next(nonDefaultFeatureLookups, &index)) {
1005
hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs,
1006
glyphs, glyphs, nullptr);
1007
if (hb_set_has(glyphs, aGlyph)) {
1008
hasNonDefaultFeatureWithGlyph = true;
1009
break;
1010
}
1011
}
1012
}
1013
1014
hb_set_destroy(glyphs);
1015
hb_set_destroy(defaultFeatureLookups);
1016
hb_set_destroy(nonDefaultFeatureLookups);
1017
1018
return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph;
1019
}
1020
1021
static void HasLookupRuleWithGlyph(hb_face_t* aFace, hb_tag_t aTableTag,
1022
bool& aHasGlyph, hb_tag_t aSpecificFeature,
1023
bool& aHasGlyphSpecific, uint16_t aGlyph) {
1024
// iterate over the scripts in the font
1025
uint32_t numScripts, numLangs, script, lang;
1026
hb_set_t* otherLookups = hb_set_create();
1027
hb_set_t* specificFeatureLookups = hb_set_create();
1028
nsTHashtable<nsUint32HashKey> specificFeature;
1029
1030
specificFeature.PutEntry(aSpecificFeature);
1031
1032
numScripts =
1033
hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0, nullptr, nullptr);
1034
1035
for (script = 0; script < numScripts; script++) {
1036
// default lang
1037
CollectLookupsByLanguage(aFace, aTableTag, specificFeature, otherLookups,
1038
specificFeatureLookups, script,
1039
HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
1040
1041
// iterate over langs
1042
numLangs = hb_ot_layout_script_get_language_tags(
1043
aFace, HB_OT_TAG_GPOS, script, 0, nullptr, nullptr);
1044
for (lang = 0; lang < numLangs; lang++) {
1045
CollectLookupsByLanguage(aFace, aTableTag, specificFeature, otherLookups,
1046
specificFeatureLookups, script, lang);
1047
}
1048
}
1049
1050
// look for the glyph among non-specific feature lookups
1051
hb_set_t* glyphs = hb_set_create();
1052
hb_codepoint_t index = -1;
1053
while (hb_set_next(otherLookups, &index)) {
1054
hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
1055
glyphs, nullptr);
1056
if (hb_set_has(glyphs, aGlyph)) {
1057
aHasGlyph = true;
1058
break;
1059
}
1060
}
1061
1062
// look for the glyph among specific feature lookups
1063
hb_set_clear(glyphs);
1064
index = -1;
1065
while (hb_set_next(specificFeatureLookups, &index)) {
1066
hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
1067
glyphs, nullptr);
1068
if (hb_set_has(glyphs, aGlyph)) {
1069
aHasGlyphSpecific = true;
1070
break;
1071
}
1072
}
1073
1074
hb_set_destroy(glyphs);
1075
hb_set_destroy(specificFeatureLookups);
1076
hb_set_destroy(otherLookups);
1077
}
1078
1079
nsDataHashtable<nsUint32HashKey, Script>* gfxFont::sScriptTagToCode = nullptr;
1080
nsTHashtable<nsUint32HashKey>* gfxFont::sDefaultFeatures = nullptr;
1081
1082
static inline bool HasSubstitution(uint32_t* aBitVector, Script aScript) {
1083
return (aBitVector[static_cast<uint32_t>(aScript) >> 5] &
1084
(1 << (static_cast<uint32_t>(aScript) & 0x1f))) != 0;
1085
}
1086
1087
// union of all default substitution features across scripts
1088
static const hb_tag_t defaultFeatures[] = {
1089
HB_TAG('a', 'b', 'v', 'f'), HB_TAG('a', 'b', 'v', 's'),
1090
HB_TAG('a', 'k', 'h', 'n'), HB_TAG('b', 'l', 'w', 'f'),
1091
HB_TAG('b', 'l', 'w', 's'), HB_TAG('c', 'a', 'l', 't'),
1092
HB_TAG('c', 'c', 'm', 'p'), HB_TAG('c', 'f', 'a', 'r'),
1093
HB_TAG('c', 'j', 'c', 't'), HB_TAG('c', 'l', 'i', 'g'),
1094
HB_TAG('f', 'i', 'n', '2'), HB_TAG('f', 'i', 'n', '3'),
1095
HB_TAG('f', 'i', 'n', 'a'), HB_TAG('h', 'a', 'l', 'f'),
1096
HB_TAG('h', 'a', 'l', 'n'), HB_TAG('i', 'n', 'i', 't'),
1097
HB_TAG('i', 's', 'o', 'l'), HB_TAG('l', 'i', 'g', 'a'),
1098
HB_TAG('l', 'j', 'm', 'o'), HB_TAG('l', 'o', 'c', 'l'),
1099
HB_TAG('l', 't', 'r', 'a'), HB_TAG('l', 't', 'r', 'm'),
1100
HB_TAG('m', 'e', 'd', '2'), HB_TAG('m', 'e', 'd', 'i'),
1101
HB_TAG('m', 's', 'e', 't'), HB_TAG('n', 'u', 'k', 't'),
1102
HB_TAG('p', 'r', 'e', 'f'), HB_TAG('p', 'r', 'e', 's'),
1103
HB_TAG('p', 's', 't', 'f'), HB_TAG('p', 's', 't', 's'),
1104
HB_TAG('r', 'c', 'l', 't'), HB_TAG('r', 'l', 'i', 'g'),
1105
HB_TAG('r', 'k', 'r', 'f'), HB_TAG('r', 'p', 'h', 'f'),
1106
HB_TAG('r', 't', 'l', 'a'), HB_TAG('r', 't', 'l', 'm'),
1107
HB_TAG('t', 'j', 'm', 'o'), HB_TAG('v', 'a', 't', 'u'),
1108
HB_TAG('v', 'e', 'r', 't'), HB_TAG('v', 'j', 'm', 'o')};
1109
1110
void gfxFont::CheckForFeaturesInvolvingSpace() {
1111
mFontEntry->mHasSpaceFeaturesInitialized = true;
1112
1113
bool log = LOG_FONTINIT_ENABLED();
1114
TimeStamp start;
1115
if (MOZ_UNLIKELY(log)) {
1116
start = TimeStamp::Now();
1117
}
1118
1119
bool result = false;
1120
1121
uint32_t spaceGlyph = GetSpaceGlyph();
1122
if (!spaceGlyph) {
1123
return;
1124
}
1125
1126
hb_face_t* face = GetFontEntry()->GetHBFace();
1127
1128
// GSUB lookups - examine per script
1129
if (hb_ot_layout_has_substitution(face)) {
1130
// set up the script ==> code hashtable if needed
1131
if (!sScriptTagToCode) {
1132
sScriptTagToCode = new nsDataHashtable<nsUint32HashKey, Script>(
1133
size_t(Script::NUM_SCRIPT_CODES));
1134
sScriptTagToCode->Put(HB_TAG('D', 'F', 'L', 'T'), Script::COMMON);
1135
// Ensure that we don't try to look at script codes beyond what the
1136
// current version of ICU (at runtime -- in case of system ICU)
1137
// knows about.
1138
Script scriptCount =
1139
Script(std::min<int>(u_getIntPropertyMaxValue(UCHAR_SCRIPT) + 1,
1140
int(Script::NUM_SCRIPT_CODES)));
1141
for (Script s = Script::ARABIC; s < scriptCount;
1142
s = Script(static_cast<int>(s) + 1)) {
1143
hb_script_t script = hb_script_t(GetScriptTagForCode(s));
1144
unsigned int scriptCount = 4;
1145
hb_tag_t scriptTags[4];
1146
hb_ot_tags_from_script_and_language(script, HB_LANGUAGE_INVALID,
1147
&scriptCount, scriptTags, nullptr,
1148
nullptr);
1149
for (unsigned int i = 0; i < scriptCount; i++) {
1150
sScriptTagToCode->Put(scriptTags[i], s);
1151
}
1152
}
1153
1154
uint32_t numDefaultFeatures = ArrayLength(defaultFeatures);
1155
sDefaultFeatures = new nsTHashtable<nsUint32HashKey>(numDefaultFeatures);
1156
for (uint32_t i = 0; i < numDefaultFeatures; i++) {
1157
sDefaultFeatures->PutEntry(defaultFeatures[i]);
1158
}
1159
}
1160
1161
// iterate over the scripts in the font
1162
hb_tag_t scriptTags[8];
1163
1164
uint32_t len, offset = 0;
1165
do {
1166
len = ArrayLength(scriptTags);
1167
hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset, &len,
1168
scriptTags);
1169
for (uint32_t i = 0; i < len; i++) {
1170
bool isDefaultFeature = false;
1171
Script s;
1172
if (!HasLookupRuleWithGlyphByScript(
1173
face, HB_OT_TAG_GSUB, scriptTags[i], offset + i, spaceGlyph,
1174
*sDefaultFeatures, isDefaultFeature) ||
1175
!sScriptTagToCode->Get(scriptTags[i], &s)) {
1176
continue;
1177
}
1178
result = true;
1179
uint32_t index = static_cast<uint32_t>(s) >> 5;
1180
uint32_t bit = static_cast<uint32_t>(s) & 0x1f;
1181
if (isDefaultFeature) {
1182
mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
1183
} else {
1184
mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
1185
}
1186
}
1187
offset += len;
1188
} while (len == ArrayLength(scriptTags));
1189
}
1190
1191
// spaces in default features of default script?
1192
// ==> can't use word cache, skip GPOS analysis
1193
bool canUseWordCache = true;
1194
if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, Script::COMMON)) {
1195
canUseWordCache = false;
1196
}
1197
1198
// GPOS lookups - distinguish kerning from non-kerning features
1199
mFontEntry->mHasSpaceFeaturesKerning = false;
1200
mFontEntry->mHasSpaceFeaturesNonKerning = false;
1201
1202
if (canUseWordCache && hb_ot_layout_has_positioning(face)) {
1203
bool hasKerning = false, hasNonKerning = false;
1204
HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
1205
HB_TAG('k', 'e', 'r', 'n'), hasKerning, spaceGlyph);
1206
if (hasKerning || hasNonKerning) {
1207
result = true;
1208
}
1209
mFontEntry->mHasSpaceFeaturesKerning = hasKerning;
1210
mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning;
1211
}
1212
1213
hb_face_destroy(face);
1214
mFontEntry->mHasSpaceFeatures = result;
1215
1216
if (MOZ_UNLIKELY(log)) {
1217
TimeDuration elapsed = TimeStamp::Now() - start;
1218
LOG_FONTINIT(
1219
("(fontinit-spacelookups) font: %s - "
1220
"subst default: %8.8x %8.8x %8.8x %8.8x "
1221
"subst non-default: %8.8x %8.8x %8.8x %8.8x "
1222
"kerning: %s non-kerning: %s time: %6.3f\n",
1223
mFontEntry->Name().get(), mFontEntry->mDefaultSubSpaceFeatures[3],
1224
mFontEntry->mDefaultSubSpaceFeatures[2],
1225
mFontEntry->mDefaultSubSpaceFeatures[1],
1226
mFontEntry->mDefaultSubSpaceFeatures[0],
1227
mFontEntry->mNonDefaultSubSpaceFeatures[3],
1228
mFontEntry->mNonDefaultSubSpaceFeatures[2],
1229
mFontEntry->mNonDefaultSubSpaceFeatures[1],
1230
mFontEntry->mNonDefaultSubSpaceFeatures[0],
1231
(mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"),
1232
(mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false"),
1233
elapsed.ToMilliseconds()));
1234
}
1235
}
1236
1237
bool gfxFont::HasSubstitutionRulesWithSpaceLookups(Script aRunScript) {
1238
NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
1239
"need to initialize space lookup flags");
1240
NS_ASSERTION(aRunScript < Script::NUM_SCRIPT_CODES, "weird script code");
1241
if (aRunScript == Script::INVALID || aRunScript >= Script::NUM_SCRIPT_CODES) {
1242
return false;
1243
}
1244
1245
// default features have space lookups ==> true
1246
if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, Script::COMMON) ||
1247
HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, aRunScript)) {
1248
return true;
1249
}
1250
1251
// non-default features have space lookups and some type of
1252
// font feature, in font or style is specified ==> true
1253
if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
1254
Script::COMMON) ||
1255
HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures, aRunScript)) &&
1256
(!mStyle.featureSettings.IsEmpty() ||
1257
!mFontEntry->mFeatureSettings.IsEmpty())) {
1258
return true;
1259
}
1260
1261
return false;
1262
}
1263
1264
tainted_boolean_hint gfxFont::SpaceMayParticipateInShaping(Script aRunScript) {
1265
// avoid checking fonts known not to include default space-dependent features
1266
if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
1267
if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
1268
mFontEntry->mFeatureSettings.IsEmpty()) {
1269
return false;
1270
}
1271
}
1272
1273
if (FontCanSupportGraphite()) {
1274
if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
1275
return mFontEntry->HasGraphiteSpaceContextuals();
1276
}
1277
}
1278
1279
// We record the presence of space-dependent features in the font entry
1280
// so that subsequent instantiations for the same font face won't
1281
// require us to re-check the tables; however, the actual check is done
1282
// by gfxFont because not all font entry subclasses know how to create
1283
// a harfbuzz face for introspection.
1284
if (!mFontEntry->mHasSpaceFeaturesInitialized) {
1285
CheckForFeaturesInvolvingSpace();
1286
}
1287
1288
if (!mFontEntry->mHasSpaceFeatures) {
1289
return false;
1290
}
1291
1292
// if font has substitution rules or non-kerning positioning rules
1293
// that involve spaces, bypass
1294
if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
1295
mFontEntry->mHasSpaceFeaturesNonKerning) {
1296
return true;
1297
}
1298
1299
// if kerning explicitly enabled/disabled via font-feature-settings or
1300
// font-kerning and kerning rules use spaces, only bypass when enabled
1301
if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) {
1302
return mKerningEnabled;
1303
}
1304
1305
return false;
1306
}
1307
1308
bool gfxFont::SupportsFeature(Script aScript, uint32_t aFeatureTag) {
1309
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
1310
return GetFontEntry()->SupportsGraphiteFeature(aFeatureTag);
1311
}
1312
return GetFontEntry()->SupportsOpenTypeFeature(aScript, aFeatureTag);
1313
}
1314
1315
bool gfxFont::SupportsVariantCaps(Script aScript, uint32_t aVariantCaps,
1316
bool& aFallbackToSmallCaps,
1317
bool& aSyntheticLowerToSmallCaps,
1318
bool& aSyntheticUpperToSmallCaps) {
1319
bool ok = true; // cases without fallback are fine
1320
aFallbackToSmallCaps = false;
1321
aSyntheticLowerToSmallCaps = false;
1322
aSyntheticUpperToSmallCaps = false;
1323
switch (aVariantCaps) {
1324
case NS_FONT_VARIANT_CAPS_SMALLCAPS:
1325
ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p'));
1326
if (!ok) {
1327
aSyntheticLowerToSmallCaps = true;
1328
}
1329
break;
1330
case NS_FONT_VARIANT_CAPS_ALLSMALL:
1331
ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p')) &&
1332
SupportsFeature(aScript, HB_TAG('c', '2', 's', 'c'));
1333
if (!ok) {
1334
aSyntheticLowerToSmallCaps = true;
1335
aSyntheticUpperToSmallCaps = true;
1336
}
1337
break;
1338
case NS_FONT_VARIANT_CAPS_PETITECAPS:
1339
ok = SupportsFeature(aScript, HB_TAG('p', 'c', 'a', 'p'));
1340
if (!ok) {
1341
ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p'));
1342
aFallbackToSmallCaps = ok;
1343
}
1344
if (!ok) {
1345
aSyntheticLowerToSmallCaps = true;
1346
}
1347
break;
1348
case NS_FONT_VARIANT_CAPS_ALLPETITE:
1349
ok = SupportsFeature(aScript, HB_TAG('p', 'c', 'a', 'p')) &&
1350
SupportsFeature(aScript, HB_TAG('c', '2', 'p', 'c'));
1351
if (!ok) {
1352
ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p')) &&
1353
SupportsFeature(aScript, HB_TAG('c', '2', 's', 'c'));
1354
aFallbackToSmallCaps = ok;
1355
}
1356
if (!ok) {
1357
aSyntheticLowerToSmallCaps = true;
1358
aSyntheticUpperToSmallCaps = true;
1359
}
1360
break;
1361
default:
1362
break;
1363
}
1364
1365
NS_ASSERTION(
1366
!(ok && (aSyntheticLowerToSmallCaps || aSyntheticUpperToSmallCaps)),
1367
"shouldn't use synthetic features if we found real ones");
1368
1369
NS_ASSERTION(!(!ok && aFallbackToSmallCaps),
1370
"if we found a usable fallback, that counts as ok");
1371
1372
return ok;
1373
}
1374
1375
bool gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
1376
const uint8_t* aString, uint32_t aLength,
1377
Script aRunScript) {
1378
NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString),
1379
aLength);
1380
return SupportsSubSuperscript(aSubSuperscript, unicodeString.get(), aLength,
1381
aRunScript);
1382
}
1383
1384
bool gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
1385
const char16_t* aString, uint32_t aLength,
1386
Script aRunScript) {
1387
NS_ASSERTION(aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ||
1388
aSubSuperscript == NS_FONT_VARIANT_POSITION_SUB,
1389
"unknown value of font-variant-position");
1390
1391
uint32_t feature = aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER
1392
? HB_TAG('s', 'u', 'p', 's')
1393
: HB_TAG('s', 'u', 'b', 's');
1394
1395
if (!SupportsFeature(aRunScript, feature)) {
1396
return false;
1397
}
1398
1399
// xxx - for graphite, don't really know how to sniff lookups so bail
1400
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
1401
return true;
1402
}
1403
1404
if (!mHarfBuzzShaper) {
1405
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
1406
}
1407
gfxHarfBuzzShaper* shaper =
1408
static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
1409
if (!shaper->Initialize()) {
1410
return false;
1411
}
1412
1413
// get the hbset containing input glyphs for the feature
1414
const hb_set_t* inputGlyphs =
1415
mFontEntry->InputsForOpenTypeFeature(aRunScript, feature);
1416
1417
// create an hbset containing default glyphs for the script run
1418
hb_set_t* defaultGlyphsInRun = hb_set_create();
1419
1420
// for each character, get the glyph id
1421
for (uint32_t i = 0; i < aLength; i++) {
1422
uint32_t ch = aString[i];
1423
1424
if (i + 1 < aLength && NS_IS_SURROGATE_PAIR(ch, aString[i + 1])) {
1425
i++;
1426
ch = SURROGATE_TO_UCS4(ch, aString[i]);
1427
}
1428
1429
if (ch == 0xa0) {
1430
ch = ' ';
1431
}
1432
1433
hb_codepoint_t gid = shaper->GetNominalGlyph(ch);
1434
hb_set_add(defaultGlyphsInRun, gid);
1435
}
1436
1437
// intersect with input glyphs, if size is not the same ==> fallback
1438
uint32_t origSize = hb_set_get_population(defaultGlyphsInRun);
1439
hb_set_intersect(defaultGlyphsInRun, inputGlyphs);
1440
uint32_t intersectionSize = hb_set_get_population(defaultGlyphsInRun);
1441
hb_set_destroy(defaultGlyphsInRun);
1442
1443
return origSize == intersectionSize;
1444
}
1445
1446
bool gfxFont::FeatureWillHandleChar(Script aRunScript, uint32_t aFeature,
1447
uint32_t aUnicode) {
1448
if (!SupportsFeature(aRunScript, aFeature)) {
1449
return false;
1450
}
1451
1452
// xxx - for graphite, don't really know how to sniff lookups so bail
1453
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
1454
return true;
1455
}
1456
1457
if (!mHarfBuzzShaper) {
1458
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
1459
}
1460
gfxHarfBuzzShaper* shaper =
1461
static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
1462
if (!shaper->Initialize()) {
1463
return false;
1464
}
1465
1466
// get the hbset containing input glyphs for the feature
1467
const hb_set_t* inputGlyphs =
1468
mFontEntry->InputsForOpenTypeFeature(aRunScript, aFeature);
1469
1470
if (aUnicode == 0xa0) {
1471
aUnicode = ' ';
1472
}
1473
1474
hb_codepoint_t gid = shaper->GetNominalGlyph(aUnicode);
1475
return hb_set_has(inputGlyphs, gid);
1476
}
1477
1478
bool gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn) {
1479
aFeatureOn = false;
1480
1481
if (mStyle.featureSettings.IsEmpty() &&
1482
GetFontEntry()->mFeatureSettings.IsEmpty()) {
1483
return false;
1484
}
1485
1486
// add feature values from font
1487
bool featureSet = false;
1488
uint32_t i, count;
1489
1490
nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
1491
count = fontFeatures.Length();
1492
for (i = 0; i < count; i++) {
1493
const gfxFontFeature& feature = fontFeatures.ElementAt(i);
1494
if (feature.mTag == aFeature) {
1495
featureSet = true;
1496
aFeatureOn = (feature.mValue != 0);
1497
}
1498
}
1499
1500
// add feature values from style rules
1501
nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
1502
count = styleFeatures.Length();
1503
for (i = 0; i < count; i++) {
1504
const gfxFontFeature& feature = styleFeatures.ElementAt(i);
1505
if (feature.mTag == aFeature) {
1506
featureSet = true;
1507
aFeatureOn = (feature.mValue != 0);
1508
}
1509
}
1510
1511
return featureSet;
1512
}
1513
1514
void gfxFont::InitializeScaledFont() {
1515
if (!mAzureScaledFont) {
1516
return;
1517
}
1518
1519
float angle = AngleForSyntheticOblique();
1520
if (angle != 0.0f) {
1521
mAzureScaledFont->SetSyntheticObliqueAngle(angle);
1522
}
1523
}
1524
1525
/**
1526
* A helper function in case we need to do any rounding or other
1527
* processing here.
1528
*/
1529
#define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
1530
(double(aAppUnits) * double(aDevUnitsPerAppUnit))
1531
1532
static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
1533
switch (aAAOption) {
1534
case gfxFont::kAntialiasSubpixel:
1535
return AntialiasMode::SUBPIXEL;
1536
case gfxFont::kAntialiasGrayscale:
1537
return AntialiasMode::GRAY;
1538
case gfxFont::kAntialiasNone:
1539
return AntialiasMode::NONE;
1540
default:
1541
return AntialiasMode::DEFAULT;
1542
}
1543
}
1544
1545
class GlyphBufferAzure {
1546
#define AUTO_BUFFER_SIZE (2048 / sizeof(Glyph))
1547
1548
typedef mozilla::image::imgDrawingParams imgDrawingParams;
1549
1550
public:
1551
GlyphBufferAzure(const TextRunDrawParams& aRunParams,
1552
const FontDrawParams& aFontParams)
1553
: mRunParams(aRunParams),
1554
mFontParams(aFontParams),
1555
mBuffer(*mAutoBuffer.addr()),
1556
mBufSize(AUTO_BUFFER_SIZE),
1557
mCapacity(0),
1558
mNumGlyphs(0) {}
1559
1560
~GlyphBufferAzure() {
1561
if (mNumGlyphs > 0) {
1562
FlushGlyphs();
1563
}
1564
1565
if (mBuffer != *mAutoBuffer.addr()) {
1566
free(mBuffer);
1567
}
1568
}
1569
1570
// Ensure the buffer has enough space for aGlyphCount glyphs to be added,
1571
// considering the supplied strike multipler aStrikeCount.
1572
// This MUST be called before OutputGlyph is used to actually store glyph
1573
// records in the buffer. It may be called repeated to add further capacity
1574
// in case we don't know up-front exactly what will be needed.
1575
void AddCapacity(uint32_t aGlyphCount, uint32_t aStrikeCount) {
1576
// Calculate the new capacity and ensure it will fit within the maximum
1577
// allowed capacity.
1578
static const uint64_t kMaxCapacity = 64 * 1024;
1579
mCapacity = uint32_t(std::min(
1580
kMaxCapacity,
1581
uint64_t(mCapacity) + uint64_t(aGlyphCount) * uint64_t(aStrikeCount)));
1582
// See if the required capacity fits within the already-allocated space
1583
if (mCapacity <= mBufSize) {
1584
return;
1585
}
1586
// We need to grow the buffer: determine a new size, allocate, and
1587
// copy the existing data over if we didn't use realloc (which would
1588
// do it automatically).
1589
mBufSize = std::max(mCapacity, mBufSize * 2);
1590
if (mBuffer == *mAutoBuffer.addr()) {
1591
// switching from autobuffer to malloc, so we need to copy
1592
mBuffer = reinterpret_cast<Glyph*>(moz_xmalloc(mBufSize * sizeof(Glyph)));
1593
std::memcpy(mBuffer, *mAutoBuffer.addr(), mNumGlyphs * sizeof(Glyph));
1594
} else {
1595
mBuffer = reinterpret_cast<Glyph*>(
1596
moz_xrealloc(mBuffer, mBufSize * sizeof(Glyph)));
1597
}
1598
}
1599
1600
void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt) {
1601
// If the buffer is full, flush to make room for the new glyph.
1602
if (mNumGlyphs >= mCapacity) {
1603
// Check that AddCapacity has been used appropriately!
1604
MOZ_ASSERT(mCapacity > 0 && mNumGlyphs == mCapacity);
1605
Flush();
1606
}
1607
Glyph* glyph = mBuffer + mNumGlyphs++;
1608
glyph->mIndex = aGlyphID;
1609
glyph->mPosition = aPt;
1610
}
1611
1612
void Flush() {
1613
if (mNumGlyphs > 0) {
1614
FlushGlyphs();
1615
mNumGlyphs = 0;
1616
}
1617
}
1618
1619
const TextRunDrawParams& mRunParams;
1620
const FontDrawParams& mFontParams;
1621
1622
private:
1623
static DrawMode GetStrokeMode(DrawMode aMode) {
1624
return aMode & (DrawMode::GLYPH_STROKE | DrawMode::GLYPH_STROKE_UNDERNEATH);
1625
}
1626
1627
// Render the buffered glyphs to the draw target.
1628
void FlushGlyphs() {
1629
gfx::GlyphBuffer buf;
1630
buf.mGlyphs = mBuffer;
1631
buf.mNumGlyphs = mNumGlyphs;
1632
1633
const gfxContext::AzureState& state = mRunParams.context->CurrentState();
1634
1635
// Draw stroke first if the UNDERNEATH flag is set in drawMode.
1636
if (mRunParams.strokeOpts &&
1637
GetStrokeMode(mRunParams.drawMode) ==
1638
(DrawMode::GLYPH_STROKE | DrawMode::GLYPH_STROKE_UNDERNEATH)) {
1639
DrawStroke(state, buf);
1640
}
1641
1642
if (mRunParams.drawMode & DrawMode::GLYPH_FILL) {
1643
if (state.pattern || mFontParams.contextPaint) {
1644
Pattern* pat;
1645
1646
RefPtr<gfxPattern> fillPattern;
1647
if (mFontParams.contextPaint) {
1648
imgDrawingParams imgParams;
1649
fillPattern = mFontParams.contextPaint->GetFillPattern(
1650
mRunParams.context->GetDrawTarget(),
1651
mRunParams.context->CurrentMatrixDouble(), imgParams);
1652
}
1653
if (!fillPattern) {
1654
if (state.pattern) {
1655
RefPtr<gfxPattern> statePattern =
1656
mRunParams.context->CurrentState().pattern;
1657
pat = statePattern->GetPattern(mRunParams.dt,
1658
state.patternTransformChanged
1659
? &state.patternTransform
1660
: nullptr);
1661
} else {
1662
pat = nullptr;
1663
}
1664
} else {
1665
pat = fillPattern->GetPattern(mRunParams.dt);
1666
}
1667
1668
if (pat) {
1669
mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf, *pat,
1670
mFontParams.drawOptions);
1671
}
1672
} else {
1673
mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
1674
ColorPattern(state.color),
1675
mFontParams.drawOptions);
1676
}
1677
}
1678
1679
// Draw stroke if the UNDERNEATH flag is not set.
1680
if (mRunParams.strokeOpts &&
1681
GetStrokeMode(mRunParams.drawMode) == DrawMode::GLYPH_STROKE) {
1682
DrawStroke(state, buf);
1683
}
1684
1685
if (mRunParams.drawMode & DrawMode::GLYPH_PATH) {
1686
mRunParams.context->EnsurePathBuilder();
1687
Matrix mat = mRunParams.dt->GetTransform();
1688
mFontParams.scaledFont->CopyGlyphsToBuilder(
1689
buf, mRunParams.context->mPathBuilder, &mat);
1690
}
1691
}
1692
1693
void DrawStroke(const gfxContext::AzureState& aState,
1694
gfx::GlyphBuffer& aBuffer) {
1695
if (mRunParams.textStrokePattern) {
1696
Pattern* pat = mRunParams.textStrokePattern->GetPattern(
1697
mRunParams.dt,
1698
aState.patternTransformChanged ? &aState.patternTransform : nullptr);
1699
1700
if (pat) {
1701
FlushStroke(aBuffer, *pat);
1702
}
1703
} else {
1704
FlushStroke(aBuffer,
1705
ColorPattern(ToDeviceColor(mRunParams.textStrokeColor)));
1706
}
1707
}
1708
1709
void FlushStroke(gfx::GlyphBuffer& aBuf, const Pattern& aPattern) {
1710
mRunParams.dt->StrokeGlyphs(mFontParams.scaledFont, aBuf, aPattern,
1711
*mRunParams.strokeOpts,
1712
mFontParams.drawOptions);
1713
}
1714
1715
// We use an "inline" buffer automatically allocated (on the stack) as part
1716
// of the GlyphBufferAzure object to hold the glyphs in most cases, falling
1717
// back to a separately-allocated heap buffer if the count of buffered
1718
// glyphs gets too big.
1719
//
1720
// This is basically a rudimentary AutoTArray; so why not use AutoTArray
1721
// itself?
1722
//
1723
// If we used an AutoTArray, we'd want to avoid using SetLength or
1724
// AppendElements to allocate the space we actually need, because those
1725
// methods would default-construct the new elements.
1726
//
1727
// Could we use SetCapacity to reserve the necessary buffer space without
1728
// default-constructing all the Glyph records? No, because of a failure
1729
// that could occur when we need to grow the buffer, which happens when we
1730
// encounter a DetailedGlyph in the textrun that refers to a sequence of
1731
// several real glyphs. At that point, we need to add some extra capacity
1732
// to the buffer we initially allocated based on the length of the textrun
1733
// range we're rendering.
1734
//
1735
// This buffer growth would work fine as long as it still fits within the
1736
// array's inline buffer (we just use a bit more of it), or if the buffer
1737
// was already heap-allocated (in which case AutoTArray will use realloc(),
1738
// preserving its contents). But a problem will arise when the initial
1739
// capacity we allocated (based on the length of the run) fits within the
1740
// array's inline buffer, but subsequently we need to extend the buffer
1741
// beyond the inline buffer size, so we reallocate to the heap. Because we
1742
// haven't "officially" filled the array with SetLength or AppendElements,
1743
// its mLength is still zero; as far as it's concerned the buffer is just
1744
// uninitialized space, and when it switches to use a malloc'd buffer it
1745
// won't copy the existing contents.
1746
1747
// Allocate space for a buffer of Glyph records, without initializing them.
1748
AlignedStorage2<Glyph[AUTO_BUFFER_SIZE]> mAutoBuffer;
1749
1750
// Pointer to the buffer we're currently using -- initially mAutoBuffer,
1751
// but may be changed to a malloc'd buffer, in which case that buffer must
1752
// be free'd on destruction.
1753
Glyph* mBuffer;
1754
1755
uint32_t mBufSize; // size of allocated buffer; capacity can grow to
1756
// this before reallocation is needed
1757
uint32_t mCapacity; // amount of buffer size reserved
1758
uint32_t mNumGlyphs; // number of glyphs actually present in the buffer
1759
1760
#undef AUTO_BUFFER_SIZE
1761
};
1762
1763
// Bug 674909. When synthetic bolding text by drawing twice, need to
1764
// render using a pixel offset in device pixels, otherwise text