My intention is to write a function which can extract a rotated rectangular portion of a larger image. That rotated rectangular portion is described with a center point (from 0,0 to full image width/height) a size, and a rotation angle.
I worry that I'm not understanding something about CGContexts. I know that after a rotation, my coordinate space is rotated.
This function always works if the angle is 0 and appears to be correct if the angle is small. However, as the angle increases, everything is offset improperly.
I am calculating the top-left corner with an affine transform similar to the image transformation to avoid any inaccuracy. I have also tried further changing the top-left corner by applying the image transformation to it, as well as the inverse of the image transformation (just as a random thought).
Will this approach work? Am I missing a space transformation somewhere?
Note: It was extremely easy to black out the image area by drawing the full image and then drawing a black rectangle at the correct position with the right size and rotation. Given this, I know my inputs are correct.
Visualizations:
This represents the full image, and the rotated rect I am trying to sample:
This image represents the desired output of this function:
- (UIImage*) croppedImage:(UIImage*) image center:(CGPoint) rotationCenter size:(CGSize) size rotation:(CGFloat) rotation
{
CGFloat fullWidth = image.size.width;
CGFloat fullHeight = image.size.height;
CGRect imageRect = CGRectMake(0, 0, fullWidth, fullHeight);
//must convert UI space to CG space, in this case that just means adjusting the y coordinate
rotationCenter.y = fullHeight - rotationCenter.y;
CGFloat width = size.width;
CGFloat height = size.height;
int flags = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
CGContextRef btx = CGBitmapContextCreate(NULL, width, height, 8, 0, CGColorSpaceCreateDeviceRGB(), flags);
CGContextSetAllowsAntialiasing(btx, YES);
CGContextSetShouldAntialias(btx, YES);
//use a transformation to rotate at the center of the request area
CGAffineTransform imageTransform = CGAffineTransformIdentity;
imageTransform = CGAffineTransformTranslate(imageTransform, rotationCenter.x, rotationCenter.y);
imageTransform = CGAffineTransformRotate(imageTransform, rotation);
imageTransform = CGAffineTransformTranslate(imageTransform, -rotationCenter.x, -rotationCenter.y);
//use a transformation to determine the top-left coordinate of the rect
CGAffineTransform ptTransform = CGAffineTransformIdentity;
ptTransform = CGAffineTransformTranslate(ptTransform, rotationCenter.x, rotationCenter.y);
ptTransform = CGAffineTransformRotate(ptTransform, rotation);
ptTransform = CGAffineTransformTranslate(ptTransform, -width/2.0, -height/2.0);
//this gets me the position of the top-left corner of the image no matter the rotation
CGPoint topleft = CGPointApplyAffineTransform(CGPointZero, ptTransform);
CGContextConcatCTM(btx, imageTransform);
//move the image away from the origin, to align origin with where i want my image sampled from
CGContextTranslateCTM(btx, -topleft.x,
-topleft.y);
//Close... but wrong
CGContextDrawImage(btx, imageRect, image.CGImage);
CGImageRef img = CGBitmapContextCreateImage(btx);
UIImage* uiimage = [UIImage imageWithCGImage:img scale:image.scale orientation:image.imageOrientation];
CGContextRelease(btx);
CGImageRelease(img);
return uiimage;
}