aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ports
diff options
context:
space:
mode:
authorGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-03-11 18:51:19 +0000
committerGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-03-11 18:51:19 +0000
commitd1c7f71ee371738a8c7896a4f06d28b32e3c713e (patch)
tree626c9cab9000a77c1c514cddc039a1270684b59b /src/ports
parente0e385c1d4171e065348ba17c546b3463a0bd651 (diff)
Use correct metrics for GDI glyphs.
https://codereview.appspot.com/7635045/ This will require rebaselining all GMs using GDI text. On the Chromium side this will require rebaslining fast/writing-mode/Kusa-Makura-background-canvas.html git-svn-id: http://skia.googlecode.com/svn/trunk@8069 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/ports')
-rwxr-xr-xsrc/ports/SkFontHost_win.cpp300
1 files changed, 113 insertions, 187 deletions
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 49289712e1..0f7295f5a0 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -29,65 +29,6 @@
#include <usp10.h>
#include <objbase.h>
-static bool compute_bounds_outset(const LOGFONT& lf, SkIRect* outset) {
-
- static const struct {
- const char* fUCName; // UTF8 encoded, ascii is upper-case
- SkIRect fOutset; // these are deltas for the glyph's bounds
- } gData[] = {
- // http://code.google.com/p/chromium/issues/detail?id=130842
- { "DOTUM", { 0, 0, 0, 1 } },
- { "DOTUMCHE", { 0, 0, 0, 1 } },
- { "\xEB\x8F\x8B\xEC\x9B\x80", { 0, 0, 0, 1 } },
- { "\xEB\x8F\x8B\xEC\x9B\x80\xEC\xB2\xB4", { 0, 0, 0, 1 } },
- { "MS UI GOTHIC", { 1, 0, 0, 0 } },
- };
-
- /**
- * We convert the target name into upper-case (for ascii chars) UTF8.
- * Our database is already stored in this fashion, and it allows us to
- * search it with straight memcmp, since everyone is in this canonical
- * form.
- */
-
- // temp storage is max # TCHARs * max expantion for UTF8 + null
- char name[kMaxBytesInUTF8Sequence * LF_FACESIZE + 1];
- int index = 0;
- for (int i = 0; i < LF_FACESIZE; ++i) {
- uint16_t c = lf.lfFaceName[i];
- if (c >= 'a' && c <= 'z') {
- c = c - 'a' + 'A';
- }
- size_t n = SkUTF16_ToUTF8(&c, 1, &name[index]);
- index += n;
- if (0 == c) {
- break;
- }
- }
-
- for (size_t j = 0; j < SK_ARRAY_COUNT(gData); ++j) {
- if (!strcmp(gData[j].fUCName, name)) {
- *outset = gData[j].fOutset;
- return true;
- }
- }
- return false;
-}
-
-// outset isn't really a rect, but 4 (non-negative) values to outset the
-// glyph's metrics by. For "normal" fonts, all these values should be 0.
-static void apply_outset(SkGlyph* glyph, const SkIRect& outset) {
- SkASSERT(outset.fLeft >= 0);
- SkASSERT(outset.fTop >= 0);
- SkASSERT(outset.fRight >= 0);
- SkASSERT(outset.fBottom >= 0);
-
- glyph->fLeft -= outset.fLeft;
- glyph->fTop -= outset.fTop;
- glyph->fWidth += outset.fLeft + outset.fRight;
- glyph->fHeight += outset.fTop + outset.fBottom;
-}
-
// always packed xxRRGGBB
typedef uint32_t SkGdiRGB;
@@ -557,21 +498,12 @@ private:
HDCOffscreen fOffscreen;
SkScalar fScale; // to get from canonical size to real size
MAT2 fMat22;
- XFORM fXform;
HDC fDDC;
HFONT fSavefont;
HFONT fFont;
SCRIPT_CACHE fSC;
int fGlyphCount;
- /**
- * Some fonts need extra pixels added to avoid clipping, as the bounds
- * returned by getOutlineMetrics does not match what GDI draws. Since
- * this costs more RAM and therefore slower blits, we have a table to
- * only do this for known "bad" fonts.
- */
- SkIRect fOutset;
-
HFONT fHiResFont;
MAT2 fMat22Identity;
SkMatrix fHiResMatrix;
@@ -634,10 +566,6 @@ SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
lf.lfQuality = compute_quality(fRec);
fFont = CreateFontIndirect(&lf);
- if (!compute_bounds_outset(lf, &fOutset)) {
- fOutset.setEmpty();
- }
-
// if we're rotated, or want fractional widths, create a hires font
fHiResFont = 0;
if (needHiResMetrics(fRec.fPost2x2)) {
@@ -670,23 +598,27 @@ SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
SkASSERT(!(fTM.tmPitchAndFamily & TMPF_VECTOR) ||
(fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)));
+ XFORM xform;
if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
// 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;
- fXform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]);
- fXform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]);
- fXform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]);
- fXform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]);
- fXform.eDx = 0;
- fXform.eDy = 0;
-
- fMat22.eM11 = float2FIXED(fXform.eM11);
- fMat22.eM12 = float2FIXED(fXform.eM12);
- fMat22.eM21 = float2FIXED(-fXform.eM21);
- fMat22.eM22 = float2FIXED(-fXform.eM22);
+ // 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.eDx = 0;
+ xform.eDy = 0;
+
+ // MAT2 is row major, right handed (y up).
+ fMat22.eM11 = float2FIXED(xform.eM11);
+ fMat22.eM12 = float2FIXED(-xform.eM12);
+ fMat22.eM21 = float2FIXED(-xform.eM21);
+ fMat22.eM22 = float2FIXED(xform.eM22);
if (needToRenderWithSkia(fRec)) {
this->forceGenerateImageFromPath();
@@ -697,17 +629,19 @@ SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
fType = SkScalerContext_Windows::kBitmap_Type;
fScale = SK_Scalar1;
- fXform.eM11 = 1.0f;
- fXform.eM12 = 0.0f;
- fXform.eM21 = 0.0f;
- fXform.eM22 = 1.0f;
- fXform.eDx = 0.0f;
- fXform.eDy = 0.0f;
+ xform.eM11 = 1.0f;
+ xform.eM12 = 0.0f;
+ xform.eM21 = 0.0f;
+ xform.eM22 = 1.0f;
+ xform.eDx = 0.0f;
+ xform.eDy = 0.0f;
+ // fPost2x2 is column-major, left handed (y down).
+ // MAT2 is row major, right handed (y up).
fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
- fMat22.eM12 = SkScalarToFIXED(fRec.fPost2x2[1][0]);
+ fMat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[1][0]);
fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
- fMat22.eM22 = SkScalarToFIXED(-fRec.fPost2x2[1][1]);
+ fMat22.eM22 = SkScalarToFIXED(fRec.fPost2x2[1][1]);
lf.lfHeight = -SkScalarCeilToInt(fRec.fTextSize);
HFONT bitmapFont = CreateFontIndirect(&lf);
@@ -722,7 +656,7 @@ SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
}
}
- fOffscreen.init(fFont, fXform);
+ fOffscreen.init(fFont, xform);
}
SkScalerContext_Windows::~SkScalerContext_Windows() {
@@ -781,7 +715,6 @@ void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
}
void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
-
SkASSERT(fDDC);
if (fType == SkScalerContext_Windows::kBitmap_Type) {
@@ -799,73 +732,69 @@ void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
glyph->fAdvanceX = SkIntToFixed(glyph->fWidth);
glyph->fAdvanceY = 0;
- //Apply matrix to values.
+ // Apply matrix to advance.
glyph->fAdvanceY = SkFixedMul(SkFIXEDToFixed(fMat22.eM21), glyph->fAdvanceX);
glyph->fAdvanceX = SkFixedMul(SkFIXEDToFixed(fMat22.eM11), glyph->fAdvanceX);
- apply_outset(glyph, fOutset);
return;
}
+ UINT glyphId = glyph->getGlyphID(0);
+
GLYPHMETRICS gm;
sk_bzero(&gm, sizeof(gm));
- glyph->fRsbDelta = 0;
- glyph->fLsbDelta = 0;
-
- // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
- // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
- uint32_t ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
- if (GDI_ERROR == ret) {
+ DWORD status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
+ if (GDI_ERROR == status) {
ensure_typeface_accessible(fRec.fFontID);
- ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
+ status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
+ if (GDI_ERROR == status) {
+ glyph->zeroMetrics();
+ return;
+ }
}
- if (GDI_ERROR != ret) {
- if (ret == 0) {
- // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
- gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
- }
- glyph->fWidth = gm.gmBlackBoxX;
- glyph->fHeight = gm.gmBlackBoxY;
- glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
- glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
- glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
- glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
-
- // we outset in all dimensions, since the image may bleed outside
- // of the computed bounds returned by GetGlyphOutline.
- // This was deduced by trial and error for small text (e.g. 8pt), so there
- // maybe a more precise way to make this adjustment...
- //
- // This test shows us clipping the tops of some of the CJK fonts unless we
- // increase the top of the box by 2, hence the height by 4. This seems to
- // correspond to an embedded bitmap font, but not sure.
- // LayoutTests/fast/text/backslash-to-yen-sign-euc.html
- //
- if (glyph->fWidth) { // don't outset an empty glyph
- glyph->fWidth += 4;
- glyph->fHeight += 4;
- glyph->fTop -= 2;
- glyph->fLeft -= 2;
-
- apply_outset(glyph, fOutset);
- }
+ bool empty = false;
+ // The black box is either the embedded bitmap size or the outline extent.
+ // It is 1x1 if nothing is to be drawn, but will also be 1x1 if something very small
+ // is to be drawn, like a '.'. We need to outset '.' but do not wish to outset ' '.
+ if (1 == gm.gmBlackBoxX && 1 == gm.gmBlackBoxY) {
+ // If GetGlyphOutline with GGO_NATIVE returns 0, we know there was no outline.
+ DWORD bufferSize = GetGlyphOutlineW(fDDC, glyphId, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22);
+ empty = (0 == bufferSize);
+ }
- if (fHiResFont) {
- SelectObject(fDDC, fHiResFont);
- sk_bzero(&gm, sizeof(gm));
- ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
- if (GDI_ERROR != ret) {
- SkPoint advance;
- fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
- glyph->fAdvanceX = SkScalarToFixed(advance.fX);
- glyph->fAdvanceY = SkScalarToFixed(advance.fY);
- }
- SelectObject(fDDC, fFont);
- }
+ glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
+ glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
+ if (empty) {
+ glyph->fWidth = 0;
+ glyph->fHeight = 0;
} else {
- glyph->zeroMetrics();
+ // Outset, since the image may bleed out of the black box.
+ // For embedded bitmaps the black box should be exact.
+ // For outlines we need to outset by 1 in all directions for bleed.
+ // For ClearType we need to outset by 2 for bleed.
+ glyph->fWidth = gm.gmBlackBoxX + 4;
+ glyph->fHeight = gm.gmBlackBoxY + 4;
+ glyph->fTop -= 2;
+ glyph->fLeft -= 2;
+ }
+ glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
+ glyph->fAdvanceY = SkIntToFixed(gm.gmCellIncY);
+ glyph->fRsbDelta = 0;
+ glyph->fLsbDelta = 0;
+
+ if (fHiResFont) {
+ SelectObject(fDDC, fHiResFont);
+ sk_bzero(&gm, sizeof(gm));
+ status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
+ if (GDI_ERROR != status) {
+ SkPoint advance;
+ fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY), &advance);
+ glyph->fAdvanceX = SkScalarToFixed(advance.fX);
+ glyph->fAdvanceY = SkScalarToFixed(advance.fY);
+ }
+ SelectObject(fDDC, fFont);
}
}
@@ -1228,73 +1157,70 @@ void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
- SkAutoMutexAcquire ac(gFTMutex);
+ SkAutoMutexAcquire ac(gFTMutex);
SkASSERT(&glyph && path);
SkASSERT(fDDC);
path->reset();
-#if 0
- char buf[1024];
- sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
- OutputDebugString(buf);
-#endif
-
GLYPHMETRICS gm;
uint32_t total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
if (GDI_ERROR == total_size) {
ensure_typeface_accessible(fRec.fFontID);
total_size = GetGlyphOutlineW(fDDC, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &fMat22);
+ if (GDI_ERROR == total_size) {
+ SkASSERT(false);
+ return;
+ }
}
- if (GDI_ERROR != total_size) {
+ const uint8_t* cur_glyph = glyphbuf;
+ const uint8_t* end_glyph = glyphbuf + total_size;
- 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;
- 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);
- 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(th->pfxStart.y)));
- path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
+ while (cur_poly < end_poly) {
+ const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
- 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(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
- }
+ 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(pc->apfx[i].y)));
}
+ }
- if (pc->wType == TT_PRIM_QSPLINE) {
- for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
- POINTFX pnt_b = pc->apfx[u]; // B is always the current point
- POINTFX pnt_c = pc->apfx[u+1];
-
- if (u < pc->cpfx - 2) { // If not on last spline, compute C
- pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
- pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
- }
+ if (pc->wType == TT_PRIM_QSPLINE) {
+ for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
+ POINTFX pnt_b = pc->apfx[u]; // B is always the current point
+ POINTFX pnt_c = pc->apfx[u+1];
- path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&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)));
}
- cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
}
- cur_glyph += th->cb;
- path->close();
+ // Advance past this TTPOLYCURVE.
+ cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx;
}
+ cur_glyph += th->cb;
+ path->close();
}
- else {
- SkASSERT(false);
- }
- //char buf[1024];
- //sprintf(buf, "generatePath: count:%d\n", count);
- //OutputDebugString(buf);
}
static void logfont_for_name(const char* familyName, LOGFONT& lf) {