| 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.
|
|
|