I have a UIImage like so:
I want to remove a rect in the middle to leave it like so:
The easiest way to punch a hole through would be to add a layer mask.
#import <QuartzCore/QuartzCore.h>
Create the path you want to clip (the rect you want to punch through)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:yourView.bounds];
[maskPath appendPath:[UIBezierPath bezierPathWithRect:rectToPunchThroughTheView]];
Then create a mask layer and add it to your view - CAShapeLayer will allow you to define any path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.fillColor = [UIColor blackColor].CGColor;
maskLayer.path = maskPath.CGPath;
yourView.layer.mask = maskLayer;
I would create a UIImage category and crop the image two times with different rects.
Here is the method I added yo my UIImage category a while ago that suited my needs good:
- (UIImage *)cropWithRect:(CGRect)rect {
// If run on a retina device scale is 2 else 1
rect = CGRectMake(rect.origin.x * self.scale,
rect.origin.y * self.scale,
rect.size.width * self.scale,
rect.size.height * self.scale);
CGImageRef imgRf = CGImageCreateWithImageInRect(self.CGImage, rect);
UIImage *croppedImage = [UIImage imageWithCGImage:imageRef
scale:self.scale
orientation:self.imageOrientation];
CGImageRelease(imageRef);
return croppedImage;
}
You can do this way
CGRect fullimageRect;
CGRect upperRect;
CGRect lowerRect;
UIImage *upperImage;
UIImage *lowerImage;
UIImage *finalImage;
UIGraphicsBeginImageContext(upperRect.size);
[[UIImage imageNamed:#"yourimage.png"] drawInRect:lowerRect];
upperImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContext(lowerRect.size);
[[UIImage imageNamed:#"yourimage.png"] drawInRect:lowerRect];
lowerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSLog(#"%#",upperImage);
NSLog(#"%#",lowerImage);
UIGraphicsBeginImageContext(fullimageRect.size);
[upperImage drawInRect:upperRect];
[lowerImage drawInRect:lowerRect];
finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSLog(#"%#",finalImage);
Related
I have two images, one is a mask that is transparent with some edges / borders and the other is the actual image. I want to merge both of them.
I have used the following code to mask and combine the image:
- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {
// create a bitmap graphics context the size of the image
CGFloat dim = MIN(image.size.width, image.size.height);
CGSize size = CGSizeMake(dim, dim);
UIGraphicsBeginImageContextWithOptions(size, NO, .0);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithOvalInRect:(CGRect){ CGPointZero, size }];
[bezierPath fill];
[bezierPath addClip];
CGPoint offset = CGPointMake((dim - image.size.width) * 0.5, (dim - image.size.height) * 0.5);
[image drawInRect:(CGRect){ offset, image.size }];
UIImage *ret = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return ret;
}
The result:
In the result image, the border of the image used as a mask is missing. Can someone please help me with this?
I wrote a masking category for ios (well it is basically cross platform because CoreImage is on both platforms anyway:
github project
the core functionality boils down to this (for your example)
UIImage *person = ...
UIImage *circle = ...
UIImage *result = [person imageMaskedWith:circle];
UIImageView *redbox = [[UIImageView alloc] initWithImage:result];
redbox.backgroundColor = [UIColor redColor]; //this can be a gradient!
the main part of the code from the category:
CGImageRef imageReference = image.CGImage;
CGImageRef maskReference = mask.CGImage;
CGRect rect = CGRectMake(0, 0, CGImageGetWidth(imageReference), CGImageGetHeight(imageReference));
// draw with Core Graphics
UIGraphicsBeginImageContext(rect.size);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, 0.0, rect.size.height);
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextClipToMask(bitmap, rect, maskReference);
CGContextDrawImage(bitmap, rect, imageReference);
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I am using this library to crop image form appropriate path.
Seems it works perfect, but after modification the image has another size, that I can't understand how to control.
I have added image with the same size as a container. Sizes = 320 x 524
There is an method in the sources that return cropping image:
- (UIImage *)deleteBackgroundOfImage:(UIImageView *)image
{
NSArray *points = [self.croppingPath points];
CGRect rect = CGRectZero;
rect.size = image.image.size;
UIBezierPath *aPath;
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0);
{
[[UIColor blackColor] setFill];
UIRectFill(rect);
[[UIColor whiteColor] setFill];
aPath = [UIBezierPath bezierPath];
// Set the starting point of the shape.
CGPoint p1 = [MZCroppableView convertCGPoint:[[points objectAtIndex:0] CGPointValue] fromRect1:image.frame.size toRect2:image.image.size];
[aPath moveToPoint:CGPointMake(p1.x, p1.y)];
for (uint i=1; i<points.count; i++)
{
CGPoint p = [MZCroppableView convertCGPoint:[[points objectAtIndex:i] CGPointValue] fromRect1:image.frame.size toRect2:image.image.size];
[aPath addLineToPoint:CGPointMake(p.x, p.y)];
}
[aPath closePath];
[aPath fill];
}
UIImage *mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
{
CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage);
[image.image drawAtPoint:CGPointZero];
}
UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect croppedRect = aPath.bounds;
croppedRect.origin.y = rect.size.height - CGRectGetMaxY(aPath.bounds);//This because mask become inverse of the actual image;
croppedRect.origin.x = croppedRect.origin.x*2;
croppedRect.origin.y = croppedRect.origin.y*2;
croppedRect.size.width = croppedRect.size.width*2;
croppedRect.size.height = croppedRect.size.height*2;
CGImageRef imageRef = CGImageCreateWithImageInRect(maskedImage.CGImage, croppedRect);
maskedImage = [UIImage imageWithCGImage:imageRef];
return maskedImage;
}
When I debug maskedImage before maskedImage = [UIImage imageWithCGImage:imageRef]; line it seems the method remove background correct, but by some reason after the method CGImageCreateWithImageInRect(maskedImage.CGImage, croppedRect); modify frame of cropped image. I tried to set cropped rect manually in CGImageCreateWithImageInRect to crop just needed frame.
CGImageRef imageRef = CGImageCreateWithImageInRect(maskedImage.CGImage, CGRectMake(0, 0, 320, 100));
But it return nothing for me.
How can I extract appropriate rect?
Also this lines multiply rect x2:
croppedRect.origin.x = croppedRect.origin.x*2;
croppedRect.origin.y = croppedRect.origin.y*2;
croppedRect.size.width = croppedRect.size.width*2;
croppedRect.size.height = croppedRect.size.height*2;
So when I debug it:
origin=(x=242, y=874) size=(width=191, height=164)
So as you can see the Y point out of my image height size 524, but by some reason it works.
I'm currently coloring an existing image using a mask. For example, I have a white image with a black border and a circular mask (like the first two images). Then, I can create a third image with a color (i.e. green) which has green on the center of the original image (because the mask is present there).
The code I'm using is this (suggestions welcomed):
-(UIImage *)paintWithMask:(UIImage *)mask color:(UIColor *)color andSize:(CGSize)size{
UIImage *image = self;
UIImage *rotatedMask = [self rotateImage:mask]; //For some reason this is needed.
UIGraphicsBeginImageContextWithOptions(size, NO, image.scale);
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
[image drawInRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(context, kCGBlendModeSourceIn);
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextClipToMask(context, rect, [rotatedMask CGImage]);
CGContextFillRect(context, rect);
UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return coloredImage;
}
What I need to do now is paint the green circle using only the mask (without the black border obviously), like this:
Any ideas? Thanks a lot!!
There is a much easier way of doing this without CoreGraphics. Simply do the following:
-(UIImageView *)imageViewWithMask:(UIImage *)mask color:(UIColor *)color andSize:(CGSize)size{
UIImage *tempImage = mask;
tempImage = [tempImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIGraphicsBeginImageContextWithOptions(size, NO,0);
[tempImage drawInRect: CGRectMake(0,0,size.width,size.height)];
tempImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *iv = [[UIImageView alloc] initWithImage: tempImage];
iv.tintColor = color;
return iv;
}
I want to crop a part of an uiimageview that on my view controller. I'm creating a rectangle on top of it:
UIGraphicsBeginImageContext(self.view.bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(context, newPoint1.x, newPoint1.y);
CGContextAddLineToPoint(context, newPoint1.x, newPoint2.y);
CGContextAddLineToPoint(context, newPoint2.x, newPoint2.y);
CGContextAddLineToPoint(context, newPoint2.x, newPoint1.y);
CGContextAddLineToPoint(context, newPoint1.x, newPoint1.y);
CGContextClosePath(context);
UIColor *blue = [UIColor colorWithRed: (0.0/255.0 ) green: (0.0/255.0) blue: (255.0/255.0) alpha:0.4];
CGContextSetFillColorWithColor(context, blue.CGColor);
CGContextDrawPath(context, kCGPathFillStroke);
I can't figure out how to crop it right. I'm able to retrieve a capture of the screen : entirely blank with my rectangle on it:
UIImage *cropImage = UIGraphicsGetImageFromCurrentImageContext();
rectImage = cropImage;
UIGraphicsEndImageContext();
UIImageCrop *rectImageView = [[UIImageCrop alloc]initWithImage:rectImage];
[self.view addSubview:rectImageView];
So I'm aware that there is something I've missed about it, any help?
- (UIImage *)captureScreenInRect:(CGRect)captureFrame
{
CALayer *layer;
layer = self.view.layer;
UIGraphicsBeginImageContext(self.view.frame.size);
CGContextClipToRect (UIGraphicsGetCurrentContext(),captureFrame);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenImage;
}
this is just for reference change this code according to your requirement
hope this will help you
You can get cropped image using :
- (UIImage*) getCroppedImage {
CGRect rect = PASS_YOUR_RECT;
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// translated rectangle for drawing sub image
CGRect drawRect = CGRectMake(-rect.origin.x, -rect.origin.y, your_image.size.width, your_image.size.height);
// clip to the bounds of the image context
// not strictly necessary as it will get clipped anyway?
CGContextClipToRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height));
// draw image
[your_image drawInRect:drawRect];
// grab image
UIImage* croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return croppedImage;
}
Hope it helps you.
I'm working with an image where the user has selected part of it using UIBezierPath. How can I delete/clear out/make transparent everything that is not part of that selection?
With one path it's very easy. Just set the path as the clipping path:
- (UIImage *)maskImage:(UIImage *)originalImage toPath:(UIBezierPath *)path {
UIGraphicsBeginImageContextWithOptions(originalImage.size, NO, 0);
[path addClip];
[originalImage drawAtPoint:CGPointZero];
UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return maskedImage;
}
If you want to use the union of multiple paths, it's harder, because Quartz doesn't have any functions that directly compute the union of two paths. One way is to fill each path one by one into a mask, and then draw the image through the mask:
- (UIImage *)maskedImage
{
CGRect rect = CGRectZero;
rect.size = self.originalImage.size;
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0); {
[[UIColor blackColor] setFill];
UIRectFill(rect);
[[UIColor whiteColor] setFill];
for (UIBezierPath *path in self.paths)
[path fill];
}
UIImage *mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0); {
CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage);
[self.originalImage drawAtPoint:CGPointZero];
}
UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return maskedImage;
}
I tried the code of union multiple paths, and it doesn't work.
Actually, if the union of paths don't overlap with each other, we append one path to another and crop with the final path.
UIGraphicsBeginImageContextWithOptions(originalImg.size, NO, 0);
[path1 appendPath:path2]; // append path2 to path1
[path1 addClip];
[originalImg drawAtPoint:CGPointZero];
result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();