From cb6ccdde5120ec45df208c0b958b263d8252a505 Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Tue, 23 Aug 2011 21:30:47 +0000 Subject: respect subpixel positioning git-svn-id: http://skia.googlecode.com/svn/trunk@2165 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkScalerContext.h | 26 ++++++++++ src/core/SkDraw.cpp | 35 +++---------- src/ports/SkFontHost_mac_coretext.cpp | 95 +++++++++++++++++++++++++++++++---- 3 files changed, 118 insertions(+), 38 deletions(-) diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h index fe59b2a18e..e2ce537de1 100644 --- a/include/core/SkScalerContext.h +++ b/include/core/SkScalerContext.h @@ -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 diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index 12c8288055..8d7f9b5f60 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -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; } } diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp index 5a5ad8f605..a0c42c02c4 100644 --- a/src/ports/SkFontHost_mac_coretext.cpp +++ b/src/ports/SkFontHost_mac_coretext.cpp @@ -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, -- cgit v1.2.3