Bug 1799258 - Fix constexpr issue on base toolchain builds. r=gfx-reviewers,lsalzman
[gecko.git] / gfx / thebes / gfxDWriteFonts.cpp
bloba11c91d648aa74f4a7c2978e4c4ee4b911a030fd
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/. */
6 #include "gfxDWriteFonts.h"
8 #include <algorithm>
9 #include "gfxDWriteFontList.h"
10 #include "gfxContext.h"
11 #include "gfxHarfBuzzShaper.h"
12 #include "gfxTextRun.h"
13 #include "mozilla/gfx/2D.h"
14 #include "mozilla/gfx/DWriteSettings.h"
15 #include "mozilla/gfx/Logging.h"
16 #include "mozilla/gfx/gfxVars.h"
17 #include "mozilla/Preferences.h"
19 #include "harfbuzz/hb.h"
20 #include "mozilla/FontPropertyTypes.h"
22 using namespace mozilla;
23 using namespace mozilla::gfx;
25 // Code to determine whether Windows is set to use ClearType font smoothing;
26 // based on private functions in cairo-win32-font.c
28 #ifndef SPI_GETFONTSMOOTHINGTYPE
29 # define SPI_GETFONTSMOOTHINGTYPE 0x200a
30 #endif
31 #ifndef FE_FONTSMOOTHINGCLEARTYPE
32 # define FE_FONTSMOOTHINGCLEARTYPE 2
33 #endif
35 // Cleartype can be dynamically enabled/disabled, so we have to allow for
36 // dynamically updating it.
37 static BYTE GetSystemTextQuality() {
38 BOOL font_smoothing;
39 UINT smoothing_type;
41 if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
42 return DEFAULT_QUALITY;
45 if (font_smoothing) {
46 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing_type,
47 0)) {
48 return DEFAULT_QUALITY;
51 if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) {
52 return CLEARTYPE_QUALITY;
55 return ANTIALIASED_QUALITY;
58 return DEFAULT_QUALITY;
61 #ifndef SPI_GETFONTSMOOTHINGCONTRAST
62 # define SPI_GETFONTSMOOTHINGCONTRAST 0x200c
63 #endif
65 // "Retrieves a contrast value that is used in ClearType smoothing. Valid
66 // contrast values are from 1000 to 2200. The default value is 1400."
67 static FLOAT GetSystemGDIGamma() {
68 UINT value = 0;
69 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &value, 0) ||
70 value < 1000 || value > 2200) {
71 value = 1400;
73 return value / 1000.0f;
76 ////////////////////////////////////////////////////////////////////////////////
77 // gfxDWriteFont
78 gfxDWriteFont::gfxDWriteFont(const RefPtr<UnscaledFontDWrite>& aUnscaledFont,
79 gfxFontEntry* aFontEntry,
80 const gfxFontStyle* aFontStyle,
81 RefPtr<IDWriteFontFace> aFontFace,
82 AntialiasOption anAAOption)
83 : gfxFont(aUnscaledFont, aFontEntry, aFontStyle, anAAOption),
84 mFontFace(aFontFace ? aFontFace : aUnscaledFont->GetFontFace()),
85 mUseSubpixelPositions(false),
86 mAllowManualShowGlyphs(true),
87 mAzureScaledFontUsedClearType(false) {
88 // If the IDWriteFontFace1 interface is available, we can use that for
89 // faster glyph width retrieval.
90 mFontFace->QueryInterface(__uuidof(IDWriteFontFace1),
91 (void**)getter_AddRefs(mFontFace1));
92 // If a fake-bold effect is needed, determine whether we're using DWrite's
93 // "simulation" or applying our multi-strike "synthetic bold".
94 if (aFontStyle->NeedsSyntheticBold(aFontEntry)) {
95 switch (StaticPrefs::gfx_font_rendering_directwrite_bold_simulation()) {
96 case 0: // never use the DWrite simulation
97 mApplySyntheticBold = true;
98 break;
99 case 1: // use DWrite simulation for installed fonts but not webfonts
100 mApplySyntheticBold = aFontEntry->mIsDataUserFont;
101 break;
102 default: // always use DWrite bold simulation
103 // the flag is initialized to false in gfxFont
104 break;
107 ComputeMetrics(anAAOption);
110 gfxDWriteFont::~gfxDWriteFont() {
111 if (auto* scaledFont = mAzureScaledFontGDI.exchange(nullptr)) {
112 scaledFont->Release();
116 /* static */
117 bool gfxDWriteFont::InitDWriteSupport() {
118 if (!Factory::EnsureDWriteFactory()) {
119 return false;
122 if (XRE_IsParentProcess()) {
123 UpdateSystemTextVars();
124 } else {
125 // UpdateClearTypeVars doesn't update the vars in non parent processes, but
126 // it does set sForceGDIClassicEnabled so we still need to call it.
127 UpdateClearTypeVars();
129 DWriteSettings::Initialize();
131 return true;
134 /* static */
135 void gfxDWriteFont::UpdateSystemTextVars() {
136 MOZ_ASSERT(XRE_IsParentProcess());
138 BYTE newQuality = GetSystemTextQuality();
139 if (gfxVars::SystemTextQuality() != newQuality) {
140 gfxVars::SetSystemTextQuality(newQuality);
143 FLOAT newGDIGamma = GetSystemGDIGamma();
144 if (gfxVars::SystemGDIGamma() != newGDIGamma) {
145 gfxVars::SetSystemGDIGamma(newGDIGamma);
148 UpdateClearTypeVars();
151 void gfxDWriteFont::SystemTextQualityChanged() {
152 // If ClearType status has changed, update our value,
153 Factory::SetSystemTextQuality(gfxVars::SystemTextQuality());
154 // flush cached stuff that depended on the old setting, and force
155 // reflow everywhere to ensure we are using correct glyph metrics.
156 gfxPlatform::FlushFontAndWordCaches();
157 gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::No);
160 mozilla::Atomic<bool> gfxDWriteFont::sForceGDIClassicEnabled{true};
162 /* static */
163 void gfxDWriteFont::UpdateClearTypeVars() {
164 // We don't force GDI classic if the cleartype rendering mode pref is set to
165 // something valid.
166 int32_t renderingModePref =
167 Preferences::GetInt(GFX_CLEARTYPE_PARAMS_MODE, -1);
168 if (renderingModePref < 0 || renderingModePref > 5) {
169 renderingModePref = -1;
171 sForceGDIClassicEnabled = (renderingModePref == -1);
173 if (!XRE_IsParentProcess()) {
174 return;
177 if (!Factory::GetDWriteFactory()) {
178 return;
181 // First set sensible hard coded defaults.
182 float clearTypeLevel = 1.0f;
183 float enhancedContrast = 1.0f;
184 float gamma = 2.2f;
185 int pixelGeometry = DWRITE_PIXEL_GEOMETRY_RGB;
186 int renderingMode = DWRITE_RENDERING_MODE_DEFAULT;
188 // Override these from DWrite function if available.
189 RefPtr<IDWriteRenderingParams> defaultRenderingParams;
190 HRESULT hr = Factory::GetDWriteFactory()->CreateRenderingParams(
191 getter_AddRefs(defaultRenderingParams));
192 if (SUCCEEDED(hr) && defaultRenderingParams) {
193 clearTypeLevel = defaultRenderingParams->GetClearTypeLevel();
195 // For enhanced contrast, we only use the default if the user has set it
196 // in the registry (by using the ClearType Tuner).
197 // XXXbobowen it seems slightly odd that we do this and only for enhanced
198 // contrast, but this reproduces previous functionality from
199 // gfxWindowsPlatform::SetupClearTypeParams.
200 HKEY hKey;
201 LONG res = RegOpenKeyExW(DISPLAY1_REGISTRY_KEY, 0, KEY_READ, &hKey);
202 if (res == ERROR_SUCCESS) {
203 res = RegQueryValueExW(hKey, ENHANCED_CONTRAST_VALUE_NAME, nullptr,
204 nullptr, nullptr, nullptr);
205 if (res == ERROR_SUCCESS) {
206 enhancedContrast = defaultRenderingParams->GetEnhancedContrast();
208 RegCloseKey(hKey);
211 gamma = defaultRenderingParams->GetGamma();
212 pixelGeometry = defaultRenderingParams->GetPixelGeometry();
213 renderingMode = defaultRenderingParams->GetRenderingMode();
214 } else {
215 gfxWarning() << "Failed to create default rendering params";
218 // Finally override from prefs if valid values are set. If ClearType is
219 // turned off we just use the default params, this reproduces the previous
220 // functionality that was spread across gfxDWriteFont::GetScaledFont and
221 // gfxWindowsPlatform::SetupClearTypeParams, but it seems odd because the
222 // default params will still be the ClearType ones although we won't use the
223 // anti-alias for ClearType because of GetSystemDefaultAAMode.
224 if (gfxVars::SystemTextQuality() == CLEARTYPE_QUALITY) {
225 int32_t prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_LEVEL, -1);
226 if (prefInt >= 0 && prefInt <= 100) {
227 clearTypeLevel = float(prefInt / 100.0);
230 prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_CONTRAST, -1);
231 if (prefInt >= 0 && prefInt <= 1000) {
232 enhancedContrast = float(prefInt / 100.0);
235 prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_GAMMA, -1);
236 if (prefInt >= 1000 && prefInt <= 2200) {
237 gamma = float(prefInt / 1000.0);
240 prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_STRUCTURE, -1);
241 if (prefInt >= 0 && prefInt <= 2) {
242 pixelGeometry = prefInt;
245 // renderingModePref is retrieved and validated above.
246 if (renderingModePref != -1) {
247 renderingMode = renderingModePref;
251 if (gfxVars::SystemTextClearTypeLevel() != clearTypeLevel) {
252 gfxVars::SetSystemTextClearTypeLevel(clearTypeLevel);
255 if (gfxVars::SystemTextEnhancedContrast() != enhancedContrast) {
256 gfxVars::SetSystemTextEnhancedContrast(enhancedContrast);
259 if (gfxVars::SystemTextGamma() != gamma) {
260 gfxVars::SetSystemTextGamma(gamma);
263 if (gfxVars::SystemTextPixelGeometry() != pixelGeometry) {
264 gfxVars::SetSystemTextPixelGeometry(pixelGeometry);
267 if (gfxVars::SystemTextRenderingMode() != renderingMode) {
268 gfxVars::SetSystemTextRenderingMode(renderingMode);
271 // Set cairo dwrite params in the parent process where it might still be
272 // needed for printing. We use the validated pref int directly for rendering
273 // mode, because a negative (i.e. not set) rendering mode is also used for
274 // deciding on forcing GDI in cairo.
275 cairo_dwrite_set_cleartype_params(gamma, enhancedContrast, clearTypeLevel,
276 pixelGeometry, renderingModePref);
279 gfxFont* gfxDWriteFont::CopyWithAntialiasOption(
280 AntialiasOption anAAOption) const {
281 auto entry = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
282 RefPtr<UnscaledFontDWrite> unscaledFont =
283 static_cast<UnscaledFontDWrite*>(mUnscaledFont.get());
284 return new gfxDWriteFont(unscaledFont, entry, &mStyle, mFontFace, anAAOption);
287 bool gfxDWriteFont::GetFakeMetricsForArialBlack(
288 DWRITE_FONT_METRICS* aFontMetrics) {
289 gfxFontStyle style(mStyle);
290 style.weight = FontWeight::FromInt(700);
292 gfxFontEntry* fe = gfxPlatformFontList::PlatformFontList()->FindFontForFamily(
293 nullptr, "Arial"_ns, &style);
294 if (!fe || fe == mFontEntry) {
295 return false;
298 RefPtr<gfxFont> font = fe->FindOrMakeFont(&style);
299 gfxDWriteFont* dwFont = static_cast<gfxDWriteFont*>(font.get());
300 dwFont->mFontFace->GetMetrics(aFontMetrics);
302 return true;
305 void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
306 ::memset(&mMetrics, 0, sizeof(mMetrics));
308 DWRITE_FONT_METRICS fontMetrics;
309 if (!(mFontEntry->Weight().Min() == FontWeight::FromInt(900) &&
310 mFontEntry->Weight().Max() == FontWeight::FromInt(900) &&
311 !mFontEntry->IsUserFont() &&
312 mFontEntry->Name().EqualsLiteral("Arial Black") &&
313 GetFakeMetricsForArialBlack(&fontMetrics))) {
314 mFontFace->GetMetrics(&fontMetrics);
317 if (GetAdjustedSize() > 0.0 && mStyle.sizeAdjust >= 0.0 &&
318 FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
319 FontSizeAdjust::Tag::None) {
320 // For accurate measurement during the font-size-adjust computations;
321 // these may be reset later according to the adjusted size.
322 mUseSubpixelPositions = true;
323 mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
324 gfxFloat aspect;
325 switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) {
326 default:
327 MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
328 aspect = 0.0;
329 break;
330 case FontSizeAdjust::Tag::ExHeight:
331 aspect = (gfxFloat)fontMetrics.xHeight / fontMetrics.designUnitsPerEm;
332 break;
333 case FontSizeAdjust::Tag::CapHeight:
334 aspect = (gfxFloat)fontMetrics.capHeight / fontMetrics.designUnitsPerEm;
335 break;
336 case FontSizeAdjust::Tag::ChWidth: {
337 gfxFloat advance = GetCharAdvance('0');
338 aspect = advance > 0.0 ? advance / mAdjustedSize : 0.5;
339 break;
341 case FontSizeAdjust::Tag::IcWidth:
342 case FontSizeAdjust::Tag::IcHeight: {
343 bool vertical = FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) ==
344 FontSizeAdjust::Tag::IcHeight;
345 gfxFloat advance = GetCharAdvance(kWaterIdeograph, vertical);
346 aspect = advance > 0.0 ? advance / mAdjustedSize : 1.0;
347 break;
350 if (aspect > 0.0) {
351 // If we created a shaper above (to measure glyphs), discard it so we
352 // get a new one for the adjusted scaling.
353 delete mHarfBuzzShaper.exchange(nullptr);
354 mAdjustedSize = mStyle.GetAdjustedSize(aspect);
358 // Update now that we've adjusted the size if necessary.
359 mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
361 // Note that GetMeasuringMode depends on mAdjustedSize
362 if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType() &&
363 GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) ||
364 anAAOption == gfxFont::kAntialiasSubpixel) {
365 mUseSubpixelPositions = true;
366 // note that this may be reset to FALSE if we determine that a bitmap
367 // strike is going to be used
368 } else {
369 mUseSubpixelPositions = false;
372 gfxDWriteFontEntry* fe = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
373 if (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) {
374 mAdjustedSize = NS_lround(mAdjustedSize);
375 mUseSubpixelPositions = false;
376 // if we have bitmaps, we need to tell Cairo NOT to use subpixel AA,
377 // to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp
378 // which fails to render bitmap glyphs (see bug 626299).
379 // This option will be passed to the cairo_dwrite_scaled_font_t
380 // after creation.
381 mAllowManualShowGlyphs = false;
384 mMetrics.xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
385 mMetrics.capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
387 mMetrics.maxAscent = round(fontMetrics.ascent * mFUnitsConvFactor);
388 mMetrics.maxDescent = round(fontMetrics.descent * mFUnitsConvFactor);
389 mMetrics.maxHeight = mMetrics.maxAscent + mMetrics.maxDescent;
391 mMetrics.emHeight = mAdjustedSize;
392 mMetrics.emAscent =
393 mMetrics.emHeight * mMetrics.maxAscent / mMetrics.maxHeight;
394 mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent;
396 mMetrics.maxAdvance = mAdjustedSize;
398 // try to get the true maxAdvance value from 'hhea'
399 gfxFontEntry::AutoTable hheaTable(GetFontEntry(),
400 TRUETYPE_TAG('h', 'h', 'e', 'a'));
401 if (hheaTable) {
402 uint32_t len;
403 const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
404 hb_blob_get_data(hheaTable, &len));
405 if (len >= sizeof(MetricsHeader)) {
406 mMetrics.maxAdvance = uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
410 mMetrics.internalLeading =
411 std::max(mMetrics.maxHeight - mMetrics.emHeight, 0.0);
412 mMetrics.externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
414 UINT32 ucs = L' ';
415 UINT16 glyph;
416 if (SUCCEEDED(mFontFace->GetGlyphIndices(&ucs, 1, &glyph)) && glyph != 0) {
417 mSpaceGlyph = glyph;
418 mMetrics.spaceWidth = MeasureGlyphWidth(glyph);
419 } else {
420 mMetrics.spaceWidth = 0;
423 // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
424 // if the table is not available or if using hinted/pixel-snapped widths
425 if (mUseSubpixelPositions) {
426 mMetrics.aveCharWidth = 0;
427 gfxFontEntry::AutoTable os2Table(GetFontEntry(),
428 TRUETYPE_TAG('O', 'S', '/', '2'));
429 if (os2Table) {
430 uint32_t len;
431 const OS2Table* os2 =
432 reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
433 if (len >= 4) {
434 // Not checking against sizeof(mozilla::OS2Table) here because older
435 // versions of the table have different sizes; we only need the first
436 // two 16-bit fields here.
437 mMetrics.aveCharWidth = int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
442 if (mMetrics.aveCharWidth < 1) {
443 mMetrics.aveCharWidth = GetCharAdvance('x');
444 if (mMetrics.aveCharWidth < 1) {
445 // Let's just assume the X is square.
446 mMetrics.aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
450 mMetrics.zeroWidth = GetCharAdvance('0');
452 mMetrics.ideographicWidth = GetCharAdvance(kWaterIdeograph);
454 mMetrics.underlineOffset = fontMetrics.underlinePosition * mFUnitsConvFactor;
455 mMetrics.underlineSize = fontMetrics.underlineThickness * mFUnitsConvFactor;
456 mMetrics.strikeoutOffset =
457 fontMetrics.strikethroughPosition * mFUnitsConvFactor;
458 mMetrics.strikeoutSize =
459 fontMetrics.strikethroughThickness * mFUnitsConvFactor;
461 SanitizeMetrics(&mMetrics, GetFontEntry()->mIsBadUnderlineFont);
463 if (ApplySyntheticBold()) {
464 auto delta = GetSyntheticBoldOffset();
465 mMetrics.spaceWidth += delta;
466 mMetrics.aveCharWidth += delta;
467 mMetrics.maxAdvance += delta;
468 if (mMetrics.zeroWidth > 0) {
469 mMetrics.zeroWidth += delta;
471 if (mMetrics.ideographicWidth > 0) {
472 mMetrics.ideographicWidth += delta;
476 #if 0
477 printf("Font: %p (%s) size: %f\n", this,
478 NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
479 printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
480 printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
481 printf(" internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
482 printf(" spaceWidth: %f aveCharWidth: %f zeroWidth: %f\n",
483 mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.zeroWidth);
484 printf(" xHeight: %f capHeight: %f\n", mMetrics.xHeight, mMetrics.capHeight);
485 printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
486 mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
487 #endif
490 using namespace mozilla; // for AutoSwap_* types
492 struct EBLCHeader {
493 AutoSwap_PRUint32 version;
494 AutoSwap_PRUint32 numSizes;
497 struct SbitLineMetrics {
498 int8_t ascender;
499 int8_t descender;
500 uint8_t widthMax;
501 int8_t caretSlopeNumerator;
502 int8_t caretSlopeDenominator;
503 int8_t caretOffset;
504 int8_t minOriginSB;
505 int8_t minAdvanceSB;
506 int8_t maxBeforeBL;
507 int8_t minAfterBL;
508 int8_t pad1;
509 int8_t pad2;
512 struct BitmapSizeTable {
513 AutoSwap_PRUint32 indexSubTableArrayOffset;
514 AutoSwap_PRUint32 indexTablesSize;
515 AutoSwap_PRUint32 numberOfIndexSubTables;
516 AutoSwap_PRUint32 colorRef;
517 SbitLineMetrics hori;
518 SbitLineMetrics vert;
519 AutoSwap_PRUint16 startGlyphIndex;
520 AutoSwap_PRUint16 endGlyphIndex;
521 uint8_t ppemX;
522 uint8_t ppemY;
523 uint8_t bitDepth;
524 uint8_t flags;
527 typedef EBLCHeader EBSCHeader;
529 struct BitmapScaleTable {
530 SbitLineMetrics hori;
531 SbitLineMetrics vert;
532 uint8_t ppemX;
533 uint8_t ppemY;
534 uint8_t substitutePpemX;
535 uint8_t substitutePpemY;
538 bool gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize) {
539 uint8_t* tableData;
540 uint32_t len;
541 void* tableContext;
542 BOOL exists;
543 HRESULT hr = mFontFace->TryGetFontTable(
544 DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'), (const void**)&tableData,
545 &len, &tableContext, &exists);
546 if (FAILED(hr)) {
547 return false;
550 bool hasStrike = false;
551 // not really a loop, but this lets us use 'break' to skip out of the block
552 // as soon as we know the answer, and skips it altogether if the table is
553 // not present
554 while (exists) {
555 if (len < sizeof(EBLCHeader)) {
556 break;
558 const EBLCHeader* hdr = reinterpret_cast<const EBLCHeader*>(tableData);
559 if (hdr->version != 0x00020000) {
560 break;
562 uint32_t numSizes = hdr->numSizes;
563 if (numSizes > 0xffff) { // sanity-check, prevent overflow below
564 break;
566 if (len < sizeof(EBLCHeader) + numSizes * sizeof(BitmapSizeTable)) {
567 break;
569 const BitmapSizeTable* sizeTable =
570 reinterpret_cast<const BitmapSizeTable*>(hdr + 1);
571 for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
572 if (sizeTable->ppemX == aSize && sizeTable->ppemY == aSize) {
573 // we ignore a strike that contains fewer than 4 glyphs,
574 // as that probably indicates a font such as Courier New
575 // that provides bitmaps ONLY for the "shading" characters
576 // U+2591..2593
577 hasStrike = (uint16_t(sizeTable->endGlyphIndex) >=
578 uint16_t(sizeTable->startGlyphIndex) + 3);
579 break;
582 // if we reach here, we didn't find a strike; unconditionally break
583 // out of the while-loop block
584 break;
586 mFontFace->ReleaseFontTable(tableContext);
588 if (hasStrike) {
589 return true;
592 // if we didn't find a real strike, check if the font calls for scaling
593 // another bitmap to this size
594 hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'),
595 (const void**)&tableData, &len, &tableContext,
596 &exists);
597 if (FAILED(hr)) {
598 return false;
601 while (exists) {
602 if (len < sizeof(EBSCHeader)) {
603 break;
605 const EBSCHeader* hdr = reinterpret_cast<const EBSCHeader*>(tableData);
606 if (hdr->version != 0x00020000) {
607 break;
609 uint32_t numSizes = hdr->numSizes;
610 if (numSizes > 0xffff) {
611 break;
613 if (len < sizeof(EBSCHeader) + numSizes * sizeof(BitmapScaleTable)) {
614 break;
616 const BitmapScaleTable* scaleTable =
617 reinterpret_cast<const BitmapScaleTable*>(hdr + 1);
618 for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
619 if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) {
620 hasStrike = true;
621 break;
624 break;
626 mFontFace->ReleaseFontTable(tableContext);
628 return hasStrike;
631 bool gfxDWriteFont::IsValid() const { return mFontFace != nullptr; }
633 IDWriteFontFace* gfxDWriteFont::GetFontFace() { return mFontFace.get(); }
635 gfxFont::RunMetrics gfxDWriteFont::Measure(const gfxTextRun* aTextRun,
636 uint32_t aStart, uint32_t aEnd,
637 BoundingBoxType aBoundingBoxType,
638 DrawTarget* aRefDrawTarget,
639 Spacing* aSpacing,
640 gfx::ShapedTextFlags aOrientation) {
641 gfxFont::RunMetrics metrics =
642 gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType, aRefDrawTarget,
643 aSpacing, aOrientation);
645 // if aBoundingBoxType is LOOSE_INK_EXTENTS
646 // and the underlying cairo font may be antialiased,
647 // we can't trust Windows to have considered all the pixels
648 // so we need to add "padding" to the bounds.
649 // (see bugs 475968, 439831, compare also bug 445087)
650 if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
651 mAntialiasOption != kAntialiasNone &&
652 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC &&
653 metrics.mBoundingBox.Width() > 0) {
654 metrics.mBoundingBox.MoveByX(-aTextRun->GetAppUnitsPerDevUnit());
655 metrics.mBoundingBox.SetWidth(metrics.mBoundingBox.Width() +
656 aTextRun->GetAppUnitsPerDevUnit() * 3);
659 return metrics;
662 bool gfxDWriteFont::ProvidesGlyphWidths() const {
663 return !mUseSubpixelPositions ||
664 (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD) ||
665 ((gfxDWriteFontEntry*)(GetFontEntry()))->HasVariations();
668 int32_t gfxDWriteFont::GetGlyphWidth(uint16_t aGID) {
669 if (!mGlyphWidths) {
670 mGlyphWidths = MakeUnique<nsTHashMap<nsUint32HashKey, int32_t>>(128);
673 return mGlyphWidths->LookupOrInsertWith(
674 aGID, [&] { return NS_lround(MeasureGlyphWidth(aGID) * 65536.0); });
677 bool gfxDWriteFont::GetForceGDIClassic() const {
678 return sForceGDIClassicEnabled &&
679 static_cast<gfxDWriteFontEntry*>(mFontEntry.get())
680 ->GetForceGDIClassic() &&
681 GetAdjustedSize() <= gfxDWriteFontList::PlatformFontList()
682 ->GetForceGDIClassicMaxFontSize();
685 DWRITE_MEASURING_MODE
686 gfxDWriteFont::GetMeasuringMode() const {
687 return DWriteSettings::Get(GetForceGDIClassic()).MeasuringMode();
690 gfxFloat gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph) {
691 MOZ_SEH_TRY {
692 HRESULT hr;
693 if (mFontFace1) {
694 int32_t advance;
695 if (mUseSubpixelPositions) {
696 hr = mFontFace1->GetDesignGlyphAdvances(1, &aGlyph, &advance, FALSE);
697 if (SUCCEEDED(hr)) {
698 return advance * mFUnitsConvFactor;
700 } else {
701 hr = mFontFace1->GetGdiCompatibleGlyphAdvances(
702 FLOAT(mAdjustedSize), 1.0f, nullptr,
703 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL, FALSE, 1,
704 &aGlyph, &advance);
705 if (SUCCEEDED(hr)) {
706 return NS_lround(advance * mFUnitsConvFactor);
709 } else {
710 DWRITE_GLYPH_METRICS metrics;
711 if (mUseSubpixelPositions) {
712 hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE);
713 if (SUCCEEDED(hr)) {
714 return metrics.advanceWidth * mFUnitsConvFactor;
716 } else {
717 hr = mFontFace->GetGdiCompatibleGlyphMetrics(
718 FLOAT(mAdjustedSize), 1.0f, nullptr,
719 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL, &aGlyph, 1,
720 &metrics, FALSE);
721 if (SUCCEEDED(hr)) {
722 return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
727 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
728 // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
729 // the font resource; possibly a failing drive or similar hardware issue.
730 // Mark the font as invalid, and wipe the fontEntry's charmap so that font
731 // selection will skip it; we'll use a fallback font instead.
732 mIsValid = false;
733 GetFontEntry()->mCharacterMap = new gfxCharacterMap();
734 GetFontEntry()->mShmemCharacterMap = nullptr;
735 gfxCriticalError() << "Exception occurred measuring glyph width for "
736 << GetFontEntry()->Name().get();
738 return 0.0;
741 bool gfxDWriteFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
742 bool aTight) {
743 MOZ_SEH_TRY {
744 DWRITE_GLYPH_METRICS m;
745 HRESULT hr = mFontFace->GetDesignGlyphMetrics(&aGID, 1, &m, FALSE);
746 if (FAILED(hr)) {
747 return false;
749 gfxRect bounds(m.leftSideBearing, m.topSideBearing - m.verticalOriginY,
750 m.advanceWidth - m.leftSideBearing - m.rightSideBearing,
751 m.advanceHeight - m.topSideBearing - m.bottomSideBearing);
752 bounds.Scale(mFUnitsConvFactor);
753 // GetDesignGlyphMetrics returns 'ideal' glyph metrics, we need to pad to
754 // account for antialiasing.
755 if (!aTight && !aBounds->IsEmpty()) {
756 bounds.Inflate(1.0, 0.0);
758 *aBounds = bounds;
759 return true;
761 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
762 // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
763 // the font resource; possibly a failing drive or similar hardware issue.
764 // Mark the font as invalid, and wipe the fontEntry's charmap so that font
765 // selection will skip it; we'll use a fallback font instead.
766 mIsValid = false;
767 GetFontEntry()->mCharacterMap = new gfxCharacterMap();
768 GetFontEntry()->mShmemCharacterMap = nullptr;
769 gfxCriticalError() << "Exception occurred measuring glyph bounds for "
770 << GetFontEntry()->Name().get();
772 return false;
775 void gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
776 FontCacheSizes* aSizes) const {
777 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
778 if (mGlyphWidths) {
779 aSizes->mFontInstances +=
780 mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
784 void gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
785 FontCacheSizes* aSizes) const {
786 aSizes->mFontInstances += aMallocSizeOf(this);
787 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
790 already_AddRefed<ScaledFont> gfxDWriteFont::GetScaledFont(
791 const TextRunDrawParams& aRunParams) {
792 bool useClearType = UsingClearType();
793 if (mAzureScaledFontUsedClearType != useClearType) {
794 if (auto* oldScaledFont = mAzureScaledFont.exchange(nullptr)) {
795 oldScaledFont->Release();
797 if (auto* oldScaledFont = mAzureScaledFontGDI.exchange(nullptr)) {
798 oldScaledFont->Release();
801 bool forceGDI = aRunParams.allowGDI && GetForceGDIClassic();
802 ScaledFont* scaledFont = forceGDI ? mAzureScaledFontGDI : mAzureScaledFont;
803 if (scaledFont) {
804 return do_AddRef(scaledFont);
807 gfxDWriteFontEntry* fe = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
808 bool useEmbeddedBitmap =
809 (gfxVars::SystemTextRenderingMode() == DWRITE_RENDERING_MODE_DEFAULT ||
810 forceGDI) &&
811 fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize));
813 const gfxFontStyle* fontStyle = GetStyle();
814 RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForDWriteFont(
815 mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
816 useEmbeddedBitmap, ApplySyntheticBold(), forceGDI);
817 if (!newScaledFont) {
818 return nullptr;
820 InitializeScaledFont(newScaledFont);
822 if (forceGDI) {
823 if (mAzureScaledFontGDI.compareExchange(nullptr, newScaledFont.get())) {
824 Unused << newScaledFont.forget();
825 mAzureScaledFontUsedClearType = useClearType;
827 scaledFont = mAzureScaledFontGDI;
828 } else {
829 if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
830 Unused << newScaledFont.forget();
831 mAzureScaledFontUsedClearType = useClearType;
833 scaledFont = mAzureScaledFont;
835 return do_AddRef(scaledFont);
838 bool gfxDWriteFont::ShouldRoundXOffset(cairo_t* aCairo) const {
839 // show_glyphs is implemented on the font and so is used for all Cairo
840 // surface types; however, it may pixel-snap depending on the dwrite
841 // rendering mode
842 return GetMeasuringMode() != DWRITE_MEASURING_MODE_NATURAL;