How to create embossed or shadow effects using Core Graphics (for finger paint)
Asked Answered
T

3

5

I have issue to implement "Emboss/Shadow effects" in my drawing. Finger paint functionality is currently working fine with my custom UIView and below is my drawRect method code:

Edit code with all methods :

- (void)drawRect:(CGRect)rect
{
    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);
    CGContextSaveGState(context);

    // for shadow  effects
    CGContextSetShadowWithColor(context, CGSizeMake(0, 2),3, self.lineColor.CGColor);
    CGContextStrokePath(context);
    [super drawRect:rect];
}

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];
}

-(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];

}

when i have implemented this i am getting paint effects with dot dot dot ...

See below image (which does not have any shadow or embossed effects). If you have any idea of how to add these effects, please give me some suggestion. How can i resolve this?

enter image description here

Termagant answered 22/6, 2012 at 12:10 Comment(0)
A
4

It appears that you're creating hundreds, maybe even thousands of separate paths, one on each drawRect. You do flatten these out using [self.layer renderInContext] but I don't think that's a good way to go about it. Instead, I think what you want to do is create one UIBezierPath to track the finger, append paths to that and draw the UIBezierPath to the screen. If you create two layers (or views) you can set the top one (transparent) to "draw" on. When the user lifts their finger, you then render the entire UIBezierPath to the second layer (along with previously drawn data) and create a new UIBezierPath to draw the next finger-tracking. This way you're only updating one layer (the top one) when you're tracking someone's finger. This will help prevent the device from slowing down drawing too many paths.

Although, I will say, your current method does produce a cool looking "3D" effect.

Analphabetic answered 22/6, 2012 at 12:49 Comment(4)
i appreciated your suggestion. i have used UIBezierPath and implement it but we face some issue like erase functionality and choose different color of paint.. so finally all i have change the functionality and use CGContext. now all functionality like Erase, change color of brush is working fine .issue is only to create Emboss or shadow effects . that effects not working properly .. straigh line working fine with shadow effects .but curve effects (smooth) not working fine . plz tell me some suggeetion using CGContext.Termagant
Quick Question: How do you want Erase to work? Most 'drawing' programs will erase each separate 'path' (as defined as one finger touch/drag/end event). This works with my suggestion/answer. However, if you're trying to erase each path point or even each pixel....that'll be much more difficult to accomplish.Analphabetic
Sorry, I'm not sure what you mean.Analphabetic
its ok no problem .. please see my full source code for paint .#11133046 . we are using this code for smooth painting . if you have any idea than please try this code . shadow effects code are not implemented in this code.Termagant
L
0

I'm not sure about embossing, but if what you want is to apply a drop shadow to a drawing that is updated progressively, then a nice approach would be to use CALayers (link to a tutorial). Basically, your progressive drawing would update a transparent image (without attempting to draw any shadows in this image) and this image would be configured to be displayed with a drop shadow through its CALayer.

Linhliniment answered 22/6, 2012 at 12:58 Comment(0)
G
0

Shadow is created on borders and you are drawing small segments of lines and this creates this effect. You need to create a path and then stroke it once. This code might help you :)

for (int i=0; i<[currentPath count]; i++) 
{
    CGPoint mid1 = [[self midPoint:[currentPath objectAtIndex:i+1]  :[currentPath objectAtIndex:i]] CGPointValue]; 
    CGPoint mid2 = [[self midPoint:[currentPath objectAtIndex:i+2] :[currentPath objectAtIndex:i+1]] CGPointValue];
    CGContextMoveToPoint(context, mid1.x, mid1.y);
    CGContextAddQuadCurveToPoint(context, [[currentPath objectAtIndex:i+1] CGPointValue].x, [[currentPath objectAtIndex:i+1] CGPointValue].y, mid2.x, mid2.y); 
    CGContextSetShadow(context, CGSizeMake(-2, -2), 3);

    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetStrokeColorWithColor(context,[color CGColor]);              
    CGContextSetLineWidth(context, linewidth);              

    i+=2;
}
CGContextStrokePath(context);

Try this way.. This solved might solve your issue.. First create ur path and after it is fully created, stroke it. This happens since at every small line you stroke and hence shadow created make a dot on its bounds, so you get this effect.

Gliwice answered 25/6, 2012 at 4:50 Comment(2)
What is CurrntPath in your code & why u r using loop ? not getting .. plz see my full source code (edited).Termagant
I am creating an array of points, current path is that array. This array is created with points, staring from touch begin to touch end :)Gliwice

© 2022 - 2024 — McMap. All rights reserved.