aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkScalerContext.h26
-rw-r--r--src/core/SkDraw.cpp35
-rw-r--r--src/ports/SkFontHost_mac_coretext.cpp95
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,