iPhone: CoreGraphics and memory management - iphone

Can someone tell me what I am doing wrong here? I use this method to flip through pages in a PDF. But something in the code seems to not be released properly because every-time I pull a PDF page that contains an image my memory footprint increases. I am fairly new to CoreGraphics, and can't for the life of me figure out where this method would leak memory.
-(UIImage *)pageAtIndex:(NSInteger)pageNumber withWidth:(CGFloat)width andHeight:(CGFloat)height {
if((pageNumber>0) && (pageNumber<=pageCount)) {
CGFloat scaleRatio; // multiplier by which the PDF Page will be scaled
UIGraphicsBeginImageContext(CGSizeMake(width, height));
CGContextRef context = UIGraphicsGetCurrentContext();
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, pageNumber);
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFBleedBox);
//Figure out the orientation of the PDF page and set the scaleRatio accordingly
if(pageRect.size.width/pageRect.size.height < 1.0) {
scaleRatio = height/pageRect.size.height;
}
else {
scaleRatio = width/pageRect.size.width;
}
//Calculate the offset to center the image
CGFloat xOffset = 0.0;
CGFloat yOffset = height;
if(pageRect.size.width*scaleRatio<width) {
xOffset = (width/2)-(pageRect.size.width*scaleRatio/2);
}
else {
yOffset = height-((height/2)-(pageRect.size.height*scaleRatio/2));
}
CGContextTranslateCTM(context, xOffset, yOffset);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSaveGState(context);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFBleedBox, CGRectMake(0, 0, pageRect.size.width, pageRect.size.height), 0, true);
pdfTransform = CGAffineTransformScale(pdfTransform, scaleRatio, scaleRatio);
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, page);
UIImage *tempImage = UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(context);
UIGraphicsEndPDFContext();
UIGraphicsEndImageContext();
return tempImage;
}
return nil;
}

I think I resolved the problem thanks to the people on the apple core graphics mailing list. It seems that CGPDFDocument caches data between calls but never releases it. This appears to be a bug in CoreGraphics. I was told that the only real way around this is to load and unload the PDF every time I pull a page.

You probably weren't releasing something. Gotta check for things like CGPDFPageRetain(<CGPDFPageRef page>) and CGPDFPageRelease(<CGPDFPageRef page>).

Related

How to draw a PDF to a CGContext with a higher resolution?

I wrote a view to render a PDF Page:
-(UIPDFRenderView*) initWithFrame:(CGRect)frame withDocument:(CGPDFDocumentRef*)document withPageNumber:(int)pageNumber
{
if (self)
{
page = CGPDFDocumentGetPage (*document, pageNumber);
pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
pdfScale = (CGRectGetWidth(frame)/pageRect.size.width);
rect = frame;
self = [super initWithFrame:CGRectMake(CGRectGetMinX(frame),
CGRectGetMinY(frame),
CGRectGetWidth(frame),
CGRectGetHeight(pageRect) * pdfScale)];
}
return self;
}
-(void) drawRect:(CGRect)rect
{
[self displayPDFPageWithContext:UIGraphicsGetCurrentContext()];
}
-(void) displayPDFPageWithContext:(CGContextRef)context
{
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context, self.bounds);
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, pdfScale,-1*pdfScale);
CGContextDrawPDFPage (context, page);
}
I use one of that view for every page of a pdf document and add them to a acrollview.
The problem is if i zoom in, the page is pixelig. No surprise because i am using the screen context to render the pdf. Is there any posibility to render that pdf with the double resolution to enable zoomin to a factor of 2?
Okay i got the solution, i render it in original size and initiate the scollview with a low scale that the width of the pdf fits with the width of the scrollview. With that methode i can zoom till original size (scale of 1).

White border when rendering PDF when scale > 1

