Index: src/ports/SkFontHost_win.cpp |
=================================================================== |
--- src/ports/SkFontHost_win.cpp (revision 10398) |
+++ src/ports/SkFontHost_win.cpp (working copy) |
@@ -80,23 +80,11 @@ |
return true; |
} |
#endif |
- // false means allow GDI to generate the bits |
- return false; |
+ return rec.getHinting() == SkPaint::kNo_Hinting || rec.getHinting() == SkPaint::kSlight_Hinting; |
} |
using namespace skia_advanced_typeface_metrics_utils; |
-/** |
- * Since LOGFONT wants its textsize as an int, and we support fractional sizes, |
- * and since we have a cache of LOGFONTs for our tyepfaces, we always set the |
- * lfHeight to a canonical size, and then we use the 2x2 matrix to achieve the |
- * actual requested size. |
- * |
- * Not critical to match the font's upem, but we want it big enough to avoid |
- * precision loss for GDI calls that return ints (e.g. GetOutlineFontMetrics). |
- */ |
-static const int gCanonicalTextSize = 2048; |
- |
static void tchar_to_skstring(const TCHAR t[], SkString* s) { |
#ifdef UNICODE |
size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, NULL, 0, NULL, NULL); |
@@ -108,7 +96,7 @@ |
} |
static void make_canonical(LOGFONT* lf) { |
- lf->lfHeight = -gCanonicalTextSize; |
+ lf->lfHeight = -2048; |
lf->lfQuality = CLEARTYPE_QUALITY;//PROOF_QUALITY; |
lf->lfCharSet = DEFAULT_CHARSET; |
// lf->lfClipPrecision = 64; |
@@ -453,12 +441,6 @@ |
int fWidth; |
int fHeight; |
bool fIsBW; |
- |
- enum { |
- // will always trigger us to reset the color, since we |
- // should only store 0 or 0x00FFFFFF or gray (0x007F7F7F) |
- kInvalid_Color = 12345 |
- }; |
}; |
const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW, |
@@ -538,6 +520,7 @@ |
} |
////////////////////////////////////////////////////////////////////////////// |
+#define BUFFERSIZE (1 << 13) |
class SkScalerContext_Windows : public SkScalerContext { |
public: |
@@ -559,8 +542,14 @@ |
SkPaint::FontMetrics* mY) SK_OVERRIDE; |
private: |
+ DWORD getGDIGlyphPath(const SkGlyph& glyph, UINT flags, |
+ SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf); |
+ |
HDCOffscreen fOffscreen; |
- SkScalar fScale; // to get from canonical size to real size |
+ /** fGsA is the non-rotational part of total matrix without the text height scale. |
+ * Used to find the magnitude of advances. |
+ */ |
+ MAT2 fGsA; |
MAT2 fMat22; |
HDC fDDC; |
HFONT fSavefont; |
@@ -569,32 +558,21 @@ |
SCRIPT_CACHE fSC; |
int fGlyphCount; |
- MAT2 fMat22Identity; |
SkMatrix fHiResMatrix; |
+ /** fG_inv is the inverse of the rotational part of the total matrix. |
+ * Used to set the direction of advances. |
+ */ |
+ SkMatrix fG_inv; |
enum Type { |
kTrueType_Type, kBitmap_Type, |
} fType; |
TEXTMETRIC fTM; |
}; |
-static float mul2float(SkScalar a, SkScalar b) { |
- return SkScalarToFloat(SkScalarMul(a, b)); |
-} |
- |
static FIXED float2FIXED(float x) { |
return SkFixedToFIXED(SkFloatToFixed(x)); |
} |
-#define HIRES_TEXTSIZE 2048 |
-#define HIRES_SHIFT 11 |
-static inline SkFixed HiResToFixed(int value) { |
- return value << (16 - HIRES_SHIFT); |
-} |
- |
-static bool needHiResMetrics(const SkScalar mat[2][2]) { |
- return mat[1][0] || mat[0][1]; |
-} |
- |
static BYTE compute_quality(const SkScalerContext::Rec& rec) { |
switch (rec.fMaskFormat) { |
case SkMask::kBW_Format: |
@@ -627,36 +605,84 @@ |
if (!fDDC) { |
return; |
} |
- |
SetGraphicsMode(fDDC, GM_ADVANCED); |
SetBkMode(fDDC, TRANSPARENT); |
+ |
+ SkPoint h = SkPoint::Make(SK_Scalar1, 0); |
+ // A is the total matrix. |
+ SkMatrix A; |
+ fRec.getSingleMatrix(&A); |
+ A.mapPoints(&h, 1); |
- // Scaling by the DPI is inconsistent with how Skia draws elsewhere |
- //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72); |
+ // Find the Given's matrix [[c, -s],[s, c]] which rotates the baseline vector h |
+ // (where the baseline is mapped to) to the positive horizontal axis. |
+ const SkScalar& a = h.fX; |
+ const SkScalar& b = h.fY; |
+ SkScalar c, s; |
+ if (0 == b) { |
+ c = SkDoubleToScalar(_copysign(SK_Scalar1, a)); |
+ s = 0; |
+ } else if (0 == a) { |
+ c = 0; |
+ s = SkDoubleToScalar(-_copysign(SK_Scalar1, b)); |
+ } else if (SkScalarAbs(b) > SkScalarAbs(a)) { |
+ SkScalar t = a / b; |
+ SkScalar u = SkDoubleToScalar(_copysign(SkScalarSqrt(SK_Scalar1 + t*t), b)); |
+ s = -1 / u; |
+ c = -s * t; |
+ } else { |
+ SkScalar t = b / a; |
+ SkScalar u = SkDoubleToScalar(_copysign(SkScalarSqrt(SK_Scalar1 + t*t), a)); |
+ c = 1 / u; |
+ s = -c * t; |
+ } |
+ |
+ // G is the Given's Matrix for A (rotational matrix such that GA[0][1] == 0). |
+ SkMatrix G; |
+ G.setAll(c, -s, 0, |
+ s, c, 0, |
+ 0, 0, SkScalarToPersp(SK_Scalar1)); |
+ |
+ // GA is the matrix A with rotation removed. |
+ SkMatrix GA(G); |
+ GA.preConcat(A); |
+ |
+ // textSize is the actual device size we want (as opposed to the size the user requested). |
+ // If the scale is negative, this means the matrix will do the flip anyway. |
+ SkScalar textSize = SkScalarAbs(SkScalarRoundToScalar(GA.get(SkMatrix::kMScaleY))); |
+ if (textSize == 0) { |
+ textSize = SK_Scalar1; |
+ } |
+ |
+ // sA is the total matrix A without the textSize (so GDI knows the text size separately). |
+ // When this matrix is used with GetGlyphOutline, no further processing is needed. |
+ SkMatrix sA(A); |
+ SkScalar scale = SkScalarInvert(textSize); |
+ sA.preScale(scale, scale); //remove text size |
+ |
+ // GsA is the non-rotational part of A without the text height scale. |
+ // This is what is used to find the magnitude of advances. |
+ SkMatrix GsA(GA); |
+ GsA.preScale(scale, scale); //remove text size, G is rotational so reorders with the scale. |
+ |
+ fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX)); |
+ fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0. |
+ fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX)); |
+ fGsA.eM22 = SkScalarToFIXED(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)); |
+ |
LOGFONT lf = typeface->fLogFont; |
- lf.lfHeight = -gCanonicalTextSize; |
+ lf.lfHeight = -SkScalarTruncToInt(textSize); |
lf.lfQuality = compute_quality(fRec); |
fFont = CreateFontIndirect(&lf); |
if (!fFont) { |
return; |
} |
- // if we're rotated, or want fractional widths, create a hires font |
- if (needHiResMetrics(fRec.fPost2x2)) { |
- lf.lfHeight = -HIRES_TEXTSIZE; |
- fHiResFont = CreateFontIndirect(&lf); |
- if (!fHiResFont) { |
- return; |
- } |
- |
- fMat22Identity.eM11 = fMat22Identity.eM22 = SkFixedToFIXED(SK_Fixed1); |
- fMat22Identity.eM12 = fMat22Identity.eM21 = SkFixedToFIXED(0); |
- |
- // construct a matrix to go from HIRES logical units to our device units |
- fRec.getSingleMatrix(&fHiResMatrix); |
- SkScalar scale = SkScalarInvert(SkIntToScalar(HIRES_TEXTSIZE)); |
- fHiResMatrix.preScale(scale, scale); |
- } |
fSavefont = (HFONT)SelectObject(fDDC, fFont); |
if (0 == GetTextMetrics(fDDC, &fTM)) { |
@@ -680,14 +706,13 @@ |
// Truetype or PostScript. |
// Stroked FON also gets here (TMPF_VECTOR), but we don't handle it. |
fType = SkScalerContext_Windows::kTrueType_Type; |
- fScale = fRec.fTextSize / gCanonicalTextSize; |
// fPost2x2 is column-major, left handed (y down). |
// XFORM 2x2 is row-major, left handed (y down). |
- xform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]); |
- xform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]); |
- xform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]); |
- xform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]); |
+ xform.eM11 = SkScalarToFloat(sA.get(SkMatrix::kMScaleX)); |
+ xform.eM12 = SkScalarToFloat(sA.get(SkMatrix::kMSkewY)); |
+ xform.eM21 = SkScalarToFloat(sA.get(SkMatrix::kMSkewX)); |
+ xform.eM22 = SkScalarToFloat(sA.get(SkMatrix::kMScaleY)); |
xform.eDx = 0; |
xform.eDy = 0; |
@@ -701,10 +726,31 @@ |
this->forceGenerateImageFromPath(); |
} |
+ // Create a hires font if we need linear metrics. |
+ if (this->isSubpixel()) { |
+ OUTLINETEXTMETRIC otm; |
+ UINT success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm); |
+ if (0 == success) { |
+ call_ensure_accessible(lf); |
+ success = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm); |
+ } |
+ if (0 != success) { |
+ lf.lfHeight = -SkToS32(otm.otmEMSquare); |
+ fHiResFont = CreateFontIndirect(&lf); |
+ if (!fHiResFont) { |
+ return; |
+ } |
+ |
+ // construct a matrix to go from HIRES logical units to our device units |
+ fRec.getSingleMatrix(&fHiResMatrix); |
+ SkScalar scale = SkScalarInvert(SkIntToScalar(otm.otmEMSquare)); |
+ fHiResMatrix.preScale(scale, scale); |
+ } |
+ } |
+ |
} else { |
// Assume bitmap |
fType = SkScalerContext_Windows::kBitmap_Type; |
- fScale = SK_Scalar1; |
xform.eM11 = 1.0f; |
xform.eM12 = 0.0f; |
@@ -719,18 +765,6 @@ |
fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]); |
fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]); |
fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]); |
- |
- lf.lfHeight = -SkScalarCeilToInt(fRec.fTextSize); |
- HFONT bitmapFont = CreateFontIndirect(&lf); |
- SelectObject(fDDC, bitmapFont); |
- ::DeleteObject(fFont); |
- fFont = bitmapFont; |
- |
- if (0 == GetTextMetrics(fDDC, &fTM)) { |
- call_ensure_accessible(lf); |
- //if the following fails, we'll just draw at gCanonicalTextSize. |
- GetTextMetrics(fDDC, &fTM); |
- } |
} |
fOffscreen.init(fFont, xform); |
@@ -793,6 +827,7 @@ |
this->generateMetrics(glyph); |
} |
+static const MAT2 gMat2Identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; |
void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) { |
SkASSERT(fDDC); |
@@ -866,7 +901,7 @@ |
if (fHiResFont) { |
SelectObject(fDDC, fHiResFont); |
sk_bzero(&gm, sizeof(gm)); |
- status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity); |
+ status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &gMat2Identity); |
if (GDI_ERROR != status) { |
SkPoint advance; |
fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance); |
@@ -874,6 +909,14 @@ |
glyph->fAdvanceY = SkScalarToFixed(advance.fY); |
} |
SelectObject(fDDC, fFont); |
+ } else if (!isAxisAligned(this->fRec)) { |
+ status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fGsA); |
+ if (GDI_ERROR != status) { |
+ SkPoint advance; |
+ fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance); |
+ glyph->fAdvanceX = SkScalarToFixed(advance.fX); |
+ glyph->fAdvanceY = SkScalarToFixed(advance.fY); |
+ } |
} |
} |
@@ -897,7 +940,7 @@ |
if (mx) { |
mx->fTop = SkIntToScalar(-fTM.tmAscent); |
mx->fAscent = SkIntToScalar(-fTM.tmAscent); |
- mx->fDescent = -SkIntToScalar(fTM.tmDescent); |
+ mx->fDescent = SkIntToScalar(fTM.tmDescent); |
mx->fBottom = SkIntToScalar(fTM.tmDescent); |
mx->fLeading = SkIntToScalar(fTM.tmExternalLeading); |
} |
@@ -905,7 +948,7 @@ |
if (my) { |
my->fTop = SkIntToScalar(-fTM.tmAscent); |
my->fAscent = SkIntToScalar(-fTM.tmAscent); |
- my->fDescent = SkIntToScalar(-fTM.tmDescent); |
+ my->fDescent = SkIntToScalar(fTM.tmDescent); |
my->fBottom = SkIntToScalar(fTM.tmDescent); |
my->fLeading = SkIntToScalar(fTM.tmExternalLeading); |
my->fAvgCharWidth = SkIntToScalar(fTM.tmAveCharWidth); |
@@ -922,40 +965,49 @@ |
OUTLINETEXTMETRIC otm; |
uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm); |
- if (GDI_ERROR == ret) { |
+ if (0 == ret) { |
LogFontTypeface::EnsureAccessible(this->getTypeface()); |
ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm); |
} |
- if (sizeof(otm) != ret) { |
+ if (0 == ret) { |
return; |
} |
if (mx) { |
- mx->fTop = -fScale * otm.otmrcFontBox.left; |
- mx->fAscent = -fScale * otm.otmAscent; |
- mx->fDescent = -fScale * otm.otmDescent; |
- mx->fBottom = fScale * otm.otmrcFontBox.right; |
- mx->fLeading = fScale * otm.otmLineGap; |
+ mx->fTop = SkIntToScalar(-otm.otmrcFontBox.left); |
+ mx->fAscent = SkIntToScalar(-otm.otmAscent); |
+ mx->fDescent = SkIntToScalar(-otm.otmDescent); |
+ mx->fBottom = SkIntToScalar(otm.otmrcFontBox.right); |
+ mx->fLeading = SkIntToScalar(otm.otmLineGap); |
} |
if (my) { |
#ifndef SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS |
- my->fTop = -fScale * otm.otmrcFontBox.top; |
- my->fAscent = -fScale * otm.otmAscent; |
- my->fDescent = -fScale * otm.otmDescent; |
- my->fBottom = -fScale * otm.otmrcFontBox.bottom; |
- my->fLeading = fScale * otm.otmLineGap; |
- my->fAvgCharWidth = fScale * otm.otmTextMetrics.tmAveCharWidth; |
- my->fMaxCharWidth = fScale * otm.otmTextMetrics.tmMaxCharWidth; |
- my->fXMin = fScale * otm.otmrcFontBox.left; |
- my->fXMax = fScale * otm.otmrcFontBox.right; |
+ my->fTop = SkIntToScalar(-otm.otmrcFontBox.top); |
+ my->fAscent = SkIntToScalar(-otm.otmAscent); |
+ my->fDescent = SkIntToScalar(-otm.otmDescent); |
+ my->fBottom = SkIntToScalar(-otm.otmrcFontBox.bottom); |
+ my->fLeading = SkIntToScalar(otm.otmLineGap); |
+ my->fAvgCharWidth = SkIntToScalar(otm.otmTextMetrics.tmAveCharWidth); |
+ my->fMaxCharWidth = SkIntToScalar(otm.otmTextMetrics.tmMaxCharWidth); |
+ my->fXMin = SkIntToScalar(otm.otmrcFontBox.left); |
+ my->fXMax = SkIntToScalar(otm.otmrcFontBox.right); |
#endif |
- my->fXHeight = fScale * otm.otmsXHeight; |
+ my->fXHeight = SkIntToScalar(otm.otmsXHeight); |
+ |
+ GLYPHMETRICS gm; |
+ sk_bzero(&gm, sizeof(gm)); |
+ DWORD len = GetGlyphOutlineW(fDDC, 'x', GGO_METRICS, &gm, 0, 0, &gMat2Identity); |
+ if (len != GDI_ERROR && gm.gmBlackBoxY > 0) { |
+ my->fXHeight = SkIntToScalar(gm.gmBlackBoxY); |
+ } |
} |
} |
//////////////////////////////////////////////////////////////////////////////////////// |
+#define SK_SHOW_TEXT_BLIT_COVERAGE 0 |
+ |
static void build_power_table(uint8_t table[], float ee) { |
for (int i = 0; i < 256; i++) { |
float x = i / 255.f; |
@@ -1050,6 +1102,9 @@ |
U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR); |
U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG); |
U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB); |
+#if SK_SHOW_TEXT_BLIT_COVERAGE |
+ r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10); |
+#endif |
return SkPack888ToRGB16(r, g, b); |
} |
@@ -1060,6 +1115,9 @@ |
U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR); |
U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG); |
U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB); |
+#if SK_SHOW_TEXT_BLIT_COVERAGE |
+ r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10); |
+#endif |
return SkPackARGB32(0xFF, r, g, b); |
} |
@@ -1130,6 +1188,14 @@ |
src = SkTAddOffset<const SkGdiRGB>(src, srcRB); |
dst -= dstRB; |
} |
+#if SK_SHOW_TEXT_BLIT_COVERAGE |
+ if (glyph.fWidth > 0 && glyph.fHeight > 0) { |
+ uint8_t* first = (uint8_t*)glyph.fImage; |
+ uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1); |
+ *first |= 1 << 7; |
+ *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount); |
+ } |
+#endif |
} |
template<bool APPLY_PREBLEND> |
@@ -1142,6 +1208,9 @@ |
for (int y = 0; y < glyph.fHeight; y++) { |
for (int i = 0; i < width; i++) { |
dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8); |
+#if SK_SHOW_TEXT_BLIT_COVERAGE |
+ dst[i] = SkMax32(dst[i], 10); |
+#endif |
} |
src = SkTAddOffset<const SkGdiRGB>(src, srcRB); |
dst -= dstRB; |
@@ -1238,6 +1307,15 @@ |
src += srcRB; |
dst -= dstRB; |
} |
+#if SK_SHOW_TEXT_BLIT_COVERAGE |
+ if (glyph.fWidth > 0 && glyph.fHeight > 0) { |
+ int bitCount = width & 7; |
+ uint8_t* first = (uint8_t*)glyph.fImage; |
+ uint8_t* last = (uint8_t*)((char*)glyph.fImage + glyph.fHeight * dstRB - 1); |
+ *first |= 1 << 7; |
+ *last |= bitCount == 0 ? 1 : 1 << (8 - bitCount); |
+ } |
+#endif |
} else if (isAA) { |
// since the caller may require A8 for maskfilters, we can't check for BW |
// ... until we have the caller tell us that explicitly |
@@ -1275,54 +1353,146 @@ |
} |
} |
-void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) { |
- SkASSERT(&glyph && path); |
- SkASSERT(fDDC); |
+class GDIGlyphbufferPointIter { |
+public: |
+ GDIGlyphbufferPointIter(const uint8_t* glyphbuf, DWORD total_size) |
+ : fHeaderIter(glyphbuf, total_size), fCurveIter(), fPointIter() |
+ { } |
- path->reset(); |
+ POINTFX next() { |
+nextHeader: |
+ if (!fCurveIter.isSet()) { |
+ const TTPOLYGONHEADER* header = fHeaderIter.next(); |
+ SkASSERT(header); |
+ fCurveIter.set(header); |
+ const TTPOLYCURVE* curve = fCurveIter.next(); |
+ SkASSERT(curve); |
+ fPointIter.set(curve); |
+ return header->pfxStart; |
+ } |
- GLYPHMETRICS gm; |
+ const POINTFX* nextPoint = fPointIter.next(); |
+ if (NULL == nextPoint) { |
+ const TTPOLYCURVE* curve = fCurveIter.next(); |
+ if (NULL == curve) { |
+ fCurveIter.set(); |
+ goto nextHeader; |
+ } else { |
+ fPointIter.set(curve); |
+ } |
+ nextPoint = fPointIter.next(); |
+ SkASSERT(nextPoint); |
+ } |
+ return *nextPoint; |
+ } |
- // Out of all the fonts on a typical Windows box, |
- // 25% of glyphs require more than 2KB. |
- // 1% of glyphs require more than 4KB. |
- // 0.01% of glyphs require more than 8KB. |
- // 8KB is less than 1% of the normal 1MB stack on Windows. |
- // Note that some web fonts glyphs require more than 20KB. |
- static const DWORD BUFFERSIZE = (1 << 13); |
- SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE); |
+ WORD currentCurveType() { |
+ return fPointIter.fCurveType; |
+ } |
- const UINT flags = GGO_NATIVE | GGO_GLYPH_INDEX; |
- DWORD total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, BUFFERSIZE, glyphbuf, &fMat22); |
- // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0. |
- // It has been verified that this does not involve a buffer overrun. |
- if (GDI_ERROR == total_size || total_size > BUFFERSIZE) { |
- // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible. |
- // When the data is not accessable GetGlyphOutlineW fails rather quickly, |
- // so just try to get the size. If that fails then ensure the data is accessible. |
- total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22); |
- if (GDI_ERROR == total_size) { |
- LogFontTypeface::EnsureAccessible(this->getTypeface()); |
- total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22); |
- if (GDI_ERROR == total_size) { |
- SkASSERT(false); |
- return; |
+private: |
+ /** Iterates over all of the polygon headers in a glyphbuf. */ |
+ class GDIPolygonHeaderIter { |
+ public: |
+ GDIPolygonHeaderIter(const uint8_t* glyphbuf, DWORD total_size) |
+ : fCurPolygon(reinterpret_cast<const TTPOLYGONHEADER*>(glyphbuf)) |
+ , fEndPolygon(SkTAddOffset<const TTPOLYGONHEADER>(glyphbuf, total_size)) |
+ { } |
+ |
+ const TTPOLYGONHEADER* next() { |
+ if (fCurPolygon >= fEndPolygon) { |
+ return NULL; |
} |
+ const TTPOLYGONHEADER* thisPolygon = fCurPolygon; |
+ fCurPolygon = SkTAddOffset<const TTPOLYGONHEADER>(fCurPolygon, fCurPolygon->cb); |
+ return thisPolygon; |
} |
+ private: |
+ const TTPOLYGONHEADER* fCurPolygon; |
+ const TTPOLYGONHEADER* fEndPolygon; |
+ }; |
- glyphbuf.reset(total_size); |
+ /** Iterates over all of the polygon curves in a polygon header. */ |
+ class GDIPolygonCurveIter { |
+ public: |
+ GDIPolygonCurveIter() : fCurCurve(NULL), fEndCurve(NULL) { } |
- DWORD ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf, &fMat22); |
- if (GDI_ERROR == ret) { |
- LogFontTypeface::EnsureAccessible(this->getTypeface()); |
- ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf, &fMat22); |
- if (GDI_ERROR == ret) { |
- SkASSERT(false); |
- return; |
+ GDIPolygonCurveIter(const TTPOLYGONHEADER* curPolygon) |
+ : fCurCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER))) |
+ , fEndCurve(SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb)) |
+ { } |
+ |
+ bool isSet() { return fCurCurve != NULL; } |
+ |
+ void set(const TTPOLYGONHEADER* curPolygon) { |
+ fCurCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, sizeof(TTPOLYGONHEADER)); |
+ fEndCurve = SkTAddOffset<const TTPOLYCURVE>(curPolygon, curPolygon->cb); |
+ } |
+ void set() { |
+ fCurCurve = NULL; |
+ fEndCurve = NULL; |
+ } |
+ |
+ const TTPOLYCURVE* next() { |
+ if (fCurCurve >= fEndCurve) { |
+ return NULL; |
} |
+ const TTPOLYCURVE* thisCurve = fCurCurve; |
+ fCurCurve = SkTAddOffset<const TTPOLYCURVE>(fCurCurve, size_of_TTPOLYCURVE(*fCurCurve)); |
+ return thisCurve; |
} |
- } |
+ private: |
+ size_t size_of_TTPOLYCURVE(const TTPOLYCURVE& curve) { |
+ return 2*sizeof(WORD) + curve.cpfx*sizeof(POINTFX); |
+ } |
+ const TTPOLYCURVE* fCurCurve; |
+ const TTPOLYCURVE* fEndCurve; |
+ }; |
+ /** Iterates over all of the polygon points in a polygon curve. */ |
+ class GDIPolygonCurvePointIter { |
+ public: |
+ GDIPolygonCurvePointIter() : fCurveType(0), fCurPoint(NULL), fEndPoint(NULL) { } |
+ |
+ GDIPolygonCurvePointIter(const TTPOLYCURVE* curPolygon) |
+ : fCurveType(curPolygon->wType) |
+ , fCurPoint(&curPolygon->apfx[0]) |
+ , fEndPoint(&curPolygon->apfx[curPolygon->cpfx]) |
+ { } |
+ |
+ bool isSet() { return fCurPoint != NULL; } |
+ |
+ void set(const TTPOLYCURVE* curPolygon) { |
+ fCurveType = curPolygon->wType; |
+ fCurPoint = &curPolygon->apfx[0]; |
+ fEndPoint = &curPolygon->apfx[curPolygon->cpfx]; |
+ } |
+ void set() { |
+ fCurPoint = NULL; |
+ fEndPoint = NULL; |
+ } |
+ |
+ const POINTFX* next() { |
+ if (fCurPoint >= fEndPoint) { |
+ return NULL; |
+ } |
+ const POINTFX* thisPoint = fCurPoint; |
+ ++fCurPoint; |
+ return thisPoint; |
+ } |
+ |
+ WORD fCurveType; |
+ private: |
+ const POINTFX* fCurPoint; |
+ const POINTFX* fEndPoint; |
+ }; |
+ |
+ GDIPolygonHeaderIter fHeaderIter; |
+ GDIPolygonCurveIter fCurveIter; |
+ GDIPolygonCurvePointIter fPointIter; |
+}; |
+ |
+static void sk_path_from_gdi_path(SkPath* path, const uint8_t* glyphbuf, DWORD total_size) { |
const uint8_t* cur_glyph = glyphbuf; |
const uint8_t* end_glyph = glyphbuf + total_size; |
@@ -1371,6 +1541,148 @@ |
} |
} |
+static void sk_path_from_gdi_paths(SkPath* path, const uint8_t* glyphbuf, DWORD total_size, |
+ GDIGlyphbufferPointIter hintedYs) { |
+ const uint8_t* cur_glyph = glyphbuf; |
+ const uint8_t* end_glyph = glyphbuf + total_size; |
+ |
+ while (cur_glyph < end_glyph) { |
+ const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; |
+ |
+ const uint8_t* end_poly = cur_glyph + th->cb; |
+ const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); |
+ |
+ path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)), |
+ SkFixedToScalar(-SkFIXEDToFixed(hintedYs.next().y))); |
+ |
+ while (cur_poly < end_poly) { |
+ const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; |
+ |
+ if (pc->wType == TT_PRIM_LINE) { |
+ for (uint16_t i = 0; i < pc->cpfx; i++) { |
+ path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)), |
+ SkFixedToScalar(-SkFIXEDToFixed(hintedYs.next().y))); |
+ } |
+ } |
+ |
+ if (pc->wType == TT_PRIM_QSPLINE) { |
+ POINTFX currentPoint = pc->apfx[0]; |
+ POINTFX hintedY = hintedYs.next(); |
+ // only take the hinted y if it wasn't flipped |
+ if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) { |
+ currentPoint.y = hintedY.y; |
+ } |
+ for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline |
+ POINTFX pnt_b = currentPoint;//pc->apfx[u]; // B is always the current point |
+ POINTFX pnt_c = pc->apfx[u+1]; |
+ POINTFX hintedY = hintedYs.next(); |
+ // only take the hinted y if it wasn't flipped |
+ if (hintedYs.currentCurveType() == TT_PRIM_QSPLINE) { |
+ pnt_c.y = hintedY.y; |
+ } |
+ currentPoint.x = pnt_c.x; |
+ currentPoint.y = pnt_c.y; |
+ |
+ if (u < pc->cpfx - 2) { // If not on last spline, compute C |
+ pnt_c.x = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.x), |
+ SkFIXEDToFixed(pnt_c.x))); |
+ pnt_c.y = SkFixedToFIXED(SkFixedAve(SkFIXEDToFixed(pnt_b.y), |
+ SkFIXEDToFixed(pnt_c.y))); |
+ } |
+ |
+ path->quadTo(SkFixedToScalar( SkFIXEDToFixed(pnt_b.x)), |
+ SkFixedToScalar(-SkFIXEDToFixed(pnt_b.y)), |
+ SkFixedToScalar( SkFIXEDToFixed(pnt_c.x)), |
+ SkFixedToScalar(-SkFIXEDToFixed(pnt_c.y))); |
+ } |
+ } |
+ // Advance past this TTPOLYCURVE. |
+ cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx; |
+ } |
+ cur_glyph += th->cb; |
+ path->close(); |
+ } |
+} |
+ |
+DWORD SkScalerContext_Windows::getGDIGlyphPath(const SkGlyph& glyph, UINT flags, |
+ SkAutoSTMalloc<BUFFERSIZE, uint8_t>* glyphbuf) |
+{ |
+ GLYPHMETRICS gm; |
+ |
+ DWORD total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, BUFFERSIZE, glyphbuf->get(), &fMat22); |
+ // Sometimes GetGlyphOutlineW returns a number larger than BUFFERSIZE even if BUFFERSIZE > 0. |
+ // It has been verified that this does not involve a buffer overrun. |
+ if (GDI_ERROR == total_size || total_size > BUFFERSIZE) { |
+ // GDI_ERROR because the BUFFERSIZE was too small, or because the data was not accessible. |
+ // When the data is not accessable GetGlyphOutlineW fails rather quickly, |
+ // so just try to get the size. If that fails then ensure the data is accessible. |
+ total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22); |
+ if (GDI_ERROR == total_size) { |
+ LogFontTypeface::EnsureAccessible(this->getTypeface()); |
+ total_size = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, 0, NULL, &fMat22); |
+ if (GDI_ERROR == total_size) { |
+ SkASSERT(false); |
+ return 0; |
+ } |
+ } |
+ |
+ glyphbuf->reset(total_size); |
+ |
+ DWORD ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf->get(), &fMat22); |
+ if (GDI_ERROR == ret) { |
+ LogFontTypeface::EnsureAccessible(this->getTypeface()); |
+ ret = GetGlyphOutlineW(fDDC, glyph.fID, flags, &gm, total_size, glyphbuf->get(), &fMat22); |
+ if (GDI_ERROR == ret) { |
+ SkASSERT(false); |
+ return 0; |
+ } |
+ } |
+ } |
+ return total_size; |
+} |
+ |
+void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) { |
+ SkASSERT(&glyph && path); |
+ SkASSERT(fDDC); |
+ |
+ path->reset(); |
+ |
+ // Out of all the fonts on a typical Windows box, |
+ // 25% of glyphs require more than 2KB. |
+ // 1% of glyphs require more than 4KB. |
+ // 0.01% of glyphs require more than 8KB. |
+ // 8KB is less than 1% of the normal 1MB stack on Windows. |
+ // Note that some web fonts glyphs require more than 20KB. |
+ //static const DWORD BUFFERSIZE = (1 << 13); |
+ |
+ //GDI only uses hinted outlines when axis aligned. |
+ UINT format = GGO_NATIVE | GGO_GLYPH_INDEX; |
+ if (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting){ |
+ format |= GGO_UNHINTED; |
+ } |
+ SkAutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE); |
+ DWORD total_size = getGDIGlyphPath(glyph, format, &glyphbuf); |
+ if (0 == total_size) { |
+ return; |
+ } |
+ |
+ if (fRec.getHinting() != SkPaint::kSlight_Hinting) { |
+ sk_path_from_gdi_path(path, glyphbuf, total_size); |
+ } else { |
+ //GDI only uses hinted outlines when axis aligned. |
+ UINT format = GGO_NATIVE | GGO_GLYPH_INDEX; |
+ |
+ SkAutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE); |
+ DWORD hinted_total_size = getGDIGlyphPath(glyph, format, &hintedGlyphbuf); |
+ if (0 == hinted_total_size) { |
+ return; |
+ } |
+ |
+ sk_path_from_gdi_paths(path, glyphbuf, total_size, |
+ GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size)); |
+ } |
+} |
+ |
static void logfont_for_name(const char* familyName, LOGFONT* lf) { |
sk_bzero(lf, sizeof(LOGFONT)); |
#ifdef UNICODE |
@@ -1795,20 +2107,19 @@ |
SkScalerContext::kAutohinting_Flag | |
SkScalerContext::kEmbeddedBitmapText_Flag | |
SkScalerContext::kEmbolden_Flag | |
- SkScalerContext::kSubpixelPositioning_Flag | |
SkScalerContext::kLCD_BGROrder_Flag | |
SkScalerContext::kLCD_Vertical_Flag; |
rec->fFlags &= ~flagsWeDontSupport; |
SkPaint::Hinting h = rec->getHinting(); |
- |
- // I think we can support no-hinting, if we get hires outlines and just |
- // use skia to rasterize into a gray-scale mask... |
-#if 0 |
switch (h) { |
case SkPaint::kNo_Hinting: |
+ break; |
case SkPaint::kSlight_Hinting: |
- h = SkPaint::kNo_Hinting; |
+ // Only do slight hinting when axis aligned. |
+ if (!isAxisAligned(*rec)) { |
+ h = SkPaint::kNo_Hinting; |
+ } |
break; |
case SkPaint::kNormal_Hinting: |
case SkPaint::kFull_Hinting: |
@@ -1817,9 +2128,7 @@ |
default: |
SkDEBUGFAIL("unknown hinting"); |
} |
-#else |
- h = SkPaint::kNormal_Hinting; |
-#endif |
+ //TODO: if this is a bitmap font, squash hinting and subpixel. |
rec->setHinting(h); |
// turn this off since GDI might turn A8 into BW! Need a bigger fix. |