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
This commit is contained in:
parent
e0e385c1d4
commit
d1c7f71ee3
@ -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;
|
||||
// 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;
|
||||
|
||||
fMat22.eM11 = float2FIXED(fXform.eM11);
|
||||
fMat22.eM12 = float2FIXED(fXform.eM12);
|
||||
fMat22.eM21 = float2FIXED(-fXform.eM21);
|
||||
fMat22.eM22 = float2FIXED(-fXform.eM22);
|
||||
// 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,64 +732,63 @@ 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;
|
||||
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);
|
||||
}
|
||||
glyph->fWidth = gm.gmBlackBoxX;
|
||||
glyph->fHeight = gm.gmBlackBoxY;
|
||||
glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
|
||||
|
||||
glyph->fTop = SkToS16(-gm.gmptGlyphOrigin.y);
|
||||
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;
|
||||
if (empty) {
|
||||
glyph->fWidth = 0;
|
||||
glyph->fHeight = 0;
|
||||
} else {
|
||||
// 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;
|
||||
|
||||
apply_outset(glyph, fOutset);
|
||||
}
|
||||
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));
|
||||
ret = GetGlyphOutlineW(fDDC, glyph->getGlyphID(0), GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL, &fMat22Identity);
|
||||
if (GDI_ERROR != ret) {
|
||||
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);
|
||||
@ -864,9 +796,6 @@ void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
|
||||
}
|
||||
SelectObject(fDDC, fFont);
|
||||
}
|
||||
} else {
|
||||
glyph->zeroMetrics();
|
||||
}
|
||||
}
|
||||
|
||||
void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
|
||||
@ -1235,20 +1164,16 @@ void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
|
||||
|
||||
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;
|
||||
@ -1259,14 +1184,16 @@ void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
|
||||
const uint8_t* end_poly = cur_glyph + th->cb;
|
||||
const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
|
||||
|
||||
path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
|
||||
path->moveTo(SkFixedToScalar( SkFIXEDToFixed(th->pfxStart.x)),
|
||||
SkFixedToScalar(-SkFIXEDToFixed(th->pfxStart.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(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
|
||||
path->lineTo(SkFixedToScalar( SkFIXEDToFixed(pc->apfx[i].x)),
|
||||
SkFixedToScalar(-SkFIXEDToFixed(pc->apfx[i].y)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1276,26 +1203,25 @@ void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
|
||||
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)));
|
||||
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(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&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;
|
||||
// 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) {
|
||||
memset(&lf, 0, sizeof(LOGFONT));
|
||||
|
Loading…
Reference in New Issue
Block a user