eurekabeacon
eurekabeacon

Reputation: 31

How to rotate and crop iOS CGImage from file

I'm having some trouble with image rotation in iOS. I'm performing some image manipulation in the background of an app... I would like to rotate and crop the images. Currently, the rotation seems to be working correctly, but no matter what I have tried, the crop is off. I have performed these same operations in GIMP and found that the images crop correctly, so I believe it has something to do with the Quartz coordinate system. Furthermore, the greater the radians in either direction, the further "off" the crop becomes. Here is the code I am using to rotate and crop:

+(UIImage*)imageWithImageFile:(NSString*)imageFile
                      radians:(float)radians
                    imageSize:(CGSize)imageSize
                croppedToRect:(CGRect)croppedToRect
{
    UIImage* returnImg = nil;
    @autoreleasepool {
        CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename([imageFile UTF8String]);
        CGImageRef image = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, YES, kCGRenderingIntentDefault);
        UIGraphicsBeginImageContext(croppedToRect.size);

        CGContextRef context = UIGraphicsGetCurrentContext();

        CGContextSetShouldAntialias(context, YES);
        CGContextSetAllowsAntialiasing(context, YES);
        CGContextSetInterpolationQuality(context, kCGInterpolationHigh);

        CGContextTranslateCTM(context, imageSize.width * 0.5,
                              imageSize.height * 0.5);
        CGContextRotateCTM(context, radians);
        CGContextTranslateCTM(context, -imageSize.width * 0.5, -imageSize.height * 0.5);

        //Translate and scale upside-down to compensate for Quartz's inverted coordinate system
        CGContextTranslateCTM(context, 0.0, imageSize.height);
        CGContextScaleCTM(context, 1.0, -1.0);

        CGContextDrawImage(context, (CGRect) {croppedToRect.origin, imageSize}, image);
        returnImg = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();
        CGDataProviderRelease(dataProvider);
        CGImageRelease(image);
    }

    return returnImg;
}

Upvotes: 0

Views: 460

Answers (1)

eurekabeacon
eurekabeacon

Reputation: 31

I believe this is the solution: Since the context is a different size and has a different origin inside the original image, you must offset the point where you "paint" the image on the context (canvas for those of you more visual people). You first find the origin of the centered context, then compare it to the origin of the desired crop. The difference (or offset) can be subtracted from the center point of the context, which will offset the point at which the image is painted.

    CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename([imageFile UTF8String]);
    CGImageRef image = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, YES, kCGRenderingIntentDefault);

    UIGraphicsBeginImageContext(croppedToRect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGPoint contextCenter = CGPointMake(croppedToRect.size.width * 0.5f,
                                          croppedToRect.size.height * 0.5f);
    CGPoint centerOfImage = CGPointMake(imageSize.width * 0.5f,
                                        imageSize.height * 0.5f);
    CGPoint contextOriginInImage = CGPointMake(centerOfImage.x - contextCenter.x,
                                        centerOfImage.y - contextCenter.y);
    CGPoint desiredOrigin = croppedToRect.origin;
    CGPoint offset = CGPointMake(desiredOrigin.x - contextOriginInImage.x,
                                 desiredOrigin.y - contextOriginInImage.y);
    CGContextTranslateCTM(context, contextCenter.x - offset.x, contextCenter.y - offset.y);
    CGContextRotateCTM(context, radians);
    CGContextScaleCTM(context, 1.0f, -1.0f);
    CGContextDrawImage(context, CGRectMake(-imageSize.width * 0.5f,
                                          -imageSize.height * 0.5f,
                                          imageSize.width,
                                          imageSize.height), image);
    returnImg = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();
    CGDataProviderRelease(dataProvider);
    CGImageRelease(image);

Upvotes: 0

Related Questions