Swift rotating portrait image without scale - ios

I have a problem with swift, I have an app that, when a button is pressed, must rotate an image of 90°, for doing this I use this code
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool) -> UIImage {
let radiansToDegrees: (CGFloat) -> CGFloat = {
return $0 * (180.0 / CGFloat(M_PI))
}
let degreesToRadians: (CGFloat) -> CGFloat = {
return $0 / 180.0 * CGFloat(M_PI)
}
// calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox = UIView(frame: CGRect(origin: CGPointZero, size: size))
let t = CGAffineTransformMakeRotation(degreesToRadians(degrees));
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size
// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap = UIGraphicsGetCurrentContext()
// Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, 0.5 * rotatedSize.width, 0.5 * rotatedSize.height);
// // Rotate the image context
CGContextRotateCTM(bitmap, degreesToRadians(degrees));
// Now, draw the rotated/scaled image into the context
var yFlip: CGFloat
if(flip){
yFlip = CGFloat(-1.0)
} else {
yFlip = CGFloat(1.0)
}
CGContextScaleCTM(bitmap, yFlip, -1.0)
CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}}
with pictures in landscape i haven't any problem but when I try to rotate an image in portrait, on the first press of the button the image is scaled adapting its width to the width of the ImageView, losing his proportions, and without rotate, it is rotated, always with new proportions, only with the second pressure... can anyone help me?
here a screenshot of the starting situation
after first press
after second press

Related

how to get the rect of an image to crop it

I am building a app where you can crop multiple images. I am using this code directly from apple:
func cropImage(_ inputImage: UIImage, toRect cropRect: CGRect, viewWidth: CGFloat, viewHeight: CGFloat) -> UIImage?
{
let imageViewScale = max(inputImage.size.width / viewWidth,
inputImage.size.height / viewHeight)
// Scale cropRect to handle images larger than shown-on-screen size
let cropZone = CGRect(x:cropRect.origin.x * imageViewScale,
y:cropRect.origin.y * imageViewScale,
width:cropRect.size.width * imageViewScale,
height:cropRect.size.height * imageViewScale)
// Perform cropping in Core Graphics
guard let cutImageRef: CGImage = inputImage.cgImage?.cropping(to:cropZone)
else {
return nil
}
// Return image to UIImage
let croppedImage: UIImage = UIImage(cgImage: cutImageRef)
return croppedImage
}
to crop the image I need a cropRect. I found also a solution in the Internet that I implemented in my code:
func realImageRect() -> CGRect {
let imageViewSize = self.frame.size
let imgSize = self.image?.size
guard let imageSize = imgSize else {
return CGRect.zero
}
let scaleWidth = imageViewSize.width / imageSize.width
let scaleHeight = imageViewSize.height / imageSize.height
let aspect = fmin(scaleWidth, scaleHeight)
var imageRect = CGRect(x: 0, y: 0, width: imageSize.width * aspect, height: imageSize.height * aspect)
// Center image
imageRect.origin.x = (imageViewSize.width - imageRect.size.width) / 2
imageRect.origin.y = (imageViewSize.height - imageRect.size.height) / 2
// Add imageView offset
imageRect.origin.x += self.frame.origin.x
imageRect.origin.y += self.frame.origin.y
return imageRect
}
As I already said, the app can crop multiple images. The images are stored in a array. I also have a crop view, which you can drag around the image with a pan gesture
for i in 0..<imageContentView.count {
let cropRect = CGRect(x: croppedViewArray[i].frame.origin.x - imageContentView[i].realImageRect().origin.x, y: croppedViewArray[i].frame.origin.y - imageContentView[i].realImageRect().origin.y, width: croppedViewArray[i].frame.width, height: croppedViewArray[i].frame.height)
print("cropRect", cropRect)
let croppedImage = ImageCrophandler.sharedInstance.cropImage(imageContentView[i].image!, toRect: cropRect, viewWidth: imageContentView[i].frame.width, viewHeight: imageContentView[i].frame.height)
print("cheight", croppedImage!.size.height,"cwidth", croppedImage!.size.width)
arrayOfCropedImages.append(croppedImage!)
}
The problem what I have is, that every cropped image has a different height and widths, but the images should be all the same size.
I figured out that the size gets calculated on which position the crop view is located.

Resize UIImage's canvas and fill the empty place with color?

