How to do custom drawing in on a image in objective-c - iphone

first of all i want to say that i'm newbie in objective-c.
till know i have build an app that simply draws on one image (which is .png image that has 3 colors: red green blue). the applications draws lines in all over the image.
what i want is to draw lines in only one color (ex: red), even if the touch (mouse) goes up the other colors. (... as I read on internet, it should be done using flood-fill algorithm)
can you give me some ideas on how to to this.
the code that i'm using now:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
currentPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(CGSizeMake(320, 568));
[drawImage.image drawInRect:CGRectMake(0, 0, 320, 568)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0, 2, 0, 2);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
[drawImage setFrame:CGRectMake(0, 0, 320, 568)];
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
[self.view addSubview:drawImage];
}
thnx in advance.

You can create custom class derived from UIView and override message drawRect:. Then put your draw code to drawRect:. Add this custom class as subview above your UIImageView.
#import <QuartzCore/QuartzCore.h>
#interface YourView : UIView {
...
}
in .m file
- (void)drawRect:(CGRect)rect{
CGContextRef ctx = UIGraphicsGetCurrentContext();
//your drawings
}

Related

I have used Quartz2D to draw the line, Now to UNDO the drawn line?

Well i'm using Quartz2D to draw the things, i have followed this tutorial and its working fine.But i need to implement the UNDO option in it.
I have a undo button when i press it , it must undo the drawn line .
I"M using below code to draw . Does any one know the solution for it.
Thaks in Advance.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(self.view.frame.size);
[self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush );
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
CGContextStrokePath(UIGraphicsGetCurrentContext());
self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();
[self.tempDrawImage setAlpha:opacity];
UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(!mouseSwiped) {
UIGraphicsBeginImageContext(self.view.frame.size);
[self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, opacity);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
UIGraphicsBeginImageContext(self.mainImage.frame.size);
[self.mainImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:1.0];
[self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) blendMode:kCGBlendModeNormal alpha:opacity];
self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
self.tempDrawImage.image = nil;
UIGraphicsEndImageContext();
}
you can not undo a specific drawing operation. You have to save all the touches and store it in an appropriate way (your "step" object). You store this "step" objects in a drawing-session (an array) and if you want to do an undo you delete the last object in this array and after this redraw the whole image with all the steps remaining in the (session)array.
The simplest way to do undo is to capture the rectangle that is about to change and store the contents of that rectangle in memory or in a file. When you want to undo, simply draw the rectangle that was saved at the correct coordinates using kCGBlendModeCopy.
For multiple undo, you can store a stack of these rectangles and pop when they want to undo. Redo is also easy, instead of a pop, you just move back one position in an array. For a redo, you move forward one position.
If you are modifying your image in real time (i.e. they are drawing with their finger) then you can't pre-get the rectangle before-hand, and instead you'll need a second buffer that contains a copy of your image, and you can use that to get your undo rectangle after they finish the draw operation. Once they finish, you copy the image to the undo buffer.
Good luck!

Creating Mask from Path

Not a CoreGraphics expert, I'd really appreciate some help.
I'm trying to create a mask from a path. (See red path in image).
Instead of the red path, I'd like the path to be a mask to an image underneath.
Could really use a hand. Thanks in advance
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
currentPoint.y -= 20;
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 20.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);
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;
}
}
Once you have the path created, use CGContextReplacePathWithStrokedPath convert it to a path you can fill to get the same results as stroking the path. Then you can use CGContextClip to set the clipping mask to this path.
The solution I came up with was:
Have UIView image on screen.
Create UIImageView image with [UIColor clearColor] background color
Use CoreGraphics to fill UIImageView with color
Save UIImageView, then in TouchesMoved or UIPanGestureRecognizer, draw savedImage, Stroke Path after setting kCGContentBlendClear, save Image and repeat.

How would I save a CGContext into an NSUndoManager

