Divide UIImage into two parts along a UIBezierPath - objective-c

How to divide this UIImage by the black line into two parts. The upper contour set of UIBezierPath.
I need to get two resulting UIImages. So is it possible?

The following set of routines create versions of a UIImage with either only the content inside a path, or only content outside that path.
Both make use of the compositeImage method, which uses CGBlendMode. CGBlendMode is very powerful for masking anything you can draw against anything else you can draw. Calling compositeImage: with other blend modes can have interesting (if not always useful) effects. See the CGContext Reference for all the modes.
The clipping method I described in my comment to your OP does work and is probably faster, but only if you have UIBezierPaths defining all the regions you want to clip.
- (UIImage*) compositeImage:(UIImage*) sourceImage onPath:(UIBezierPath*) path usingBlendMode:(CGBlendMode) blend;
{
// Create a new image of the same size as the source.
UIGraphicsBeginImageContext([sourceImage size]);
// First draw an opaque path...
[path fill];
// ...then composite with the image.
[sourceImage drawAtPoint:CGPointZero blendMode:blend alpha:1.0];
// With drawing complete, store the composited image for later use.
UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
// Graphics contexts must be ended manually.
UIGraphicsEndImageContext();
return maskedImage;
}
- (UIImage*) maskImage:(UIImage*) sourceImage toAreaInsidePath:(UIBezierPath*) maskPath;
{
return [self compositeImage:sourceImage onPath:maskPath usingBlendMode:kCGBlendModeSourceIn];
}
- (UIImage*) maskImage:(UIImage*) sourceImage toAreaOutsidePath:(UIBezierPath*) maskPath;
{
return [self compositeImage:sourceImage onPath:maskPath usingBlendMode:kCGBlendModeSourceOut];
}

I tested clipping, and in a few different tests it was 25% slower than masking to achieve the same result as the [maskImage: toAreaInsidePath:] method in my other answer. For completeness I include it here, but please don't use it without a good reason.
- (UIImage*) clipImage:(UIImage*) sourceImage toPath:(UIBezierPath*) path;
{
// Create a new image of the same size as the source.
UIGraphicsBeginImageContext([sourceImage size]);
// Clipping means drawing only happens within the path.
[path addClip];
// Draw the image to the context.
[sourceImage drawAtPoint:CGPointZero];
// With drawing complete, store the composited image for later use.
UIImage *clippedImage = UIGraphicsGetImageFromCurrentImageContext();
// Graphics contexts must be ended manually.
UIGraphicsEndImageContext();
return clippedImage;
}

This can be done but it requires some trigonometry. Let's consider the case for the upper image. First, determine the bottommost end point of the UIBezierPath and use UIGraphicsBeginImageContext to get the top part of the image above the line. This will look as follows:
Now, assuming that your line is straight, move pixel by pixel along the line drawing vertical strokes of clearColor (loop for top portion. Proceed on similar lines for bottom portion):
for(int currentPixel_x=0;currentPixel_x<your_ui_image_top.size.width)
UIGraphicsBeginImageContext(your_ui_image_top.size);
[your_ui_image_top drawInRect:CGRectMake(0, 0, your_ui_image_top.size.width, your_ui_image_top.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 1.0);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeClear);
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(),[UIColor clearColor].CGColor);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), currentPixel_x, m*currentPixel_x + c);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPixel_x, your_ui_image_top.size.height);
CGContextStrokePath(UIGraphicsGetCurrentContext());
your_ui_image_top = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
Your UIBezierPath will have to be converted to a straight line of the form y = m*x + c. The x in this equation will be currentPixel_x above. Iterate through the width of the image, increasingcurrentPixel_x by 1 each time. next_y_point_on_your_line will be calculated as:
next_y_point_on_your_line = m*currentPixel_x + c
Each vertical stroke will be 1 pixel wide and its height will depend on how you traverse through them. After some iterations, your image will look roughly (please excuse my poor photo-editing skills!) like:
There are multiple ways of how you draw the clear strokes and this is just one way of going about it. You can also have clear strokes that are parallel to the given path if it gives better results.
Another way is to set the alpha of the pixels below the line to 0.

Related

IOS::How we can implement this [Curved Progress Bar]

