Always round text position correctly.

https://codereview.appspot.com/7383049/

Will require rebaseline of fontscaler GM.

Must add SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX to Chromium
until ~150 layout tests can be rebaselined.



git-svn-id: http://skia.googlecode.com/svn/trunk@7842 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bungeman@google.com 2013-02-25 15:55:13 +00:00
parent 570ed6466c
commit 9447103029
4 changed files with 78 additions and 83 deletions

View File

@ -1606,6 +1606,12 @@ SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
fBlitter = blitter; fBlitter = blitter;
fCache = cache; fCache = cache;
if (cache->isSubpixel()) {
fHalfSampleX = fHalfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
} else {
fHalfSampleX = fHalfSampleY = SK_FixedHalf;
}
if (hasCustomD1GProc(*draw)) { if (hasCustomD1GProc(*draw)) {
// todo: fix this assumption about clips w/ custom // todo: fix this assumption about clips w/ custom
fClip = draw->fClip; fClip = draw->fClip;
@ -1662,15 +1668,13 @@ void SkDraw::drawText(const char text[], size_t byteLength,
SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
const SkMatrix* matrix = fMatrix; SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, matrix);
SkGlyphCache* cache = autoCache.getCache(); SkGlyphCache* cache = autoCache.getCache();
// transform our starting point // transform our starting point
{ {
SkPoint loc; SkPoint loc;
matrix->mapXY(x, y, &loc); fMatrix->mapXY(x, y, &loc);
x = loc.fX; x = loc.fX;
y = loc.fY; y = loc.fY;
} }
@ -1692,33 +1696,13 @@ void SkDraw::drawText(const char text[], size_t byteLength,
y -= stopY; y -= stopY;
} }
SkFixed fx = SkScalarToFixed(x);
SkFixed fy = SkScalarToFixed(y);
const char* stop = text + byteLength; const char* stop = text + byteLength;
SkFixed fxMask = ~0;
SkFixed fyMask = ~0;
if (cache->isSubpixel()) {
SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*matrix);
if (kX_SkAxisAlignment == baseline) {
fyMask = 0;
} else if (kY_SkAxisAlignment == baseline) {
fxMask = 0;
}
// apply bias here to avoid adding 1/2 the sampling frequency in the loop
fx += SK_FixedHalf >> SkGlyph::kSubBits;
fy += SK_FixedHalf >> SkGlyph::kSubBits;
} else {
fx += SK_FixedHalf;
fy += SK_FixedHalf;
}
SkAAClipBlitter aaBlitter; SkAAClipBlitter aaBlitter;
SkAutoBlitterChoose blitterChooser; SkAutoBlitterChoose blitterChooser;
SkBlitter* blitter = NULL; SkBlitter* blitter = NULL;
if (needsRasterTextBlit(*this)) { if (needsRasterTextBlit(*this)) {
blitterChooser.choose(*fBitmap, *matrix, paint); blitterChooser.choose(*fBitmap, *fMatrix, paint);
blitter = blitterChooser.get(); blitter = blitterChooser.get();
if (fRC->isAA()) { if (fRC->isAA()) {
aaBlitter.init(blitter, &fRC->aaRgn()); aaBlitter.init(blitter, &fRC->aaRgn());
@ -1730,6 +1714,22 @@ void SkDraw::drawText(const char text[], size_t byteLength,
SkDraw1Glyph d1g; SkDraw1Glyph d1g;
SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache); SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache);
SkFixed fxMask = ~0;
SkFixed fyMask = ~0;
if (cache->isSubpixel()) {
SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*fMatrix);
if (kX_SkAxisAlignment == baseline) {
fyMask = 0;
d1g.fHalfSampleY = SK_FixedHalf;
} else if (kY_SkAxisAlignment == baseline) {
fxMask = 0;
d1g.fHalfSampleX = SK_FixedHalf;
}
}
SkFixed fx = SkScalarToFixed(x) + d1g.fHalfSampleX;
SkFixed fy = SkScalarToFixed(y) + d1g.fHalfSampleY;
while (text < stop) { while (text < stop) {
const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask); const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
@ -1855,17 +1855,15 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
return; return;
} }
const SkMatrix* matrix = fMatrix;
SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, matrix); SkAutoGlyphCache autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
SkGlyphCache* cache = autoCache.getCache(); SkGlyphCache* cache = autoCache.getCache();
SkAAClipBlitterWrapper wrapper; SkAAClipBlitterWrapper wrapper;
SkAutoBlitterChoose blitterChooser; SkAutoBlitterChoose blitterChooser;
SkBlitter* blitter = NULL; SkBlitter* blitter = NULL;
if (needsRasterTextBlit(*this)) { if (needsRasterTextBlit(*this)) {
blitterChooser.choose(*fBitmap, *matrix, paint); blitterChooser.choose(*fBitmap, *fMatrix, paint);
blitter = blitterChooser.get(); blitter = blitterChooser.get();
if (fRC->isAA()) { if (fRC->isAA()) {
wrapper.init(*fRC, blitter); wrapper.init(*fRC, blitter);
@ -1877,29 +1875,33 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
AlignProc alignProc = pick_align_proc(paint.getTextAlign()); AlignProc alignProc = pick_align_proc(paint.getTextAlign());
SkDraw1Glyph d1g; SkDraw1Glyph d1g;
SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache); SkDraw1Glyph::Proc proc = d1g.init(this, blitter, cache);
TextMapState tms(*matrix, constY); TextMapState tms(*fMatrix, constY);
TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition); TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
if (cache->isSubpixel()) { if (cache->isSubpixel()) {
// maybe we should skip the rounding if linearText is set // maybe we should skip the rounding if linearText is set
SkAxisAlignment roundBaseline = SkComputeAxisAlignmentForHText(*matrix); SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*fMatrix);
SkFixed fxMask = ~0;
SkFixed fyMask = ~0;
if (kX_SkAxisAlignment == baseline) {
fyMask = 0;
#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
d1g.fHalfSampleY = SK_FixedHalf;
#endif
} else if (kY_SkAxisAlignment == baseline) {
fxMask = 0;
#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
d1g.fHalfSampleX = SK_FixedHalf;
#endif
}
if (SkPaint::kLeft_Align == paint.getTextAlign()) { if (SkPaint::kLeft_Align == paint.getTextAlign()) {
while (text < stop) { while (text < stop) {
tmsProc(tms, pos); tmsProc(tms, pos);
SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + (SK_FixedHalf >> SkGlyph::kSubBits); SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + d1g.fHalfSampleX;
SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + (SK_FixedHalf >> SkGlyph::kSubBits); SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + d1g.fHalfSampleY;
SkFixed fxMask = ~0;
SkFixed fyMask = ~0;
if (kX_SkAxisAlignment == roundBaseline) {
fyMask = 0;
} else if (kY_SkAxisAlignment == roundBaseline) {
fxMask = 0;
}
const SkGlyph& glyph = glyphCacheProc(cache, &text, const SkGlyph& glyph = glyphCacheProc(cache, &text,
fx & fxMask, fy & fyMask); fx & fxMask, fy & fyMask);
@ -1912,38 +1914,28 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
} else { } else {
while (text < stop) { while (text < stop) {
const char* currentText = text; const char* currentText = text;
const SkGlyph* glyph = &glyphCacheProc(cache, &text, 0, 0); const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
if (glyph->fWidth) { if (metricGlyph.fWidth) {
SkDEBUGCODE(SkFixed prevAdvX = glyph->fAdvanceX;) SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
SkDEBUGCODE(SkFixed prevAdvY = glyph->fAdvanceY;) SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
SkFixed fx, fy;
SkFixed fxMask = ~0;
SkFixed fyMask = ~0;
tmsProc(tms, pos); tmsProc(tms, pos);
SkIPoint fixedLoc;
alignProc(tms.fLoc, metricGlyph, &fixedLoc);
{ SkFixed fx = fixedLoc.fX + d1g.fHalfSampleX;
SkIPoint fixedLoc; SkFixed fy = fixedLoc.fY + d1g.fHalfSampleY;
alignProc(tms.fLoc, *glyph, &fixedLoc);
fx = fixedLoc.fX + (SK_FixedHalf >> SkGlyph::kSubBits);
fy = fixedLoc.fY + (SK_FixedHalf >> SkGlyph::kSubBits);
if (kX_SkAxisAlignment == roundBaseline) {
fyMask = 0;
} else if (kY_SkAxisAlignment == roundBaseline) {
fxMask = 0;
}
}
// have to call again, now that we've been "aligned" // have to call again, now that we've been "aligned"
glyph = &glyphCacheProc(cache, &currentText, const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
fx & fxMask, fy & fyMask); fx & fxMask, fy & fyMask);
// the assumption is that the advance hasn't changed // the assumption is that the metrics haven't changed
SkASSERT(prevAdvX == glyph->fAdvanceX); SkASSERT(prevAdvX == glyph.fAdvanceX);
SkASSERT(prevAdvY == glyph->fAdvanceY); SkASSERT(prevAdvY == glyph.fAdvanceY);
SkASSERT(glyph.fWidth);
proc(d1g, fx, fy, *glyph); proc(d1g, fx, fy, glyph);
} }
pos += scalarsPerPosition; pos += scalarsPerPosition;
} }
@ -1958,8 +1950,8 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
tmsProc(tms, pos); tmsProc(tms, pos);
proc(d1g, proc(d1g,
SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf, SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf, //d1g.fHalfSampleX,
SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf, SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf, //d1g.fHalfSampleY,
glyph); glyph);
} }
pos += scalarsPerPosition; pos += scalarsPerPosition;
@ -1976,8 +1968,8 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
alignProc(tms.fLoc, glyph, &fixedLoc); alignProc(tms.fLoc, glyph, &fixedLoc);
proc(d1g, proc(d1g,
fixedLoc.fX + SK_FixedHalf, fixedLoc.fX + SK_FixedHalf, //d1g.fHalfSampleX,
fixedLoc.fY + SK_FixedHalf, fixedLoc.fY + SK_FixedHalf, //d1g.fHalfSampleY,
glyph); glyph);
} }
pos += scalarsPerPosition; pos += scalarsPerPosition;

View File

@ -21,10 +21,18 @@ struct SkDraw1Glyph {
SkBlitter* fBlitter; SkBlitter* fBlitter;
SkGlyphCache* fCache; SkGlyphCache* fCache;
SkIRect fClipBounds; SkIRect fClipBounds;
/** Half the sampling frequency of the rasterized glyph in x. */
SkFixed fHalfSampleX;
/** Half the sampling frequency of the rasterized glyph in y. */
SkFixed fHalfSampleY;
// The fixed x,y are pre-rounded, so impls just trunc them down to ints. /** Draws one glyph.
// i.e. half the sampling frequency has been added. *
// e.g. 1/2 or 1/(2^(SkGlyph::kSubBits+1)) has already been added. * The x and y are pre-biased, so implementations may just truncate them.
* i.e. half the sampling frequency has been added.
* e.g. 1/2 or 1/(2^(SkGlyph::kSubBits+1)) has already been added.
* This added bias can be found in fHalfSampleX,Y.
*/
typedef void (*Proc)(const SkDraw1Glyph&, SkFixed x, SkFixed y, const SkGlyph&); typedef void (*Proc)(const SkDraw1Glyph&, SkFixed x, SkFixed y, const SkGlyph&);
Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache); Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache);

View File

@ -2182,13 +2182,8 @@ static void xps_draw_1_glyph(const SkDraw1Glyph& state,
SkXPSDrawProcs* procs = static_cast<SkXPSDrawProcs*>(state.fDraw->fProcs); SkXPSDrawProcs* procs = static_cast<SkXPSDrawProcs*>(state.fDraw->fProcs);
//Draw pre-adds half the sampling frequency for floor rounding. //Draw pre-adds half the sampling frequency for floor rounding.
if (state.fCache->isSubpixel()) { x -= state.fHalfSampleX;
x -= (SK_FixedHalf >> SkGlyph::kSubBits); y -= state.fHalfSampleY;
y -= (SK_FixedHalf >> SkGlyph::kSubBits);
} else {
x -= SK_FixedHalf;
y -= SK_FixedHalf;
}
XPS_GLYPH_INDEX* xpsGlyph = procs->xpsGlyphs.append(); XPS_GLYPH_INDEX* xpsGlyph = procs->xpsGlyphs.append();
uint16_t glyphID = skGlyph.getGlyphID(); uint16_t glyphID = skGlyph.getGlyphID();

View File

@ -145,7 +145,7 @@ static void test_advances(skiatest::Reporter* reporter) {
char txt[] = "long.text.with.lots.of.dots."; char txt[] = "long.text.with.lots.of.dots.";
for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) { for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) {
SkTypeface* face = SkTypeface::CreateFromName(faces[i], SkTypeface::kNormal); SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(faces[i], SkTypeface::kNormal));
paint.setTypeface(face); paint.setTypeface(face);
for (size_t j = 0; j < SK_ARRAY_COUNT(settings); j++) { for (size_t j = 0; j < SK_ARRAY_COUNT(settings); j++) {