respect subpixel positioning

git-svn-id: http://skia.googlecode.com/svn/trunk@2165 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2011-08-23 21:30:47 +00:00
parent 1f221a7021
commit cb6ccdde51
3 changed files with 118 additions and 38 deletions

View File

@ -293,5 +293,31 @@ private:
#define kMaskFilter_SkDescriptorTag SkSetFourByteTag('m', 's', 'k', 'f')
#define kRasterizer_SkDescriptorTag SkSetFourByteTag('r', 'a', 's', 't')
///////////////////////////////////////////////////////////////////////////////
enum SkAxisAlignment {
kNone_SkAxisAlignment,
kX_SkAxisAlignment,
kY_SkAxisAlignment
};
/**
* Return the axis (if any) that the baseline for horizontal text will land on
* after running through the specified matrix.
*
* As an example, the identity matrix will return kX_SkAxisAlignment
*/
static SkAxisAlignment SkComputeAxisAlignmentForHText(const SkMatrix& matrix) {
SkASSERT(!matrix.hasPerspective());
if (0 == matrix[SkMatrix::kMSkewY]) {
return kX_SkAxisAlignment;
}
if (0 == matrix[SkMatrix::kMScaleX]) {
return kY_SkAxisAlignment;
}
return kNone_SkAxisAlignment;
}
#endif

View File

