7

is there an easy way to not draw this points in my lines?
I don't know why this points are there because i never release my finger from screen during drawing of a line. enter image description here

I got the code from a drawing example.

// draw a line
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

mouseSwiped = YES;
UITouch *touch = [touches anyObject];   
CGPoint currentPoint = [touch locationInView:self.view];
currentPoint.y -= 0; // 20 only for 'kCGLineCapRound'
UIGraphicsBeginImageContext(self.view.frame.size);
//Albert Renshaw - Apps4Life
[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)]; //originally self.frame.size.width, self.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound); //kCGLineCapSquare, kCGLineCapButt, kCGLineCapRound
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushSize); // for size
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), r, g, b, alpha); //values for R, G, B, and Alpha
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

lastPoint = currentPoint;

mouseMoved++;

if (mouseMoved == 10) {
    mouseMoved = 0;
}

    }

    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {


//Draw a dot
if(!mouseSwiped) {

    UIGraphicsBeginImageContext(self.view.frame.size);
    [drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)]; //originally self.frame.size.width, self.frame.size.height)];
    CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound); //kCGLineCapSquare, kCGLineCapButt, kCGLineCapRound
    CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushSize);
    CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), r, g, b, alpha);
    CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
    CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
    CGContextStrokePath(UIGraphicsGetCurrentContext());
    CGContextFlush(UIGraphicsGetCurrentContext());
    drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}
    }

This is the final version with unique alpha, color, brushSize for every line:

- (void) updateDrawingBoard 
{
UIGraphicsBeginImageContext(self.drawImage.bounds.size);

for ( NSDictionary *dict in paths ) {

   UIBezierPath *p = (UIBezierPath*)[dict objectForKey:@"path"];
   p.lineWidth = [[dict objectForKey:@"size"]floatValue];
   [[UIColor colorWithRed:[[dict objectForKey:@"red"]floatValue] 
                     green:[[dict objectForKey:@"green"]floatValue] 
                      blue:[[dict objectForKey:@"blue"]floatValue] 
                     alpha:[[dict objectForKey:@"alpha"]floatValue]] setStroke];
  [p stroke];
}
[[UIColor colorWithRed:r green:g blue:b alpha:alpha] setStroke];
[path stroke];

self.drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInView:self.drawImage];

path = [[UIBezierPath bezierPath] retain];
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinBevel;
path.lineWidth = brushSize;
[path moveToPoint:touchPoint];

[self updateDrawingBoard];
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInView:self.drawImage];

[path addLineToPoint:touchPoint];

NSDictionary   *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                        path,@"path",
                        [NSNumber numberWithFloat:r], @"red", 
                        [NSNumber numberWithFloat:g], @"green", 
                        [NSNumber numberWithFloat:b], @"blue", 
                        [NSNumber numberWithFloat:alpha], @"alpha", 
                        [NSNumber numberWithFloat:brushSize], @"size", nil];
[paths addObject:dict];
[path release];
path = nil;

[self updateDrawingBoard];
}

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInView:self.drawImage];

[path addLineToPoint:touchPoint];

[self updateDrawingBoard];
}
madmax
  • 1,803
  • 3
  • 25
  • 50
  • Where does `alpha` come from? Looks like you can fix this by setting alpha to `1.` – cem Jun 07 '11 at 08:23
  • Yes i know, with alpha = 1 it works. But i want to have transparent color to highlight text beneath it. – madmax Jun 07 '11 at 08:27
  • Did you try to manipulate the line join? http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CGContext/Reference/reference.html%23//apple_ref/c/func/CGContextSetLineJoin – cem Jun 07 '11 at 08:33
  • Did you get the answer? I am facing same problem. – Prerna chavan Oct 05 '12 at 06:58

2 Answers2

6

The problem is that you are gradually accumulating the line by drawing into an image. Where the old line and the new line overlap, you see "dots" from the overdraw.

The solution is to accumulate your points in a path and draw the image afresh each time using that path. Since you will be drawing a single path, not multiple overlapping paths, you shouldn't see the dots.

Outline of code:

  • Some time before drawing begins, create a CGMutablePathRef.
  • When you get a new point you want to add to your line, use CGPathAddLineToPoint.
  • When it's time to draw the path, use CGContextAddPath to add the line to the context, then fill or stroke as desired. You could also use CGContextDrawPath.

Alternatively, you can use UIBezierPath instead of CGMutablePathRef, in which case the steps are:

  • Create a UIBezierPath.
  • Use -addLineToPoint: to add lines to the path.
  • Use -stroke, -fill, and similar to draw the path into the context.

This is likely to be simpler if you are not accustomed to working with CoreGraphics directly.

I would drop the intermediate image and move this code into a view class (LineDrawView or CanvasView or something) rather than leaving the code in a view controller. Instead of updating an image, the view can just draw itself directly to the screene. The view would have methods to clear the path, undo strokes, and create an image of the path. The view controller would then use these to clear the canvas, undo lines, and save the drawing. You could enrich this later with functionality to configure the line style.

Jeremy W. Sherman
  • 35,901
  • 5
  • 77
  • 111
  • Ok, I can draw now. But how can I save the paths to my view, or an image? Because I load in UIImageView and set it as a subview from this view, so i can draw above it. At the end I want to save the loaded image with the new paths in a file. Edited the code in my question. – madmax Jun 07 '11 at 18:21
  • 1
    Use `UIGraphicsBeginImageContextWithOptions` as you did in the old code. Make the size of the image be the bounds size of your view, `[self bounds].size`. Then, just draw as you normally would; you could call `[self drawRect:[self bounds]]`, provided your bounds origin is still (0, 0). – Jeremy W. Sherman Jun 07 '11 at 19:28
  • How is this supposed to work? Now my app crashes all time. I don't know where to put UIGraphicsBeginImageContext and the drawRect function. Edited my code. – madmax Jun 07 '11 at 21:14
  • At this point, you are flailing. You need to review the [Drawing and Printing Guide for iOS](http://developer.apple.com/library/ios/#documentation/2DDrawing/Conceptual/DrawingPrintingiOS/Introduction/Introduction.html). If you have further questions, feel free to ask a new question on Stack Overflow. – Jeremy W. Sherman Jun 07 '11 at 21:18
4

I have been trying to beat this problem for a few days, trying out Jeremy W. Sherman's answer that is probably a very good idea but was not feasable for my implementation.

In my drawing app, I could not get the alpha curves to blend into each other by constantly render the path, though the dots disappeared. If you just want a clean, alpha-colored path this is probably the way to go.

But I found a solution to this that was much simpler; these adjustments create a perfect, alpha-colored path in realtime with blending (think of it as a felt tip pen kind of style):

CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapButt);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeMultiply);

Hope this helps anyone who's doing some simple CG painting in iOS.

machineboy
  • 321
  • 3
  • 14