I have a method that return an UIImage from a CGPDFPageRef. You can specify a width for the image.
The problem is that when pdfScale is > 1, a white border appears in the image. So the PDF is always drawn at scale 1 with a border instead of a bigger scale. Smaller scales are OK.
I've tried to change the type of PDFBox but that doesn't seems to change anything and the documentation is not really clear.
Does somebody sees the error?
- (UIImage*) PDFImageForWidth:(CGFloat) width {
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
CGFloat pdfScale = width/pageRect.size.width;
pageRect.size = CGSizeMake(pageRect.size.width*pdfScale, pageRect.size.height*pdfScale);
pageRect.origin = CGPointZero;
UIGraphicsBeginImageContext(pageRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context, pageRect);
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, pageRect, 0, true));
CGContextDrawPDFPage(context, page);
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
The problem was that CGPDFPageGetDrawingTransform doesn't scale the PDF when scale > 1. You have to do it yourself.
Also, the method is thread safe now.
- (UIImage*) PDFImageForWidth:(CGFloat) width {
CGRect smallPageRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
CGFloat pdfScale = width/smallPageRect.size.width;
CGRect pageRect;
pageRect.size = CGSizeMake(smallPageRect.size.width*pdfScale, smallPageRect.size.height*pdfScale);
pageRect.origin = CGPointZero;
__block CGContextRef context;
dispatch_sync(dispatch_get_main_queue(), ^{
UIGraphicsBeginImageContext(pageRect.size);
context = UIGraphicsGetCurrentContext();
});
if (context != nil) {
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context, pageRect);
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGAffineTransform transform = CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, pageRect, 0, true);
if (pdfScale > 1) {
transform = CGAffineTransformScale(transform, pdfScale, pdfScale);
transform.tx = 0;
transform.ty = 0;
}
CGContextConcatCTM(context, transform);
CGContextDrawPDFPage(context, page);
__block UIImage* image;
dispatch_sync(dispatch_get_main_queue(), ^{
image = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
});
return [image autorelease];
}
else return nil;
}
I was having the same problem and the answer before did help me in some parts but it didn't solve the whole problem.
This is the how I solved my problem.
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
NSLog(#"pdfQuartzViewViewController - drawLayer");
CGPDFDocumentRef myDocumentRef = CGPDFDocumentCreateWithURL((CFURLRef)[NSURL fileURLWithPath:self.strFilename]);
CGPDFPageRef pageReference = CGPDFDocumentGetPage(myDocumentRef, 1);
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // White
CGContextFillRect(context, CGContextGetClipBoundingBox(context)); // Fill
CGContextTranslateCTM(context, 0.0f, layer.bounds.size.height);
// Scaling the pdf page a little big bigger than the view so we can cover the white borders on the page
CGContextScaleCTM(context, 1.0015f, -1.0015f);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(pageReference, kCGPDFCropBox, layer.bounds, 0, true);
pdfTransform.tx = pdfTransform.tx - 0.25f;
pdfTransform.ty = pdfTransform.ty - 0.40f;
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, pageReference); // Render the PDF page into the context
CGPDFDocumentRelease(myDocumentRef);
myDocumentRef = NULL;
}
Here there is a good link where you can find more information about it. The post explains how to use all this methods.
http://iphonedevelopment.blogspot.ch/2008/10/demystifying-cgaffinetransform.html

Rendering a CGPDFPage into a UIImage

I'm trying to render a CGPDFPage (selected from a CGPDFDocument) into a UIImage to display on a view.
I have the following code in MonoTouch which gets me part way there.
RectangleF PDFRectangle = new RectangleF(0, 0, UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height);
public override void ViewDidLoad ()
{
UIGraphics.BeginImageContext(new SizeF(PDFRectangle.Width, PDFRectangle.Height));
CGContext context = UIGraphics.GetCurrentContext();
context.SaveState();
CGPDFDocument pdfDoc = CGPDFDocument.FromFile("test.pdf");
CGPDFPage pdfPage = pdfDoc.GetPage(1);
context.DrawPDFPage(pdfPage);
UIImage testImage = UIGraphics.GetImageFromCurrentImageContext();
pdfDoc.Dispose();
context.RestoreState();
UIImageView imageView = new UIImageView(testImage);
UIGraphics.EndImageContext();
View.AddSubview(imageView);
}
A section of the CGPDFPage is displayed but rotated back-to-front and upside down. My question is, how do I select the full pdf page and flip it round to display correctly. I have seen a few examples using ScaleCTM and TranslateCTM but couldn't seem to get them working.
Any examples in ObjectiveC are fine, I'll take all the help I can get :)
Thanks
I haven't worked with MonoTouch. However, in objective-C you would get an image for a PDF page like this (notice the CTM transforms):
-(UIImage *)getThumbForPage:(int)page_number{
CGFloat width = 60.0;
// Get the page
CGPDFPageRef myPageRef = CGPDFDocumentGetPage(myDocumentRef, page);
// Changed this line for the line above which is a generic line
//CGPDFPageRef page = [self getPage:page_number];
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
CGFloat pdfScale = width/pageRect.size.width;
pageRect.size = CGSizeMake(pageRect.size.width*pdfScale, pageRect.size.height*pdfScale);
pageRect.origin = CGPointZero;
UIGraphicsBeginImageContext(pageRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// White BG
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,pageRect);
CGContextSaveGState(context);
// ***********
// Next 3 lines makes the rotations so that the page look in the right direction
// ***********
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page, kCGPDFMediaBox, pageRect, 0, true));
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
UIImage *thm = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return thm;
}

