cocos2d for iPhone

A fast, easy to use, free, and community supported 2D game engine

Register or log in - lost password?
  • Blog
  • Store
  • Games
  • Documentation
  • Download
  • About

cocos2d for iPhone » Programming » Programming - Everything else

Font Stroke

(33 posts) (20 voices)
  • Started 2 years ago by leocck
  • Latest reply from tcg

Tags:

  • antialising
  • CCLabel
  • CCRenderTexture
  • font
  • font outline
  • replica watches
  • replica watches for sale
  • replicawatches
  • Rotated
  • source
  • stroke
  • watches replica
12Next »
  1. leocck
    Member

    A long time ago I was thinking about a code to create a stroke around the letters to achieve better results than Hiero stroke.

    So I did it and I like to share with you others. I didn't search on cocos2d forum if there's another method already, if so please excuse me.

    The code can create a stroke on a CCLabel given a color and a size, and returns a CCRenderTexture to put behind the original CCLabel, giving this result:

    I'm not using the latest version of Cocos2D, and haven't test its performance with dynamic labels (since I'm using it on static hints). That been said, here's the code:

    +(CCRenderTexture*) createStroke: (CCLabel*) label   size:(float)size   color:(ccColor3B)cor
    {
    	CCRenderTexture* rt = [CCRenderTexture renderTextureWithWidth:label.texture.contentSize.width+size*2  height:label.texture.contentSize.height+size*2];
    	CGPoint originalPos = [label position];
    	ccColor3B originalColor = [label color];
    	[label setColor:cor];
    	ccBlendFunc originalBlend = [label blendFunc];
    	[label setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE }];
    	CGPoint center = ccp(label.texture.contentSize.width/2+size, label.texture.contentSize.height/2+size);
    	[rt begin];
    	for (int i=0; i<360; i+=15)
    	{
    		[label setPosition:ccp(center.x + sin(CC_DEGREES_TO_RADIANS(i))*size, center.y + cos(CC_DEGREES_TO_RADIANS(i))*size)];
    		[label visit];
    	}
    	[rt end];
    	[label setPosition:originalPos];
    	[label setColor:originalColor];
    	[label setBlendFunc:originalBlend];
    	[rt setPosition:originalPos];
    	return rt;
    }

    And here's an example of how to use it:

    CCLabel* label = [CCLabel labelWithString: @"Some Text"
    	dimensions:CGSizeMake(305,179) alignment:UITextAlignmentLeft
    	fontName:@"SomeFont" fontSize:23];
    [label setPosition:ccp(167,150)];
    [label setColor:ccWHITE];
    CCRenderTexture* stroke = [SomeUtilityClass createStroke:label  size:3  color:ccBLACK];
    [self addChild:stroke];
    [self addChild:label];
    Posted 2 years ago #
  2. hactar
    Moderator

    Thanks for sharing this. Nice results on your screen shot there. Bookmarking for possible future use.

    Posted 2 years ago #
  3. cocos
    Member

    @leocck Great! Thank you! Bookmarking for reference.

    Posted 2 years ago #
  4. carribus
    Member

    This really does look nice.

    My concern would be about the 360 iterations per text label.
    Although I don't have a better solution for the type of stroke quality you achieve, you could probably get away with an iteration of 8 loops, each loop changing the direction by 45 degrees.

    Posted 2 years ago #
  5. leocck
    Member

    @carribus
    In fact it doesn't have 360 iterations, since I'm adding 15 on each iteration, it'll have 24 iterations.
    But you can optimize the number of iterations, since thinner strokes with small fonts needs less iterations to look good ;)

    Posted 2 years ago #
  6. leocck
    Member

    I have updated the code, 'cause it had a visibility bug. Here's the code:

    +(CCRenderTexture*) createStroke: (CCLabel*) label   size:(float)size   color:(ccColor3B)cor
    {
    	CCRenderTexture* rt = [CCRenderTexture renderTextureWithWidth:label.texture.contentSize.width+size*2  height:label.texture.contentSize.height+size*2];
    	CGPoint originalPos = [label position];
    	ccColor3B originalColor = [label color];
    	BOOL originalVisibility = [label visible];
    	[label setColor:cor];
    	[label setVisible:YES];
    	ccBlendFunc originalBlend = [label blendFunc];
    	[label setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE }];
    	CGPoint meio = ccp(label.texture.contentSize.width/2+size, label.texture.contentSize.height/2+size);
    	[rt begin];
    	for (int i=0; i<360; i+=30) // you should optimize that for your needs
    	{
    		[label setPosition:ccp(meio.x + sin(CC_DEGREES_TO_RADIANS(i))*size, meio.y + cos(CC_DEGREES_TO_RADIANS(i))*size)];
    		[label visit];
    	}
    	[rt end];
    	[label setPosition:originalPos];
    	[label setColor:originalColor];
    	[label setBlendFunc:originalBlend];
    	[label setVisible:originalVisibility];
    	[rt setPosition:originalPos];
    	return rt;
    }
    Posted 2 years ago #
  7. jd
    Member

    Sweet. Adding to favorites.

    Posted 2 years ago #
  8. bobertperry
    Member

    I updated your code so the anchor point does not have to be .5,.5.

    +(CCRenderTexture*) createStroke: (CCLabelTTF*) label   size:(float)size   color:(ccColor3B)cor
    {
    	CCRenderTexture* rt = [CCRenderTexture renderTextureWithWidth:label.texture.contentSize.width+size*2  height:label.texture.contentSize.height+size*2];
    	CGPoint originalPos = [label position];
    	ccColor3B originalColor = [label color];
    	BOOL originalVisibility = [label visible];
    	[label setColor:cor];
    	[label setVisible:YES];
    	ccBlendFunc originalBlend = [label blendFunc];
    	[label setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE }];
    	CGPoint bottomLeft = ccp(label.texture.contentSize.width * label.anchorPoint.x + size, label.texture.contentSize.height * label.anchorPoint.y + size);
    	CGPoint positionOffset = ccp(label.texture.contentSize.width * label.anchorPoint.x - label.texture.contentSize.width/2,label.texture.contentSize.height * label.anchorPoint.y - label.texture.contentSize.height/2);
    	CGPoint position = ccpSub(originalPos, positionOffset);
    
    	[rt begin];
    	for (int i=0; i<360; i+=30) // you should optimize that for your needs
    	{
    		[label setPosition:ccp(bottomLeft.x + sin(CC_DEGREES_TO_RADIANS(i))*size, bottomLeft.y + cos(CC_DEGREES_TO_RADIANS(i))*size)];
    		[label visit];
    	}
    	[rt end];
    	[label setPosition:originalPos];
    	[label setColor:originalColor];
    	[label setBlendFunc:originalBlend];
    	[label setVisible:originalVisibility];
    	[rt setPosition:position];
    	return rt;
    }

    and made a wrapper for CCMenuItemFont to include your stroke thingy:

    @implementation CCMenuItemFontWithStroke
    
    -(id) initFromString: (NSString*) value target:(id) rec selector:(SEL) cb
    {
    	NSAssert( [value length] != 0, @"Value length must be greater than 0");
    
    	CCLabelTTF *label = [CCLabelTTF labelWithString:value fontName:[CCMenuItemFontWithStroke fontName] fontSize:[CCMenuItemFontWithStroke fontSize]];
    
    	if((self=[super initWithLabel:label target:rec selector:cb]) ) {
    		// do something ?
    	}
    
    	CCRenderTexture * stroke  = [WhateverClassItsIn createStroke:label size:3 color:strokeColor];
    	[self addChild:stroke z:-1];
    
    	return self;
    }
    
    @end

    Works freeking awesome~

    Posted 2 years ago #
  9. bobertperry
    Member

    The wrapper I mentioned before did not update when you update a menu's string. I updated the code to fix this if anyone is interested.

    @implementation CCMenuItemFontWithStroke
    
    #define kTagStroke 1029384756
    
    -(id) initFromString: (NSString*) value target:(id) rec selector:(SEL) cb
    {
    	self = [super initFromString:value target:rec selector:cb];
    
    	if ([label_ isKindOfClass: [CCLabelTTF class]]) {
    		CCRenderTexture * stroke  = [GameSingleton createStroke:(CCLabelTTF*)label_ size:3 color:strokeColor];
    		[self addChild:stroke z:-1 tag:kTagStroke];
    	}else{
    		NSLog(@"Error adding stroke in menu, label_ is not a CCLabelTTF.  This has only been tested on cocos2d 99.5");
    	}
    
    	return self;
    }
    
    -(void) setString:(NSString *)string
    {
    	[super setString:string];
    
    	if ([label_ isKindOfClass: [CCLabelTTF class]]) {
    		[self removeChildByTag:kTagStroke cleanup:YES];
    		CCRenderTexture * stroke  = [GameSingleton createStroke:(CCLabelTTF*)label_ size:3 color:strokeColor];
    		[self addChild:stroke z:-1 tag:kTagStroke];
    
    	}else{
    		NSLog(@"Error adding stroke in menu, label_ is not a CCLabelTTF.  This has only been tested on cocos2d 99.5");
    	}
    
    }
    
    @end
    Posted 2 years ago #
  10. leocck
    Member

    Many thanks bobertperry !

    Posted 2 years ago #
  11. FiberCore
    Member

    Thank you @leocck ,thanks for your wonderful work. I use CCLabelBMFont, if anyone use it like me, you should change

    CCRenderTexture* rt = [CCRenderTexture renderTextureWithWidth:label.texture.contentSize.width+size*2  height:label.texture.contentSize.height+size*2];

    to

    CCRenderTexture* rt = [CCRenderTexture renderTextureWithWidth:label.contentSize.width+size*2  height:label.contentSize.height+size*2];
    Posted 2 years ago #
  12. leocck
    Member

    Thanks @FiberCore

    It seems imageshack don't works anymore. Here's a new image to see the result.

    Posted 2 years ago #
  13. FiberCore
    Member

    Hi @leocck, I met a problem with stroke color, I created a label with stroke like this:

    static const ccColor3B ccGreenOutline = {209,255,130};
    	static const ccColor3B ccCoffeeColor = {102,59,49};
    	......
    	CCLabelBMFont *timerLabel = [CCLabelBMFont labelWithString:@"Your" fntFile:@"SomeFont.fnt"];
    	timerLabel.positionInPixels = ccp(200, 200);
    	timerLabel.color = ccCoffeeColor;
    	CCRenderTexture* stroke = [CCLabelBMFont createStroke:timerLabel size:3 color:ccGreenOutline];
    	[self addChild:stroke z:30];
    	[self addChild:timerLabel z:30];

    But I just got wrong label with white stroke color, which I expect should be green.

    The correct color is :

    I tested

    static const ccColor3B ccWHITE = {255,255,255};
    static const ccColor3B ccYELLOW = {255,255,0};
    static const ccColor3B ccBLUE = {0,0,255};
    static const ccColor3B ccGREEN = {0,255,0};
    static const ccColor3B ccRED = {255,0,0};

    It works fine. But I cannot got right ccGreenOutline (r = 209, g = 255, b = 130)

    Posted 2 years ago #
  14. leocck
    Member

    This seems like a blend problem... Try some blending parameters on this line to see if it works better:

    [label setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE }];
    Posted 2 years ago #
  15. FiberCore
    Member

    Thanks, GL_SRC_COLOR works fine :)

    Posted 2 years ago #
  16. leocck
    Member

    Wow that's great, thanks @FiberCore

    Posted 2 years ago #
  17. varedis
    Moderator

    I have a slight problem trying to use this, using a dark color works fine but I am trying to add a white stroke to some of my labels and the color gets blended with the original color in places, I have tried changing blending modes but this is the best I can get it to look:

    This is using:

    [label setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR }];

    Posted 2 years ago #
  18. wybielacz
    Member

    Could someone point me how to add the font stroke to a CCMenuItemFont ?

    Posted 2 years ago #
  19. JesseJames
    Member

    Hi,
    I have troubles updating the stroke when the label changes. I tried this but it doesn't work:

    // update label
    [labelTime setString: time];
    // update stroke ?!
    labelTimeStroke = [SomeUtilityClass createStroke:labelTime size:1 color:ccBLACK];

    cheers,
    Tex

    Posted 2 years ago #
  20. wybielacz
    Member

    @JesseJames You have to remove the stroke and add it again at each update in order to make it work.

    Anyone can tell me how to add the font stroke to a CCMenuItemFont ?

    Posted 2 years ago #
  21. timTheMystic
    Member

    You could do something like this:

    CCLabel* aLabel = [CCLabel labelWithString: @"Trip Up!"
    				dimensions: CGSizeMake(200, 40)
                                     alignment: UITextAlignmentCenter
                                    fontName: @"Marker Felt"
                                     fontSize:36];
    
    aLabel.color = ccc3(255, 255, 10);
    aLabelItem = [CCMenuItemLabel itemWithLabel:aLabel target:self selector:@selector(gotoMainMenu:)];
    
    CCRenderTexture* strokeT = [GenUtils createStroke:aLabel size:2  color:ccc3(255,240,255) withBlend:1];
     aLabelItem.position = ccp(0, 0);
     strokeT.position = ccp(240, 160);
     [_menuLayer addChild:strokeT];
    Posted 2 years ago #
  22. Zhenmuron
    Member

    Is there any simple way to change the stroke alpha/opacity after its creation, if I would like to fade in/out the text? Or do I basically have to redraw the stroke each update with a different alpha?

    Posted 2 years ago #
  23. bobertperry
    Member

    I know its old but I ended up doing this too:

    Could someone point me how to add the font stroke to a CCMenuItemFont ?

    .h

    //
    //  CCMenuItemFontWithStroke.h
    //  test2
    //
    //  Created by Robert Perry on 2/5/11.
    //  Copyright 2011 __MyCompanyName__. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import "cocos2d.h"
    
    @interface CCMenuItemFontWithStroke : CCMenuItemFont  {
        int stokeSize;
        ccColor3B strokeColor;
    
    }
    
    @property (nonatomic) int stokeSize;
    @property (nonatomic)ccColor3B strokeColor;
    
    -(id) initFromString: (NSString*) value target:(id) rec selector:(SEL) cb  strokeSize:(int)strokeSize stokeColor:(ccColor3B)color;
    @end

    .m

    //
    //  CCMenuItemFontWithStroke.m
    //  test2
    //
    //  Created by Robert Perry on 2/5/11.
    //  Copyright 2011 __MyCompanyName__. All rights reserved.
    //
    
    #import "CCMenuItemFontWithStroke.h"
    
    @implementation CCMenuItemFontWithStroke
    
    @synthesize stokeSize;
    @synthesize strokeColor;
    
    #define kTagStroke 1029384756
    
    +(CCRenderTexture*) createStroke: (CCLabelTTF*) label   size:(float)size   color:(ccColor3B)cor
    {
    	CCRenderTexture* rt = [CCRenderTexture renderTextureWithWidth:label.texture.contentSize.width+size*2  height:label.texture.contentSize.height+size*2];
    	CGPoint originalPos = [label position];
    	ccColor3B originalColor = [label color];
    	BOOL originalVisibility = [label visible];
    	[label setColor:cor];
    	[label setVisible:YES];
    	ccBlendFunc originalBlend = [label blendFunc];
    	[label setBlendFunc:(ccBlendFunc) { GL_SRC_ALPHA, GL_ONE }];
    	CGPoint bottomLeft = ccp(label.texture.contentSize.width * label.anchorPoint.x + size, label.texture.contentSize.height * label.anchorPoint.y + size);
    	//CGPoint positionOffset = ccp(label.texture.contentSize.width * label.anchorPoint.x - label.texture.contentSize.width/2,label.texture.contentSize.height * label.anchorPoint.y - label.texture.contentSize.height/2);
        //use this for adding stoke to its self...
        CGPoint positionOffset= ccp(-label.contentSize.width/2,-label.contentSize.height/2);
    
    	CGPoint position = ccpSub(originalPos, positionOffset);
    
    	[rt begin];
    	for (int i=0; i<360; i+=30) // you should optimize that for your needs
    	{
    		[label setPosition:ccp(bottomLeft.x + sin(CC_DEGREES_TO_RADIANS(i))*size, bottomLeft.y + cos(CC_DEGREES_TO_RADIANS(i))*size)];
    		[label visit];
    	}
    	[rt end];
    	[label setPosition:originalPos];
    	[label setColor:originalColor];
    	[label setBlendFunc:originalBlend];
    	[label setVisible:originalVisibility];
    	[rt setPosition:position];
    	return rt;
    }
    
    -(id) initFromString: (NSString*) value target:(id) rec selector:(SEL) cb  strokeSize:(int)strokeSize stokeColor:(ccColor3B)color
    {
    
    	self = [super initFromString:value target:rec selector:cb];
    
        self.strokeColor = color;
        self.stokeSize = strokeSize;
    
    	if ([label_ isKindOfClass: [CCLabelTTF class]]) {
    		CCRenderTexture * stroke  = [CCMenuItemFontWithStroke createStroke:(CCLabelTTF*)label_ size:strokeSize color:strokeColor];
    		[self addChild:stroke z:-1 tag:kTagStroke];
    	}else{
    		NSLog(@"Error adding stroke in menu, label_ is not a CCLabelTTF.  This has only been tested on cocos2d 99.5");
    	}
    
    	return self;
    }
    
    //default 1 pixel, black
    -(id) initFromString: (NSString*) value target:(id) rec selector:(SEL) cb {
        return [self initFromString:value target:rec selector:cb strokeSize:3 stokeColor:ccBLACK];
    }
    
    -(void) setString:(NSString *)string
    {
    	[super setString:string];
    
    	if ([label_ isKindOfClass: [CCLabelTTF class]]) {
    		[self removeChildByTag:kTagStroke cleanup:YES];
    		CCRenderTexture * stroke  = [CCMenuItemFontWithStroke createStroke:(CCLabelTTF*)label_ size:stokeSize color:strokeColor];
    		[self addChild:stroke z:-1 tag:kTagStroke];
    
    	}else{
    		NSLog(@"Error adding stroke in menu, label_ is not a CCLabelTTF.  This has only been tested on cocos2d 99.5");
    	}
    
    }
    
    @end
    Posted 2 years ago #
  24. betzerra
    Member

    I couldn't make it work... no font stroke displayed. Any thoughts on what I'm doing wrong?
    Sorry about my dumb question, I'm new on cocos2d.

    Posted 1 year ago #
  25. praveencastelino
    Member

    Its not working when the fill color is white and fill color is red(can be anything).
    I guess there is some problem with blend function.
    Any idea on what should be the blending function?

    Posted 1 year ago #
  26. timTheMystic
    Member

    You could give this a try: (ccBlendFunc) { GL_SRC_COLOR, GL_ONE }

    Posted 1 year ago #
  27. praveencastelino
    Member

    it does work but there is a white background. What could be the reason. http://imageshack.us/photo/my-images/96/screenshot20120203at123.png
    fillColor - white
    stroke Color - green

    Posted 1 year ago #
  28. badben
    Member

    Thanks !
    :)

    Works on cocos2d 1.0.0

    Posted 12 months ago #
  29. Pwiggin
    Member

    Okay. Uber dumb question incoming, but where exactly does this code go? Before the init of my scene?

    Posted 11 months ago #
  30. Mudaseriqbal
    Member

    Any solution other then CCRenderTexture, instead of adding the render texture's sprite to the scene graph, the render texture itself is being added. As for the CCArray (the node's children_ array) being over-released,

    Signal 11 4 libobjc.A.dylib 0x37869175 _objc_rootRelease 36 5 test 0x0003e6c3 -[CCArray dealloc] 54 6 libobjc.A.dylib 0x37869175 _objc_rootRelease 36 7 test 0x0001cb71 -[CCNode dealloc] 172 8 test 0x000259c7 -[CCRenderTexture dealloc] 62

    Posted 10 months ago #

RSS feed for this topic

12Next »

Reply »

You must log in to post.

cocos2d for iPhone is proudly powered by bbPress.