Swift压缩PNG图像
Swift压缩PNG图像

Swift压缩PNG图像

选择方案

当前 macOS / Swift 平台上压缩 PNG 图像的最佳实践和第三方库选择方案:

1、pngquant:色彩量化,有损压缩,支持CLI / C API,最佳选择(压缩可达70-90%)。

2、ImageOptim CLI:多工具合集,有损压缩,支持CLI,调用 pngquant/optipng/zopfli 等。

3、optipng:重排 + 去冗余,无损压缩,支持CLI,通常压缩 5-20%。

4、tinypng.com API:色彩分析+压缩,有损压缩,支持HTTP API,效果好,免费有额度限制。

5、NSBitmapImageRep:重新编码,无损压缩,macOS 原生,几乎无压缩。

6、Image I/O (CGImageDestination):写入 PNG,无损压缩,macOS 原生,也几乎无压缩。

测试方案

使用这个PNG图片测试全部方案:     

1、pngquant

CLI安装:

brew install pngquant
pngquant --quality=65-80 --output output.png your_image.png

参数含义:压缩质量范围 65%-80%,输出文件名为 output.png。

–quality=0-100:理论范围。

–quality=65-80(推荐):平衡压缩率和画质(TinyPNG 类似)。

–quality=100:只接受完全无损的视觉输出(几乎等于不压缩)。

–quality=90-100:非常高质量,压缩率极低。

–quality=0-50:压缩率很高,但可能出现明显色彩损失。

实际代码

pngquant --quality=65-80 --output "/Users/fangjunyu/Desktop/IMG_3104_compressed.png" "/Users/fangjunyu/Desktop/IMG_3104.PNG"

发现1.9MB的图片被压缩成209KB,压缩率为89%,压缩率高。

如果设置—quality为0-1:

1.9MB的图片可以被极限的压缩为20KB,压缩率为99.01%

2、ImageOptim

下载ImageOptiom,默认压缩54%的体积,处于中等水平,压缩JPG图片没有Mac原生的压缩功能好用。

在开启最低的压缩配置后:

JPG压缩64%的体积(和mac原生的压缩功能差不多),PNG压缩96.5%的体积,PNG从1.9MB变成了66KB,也是非常高的压缩比。

3、optipng

CLI安装:

brew install optipng
optipng -o7 your_image.png  // -o7 表示最强压缩等级

级别:

-o0:最低压缩,非常快,仅做基本优化。

-o1(默认):较低压缩,快,平衡。

-o2~-o3:中等压缩,稍慢,尝试更多滤波器组合。

-o4~-o5:更高压缩,较慢,搜索空间增大。

-o6~-o7:最强压缩,非常慢,尝试所有可能组合,体积最小。

每增加一级压缩等级,optipng 会尝试更多的过滤器、压缩策略,可能获得略微更好的压缩率,但耗时会显著增加。

使用optipng的o7级别可以压缩51.5%,o8级别和o20级别都只能压缩到51.5%。

并且,每层每层都需要花费时间计算,比前面的方法耗时都要多。

4、tinypng.com API

使用tinypng可以压缩75%的体积。

5、NSBitmapImageRep

可以压缩35%的体积。

6、CGImageDestination

和NSBitmapImageRep一样,都是压缩35%的体积。

7、测试方案总结

在所有的方案中,pngquant和ImageOptim压缩比率最夸张,压缩率分别是99%和96.5%,是最好的PNG压缩方案。所以,推荐使用pngquant和ImageOptim接入Swift压缩PNG图片。

tinypng压缩率为75%,optipng压缩率为51.5%,tinypng作为我工作常用的PNG免费网页压缩图片工具,压缩率已经非常高了。但因为tinypng api是接口,测试的是tinypng网页,所以仅用于相比参考,无法在单机应用中使用。optipng压缩中规中矩,只比mac原生的两个图片类要高,比其他方案都要低。

最后是NSBitmapImageRep和Image I/O (CGImageDestination),这两个在我的代码中都是35%的压缩率,其他照片在这两个macOS内置类中输出的大小都一样,因此在处理PNG图片方法,两个类的代码可能完全一样,但是在其他的PNG压缩工具比起来是最差的。如果是不使用第三方包,那么可以使用NSBitmapImageRep和Image I/O (CGImageDestination)压缩图片。

集成pngquant

1、下载pngquant

pngquant官方网址:https://pngquant.org/

首先下载适用于macOS的二进制文件【下载地址】。

将下载的tar.bz2压缩包解压,解压后打开pngquant文件夹,可以看到四个文件。

2、添加到Xcode项目中

把pngquant文件拖入Xcode项目中。

如果是Xcode15及以前的版本,则需要勾选“Copy items if needed”,不勾选的话,Xcode可能使用的是pngquant文件的引用,而不是将pngquant文件打包进Xcode。

在Xcode中,找到 Build Phases > Copy Bundle Resources,检查是否包含pngquant文件,确保pngquant文件被打包进去。

3、在Swift中调用pngquant

import Foundation

class PNGQuantCompressor {
    static func compress(inputURL: URL, outputURL: URL, quality: String = "65-80") {
        guard let toolPath = Bundle.main.path(forResource: "pngquant", ofType: nil) else {
            print("pngquant not found in app bundle.")
            return
        }

        // 确保文件可执行,如果文件已在本地授权,可忽略该配置权限
        try? FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: toolPath)		