How to save an image using UIImageWriteToSavedPhotosAlbum after it is transformed

Ok I have searched all over and I can't not seem to find the answer I need. :-(
Here is what I am doing and what I need to happen. I have a UIImageView that I am able to transfrom using UIPanGestureRecognizer, UIRotationGestureRecognizer, and UIPinchGestureRecognizer all that works great. The problem comes when it is time to save those transfomations to my PhotoAlbum. The results are not what i am expecting. So far here is the code that i am using (al-be-it very incomplete)
-(IBAction)saveface:(id)sender
{
static int kMaxResolution = 640;
CGImageRef imgRef = face.image.CGImage;
CGFloat width = CGImageGetWidth(imgRef);
CGFloat height = CGImageGetHeight(imgRef);
CGRect bounds = CGRectMake(0, 0, width, height);
if (width > kMaxResolution || height > kMaxResolution) {
CGFloat ratio = width/height;
if (ratio > 1) {
bounds.size.width = kMaxResolution;
bounds.size.height = bounds.size.width / ratio;
} else {
bounds.size.height = kMaxResolution;
bounds.size.width = bounds.size.height * ratio;
}
}
UIGraphicsBeginImageContext(bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -height);
NSLog(#"SAVE TX:%f TY:%f A:%f B:%f C:%f D:%f", face.transform.tx, face.transform.ty, face.transform.a,face.transform.b,face.transform.c,face.transform.d);
CGFloat x = face.transform.tx;
CGFloat y = face.transform.ty;
CGContextTranslateCTM (context, x,-y);
CGContextConcatCTM(context, face.transform);
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(imageCopy, self, nil, nil);
}
From this method I am able to save the image however the coordinates are way off. I can see any scaling I did that works ok. I see my rotation but it seems to be backwards and the paning is WAY off.
The coords are all wrong and for the life of me I can't figure out what is wrong. I admit I am new to Objective-C and I have never used Quartz 2D before and my understanding of the transform matrixes is limited. I do understand that my transformations are not on the image data itself as trying to save the image out right with out applying this context to it does nothing. So please can anyone set me straight on this?
You generally want to translate before you scale.
You've flipped your coordinate system but you still assign the old x and y values. I think you should have:
CGFloat y = face.transform.tx;
CGFloat x = face.transform.ty;
The face object is not defined in this code. If it is off, everything else will be off. If it is a property you should use the self.face reference form to make sure it is accessed properly.
I would recommend performing the scale transform last.
If none of this works. Comment out all but one transform and see if that single transform works. Then add the others in until it fails.
This kind of work is much easier if you the image in a UIView, transform, scale and then render the layer. It saves all kinds of hassle later.
Something like:
CGSize vscaledSize = myOriginalImage.size;
//add in the scaled bits of the view
//scaling
CGFloat wratio = vscaledSize.width/self.pictureView.frame.size.width;
CGFloat vhightScaled = self.pictureView.frame.size.height * wratio;
vscaledSize.height = vhightScaled;
CGFloat hratio = vscaledSize.height/self.pictureView.frame.size.height;
//create context
UIGraphicsBeginImageContext(myOriginalImage.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context); //1 original context
// translate/flip the graphics context (for transforming from CG* coords to UI* coords
CGContextTranslateCTM(context, 0, vscaledSize.height);
CGContextScaleCTM(context, 1.0, -1.0);
//original image
CGContextDrawImage(context, CGRectMake(0,0,vscaledSize.width,vscaledSize.height), myOriginalImage.CGImage);
CGContextRestoreGState(context);//1 restore to original;
//scale context to match view size
CGContextSaveGState(context); //1 pre-scaled size
CGContextScaleCTM(context, wratio, hratio);
//render
[self.pictureView.layer renderInContext:context];
CGContextRestoreGState(context);//1 restore to pre-scaled size;
UIImage *exportImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
You'll notice that I flip the coord system for the image render, but restore it to the original coordinate system for the UIView.layer render.
Hope this helps

How to crop the UIImage?

I develop an application in which i process the image using its pixels but in that image processing it takes a lot of time. Therefore i want to crop UIImage (Only middle part of image i.e. removing/croping bordered part of image).I have the develop code are,
- (NSInteger) processImage1: (UIImage*) image
{
CGFloat width = image.size.width;
CGFloat height = image.size.height;
struct pixel* pixels = (struct pixel*) calloc(1, image.size.width * image.size.height * sizeof(struct pixel));
if (pixels != nil)
{
// Create a new bitmap
CGContextRef context = CGBitmapContextCreate(
(void*) pixels,
image.size.width,
image.size.height,
8,
image.size.width * 4,
CGImageGetColorSpace(image.CGImage),
kCGImageAlphaPremultipliedLast
);
if (context != NULL)
{
// Draw the image in the bitmap
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), image.CGImage);
NSUInteger numberOfPixels = image.size.width * image.size.height;
NSMutableArray *numberOfPixelsArray = [[[NSMutableArray alloc] initWithCapacity:numberOfPixelsArray] autorelease];
}
How i take(croping outside bordered) the middle part of UIImage?????????
Try something like this:
CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
Note: cropRect is smaller rectangle with middle part of the image...
I was looking for a way to get an arbitrary rectangular crop (ie., sub-image) of a UIImage.
Most of the solutions I tried do not work if the orientation of the image is anything but UIImageOrientationUp.
For example:
http://www.hive05.com/2008/11/crop-an-image-using-the-iphone-sdk/
Typically if you use your iPhone camera, you will have other orientations like UIImageOrientationLeft, and you will not get a correct crop with the above. This is because of the use of CGImageRef/CGContextDrawImage which differ in the coordinate system with respect to UIImage.
The code below uses UI* methods (no CGImageRef), and I have tested this with up/down/left/right oriented images, and it seems to work great.
// get sub image
- (UIImage*) getSubImageFrom: (UIImage*) img WithRect: (CGRect) rect {
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
// translated rectangle for drawing sub image
CGRect drawRect = CGRectMake(-rect.origin.x, -rect.origin.y, img.size.width, img.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
[img drawInRect:drawRect];
// grab image
UIImage* subImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return subImage;
}
Because I needed it just now, here is M-V 's code in Swift 4:
func imageWithImage(image: UIImage, croppedTo rect: CGRect) -> UIImage {
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
let drawRect = CGRect(x: -rect.origin.x, y: -rect.origin.y,
width: image.size.width, height: image.size.height)
context?.clip(to: CGRect(x: 0, y: 0,
width: rect.size.width, height: rect.size.height))
image.draw(in: drawRect)
let subImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return subImage!
}
It would ultimately be faster, with a lot less image creation from sprite atlases, if you could set not only the image for a UIImageView, but also the top-left offset to display within that UIImage. Maybe this is possible. It would certainly eliminate a lot of effort!
Meanwhile, I created these useful functions in a utility class that I use in my apps. It creates a UIImage from part of another UIImage, with options to rotate, scale, and flip using standard UIImageOrientation values to specify. The pixel scaling is preserved from the original image.
My app creates a lot of UIImages during initialization, and this necessarily takes time. But some images aren't needed until a certain tab is selected. To give the appearance of quicker load I could create them in a separate thread spawned at startup, then just wait till it's done when that tab is selected.
This code is also posted at Most efficient way to draw part of an image in iOS
+ (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture {
return [ChordCalcController imageByCropping:imageToCrop toRect:aperture withOrientation:UIImageOrientationUp];
}
// Draw a full image into a crop-sized area and offset to produce a cropped, rotated image
+ (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture withOrientation:(UIImageOrientation)orientation {
// convert y coordinate to origin bottom-left
CGFloat orgY = aperture.origin.y + aperture.size.height - imageToCrop.size.height,
orgX = -aperture.origin.x,
scaleX = 1.0,
scaleY = 1.0,
rot = 0.0;
CGSize size;
switch (orientation) {
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
size = CGSizeMake(aperture.size.height, aperture.size.width);
break;
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
case UIImageOrientationUp:
case UIImageOrientationUpMirrored:
size = aperture.size;
break;
default:
assert(NO);
return nil;
}
switch (orientation) {
case UIImageOrientationRight:
rot = 1.0 * M_PI / 2.0;
orgY -= aperture.size.height;
break;
case UIImageOrientationRightMirrored:
rot = 1.0 * M_PI / 2.0;
scaleY = -1.0;
break;
case UIImageOrientationDown:
scaleX = scaleY = -1.0;
orgX -= aperture.size.width;
orgY -= aperture.size.height;
break;
case UIImageOrientationDownMirrored:
orgY -= aperture.size.height;
scaleY = -1.0;
break;
case UIImageOrientationLeft:
rot = 3.0 * M_PI / 2.0;
orgX -= aperture.size.height;
break;
case UIImageOrientationLeftMirrored:
rot = 3.0 * M_PI / 2.0;
orgY -= aperture.size.height;
orgX -= aperture.size.width;
scaleY = -1.0;
break;
case UIImageOrientationUp:
break;
case UIImageOrientationUpMirrored:
orgX -= aperture.size.width;
scaleX = -1.0;
break;
}
// set the draw rect to pan the image to the right spot
CGRect drawRect = CGRectMake(orgX, orgY, imageToCrop.size.width, imageToCrop.size.height);
// create a context for the new image
UIGraphicsBeginImageContextWithOptions(size, NO, imageToCrop.scale);
CGContextRef gc = UIGraphicsGetCurrentContext();
// apply rotation and scaling
CGContextRotateCTM(gc, rot);
CGContextScaleCTM(gc, scaleX, scaleY);
// draw the image to our clipped context using the offset rect
CGContextDrawImage(gc, drawRect, imageToCrop.CGImage);
// pull the image from our cropped context
UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();
// pop the context to get back to the default
UIGraphicsEndImageContext();
// Note: this is autoreleased
return cropped;
}
#Very small/simple Swift 5 version,
You shouldn't mix UI and CG objects, they sometimes have very different coordinate spaces. This can make you sad.
Note 👉 : self.draw(at:)
#inlinable private prefix func - (right: CGPoint) -> CGPoint
{
return CGPoint(x: -right.x, y: -right.y)
}
extension UIImage
{
public func cropped(to cropRect: CGRect) -> UIImage?
{
let renderer = UIGraphicsImageRenderer(size: cropRect.size)
return renderer.image
{
_ in
self.draw(at: -cropRect.origin)
}
}
}
Using the function
CGContextClipToRect(context, CGRectMake(0, 0, size.width, size.height));
Here's an example code, used for a different purpose but clips ok.
- (UIImage *)aspectFillToSize:(CGSize)size
{
CGFloat imgAspect = self.size.width / self.size.height;
CGFloat sizeAspect = size.width/size.height;
CGSize scaledSize;
if (sizeAspect > imgAspect) { // increase width, crop height
scaledSize = CGSizeMake(size.width, size.width / imgAspect);
} else { // increase height, crop width
scaledSize = CGSizeMake(size.height * imgAspect, size.height);
}
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClipToRect(context, CGRectMake(0, 0, size.width, size.height));
[self drawInRect:CGRectMake(0.0f, 0.0f, scaledSize.width, scaledSize.height)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
If you want a portrait crop down the center of every photo.
Use #M-V solution, & replace cropRect.
CGFloat height = imageTaken.size.height;
CGFloat width = imageTaken.size.width;
CGFloat newWidth = height * 9 / 16;
CGFloat newX = abs((width - newWidth)) / 2;
CGRect cropRect = CGRectMake(newX,0, newWidth ,height);
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, 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/