How can I make Mirrored Button in iOS Camera? - ios

I want making mirroring button(left right reverse) in Camera.
But I don't know how to make. TnT
#IBAction func MirroringImage(_ sender: Any) {
func MirroringImage(_ image: UIImage) -> UIImage? {
}
}

If you just want to mirror a single image, but not changing the image raw data, you can use withHorizontallyFlippedOrientation() on your UIImage.
https://developer.apple.com/documentation/uikit/uiimage/2113668-withhorizontallyflippedorientati
If you want to change the image raw data, try this:
func flip(image: UIImage) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
let bitmap = UIGraphicsGetCurrentContext()!
bitmap.translateBy(x: image.size.width / 2, y: image.size.height / 2)
bitmap.scaleBy(x: -1.0, y: -1.0)
bitmap.translateBy(x: -image.size.width / 2, y: -image.size.height / 2)
bitmap.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
let out = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return out
}
If you are using AVCaptureSesstion, then you can use isVideoMirrored :
https://developer.apple.com/documentation/avfoundation/avcaptureconnection/1389172-isvideomirrored

Related

How to apply scale when drawing and composing UIImage

I have the following functions.
extension UIImage
{
var width: CGFloat
{
return size.width
}
var height: CGFloat
{
return size.height
}
private static func circularImage(diameter: CGFloat, color: UIColor) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(CGSize(width: diameter, height: diameter), false, 0)
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
let rect = CGRect(x: 0, y: 0, width: diameter, height: diameter)
context.setFillColor(color.cgColor)
context.fillEllipse(in: rect)
context.restoreGState()
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
private func addCentered(image: UIImage, tintColor: UIColor) -> UIImage
{
let topImage = image.withTintColor(tintColor, renderingMode: .alwaysTemplate)
let bottomImage = self
UIGraphicsBeginImageContext(size)
let bottomRect = CGRect(x: 0, y: 0, width: bottomImage.width, height: bottomImage.height)
bottomImage.draw(in: bottomRect)
let topRect = CGRect(x: (bottomImage.width - topImage.width) / 2.0,
y: (bottomImage.height - topImage.height) / 2.0,
width: topImage.width,
height: topImage.height)
topImage.draw(in: topRect, blendMode: .normal, alpha: 1.0)
let mergedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return mergedImage
}
}
They work fine, but how do I properly apply UIScreen.main.scale to support retina screens?
I've looked at what's been done here but can't figure it out yet.
Any ideas?
Accessing UIScreen.main.scale itself is a bit problematic, as you have to access it only from main thread (while you usually want to put a heavier image processing on a background thread). So I suggest one of these ways instead.
First of all, you can replace UIGraphicsBeginImageContext(size) with
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
The last argument (0.0) is a scale, and based on docs "if you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen."
If instead you want to retain original image's scale on resulting UIImage, you can do this: after topImage.draw, instead of getting the UIImage with UIGraphicsGetImageFromCurrentImageContext, get CGImage with
let cgImage = context.makeImage()
and then construct UIImage with the scale and orientation of the original image (as opposed to defaults)
let mergedImage = UIImage(
cgImage: cgImage,
scale: image.scale,
orientation: image.opientation)

Flip image vertically (Swift)

