Permalink
Browse files

Improved underline and strike-out drawing.

Modified to avoid anti-aliasing by rounding pixels taking the context scale into account.

related to #351
  • Loading branch information...
1 parent caa80ab commit 45d0ce03247ef85d776bcf66db392de9599c3e16 @odrobnik odrobnik committed Mar 20, 2013
@@ -19,3 +19,24 @@ CTFontRef DTCTFontCreateWithUIFont(UIFont *font);
Converts an NSLineBreakMode into CoreText line truncation type
*/
CTLineTruncationType DTCTLineTruncationTypeFromNSLineBreakMode(NSLineBreakMode lineBreakMode);
+
+/**
+ Rounds the passed value according to the specifed content scale.
+
+ With contentScale 1 the results are identical to roundf, with Retina content scale 2 the results are multiples of 0.5.
+ */
+CGFloat DTRoundWithContentScale(CGFloat value, CGFloat contentScale);
+
+/**
+ Rounds up the passed value according to the specifed content scale.
+
+ With contentScale 1 the results are identical to roundf, with Retina content scale 2 the results are multiples of 0.5.
+ */
+CGFloat DTCeilWithContentScale(CGFloat value, CGFloat contentScale);
+
+/**
+ Rounds down the passed value according to the specifed content scale.
+
+ With contentScale 1 the results are identical to roundf, with Retina content scale 2 the results are multiples of 0.5.
+ */
+CGFloat DTFloorWithContentScale(CGFloat value, CGFloat contentScale);
@@ -43,3 +43,20 @@ CTLineTruncationType DTCTLineTruncationTypeFromNSLineBreakMode(NSLineBreakMode l
}
#endif
}
+
+CGFloat DTRoundWithContentScale(CGFloat value, CGFloat contentScale)
+{
+ return roundf(value*contentScale)/contentScale;
+}
+
+CGFloat DTCeilWithContentScale(CGFloat value, CGFloat contentScale)
+{
+ return ceilf(value*contentScale)/contentScale;
+}
+
+CGFloat DTFloorWithContentScale(CGFloat value, CGFloat contentScale)
+{
+ return floorf(value*contentScale)/contentScale;
+}
+
+
@@ -11,6 +11,7 @@
#import "DTTextAttachment.h"
#import "DTCoreTextConstants.h"
#import "DTCoreTextParagraphStyle.h"
+#import "DTCoreTextFunctions.h"
#ifndef __IPHONE_4_3
#define __IPHONE_4_3 40300
@@ -214,6 +215,11 @@ - (void)drawInContext:(CGContextRef)context
- (void)drawDecorationInContext:(CGContextRef)context
{
+ // get the scaling factor of the current translation matrix
+ CGAffineTransform ctm = CGContextGetCTM(context);
+ CGFloat contentScale = ctm.a; // needed for rounding operations
+ CGFloat smallestPixelWidth = 1.0f/contentScale;
+
CGColorRef backgroundColor = (__bridge CGColorRef)[_attributes objectForKey:DTBackgroundColorAttribute];
// can also be iOS 6 attribute
@@ -283,32 +289,46 @@ - (void)drawDecorationInContext:(CGContextRef)context
if (drawStrikeOut || drawUnderline)
{
+ CGContextSaveGState(context);
+
CTFontRef usedFont = (__bridge CTFontRef)([_attributes objectForKey:(id)kCTFontAttributeName]);
+ CGFloat fontUnderlineThickness;
+
if (usedFont)
{
- CGFloat underlineThickness = CTFontGetUnderlineThickness(usedFont);
- CGContextSetLineWidth(context, underlineThickness);
+ fontUnderlineThickness = CTFontGetUnderlineThickness(usedFont);
+ }
+ else
+ {
+ fontUnderlineThickness = smallestPixelWidth;
}
+ CGFloat usedUnderlineThickness = DTCeilWithContentScale(fontUnderlineThickness, contentScale);
+
+ CGContextSetLineWidth(context, usedUnderlineThickness);
+
if (drawStrikeOut)
{
CGFloat y;
if (usedFont)
{
CGFloat strokePosition = CTFontGetXHeight(usedFont)/2.0;
- y = runStrokeBounds.origin.y + _ascent - strokePosition;
+ y = DTRoundWithContentScale(runStrokeBounds.origin.y + _ascent - strokePosition, contentScale);
}
else
{
- y = roundf(runStrokeBounds.origin.y + _frame.size.height/2.0f + 1)+0.5f;
+ y = DTRoundWithContentScale((runStrokeBounds.origin.y + self.frame.size.height/2.0f + 1), contentScale);
+ }
+
+ if ((int)(usedUnderlineThickness/smallestPixelWidth)%2) // odd line width
+ {
+ y += smallestPixelWidth/2.0f; // shift down half a pixel to avoid aliasing
}
CGContextMoveToPoint(context, runStrokeBounds.origin.x, y);
CGContextAddLineToPoint(context, runStrokeBounds.origin.x + runStrokeBounds.size.width, y);
-
- CGContextStrokePath(context);
}
if (drawUnderline)
@@ -318,18 +338,27 @@ - (void)drawDecorationInContext:(CGContextRef)context
if (usedFont)
{
CGFloat underlinePosition = CTFontGetUnderlinePosition(usedFont);
- y = runStrokeBounds.origin.y + runStrokeBounds.size.height - _descent - underlinePosition;
+
+ y = DTRoundWithContentScale(runStrokeBounds.origin.y + runStrokeBounds.size.height - _descent - underlinePosition - fontUnderlineThickness/2.0f, contentScale);
}
else
{
- y = roundf(runStrokeBounds.origin.y + runStrokeBounds.size.height - _descent + 1)+0.5f;
+ y = DTRoundWithContentScale((runStrokeBounds.origin.y + runStrokeBounds.size.height - self.descent + 1.0f), contentScale);
+ }
+
+ if ((int)(usedUnderlineThickness/smallestPixelWidth)%2) // odd line width
+ {
+ y += smallestPixelWidth/2.0f; // shift down half a pixel to avoid aliasing
}
CGContextMoveToPoint(context, runStrokeBounds.origin.x, y);
CGContextAddLineToPoint(context, runStrokeBounds.origin.x + runStrokeBounds.size.width, y);
-
- CGContextStrokePath(context);
}
+
+
+ CGContextStrokePath(context);
+
+ CGContextRestoreGState(context); // restore antialiasing
}
}
}
@@ -1,10 +1,10 @@
<!-- shows different unline position in a hyperlink that has a part with superscript text -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html40/strict.dtd">
<html>
- <p style="font-family:Arial">Some <u>underlined underlined <sub>underlined</sub> <sup>underlined</sup> underlined</u> and <del>stroked stroked stroked</del> text</p>
- <p style="font-family:Arial;font-size:20px">Some <u>underlined underlined <sub>underlined</sub> <sup>underlined</sup> underlined</u> and <del>stroked stroked stroked</del> text</p>
- <p style="font-family:Arial;font-size:40px">Some <u>underlined underlined <sub>underlined</sub> <sup>underlined</sup> underlined</u> and <del>stroked stroked stroked</del> text</p>
- <p style="font-family:Arial;font-size:40px"><a href="http://www.cocoanetics.com">Some <u>underlined underlined <sub>underlined</sub> <sup>underlined</sup> underlined</u> and <del>stroked stroked stroked</del> text</a></p>
- <p dir="rtl" style="font-weight:normal;font-family:'XB Niloofar';font-size:34px"><u>بِسۡمِ ٱللَّهِ بِسۡمِ ٱللَّهِ ٱبِسۡمِ ٱللَّهِ ٱ بِسۡمِ ٱللَّهِ ٱ بِسۡمِ ٱللَّهِ ٱ بِسۡمِ ٱللَّهِ ٱ بِسۡمِ ٱللَّهِ ٱ</u></p>
+ <p style="font-family:'Times New Roman';font-size:50px">50px: <u>Underlining</u></p>
+ <p style="font-family:'Times New Roman';font-size:40px">40px: <u>Underlining</u></p>
+ <p style="font-family:'Times New Roman';font-size:30px">30px: <u>Underlining</u></p>
+ <p style="font-family:'Times New Roman';font-size:20px">20px: <u>Underlining</u></p>
+ <p style="font-family:'Times New Roman';font-size:10px">10px: <u>Underlining</u></p>
</body>
</html>

0 comments on commit 45d0ce0

Please sign in to comment.