iOS ImageIO 的使用- CGImageSource图像解码

1,628 阅读4分钟

这里只讲ImageIO中CGImageSource常见的三种使用方式,一种是进行图片格式解码读取(包括原始图片和生成缩略图),另一种是获取图片的相关信息(如:定位,拍摄设备,分辨率等),最后一种是渐进式加载。

基本知识

CGImageSource:解码-读取图片数据类

    //查看支持解码的type类型
    let mySourceTypes : CFArray = CGImageSourceCopyTypeIdentifiers();
    print(mySourceTypes);
    ps.支持格式过多,我删了一部分日志
(
    "public.jpeg",
    "public.png",
    "com.compuserve.gif",
    "com.canon.tif-raw-image",
    "com.adobe.raw-image",
    "com.dxo.raw-image",
    "com.konicaminolta.raw-image",
    "com.olympus.sr-raw-image",
    "com.microsoft.ico",
    "com.microsoft.bmp",
    ...
    "com.apple.icns",
    "com.adobe.photoshop-image",
    "com.microsoft.cur",
    "com.truevision.tga-image",
    "com.ilm.openexr-image",
    "org.webmproject.webp",
    "public.radiance",
    "public.pbm",
    "public.mpo-image",
    "public.pvr",
    "com.microsoft.dds"
)

CGImageDestination:编码-写入数据类,写入数据类放在另一篇文章中

     //查看支持编码的type类型
     let myDestinationTypes : CFArray = CGImageDestinationCopyTypeIdentifiers();
     print(myDestinationTypes);

ImageSourceOption 键值

kCGImageSourceTypeIdentifierHint:设置预设的图片格式,格式参照UTType.h
kCGImageSourceShouldAllowFloat:如果文件格式支持,是否应将图像作为浮点CGImageRef返回
kCGImageSourceShouldCache:是否应以解码形式缓存图像
kCGImageSourceCreateThumbnailFromImageIfAbsent:如果原图中缩略图不存在,是否根据原图创建缩略图
kCGImageSourceCreateThumbnailFromImageAlways:是否始终根据原图创建缩略图,即使原图中存在缩略图
kCGImageSourceThumbnailMaxPixelSize:缩略图最大尺寸,CFNumber格式
kCGImageSourceCreateThumbnailWithTransform:缩略图是否根据原图像的方向和像素纵横比进行旋转和缩放

一.导入图片数据,进行图片格式解码

正如前文基础知识中CGImageSource支持解码的图片数据类型,是不是除了png,jpg很多格式都没见过。有一些格式图片没有办法通过UIImage(named:String)来进行加载,这里就需要通过CGImageSource进行格式转换成CGImage进行加载

    func createImageFromSource()-> CGImage?{
        let myOptions = [kCGImageSourceShouldCache : kCFBooleanTrue,kCGImageSourceShouldAllowFloat : kCFBooleanTrue] as CFDictionary;
        //这里我放了一张png图片在工程中
        guard let imgPath = Bundle.main.path(forResource: "IMG_0868", ofType: ".PNG")else{
            return nil
        }
        guard let myImageSource = CGImageSourceCreateWithURL(URL(fileURLWithPath: imgPath) as CFURL, myOptions) else {
            print(stderr, "Image source is NULL.");
            return nil
        }
        //通过CGImageSourceCreateImageAtIndex函数生成cgimage格式数据
        guard let myImage = CGImageSourceCreateImageAtIndex(myImageSource,0,nil)else {
            print(stderr, "Image not created from image source.");
            return nil
        };
        return myImage;
    }

