Export CGPath as JPG or PNG - iphone

Is it possible to take a path draw in an UIView with CGPath and export it as a PNG?

Assuming you want a UIImage, not a png file, you can do something like this:
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetLineWidth(context, 2.0);
CGContextSetLineCap(context, kCGLineCapSquare);
//DRAW YOUR PATH HERE
CGContextStrokePath(context);
myUIImage = UIGraphicsGetImageFromCurrentImageContext();
[myUIImage retain];
UIGraphicsEndImageContext();

The cleanest way to do that is to perform the whole drawing again in an image context produced by UIGraphicsBeginImageContext(), get an UIImage out of it, then save it via the UIImagePNG/JPEGRepresentation() functions.
Note that UIView do not "hold" images. You can rerender a UIView's layer, but it's a gross violation of MVC (you're using views to store model data!), and it doesn't look clean to me.

Related

How to draw a line with a custom style using Core Graphics?

I'm currently drawing a line using Core Graphics. It's really bare bones and simple.
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextSetStrokeColor(c, red);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 5.0f, 5.0f);
CGContextAddLineToPoint(c, 300.0f, 600.0f);
CGContextSetLineWidth(c, 25);
CGContextSetLineCap(c, kCGLineCapRound);
CGContextStrokePath(c);
}
This works well. Let's say that we wanted to draw a custom style line. Say we wanted to imitate the style of a crayon for example. And that the designer handed your crayon style images: http://imgur.com/a/N40ig
To do accomplish this effect I think I need to do something like this:
Create a special colored versions of crayonImage1-crayonImage4
Every time you add a line to line you use one of the crayonImages
You alternate the crayonImages every time you draw a point.
Step 1 makes sense. I can use the following method:
- (UIImage *)image:(UIImage *)img withColor:(UIColor *)color {
// begin a new image context, to draw our colored image onto
UIGraphicsBeginImageContext(img.size);
// get a reference to that context we created
CGContextRef context = UIGraphicsGetCurrentContext();
// set the fill color
[color setFill];
// translate/flip the graphics context (for transforming from CG* coords to UI* coords
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// set the blend mode to color burn, and the original image
CGContextSetBlendMode(context, kCGBlendModeColorBurn);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, rect, img.CGImage);
// set a mask that matches the shape of the image, then draw (color burn) a colored rectangle
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context,kCGPathFill);
// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//return the color-burned image
return coloredImg;
}
I'm unsure how I can complete steps 2 and 3. Is there an API in CoreGraphics for setting an image as the point of line? If so what is it and how can I use it?
Thanks in advance,
-David
Start with the following example: http://www.ifans.com/forums/showthread.php?t=132024
But for brushes, don't draw a line. Simply draw the brush image using CGContextDrawImage.
Basically, you simply draw an image for every touch.

How to make a CAShapeLayer throw a shadow in iPhone OS 3.0?

