图像处理相关(三) —— HEIC图像压缩(二)

版本记录

版本号 时间
V1.0 2019.10.21 星期一

前言

App中很多时候都需要进行图像处理,包括各种缩放、滤镜和压缩等处理,好的图像处理不仅可以提高App的性能,也会给用户带来耳目一新的感觉,这里重新开了一个专题,专门讲述对图像的各种处理。感兴趣的可以看下面几篇文章。
1. 图像处理相关(一) —— 图像深度相关处理简单示例(一)
2. 图像处理相关(二) —— HEIC图像压缩(一)

源码

1. Swift

首先看下工程组织结构

接着就是sb了

下面就是源码了

1. MainViewController.swift
import UIKit

class MainViewController: UIViewController {
  @IBOutlet var jpgSizeLabel: UILabel!
  @IBOutlet var jpgTimeLabel: UILabel!
  @IBOutlet var jpgActivityIndicator: UIActivityIndicatorView!
  @IBOutlet var jpgImageView: UIImageView!
  @IBOutlet var heicSizeLabel: UILabel!
  @IBOutlet var heicTimeLabel: UILabel!
  @IBOutlet var heicActivityIndicator: UIActivityIndicatorView!
  @IBOutlet var heicImageView: UIImageView!
  @IBOutlet var compressionSlider: UISlider!
  
  private let numberFormatter = NumberFormatter()
  private let compressionQueue = OperationQueue()
  private var previousQuality: Float = 0
  private var originalImage: UIImage = #imageLiteral(resourceName: "jeremy-thomas-unsplash")
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    title = "Compressor"
    
    navigationItem.rightBarButtonItem = UIBarButtonItem(
      barButtonSystemItem: .add,
      target: self,
      action: #selector(addButtonPressed)
    )
    navigationItem.leftBarButtonItem = UIBarButtonItem(
      barButtonSystemItem: .action,
      target: self,
      action: #selector(shareButtonPressed)
    )
    
    #if targetEnvironment(simulator)
    compressionSlider.addTarget(
      self,
      action: #selector(sliderEndedTouch),
      for: [.touchUpInside, .touchUpOutside]
    )
    #else
    compressionSlider.addTarget(
      self,
      action: #selector(sliderDidChange),
      for: .valueChanged
    )
    #endif
    
    numberFormatter.maximumSignificantDigits = 1
    numberFormatter.maximumFractionDigits = 3
    
