Prevent blurry fonts when using CCLabelTTF
Note: this in an updated blog (August 11th, 2012) to the original version, including a new download!
When using CCLabelTTF to use True Type fonts with Cocos2D, you might have noticed that they sometimes look pretty blurry, while in other places they appear reasonably sharp. What’s wrong here?
The rendering of the fonts tries to strictly consider the exact position of a font, which is given as a floating point number. If this position is not exactly hitting an actual pixel on the screen, the rendering is adjusted accordingly by introducing additional anti-aliasing, which makes the font appear to start exactly between two pixels. In theory. In reality, it simply looks blurry now, and I am sure in favor of a sharp font you would have rather adjusted a correct 0.3 pixel offset to the nearest pixel instead.
Fortunately, it is really not a big deal to do exactly this. We just need to derive a new class from CCLabelTTF and check (and possibly correct) the top left position each time it might change. Considering the complete node structure we are embedded in, we need to round the top left corner coordinates (in world/screen space) to a full pixel, and then recompute the position all the way back to our local coordinate system and fix the position parameter accordingly. Why the top left corner? Because the string is ultimately converted to a Cocos2D texture using the core NSString drawInRect method, which is based on the UIKit coordinate system and has its (0,0) coordinates always top left (rather than Cocos2D bottom left origin).
To make sure that rounding errors don’t sum up and make your label move uncontrolled, we always remember the originally intended position and adjust always referring to that position.
As a reward for reading this blog, you can simply download a ready-made class SharpLabelTTF right away! You can use it instead of CCLabelTTF and integrate it exactly like that. You don’t need to consider anything else.
Using CCLabelTTF:
Using SharpLabelTTF:
And here is the beef:
@implementation SharpLabelTTF - (void) fixPosition { CGSize dim = self.texture.contentSize; [super setPosition:intendedPosition_]; if (scaleX_ < 0.3 || scaleY_ < 0.3) return; // compute world (= screen) coordinate of top left position of label CGPoint worldSpaceTopleft = [self convertToWorldSpace: ccp(0,dim.height)]; // align origin on a pixel boundary on screen coordinates worldSpaceTopleft.x = roundf(worldSpaceTopleft.x * CC_CONTENT_SCALE_FACTOR()) / CC_CONTENT_SCALE_FACTOR(); worldSpaceTopleft.y = roundf(worldSpaceTopleft.y * CC_CONTENT_SCALE_FACTOR()) / CC_CONTENT_SCALE_FACTOR(); // transform back into node space CGPoint nodeSpaceTopleft = [self convertToNodeSpace:worldSpaceTopleft]; // adjust position by the computed delta CGPoint delta = ccpSub(nodeSpaceTopleft, ccp(0,dim.height)); CGPoint newPos = ccpAdd(position_, delta); // finally set the position data [super setPosition:newPos]; } // capture modification calls to adjust position - (void)onEnter { [self fixPosition]; [super onEnter]; } - (void)setParent:(CCNode *)parent { [super setParent:parent]; [self fixPosition]; } - (void)setString:(NSString *)str { [super setString:str]; [self fixPosition]; } - (void)setPosition:(CGPoint)position { intendedPosition_ = position; [self fixPosition]; } -(void)setRotation:(float)rotation { [super setRotation:rotation]; [self fixPosition]; } @end
I used this class in my project but there is a problem.
I have a fast counter (increased every 0.01 sec) very blurred with the original CCLabelTTF. With the Sharp version, it is unblurred ! But the label gets its position changed by +1 pix to the right every time it updates.
Any clue ?
I fixed that a while ago for me, but forgot to update the blog. I did that now, so please download V2.1.
It now always remembers the intended position and adjusts referring to that for every required fix, so that rounding errors don’t sum up and move your label uncontrolled.
Thank you very much ! Works like a charm =D !
Nice job!
Thank you! I’m a teen learning to develop games and software and this really helped me, thank you! Credit is given in the “Credits” section of my latest game.
As well as in the files that you created.
Hello, is there a Cocos2dx C++ port available? CAN it be ported just like that? Or is upgrading to Cocos2dx 3.x solving the problem? Thanks very much for any help!!
Hi, I am still just using Cocos 2D, which works great for my usecases. So I haven’t tried a port yet. The underlying principle is no rocket science, though, so porting should be easy.