@ -1438,24 +1438,6 @@ SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
}
}
enum RoundBaseline {
kDont_Round_Baseline,
kRound_X_Baseline,
kRound_Y_Baseline
};
static RoundBaseline computeRoundBaseline(const SkMatrix& mat) {
if (mat[1] == 0 && mat[3] == 0) {
// we're 0 or 180 degrees, round the y coordinate of the baseline
return kRound_Y_Baseline;
} else if (mat[0] == 0 && mat[4] == 0) {
// we're 90 or 270 degrees, round the x coordinate of the baseline
return kRound_X_Baseline;
} else {
return kDont_Round_Baseline;
}
}
///////////////////////////////////////////////////////////////////////////////
void SkDraw::drawText(const char text[], size_t byteLength,
@ -1523,11 +1505,10 @@ void SkDraw::drawText(const char text[], size_t byteLength,
SkFixed fxMask = ~0;
SkFixed fyMask = ~0;
if (paint.isSubpixelText()) {
RoundBaseline roundBaseline = computeRoundBaseline(*matrix);
if (kRound_Y_Baseline == roundBaseline) {
SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(*matrix);
if (kX_SkAxisAlignment == baseline) {
fyMask = 0;
// fy = (fy + 0x8000) & ~0xFFFF;
} else if (kRound_X_Baseline == roundBaseline) {
} else if (kY_SkAxisAlignment == baseline) {
fxMask = 0;
}
}
@ -1698,7 +1679,7 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
if (paint.isSubpixelText()) {
// maybe we should skip the rounding if linearText is set
RoundBaseline roundBaseline = computeRoundBaseline(*matrix);
SkAxisAlignment roundBaseline = SkComputeAxisAlignmentForHText(*matrix);
if (SkPaint::kLeft_Align == paint.getTextAlign()) {
while (text < stop) {
@ -1710,9 +1691,9 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
SkFixed fxMask = ~0;
SkFixed fyMask = ~0;
if (kRound_Y_Baseline == roundBaseline) {
if (kX_SkAxisAlignment == roundBaseline) {
fyMask = 0;
} else if (kRound_X_Baseline == roundBaseline) {
} else if (kY_SkAxisAlignment == roundBaseline) {
fxMask = 0;
}
@ -1743,9 +1724,9 @@ void SkDraw::drawPosText(const char text[], size_t byteLength,
fx = fixedLoc.fX;
fy = fixedLoc.fY;
if (kRound_Y_Baseline == roundBaseline) {
if (kX_SkAxisAlignment == roundBaseline) {
fyMask = 0;
} else if (kRound_X_Baseline == roundBaseline) {
} else if (kY_SkAxisAlignment == roundBaseline) {
fxMask = 0;
}
}

View File

@ -63,6 +63,26 @@ static bool isLCDFormat(unsigned format) {
return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
}
static CGFloat ScalarToCG(SkScalar scalar) {
if (sizeof(CGFloat) == sizeof(float)) {
return SkScalarToFloat(scalar);
} else {
SkASSERT(sizeof(CGFloat) == sizeof(double));
return SkScalarToDouble(scalar);
}
}
static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
float sx = 1, float sy = 1) {
return CGAffineTransformMake(ScalarToCG(matrix[SkMatrix::kMScaleX]) * sx,
-ScalarToCG(matrix[SkMatrix::kMSkewY]) * sy,
-ScalarToCG(matrix[SkMatrix::kMSkewX]) * sx,
ScalarToCG(matrix[SkMatrix::kMScaleY]) * sy,
ScalarToCG(matrix[SkMatrix::kMTransX]) * sx,
ScalarToCG(matrix[SkMatrix::kMTransY]) * sy);
}
//============================================================================
// Macros
//----------------------------------------------------------------------------
@ -343,17 +363,11 @@ SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
// mColorSpaceRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
// mColorSpaceRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);
mColorSpaceGray = CGColorSpaceCreateDeviceGray();
mTransform = CGAffineTransformMake(SkScalarToFloat(skMatrix[SkMatrix::kMScaleX]),
-SkScalarToFloat(skMatrix[SkMatrix::kMSkewY]),
-SkScalarToFloat(skMatrix[SkMatrix::kMSkewX]),
SkScalarToFloat(skMatrix[SkMatrix::kMScaleY]),
SkScalarToFloat(skMatrix[SkMatrix::kMTransX]),
SkScalarToFloat(skMatrix[SkMatrix::kMTransY]));
mTransform = MatrixToCGAffineTransform(skMatrix);
// since our matrix includes everything, we pass 1 for pointSize
mFont = CTFontCreateCopyWithAttributes(ctFont, 1, &mTransform, NULL);
mGlyphCount = (uint16_t) numGlyphs;
mGlyphCount = SkToU16(numGlyphs);
}
SkScalerContext_Mac::~SkScalerContext_Mac(void)
@ -547,11 +561,17 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
CGContextSetAllowsFontSmoothing(cgContext, doLCD);
// need to pass the fractional part of our position to cg...
bool doSubPosition = SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
CGContextSetAllowsFontSubpixelPositioning(cgContext, doSubPosition);
CGContextSetShouldSubpixelPositionFonts(cgContext, doSubPosition);
float subX = 0;
float subY = 0;
if (doSubPosition) {
subX = SkFixedToFloat(glyph.getSubXFixed());
subY = SkFixedToFloat(glyph.getSubYFixed());
}
// skia handles quantization itself, so we disable this for cg to get
// full fractional data from them.
CGContextSetAllowsFontSubpixelQuantization(cgContext, false);
@ -563,7 +583,9 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
CGContextSetFont( cgContext, cgFont);
CGContextSetFontSize( cgContext, 1); // cgFont know's its size
CGContextSetTextMatrix( cgContext, mTransform);
CGContextShowGlyphsAtPoint( cgContext, -glyph.fLeft, glyph.fTop + glyph.fHeight, &cgGlyph, 1);
CGContextShowGlyphsAtPoint( cgContext, -glyph.fLeft + subX,
glyph.fTop + glyph.fHeight - subY,
&cgGlyph, 1);
if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
// downsample from rgba to rgb565
@ -608,15 +630,66 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
CFSafeRelease(cgContext);
}
/*
* Our subpixel resolution is only 2 bits in each direction, so a scale of 4
* seems sufficient, and possibly even correct, to allow the hinted outline
* to be subpixel positioned.
*/
#define kScaleForSubPixelPositionHinting 4
void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
CTFontRef font = mFont;
float scaleX = 1;
float scaleY = 1;
/*
* For subpixel positioning, we want to return an unhinted outline, so it
* can be positioned nicely at fractional offsets. However, we special-case
* if the baseline of the (horizontal) text is axis-aligned. In those cases
* we want to retain hinting in the direction orthogonal to the baseline.
* e.g. for horizontal baseline, we want to retain hinting in Y.
* The way we remove hinting is to scale the font by some value (4) in that
* direction, ask for the path, and then scale the path back down.
*/
if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
SkMatrix m;
fRec.getSingleMatrix(&m);
// start out by assuming that we want no hining in X and Y
scaleX = scaleY = kScaleForSubPixelPositionHinting;
// now see if we need to restore hinting for axis-aligned baselines
switch (SkComputeAxisAlignmentForHText(m)) {
case kX_SkAxisAlignment:
scaleY = 1; // want hinting in the Y direction
break;
case kY_SkAxisAlignment:
scaleX = 1; // want hinting in the X direction
break;
default:
break;
}
CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
// need to release font when we're done
font = CTFontCreateCopyWithAttributes(mFont, 1, &xform, NULL);
}
CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
CGPathRef cgPath = CTFontCreatePathForGlyph(mFont, cgGlyph, NULL);
CGPathRef cgPath = CTFontCreatePathForGlyph(font, cgGlyph, NULL);
path->reset();
if (cgPath != NULL) {
CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
CFRelease(cgPath);
}
if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
SkMatrix m;
m.setScale(SkFloatToScalar(1 / scaleX), SkFloatToScalar(1 / scaleY));
path->transform(m);
// balance the call to CTFontCreateCopyWithAttributes
CFRelease(font);
}
}
void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,