    updateImages()
  }
  
  // MARK: - Helpers
  
  private func resetLabels() {
    jpgSizeLabel.text = "--"
    jpgTimeLabel.text = "--"
    heicSizeLabel.text = "--"
    heicTimeLabel.text = "--"
  }
  
  private func elapsedTime(from startDate: Date) -> String? {
    let endDate = Date()
    let interval = endDate.timeIntervalSince(startDate)
    let intervalNumber = NSNumber(value: interval)
    
    return numberFormatter.string(from: intervalNumber)
  }
  
  private func compressJPGImage(with quality: CGFloat) {
    let startDate = Date()

    jpgImageView.image = nil
    jpgActivityIndicator.startAnimating()
    
    compressionQueue.addOperation {
      guard let data = self.originalImage.jpegData(compressionQuality: quality) else {
        return
      }
      
      DispatchQueue.main.async {
        if let time = self.elapsedTime(from: startDate) {
          self.jpgTimeLabel.text = "\(time) s"
        }
        self.jpgSizeLabel.text = data.prettySize
        self.jpgImageView.image = UIImage(data: data)
        self.navigationItem.leftBarButtonItem?.isEnabled = true
        
        UIView.animate(withDuration: 0.3) {
          self.jpgActivityIndicator.stopAnimating()
        }
      }
    }
  }
  
  private func compressHEICImage(with quality: CGFloat) {
    let startDate = Date()
    
    heicImageView.image = nil
    heicActivityIndicator.startAnimating()
    
    compressionQueue.addOperation {
      do {
        let data = try self.originalImage.heicData(compressionQuality: quality)
        
        DispatchQueue.main.async {
          if let time = self.elapsedTime(from: startDate) {
            self.heicTimeLabel.text = "\(time) s"
          }
          self.heicSizeLabel.text = data.prettySize
          self.heicImageView.image = UIImage(data: data)
          self.navigationItem.leftBarButtonItem?.isEnabled = true
          
          UIView.animate(withDuration: 0.3) {
            self.heicActivityIndicator.stopAnimating()
          }
        }
      } catch {
        print("Error creating HEIC data: \(error.localizedDescription)")
      }
    }
  }
  
  private func updateImages() {
    let quality = CGFloat(compressionSlider.value)
    
    resetLabels()
    compressionQueue.cancelAllOperations()
    navigationItem.leftBarButtonItem?.isEnabled = false
    
    compressJPGImage(with: quality)
    compressHEICImage(with: quality)
  }
  
  private func shareImage(_ image: UIImage) {
    let avc = UIActivityViewController(
      activityItems: [image],
      applicationActivities: nil
    )
    present(avc, animated: true)
  }
  
  // MARK: - Actions
  
  @objc private func addButtonPressed() {
    let picker = UIImagePickerController()
    picker.delegate = self
    
    present(picker, animated: true)
  }
  
  @objc private func shareButtonPressed() {
    let avc = UIAlertController(title: "Share", message: "How would you like to share?", preferredStyle: .alert)
    
    if let jpgImage = jpgImageView.image {
      avc.addAction(UIAlertAction(title: "JPG", style: .default) { _ in
        self.shareImage(jpgImage)
      })
    }
    
    if let heicImage = heicImageView.image {
      avc.addAction(UIAlertAction(title: "HEIC", style: .default) { _ in
        self.shareImage(heicImage)
      })
    }
    
    avc.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

    present(avc, animated: true)
  }
  
  @objc private func sliderEndedTouch() {
    updateImages()
  }
  
  @objc private func sliderDidChange() {
    let diff = abs(compressionSlider.value - previousQuality)
    
    guard diff > 0.1 else {
      return
    }
    
    previousQuality = compressionSlider.value
    
    updateImages()
  }
}

extension MainViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
  func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    picker.dismiss(animated: true)
  }
  
  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    picker.dismiss(animated: true)

    guard let image = info[.originalImage] as? UIImage else {
      return
    }
    
    originalImage = image
    updateImages()
  }
}
2. UIImage+Additions.swift
import UIKit
import AVFoundation

extension UIImage {
  enum HEICError: Error {
    case heicNotSupported
    case cgImageMissing
    case couldNotFinalize
  }
  
  func heicData(compressionQuality: CGFloat) throws -> Data {
    let data = NSMutableData()
    guard let imageDestination =
      CGImageDestinationCreateWithData(
        data, AVFileType.heic as CFString, 1, nil
      )
      else {
        throw HEICError.heicNotSupported
    }
    
    guard let cgImage = self.cgImage else {
      throw HEICError.cgImageMissing
    }
    
    let options: NSDictionary = [
      kCGImageDestinationLossyCompressionQuality: compressionQuality
    ]
    
    CGImageDestinationAddImage(imageDestination, cgImage, options)
    guard CGImageDestinationFinalize(imageDestination) else {
      throw HEICError.couldNotFinalize
    }
    
    return data as Data
  }
}
3. Data+Additions.swift
import Foundation

extension Data {
  var prettySize: String {
    let formatter = ByteCountFormatter()
    formatter.countStyle = .binary
    return formatter.string(fromByteCount: Int64(count))
  }
}

后记

本篇主要讲述了HEIC图像压缩,感兴趣的给个赞或者关注~~~

你可能感兴趣的:(图像处理相关(三) —— HEIC图像压缩(二))