华北野战军 發表於 2021-8-22 16:35:12

SwiftUI图片缩放、拼图等处理教程

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>前言</li><li>1、图片缩放
</li><li>2、图片拼图
</li><li>3、图片操作方法
</li><li>4、示例代码
</li><li>5、结尾
</li></ul></div><p class="maodian"></p><h2>前言</h2>
<p>采用SwiftUI Core Graphics技术,与C#的GDI+绘图类似,具体概念不多说,毕竟我也是新手,本文主要展示效果图及代码,本文示例代码需要请拉到文末自取。</p>
<p class="maodian"></p><h2>1、图片缩放<br />
</h2>
<ul>
    <li>完全填充,变形压缩</li>
    <li>将图像居中缩放截取</li>
    <li>等比缩放<br />
    </li>
</ul>
<p>上面三个效果,放一起比较好对比,如下</p>
<p style="text-align: center"><img alt="" src="https://img.jbzj.com/file_images/article/202108/2021822163016168.jpg?2021722163026" /></p>
<p>原图 - 完全填充,变形压缩 - 居中缩放截取 - 等比缩放</p>
<ul>
    <li>第1张为原图</li>
    <li>第2张为完全填充,变形压缩</li>
    <li>第3张为图像居中缩放截取</li>
    <li>第4张为等比缩放<br />
    </li>
</ul>
<p>示例中缩放前后的图片可导出</p>
<p class="maodian"></p><h2>2、图片拼图<br />
</h2>
<p>顾名思义,将多张图片组合成一张图,以下为多张美图原图:</p>
<p style="text-align: center"><img alt="" src="https://img.jbzj.com/file_images/article/202108/2021822163057073.jpg?202172216316" /></p>
<p style="text-align: center">多张美图原图</p>
<p>选择后,界面中预览:</p>
<p style="text-align: center"><img alt="" src="https://img.jbzj.com/file_images/article/202108/2021822163119458.jpg?2021722163125" /></p>
<p style="text-align: center">界面中预览</p>
<p>导出拼图查看效果:</p>
<p style="text-align: center"><img alt="" src="https://img.jbzj.com/file_images/article/202108/2021822163156418.jpg?202172216322" /></p>
<p style="text-align: center">导出拼图</p>
<p class="maodian"></p><h2>3、图片操作方法<br />
</h2>
<p>最后上图片缩放、拼图代码:</p>
<div class="jb51code">
<pre class="brush:java;">
import SwiftUI

struct ImageHelper {
   
   
    static let shared = ImageHelper()
    private init() {}

    // NSView 转 NSImage
    func imageFromView(cview: NSView) -&gt; NSImage&#63; {

      // 从view、data、CGImage获取BitmapImageRep
      // NSBitmapImageRep *bitmap = ;
      // NSBitmapImageRep *bitmap = [[ 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) -&gt; Bool {
      guard var imageData = image.tiffRepresentation,
            let imageRep = NSBitmapImageRep(data: imageData) else { return false }
      