I'm using a CAShapeLayer with a path. Now I want it to throw a smooth shadow with about 10 units of thickness.
First: Yeah, I could create just 11 CAShapeLayer objects and each time increase the outline of the path by 1 unit with an different color and some more alpha on every iteration. But this way I blow up my memory footprint since the thing is half screen size and this would mean to have 11x a bitmap of half screen size in memory.
So since iPhone OS 3.2 I could probably use those nifty shadow properties on CALayer. But I want to stick to OS 3.0. So what options do I have, other than the nasty one above?
You could create the shadow using Core Graphics. The building blocks you'll need are described in QuartzDemo sample. In particular have a look at class QuartzMaskingView in QuartzClipping.m.
Capture the content of the shape layer into image
Set the shadow to your liking
Begin transparency layer
Clip to the image of layers content - you'll be drawing outside of it
Draw your image again
This results in shadow being painted outside of your masked area.
CGSize size = CGSizeMake(300, 100);
UIGraphicsBeginImageContextWithOptions(size,NO, 0.0);
[shapeLayer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect flippedImageRect =
CGRectMake(0, 0, image.size.width, -image.size.height);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextSetShadowWithColor(ctx, CGSizeMake(4, 4), 2,
[[UIColor colorWithWhite:0 alpha:0.4] CGColor]);
CGContextBeginTransparencyLayer(ctx, NULL);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextClipToMask(ctx, flippedImageRect, [image CGImage]);
CGContextSetFillColorWithColor(ctx, [[UIColor redColor] CGColor]);
CGContextDrawImage(ctx, flippedImageRect, [image CGImage]);
CGContextEndTransparencyLayer(ctx);
CGContextRestoreGState(ctx);

get the UIImage (or CGImage) from drawRect

I'm calling the following code from drawRect
- (void) drawPartial:(UIImage *)img colour:(UIColor *)colour {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, self.frame, img.CGImage);
if (colour!=nil) {
CGContextSetBlendMode (context, kCGBlendModeColor );
CGContextClipToMask(context, self.bounds, img.CGImage);
CGContextSetFillColor(context, CGColorGetComponents(colour.CGColor));
CGContextFillRect (context, self.bounds);
}
self.currentImage=UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(context);
}
Infact I call it multiple times to build a composite image dynamically coloured in. Each img passed in is effectively a transparent overlay with a different part of the image on it.
I can build my compound image by calling this multiple times in drawrect. The issue comes when I want to update one part of the image. Ideally I would be able to call the above function and just change the one part: I've tried playing with self.clearsContextBeforeDrawing to no avail.
Then I thought I would try to keep a copy of the image from each draw, as a cached image of the state - that way I just need to overlay the new part on that: two calls rather than 15.
However the line self.currentImage=UIGraphicsGetImageFromCurrentImageContext() is not returning me the current image so I can't build a cached copy.
Any help on either approach would really be appreciated. (or obviously point me the way I should be doing this!)
EDIT
I also tried compositing the image separtly using virtually the same code, and then drawing that, but again I don't get the image out...
- (UIImage *) overlayImage:(UIImage *)srcImage withImage:(UIImage *)overlayImage ofColour:(UIColor *)colour {
CGSize size =srcImage.size;
CGRect box = CGRectMake(0, 0, size.width, size.height);
UIGraphicsBeginImageContext(size);
CGContextRef context=UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, box, srcImage.CGImage);
if (colour!=nil) {
CGContextSetBlendMode (context, kCGBlendModeColor ); //kCGBlendModeMultiply
CGContextClipToMask(context, box, overlayImage.CGImage); // respect alpha mask
CGContextSetFillColor(context, CGColorGetComponents(colour.CGColor));
CGContextFillRect (context, box);
}
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
//UIImage *result = [UIImage imageWithCGImage: CGBitmapContextCreateImage (context)];
UIGraphicsEndImageContext();
return result;
}
As a rule, you want -drawRect: to be as simple as possible.
Move your drawing code from -drawRect: into a method that creates and draws the entire image you want. Reduce -drawRect: to just compositing this image into the current context.
It's entirely possible that your UIImage object simply isn't retaining the stored data. Furthermore you should know that UIImage is not a mutable object type. You'll get memory leaks unless you release it before reassigning it. Try this:
UIImage *result = [UIGraphicsGetImageFromCurrentImageContext() retain];

How can i take an UIImage and give it a black border programmatically?

How can i take an UIImage and give it a black border programmatically?
If i can receive code, it will be great.
tnx
If you only need to display the border you can do that with Core Animation on the UIImageView's layer. If you need to do it on the image itself then you will need to create a new image, draw the old image into the new image and then draw a rect on top of it.
- (UIImage*)imageWithBorderFromImage:(UIImage*)source;
{
CGSize size = [source size];
UIGraphicsBeginImageContext(size);
CGRect rect = CGRectMake(0, 0, size.width, size.height);
[source drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0.5, 1.0, 1.0);
CGContextStrokeRect(context, rect);
UIImage *testImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return testImg;
}
This will put a pink border on an image and return the new image.
I'd have a look at this:
Can I Edit the Pixels of the UIImage's Property CGImage
As for the black border part, I assume you can figure that one out. Just iterate along each side and change the pixels to (0,0,0,255) for a certain amount.

problem to draw and fill rect after cgcontextclip

I want to clip a rectangle from cgpath with the following code. I don't get any output. What's wrong?
CGContextRef context = UIGraphicsGetCurrentContext();
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,(self.x1),(self.y1));
CGPathAddLineToPoint(thePath,NULL,(self.x2),(self.y2));
CGPathAddLineToPoint(thePath,NULL,bx2,by2);
CGPathAddLineToPoint(thePath,NULL,bx1,by1);
CGContextAddPath(context, thePath);
CGContextClip(context);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
CFRelease(thePath);
I noticed that you set the fill color, but did you actually fill the path? e.g.
CGContextFillPath(context);
Do you have a current context? i.e. did you use UIGraphicsBeginImageContext() and UIGraphicsPushContext() before you execute the code shown?