How to flip an UIImage vertically (up/down)? This is a question that is asked before...
Flip UIImage Along Either Axis
How to flip UIImage horizontally?
How to flip UIImage horizontally with Swift?
I am showing an image in a image view and it is possible to draw in the image. For the erase function I use an UIBezierpath in a CAShapeLayer. The strokeColor of the CAShapeLayer is UIColor(patternImage: background).cgColor, where background is the same image as the image in the image view.
The background turned out to be upside down. From other Stackoverflow posts I understand that this is caused because UIKit and Core Graphics use another origin for their coordinates system.
I tried some of the solutions in the Stackoverflow posts to flip my background image. None of these where working:
UIImage* sourceImage = [UIImage imageNamed:#"whatever.png"];
UIImage* flippedImage = [UIImage imageWithCGImage:sourceImage.CGImage
scale:sourceImage.scale
orientation:UIImageOrientationUpMirrored];
let ciimage: CIImage = CIImage(CGImage: imagenInicial.CGImage!)
let rotada3 = ciimage.imageByApplyingTransform(CGAffineTransformMakeScale(1, -1))
func flipImageVertically() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let bitmap = UIGraphicsGetCurrentContext()!
bitmap.translateBy(x: size.width / 2, y: size.height / 2)
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.translateBy(x: -size.width / 2, y: -size.height / 2)
bitmap.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
I started experimenting with the last solution by changing the scale and just watching the result. It turned out that my solution was that I needed to use
bitmap.scaleBy(x: 1.0, y: 1.0)
in the function flipImageVertically() to create a new upside down image.
I don't understand this. By scaling with 1 I thought I was not changing anything.
I don't know if the imageOrientation of my original image is important. But the imageOrientation is .up.
I hope someone can explain why I can flip an image vertically with the function
func flipImageVertically() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let bitmap = UIGraphicsGetCurrentContext()!
bitmap.translateBy(x: size.width / 2, y: size.height / 2)
bitmap.scaleBy(x: 1.0, y: 1.0)
bitmap.translateBy(x: -size.width / 2, y: -size.height / 2)
bitmap.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
I think that the point is that you are getting the context with UIGraphicsGetCurrentContext() and then drawing using the draw(_:CGImage, in: CGRect) function.
In fact you can safely delete these lines in your code because they do exactly nothing:
bitmap.translateBy(x: size.width / 2, y: size.height / 2)
bitmap.scaleBy(x: 1.0, y: 1.0)
bitmap.translateBy(x: -size.width / 2, y: -size.height / 2)
I mean... you are scaling by 1.0 (which is doing nothing), and you are translating the context by the same amount in opposite directions which means you are not translating at all.
If you instead draw to the context using the UIImage instance method draw(in: CGRect), the image is not flipped.
Changing the following line worked for me: context.scaleBy(x: 1.0, y: -1.0)
extension UIImage {
public func flipVertically() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
let context = UIGraphicsGetCurrentContext()!
context.translateBy(x: self.size.width/2, y: self.size.height/2)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -self.size.width/2, y: -self.size.height/2)
self.draw(in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}

Visible Artifacts Compositing two UIImages with UIGraphicsContext using Hard Light transfer mode

I am compositing two UIImages together using a UIGraphicsContext.
It works for the most part, but using the Hard Light CGBlendMode, I am getting strange artifacts that are not present if I use a Hard Light transfer mode in Photoshop.
I would try to use Core Image, but I need to be able to control the opacity of the top layer being transferred with Hard Light, and as far as I can tell that is not possible.
Here is the image created with a UIGraphicsContext:
Here it is using the same layers and opacity in Photoshop:
Here is the base image:
And the layer being composited with 60% opacity using Hard Light:
I have tried setting the context interpolation quality to high, using a transparency layer, but nothing has made any improvement.
My code is below. Does anyone know how to fix this, or alternative ways of achieving the same results that I am getting in Photoshop?
public extension UIImage {
public func compImagesWithImage(_ topImg: UIImage, blendMode: CGBlendMode, opacity: CGFloat) -> UIImage? {
let baseImg = self
if let cgImage = baseImg.cgImage {
let baseW = CGFloat(baseImg.cgImage!.width as size_t)
let baseH = CGFloat(baseImg.cgImage!.height as size_t)
UIGraphicsBeginImageContext(CGSize(width: baseW, height: baseH))
if let context = UIGraphicsGetCurrentContext() {
context.saveGState();
context.interpolationQuality = .high
let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: baseH)
context.concatenate(flipVertical)
context.setAlpha(1.0)
context.setBlendMode(CGBlendMode.normal)
context.draw(cgImage, in: CGRect(origin: CGPoint.zero, size: CGSize(width: CGFloat(baseW), height: CGFloat(baseH))))
context.setAlpha(opacity)
context.setBlendMode(blendMode)
context.beginTransparencyLayer(auxiliaryInfo: nil)
context.draw(topImg.cgImage!, in: CGRect(origin: CGPoint.zero, size: CGSize(width: CGFloat(baseW), height: CGFloat(baseH))))
context.endTransparencyLayer()
context.restoreGState();
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return finalImage
}
}
return nil
}
}
This fixed it: Higlight Artifacts When Using Hard Light Blend Mode
I just had to set the alpha of the base layer to 0.99.
Here is the updated code:
public func compImagesWithImage(_ topImg: UIImage, blendMode: CGBlendMode, opacity: CGFloat) -> UIImage? {
let baseImg = self
if let cgImage = baseImg.cgImage {
let baseW = CGFloat(baseImg.cgImage!.width as size_t)
let baseH = CGFloat(baseImg.cgImage!.height as size_t)
UIGraphicsBeginImageContext(CGSize(width: baseW, height: baseH))
if let context = UIGraphicsGetCurrentContext() {
context.saveGState();
context.interpolationQuality = .high
let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: baseH)
context.concatenate(flipVertical)
context.setAlpha(0.99)
context.setBlendMode(CGBlendMode.normal)
context.draw(cgImage, in: CGRect(origin: CGPoint.zero, size: CGSize(width: CGFloat(baseW), height: CGFloat(baseH))))
context.setAlpha(opacity)
context.setBlendMode(blendMode)
context.beginTransparencyLayer(auxiliaryInfo: nil)
context.draw(topImg.cgImage!, in: CGRect(origin: CGPoint.zero, size: CGSize(width: CGFloat(baseW), height: CGFloat(baseH))))
context.endTransparencyLayer()
context.restoreGState();
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return finalImage
}
}

