I'm trying to change the color of an area of my UIImage with CGContext then create a new image from the current state. The original image has the correct orientation but after this, the image turns side ways.
CGRect imageRect = CGRectMake(0, 0, originalImage.size.width, originalImage.size.height);
UIGraphicsBeginImageContextWithOptions(originalImage.size, false, originalImage.scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextDrawImage(context, imageRect, originalImage.CGImage);
CGContextSetRGBFillColor(context, 255.0f, 255.0f, 255.0f, 1.0f);
CGContextFillRect(context, CGRectMake(5,5,100,100));
CGContextRotateCTM (context, radians(270));
CGContextRestoreGState(context);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
It's a very easy question.
Because iOS system has 3 different Coordinate System.
UIKit - y axis direction is down
Core Graphics(Quartz) - y axis direction is up
OpenGL ES - y axis direction is up
The originalImage is create by UIKit and the target image is create by Core Graphics, so it should be upside-down.
If you want get the correct image, you can use:
UIImageView* imageV = [[UIImageView alloc] initWithFrame:CGRectMake(100,100, 100, 100)];
imageV.layer.borderColor = [UIColor redColor].CGColor;
imageV.layer.borderWidth = 1;
imageV.image = img;
imageV.layer.transform = CATransform3DRotate(imageV.layer.transform, M_PI, 1.0f, 0.0f, 0.0f);
[self.view addSubview:imageV];
Hope it can help you.
Related
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]]
I would like to fill the transparent part of an image with another image in iOS.
I have tried some stuff with UIGraphicsContext, but I can't get it working because I've never used that before.
Here is the image:
Some code I tried:
UIImageView *v = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 70, 70)];
UIGraphicsBeginImageContextWithOptions(v.frame.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// beach image
UIImage *img = [UIImage imageNamed:#"zivogosce_camping_05.jpg"];
//[img drawInRect:CGRectMake(2, 0, 12, 12)];
UIImage *annotationImg = [UIImage imageNamed:#"custom_annotation"];
[annotationImg drawAtPoint:CGPointMake(0, 0)];
CGContextSetFillColorWithColor(context, [[UIColor colorWithPatternImage:img] CGColor]);
CGContextFillRect(context, CGRectMake(0, 0, 50, 50));
CGContextDrawImage(context, CGRectMake(0, 0, v.frame.size.width, v.frame.size.height), [annotationImg CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
[v setImage:newImage];
UIGraphicsEndImageContext();
But images don't fit well...
Here's how I would do it:
You can make a clipping path in the shape of the circle by doing something like this:
CGContextSaveGState(context);
CGRect circleRect = CGRectMake(circleCenter.x - radius, circleCenter.y - radius, radius * 2.0, radius * 2.0);
CGContextAddEllipseInRect (context, circleRect);
CGContextClip(context);
// ... do whatever you need to in order to draw the inner image ...
CGContextRestoreGState(context);
// ... draw the transparent png ...
Setting a clipping path will cause drawing to only happen within the path. When you restore the state, it removes the clipping path (or rather sets it back to its previous value which is usually allowing it to draw to the entire canvas).
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;
}
How to draw smaller image than size of context without using CGAffineTransform?
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
// center image
CGContextTranslateCTM(context, placement.origin.x, placement.origin.y);
[imageView.layer renderInContext:context];
The problem is that if imageView.image.size is bigger than size it will be drawn centered, but cut at borders (bigger image than drawing area). I want the image to fit, so would like to decrease size of imageView.image.size. Is there any way to achieve this using renderInContext?
Via [imageView.layer renderInContext:context]; you can't change the size (or i didn't find a way). The only option to do so, is to create now image with drawImage (on requested size), allocate new UIImageView and draw its layer.
UIImage *resizedImage = [self createNewImageFrom:currentImage withSize:size];
UIImageView *imageView = [[UIImageView alloc] initWithImage:resizedImage];
// ...
[imageView.layer renderInContext:context];
- (UIImage*)createNewImageFrom:(UIImage*)image withSize:(CGSize)size {
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0f, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, size.width, size.height), image.CGImage);
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
Why do you want to do this without transforming the context? It's the exact right tool for the job!
The simplest way here would be to call CGContextScaleCTM to apply a scale factor to the context. That scale factor will effect all drawing to the context, including the call to -renderIntoContext:.
this is version for scaled size, just replace scale with size.width and size.height for any target size.
for UIView:
-(UIImage*)convertViewToImage:(UIView*)v withScale:(float)scale{
//create bitmap context with scaled size
CGSize s = v.bounds.size;
s.width *= scale;
s.height *= scale;
UIGraphicsBeginImageContextWithOptions(s, NO, [UIScreen mainScreen].scale);
//scale CTM to render and resotre CTM
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextScaleCTM(context, scale, scale);
[v.layer renderInContext: context];
CGContextRestoreGState(context);
UIImage*image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
for UIScrollView
-(UIImage*)convertScrollContentToImage:(UIScrollView*)sv withScale:(float)scale{
//create bitmap context with scaled size
CGSize s = sv.contentSize;
s.width *= scale;
s.height *= scale;
UIGraphicsBeginImageContextWithOptions(s, NO, [UIScreen mainScreen].scale);
{
//set scroll.frame.size to contentSize, and contentOffset to 0
CGPoint savedContentOffset = sv.contentOffset;
CGRect savedFrame = sv.frame;
sv.contentOffset = CGPointZero;
sv.frame = CGRectMake(0, 0, sv.contentSize.width, sv.contentSize.height);
//scale CTM to render and resotre CTM
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextScaleCTM(context, scale, scale);
[sv.layer renderInContext: context];
CGContextRestoreGState(context);
//set scroll.frame.size and ContentOffset back
sv.contentOffset = savedContentOffset;
sv.frame = savedFrame;
}
UIImage*image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
I know i have to translate the coordinate system, when drawing. The thing is that when i use:
CGContextTranslateCTM(_context, 0.0, _size.height);
CGContextScaleCTM(_context, 1.0, -1.0);
My rect of the image is flipped, and the image is 'non-flipped', if i dont use the above my image is flipped, and the rect is non-flipped.
Here's my code:
CGRectMake(60, 100, image.size.width, image.size.height);
CGContextSaveGState(_context);
UIGraphicsBeginImageContext(image.size);
CGContextClearRect(_context, placeholderRect);
CGContextDrawImage(_context, placeholderRect, image.CGImage);
UIGraphicsEndImageContext();
CGContextRestoreGState(_context);
How do i maintain my rect (non-flipped), while the image is flipped?
Thanks
Instead of using CGContextDrawImage use [image drawAtPoint:CGPointMake(0,0)];
The trick may lie in this UIImage method
+ (UIImage *)imageWithCGImage:(CGImageRef)imageRef scale:(CGFloat)scale orientation:(UIImageOrientation)orientation
Where orientation is one of these :)
typedef enum {
UIImageOrientationUp,
UIImageOrientationDown, // 180 deg rotation
UIImageOrientationLeft, // 90 deg CCW
UIImageOrientationRight, // 90 deg CW
UIImageOrientationUpMirrored, // as above but image mirrored along other axis. horizontal flip
UIImageOrientationDownMirrored, // horizontal flip
UIImageOrientationLeftMirrored, // vertical flip
UIImageOrientationRightMirrored, // vertical flip
} UIImageOrientation;
Edit: Update
I just did this simple code sample in a UIView subclass overriding drawrect and it worked perfectly. It doesn't use the UIImage method but works all the same.
UIImage *image = [UIImage imageNamed:#"image.png"];
CGContextRef _context = UIGraphicsGetCurrentContext();
CGContextSaveGState(_context);
CGContextDrawImage(_context, CGRectMake(100, 20, image.size.width, image.size.height), image.CGImage);
CGContextScaleCTM(_context, -1.0f, 1.0f);
CGContextDrawImage(_context, CGRectMake(-300, 20, image.size.width, image.size.height), image.CGImage);
CGContextRestoreGState(_context);