前言
采用SwiftUI Core Graphics技术,与C#的GDI+绘图类似,具体概念不多说,毕竟我也是新手,本文主要展示效果图及代码,本文示例代码需要请拉到文末自取。
1、图片缩放
- 完全填充,变形压缩
- 将图像居中缩放截取
- 等比缩放
上面三个效果,放一起比较好对比,如下
原图 - 完全填充,变形压缩 - 居中缩放截取 - 等比缩放
- 第1张为原图
- 第2张为完全填充,变形压缩
- 第3张为图像居中缩放截取
- 第4张为等比缩放
示例中缩放前后的图片可导出
2、图片拼图
顾名思义,将多张图片组合成一张图,以下为多张美图原图:
多张美图原图
选择后,界面中预览:
界面中预览
导出拼图查看效果:
导出拼图
3、图片操作方法
最后上图片缩放、拼图代码:
import SwiftUI struct ImageHelper { static let shared = ImageHelper() private init() {} // NSView 转 NSImage func imageFromView(cview: NSView) -> NSImage? { // 从view、data、CGImage获取BitmapImageRep // NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:data]; // NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:CGImage]; guard let bitmap: NSBitmapImageRep = cview.bitmapImageRepForCachingDisplay(in: cview.visibleRect) else { return nil } cview.cacheDisplay(in: cview.visibleRect, to: bitmap) let image: NSImage = NSImage(size: cview.frame.size) image.addRepresentation(bitmap) return image; } // 保存图片到本地 func saveImage(image: NSImage, fileName: String) -> Bool { guard var imageData = image.tiffRepresentation, let imageRep = NSBitmapImageRep(data: imageData) else { return false } // [imageRep setSize:size]; // 只是打开图片时的初始大小,对图片本省没有影响 // jpg if(fileName.hasSuffix("jpg")) { let quality:NSNumber = 0.85 // 压缩率 imageData = imageRep.representation(using: .jpeg, properties:[.compressionFactor:quality])! } else { // png imageData = imageRep.representation(using: .png, properties:[:])! } do { // 写文件 保存到本地需要关闭沙盒 ---- 保存的文件路径一定要是绝对路径,相对路径不行 try imageData.write(to: URL(fileURLWithPath: fileName), options: .atomic) return true } catch { return false } } // 将图片按照比例压缩 // rate 压缩比0.1~1.0之间 func compressedImageDataWithImg(image: NSImage, rate: CGFloat) -> NSData? { guard let imageData = image.tiffRepresentation, let imageRep = NSBitmapImageRep(data: imageData) else { return nil } guard let data: Data = imageRep.representation(using: .jpeg, properties:[.compressionFactor:rate]) else { return nil } return data as NSData; } // 完全填充,变形压缩 func resizeImage(sourceImage: NSImage, forSize size: NSSize) -> NSImage { let targetFrame: NSRect = NSMakeRect(0, 0, size.width, size.height); let sourceImageRep: NSImageRep = sourceImage.bestRepresentation(for: targetFrame, context: nil, hints: nil)! let targetImage: NSImage = NSImage(size: size) targetImage.lockFocus() sourceImageRep.draw(in: targetFrame) targetImage.unlockFocus() return targetImage; } // 将图像居中缩放截取targetsize func resizeImage1(sourceImage: NSImage, forSize targetSize: CGSize) -> NSImage { let imageSize: CGSize = sourceImage.size let width: CGFloat = imageSize.width let height: CGFloat = imageSize.height let targetWidth: CGFloat = targetSize.width let targetHeight: CGFloat = targetSize.height var scaleFactor: CGFloat = 0.0 let widthFactor: CGFloat = targetWidth / width let heightFactor: CGFloat = targetHeight / height scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor // 需要读取的源图像的高度或宽度 let readHeight: CGFloat = targetHeight / scaleFactor let readWidth: CGFloat = targetWidth / scaleFactor let readPoint: CGPoint = CGPoint(x: widthFactor > heightFactor ? 0 : (width - readWidth) * 0.5, y: widthFactor < heightFactor ? 0 : (height - readHeight) * 0.5) let newImage: NSImage = NSImage(size: targetSize) let thumbnailRect: CGRect = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height) let imageRect: NSRect = NSRect(x: readPoint.x, y: readPoint.y, width: readWidth, height: readHeight) newImage.lockFocus() sourceImage.draw(in: thumbnailRect, from: imageRect, operation: .copy, fraction: 1.0) newImage.unlockFocus() return newImage; } // 等比缩放 func resizeImage2(sourceImage: NSImage, forSize targetSize: CGSize) -> NSImage { let imageSize: CGSize = sourceImage.size let width: CGFloat = imageSize.width let height: CGFloat = imageSize.height let targetWidth: CGFloat = targetSize.width let targetHeight: CGFloat = targetSize.height var scaleFactor: CGFloat = 0.0 var scaledWidth: CGFloat = targetWidth var scaledHeight: CGFloat = targetHeight var thumbnailPoint: CGPoint = CGPoint(x: 0.0, y: 0.0) if __CGSizeEqualToSize(imageSize, targetSize) == false { let widthFactor: CGFloat = targetWidth / width let heightFactor: CGFloat = targetHeight / height // scale to fit the longer scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor scaledWidth = ceil(width * scaleFactor) scaledHeight = ceil(height * scaleFactor) // center the image if (widthFactor > heightFactor) { thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5 } else if (widthFactor < heightFactor) { thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5 } } let newImage: NSImage = NSImage(size: NSSize(width: scaledWidth, height: scaledHeight)) let thumbnailRect: CGRect = CGRect(x: thumbnailPoint.x, y: thumbnailPoint.y, width: scaledWidth, height: scaledHeight) let imageRect: NSRect = NSRect(x: 0.0, y:0.0, width: width, height: height) newImage.lockFocus() sourceImage.draw(in: thumbnailRect, from: imageRect, operation: .copy, fraction: 1.0) newImage.unlockFocus() return newImage; } // 将图片压缩到指定大小(KB) func compressImgData(imgData: NSData, toAimKB aimKB: NSInteger) -> NSData? { let aimRate: CGFloat = CGFloat(aimKB * 1000) / CGFloat(imgData.length) let imageRep: NSBitmapImageRep = NSBitmapImageRep(data: imgData as Data)! guard let data: Data = imageRep.representation(using: .jpeg, properties:[.compressionFactor:aimRate]) else { return nil } print("数据最终大小:\(CGFloat(data.count) / 1000), 压缩比率:\(CGFloat(data.count) / CGFloat(imgData.length))") return data as NSData } // 组合图片 func jointedImageWithImages(imgArray: [NSImage]) -> NSImage { var imgW: CGFloat = 0 var imgH: CGFloat = 0 for img in imgArray { imgW += img.size.width; if (imgH < img.size.height) { imgH = img.size.height; } } print("size : \(NSStringFromSize(NSSize(width: imgW, height: imgH)))") let togetherImg: NSImage = NSImage(size: NSSize(width: imgW, height: imgH)) togetherImg.lockFocus() let imgContext: CGContext? = NSGraphicsContext.current?.cgContext var imgX: CGFloat = 0 for imgItem in imgArray { if let img = imgItem as? NSImage { let imageRef: CGImage = self.getCGImageRefFromNSImage(image: img)! imgContext?.draw(imageRef, in: NSRect(x: imgX, y: 0, width: img.size.width, height: img.size.height)) imgX += img.size.width; } } togetherImg.unlockFocus() return togetherImg; } // NSImage转CGImageRef func getCGImageRefFromNSImage(image: NSImage) -> CGImage? { let imageData: NSData? = image.tiffRepresentation as NSData? var imageRef: CGImage? = nil if(imageData != nil) { let imageSource: CGImageSource = CGImageSourceCreateWithData(imageData! as CFData, nil)! imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) } return imageRef; } // CGImage 转 NSImage func getNSImageWithCGImageRef(imageRef: CGImage) -> NSImage? { return NSImage(cgImage: imageRef, size: NSSize(width: imageRef.width, height: imageRef.height)) // var imageRect: NSRect = NSRect(x: 0, y: 0, width: 0, height: 0) // // var imageContext: CGContext? = nil // var newImage: NSImage? = nil // // imageRect.size.height = CGFloat(imageRef.height) // imageRect.size.width = CGFloat(imageRef.width) // // // Create a new image to receive the Quartz image data. // newImage = NSImage(size: imageRect.size) // // newImage?.lockFocus() // // Get the Quartz context and draw. // imageContext = NSGraphicsContext.current?.cgContext // imageContext?.draw(imageRef, in: imageRect) // newImage?.unlockFocus() // // return newImage; } // NSImage转CIImage func getCIImageWithNSImage(image: NSImage) -> CIImage?{ // convert NSImage to bitmap guard let imageData = image.tiffRepresentation, let imageRep = NSBitmapImageRep(data: imageData) else { return nil } // create CIImage from imageRep let ciImage: CIImage = CIImage(bitmapImageRep: imageRep)! // create affine transform to flip CIImage let affineTransform: NSAffineTransform = NSAffineTransform() affineTransform.translateX(by: 0, yBy: 128) affineTransform.scaleX(by: 1, yBy: -1) // create CIFilter with embedded affine transform let transform:CIFilter = CIFilter(name: "CIAffineTransform")! transform.setValue(ciImage, forKey: "inputImage") transform.setValue(affineTransform, forKey: "inputTransform") // get the new CIImage, flipped and ready to serve let result: CIImage? = transform.value(forKey: "outputImage") as? CIImage return result; } }
4、示例代码
界面布局及效果展示代码
import SwiftUI struct TestImageDemo: View { @State private var sourceImagePath: String? @State private var sourceImage: NSImage? @State private var sourceImageWidth: CGFloat = 0 @State private var sourceImageHeight: CGFloat = 0 @State private var resizeImage: NSImage? @State private var resizeImageWidth: String = "250" @State private var resizeImageHeight: String = "250" @State private var resize1Image: NSImage? @State private var resize1ImageWidth: String = "250" @State private var resize1ImageHeight: String = "250" @State private var resize2Image: NSImage? @State private var resize2ImageWidth: String = "250" @State private var resize2ImageHeight: String = "250" @State private var joinImage: NSImage? var body: some View { GeometryReader { reader in VStack { HStack { Button("选择展示图片缩放", action: self.choiceResizeImage) Button("选择展示图片拼图", action: self.choiceJoinImage) Spacer() } HStack { VStack { if let sImage = sourceImage { Section(header: Text("原图")) { Image(nsImage: sImage) .resizable().aspectRatio(contentMode: .fit) .frame(width: reader.size.width / 2) Text("\(self.sourceImageWidth)*\(self.sourceImageHeight)") Button("导出", action: { self.saveImage(image: sImage) }) } } if let sImage = self.joinImage { Section(header: Text("拼图")) { Image(nsImage: sImage) .resizable().aspectRatio(contentMode: .fit) .frame(width: reader.size.width) Button("导出", action: { self.saveImage(image: sImage) }) } } } VStack { Section(header: Text("完全填充,变形压缩")) { VStack { Section(header: Text("Width:")) { TextField("Width", text: self.$resizeImageWidth) } Section(header: Text("Height:")) { TextField("Height", text: self.$resizeImageHeight) } if let sImage = resizeImage { Image(nsImage: sImage) Text("\(self.resizeImageWidth)*\(self.resizeImageHeight)") Button("导出", action: { self.saveImage(image: sImage) }) } } } } VStack { Section(header: Text("将图像居中缩放截取")) { VStack { Section(header: Text("Width:")) { TextField("Width", text: self.$resize1ImageWidth) } Section(header: Text("Height:")) { TextField("Height", text: self.$resize1ImageHeight) } if let sImage = resize1Image { Image(nsImage: sImage) Text("\(self.resize1ImageWidth)*\(self.resize1ImageHeight)") Button("导出", action: { self.saveImage(image: sImage) }) } } } } VStack { Section(header: Text("等比缩放")) { VStack { Section(header: Text("Width:")) { TextField("Width", text: self.$resize2ImageWidth) } Section(header: Text("Height:")) { TextField("Height", text: self.$resize2ImageHeight) } if let sImage = resize2Image { Image(nsImage: sImage) Text("\(self.resize2ImageWidth)*\(self.resize2ImageHeight)") Button("导出", action: { self.saveImage(image: sImage) }) } } } } Spacer() } Spacer() } } } private func choiceResizeImage() { let result: (fail: Bool, url: [URL?]?) = DialogProvider.shared.showOpenFileDialog(title: "", prompt: "", message: "选择图片", directoryURL: URL(fileURLWithPath: ""), allowedFileTypes: ["png", "jpg", "jpeg"]) if result.fail { return } if let urls = result.url, let url = urls[0] { self.sourceImagePath = url.path self.sourceImage = NSImage(contentsOf: URL(fileURLWithPath: self.sourceImagePath!)) self.sourceImageWidth = (self.sourceImage?.size.width)! self.sourceImageHeight = (self.sourceImage?.size.height)! if let resizeWidth = Int(self.resizeImageWidth), let resizeHeight = Int(self.resizeImageHeight) { self.resizeImage = ImageHelper.shared.resizeImage(sourceImage: self.sourceImage!, forSize: CGSize(width: resizeWidth, height: resizeHeight)) } if let resize1Width = Int(self.resize1ImageWidth), let resize1Height = Int(self.resize1ImageHeight) { self.resize1Image = ImageHelper.shared.resizeImage1(sourceImage: self.sourceImage!, forSize: CGSize(width: resize1Width, height: resize1Height)) } if let resize2Width = Int(self.resize2ImageWidth), let resize2Height = Int(self.resize2ImageHeight) { self.resize2Image = ImageHelper.shared.resizeImage1(sourceImage: self.sourceImage!, forSize: CGSize(width: resize2Width, height: resize2Height)) } } } private func choiceJoinImage() { let result: (fail: Bool, url: [URL?]?) = DialogProvider.shared.showOpenFileDialog(title: "", prompt: "", message: "选择图片", directoryURL: URL(fileURLWithPath: ""), allowedFileTypes: ["png", "jpg", "jpeg"], allowsMultipleSelection: true) if result.fail { return } if let urls = result.url { var imgs: [NSImage] = [] for url in urls { if let filePath = url?.path { imgs.append(NSImage(contentsOf: URL(fileURLWithPath: filePath))!) } } if imgs.count > 0 { self.joinImage = ImageHelper.shared.jointedImageWithImages(imgArray: imgs) } } } private func saveImage(image: NSImage) { let result: (isOpenFail: Bool, url: URL?) = DialogProvider.shared.showSaveDialog( title: "选择图片存储路径", directoryURL: URL(fileURLWithPath: ""), prompt: "", message: "", allowedFileTypes: ["png"] ) if result.isOpenFail || result.url == nil || result.url!.path.isEmpty { return } let exportImagePath = result.url!.path _ = ImageHelper.shared.saveImage(image: image, fileName: exportImagePath) NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: exportImagePath)]) } } struct TestImageDemo_Previews: PreviewProvider { static var previews: some View { TestImageDemo() } }
5、结尾
所有代码已贴,并且代码已上传Github,见下面备注。
本文示例代码:https://github.com/dotnet9/MacTest/blob/main/src/macos_test/macos_test/TestImageDemo.swift
参考文章标题:《MAC图像NSIMAGE缩放、组合、压缩及CIIMAGEREF和NSIMAGE转换处理》
参考文章链接:https://www.freesion.com/article/774352759/
到此这篇关于SwiftUI图片缩放、拼图等处理的文章就介绍到这了,更多相关SwiftUI图片缩放、拼图内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!