Index: src/ports/SkFontHost_win_dw.cpp |
=================================================================== |
--- src/ports/SkFontHost_win_dw.cpp (revision 14488) |
+++ src/ports/SkFontHost_win_dw.cpp (working copy) |
@@ -22,7 +22,10 @@ |
#include "SkGlyph.h" |
#include "SkHRESULT.h" |
#include "SkMaskGamma.h" |
+#include "SkMatrix22.h" |
#include "SkOnce.h" |
+#include "SkOTTable_EBLC.h" |
+#include "SkOTTable_EBSC.h" |
#include "SkOTTable_head.h" |
#include "SkOTTable_hhea.h" |
#include "SkOTTable_OS_2.h" |
@@ -449,7 +452,22 @@ |
const void* drawDWMask(const SkGlyph& glyph); |
SkTDArray<uint8_t> fBits; |
+ /** The total matrix without the text height scale. */ |
+ SkMatrix fSkXform; |
+ /** The total matrix without the text height scale. */ |
DWRITE_MATRIX fXform; |
+ /** The non-rotational part of total matrix without the text height scale. |
+ * This is used to find the magnitude of gdi compatible advances. |
+ */ |
+ DWRITE_MATRIX fGsA; |
+ /** The inverse of the rotational part of the total matrix. |
+ * This is used to find the direction of gdi compatible advances. |
+ */ |
+ SkMatrix fG_inv; |
+ /** The text size to render with. */ |
+ SkScalar fTextSizeRender; |
+ /** The text size to measure with. */ |
+ SkScalar fTextSizeMeasure; |
SkAutoTUnref<DWriteFontTypeface> fTypeface; |
int fGlyphCount; |
DWRITE_RENDERING_MODE fRenderingMode; |
@@ -570,32 +588,224 @@ |
wcscmp(dwFaceFontNameChar.get(), dwFontNameChar.get()) == 0; |
} |
+class AutoDWriteTable { |
+public: |
+ AutoDWriteTable(IDWriteFontFace* fontFace, UINT32 beTag) : fFontFace(fontFace), fExists(FALSE) { |
+ // Any errors are ignored, user must check fExists anyway. |
+ fontFace->TryGetFontTable(beTag, |
+ reinterpret_cast<const void **>(&fData), &fSize, &fLock, &fExists); |
+ } |
+ ~AutoDWriteTable() { |
+ if (fExists) { |
+ fFontFace->ReleaseFontTable(fLock); |
+ } |
+ } |
+ |
+ const uint8_t* fData; |
+ UINT32 fSize; |
+ BOOL fExists; |
+private: |
+ // Borrowed reference, the user must ensure the fontFace stays alive. |
+ IDWriteFontFace* fFontFace; |
+ void* fLock; |
+}; |
+template<typename T> class AutoTDWriteTable : public AutoDWriteTable { |
+public: |
+ static const UINT32 tag = DWRITE_MAKE_OPENTYPE_TAG(T::TAG0, T::TAG1, T::TAG2, T::TAG3); |
+ AutoTDWriteTable(IDWriteFontFace* fontFace) : AutoDWriteTable(fontFace, tag) { } |
+ |
+ const T* get() const { return reinterpret_cast<const T*>(fData); } |
+ const T* operator->() const { return reinterpret_cast<const T*>(fData); } |
+}; |
+ |
+static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) { |
+ { |
+ AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get()); |
+ if (!eblc.fExists) { |
+ return false; |
+ } |
+ if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) { |
+ return false; |
+ } |
+ if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) { |
+ return false; |
+ } |
+ |
+ uint32_t numSizes = SkEndianSwap32(eblc->numSizes); |
+ if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) + |
+ sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes) |
+ { |
+ return false; |
+ } |
+ |
+ const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable = |
+ SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get()); |
+ for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) { |
+ if (sizeTable->ppemX == size && sizeTable->ppemY == size) { |
+ // TODO: determine if we should dig through IndexSubTableArray/IndexSubTable |
+ // to determine the actual number of glyphs with bitmaps. |
+ |
+ // TODO: Ensure that the bitmaps actually cover a significant portion of the strike. |
+ |
+ //TODO: Endure that the bitmaps are bi-level. |
+ if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) { |
+ return true; |
+ } |
+ } |
+ } |
+ } |
+ |
+ { |
+ AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteFontFace.get()); |
+ if (!ebsc.fExists) { |
+ return false; |
+ } |
+ if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) { |
+ return false; |
+ } |
+ if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) { |
+ return false; |
+ } |
+ |
+ uint32_t numSizes = SkEndianSwap32(ebsc->numSizes); |
+ if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) + |
+ sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes) |
+ { |
+ return false; |
+ } |
+ |
+ const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable = |
+ SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get()); |
+ for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) { |
+ if (scaleTable->ppemX == size && scaleTable->ppemY == size) { |
+ // EBSC tables are normally only found in bitmap only fonts. |
+ return true; |
+ } |
+ } |
+ } |
+ |
+ return false; |
+} |
+ |
+static bool bothZero(SkScalar a, SkScalar b) { |
+ return 0 == a && 0 == b; |
+} |
+ |
+// returns false if there is any non-90-rotation or skew |
+static bool isAxisAligned(const SkScalerContext::Rec& rec) { |
+ return 0 == rec.fPreSkewX && |
+ (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) || |
+ bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1])); |
+} |
+ |
SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface, |
const SkDescriptor* desc) |
: SkScalerContext(typeface, desc) |
, fTypeface(SkRef(typeface)) |
, fGlyphCount(-1) { |
- fXform.m11 = SkScalarToFloat(fRec.fPost2x2[0][0]); |
- fXform.m12 = SkScalarToFloat(fRec.fPost2x2[1][0]); |
- fXform.m21 = SkScalarToFloat(fRec.fPost2x2[0][1]); |
- fXform.m22 = SkScalarToFloat(fRec.fPost2x2[1][1]); |
- fXform.dx = 0; |
- fXform.dy = 0; |
+ // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC |
+ // except when bi-level rendering is requested or there are embedded |
+ // bi-level bitmaps (and the embedded bitmap flag is set and no rotation). |
+ // |
+ // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do |
+ // this. As a result, determine the actual size of the text and then see if |
+ // there are any embedded bi-level bitmaps of that size. If there are, then |
+ // force bitmaps by requesting bi-level rendering. |
+ // |
+ // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes |
+ // square pixels and only uses ppemY. Therefore the transform must track any |
+ // non-uniform x-scale. |
+ // |
+ // Also, rotated glyphs should have the same absolute advance widths as |
+ // horizontal glyphs and the subpixel flag should not affect glyph shapes. |
- if (SkMask::kBW_Format == fRec.fMaskFormat) { |
+ // A is the total matrix. |
+ SkMatrix A; |
+ fRec.getSingleMatrix(&A); |
+ |
+ // h is where A maps the horizontal baseline. |
+ SkPoint h = SkPoint::Make(SK_Scalar1, 0); |
+ A.mapPoints(&h, 1); |
+ |
+ // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0). |
+ SkMatrix G; |
+ SkComputeGivensRotation(h, &G); |
+ |
+ // GA is the matrix A with rotation removed. |
+ SkMatrix GA(G); |
+ GA.preConcat(A); |
+ |
+ // realTextSize is the actual device size we want (as opposed to the size the user requested). |
+ // gdiTextSize is the size we request when GDI compatible. |
+ // If the scale is negative, this means the matrix will do the flip anyway. |
+ SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY)); |
+ // Due to floating point math, the lower bits are suspect. Round carefully. |
+ SkScalar roundedTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f; |
+ SkScalar gdiTextSize = SkScalarFloorToScalar(roundedTextSize); |
+ if (gdiTextSize == 0) { |
+ gdiTextSize = SK_Scalar1; |
+ } |
+ |
+ bool hasBitmap = fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag && |
+ hasBitmapStrike(typeface, SkScalarTruncToInt(gdiTextSize)); |
+ bool axisAligned = isAxisAligned(fRec); |
+ bool isBiLevel = SkMask::kBW_Format == fRec.fMaskFormat || (hasBitmap && axisAligned); |
+ |
+ if (isBiLevel) { |
+ fTextSizeRender = gdiTextSize; |
fRenderingMode = DWRITE_RENDERING_MODE_ALIASED; |
fTextureType = DWRITE_TEXTURE_ALIASED_1x1; |
+ fTextSizeMeasure = gdiTextSize; |
fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; |
+ } else if (hasBitmap) { |
+ // If rotated but the horizontal text would have used a bitmap, |
+ // render high quality rotated glyphs using the bitmap metrics. |
+ fTextSizeRender = gdiTextSize; |
+ fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; |
+ fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; |
+ fTextSizeMeasure = gdiTextSize; |
+ fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; |
} else { |
+ fTextSizeRender = realTextSize; |
fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; |
fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; |
+ fTextSizeMeasure = realTextSize; |
fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; |
} |
if (this->isSubpixel()) { |
+ fTextSizeMeasure = realTextSize; |
fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; |
} |
+ |
+ // Remove the realTextSize, as that is the text height scale currently in A. |
+ SkScalar scale = SkScalarInvert(realTextSize); |
+ |
+ // fSkXform is the total matrix A without the text height scale. |
+ fSkXform = A; |
+ fSkXform.preScale(scale, scale); //remove the text height scale. |
+ |
+ fXform.m11 = SkScalarToFloat(fSkXform.getScaleX()); |
+ fXform.m12 = SkScalarToFloat(fSkXform.getSkewY()); |
+ fXform.m21 = SkScalarToFloat(fSkXform.getSkewX()); |
+ fXform.m22 = SkScalarToFloat(fSkXform.getScaleY()); |
+ fXform.dx = 0; |
+ fXform.dy = 0; |
+ |
+ // GsA is the non-rotational part of A without the text height scale. |
+ SkMatrix GsA(GA); |
+ GsA.preScale(scale, scale); //remove text height scale, G is rotational so reorders with scale. |
+ |
+ fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX)); |
+ fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0. |
+ fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX)); |
+ fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY)); |
+ |
+ // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational. |
+ fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX), |
+ -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY), |
+ G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2)); |
} |
SkScalerContext_DW::~SkScalerContext_DW() { |
@@ -631,9 +841,9 @@ |
DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) |
{ |
HRVM(fTypeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics( |
- fRec.fTextSize, |
+ fTextSizeMeasure, |
1.0f, // pixelsPerDip |
- &fXform, |
+ &fGsA, |
DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode, |
&glyphId, 1, |
&gm), |
@@ -645,7 +855,7 @@ |
DWRITE_FONT_METRICS dwfm; |
fTypeface->fDWriteFontFace->GetMetrics(&dwfm); |
- SkScalar advanceX = SkScalarMulDiv(fRec.fTextSize, |
+ SkScalar advanceX = SkScalarMulDiv(fTextSizeMeasure, |
SkIntToScalar(gm.advanceWidth), |
SkIntToScalar(dwfm.designUnitsPerEm)); |
@@ -654,9 +864,13 @@ |
} |
SkVector vecs[1] = { { advanceX, 0 } }; |
- SkMatrix mat; |
- fRec.getMatrixFrom2x2(&mat); |
- mat.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); |
+ if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || |
+ DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) |
+ { |
+ fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); |
+ } else { |
+ fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); |
+ } |
glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX); |
glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY); |
@@ -683,7 +897,7 @@ |
run.glyphCount = 1; |
run.glyphAdvances = &advance; |
run.fontFace = fTypeface->fDWriteFontFace.get(); |
- run.fontEmSize = SkScalarToFloat(fRec.fTextSize); |
+ run.fontEmSize = SkScalarToFloat(fTextSizeRender); |
run.bidiLevel = 0; |
run.glyphIndices = &glyphId; |
run.isSideways = FALSE; |
@@ -728,7 +942,7 @@ |
DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) |
{ |
fTypeface->fDWriteFontFace->GetGdiCompatibleMetrics( |
- fRec.fTextSize, |
+ fTextSizeRender, |
1.0f, // pixelsPerDip |
&fXform, |
&dwfm); |
@@ -738,28 +952,28 @@ |
SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm); |
if (mx) { |
- mx->fTop = -fRec.fTextSize * SkIntToScalar(dwfm.ascent) / upem; |
+ mx->fTop = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem; |
mx->fAscent = mx->fTop; |
- mx->fDescent = fRec.fTextSize * SkIntToScalar(dwfm.descent) / upem; |
+ mx->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem; |
mx->fBottom = mx->fDescent; |
- mx->fLeading = fRec.fTextSize * SkIntToScalar(dwfm.lineGap) / upem; |
- mx->fXHeight = fRec.fTextSize * SkIntToScalar(dwfm.xHeight) / upem; |
- mx->fUnderlineThickness = fRec.fTextSize * SkIntToScalar(dwfm.underlinePosition) / upem; |
- mx->fUnderlinePosition = -(fRec.fTextSize * SkIntToScalar(dwfm.underlineThickness) / upem); |
+ mx->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem; |
+ mx->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem; |
+ mx->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem; |
+ mx->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem); |
mx->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; |
mx->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; |
} |
if (my) { |
- my->fTop = -fRec.fTextSize * SkIntToScalar(dwfm.ascent) / upem; |
+ my->fTop = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem; |
my->fAscent = my->fTop; |
- my->fDescent = fRec.fTextSize * SkIntToScalar(dwfm.descent) / upem; |
+ my->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem; |
my->fBottom = my->fDescent; |
- my->fLeading = fRec.fTextSize * SkIntToScalar(dwfm.lineGap) / upem; |
- my->fXHeight = fRec.fTextSize * SkIntToScalar(dwfm.xHeight) / upem; |
- my->fUnderlineThickness = fRec.fTextSize * SkIntToScalar(dwfm.underlinePosition) / upem; |
- my->fUnderlinePosition = -(fRec.fTextSize * SkIntToScalar(dwfm.underlineThickness) / upem); |
+ my->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem; |
+ my->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem; |
+ my->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem; |
+ my->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem); |
my->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; |
my->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; |
@@ -888,7 +1102,7 @@ |
run.glyphCount = 1; |
run.glyphAdvances = &advance; |
run.fontFace = fTypeface->fDWriteFontFace.get(); |
- run.fontEmSize = SkScalarToFloat(fRec.fTextSize); |
+ run.fontEmSize = SkScalarToFloat(fTextSizeRender); |
run.bidiLevel = 0; |
run.glyphIndices = &index; |
run.isSideways = FALSE; |
@@ -966,7 +1180,7 @@ |
uint16_t glyphId = glyph.getGlyphID(); |
//TODO: convert to<->from DIUs? This would make a difference if hinting. |
//It may not be needed, it appears that DirectWrite only hints at em size. |
- HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fRec.fTextSize), |
+ HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fTextSizeRender), |
&glyphId, |
NULL, //advances |
NULL, //offsets |
@@ -976,9 +1190,7 @@ |
geometryToPath.get()), |
"Could not create glyph outline."); |
- SkMatrix mat; |
- fRec.getMatrixFrom2x2(&mat); |
- path->transform(mat); |
+ path->transform(fSkXform); |
} |
void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc, |
@@ -1146,28 +1358,6 @@ |
return stream.get() ? SkFontStream::GetTableTags(stream, ttcIndex, tags) : 0; |
} |
-class AutoDWriteTable { |
-public: |
- AutoDWriteTable(IDWriteFontFace* fontFace, UINT32 beTag) : fFontFace(fontFace), fExists(FALSE) { |
- // Any errors are ignored, user must check fExists anyway. |
- fontFace->TryGetFontTable(beTag, |
- reinterpret_cast<const void **>(&fData), &fSize, &fLock, &fExists); |
- } |
- ~AutoDWriteTable() { |
- if (fExists) { |
- fFontFace->ReleaseFontTable(fLock); |
- } |
- } |
- |
- const uint8_t* fData; |
- UINT32 fSize; |
- BOOL fExists; |
-private: |
- // Borrowed reference, the user must ensure the fontFace stays alive. |
- IDWriteFontFace* fFontFace; |
- void* fLock; |
-}; |
- |
size_t DWriteFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset, |
size_t length, void* data) const |
{ |
@@ -1260,7 +1450,6 @@ |
unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag | |
SkScalerContext::kForceAutohinting_Flag | |
- SkScalerContext::kEmbeddedBitmapText_Flag | |
SkScalerContext::kEmbolden_Flag | |
SkScalerContext::kLCD_BGROrder_Flag | |
SkScalerContext::kLCD_Vertical_Flag; |
@@ -1362,14 +1551,6 @@ |
return true; |
} |
-template<typename T> class AutoTDWriteTable : public AutoDWriteTable { |
-public: |
- static const UINT32 tag = DWRITE_MAKE_OPENTYPE_TAG(T::TAG0, T::TAG1, T::TAG2, T::TAG3); |
- AutoTDWriteTable(IDWriteFontFace* fontFace) : AutoDWriteTable(fontFace, tag) { } |
- |
- const T* operator->() const { return reinterpret_cast<const T*>(fData); } |
-}; |
- |
SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics( |
SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo, |
const uint32_t* glyphIDs, |