I've started to learn Objective-c with making drawing app. Many people ask how to make a brush like in Photoshop, so I am posting here my solution.
Step-by-step instruction.
1) You need to store points of the curve that user has drawn by touch (its easy - you need to create NSMutableArray and add CGPoints created with location of touch to it in touchesMoved event)
2) Now lets create brush
This is how we are going to make it:
1) paint image that we are going to use as brush in appropriate color and scale it
2) draw this image along the path we created in 1st part
Create extension of UIImage class
and add following method
+(UIImage*)imageNamed:(NSString *)name withColor:(UIColor*)color Scale:(float)scale
{
UIImage* img=[UIImage imageNamed:name];
UIGraphicsBeginImageContext(img.size);
CGContextRef context=UIGraphicsGetCurrentContext();
[color setFill];
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGRect rect =CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, rect, img.CGImage);
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context, kCGPathFill);
UIImage*coloredImage=UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImage*ret=[UIImage imageWithCGImage:coloredImage.CGImage scale:1/scale orientation:coloredImage.imageOrientation];
return ret;
}
Now lets draw it
I've made my own classes to store curve and points: drawnCurve and drawnPoint, drawnCurve has NSMutableArray of drawnPoints-drawnCurve.points.
This method is called in drawnCurve class. shouldFar tells if we should draw images far from each other like in spray,or not-like in pencil.
-(void)primitiveDrawAtContext:(CGContextRef)context withBrush:(UIImage*)brush shouldFar:(BOOL)f1
CGFloat hyp;
hyp=sqrtf(brush.size.width*brush.size.width+brush.size.height*brush.size.height);
drawnPoint*beg=[self.points objectAtIndex:0];
CGContextMoveToPoint(context, beg.x, beg.y);
CGPoint t=CGPointMake(beg.x, beg.y);
CGLayerRef layer=CGLayerCreateWithContext(context, CGSizeMake(brush.size.width,brush.size.height), NULL);
CGContextRef tmp=CGLayerGetContext(layer);
CGContextDrawImage(tmp,CGRectMake(0, 0, brush.size.width, brush.size.height), brush.CGImage);
CGContextBeginTransparencyLayer(context, NULL);
CGContextDrawLayerAtPoint(context, CGPointMake(t.x-brush.size.width/2, t.y-brush.size.height/2), layer);
for(int i=0;i<self.points.count-1;i++)
{
drawnPoint* cur=[points objectAtIndex:i];
drawnPoint* next=[points objectAtIndex:i+1];
if(f1)
count=(int)sqrt((cur.x-next.x)*(cur.x-next.x)+(cur.y-next.y)*(cur.y-next.y))/(hyp);
else {
count=(int)sqrt((cur.x-next.x)*(cur.x-next.x)+(cur.y-next.y)*(cur.y-next.y))/(0.25*hyp);
}
double stepX=(next.x-cur.x)/count;
double stepY=(next.y-cur.y)/count;
for(int j=0;j<count+1;j++)
{
CGContextDrawLayerAtPoint(context,CGPointMake(cur.x+j*stepX-brush.size.width/2, (cur.y+j*stepY-brush.size.height/2)) , layer); }
if(f1==YES) {
if((count==0) && (sqrt(pow((cur.x-t.x),2)+pow((cur.y-t.y),2))> max(0.5*hyp,3)))
{
CGContextDrawLayerAtPoint(context, CGPointMake(cur.x-brush.size.width/2, cur.y-brush.size.height/2), layer);
t.x=cur.x;
t.y=cur.y;
}
}
else {
if((count==0) && (sqrt(pow((cur.x-t.x),2)+pow((cur.y-t.y),2))>min(3,0.5*hyp)))
{
CGContextDrawLayerAtPoint(context, CGPointMake(cur.x-brush.size.width/2, cur.y-brush.size.height/2), layer);
t.x=cur.x;
t.y=cur.y;
}
}
}
Here is the result
If you have any questions or you need some code - you are welcome to ask!