Rotate UIImage in swift

I want to rotate UIImage in clockwise direction. But my current code do not perform function accurate some time it rotate and some time its skip rotation. I want my UIImage will continuously rotate clockwise on my action button.
Here is my current code:
imageView.image = imageView.image!.imageRotatedByDegrees(angle, flip: false)
angle = angle + 90
if angle > 360{
angle = 0
}
This works for me:
works Fine :)
func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRectMake(0, 0, oldImage.size.width, oldImage.size.height))
let t: CGAffineTransform = CGAffineTransformMakeRotation(degrees * CGFloat(M_PI / 180))
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContextRef = UIGraphicsGetCurrentContext()!
//Move the origin to the middle of the image so we will rotate and scale around the center.
CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2)
//Rotate the image context
CGContextRotateCTM(bitmap, (degrees * CGFloat(M_PI / 180)))
//Now, draw the rotated/scaled image into the context
CGContextScaleCTM(bitmap, 1.0, -1.0)
CGContextDrawImage(bitmap, CGRectMake(-oldImage.size.width / 2, -oldImage.size.height / 2, oldImage.size.width, oldImage.size.height), oldImage.CGImage)
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Swift 3:
func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: oldImage.size.width, height: oldImage.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat.pi / 180)
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = 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, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat.pi / 180)
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = 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, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(self.cgImage!, in: CGRect(x: -self.size.width / 2, y: -self.size.height / 2, width: self.size.width, height: self.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
public func fixedOrientation() -> UIImage {
if imageOrientation == UIImageOrientation.up {
return self
}
var transform: CGAffineTransform = CGAffineTransform.identity
switch imageOrientation {
case UIImageOrientation.down, UIImageOrientation.downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat.pi)
break
case UIImageOrientation.left, UIImageOrientation.leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat.pi/2)
break
case UIImageOrientation.right, UIImageOrientation.rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: -CGFloat.pi/2)
break
case UIImageOrientation.up, UIImageOrientation.upMirrored:
break
}
switch imageOrientation {
case UIImageOrientation.upMirrored, UIImageOrientation.downMirrored:
transform.translatedBy(x: size.width, y: 0)
transform.scaledBy(x: -1, y: 1)
break
case UIImageOrientation.leftMirrored, UIImageOrientation.rightMirrored:
transform.translatedBy(x: size.height, y: 0)
transform.scaledBy(x: -1, y: 1)
case UIImageOrientation.up, UIImageOrientation.down, UIImageOrientation.left, UIImageOrientation.right:
break
}
let ctx: CGContext = CGContext(data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: self.cgImage!.bitsPerComponent,
bytesPerRow: 0,
space: self.cgImage!.colorSpace!,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
ctx.concatenate(transform)
switch imageOrientation {
case UIImageOrientation.left, UIImageOrientation.leftMirrored, UIImageOrientation.right, UIImageOrientation.rightMirrored:
ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
default:
ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
break
}
let cgImage: CGImage = ctx.makeImage()!
return UIImage(cgImage: cgImage)
}
}
#Jason, You should fix orientation of image before rotation.
let image = oldimage.fixedOrientation().imageRotatedByDegrees(90.0)
#Jason Chitla, I cant comment yet, so Im posting it as a new answer. The above solution from #Albert works, but only for square images. The problem is, that
bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
is still using the original size, so if you rotate it 90 degrees, aspect is wrong.
What works for me is to use the new rotatedSize, like this:
func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: oldImage.size.width, height: oldImage.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat(M_PI / 180))
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = 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, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat(M_PI / 180)))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(self.cgImage!, in: CGRect(x: -rotatedSize.width / 2, y: -rotatedSize.height / 2, width: rotatedSize.width, height: rotatedSize.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
You can create a function like this to make your image rotate in clockwise forever:
override func viewDidLoad() {
super.viewDidLoad()
let kRotationAnimationKey = "com.myapplication.rotationanimationkey"
func rotateView(view: UIView, duration: Double = 1) {
if view.layer.animationForKey(kRotationAnimationKey) == nil {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotationAnimation.fromValue = 0.0
rotationAnimation.toValue = Float(M_PI * 2.0)
rotationAnimation.duration = duration
rotationAnimation.repeatCount = Float.infinity
view.layer.addAnimation(rotationAnimation, forKey: kRotationAnimationKey)
}
}
rotateView(self.imageView)
}
Result is:
For those who need the code from #Mughees in Swift 3:
func imageRotatedByDegrees(oldImage: UIImage, deg degrees: CGFloat) -> UIImage {
//Calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: oldImage.size.width, height: oldImage.size.height))
let t: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat(M_PI / 180))
rotatedViewBox.transform = t
let rotatedSize: CGSize = rotatedViewBox.frame.size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = 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, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat(M_PI / 180)))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(oldImage.cgImage!, in: CGRect(x: -oldImage.size.width / 2, y: -oldImage.size.height / 2, width: oldImage.size.width, height: oldImage.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
I fixed first function of #gleb's answer little bit. Size calculation is not necessary there. We simply should do it with reverse.
Because if you do it size of 1149 x 356 with #gleb's function then you will get image sized 357 x 1149.
extension UIImage {
public func imageRotatedByDegrees(degrees: CGFloat) -> UIImage {
let rotatedSize: CGSize = CGRect(x: 0, y: 0, width: self.size.height, height: self.size.width).size
//Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap: CGContext = 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, y: rotatedSize.height / 2)
//Rotate the image context
bitmap.rotate(by: (degrees * CGFloat.pi / 180))
//Now, draw the rotated/scaled image into the context
bitmap.scaleBy(x: 1.0, y: -1.0)
bitmap.draw(self.cgImage!, in: CGRect(x: -self.size.width / 2, y: -self.size.height / 2, width: self.size.width, height: self.size.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
This s probably easier and it is Swift 5 syntax:
// MARK: Rotate
extension UIImage {
func rotate(radians: Float) -> UIImage? {
var newSize = CGRect(origin: CGPoint.zero, size: self.size).applying(CGAffineTransform(rotationAngle: CGFloat(radians))).size
// Trim off the extremely small float value to prevent core graphics from rounding it up
newSize.width = floor(newSize.width)
newSize.height = floor(newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, self.scale)
let context = UIGraphicsGetCurrentContext()!
// Move origin to middle
context.translateBy(x: newSize.width/2, y: newSize.height/2)
// Rotate around middle
context.rotate(by: CGFloat(radians))
// Draw the image at its center
self.draw(in: CGRect(x: -self.size.width/2, y: -self.size.height/2, width: self.size.width, height: self.size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
I believe it's worth to post solution using CoreImage. It's just couple of lines and it works great for me.
Please note: when getting final UIImage, it's necessary to convert to CGImage first to respect extent of CIImage
extension UIImage {
func imageRotated(by degrees: CGFloat) -> UIImage {
let orientation = CGImagePropertyOrientation(imageOrientation)
// Create CIImage respecting image's orientation
guard let inputImage = CIImage(image: self)?.oriented(orientation)
else { return self }
// Rotate the image itself
let rotation = CGAffineTransform(rotationAngle: (degrees * CGFloat.pi / 180))
let outputImage = inputImage.transformed(by: rotation)
// Create CGImage first
guard let cgImage = CIContext().createCGImage(outputImage, from: outputImage.extent)
else { return self }
// Create output UIImage from CGImage
return UIImage(cgImage: cgImage)
}
}

Resize and Crop 2 Images affected the original image quality

Supposed that I have a UIImage's object on the UIViewController, and I want to set the image from the Controller. Basically what I want to do is, merging two images together, that the first image is the 5 star with blue color :
and the second image is the 5 star with grey color :
It's intended for rating image. Since the maximum rating is 5, then I have to multiply it by 20 to get 100 point to make the calculation easier. Please see code for more details logic.
So I have this (BM_RatingHelper.swift) :
static func getRatingImageBasedOnRating(rating: CGFloat, width: CGFloat, height: CGFloat) -> UIImage {
// available maximum rating is 5.0, so we have to multiply it by 20 to achieve 100.0 point
let ratingImageWidth = ( width / 100.0 ) * ( rating * 20.0 )
// get active rating image
let activeRatingImage = BM_ImageHelper.resize(UIImage(named: "StarRatingFullActive")!, targetSize: CGSize(width: width, height: height))
let activeRatingImageView = UIImageView(frame: CGRectMake(0, 0, ratingImageWidth, height));
activeRatingImageView.image = BM_ImageHelper.crop(activeRatingImage, x: 0, y: 0, width: ratingImageWidth, height: height);
// get inactive rating image
let inactiveRatingImage = BM_ImageHelper.resize(UIImage(named: "StarRatingFullInactive")!, targetSize: CGSize(width: width, height: height))
let inactiveRatingImageView = UIImageView(frame: CGRectMake(ratingImageWidth, 0, ( 100.0 - ratingImageWidth ), height));
inactiveRatingImageView.image = BM_ImageHelper.crop(inactiveRatingImage, x: ratingImageWidth, y: 0, width: ( 100.0 - ratingImageWidth ), height: height);
// combine the images
let ratingView = UIView.init(frame: CGRect(x: 0, y: 0, width: width, height: height))
ratingView.backgroundColor = BM_Color.colorForType(BM_ColorType.ColorWhiteTransparent)
ratingView.addSubview(activeRatingImageView)
ratingView.addSubview(inactiveRatingImageView)
return ratingView.capture()
}
The BM_ImageHelper.swift :
import UIKit
class BM_ImageHelper: NSObject {
// http://stackoverflow.com/questions/158914/cropping-an-uiimage
static func crop(image: UIImage, x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -> UIImage {
let rect = CGRect(x: x, y: y, width: width, height: height)
let imageRef = CGImageCreateWithImageInRect(image.CGImage, rect)!
let croppedImage = UIImage(CGImage: imageRef)
return croppedImage
}
// http://iosdevcenters.blogspot.com/2015/12/how-to-resize-image-in-swift-in-ios.html
static func resize(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / image.size.width
let heightRatio = targetSize.height / image.size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSizeMake(size.width * heightRatio, size.height * heightRatio)
} else {
newSize = CGSizeMake(size.width * widthRatio, size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRectMake(0, 0, newSize.width, newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.drawInRect(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
extension UIView {
// http://stackoverflow.com/a/34895760/897733
func capture() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.frame.size, self.opaque, UIScreen.mainScreen().scale)
self.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
I call that function like (supposed that the image need to filled is ratingImage) :
self.ratingImage.image =
BM_RatingHelper.getRatingImageBasedOnRating(3.7, width: 100.0, height: 20.0)
The code works perfectly, but the merged image is so low in quality although I have use the high quality image. This is the image for 3.7 rating :
What should I do to merge the images without lose the original quality? Thanks.
In your BM_ImageHelper.resize method its giving the scale 1.0. It should be the devices's screens scale.
Change it to
UIGraphicsBeginImageContextWithOptions(newSize, false, UIScreen.mainScreen().scale)
UPDATE
Also change your crop method to address the scale, like
static func crop(image: UIImage, x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -> UIImage {
let transform = CGAffineTransformMakeScale(image.scale, image.scale)
let rect = CGRect(x: x, y: y, width: width, height: height)
let transformedCropRect = CGRectApplyAffineTransform(rect, transform);
let imageRef = CGImageCreateWithImageInRect(image.CGImage, transformedCropRect)!
let croppedImage = UIImage(CGImage: imageRef, scale: image.scale, orientation: image.imageOrientation)
return croppedImage
}

Resources