I am trying to create an application where I can write. I have progressed in that by using CGContextRef. I have written a following code.
previousPoint2 = previousPoint1;
previousPoint1 = [touch previousLocationInView:m_secondaryDrawingImgeView];
currentPoint = [touch locationInView:m_secondaryDrawingImgeView];
// calculate mid point
CGPoint mid1 = [self midPoint:previousPoint1 :previousPoint2];
CGPoint mid2 =[self midPoint:currentPoint :previousPoint1];
UIGraphicsBeginImageContext(m_secondaryDrawingImgeView.frame.size);
[m_secondaryDrawingImgeView.image drawInRect:
CGRectMake(0, 0, m_secondaryDrawingImgeView.frame.size.width,
m_secondaryDrawingImgeView.frame.size.height)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(context, mid1.x, mid1.y);
// Use QuadCurve is the key
CGContextAddQuadCurveToPoint(context,
previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
// NSMutableArray *arr = [NSMutableArray arrayWithObjects:mid1, nil]
// NSLog(#"%#",color);
CGContextSetStrokeColorWithColor(context, color.CGColor);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context,m_width);
CGContextStrokePath(context);
m_secondaryDrawingImgeView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
When I set opacity to my color it shows dots in line, which I want to remove.
Is it the right path i am going on?
Is there any way i can remove those dots?
Attaching images for more clarity. What is happening after writing this code is image-1 and what i want is image-2
Finally I cracked it. you have to set blend mode for this following is the code
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeCopy);
Related
I am using CoreGraphics to implement free hand drawing on a background image which is working fine for me and now I want to implement erase feature for this drawing so that user can clear his drawing
If all you want to do is clear the screen, delete the CGPaths you've created and it will draw nothing.
If you're looking to let the use switch to an eraser tool and only erase certain things, you need to set up a new drawing class that lets you draw the background over top your other drawings or adjust the class you have to not draw intersections between original paths and the eraser path.
you can use this in the UITouch methods"touchesMoved" to clear the path you have drawn
`UITouch *touch = [[event allTouches] anyObject];
currenttouch = [touch locationInView:drawingImageView];
CGColorRef strokeColor = [UIColor blackColor].CGColor;
UIGraphicsBeginImageContext(drawingImageView.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[drawingImageView.image drawInRect:CGRectMake(0, 0, drawingImageView.frame.size.width, drawingImageView.frame.size.height)];
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, brushSize);
CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextBeginPath(context);
CGContextMoveToPoint(context, currenttouch.x, currenttouch.y);
CGContextAddLineToPoint(context, currenttouch.x, currenttouch.y);
CGContextStrokePath(context);
drawingImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
`
i'm stuck at this problem. I need to draw a line between the previous end point to next start point. My code for drawing a line between 2 points is
- (void)drawRect:(CGRect)rect
{
CGContextRef c=UIGraphicsGetCurrentContext();
CGContextSetLineWidth(c, 4.0);
CGFloat red[4]={0.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(c, red);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 50.0f, 50.0f);
CGContextAddLineToPoint(c, 100.0f, 100.0f);
CGContextStrokePath(c) ;
}
How can i give multiple points, so that i can it shows like a graph.
Thanks in advance
On a side note, Apple prefers that you use the higher UIKit methods instead of the Core Graphics calls for simple stuff like drawing lines and curves, so you could rewrite your method like this:
[[UIColor redColor] setStroke];
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:CGPointMake(50.0, 50.0)];
[linePath addLineToPoint:CGPointMake(100.0, 100.0)];
[linePath setLineWidth:4.0];
[linePath stroke];
Just add more CGContextAddLineToPoint calls
- (void)drawRect:(CGRect)rect
{
CGContextRef c=UIGraphicsGetCurrentContext();
CGContextSetLineWidth(c, 4.0);
CGFloat red[4]={0.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(c, red);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 50.0f, 50.0f);
CGContextAddLineToPoint(c, 100.0f, 100.0f);
CGContextAddLineToPoint(c, 120.0f, 80.0f);
CGContextAddLineToPoint(c, 140.0f, 120.0f);
CGContextAddLineToPoint(c, 160.0f, 80.0f);
...
CGContextStrokePath(c) ;
}
Jus a little correction to your code:
-(void)drawLineFromPoint:(CGPoint)startPoint toPoint:(CGPoint)endPoint{
UIGraphicsBeginImageContext(YOUR_IMAGEVIEW.image.size);
[YOUR_IMAGEVIEW.image drawInRect:CGRectMake(0, 0, YOUR_IMAGEVIEW.image.size.width, YOUR_IMAGEVIEW.image.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.0, 0.0, 0.0, 1.0);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), startPoint.x, startPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), endPoint.x, endPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
[YOUR_IMAGEVIEW setImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
}
YOUR_IMAGEVIEW is an outlet to your imageview you are drawing on.
As you can see - you just have to send to this method your start point and the end one! Easy as 1-2-3.
EDIT 1
How to use it? Declare a global CGPoint "startPoint";
Then say:
//___________________________________________________
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
startPoint = [touch locationInView:YOUR_IMAGEVIEW];
}
//___________________________________________________
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint nextPoint = [touch locationInView:YOUR_IMAGEVIEW];
[self drawLineFromPoint:startPoint toPoint:nextPoint];
startPoint = nextPoint;
}
EDIT 2
Here is another way to draw your points:
- (void)drawGraphMethod{
NSMutableArray *pointsArr = [[[NSMutableArray alloc]init]autorelease];
//now you should add the needed points to your array
//I have no idea what graph do you have, so I just
//put there some random points that will look like a graph
[pointsArr addObject:[NSValue valueWithCGPoint:CGPointMake(30.0, 10.0)]];
[pointsArr addObject:[NSValue valueWithCGPoint:CGPointMake(40.0, 26.0)]];
[pointsArr addObject:[NSValue valueWithCGPoint:CGPointMake(70.0, 55.0)]];
[pointsArr addObject:[NSValue valueWithCGPoint:CGPointMake(80.0, 88.0)]];
[pointsArr addObject:[NSValue valueWithCGPoint:CGPointMake(90.0, 33.0)]];
[pointsArr addObject:[NSValue valueWithCGPoint:CGPointMake(130.0, 100.0)]];
//well, you can store only objects, that's why I used NSValue.
//it's not hard to cenvert it back
//now parse the array
for (int i = 0; i < pointsArr.count-1; i++) {
CGPoint startPoint = [[pointsArr objectAtIndex:i] CGPointValue];
CGPoint nextPoint = [[pointsArr objectAtIndex:i+1] CGPointValue];
[self drawLineFromPoint:startPoint toPoint:nextPoint];
}
}
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?
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.
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.
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.
I have integrate draw line in my application i have not used OpenGL or any other similar framework.
So now i want to give glow effect to their lines so how can i give it ?
Thanks in advance.
Set the shadow in your graphics context to have a zero size offset, a blur of around 6-10 (change according to taste) and the same colour as your stroke colour. This will give all subsequent drawing a glow effect. The command is
CGContextSetShadowWithColor()
Documented here.
-(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);
// Use QuadCurve is the key
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
//------------ Glow Lines ----------
if (appDel.BrushType == 201) // (201 is glow brush type)
{
CGContextSetLineWidth(context, 7);
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGFloat components[4]={appDel.R_color/255.0,appDel.G_color/255.0,appDel.B_color/255.0,1.0};
CGColorRef color1 = CGColorCreate(space, components);
CGContextSetShadowWithColor(context, CGSizeMake( 0.0, 0.0 ), 15, color1);
CGContextStrokePath(UIGraphicsGetCurrentContext());
}
//--------------
CGContextStrokePath(context);
[super drawRect:rect];
[curImage release];
}
I'm basically looking to create something akin to a very simple version of iSteam/iFog alebit for a different purpose. In effect there will be two images, one of the subject matter and the other an image of condensation or some such. The user can then wipe their finger over the screen and it will "cut" that from the top layer to reveal the lower layer. So far I've been able to draw a basic line on the screen using CoreGraphics and strokes but I can't find a way to then use this as an alpha mask for the steam layer.
If anyone could give me advice on what to use or even better some sample code I'd be very grateful as right now I'm pulling my hair out. Here is what I have so far:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(self.view.frame.size);
CGRect rect = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[drawImage.image drawInRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 36.0);
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y);
CGContextStrokePath(context);
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
}
The method you want to call is CGContextClipToMask. Just draw your "steam" image and then the stroke on another CGImage. Then, clip the steam to the stroke. Something like this:
- (void)somewhereElse{
UIImage *steam = [[UIImage imageNamed:#"steamImage.png"] retain];
steamRef = steam.CGImage;
//...
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, self.bounds, steamRef); //draw the main image
CGContextClipToMask(context, self.bounds, maskRef); //respect alpha mask
//where maskRef is a GCImage of your stroked path
}