mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			191 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Swift
		
	
		
		
			
		
	
	
			191 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Swift
		
	
| 
											1 year ago
										 | // Copyright © 2024 Rangeproof Pty Ltd. All rights reserved. | ||
|  | 
 | ||
|  | import UIKit.UIImage | ||
|  | 
 | ||
|  | public extension UIImage { | ||
|  |     func normalizedImage() -> UIImage { | ||
|  |         guard | ||
|  |             let imgRef: CGImage = self.cgImage, | ||
|  |             imageOrientation != .up | ||
|  |         else { return self } | ||
|  |          | ||
|  |         // The actual resize: draw the image on a new context, applying a transform matrix | ||
|  |         let bounds: CGRect = CGRect(x: 0, y: 0, width: size.width, height: size.height) | ||
|  |         let format = UIGraphicsImageRendererFormat() | ||
|  |         format.scale = self.scale | ||
|  |         format.opaque = false | ||
|  |          | ||
|  |         let renderer: UIGraphicsImageRenderer = UIGraphicsImageRenderer(bounds: bounds, format: format) | ||
|  | 
 | ||
|  |         return renderer.image { rendererContext in | ||
|  |             rendererContext.cgContext.draw(imgRef, in: bounds, byTiling: false) | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     func resized(to targetSize: CGSize) -> UIImage? { | ||
|  |         guard let imgRef: CGImage = self.cgImage else { return nil } | ||
|  |          | ||
|  |         // the below values are regardless of orientation : for UIImages from Camera, width>height (landscape) | ||
|  |         // | ||
|  |         // Note: Not equivalent to self.size (which is dependant on the imageOrientation)! | ||
|  |         let srcSize: CGSize = CGSize(width: imgRef.width, height: imgRef.height) | ||
|  |         var dstSize: CGSize = targetSize | ||
|  |          | ||
|  |         // Don't resize if we already meet the required destination size | ||
|  |         guard dstSize != srcSize else { return self } | ||
|  |          | ||
|  |         let scaleRatio: CGFloat = (dstSize.width / srcSize.width) | ||
|  |         let orient: UIImage.Orientation = self.imageOrientation | ||
|  |         var transform: CGAffineTransform = .identity | ||
|  |          | ||
|  |         switch orient { | ||
|  |             case .up: break                      // EXIF = 1 | ||
|  |             case .upMirrored:                    // EXIF = 2 | ||
|  |                 transform = CGAffineTransform(translationX: srcSize.width, y: 0) | ||
|  |                     .scaledBy(x: -1, y: 1) | ||
|  |                  | ||
|  |             case .down:                          // EXIF = 3 | ||
|  |                 transform = CGAffineTransform(translationX: srcSize.width, y: srcSize.height) | ||
|  |                     .rotated(by: CGFloat.pi) | ||
|  | 
 | ||
|  |             case .downMirrored:                  // EXIF = 4 | ||
|  |                 transform = CGAffineTransform(translationX: 0, y: srcSize.height) | ||
|  |                     .scaledBy(x: 1, y: -1) | ||
|  |                  | ||
|  |             case .leftMirrored:                  // EXIF = 5 | ||
|  |                 dstSize = CGSize(width: dstSize.height, height: dstSize.width) | ||
|  |                 transform = CGAffineTransform(translationX: srcSize.height, y: srcSize.width) | ||
|  |                     .scaledBy(x: -1, y: 1) | ||
|  |                     .rotated(by: (3 * (CGFloat.pi / 2))) | ||
|  |                  | ||
|  |             case .left:                          // EXIF = 6 | ||
|  |                 dstSize = CGSize(width: dstSize.height, height: dstSize.width) | ||
|  |                 transform = CGAffineTransform(translationX: 0, y: srcSize.width) | ||
|  |                     .scaledBy(x: -1, y: 1) | ||
|  |                     .rotated(by: (3 * (CGFloat.pi / 2))) | ||
|  |                  | ||
|  |             case .rightMirrored:                 // EXIF = 7 | ||
|  |                 dstSize = CGSize(width: dstSize.height, height: dstSize.width) | ||
|  |                 transform = CGAffineTransform(scaleX: -1, y: 1) | ||
|  |                     .rotated(by: (CGFloat.pi / 2)) | ||
|  |                  | ||
|  |             case .right:                         // EXIF = 8 | ||
|  |                 dstSize = CGSize(width: dstSize.height, height: dstSize.width) | ||
|  |                 transform = CGAffineTransform(translationX: srcSize.height, y: 0) | ||
|  |                     .rotated(by: (CGFloat.pi / 2)) | ||
|  |              | ||
|  |             @unknown default: return nil | ||
|  |         } | ||
|  | 
 | ||
|  |         // The actual resize: draw the image on a new context, applying a transform matrix | ||
|  |         let bounds: CGRect = CGRect(x: 0, y: 0, width: dstSize.width, height: dstSize.height) | ||
|  |         let format = UIGraphicsImageRendererFormat() | ||
|  |         format.scale = self.scale | ||
|  |         format.opaque = false | ||
|  |          | ||
|  |         let renderer: UIGraphicsImageRenderer = UIGraphicsImageRenderer(bounds: bounds, format: format) | ||
|  | 
 | ||
|  |         return renderer.image { rendererContext in | ||
|  |             rendererContext.cgContext.interpolationQuality = .high | ||
|  |              | ||
|  |             switch orient { | ||
|  |                 case .right, .left: | ||
|  |                     rendererContext.cgContext.scaleBy(x: -scaleRatio, y: scaleRatio) | ||
|  |                     rendererContext.cgContext.translateBy(x: -srcSize.height, y: 0) | ||
|  |                      | ||
|  |                 default: | ||
|  |                     rendererContext.cgContext.scaleBy(x: scaleRatio, y: -scaleRatio) | ||
|  |                     rendererContext.cgContext.translateBy(x: 0, y: -srcSize.height) | ||
|  |             } | ||
|  |              | ||
|  |             rendererContext.cgContext.concatenate(transform) | ||
|  |              | ||
|  |             // we use srcSize (and not dstSize) as the size to specify is in user space (and we use the CTM to apply a | ||
|  |             // scaleRatio) | ||
|  |             rendererContext.cgContext.draw( | ||
|  |                 imgRef, | ||
|  |                 in: CGRect(x: 0, y: 0, width: srcSize.width, height: srcSize.height), | ||
|  |                 byTiling: false | ||
|  |             ) | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     func resized(toFillPixelSize dstSize: CGSize) -> UIImage { | ||
|  |         let normalized: UIImage = self.normalizedImage() | ||
|  |          | ||
|  |         guard | ||
|  |             let normalizedRef: CGImage = normalized.cgImage, | ||
|  |             let imgRef: CGImage = self.cgImage | ||
|  |         else { return self } | ||
|  |          | ||
|  |         // Get the size in pixels, not points | ||
|  |         let srcSize: CGSize = CGSize(width: normalizedRef.width, height: normalizedRef.height) | ||
|  |         let widthRatio: CGFloat = (srcSize.width / srcSize.height) | ||
|  |         let heightRatio: CGFloat = (srcSize.height / srcSize.height) | ||
|  |         let drawRect: CGRect = { | ||
|  |             guard widthRatio <= heightRatio else { | ||
|  |                 let targetWidth: CGFloat = (dstSize.height * srcSize.width / srcSize.height) | ||
|  |                  | ||
|  |                 return CGRect( | ||
|  |                     x: (targetWidth - dstSize.width) * -0.5, | ||
|  |                     y: 0, | ||
|  |                     width: targetWidth, | ||
|  |                     height: dstSize.height | ||
|  |                 ) | ||
|  |             } | ||
|  |              | ||
|  |             let targetHeight: CGFloat = (dstSize.width * srcSize.height / srcSize.width) | ||
|  |              | ||
|  |             return CGRect( | ||
|  |                 x: 0, | ||
|  |                 y: (targetHeight - dstSize.height) * -0.5, | ||
|  |                 width: dstSize.width, | ||
|  |                 height: targetHeight | ||
|  |             ) | ||
|  |         }() | ||
|  |          | ||
|  |         let bounds: CGRect = CGRect(x: 0, y: 0, width: dstSize.width, height: dstSize.height) | ||
|  |         let format = UIGraphicsImageRendererFormat() | ||
|  |         format.scale = 1    // We are specifying a specific pixel size rather than a point size | ||
|  |         format.opaque = false | ||
|  |          | ||
|  |         let renderer: UIGraphicsImageRenderer = UIGraphicsImageRenderer(bounds: bounds, format: format) | ||
|  | 
 | ||
|  |         return renderer.image { rendererContext in | ||
|  |             rendererContext.cgContext.interpolationQuality = .high | ||
|  |              | ||
|  |             // we use srcSize (and not dstSize) as the size to specify is in user space (and we use the CTM to apply a | ||
|  |             // scaleRatio) | ||
|  |             rendererContext.cgContext.draw(imgRef, in: drawRect, byTiling: false) | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     func resized(maxDimensionPoints: CGFloat) -> UIImage? { | ||
|  |         let originalSize: CGSize = self.size | ||
|  |         let maxOriginalDimensionPoints: CGFloat = max(originalSize.width, originalSize.height) | ||
|  |          | ||
|  |         guard originalSize.width > 0 && originalSize.height > 0 else { return nil } | ||
|  |          | ||
|  |         // Don't bother scaling an image that is already smaller than the max dimension. | ||
|  |         guard maxOriginalDimensionPoints > maxDimensionPoints else { return self } | ||
|  |          | ||
|  |         let thumbnailSize: CGSize = { | ||
|  |             guard originalSize.width <= originalSize.height else { | ||
|  |                 return CGSize( | ||
|  |                     width: maxDimensionPoints, | ||
|  |                     height: round(maxDimensionPoints * originalSize.height / originalSize.width) | ||
|  |                 ) | ||
|  |             } | ||
|  |              | ||
|  |             return CGSize( | ||
|  |                 width: round(maxDimensionPoints * originalSize.width / originalSize.height), | ||
|  |                 height: maxDimensionPoints | ||
|  |             ) | ||
|  |         }() | ||
|  |          | ||
|  |         guard thumbnailSize.width > 0 && thumbnailSize.height > 0 else { return nil } | ||
|  |          | ||
|  |         return resized(to: thumbnailSize) | ||
|  |     } | ||
|  | } |