Drawing tool in iOS ( openGLES or UIBezierPath )

New to the problem of making a drawing/painting tool for an iOS application many developers get confused on using either OpenGLES or UIBeizerPath. Sometimes sample code on apples developer website GLPAINT  points many developers to use OpenGL Views for making paint layers for an application. A major issue with this that the developers do not understand is that OpenGL Views leave very heavy memory footprints and on devices like ipad1 having 24mb of memory to use per view , this can be a pain.

Trying to make a transparent openGL view which allows the user to paint on a transparent canvas with the background views visible is a problem I faced. Often crashes start occuring once you add multiple openGL views on a single view i.e adding multiple transparent paint canvases on a single view. These crashes are shown to be occuring because of the application receiving memory warning and the delegate “didReceiveMemoryWarning” being called.

After spending a lot of time with OpenGL , trying to figure out  a way to reduce the memory footprint (using instruments in Xcode), one gives up. The solution thus thought turns out that ipad1 cannot support multiple paint layers and the functionality has to be restricted for user to be able to add only a single layer per view, which indeed is not true.

Now comes the boon of UIBeizerPath class. This class introduced in iOS 4 SDK  makes it a lot easier for a developer to make multiple transparent paint canvases and add it to a single view without having a high memory footprint.

Steps on how to make use of UIBezierPath :

–       Add a new UIView class to the project , name it paintCanvasView.

–       Add two objects  in header file,

UIBezierPath *bPath;

UIColor *bColor;

–       Initialize these objects in the init method by overridding it.

self.backgroundColor=[UIColor clearColor]; //Any color

bPath=[[UIBezierPath alloc]init];

bPath.lineWidth=10;

bColor=[UIColor redColor]; //Any color

–       Override drawRect

[bColor setStroke];

[bPath strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];

–       Implement Touches Began

UITouch *bTouch=[[touches allObjects] objectAtIndex:0];

[bPath moveToPoint:[ bTouch locationInView:self]];

–       Implement Touches Moved

UITouch *bTouch=[[touches allObjects] objectAtIndex:0];

[bPath addLineToPoint:[bTouch locationInView:self]];

//We need to reset the display at each finger movement

[self setNeedsDisplay];

That is it.

You can use this PaintCanvasView Object in your code like any other UIView Object.  I hope this post would help some developers who are unaware of UIBeizerPath class and its uses. A further implementation of this is adding pattern to the rendered strokes. This pattern can be rendered on the strokes using pattern image , assuming pattern.png. Replacing the bColor with a pattern image in init method would do this trick.


bColor=[[UIColor alloc]initWithPatternImage:[UIImage imageNamed:@"pattern.png"]];

 

Update 16May’12

Adding infinite undo and Redo managers:

–   Add two arrays to paintCanvasView.h

NSMutableArray *pathArray;

NSMutableArray *bufferArray;

–   Add two methods to paintCanvasView.h


-(void)undoButtonClicked;

-(void)redoButtonClicked;

–   Allocate the arrays in init method


pathArray=[[NSMutableArray alloc]init];

bufferArray=[[NSMutableArray alloc]init];

–   Modify drawRect in paintCanvasView.m to draw from pathArray


- (void)drawRect:(CGRect)rect

{

[[UIColor redColor] setStroke];

for (UIBezierPath *_path in pathArray)

[_path strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];

}

–   Add bPath to pathArray  in touchesBegan


UITouch *btouch=[[touches allObjects] objectAtIndex:0];

[bPath moveToPoint:[btouch locationInView:self]];

[pathArray addObject:bPath];

–   Define undoButtonClicked and redoButtonClicked to modify pathArray as desired.


-(void)undoButtonClicked

{

if([pathArray count]>0){

UIBezierPath *_path=[pathArray lastObject];

[bufferArray addObject:_path];

[pathArray removeLastObject];

[self setNeedsDisplay];

}

}

-(void)redoButtonClicked

{

if([bufferArray count]>0){

UIBezierPath *_path=[bufferArray lastObject];

[pathArray addObject:_path];

[bufferArray removeLastObject];

[self setNeedsDisplay];

}

}

That is it.

There is no rocket science to make an infinite undo manager for paint or sketch. One can simply maintain two arrays, buffer and path. By easily maintaining all items removed from path array in buffer array, redo functionality is achieved.

One thought on “Drawing tool in iOS ( openGLES or UIBezierPath )

  1. Hi,

    Thanks for the detailed explanation. But I have loaded texture using OpenGL es2.0 , now i want to draw to create a layer/mask on that texture. Could you share how to achieve it. onDraw method will not call when we will use CAEagleLayer.

    Regards
    Sathiya

Leave a Reply

Your email address will not be published. Required fields are marked *