user4812000 user4812000 - 7 months ago 26
Swift Question

CGImageCreateWithImageInRect cutting cutting off

I am using CGImageCreateWithImageInRect in Swift to create a copy of part of an image at the touch location.

My code works great except when the user touches the edge of the screen and the rect size falls outside the view frame. Instead of returning a square, it returns a rectangle (I assume) from cutting off the amount that lays outside the view. The change is shape throws off the position of the copied image, so it does not align with the lower image.

How do I keep the square shape/size and ignore the bounds, so my images align?

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

let touch = touches.first!.locationInView(mainImageView?.superview)

UIGraphicsBeginImageContext(self.view.bounds.size)
UIApplication.sharedApplication().keyWindow!.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

let crop = CGRectMake(touch.x - CGFloat(brushWidth) / 2, touch.y - CGFloat(brushWidth) / 2, CGFloat(brushWidth), CGFloat(brushWidth))

let imageRef = CGImageCreateWithImageInRect(screenshot.CGImage, crop)

if imageRef != nil {
let newImage: UIImage = UIImage(CGImage: imageRef!, scale: mainImageView.image!.scale, orientation: mainImageView.image!.imageOrientation)

let bgImage = UIImageView(image: processedImage)
bgImage.center = touch
self.view.addSubview(bgImage)

}
}
}

}


Here is an example of it working:

enter image description here

Here is an example of it warping the image, because I touched near the edge:

enter image description here

Answer

CGImageCreateWithImageInRect(screenshot.CGImage, crop) does indeed return a cropped version. Here is the relevant comment from the documentation:

Quartz performs these tasks to create the subimage:

  • Adjusts the area specified by the rect parameter to integral bounds by calling the function CGRectIntegral.

  • Intersects the result with a rectangle whose origin is (0,0) and size is equal to the size of the image specified by the image parameter.

  • References the pixels within the resulting rectangle, treating the first pixel within the rectangle as the origin of the sub image.

A simple way to solve this is to adjust the position of the resulting UIImageView so it is correct for its size. Here is the relevant calculation:

let screenshotBounds = CGRectMake(0.0, 0.0, screenshot.size.width, screenshot.size.height)
let cropIntegral = CGRectIntegral(crop)
let cropIntersection = CGRectIntersection(cropIntegral, screenshotBounds)
bgImage.center = CGPoint(CGRectGetMidX(cropIntersection), CGRectGetMidY(cropIntersection))

cropIntersection is the bounding rectangle of our extracted image (following the first two steps given in the documentation). Therefore, we can use it to position the imageView to the same place in the original image.