V.V
V.V

Reputation: 3094

issue in drawing line using core graphics : bubbles are shown

Using core graphics , I am trying to draw a line, in that when I try to add shadow is creates lin and bubbles inside the line, see the image, I am not able to solve the issue. please find my code below

-(void)drawRect:(CGRect)rect
{

    [curImage drawAtPoint:CGPointMake(0, 0)];
    CGPoint mid1 = midPoint(previousPoint1, previousPoint2); 
    CGPoint mid2 = midPoint(currentPoint, previousPoint1);

    CGContextRef context = UIGraphicsGetCurrentContext(); 

    [self.layer renderInContext:context];

    CGContextMoveToPoint(context, mid1.x, mid1.y);
    CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y); 
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, self.lineWidth);
    CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);

    CGContextSetShadow(context, CGSizeMake(-16.00, -5.0f), 5.0f);

    CGContextStrokePath(context);

    [super drawRect:rect];

    [curImage release];

}

Below is the touchesMoved method.

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

    UITouch *touch  = [touches anyObject];

    previousPoint2  = previousPoint1;
    previousPoint1  = [touch previousLocationInView:self];
    currentPoint    = [touch locationInView:self];

    // calculate mid point
    CGPoint mid1    = midPoint(previousPoint1, previousPoint2); 
    CGPoint mid2    = midPoint(currentPoint, previousPoint1);

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, mid1.x, mid1.y);
    CGPathAddQuadCurveToPoint(path, NULL, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
    CGRect bounds = CGPathGetBoundingBox(path);
    CGPathRelease(path);

    CGRect drawBox = bounds;

    //Pad our values so the bounding box respects our line width
    drawBox.origin.x        -= self.lineWidth * 2;
    drawBox.origin.y        -= self.lineWidth * 2;
    drawBox.size.width      += self.lineWidth * 4;
    drawBox.size.height     += self.lineWidth * 4;

    UIGraphicsBeginImageContext(drawBox.size);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    curImage = UIGraphicsGetImageFromCurrentImageContext();
    [curImage retain];
    UIGraphicsEndImageContext();
   [self setNeedsDisplayInRect:drawBox];

}

CGPoint midPoint(CGPoint p1, CGPoint p2)
{
    return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);
}


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

    UITouch *touch = [touches anyObject];

    previousPoint1 = [touch previousLocationInView:self];
    previousPoint2 = [touch previousLocationInView:self];
    currentPoint = [touch locationInView:self];

    [self touchesMoved:touches withEvent:event];
}

Here the issue arrives when I try to add the line

CGContextSetShadow(context, CGSizeMake(-16.00, -5.0f), 5.0f); 

Issue image

Please help me out from the issue, thanks in advance. please see editing question with methods .

Upvotes: 10

Views: 1393

Answers (3)

DivineDesert
DivineDesert

Reputation: 6954

Another way round to this is to create an array of points on touch move events, and then draw them..

Create an array of points :

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[pointArray release]; pointArray = nil;
pointArray=[[NSMutableArray alloc] init];
UITouch *touch = [touches anyObject];
[bufferArray removeAllObjects];

previousPoint1 = [touch previousLocationInView:self];
previousPoint2 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];

}


-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
previousPoint2 = previousPoint1;
previousPoint1 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];
[pointArray addObject: [NSValue valueWithCGPoint:previousPoint2]];
[pointArray addObject:[NSValue valueWithCGPoint:previousPoint1]];   
[pointArray addObject:[NSValue valueWithCGPoint:currentPoint]];
[self setNeedsDisplay];
}

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

}


-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext(); 
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);

CGContextSetShadow(context, CGSizeMake(-16.00, -5.0f), 5.0f);

for(int i = 1 ; i < [pointArray count]-1;i++)
{
CGPoint mid1 = midPoint([pointArray objectAtIndex:i+1], [pointArray objectAtIndex:i]); 
CGPoint mid2 = midPoint([pointArray objectAtIndex:i+2], [pointArray objectAtIndex:i+1]);


CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y); 
i+=2;
}
CGContextStrokePath(context);

}

You can manage multiple lines with multiple dimensional array :)

