gecko-dev/gfx/thebes/gfxDWriteFonts.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

844 строки
29 KiB
C++
Исходник Обычный вид История

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2012-05-21 15:12:37 +04:00
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxDWriteFonts.h"
#include <algorithm>
#include "gfxDWriteFontList.h"
#include "gfxContext.h"
#include "gfxHarfBuzzShaper.h"
#include "gfxTextRun.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DWriteSettings.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/Preferences.h"
#include "harfbuzz/hb.h"
#include "mozilla/FontPropertyTypes.h"
using namespace mozilla;
using namespace mozilla::gfx;
// Code to determine whether Windows is set to use ClearType font smoothing;
// based on private functions in cairo-win32-font.c
#ifndef SPI_GETFONTSMOOTHINGTYPE
# define SPI_GETFONTSMOOTHINGTYPE 0x200a
#endif
#ifndef FE_FONTSMOOTHINGCLEARTYPE
# define FE_FONTSMOOTHINGCLEARTYPE 2
#endif
// Cleartype can be dynamically enabled/disabled, so we have to allow for
// dynamically updating it.
static BYTE GetSystemTextQuality() {
BOOL font_smoothing;
UINT smoothing_type;
if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
return DEFAULT_QUALITY;
}
if (font_smoothing) {
if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing_type,
0)) {
return DEFAULT_QUALITY;
}
if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) {
return CLEARTYPE_QUALITY;
}
return ANTIALIASED_QUALITY;
}
return DEFAULT_QUALITY;
}
#ifndef SPI_GETFONTSMOOTHINGCONTRAST
# define SPI_GETFONTSMOOTHINGCONTRAST 0x200c
#endif
// "Retrieves a contrast value that is used in ClearType smoothing. Valid
// contrast values are from 1000 to 2200. The default value is 1400."
static FLOAT GetSystemGDIGamma() {
UINT value = 0;
if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &value, 0) ||
value < 1000 || value > 2200) {
value = 1400;
}
return value / 1000.0f;
}
////////////////////////////////////////////////////////////////////////////////
// gfxDWriteFont
gfxDWriteFont::gfxDWriteFont(const RefPtr<UnscaledFontDWrite>& aUnscaledFont,
gfxFontEntry* aFontEntry,
const gfxFontStyle* aFontStyle,
RefPtr<IDWriteFontFace> aFontFace,
AntialiasOption anAAOption)
: gfxFont(aUnscaledFont, aFontEntry, aFontStyle, anAAOption),
mFontFace(aFontFace ? aFontFace : aUnscaledFont->GetFontFace()),
mUseSubpixelPositions(false),
mAllowManualShowGlyphs(true),
mAzureScaledFontUsedClearType(false) {
// If the IDWriteFontFace1 interface is available, we can use that for
// faster glyph width retrieval.
mFontFace->QueryInterface(__uuidof(IDWriteFontFace1),
(void**)getter_AddRefs(mFontFace1));
// If a fake-bold effect is needed, determine whether we're using DWrite's
// "simulation" or applying our multi-strike "synthetic bold".
if (aFontStyle->NeedsSyntheticBold(aFontEntry)) {
switch (StaticPrefs::gfx_font_rendering_directwrite_bold_simulation()) {
case 0: // never use the DWrite simulation
mApplySyntheticBold = true;
break;
case 1: // use DWrite simulation for installed fonts but not webfonts
mApplySyntheticBold = aFontEntry->mIsDataUserFont;
break;
default: // always use DWrite bold simulation
// the flag is initialized to false in gfxFont
break;
}
}
ComputeMetrics(anAAOption);
}
gfxDWriteFont::~gfxDWriteFont() {
if (auto* scaledFont = mAzureScaledFontGDI.exchange(nullptr)) {
scaledFont->Release();
}
}
/* static */
bool gfxDWriteFont::InitDWriteSupport() {
if (!Factory::EnsureDWriteFactory()) {
return false;
}
if (XRE_IsParentProcess()) {
UpdateSystemTextVars();
} else {
// UpdateClearTypeVars doesn't update the vars in non parent processes, but
// it does set sForceGDIClassicEnabled so we still need to call it.
UpdateClearTypeVars();
}
DWriteSettings::Initialize();
return true;
}
/* static */
void gfxDWriteFont::UpdateSystemTextVars() {
MOZ_ASSERT(XRE_IsParentProcess());
BYTE newQuality = GetSystemTextQuality();
if (gfxVars::SystemTextQuality() != newQuality) {
gfxVars::SetSystemTextQuality(newQuality);
}
FLOAT newGDIGamma = GetSystemGDIGamma();
if (gfxVars::SystemGDIGamma() != newGDIGamma) {
gfxVars::SetSystemGDIGamma(newGDIGamma);
}
UpdateClearTypeVars();
}
void gfxDWriteFont::SystemTextQualityChanged() {
// If ClearType status has changed, update our value,
Factory::SetSystemTextQuality(gfxVars::SystemTextQuality());
// flush cached stuff that depended on the old setting, and force
// reflow everywhere to ensure we are using correct glyph metrics.
gfxPlatform::FlushFontAndWordCaches();
gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::No);
}
mozilla::Atomic<bool> gfxDWriteFont::sForceGDIClassicEnabled{true};
/* static */
void gfxDWriteFont::UpdateClearTypeVars() {
// We don't force GDI classic if the cleartype rendering mode pref is set to
// something valid.
int32_t renderingModePref =
Preferences::GetInt(GFX_CLEARTYPE_PARAMS_MODE, -1);
if (renderingModePref < 0 || renderingModePref > 5) {
renderingModePref = -1;
}
sForceGDIClassicEnabled = (renderingModePref == -1);
if (!XRE_IsParentProcess()) {
return;
}
if (!Factory::GetDWriteFactory()) {
return;
}
// First set sensible hard coded defaults.
float clearTypeLevel = 1.0f;
float enhancedContrast = 1.0f;
float gamma = 2.2f;
int pixelGeometry = DWRITE_PIXEL_GEOMETRY_RGB;
int renderingMode = DWRITE_RENDERING_MODE_DEFAULT;
// Override these from DWrite function if available.
RefPtr<IDWriteRenderingParams> defaultRenderingParams;
HRESULT hr = Factory::GetDWriteFactory()->CreateRenderingParams(
getter_AddRefs(defaultRenderingParams));
if (SUCCEEDED(hr) && defaultRenderingParams) {
clearTypeLevel = defaultRenderingParams->GetClearTypeLevel();
// For enhanced contrast, we only use the default if the user has set it
// in the registry (by using the ClearType Tuner).
// XXXbobowen it seems slightly odd that we do this and only for enhanced
// contrast, but this reproduces previous functionality from
// gfxWindowsPlatform::SetupClearTypeParams.
HKEY hKey;
LONG res = RegOpenKeyExW(DISPLAY1_REGISTRY_KEY, 0, KEY_READ, &hKey);
if (res == ERROR_SUCCESS) {
res = RegQueryValueExW(hKey, ENHANCED_CONTRAST_VALUE_NAME, nullptr,
nullptr, nullptr, nullptr);
if (res == ERROR_SUCCESS) {
enhancedContrast = defaultRenderingParams->GetEnhancedContrast();
}
RegCloseKey(hKey);
}
gamma = defaultRenderingParams->GetGamma();
pixelGeometry = defaultRenderingParams->GetPixelGeometry();
renderingMode = defaultRenderingParams->GetRenderingMode();
} else {
gfxWarning() << "Failed to create default rendering params";
}
// Finally override from prefs if valid values are set. If ClearType is
// turned off we just use the default params, this reproduces the previous
// functionality that was spread across gfxDWriteFont::GetScaledFont and
// gfxWindowsPlatform::SetupClearTypeParams, but it seems odd because the
// default params will still be the ClearType ones although we won't use the
// anti-alias for ClearType because of GetSystemDefaultAAMode.
if (gfxVars::SystemTextQuality() == CLEARTYPE_QUALITY) {
int32_t prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_LEVEL, -1);
if (prefInt >= 0 && prefInt <= 100) {
clearTypeLevel = float(prefInt / 100.0);
}
prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_CONTRAST, -1);
if (prefInt >= 0 && prefInt <= 1000) {
enhancedContrast = float(prefInt / 100.0);
}
prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_GAMMA, -1);
if (prefInt >= 1000 && prefInt <= 2200) {
gamma = float(prefInt / 1000.0);
}
prefInt = Preferences::GetInt(GFX_CLEARTYPE_PARAMS_STRUCTURE, -1);
if (prefInt >= 0 && prefInt <= 2) {
pixelGeometry = prefInt;
}
// renderingModePref is retrieved and validated above.
if (renderingModePref != -1) {
renderingMode = renderingModePref;
}
}
if (gfxVars::SystemTextClearTypeLevel() != clearTypeLevel) {
gfxVars::SetSystemTextClearTypeLevel(clearTypeLevel);
}
if (gfxVars::SystemTextEnhancedContrast() != enhancedContrast) {
gfxVars::SetSystemTextEnhancedContrast(enhancedContrast);
}
if (gfxVars::SystemTextGamma() != gamma) {
gfxVars::SetSystemTextGamma(gamma);
}
if (gfxVars::SystemTextPixelGeometry() != pixelGeometry) {
gfxVars::SetSystemTextPixelGeometry(pixelGeometry);
}
if (gfxVars::SystemTextRenderingMode() != renderingMode) {
gfxVars::SetSystemTextRenderingMode(renderingMode);
}
// Set cairo dwrite params in the parent process where it might still be
// needed for printing. We use the validated pref int directly for rendering
// mode, because a negative (i.e. not set) rendering mode is also used for
// deciding on forcing GDI in cairo.
cairo_dwrite_set_cleartype_params(gamma, enhancedContrast, clearTypeLevel,
pixelGeometry, renderingModePref);
}
gfxFont* gfxDWriteFont::CopyWithAntialiasOption(
AntialiasOption anAAOption) const {
auto entry = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
RefPtr<UnscaledFontDWrite> unscaledFont =
static_cast<UnscaledFontDWrite*>(mUnscaledFont.get());
return new gfxDWriteFont(unscaledFont, entry, &mStyle, mFontFace, anAAOption);
}
bool gfxDWriteFont::GetFakeMetricsForArialBlack(
DWRITE_FONT_METRICS* aFontMetrics) {
gfxFontStyle style(mStyle);
style.weight = FontWeight::FromInt(700);
Bug 1449605 - part 1 - Rearrange thebes font code so that the decision whether to apply synthetic-bold is deferred until actually instantiating a font, not made during the font-matching process. r=jwatt This rearranges how synthetic-bold use is determined in the font selection & rendering code. Previously, we would decide during the font-selection algorithm whether we need to apply synthetic-bold to the chosen face, and then pass that decision through the fontgroup (storing it in the FamilyFace entries of the mFonts array there) down to the actual rendering code that instantiates fonts from the faces (font entries) we've selected. That became a problem for variation fonts because in the case of a user font, we may not have downloaded the resource yet, so we just have a "user font container" entry, which carries the descriptors from the @font-face rule and will fetch the actual resource when needed. But in the case of a @font-face rule without a weight descriptor, we don't actually know at font-selection time whether the face will support "true" bold (via a variation axis) or not, so we can't reliably make the right decision about applying synthetic bold. So we now defer that decision until we actually instantiate a platform font object to shape/measure/draw text. At that point, we have the requested style and we also have the real font resource, so we can easily determine whether fake-bold is required. (This patch should not result in any visible behavior change; that will come in a second patch now that the architecture supports it.)
2018-05-01 12:30:50 +03:00
gfxFontEntry* fe = gfxPlatformFontList::PlatformFontList()->FindFontForFamily(
nullptr, "Arial"_ns, &style);
if (!fe || fe == mFontEntry) {
return false;
}
Bug 1449605 - part 1 - Rearrange thebes font code so that the decision whether to apply synthetic-bold is deferred until actually instantiating a font, not made during the font-matching process. r=jwatt This rearranges how synthetic-bold use is determined in the font selection & rendering code. Previously, we would decide during the font-selection algorithm whether we need to apply synthetic-bold to the chosen face, and then pass that decision through the fontgroup (storing it in the FamilyFace entries of the mFonts array there) down to the actual rendering code that instantiates fonts from the faces (font entries) we've selected. That became a problem for variation fonts because in the case of a user font, we may not have downloaded the resource yet, so we just have a "user font container" entry, which carries the descriptors from the @font-face rule and will fetch the actual resource when needed. But in the case of a @font-face rule without a weight descriptor, we don't actually know at font-selection time whether the face will support "true" bold (via a variation axis) or not, so we can't reliably make the right decision about applying synthetic bold. So we now defer that decision until we actually instantiate a platform font object to shape/measure/draw text. At that point, we have the requested style and we also have the real font resource, so we can easily determine whether fake-bold is required. (This patch should not result in any visible behavior change; that will come in a second patch now that the architecture supports it.)
2018-05-01 12:30:50 +03:00
RefPtr<gfxFont> font = fe->FindOrMakeFont(&style);
gfxDWriteFont* dwFont = static_cast<gfxDWriteFont*>(font.get());
dwFont->mFontFace->GetMetrics(aFontMetrics);
return true;
}
void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
::memset(&mMetrics, 0, sizeof(mMetrics));
DWRITE_FONT_METRICS fontMetrics;
if (!(mFontEntry->Weight().Min() == FontWeight::FromInt(900) &&
mFontEntry->Weight().Max() == FontWeight::FromInt(900) &&
!mFontEntry->IsUserFont() &&
mFontEntry->Name().EqualsLiteral("Arial Black") &&
GetFakeMetricsForArialBlack(&fontMetrics))) {
mFontFace->GetMetrics(&fontMetrics);
}
if (GetAdjustedSize() > 0.0 && mStyle.sizeAdjust >= 0.0 &&
FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
FontSizeAdjust::Tag::None) {
// For accurate measurement during the font-size-adjust computations;
// these may be reset later according to the adjusted size.
mUseSubpixelPositions = true;
mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
gfxFloat aspect;
switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) {
default:
MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
aspect = 0.0;
break;
case FontSizeAdjust::Tag::ExHeight:
aspect = (gfxFloat)fontMetrics.xHeight / fontMetrics.designUnitsPerEm;
break;
case FontSizeAdjust::Tag::CapHeight:
aspect = (gfxFloat)fontMetrics.capHeight / fontMetrics.designUnitsPerEm;
break;
case FontSizeAdjust::Tag::ChWidth: {
gfxFloat advance = GetCharAdvance('0');
aspect = advance > 0.0 ? advance / mAdjustedSize : 0.5;
break;
}
case FontSizeAdjust::Tag::IcWidth:
case FontSizeAdjust::Tag::IcHeight: {
bool vertical = FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) ==
FontSizeAdjust::Tag::IcHeight;
gfxFloat advance = GetCharAdvance(kWaterIdeograph, vertical);
aspect = advance > 0.0 ? advance / mAdjustedSize : 1.0;
break;
}
}
if (aspect > 0.0) {
// If we created a shaper above (to measure glyphs), discard it so we
// get a new one for the adjusted scaling.
delete mHarfBuzzShaper.exchange(nullptr);
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
}
}
// Update now that we've adjusted the size if necessary.
mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
// Note that GetMeasuringMode depends on mAdjustedSize
if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType() &&
GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) ||
anAAOption == gfxFont::kAntialiasSubpixel) {
mUseSubpixelPositions = true;
// note that this may be reset to FALSE if we determine that a bitmap
// strike is going to be used
} else {
mUseSubpixelPositions = false;
}
gfxDWriteFontEntry* fe = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
if (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) {
mAdjustedSize = NS_lround(mAdjustedSize);
mUseSubpixelPositions = false;
// if we have bitmaps, we need to tell Cairo NOT to use subpixel AA,
// to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp
// which fails to render bitmap glyphs (see bug 626299).
// This option will be passed to the cairo_dwrite_scaled_font_t
// after creation.
mAllowManualShowGlyphs = false;
}
mMetrics.xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
mMetrics.capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
mMetrics.maxAscent = round(fontMetrics.ascent * mFUnitsConvFactor);
mMetrics.maxDescent = round(fontMetrics.descent * mFUnitsConvFactor);
mMetrics.maxHeight = mMetrics.maxAscent + mMetrics.maxDescent;
mMetrics.emHeight = mAdjustedSize;
mMetrics.emAscent =
mMetrics.emHeight * mMetrics.maxAscent / mMetrics.maxHeight;
mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent;
mMetrics.maxAdvance = mAdjustedSize;
// try to get the true maxAdvance value from 'hhea'
gfxFontEntry::AutoTable hheaTable(GetFontEntry(),
TRUETYPE_TAG('h', 'h', 'e', 'a'));
if (hheaTable) {
uint32_t len;
const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
hb_blob_get_data(hheaTable, &len));
if (len >= sizeof(MetricsHeader)) {
mMetrics.maxAdvance = uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
}
}
mMetrics.internalLeading =
std::max(mMetrics.maxHeight - mMetrics.emHeight, 0.0);
mMetrics.externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
UINT32 ucs = L' ';
UINT16 glyph;
if (SUCCEEDED(mFontFace->GetGlyphIndices(&ucs, 1, &glyph)) && glyph != 0) {
mSpaceGlyph = glyph;
mMetrics.spaceWidth = MeasureGlyphWidth(glyph);
} else {
mMetrics.spaceWidth = 0;
}
// try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
// if the table is not available or if using hinted/pixel-snapped widths
if (mUseSubpixelPositions) {
mMetrics.aveCharWidth = 0;
gfxFontEntry::AutoTable os2Table(GetFontEntry(),
TRUETYPE_TAG('O', 'S', '/', '2'));
if (os2Table) {
uint32_t len;
const OS2Table* os2 =
reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
if (len >= 4) {
// Not checking against sizeof(mozilla::OS2Table) here because older
// versions of the table have different sizes; we only need the first
// two 16-bit fields here.
mMetrics.aveCharWidth = int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
}
}
}
if (mMetrics.aveCharWidth < 1) {
mMetrics.aveCharWidth = GetCharAdvance('x');
if (mMetrics.aveCharWidth < 1) {
// Let's just assume the X is square.
mMetrics.aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
}
}
mMetrics.zeroWidth = GetCharAdvance('0');
mMetrics.ideographicWidth = GetCharAdvance(kWaterIdeograph);
mMetrics.underlineOffset = fontMetrics.underlinePosition * mFUnitsConvFactor;
mMetrics.underlineSize = fontMetrics.underlineThickness * mFUnitsConvFactor;
mMetrics.strikeoutOffset =
fontMetrics.strikethroughPosition * mFUnitsConvFactor;
mMetrics.strikeoutSize =
fontMetrics.strikethroughThickness * mFUnitsConvFactor;
SanitizeMetrics(&mMetrics, GetFontEntry()->mIsBadUnderlineFont);
if (ApplySyntheticBold()) {
auto delta = GetSyntheticBoldOffset();
mMetrics.spaceWidth += delta;
mMetrics.aveCharWidth += delta;
mMetrics.maxAdvance += delta;
if (mMetrics.zeroWidth > 0) {
mMetrics.zeroWidth += delta;
}
if (mMetrics.ideographicWidth > 0) {
mMetrics.ideographicWidth += delta;
}
}
#if 0
printf("Font: %p (%s) size: %f\n", this,
NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
printf(" internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
printf(" spaceWidth: %f aveCharWidth: %f zeroWidth: %f\n",
mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.zeroWidth);
printf(" xHeight: %f capHeight: %f\n", mMetrics.xHeight, mMetrics.capHeight);
printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
#endif
}
using namespace mozilla; // for AutoSwap_* types
struct EBLCHeader {
AutoSwap_PRUint32 version;
AutoSwap_PRUint32 numSizes;
};
struct SbitLineMetrics {
int8_t ascender;
int8_t descender;
uint8_t widthMax;
int8_t caretSlopeNumerator;
int8_t caretSlopeDenominator;
int8_t caretOffset;
int8_t minOriginSB;
int8_t minAdvanceSB;
int8_t maxBeforeBL;
int8_t minAfterBL;
int8_t pad1;
int8_t pad2;
};
struct BitmapSizeTable {
AutoSwap_PRUint32 indexSubTableArrayOffset;
AutoSwap_PRUint32 indexTablesSize;
AutoSwap_PRUint32 numberOfIndexSubTables;
AutoSwap_PRUint32 colorRef;
SbitLineMetrics hori;
SbitLineMetrics vert;
AutoSwap_PRUint16 startGlyphIndex;
AutoSwap_PRUint16 endGlyphIndex;
uint8_t ppemX;
uint8_t ppemY;
uint8_t bitDepth;
uint8_t flags;
};
typedef EBLCHeader EBSCHeader;
struct BitmapScaleTable {
SbitLineMetrics hori;
SbitLineMetrics vert;
uint8_t ppemX;
uint8_t ppemY;
uint8_t substitutePpemX;
uint8_t substitutePpemY;
};
bool gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize) {
uint8_t* tableData;
uint32_t len;
void* tableContext;
BOOL exists;
HRESULT hr = mFontFace->TryGetFontTable(
DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'), (const void**)&tableData,
&len, &tableContext, &exists);
if (FAILED(hr)) {
return false;
}
bool hasStrike = false;
// not really a loop, but this lets us use 'break' to skip out of the block
// as soon as we know the answer, and skips it altogether if the table is
// not present
while (exists) {
if (len < sizeof(EBLCHeader)) {
break;
}
const EBLCHeader* hdr = reinterpret_cast<const EBLCHeader*>(tableData);
if (hdr->version != 0x00020000) {
break;
}
uint32_t numSizes = hdr->numSizes;
if (numSizes > 0xffff) { // sanity-check, prevent overflow below
break;
}
if (len < sizeof(EBLCHeader) + numSizes * sizeof(BitmapSizeTable)) {
break;
}
const BitmapSizeTable* sizeTable =
reinterpret_cast<const BitmapSizeTable*>(hdr + 1);
for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
if (sizeTable->ppemX == aSize && sizeTable->ppemY == aSize) {
// we ignore a strike that contains fewer than 4 glyphs,
// as that probably indicates a font such as Courier New
// that provides bitmaps ONLY for the "shading" characters
// U+2591..2593
hasStrike = (uint16_t(sizeTable->endGlyphIndex) >=
uint16_t(sizeTable->startGlyphIndex) + 3);
break;
}
}
// if we reach here, we didn't find a strike; unconditionally break
// out of the while-loop block
break;
}
mFontFace->ReleaseFontTable(tableContext);
if (hasStrike) {
return true;
}
// if we didn't find a real strike, check if the font calls for scaling
// another bitmap to this size
hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'),
(const void**)&tableData, &len, &tableContext,
&exists);
if (FAILED(hr)) {
return false;
}
while (exists) {
if (len < sizeof(EBSCHeader)) {
break;
}
const EBSCHeader* hdr = reinterpret_cast<const EBSCHeader*>(tableData);
if (hdr->version != 0x00020000) {
break;
}
uint32_t numSizes = hdr->numSizes;
if (numSizes > 0xffff) {
break;
}
if (len < sizeof(EBSCHeader) + numSizes * sizeof(BitmapScaleTable)) {
break;
}
const BitmapScaleTable* scaleTable =
reinterpret_cast<const BitmapScaleTable*>(hdr + 1);
for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) {
hasStrike = true;
break;
}
}
break;
}
mFontFace->ReleaseFontTable(tableContext);
return hasStrike;
}
bool gfxDWriteFont::IsValid() const { return mFontFace != nullptr; }
IDWriteFontFace* gfxDWriteFont::GetFontFace() { return mFontFace.get(); }
gfxFont::RunMetrics gfxDWriteFont::Measure(const gfxTextRun* aTextRun,
uint32_t aStart, uint32_t aEnd,
BoundingBoxType aBoundingBoxType,
DrawTarget* aRefDrawTarget,
Spacing* aSpacing,
gfx::ShapedTextFlags aOrientation) {
gfxFont::RunMetrics metrics =
gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType, aRefDrawTarget,
aSpacing, aOrientation);
// if aBoundingBoxType is LOOSE_INK_EXTENTS
// and the underlying cairo font may be antialiased,
// we can't trust Windows to have considered all the pixels
// so we need to add "padding" to the bounds.
// (see bugs 475968, 439831, compare also bug 445087)
if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
mAntialiasOption != kAntialiasNone &&
GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC &&
metrics.mBoundingBox.Width() > 0) {
metrics.mBoundingBox.MoveByX(-aTextRun->GetAppUnitsPerDevUnit());
metrics.mBoundingBox.SetWidth(metrics.mBoundingBox.Width() +
aTextRun->GetAppUnitsPerDevUnit() * 3);
}
return metrics;
}
bool gfxDWriteFont::ProvidesGlyphWidths() const {
return !mUseSubpixelPositions ||
(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD) ||
((gfxDWriteFontEntry*)(GetFontEntry()))->HasVariations();
}
int32_t gfxDWriteFont::GetGlyphWidth(uint16_t aGID) {
if (!mGlyphWidths) {
mGlyphWidths = MakeUnique<nsTHashMap<nsUint32HashKey, int32_t>>(128);
}
return mGlyphWidths->LookupOrInsertWith(
aGID, [&] { return NS_lround(MeasureGlyphWidth(aGID) * 65536.0); });
}
bool gfxDWriteFont::GetForceGDIClassic() const {
return sForceGDIClassicEnabled &&
static_cast<gfxDWriteFontEntry*>(mFontEntry.get())
->GetForceGDIClassic() &&
GetAdjustedSize() <= gfxDWriteFontList::PlatformFontList()
->GetForceGDIClassicMaxFontSize();
}
DWRITE_MEASURING_MODE
gfxDWriteFont::GetMeasuringMode() const {
return DWriteSettings::Get(GetForceGDIClassic()).MeasuringMode();
}
gfxFloat gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph) {
MOZ_SEH_TRY {
HRESULT hr;
if (mFontFace1) {
int32_t advance;
if (mUseSubpixelPositions) {
hr = mFontFace1->GetDesignGlyphAdvances(1, &aGlyph, &advance, FALSE);
if (SUCCEEDED(hr)) {
return advance * mFUnitsConvFactor;
}
} else {
hr = mFontFace1->GetGdiCompatibleGlyphAdvances(
FLOAT(mAdjustedSize), 1.0f, nullptr,
GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL, FALSE, 1,
&aGlyph, &advance);
if (SUCCEEDED(hr)) {
return NS_lround(advance * mFUnitsConvFactor);
}
}
} else {
DWRITE_GLYPH_METRICS metrics;
if (mUseSubpixelPositions) {
hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE);
if (SUCCEEDED(hr)) {
return metrics.advanceWidth * mFUnitsConvFactor;
}
} else {
hr = mFontFace->GetGdiCompatibleGlyphMetrics(
FLOAT(mAdjustedSize), 1.0f, nullptr,
GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL, &aGlyph, 1,
&metrics, FALSE);
if (SUCCEEDED(hr)) {
return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
}
}
}
}
MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
// Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
// the font resource; possibly a failing drive or similar hardware issue.
// Mark the font as invalid, and wipe the fontEntry's charmap so that font
// selection will skip it; we'll use a fallback font instead.
mIsValid = false;
GetFontEntry()->mCharacterMap = new gfxCharacterMap();
GetFontEntry()->mShmemCharacterMap = nullptr;
gfxCriticalError() << "Exception occurred measuring glyph width for "
<< GetFontEntry()->Name().get();
}
return 0.0;
}
bool gfxDWriteFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
bool aTight) {
MOZ_SEH_TRY {
DWRITE_GLYPH_METRICS m;
HRESULT hr = mFontFace->GetDesignGlyphMetrics(&aGID, 1, &m, FALSE);
if (FAILED(hr)) {
return false;
}
gfxRect bounds(m.leftSideBearing, m.topSideBearing - m.verticalOriginY,
m.advanceWidth - m.leftSideBearing - m.rightSideBearing,
m.advanceHeight - m.topSideBearing - m.bottomSideBearing);
bounds.Scale(mFUnitsConvFactor);
// GetDesignGlyphMetrics returns 'ideal' glyph metrics, we need to pad to
// account for antialiasing.
if (!aTight && !aBounds->IsEmpty()) {
bounds.Inflate(1.0, 0.0);
}
*aBounds = bounds;
return true;
}
MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
// Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
// the font resource; possibly a failing drive or similar hardware issue.
// Mark the font as invalid, and wipe the fontEntry's charmap so that font
// selection will skip it; we'll use a fallback font instead.
mIsValid = false;
GetFontEntry()->mCharacterMap = new gfxCharacterMap();
GetFontEntry()->mShmemCharacterMap = nullptr;
gfxCriticalError() << "Exception occurred measuring glyph bounds for "
<< GetFontEntry()->Name().get();
}
return false;
}
void gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const {
gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
if (mGlyphWidths) {
aSizes->mFontInstances +=
mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
}
}
void gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const {
aSizes->mFontInstances += aMallocSizeOf(this);
AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
}
already_AddRefed<ScaledFont> gfxDWriteFont::GetScaledFont(
const TextRunDrawParams& aRunParams) {
bool useClearType = UsingClearType();
if (mAzureScaledFontUsedClearType != useClearType) {
if (auto* oldScaledFont = mAzureScaledFont.exchange(nullptr)) {
oldScaledFont->Release();
}
if (auto* oldScaledFont = mAzureScaledFontGDI.exchange(nullptr)) {
oldScaledFont->Release();
}
}
bool forceGDI = aRunParams.allowGDI && GetForceGDIClassic();
ScaledFont* scaledFont = forceGDI ? mAzureScaledFontGDI : mAzureScaledFont;
if (scaledFont) {
return do_AddRef(scaledFont);
}
gfxDWriteFontEntry* fe = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
bool useEmbeddedBitmap =
(gfxVars::SystemTextRenderingMode() == DWRITE_RENDERING_MODE_DEFAULT ||
forceGDI) &&
fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize));
const gfxFontStyle* fontStyle = GetStyle();
RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForDWriteFont(
mFontFace, fontStyle, GetUnscaledFont(), GetAdjustedSize(),
useEmbeddedBitmap, ApplySyntheticBold(), forceGDI);
if (!newScaledFont) {
return nullptr;
}
InitializeScaledFont(newScaledFont);
if (forceGDI) {
if (mAzureScaledFontGDI.compareExchange(nullptr, newScaledFont.get())) {
Unused << newScaledFont.forget();
mAzureScaledFontUsedClearType = useClearType;
}
scaledFont = mAzureScaledFontGDI;
} else {
if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
Unused << newScaledFont.forget();
mAzureScaledFontUsedClearType = useClearType;
}
scaledFont = mAzureScaledFont;
}
return do_AddRef(scaledFont);
}
bool gfxDWriteFont::ShouldRoundXOffset(cairo_t* aCairo) const {
// show_glyphs is implemented on the font and so is used for all Cairo
// surface types; however, it may pixel-snap depending on the dwrite
// rendering mode
return GetMeasuringMode() != DWRITE_MEASURING_MODE_NATURAL;
}