Skip to content

Prevent blurry fonts when using CCLabelTTF

April 11, 2012

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
Advertisements

From → Development

8 Comments
  1. 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.

  2. Billy permalink

    Nice job!

  3. Alfie permalink

    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.

  4. 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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: