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