bug 574907 - don't let DWrite use fractional font sizes and subpixel positioning when embedded bitmaps are present. r=masayuki a=joe

This commit is contained in:
Jonathan Kew 2011-01-21 10:36:28 +00:00
parent 97b96b98fb
commit f168213574
3 changed files with 159 additions and 6 deletions

View File

@ -124,7 +124,7 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
, mCairoScaledFont(nsnull)
, mNeedsOblique(PR_FALSE)
, mNeedsBold(aNeedsBold)
, mUsingClearType(PR_FALSE)
, mUseSubpixelPositions(PR_FALSE)
{
gfxDWriteFontEntry *fe =
static_cast<gfxDWriteFontEntry*>(aFontEntry);
@ -150,7 +150,9 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType()) ||
anAAOption == gfxFont::kAntialiasSubpixel)
{
mUsingClearType = PR_TRUE;
mUseSubpixelPositions = PR_TRUE;
// note that this may be reset to FALSE if we determine that a bitmap
// strike is going to be used
}
ComputeMetrics();
@ -209,6 +211,11 @@ gfxDWriteFont::ComputeMetrics()
mAdjustedSize = mStyle.size;
}
if (HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) {
mAdjustedSize = NS_lround(mAdjustedSize);
mUseSubpixelPositions = PR_FALSE;
}
mMetrics.xHeight =
((gfxFloat)fontMetrics.xHeight /
fontMetrics.designUnitsPerEm) * mAdjustedSize;
@ -341,6 +348,150 @@ gfxDWriteFont::ComputeMetrics()
#endif
}
using namespace mozilla; // for AutoSwap_* types
struct EBLCHeader {
AutoSwap_PRUint32 version;
AutoSwap_PRUint32 numSizes;
};
struct SbitLineMetrics {
PRInt8 ascender;
PRInt8 descender;
PRUint8 widthMax;
PRInt8 caretSlopeNumerator;
PRInt8 caretSlopeDenominator;
PRInt8 caretOffset;
PRInt8 minOriginSB;
PRInt8 minAdvanceSB;
PRInt8 maxBeforeBL;
PRInt8 minAfterBL;
PRInt8 pad1;
PRInt8 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;
PRUint8 ppemX;
PRUint8 ppemY;
PRUint8 bitDepth;
PRUint8 flags;
};
typedef EBLCHeader EBSCHeader;
struct BitmapScaleTable {
SbitLineMetrics hori;
SbitLineMetrics vert;
PRUint8 ppemX;
PRUint8 ppemY;
PRUint8 substitutePpemX;
PRUint8 substitutePpemY;
};
PRBool
gfxDWriteFont::HasBitmapStrikeForSize(PRUint32 aSize)
{
PRUint8 *tableData;
PRUint32 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 PR_FALSE;
}
PRBool hasStrike = PR_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;
}
PRUint32 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 (PRUint32 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 = (PRUint16(sizeTable->endGlyphIndex) >=
PRUint16(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 PR_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 PR_FALSE;
}
while (exists) {
if (len < sizeof(EBSCHeader)) {
break;
}
const EBSCHeader *hdr = reinterpret_cast<const EBSCHeader*>(tableData);
if (hdr->version != 0x00020000) {
break;
}
PRUint32 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 (PRUint32 i = 0; i < numSizes; ++i, ++scaleTable) {
if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) {
hasStrike = PR_TRUE;
break;
}
}
break;
}
mFontFace->ReleaseFontTable(tableContext);
return hasStrike;
}
PRUint32
gfxDWriteFont::GetSpaceGlyph()
{
@ -494,7 +645,7 @@ gfxDWriteFont::GetGlyphWidth(gfxContext *aCtx, PRUint16 aGID)
DWRITE_GLYPH_METRICS glyphMetrics;
HRESULT hr;
if (mUsingClearType) {
if (mUseSubpixelPositions) {
hr = mFontFace->GetDesignGlyphMetrics(
&aGID, 1, &glyphMetrics, FALSE);
if (SUCCEEDED(hr)) {

View File

@ -80,7 +80,7 @@ public:
virtual hb_blob_t *GetFontTable(PRUint32 aTag);
virtual PRBool ProvidesGlyphWidths() const {
return !mUsingClearType ||
return !mUseSubpixelPositions ||
(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
}
@ -93,6 +93,8 @@ protected:
void ComputeMetrics();
PRBool HasBitmapStrikeForSize(PRUint32 aSize);
cairo_font_face_t *CairoFontFace();
cairo_scaled_font_t *CairoScaledFont();
@ -110,7 +112,7 @@ protected:
PRPackedBool mNeedsOblique;
PRPackedBool mNeedsBold;
PRPackedBool mUsingClearType;
PRPackedBool mUseSubpixelPositions;
};
#endif

View File

@ -175,7 +175,7 @@ trymoreglyphs:
continue;
}
if (!static_cast<gfxDWriteFont*>(mFont)->mUsingClearType) {
if (!static_cast<gfxDWriteFont*>(mFont)->mUseSubpixelPositions) {
hr = analyzer->GetGdiCompatibleGlyphPlacements(
aString + range.start,
clusters.Elements(),