I need to implement this functionality.Please suggest me.
It's not working properly means it is taking the end angle for the filling colour but here mentioned the "fromValue" and "toValue" but its not going through the fromValue and toValue.
Please anyone can edit my code.
Thanks in advance.
CAShapeLayer *circle=[CAShapeLayer layer];
circle.path=[UIBezierPath bezierPathWithArcCenter:CGPointMake(self.img_View.frame.origin.x, self.img_View.frame.origin.y) radius:50 startAngle:0 endAngle:90 clockwise:YES].CGPath;
circle.fillColor=[UIColor clearColor].CGColor;
circle.strokeColor=[UIColor greenColor].CGColor;
circle.lineWidth=16;
CABasicAnimation *animation=[CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration=10;
animation.removedOnCompletion=NO;
// animation.fromValue=#(0);
animation.fromValue=[NSNumber numberWithInt:0];
animation.toValue=[NSNumber numberWithInt:20];
animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[circle addAnimation:animation forKey:#"drawCircleAnimation"];
[img_View.layer.sublayers makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
[img_View.layer addSublayer:circle];
you can do it with UIBezierPath it is very efficient to draw shapes.
The bezier path you use as a clip seems to be just a fraction of a circle, while in the image you show, the path is more complex : 2 fractions of a circle, linked by 2 lines, the whole path having a 'ring' shape.
This approach should work, I used it for a timer with the same kind of look. Although I didn't used directly AngleGradientLayer, I modified its - (CGImageRef)newImageGradientInRect:(CGRect)rect method to return a UIImage. But I had to rotate this image by + PI/2, as Pavlov gradient angular gradient starts horizontally.
I use a UIImage, because it's a background that DOESN'T change, so I saved an instance of this UIImage in my layer, and draw it whenever I update the clipping path
- (void)drawInContext:(CGContextRef)ctx {
UIBezierPath *currentPath = [self timerPath];
// other drawing code for glow (shadow) and white stroke)
CGContextAddPath(ctx, currentPath.CGPath);
// clip !
CGContextClip(ctx);
CGContextDrawImage(ctx, self.bounds, _circularGradientImage.CGImage);
//_circularGradientImage from modified newImageGradientInRect method.
}

coordinate computation of the image thumbnail

This is a code snippet for creating a thumbnail sized image (from an original large image) and placing it appropriately on top of a tableviewcell. As i was studying the code i got stuck at the part where the thumbnail is being given a position by setting its abscissa and ordinate. In the method -(void)setThumbDataFromImage:(UIImage *)image they're setting the dimensions and coordinate for project thumbnail—
-(void)setThumbnailDataFromImage:(UIImage *)image{
CGSize origImageSize= [image size];
// the rectange of the thumbnail
CGRect newRect= CGRectMake(0, 0, 40, 40);
// figure out a scaling ratio to make sure we maintain the same aspect ratio
float ratio= MAX(newRect.size.width/origImageSize.width, newRect.size.height/origImageSize.height);
// Create a transparent bitmap context with a scaling factor equal to that of the screen
UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0);
// create a path that is a rounded rectangle
UIBezierPath *path= [UIBezierPath bezierPathWithRoundedRect:newRect cornerRadius:5.0];
// make all the subsequent drawing to clip to this rounded rectangle
[path addClip];
// center the image in the thumbnail rectangle
CGRect projectRect;
projectRect.size.width=ratio * origImageSize.width;
projectRect.size.height= ratio * origImageSize.height;
projectRect.origin.x= (newRect.size.width- projectRect.size.width)/2;
projectRect.origin.y= (newRect.size.height- projectRect.size.height)/2;
// draw the image on it
[image drawInRect:projectRect];
// get the image from the image context, keep it as our thumbnail
UIImage *smallImage= UIGraphicsGetImageFromCurrentImageContext();
[self setThumbnail:smallImage];
// get the PNG representation of the image and set it as our archivable data
NSData *data= UIImagePNGRepresentation(smallImage);
[self setThumbnailData:data];
// Cleanup image context resources, we're done
UIGraphicsEndImageContext();
}
I got the width and height computation wherein we multiply the origImageSize with scaling factor/ratio.
But then we use the following to give the thumbnail a position—
projectRect.origin.x= (newRect.size.width- projectRect.size.width)/2;
projectRect.origin.y= (newRect.size.height- projectRect.size.height)/2;
This i fail to understand. I cannot wrap my head around it. :?
Is this part of the centering process. I mean, are we using a mathematical relation here to position the thumbnail or is it some random calculation i.e could have been anything.. Am i missing some fundamental behind these two lines of code??
Those two lines are standard code for centering something, although they aren’t quite written in the most general way. You normally want to use:
projectRect.origin.x = newRect.origin.x + newRect.size.width / 2.0 - projectRect.size.width / 2.0;
projectRect.origin.y = newRect.origin.y + newRect.size.height / 2.0 - projectRect.size.height / 2.0;
In your case the author knows the origin is 0,0, so they omitted the first term in each line.
Since to center a rectangle in another rectangle you want the centers of the two axes to line up, you take, say, half the container’s width (the center of the outer rectangle) and subtract half the inner rectangle’s width (which takes you to the left side of the inner rectangle), and that gives you where the inner rectangle’s left side should be (e.g.: its x origin) when it is correctly centered.

Sequentially shift square blocks in UIImage

I am new to Objective-C, but I need to write a fast method, which will divide an UIImage into square blocks of fixed size, and then mix them. I have already implemented it in the following way:
Get UIImage
Represent it as PNG
Convert it to RGBA8 unsigned char array
For each block, calculate it's coordinates, then xor each pixel with pixel from block that gets replaced
Assemble that RGBA8 meat back into a new UIImage
Return it
It works as intended, but it is extremely slow. It takes about 12 seconds to process single 1024x768 PNG on iPhone 4S. Inspector shows that methods somehow connected to PNGRepresentation, eat up about 50% of total run time.
Will it possibly be faster, if I use Quartz2D here somehow? I am now simply trying to copy/paste a single rectangle from and to my _image, but I don't know how to go further. It returns an UIImage with the _image provided as is, without the blockLayer pasted inside it:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), YES, 1.0);
CGContextRef context = UIGraphicsGetCurrentContext();
/* Start drawing */
//Draw in my image first
[_image drawAtPoint:CGPointMake(0,0) blendMode:kCGBlendModeNormal alpha:1.0];
//Here I am trying to make a 400x400 square, starting presumably at the origin
CGLayerRef blockLayer = CGLayerCreateWithContext(context, CGSizeMake(400, 400), NULL);
//Then I attempt to draw it back at the middle
CGContextDrawLayerAtPoint(context, CGPointMake(1024/2, 768/2), blockLayer);
CGContextSaveGState(context);
/* End drawing */
//Make UIImage from context
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
You can follow these steps to do what you need:
Load the image
Split it up into squareshow?
Create a CALayer for each image, setting the location to the place of the square in the image before shuffling
Go through the layers, and set their positions to their target locations after shuffling
Watch the squares moving to their new placeswhat if you don't want the animation?

Adding a tint to an image

I'm creating an app which uses UIImagePickerController to present a camera to the user with a custom overlay which includes one of two grids/patterns over the camera "view" itself.
The grids themselves are .png files in a UIImageView which is added to the overlay, they're quite complex so I would really like to steer away from drawing the grid in code, even though that would present I nice clean and simple answer to my question.
I would like to be able to offer the grids in a variety of colours. The obvious solution is create more .png images in different colours, but for each colour there would have to be four separate images (regular and retina for each of the grids) so that would quickly add up to a lot of assets.
The solution which, I think, would be ideal, would be for me to just create the grids in white/gray and then apply a tint to it to colour it appropriately.
Is that possible? Or do I need to seek an alternative solution?
With thanks to Ananth for pointing me to iPhone - How do you color an image?
I've added this method to my code as suggested in the question, with the modification in willc2's answer:
-(UIImage *)colorizeImage:(UIImage *)baseImage color:(UIColor *)theColor {
UIGraphicsBeginImageContext(baseImage.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -area.size.height);
CGContextSaveGState(ctx);
CGContextClipToMask(ctx, area, baseImage.CGImage);
[theColor set];
CGContextFillRect(ctx, area);
CGContextRestoreGState(ctx);
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextDrawImage(ctx, area, baseImage.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
...and I'm getting exactly what I'm after.

crop image from certain portion of screen in iphone programmatically

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CGSize contextSize=CGSizeMake(320,400);
UIGraphicsBeginImageContext(self.view.bounds.size);
UIGraphicsBeginImageContext(contextSize);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *savedImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setSaveImage:savedImg];
to extarct some part of image from main screen.
In UIGraphicsBeginImageContext I can only use size, is there any way to use CGRect or some other way to extract image from a specific portion of screen ie (x,y, 320, 400) some thing like this
Hope this helps:
// Create new image context (retina safe)
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
// Create rect for image
CGRect rect = CGRectMake(x, y, size.width, size.height);
// Draw the image into the rect
[existingImage drawInRect:rect];
// Saving the image, ending image context
UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
This question is really a duplicate of several other questions including this: How to crop the UIImage?, but since it took me a while to find a solution, I will cross post again.
In my quest for a solution that I could more easily understand (and written in Swift), I arrived at this:
I wanted to be able to crop from a region based on an aspect ratio, and scale to a size based on a outer bounding extent. Here is my variation:
import AVFoundation
import ImageIO
class Image {
class func crop(image:UIImage, crop source:CGRect, aspect:CGSize, outputExtent:CGSize) -> UIImage {
let sourceRect = AVMakeRectWithAspectRatioInsideRect(aspect, source)
let targetRect = AVMakeRectWithAspectRatioInsideRect(aspect, CGRect(origin: CGPointZero, size: outputExtent))
let opaque = true, deviceScale:CGFloat = 0.0 // use scale of device's main screen
UIGraphicsBeginImageContextWithOptions(targetRect.size, opaque, deviceScale)
let scale = max(
targetRect.size.width / sourceRect.size.width,
targetRect.size.height / sourceRect.size.height)
let drawRect = CGRect(origin: -sourceRect.origin * scale, size: image.size * scale)
image.drawInRect(drawRect)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage
}
}
There are a couple things that I found confusing, the separate concerns of cropping and resizing. Cropping is handled with the origin of the rect that you pass to drawInRect, and scaling is handled by the size portion. In my case, I needed to relate the size of the cropping rect on the source, to my output rect of the same aspect ratio. The scale factor is then output / input, and this needs to be applied to the drawRect (passed to drawInRect).
One caveat is that this approach effectively assumes that the image you are drawing is larger than the image context. I have not tested this, but I think you can use this code to handle cropping / zooming, but explicitly defining the scale parameter to be the aforementioned scale parameter. By default, UIKit applies a multiplier based on the screen resolution.
Finally, it should be noted that this UIKit approach is higher level than CoreGraphics / Quartz and Core Image approaches, and seems to handle image orientation issues. It is also worth mentioning that it is pretty fast, second to ImageIO, according to this post here: http://nshipster.com/image-resizing/