In simple words I want to "revert" the crop operation. In crop you choose a rect and cut the image to the specified rect. In my case I need to do the opposite thing - to add empty space around the image and fill it with color. Note - image may have transparent background so I can't just draw one image over another image.
All the input data (rects and image) I already have. How to solve this task?
Basic process:
Create a solid-color UIImage
Get a CGContext
Clear a rect in the center of the new UIImage with (Obj-C) CGContextClearRect(CGContextRef c, CGRect rect); or (Swift) .clear(_ rect: CGRect)
Draw the original image into the "transparent rect" of the new image
Here is an example, using Swift:
func drawImageOnCanvas(_ useImage: UIImage, canvasSize: CGSize, canvasColor: UIColor ) -> UIImage {
let rect = CGRect(origin: .zero, size: canvasSize)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
// fill the entire image
canvasColor.setFill()
UIRectFill(rect)
// calculate a Rect the size of the image to draw, centered in the canvas rect
let centeredImageRect = CGRect(x: (canvasSize.width - useImage.size.width) / 2,
y: (canvasSize.height - useImage.size.height) / 2,
width: useImage.size.width,
height: useImage.size.height)
// get a drawing context
let context = UIGraphicsGetCurrentContext();
// "cut" a transparent rectanlge in the middle of the "canvas" image
context?.clear(centeredImageRect)
// draw the image into that rect
useImage.draw(in: centeredImageRect)
// get the new "image in the center of a canvas image"
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
and call it like this:
if let img = UIImage(named: "myimage") {
let expandedSize = CGSize(width: img.size.width + 60, height: img.size.height + 60)
let imageOnBlueCanvas = drawImageOnCanvas(img, canvasSize: expandedSize, canvasColor: .blue)
let v = UIImageView(image: imageOnBlueCanvas)
view.addSubview(v)
}
Very nice, DonMag,
here is the same as an UIImage extension with the possibility to position the image in the canvas.
extension UIImage {
enum Position { // the position of the image in the canvas
case top
case bottom
case left
case right
case middle
case topLeft
case topRight
case bottomLeft
case bottomRight
}
func drawOnCanvas(withCanvasSize canvasSize: CGSize,
andCanvasColor canvasColor: UIColor,
atPosition position: Position) -> UIImage {
let rect = CGRect(origin: .zero, size: canvasSize)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
canvasColor.setFill() // fill the entire image
UIRectFill(rect)
var x = 0.0 // the position of the image in the canvas
var y = 0.0
switch position { // look at the position specified
case .top:
x = (canvasSize.width - self.size.width) / 2 // position horizontally in the middle
case .bottom:
x = (canvasSize.width - self.size.width) / 2 // position horizontally in the middle
y = canvasSize.height - self.size.height // and vertically at the bottom
case .left:
y = (canvasSize.height - self.size.height) / 2 // position vertically in the middle
case .right:
x = canvasSize.width - self.size.width // position horizontally at the right
y = (canvasSize.height - self.size.height) / 2 // and vertically in the middle
case .middle:
x = (canvasSize.width - self.size.width) / 2 // position horizontally in the middle
y = (canvasSize.height - self.size.height) / 2 // and vertically in the middle
case .topLeft:
x = 0.0 // just dummy
case .topRight:
x = canvasSize.width - self.size.width // position horizontally at the right
case .bottomLeft:
y = canvasSize.height - self.size.height // position vertically at the bottom
case .bottomRight:
x = canvasSize.width - self.size.width // position horizontally at the right
y = canvasSize.height - self.size.height // and vertically at the bottom
}
// calculate a Rect the size of the image to draw, centered in the canvas rect
let positionedImageRect = CGRect(x: x, // position the image in the canvas
y: y,
width: self.size.width,
height: self.size.height)
let context = UIGraphicsGetCurrentContext() // get a drawing context
// "cut" a transparent rectanlge in the middle of the "canvas" image
context?.clear(positionedImageRect)
self.draw(in: positionedImageRect) // draw the image into that rect
let image = UIGraphicsGetImageFromCurrentImageContext() // get the new "image in the canvas image"
UIGraphicsEndImageContext()
return image! // and return it
}
}
with usage:
let imageOnBlueCanvas = img.drawOnCanvas(withCanvasSize: expandedSize, andCanvasColor: .blue, atPosition: .middle)

How use CGContextDrawImage in Swift 3? [duplicate]

This question already has answers here:
Swift 3 and CGContextDrawImage
(2 answers)
Closed 6 years ago.
I have an UIImage extension that uses CGContextDrawImage.
It simply rotate a UIImage.
I updated to Xcode8 and Swift3 and cant figure out how to use CGContextDrawImage.
I got error on CGContextDrawImage.
"Cannot convert value of type "CGImage.Type to expected argument type 'CGImage?'"
My code is :
CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)
And whole extension :
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool) -> UIImage {
let radiansToDegrees: (CGFloat) -> CGFloat = {
return $0 * (180.0 / CGFloat(M_PI))
}
let degreesToRadians: (CGFloat) -> CGFloat = {
return $0 / 180.0 * CGFloat(M_PI)
}
// calculate the size of the rotated view's containing box for our drawing space
let rectZero = CGPoint(dictionaryRepresentation: CGRect.zero as! CFDictionary)
let rotatedViewBox = UIView(frame: CGRect(origin: rectZero!, size: size))
let t = CGAffineTransform(rotationAngle: degreesToRadians(degrees));
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size
// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap = UIGraphicsGetCurrentContext()
// Move the origin to the middle of the image so we will rotate and scale around the center.
bitmap!.translateBy(x: rotatedSize.width / 2.0, y: rotatedSize.height / 2.0);
// // Rotate the image context
bitmap!.rotate(by: degreesToRadians(degrees));
// Now, draw the rotated/scaled image into the context
var yFlip: CGFloat
if(flip){
yFlip = CGFloat(-1.0)
} else {
yFlip = CGFloat(1.0)
}
bitmap!.scaleBy(x: yFlip, y: -1.0)
CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
In Swift 3, many functions for CGContext are imported as an instance method of CGContext, the property name CGImage has renamed to cgImage. And CGRectMake is removed.
Try this:
bitmap!.draw(cgImage!, in: CGRect(x: -size.width / 2, y: -size.height / 2, width: size.width, height: size.height))
(Hope ! would be safe for your usage.)
func draw(CGImage, in: CGRect, byTiling: Bool)

UIImage 3Transform swift

how could i transform image to look like one from the image ?
just want to reach how that transformation works
for rotating im using an extention of UIImage, but thats all a got for now, just new in swift
public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool) -> UIImage {
let radiansToDegrees: (CGFloat) -> CGFloat = {
return $0 * (180.0 / CGFloat(M_PI))
}
let degreesToRadians: (CGFloat) -> CGFloat = {
return $0 / 180.0 * CGFloat(M_PI)
}
// calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox = UIView(frame: CGRect(origin: CGPointZero, size: size))
let t = CGAffineTransformMakeRotation(degreesToRadians(degrees));
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size
// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap = UIGraphicsGetCurrentContext()
// Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width / 2.0, rotatedSize.height / 2.0);
// // Rotate the image context
CGContextRotateCTM(bitmap, degreesToRadians(degrees));
// Now, draw the rotated/scaled image into the context
var yFlip: CGFloat
if(flip){
yFlip = CGFloat(-1.0)
} else {
yFlip = CGFloat(1.0)
}
CGContextScaleCTM(bitmap, yFlip, -1.0)
CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
You would need to use Core Animation with CATransform3D, and tweak the transform to give an impression of depth. What you do is to apply a small negative value (-1/200 is a good starting point) to the M34 component of the transformation matrix. You could ether do that to the layer of each of your views, or use a CATransformLayer on a super layer and then add the other images as sublayers of the transform layer.
This is tricky, fussy stuff. I haven't messed with it in a while so I don't remember the fine details any more, but that should be enough to get you started. I suggest searching on "iOS perspective", "M34" and "CATransformLayer" to find out more.

CGImageCreateWithImageInRect with UIImagePickerControllerOriginalImage doesn't work well

Objective: Get bigger edit image from imagePickerController with correct position.
Question: Could anyone please help me to get the image and tell me what my code is wrong? In my investigation, CGImageCreateWithImageInRect with UIImagePickerControllerCropRect looks weird. But not sure...
Why: info[UIImagePickerControllerEditedImage] is too small, so I want to use info[UIImagePickerControllerEditedImage].
Issue: croppedImage in the code returns wrong position edited image.
Other info: original image is 90 rotated, so I use imageRotatedByDegrees function found in web, and it works well.
class PhotoViewController: UIImagePickerControllerDelegate {
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
let originalImage = info[UIImagePickerControllerOriginalImage] as? UIImage
let rotatedOriginalImage = originalImage!.imageRotatedByDegrees(90, flip: false)
let croppedImage = UIImage(CGImage: CGImageCreateWithImageInRect(rotatedOriginalImage.CGImage, info[UIImagePickerControllerCropRect]!.CGRectValue()))
}
}
// For rotating UIImagePickerControllerOriginalImage
// source: http://blog.ruigomes.me/how-to-rotate-an-uiimage-using-swift/
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool) -> UIImage {
let radiansToDegrees: (CGFloat) -> CGFloat = {
return $0 * (180.0 / CGFloat(M_PI))
}
let degreesToRadians: (CGFloat) -> CGFloat = {
return $0 / 180.0 * CGFloat(M_PI)
}
// calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox = UIView(frame: CGRect(origin: CGPointZero, size: size))
let t = CGAffineTransformMakeRotation(degreesToRadians(degrees));
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size
// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap = UIGraphicsGetCurrentContext()
// Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width / 2.0, rotatedSize.height / 2.0);
// // Rotate the image context
CGContextRotateCTM(bitmap, degreesToRadians(degrees));
// Now, draw the rotated/scaled image into the context
var yFlip: CGFloat
if(flip){
yFlip = CGFloat(-1.0)
} else {
yFlip = CGFloat(1.0)
}
CGContextScaleCTM(bitmap, yFlip, -1.0)
CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
Found a solution. The problem was look like the image orientation function has a bug.
iOS UIImagePickerController result image orientation after upload
I should read the code more to understand what the root cause is.

Resources