Upvotes: 2

gschandler
gschandler

Reputation: 3208

It would appear you are drawing a series of segments as quad curves. The bubbles would be where the previous segment and the current segment overlap. Since your segment line is a solid red, you don't notice the overlap, but it shows up in the shadows as darker regions.

Try setting your stroke color to have and alpha of 0.5 (or some such translucency). I think (know) you will see the overlapping segments will also show a similar effect as the the shadow.

To solve it, you will want to draw the segment as a continuous path for each line. I'm guessing you are using touchesBegan:withEvent: / touchesMoved:withEvent / touchesEnded:withEvent: to obtain the points of the lines?

In that case, when touchesBegan:withEvent: is called, start your new path. During touchesMoved:withEvent render the new path over the current image. Commit the path to a UIImage in touchesEnded:withEvent:. At this point you may discard the path until touchesBegan:withEvent: is called again with the next stroke.

Update

In your code snippets you end up rendering the whole view three times every run loop. That's a lot of drawing overhead. I quickly put together some code that demonstrates what I am talking about above. It is not tested code, but it should be mostly right:

Update 2

I had a bit of time to write and test some code that will do what you are seeking based on my latest two comments. This code is from the view controller instead of the view itself, but with a little modification, you can move in the view if you like. As such, I added an UIImageView as a subview to the generic UIView to hold and display the image (and maintained a reference to in the view controller), and made the view controller the first responder in order to receive the touch events. Lots of hard coded values that you can clean up when integrating into your own project:

static
UIImage *CreateImage( CGRect rect, NSArray *paths )
{
    UIGraphicsBeginImageContextWithOptions(rect.size, NO, 1.0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGFloat colorComponents[4] = {0.0,0.0,0.0,0.0};
    CGContextSetFillColor(context, colorComponents);
    CGContextFillRect(context, rect);

    for ( id obj in paths ) {
        CGPathRef path = (CGPathRef)obj;
        CGContextAddPath(context, path);
    }    
    CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, 5.0);
    CGContextSetShadow(context, (CGSize){-8.0,40}, 5.0);
    CGContextBeginTransparencyLayer(context, NULL);

    CGContextDrawPath(context, kCGPathStroke);

    CGContextEndTransparencyLayer(context);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

static
CGPoint MidPoint( CGPoint pt1, CGPoint pt2 )
{
    return (CGPoint){(pt1.x+pt2.x)/2.0,(pt1.y+pt2.y)/2.0};
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    CGPoint location = [[touches anyObject] locationInView:self.view];
    myPath = CGPathCreateMutable();
    CGPathMoveToPoint(myPath, NULL, location.x, location.y);

    [self.paths addObject:(id)myPath];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];
    UITouch *touch = [touches anyObject];
    CGPoint previousLocation = [touch previousLocationInView:self.view];
    CGPoint location = [touch locationInView:self.view];
    CGPoint midPoint = MidPoint(previousLocation, location);
    CGPathAddQuadCurveToPoint(myPath, NULL, midPoint.x, midPoint.y, location.x, location.y);

    self.imageView.image = CreateImage(self.view.bounds, self.paths);
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    UITouch *touch = [touches anyObject];
    CGPoint previousLocation = [touch previousLocationInView:self.view];
    CGPoint location = [touch locationInView:self.view];
    CGPoint midPoint = MidPoint(previousLocation, location);
    CGPathAddQuadCurveToPoint(myPath, NULL, midPoint.x, midPoint.y, location.x, location.y);

    self.imageView.image = CreateImage(self.view.bounds, self.paths);

    CGPathRelease(myPath);
    myPath = nil;
}

Upvotes: 8

user207616
user207616

Reputation:

the problem is you have multiple lines which lie above each other. So the shadows of both overlap and you get a darker color.

For something that easy like your line drawings you can simply copy the whole drawing code and redraw with a different color and a view pixel offset (instead of the shadow). E.g. you save the curves into an array and draw them separately later.

EDIT:
ok @gschandler describes the problem great too.

But you may also like to have a look at the Transparency Layer documentation. Group the elements together and apply a shadow for the group.

Upvotes: 4

Related Questions