        let process = Process()
        process.executableURL = URL(fileURLWithPath: toolPath)
        process.arguments = ["--quality=\(quality)", "--output", outputURL.path, inputURL.path]

        let pipe = Pipe()
        process.standardOutput = pipe
        process.standardError = pipe

        do {
            try process.run()
            process.waitUntilExit()

            let data = pipe.fileHandleForReading.readDataToEndOfFile()
            if let output = String(data: data, encoding: .utf8) {
                print("pngquant output:\n\(output)")
            }

            if process.terminationStatus == 0 {
                print("压缩完成:\(outputURL.lastPathComponent)")
            } else {
                print("压缩失败,退出码:\(process.terminationStatus)")
            }
        } catch {
            print("运行 pngquant 失败:\(error)")
        }
    }
}

调用方法:

let input = URL(fileURLWithPath: "/Users/fangjunyu/Desktop/input.png")
let output = URL(fileURLWithPath: "/Users/fangjunyu/Desktop/output.png")

PNGQuantCompressor.compress(inputURL: input, outputURL: output, quality: "70-90")

4、代码解析

1、方法签名

static func compress(inputURL: URL, outputURL: URL, quality: String = "65-80")

是一个 static 方法,因此可以直接通过类调用。

接收三个参数:

1)inputURL:原始 PNG 文件的路径。

2)outputURL:压缩后文件的输出路径。

3)quality:压缩质量范围,默认是 “65-80″,表示 pngquant 会尝试保持图片在 65% 到 80% 质量之间。

2、找到 pngquant 工具路径

guard let toolPath = Bundle.main.path(forResource: "pngquant", ofType: nil) else {
    print("pngquant not found in app bundle.")
    return
}

尝试从 app bundle 中获取 pngquant 可执行文件的路径。

要求将 pngquant 文件捆绑到 .app/Contents/Resources/ 中。

3、设置可执行权限(macOS 沙盒中可能必须)

try? FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: toolPath)

设置 pngquant 文件权限为 755(拥有者可读写执行,其他可读执行)。

加 try? 避免抛出错误。

 4、创建并配置 Process

let process = Process()
process.executableURL = URL(fileURLWithPath: toolPath)
process.arguments = ["--quality=\(quality)", "--output", outputURL.path, inputURL.path]

使用 Process 类来启动一个子进程。

设置要执行的命令行为:

pngquant --quality=65-80 --output outputPath inputPath

5、捕获标准输出与错误

let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe

设置 Process 的标准输出和标准错误输出都重定向到一个 Pipe 中,以便读取日志信息。   

6、启动进程

try process.run()
process.waitUntilExit()

启动子进程并等待其完成执行。

7、读取输出内容

let data = pipe.fileHandleForReading.readDataToEndOfFile()
if let output = String(data: data, encoding: .utf8) {
    print("pngquant output:\n\(output)")
}

从 Pipe 读取 pngquant 的输出并打印出来。

8、检查退出状态

if process.terminationStatus == 0 {
    print("压缩完成:\(outputURL.lastPathComponent)")
} else {
    print("压缩失败,退出码:\(process.terminationStatus)")
}

退出码为 0 表示压缩成功。

 否则,提示失败。

注意事项

1、需要确保 pngquant 文件具有可执行权限。如果出现权限错误,可以在终端中手动配置:

chmod +x pngquant

或Swift方式:

try? FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: path)

2、在Xcode中打包时,pngquant会提示没有启用 Hardened Runtime(加固运行时)支持,需要使用codesign命令重新签名,相关文章请见《Xcode报错:”pngquant” must be rebuilt with support for the Hardened Runtime. Enable the Hardened Runtime capability in the project editor》。

总结

经过多个方案的测试,推荐使用pngquant和ImageOptim压缩PNG图片,也可以使用tinypng的网页压缩PNG图片。

 在使用pngquant和ImageOptim时,需要注意商业应用授权,如果是开源App可以使用,如果是商用,建议购买商业授权。

1、pngquant商业授权:https://supso.org/projects/pngquant

2、ImageOptim商业授权:没有找到授权地址,但如需使用,可通过电子邮件联系kornel@imageoptim.com,如果邮件无法取得联系,查看ImageOptim下载页面的“contact me”,取得最新的联系途径。

3、如果不想支付商业授权费,可以考虑opting,许可证:https://optipng.sourceforge.net/license.txt

相关文章

1、Apple压缩图片类CGImageDestination:https://fangjunyu.com/2025/07/13/apple%e5%8e%8b%e7%bc%a9%e5%9b%be%e7%89%87%e7%b1%bbcgimagedestination/

2、macOS位图类NSBitmapImageRep:https://fangjunyu.com/2025/07/10/macos%e4%bd%8d%e5%9b%be%e7%b1%bbnsbitmapimagerep/

3、pngquant:https://pngquant.org/

4、opt PNG:http://optipng.sourceforge.net/

5、tinify:https://tinify.com/developers

6、tinypng:https://tinypng.com/

7、ImageOptim:https://imageoptim.com/mac

8、Xcode报错:”pngquant” must be rebuilt with support for the Hardened Runtime. Enable the Hardened Runtime capability in the project editor:https://fangjunyu.com/2025/07/14/xcode%e6%8a%a5%e9%94%99%ef%bc%9apngquant-must-be-rebuilt-with-support-for-the-hardened-runtime-enable-the-hardened-runtime-capability-in-the-project-editor

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注