二.通过原图获取缩略图

    func createThumbnailFromSource()-> CGImage?{
        let myOptions = [kCGImageSourceShouldCache : kCFBooleanTrue,kCGImageSourceShouldAllowFloat : kCFBooleanTrue] as CFDictionary;
        //这里我放了一张png图片在工程中
        guard let imgPath = Bundle.main.path(forResource: "IMG_0868", ofType: ".PNG")else{
            return nil
        }
        guard let myImageSource = CGImageSourceCreateWithURL(URL(fileURLWithPath: imgPath) as CFURL, myOptions) else {
            print(stderr, "Image source is NULL.");
            return nil
        }
        let thumbnailOptions = [kCGImageSourceCreateThumbnailWithTransform : kCFBooleanTrue,kCGImageSourceCreateThumbnailFromImageIfAbsent : kCFBooleanTrue, kCGImageSourceThumbnailMaxPixelSize : 200] as CFDictionary;
        // 生成缩略图
        guard let thumbnailImage = CGImageSourceCreateThumbnailAtIndex(myImageSource,0,thumbnailOptions)else {
            print(stderr, "Image not created from image source.");
            return nil
        };
     
        return thumbnailImage;
    }

三.获取图片的相关信息

图片视频等文件其实都是一个压缩包,里面包含很多信息,通过指定的格式解压才有了我们看到的效果。图片中就包含定位、拍摄设备、拍摄日期以及大小等等。通过CGImageSourceCopyPropertiesAtIndex就可以拿到这些信息,具体需要用到什么信息再自行加工。CGImageProperties详细对照表

    func getPropertiesFromImgSource() -> [String:Any]? {
        // Create the dictionary
        let myOptions : CFDictionary = [kCGImageSourceShouldCache : kCFBooleanTrue,kCGImageSourceShouldAllowFloat : kCFBooleanTrue] as CFDictionary;
        // Create an image source from the URL.
        guard let imgPath = Bundle.main.path(forResource: "IMG_0851", ofType: ".HEIC")else{
            return nil
        }
        guard let myImageSource = CGImageSourceCreateWithURL(URL(fileURLWithPath: imgPath) as CFURL, myOptions) else {
            print(stderr, "Image source is NULL.");
            return nil
        }
        
        guard let props : NSDictionary = CGImageSourceCopyPropertiesAtIndex(myImageSource, 0, nil)else{
                    return nil
            }
        print(props)
        //需要返回数据自行进行加工
        return nil
    }

打印内容
{
    ColorModel = RGB;
    DPIHeight = 72;
    DPIWidth = 72;
    Depth = 8;
    Orientation = 1;
    PixelHeight = 3024;
    PixelWidth = 4032;
    PrimaryImage = 1;
    ProfileName = "Display P3";
    "{Exif}" =     {
        ...
    };
    "{GPS}" =     {
        ...
    };
    "{MakerApple}" =     {
      ...
    };
    "{TIFF}" =     {
      ...
    };
}

四.逐步加载图片

在某些时候加载一张比较大的或者质量比较高的图片,下载时间比较长。会影响用户体验,通过CGImageSourceUpdateData逐步更新图片数据就会使体验变得更好一些。

    //用来接收请求返回的data
    var imgData = Data()
    //这里用本地网络请求下载图片,多张图片加载时请使用多线程优化,不做过多阐述
    func createCreFromSource(){
        let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
        let task = session.dataTask(with: URL(string: "http://localhost:8181/download?fileName=IMG_1438.JPG")!)
        task.resume()
    }
    //通过URLSessionDataDelegate实现,不要用block,block会在拿到所有数据后触发
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
       //拼接接收到的data数据
       imgData.append(data)

       let imageOptions = [kCGImageSourceShouldCache : kCFBooleanTrue,kCGImageSourceShouldAllowFloat : kCFBooleanTrue] as CFDictionary
       let incrementSource = CGImageSourceCreateIncremental(nil)
       CGImageSourceUpdateData(incrementSource, self.imgData as CFData, dataTask.countOfBytesExpectedToReceive == self.imgData.count)
       let status = CGImageSourceGetStatus(incrementSource)
       switch status {
             case .statusComplete,.statusIncomplete:
               if let cgImage = CGImageSourceCreateImageAtIndex(incrementSource, 0, imageOptions){
                   DispatchQueue.main.async {
                    //用到orientation因为测试图片旋转了90度,在查找原因
                    self.imgView.image = UIImage(cgImage: cgImage, scale: 1.0, orientation: UIImage.Orientation.right)
                   }
               }
             default:
                break
           }
    }
Apple官方文档:文档地址ImageIOGuide
ImageIO的使用之CGImageDestination图像编码