      //    ;// 只是打开图片时的初始大小,对图片本省没有影响
      // 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) -&gt; NSData&#63; {
      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) -&gt; 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) -&gt; 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 &gt; heightFactor) &#63; widthFactor : heightFactor
      
      // 需要读取的源图像的高度或宽度
      let readHeight: CGFloat = targetHeight / scaleFactor
      let readWidth: CGFloat = targetWidth / scaleFactor
      let readPoint: CGPoint = CGPoint(x: widthFactor &gt; heightFactor &#63; 0 : (width - readWidth) * 0.5,
                                       y: widthFactor &lt; heightFactor &#63; 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) -&gt; 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 &gt; heightFactor) &#63; widthFactor : heightFactor
            scaledWidth= ceil(width * scaleFactor)
            scaledHeight = ceil(height * scaleFactor)

            // center the image
            if (widthFactor &gt; heightFactor) {
                thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5
            } else if (widthFactor &lt; 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) -&gt; NSData&#63; {

      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: ) -&gt; NSImage {

      var imgW: CGFloat = 0
      var imgH: CGFloat = 0
      for img in imgArray {
            imgW += img.size.width;
            if (imgH &lt; 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&#63; = NSGraphicsContext.current&#63;.cgContext

      var imgX: CGFloat = 0
      for imgItem in imgArray {
            if let img = imgItem as&#63; NSImage {
                let imageRef: CGImage = self.getCGImageRefFromNSImage(image: img)!
                imgContext&#63;.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) -&gt; CGImage&#63; {

      let imageData: NSData&#63; = image.tiffRepresentation as NSData&#63;
      var imageRef: CGImage&#63; = 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) -&gt; NSImage&#63; {

      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&#63; = nil
//      var newImage: NSImage&#63; = 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&#63;.lockFocus()
//      // Get the Quartz context and draw.
//      imageContext = NSGraphicsContext.current&#63;.cgContext
//      imageContext&#63;.draw(imageRef, in: imageRect)
//      newImage&#63;.unlockFocus()
//
//      return newImage;
    }
   
    // NSImage转CIImage
    func getCIImageWithNSImage(image: NSImage) -&gt; CIImage&#63;{

      // 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&#63; = transform.value(forKey: "outputImage") as&#63; CIImage
      return result;
    }
}

</pre>
</div>
<p class="maodian"></p><h2>4、示例代码<br />
</h2>
<p>界面布局及效果展示代码</p>
<div class="jb51code">
<pre class="brush:java;">
import SwiftUI

struct TestImageDemo: View {
    @State private var sourceImagePath: String&#63;
    @State private var sourceImage: NSImage&#63;
    @State private var sourceImageWidth: CGFloat = 0
    @State private var sourceImageHeight: CGFloat = 0
    @State private var resizeImage: NSImage&#63;
    @State private var resizeImageWidth: String = "250"
    @State private var resizeImageHeight: String = "250"
    @State private var resize1Image: NSImage&#63;
    @State private var resize1ImageWidth: String = "250"
    @State private var resize1ImageHeight: String = "250"
    @State private var resize2Image: NSImage&#63;
    @State private var resize2ImageWidth: String = "250"
    @State private var resize2ImageHeight: String = "250"
    @State private var joinImage: NSImage&#63;
    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: &#63;) =
            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 {
            self.sourceImagePath = url.path
            self.sourceImage = NSImage(contentsOf: URL(fileURLWithPath: self.sourceImagePath!))
            self.sourceImageWidth = (self.sourceImage&#63;.size.width)!
            self.sourceImageHeight = (self.sourceImage&#63;.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: &#63;) =
            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: = []
            for url in urls {
                if let filePath = url&#63;.path {
                  imgs.append(NSImage(contentsOf: URL(fileURLWithPath: filePath))!)
                }
            }
            if imgs.count &gt; 0 {
                self.joinImage = ImageHelper.shared.jointedImageWithImages(imgArray: imgs)
            }
      }
    }
   
    private func saveImage(image: NSImage) {
      let result: (isOpenFail: Bool, url: URL&#63;) =
            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()
    }
}

struct TestImageDemo_Previews: PreviewProvider {
    static var previews: some View {
      TestImageDemo()
    }
}

</pre>
</div>
<p class="maodian"></p><h2>5、结尾<br />
</h2>
<p>所有代码已贴,并且代码已上传Github,见下面备注。</p>
<p>本文示例代码:https://github.com/dotnet9/MacTest/blob/main/src/macos_test/macos_test/TestImageDemo.swift</p>
<p>参考文章标题:《MAC图像NSIMAGE缩放、组合、压缩及CIIMAGEREF和NSIMAGE转换处理》</p>
<p>参考文章链接:https://www.freesion.com/article/774352759/</p>
<p>到此这篇关于SwiftUI图片缩放、拼图等处理的文章就介绍到这了,更多相关SwiftUI图片缩放、拼图内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>SwiftUI&nbsp;登录界面布局实现示例详解</li><li>SwiftUI中TabView组件的常规使用</li><li>如何利用SwiftUI实现可缩放的图片预览器</li><li>SwiftUI中@ViewBuilder的相关知识点解密</li><li>SwiftUI学习之state和Binding的区别浅析</li><li>SwiftUI 引导页界面实现示例</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: SwiftUI图片缩放、拼图等处理教程