I want to be able to save a CGContext into my NSUndoManager using these methods
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
currentPoint.x = currentPoint.x;
currentPoint.y = currentPoint.y;
mouseSwiped = YES;
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetShouldAntialias(UIGraphicsGetCurrentContext(), YES);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushSizeVal);
CGContextSetAlpha(UIGraphicsGetCurrentContext(), drawingColorAlpha);
CGContextSaveGState(UIGraphicsGetCurrentContext()); //Probably right here
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(),
drawingColorRed,
drawingColorGreen,
drawingColorBlue,
drawingColorAlpha);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(),
lastPoint.x,
lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(),
currentPoint.x,
currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext(); //drawImage is the UIImageView that I am drawing my context to
NSLog(#"current point x: %d current point y: %d",currentPoint.x, currentPoint.y); //Used for my purposes
lastPoint = currentPoint;
mouseMoved++;
and
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!optionsDisplayerVisible && canDraw)
{
if(!mouseSwiped)
{
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetShouldAntialias(UIGraphicsGetCurrentContext(), YES);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushSizeVal);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(),
drawingColorRed,
drawingColorGreen,
drawingColorBlue,
drawingColorAlpha);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext(); //drawImage is the UIImageView that I am drawing to
UIGraphicsEndImageContext();
}
}
}
By the way if you want to know I have my NSUndoManager named undoDrawing
I don't believe you want to save the CGContext, since it's part of a stack that will not be valid when you need it later. Instead, just save the image itself. First, don't create the context over the whole frame. You just need the rectangle between currentPoint and lastPoint. Then, before you start drawing, grab the image from the current context (UIGraphicsGetImageFromCurrentImageContext ()) and save that off along with the frame to the NSUndoManager.
If you're unfamiliar with optimizing your drawing just to the dirty rectangle, start with the simpler version of this and just save the entire image to the undomanager before drawing on it.

Use a CoreGraphic Stroke as Alpha Mask in iPhone App

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
}

How to draw nil using cgcontext

I want to draw nil instead of the colors, because in the end I want the code to erase a section of the UIImageView in order that you can see what is behind the UIImageView.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
currentPoint.x = currentPoint.x;
currentPoint.y = currentPoint.y;
mouseSwiped = YES;
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetShouldAntialias(UIGraphicsGetCurrentContext(), YES);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushSizeVal);
CGContextSetAlpha(UIGraphicsGetCurrentContext(), drawingColorAlpha);
CGContextSaveGState(UIGraphicsGetCurrentContext()); //Probably right here
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(),
drawingColorRed, // I want to draw nil here instead of these CGFloats that I have made
drawingColorGreen, // I want to draw nil here instead of these CGFloats that I have made
drawingColorBlue, // I want to draw nil here instead of these CGFloats that I have made
drawingColorAlpha); // I want to draw nil here instead of these CGFloats that I have made
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(),
lastPoint.x,
lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(),
currentPoint.x,
currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext(); //drawImage is the UIImageView that I am drawing my context to
NSLog(#"current point x: %d current point y: %d",currentPoint.x, currentPoint.y); //Used for my purposes
lastPoint = currentPoint;
mouseMoved++;
and
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!optionsDisplayerVisible && canDraw)
{
if(!mouseSwiped)
{
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetShouldAntialias(UIGraphicsGetCurrentContext(), YES);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushSizeVal);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(),
drawingColorRed,
drawingColorGreen,
drawingColorBlue,
drawingColorAlpha);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext(); //drawImage is the UIImageView that I am drawing to
UIGraphicsEndImageContext();
}
}
}
Colin Gislason's answer is incorrect. If you set drawingColorAlpha to 0, it just paints an invisible color on the existing image.
Here is what works.
//set this at the beginning
CGContextSetLineCap(ctx,kCGImageAlphaNone);
//after moving the point to your pen location:
CGContextClearRect (ctx, CGRectMake(lastPoint.x, lastPoint.y, 10, 10));
What you want to do is set drawingColorAlpha to 0. This creates a transparent color which is equivalent to erasing. You might also have to set the blend mode using CGContextSetBlendMode(). kCGBlendModeNormal should work.
Set blend mode as follows:
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
Hope it will help u !! :P
What you want is not nil, but a color that's transparent. If your UIImageView isn't marked as "Opaque" in IB, then it should show through in any areas where the pixels are transparent.