I'm looking for a way to add a solid border with Core Image. I have implement custom camera for taking pictures of rectangle shape documents. Now I am getting that documents four coordinates but problem to draw border on CIImage. Please help me.
CIImage *overlay = [CIImage imageWithColor:[CIColor colorWithRed:0 green:1 blue:0 alpha:0.6]];
overlay = [overlay imageByCroppingToRect:image.extent];
overlay = [overlay imageByApplyingFilter:#"CIPerspectiveTransformWithExtent" withInputParameters:#{#"inputExtent":[CIVector vectorWithCGRect:image.extent],#"inputTopLeft":[CIVector vectorWithCGPoint:topLeft],#"inputTopRight":[CIVector vectorWithCGPoint:topRight],#"inputBottomLeft":[CIVector vectorWithCGPoint:bottomLeft],#"inputBottomRight":[CIVector vectorWithCGPoint:bottomRight]}];
return [overlay imageByCompositingOverImage:image];
You can use this methods :
- (UIImage *)addBorderToImage:(UIImage *)image {
CGImageRef bgimage = [image CGImage];
float width = CGImageGetWidth(bgimage);
float height = CGImageGetHeight(bgimage);
// Create a temporary texture data buffer
void *data = malloc(width * height * 4);
// Draw image to buffer
CGContextRef ctx = CGBitmapContextCreate(data,
width,
height,
8,
width * 4,
CGImageGetColorSpace(image.CGImage),
kCGImageAlphaPremultipliedLast);
CGContextDrawImage(ctx, CGRectMake(0, 0, (CGFloat)width, (CGFloat)height), bgimage);
//Set the stroke (pen) color
CGContextSetStrokeColorWithColor(ctx, [UIColor greenColor].CGColor);
//Set the width of the pen mark
CGFloat borderWidth = (float)width*0.05;
CGContextSetLineWidth(ctx, borderWidth);
//Start at 0,0 and draw a square
CGContextMoveToPoint(ctx, 0.0, 0.0);
CGContextAddLineToPoint(ctx, 0.0, height);
CGContextAddLineToPoint(ctx, width, height);
CGContextAddLineToPoint(ctx, width, 0.0);
CGContextAddLineToPoint(ctx, 0.0, 0.0);
//Draw it
CGContextStrokePath(ctx);
// write it to a new image
CGImageRef cgimage = CGBitmapContextCreateImage(ctx);
UIImage *newImage = [UIImage imageWithCGImage:cgimage];
CFRelease(cgimage);
CGContextRelease(ctx);
// auto-released
return newImage;
}
Calling this method:
UIImage *updatedIMG = [self addBorderToImage:[[UIImage alloc] initWithCIImage:overlay]]
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();
Hi in my iOS application I want to rotate the UIImage data in 90 Degrees.
I used the below code for 180 degree rotation - and it works fine.
- (UIImage*)upsideDownBunny:(UIImage *)img {
CGSize imgSize = [img size];
UIGraphicsBeginImageContext(imgSize);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextRotateCTM(context, M_PI);
CGContextTranslateCTM(context, -imgSize.width, -imgSize.height);
[img drawInRect:CGRectMake(0, 0, imgSize.width, imgSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
But when I try to rotate in 90 degree it is not working - please correct me
- (UIImage *)upsideDownBunny:(UIImage *)img {
CGSize imgSize = [img size];
UIGraphicsBeginImageContext(imgSize);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextRotateCTM(context, M_PI_2);
CGContextTranslateCTM(context, -imgSize.width, -imgSize.height);
[img drawInRect:CGRectMake(0, 0, imgSize.width, imgSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Try this...
UIImage * LandscapeImage = [UIImage imageNamed: imgname];
UIImage * PortraitImage = [[UIImage alloc] initWithCGImage: LandscapeImage.CGImage
scale: 1.0
orientation: UIImageOrientationLeft];
hope this helps you!
Try this
- (UIImage *)upsideDownBunny:(UIImage *)image {
// Rotate in degrees
CGFloat degrees = M_PI_2;
// Calculate the size of the rotated view's containing box for our drawing space
UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
CGAffineTransform transform = CGAffineTransformMakeRotation(degrees);
rotatedViewBox.transform = transform;
CGSize rotatedSize = rotatedViewBox.frame.size;
// Create image context
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef context = UIGraphicsGetCurrentContext();
// Move the origin to the middle of the image.
CGContextTranslateCTM(context, rotatedSize.width * 0.5f, rotatedSize.height * 0.5f);
// Rotate the image context
CGContextRotateCTM(context, degrees);
// Now, draw the rotated/scaled image into the context
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(-image.size.width * 0.5f, -image.size.height * 0.5f, image.size.width, image.size.height), [image CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
The problem is your context is being rotated around (0,0) which is the top left corner and after 90 degrees rotate it will go out of bounds. So you need to move the origin of the context to middle.
If I have an image with some transparent pixels, is there any possibility to color the transparent pixels with white and make the rest of the image transparent in objective-c?
Thanks!
I found a solution:
- (UIImage*)convertToInverseWhiteMask: (UIImage *) image {
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
CGRect imageRect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
CGContextRef ctx = UIGraphicsGetCurrentContext();
// Draw a white background (for white mask)
CGContextSetRGBFillColor(ctx, 1.0f, 1.0f, 1.0f, 1.0f);
CGContextFillRect(ctx, imageRect);
// Apply the source image's alpha
[image drawInRect:imageRect blendMode:kCGBlendModeDestinationOut alpha:1.0f];
UIImage* mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return mask;
}
Here is what i am using:
- (UIImage *)imageWithTintedColor:(UIImage *)image withTint:(UIColor *)color withIntensity:(float)alpha
{
CGSize size = image.size;
UIGraphicsBeginImageContextWithOptions(size, FALSE, [[UIScreen mainScreen] scale]);
CGContextRef context = UIGraphicsGetCurrentContext();
[image drawAtPoint:CGPointZero blendMode:kCGBlendModeNormal alpha:1.0];
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
CGContextSetAlpha(context, alpha);
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(CGPointZero.x, CGPointZero.y, image.size.width, image.size.height));
UIImage * tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
How can I fill the non-transparent areas of a PNG UIImage with a linear gradient? I'd like to reuse a PNG shape for MKAnnotationViews, but change the gradient per annotation's properties.
To use an image as a mask for a gradient (i.e. to have a gradient in the shape of the non-transparent pixels of your image), you can:
create a simple view with a gradient (you can either create a simple UIView and use the addGradientLayerToView shown below to give it a gradient or you can create the gradient PNG in advance and add it to your bundle).
apply your PNG as a mask to that gradient view:
UIImage *mask = [UIImage imageNamed:#"mask.png"];
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0, 0, mask.size.width, mask.size.height);
maskLayer.contents = (id)[mask CGImage];
gradientViewToMask.layer.mask = maskLayer;
To apply a gradient to the transparent pixels, you can either:
Create a new image with a gradient:
- (UIImage *)imageWithGradient:(UIImage *)image
{
UIGraphicsBeginImageContextWithOptions(image.size, NO, 1.0);
CGContextRef context = UIGraphicsGetCurrentContext();
size_t locationCount = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 0.0, 0.8, 0.8, 1.0, // Start color
0.9, 0.9, 0.9, 1.0 }; // End color
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents (colorspace, components, locations, locationCount):
CGPoint startPoint = CGPointMake(0.0, 0.0);
CGPoint endPoint = CGPointMake(0.0, image.size.height);
CGContextDrawLinearGradient (context, gradient, startPoint, endPoint, 0);
CGContextTranslateCTM(context, 0, image.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0.0, 0.0, image.size.width, image.size.height), [image CGImage]);
UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGGradientRelease(gradient);
CGColorSpaceRelease(colorspace);
return gradientImage;
}
You can also add a CAGradientLayer to a view and then add the UIImageView as a subview of that view.
- (void)addGradientLayerToView:(UIView *)view
{
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = #[(id)[[UIColor colorWithRed:0.0 green:0.8 blue:0.8 alpha:1.0] CGColor],
(id)[[UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0] CGColor]];
[view.layer insertSublayer:gradient atIndex:0];
}
Note, you have to #import <QuartzCore/QuartzCore.h> as well as add the QuartzCore framework to your project.
I ended up hacking together some bits of Rob's code and an extension to UIImage I found at http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage
+ (UIImage *)imageNamed:(NSString *)name withGradient:(CGGradientRef)gradient
{
// load the image
UIImage *img = [UIImage imageNamed:name];
// begin a new image context, to draw our colored image onto
UIGraphicsBeginImageContextWithOptions(img.size, NO, [[UIScreen mainScreen] scale]);
// get a reference to that context we created
CGContextRef context = UIGraphicsGetCurrentContext();
// 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 overlay, and the original image
CGContextSetBlendMode(context, kCGBlendModeOverlay);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
// set a mask that matches the shape of the image, then draw (overlay) a colored rectangle
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
//gradient
CGPoint startPoint = CGPointMake(0.0, img.size.height);
CGPoint endPoint = CGPointMake(0.0, 0.0);
CGContextDrawLinearGradient (context, gradient, startPoint, endPoint, 0);
// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGGradientRelease(gradient);
//return the color-burned image
return coloredImg;
}
I want to recreate a tab bar but I stumbled on this problem. As you can see in the images below my current (right image) selected tab bar item is a lot less crisp or sharper than the one from the UITabBar. Notice the small 1 point border around the icon in the left (which I don't know how to do) as well as the gradient inside the icon which is a lot noticeable in mine. I already thought of Core Graphics and Core Images Filters as possible approaches but can't seem to get that effect. I found an older thread which is part of what I want but the answer doesn't seem to work for me and requires a manual loop through the pixels of the image (which I don't know if it is to be desired). Can someone help me?
This is the code I'm currently using which, btw, you're welcome to correct some mistakes if you see any because I'm starting with Core Graphics:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextSaveGState(context);
{
/* Adjust for different coordinate systems from UIKit and Core Graphics and center the image */
CGContextTranslateCTM(context, self.bounds.size.width/2.0 - self.image.size.width/2.0, self.bounds.size.height - self.bounds.size.height/2.0 + self.image.size.height/2.0);
CGContextScaleCTM(context, 1.0f, -1.0f);
CGRect rect = CGRectMake(0, 0, self.image.size.width, self.image.size.height);
/* Add a drop shadow */
UIColor *dropShadowColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.8f];
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 5, dropShadowColor.CGColor);
/* Draw the original image */
CGContextDrawImage(context, rect, self.image.CGImage);
/* Clip to the original image, so that we only draw the shadows on the
inside of the image but nothing outside. */
CGContextClipToMask(context, rect, self.image.CGImage);
if(self.isSelected){
/* draw background image */
CGImageRef background = [UIImage imageNamed:#"UITabBarBlueGradient"].CGImage;
CGContextDrawImage(context, rect, background);
}
else{
/* draw background color to unselected items */
CGColorRef backgroundColor = [UIColor colorWithRed:95/255.0 green:95/255.0 blue:95/255.0 alpha:1].CGColor;
CGContextSetFillColorWithColor(context, backgroundColor);
CGContextFillRect(context, rect);
/* location of the gradient's colors */
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = [NSArray arrayWithObjects:(id)[UIColor colorWithRed:1 green:1 blue:1 alpha:0].CGColor, (id)[UIColor colorWithRed:1 green:1 blue:1 alpha:0.6].CGColor, nil];
/* create the gradient with colors and locations */
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,(__bridge CFArrayRef) colors, locations);
{
/* start and end points of the gradient */
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
/* draw gradient */
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
}
CGGradientRelease(gradient);
}
}
CGContextRestoreGState(context);
CGColorSpaceRelease(colorSpace);
}
I'm working on this too, an optimization you can probably make is instead of rendering the UIImage each time drawrect is called you can save off the UIImage objects in an ivar and just update a UIImageView.image property to display them.
I'm generating my image with the "shine" like this:
(plus_icon.png is a 30 x 30 image with a 4 px wide cross occupying the entire thing in black on a transparent background: which renders like in imageView 2 and 4 like this:
-(UIImage *)tabBarImage{
UIGraphicsBeginImageContext(CGSizeMake(60, 60));
UIImage *image = [UIImage imageNamed:#"plus_icon"];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(ctx, [[UIColor clearColor] CGColor]);
CGContextFillRect(ctx, CGRectMake(0, 0, 60, 60));
CGRect imageRect = CGRectMake(15, 15, 30, 30);
CGContextDrawImage(ctx, imageRect, [image CGImage]);
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
-(UIImage *)sourceImage{
UIGraphicsBeginImageContext(CGSizeMake(60.0, 60.0));
CGContextRef context = UIGraphicsGetCurrentContext();
size_t num_locations = 2;
CGFloat locations[2] = { 0.3, 1.0 };
CGFloat components[8] = {NC(72), NC(122), NC(229), 1.0, NC(110), NC(202), NC(255), 1.0 };
CGColorSpaceRef cspace;
CGGradientRef gradient;
cspace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColorComponents (cspace, components, locations, num_locations);
CGPoint sPoint = CGPointMake(0.0, 15.0);
CGPoint ePoint = CGPointMake(0.0, 45.0);
CGContextDrawLinearGradient (context, gradient, sPoint, ePoint, kCGGradientDrawsBeforeStartLocation| kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
CGColorSpaceRelease(cspace);
[self addShineToContext:context];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
-(void)addShineToContext:(CGContextRef) context{
CGContextSaveGState(context);
size_t num_locations = 2;
CGFloat locations[2] = { 0.3, 0.7};
CGFloat components[8] = {1.0, 1.0, 1.0, 0.8, 1.0, 1.0, 1.0, 0.0};//{0.82, 0.82, 0.82, 0.4, 0.92, 0.92, 0.92, .8 };
CGColorSpaceRef cspace;
CGGradientRef gradient;
cspace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColorComponents (cspace, components, locations, num_locations);
CGPoint sPoint = CGPointMake(25.0f, 15.0);
CGPoint ePoint = CGPointMake(35.0f, 44.0f);
[self addShineClip:context];
CGContextDrawLinearGradient(context, gradient, sPoint, ePoint, kCGGradientDrawsBeforeStartLocation);
// CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
// CGContextFillRect(context, CGRectMake(15,15, 30, 30));
CGColorSpaceRelease(cspace);
CGGradientRelease(gradient);
CGContextRestoreGState(context);
}
-(void)addShineClip:(CGContextRef)context{
CGContextMoveToPoint(context, 15, 35);
CGContextAddQuadCurveToPoint(context, 25, 30, 45, 28);
CGContextAddLineToPoint(context, 45, 15);
CGContextAddLineToPoint(context, 15, 15);
CGContextClosePath(context);
CGContextClip(context);
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.imageView1.image = [self compositeOverSlate:[self drawTabBarOverSourceWithBlend:kCGBlendModeSourceIn]];
self.imageView2.image = [self compositeOverSlate:[self drawTabBarOverSourceWithBlend:kCGBlendModeDestinationIn]];
self.imageView3.image = [self compositeOverSlate:[self drawTabBarOverSourceWithBlend:kCGBlendModeSourceAtop]];
self.imageView4.image = [self compositeOverSlate:[self drawTabBarOverSourceWithBlend:kCGBlendModeDestinationAtop]];
}
-(UIImage *)compositeOverSlate:(UIImage *)image{
UIGraphicsBeginImageContext(image.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect imageRect = CGRectMake(0, 0, 0, 0);
imageRect.size = image.size;
CGContextSetFillColorWithColor(ctx, [[UIColor darkGrayColor] CGColor]);
CGContextFillRect(ctx, imageRect);
CGContextSetShadow(ctx, CGSizeMake(-1.0, 2.0), .5);
CGContextDrawImage(ctx, imageRect, [image CGImage]);
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
-(UIImage *)drawTabBarOverSourceWithBlend:(CGBlendMode)blendMode{
UIGraphicsBeginImageContext(CGSizeMake(60,60));
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextDrawImage(ctx, CGRectMake(0, 0, 60.0, 60.0), [[self sourceImage] CGImage]);
CGContextSetBlendMode(ctx, blendMode);
CGContextDrawImage(ctx, CGRectMake(0, 0, 60.0, 60.0), [[self tabBarImage] CGImage]);
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
but I don't have the border outline cracked yet, but will update if I do crack it.