如何在Swift中以编程方式剥离PNG的冗余信息?
在Swift中剥离PNG冗余信息的实现方案
核心思路:手动筛选PNG数据块
Swift现有库(如swift-png)没有直接等价于ImageMagick -strip 的封装方法,需要手动解析PNG文件结构,保留必要数据块、移除冗余块。PNG文件由签名+多组数据块构成,必须保留的块包括:IHDR(文件头)、PLTE(调色板,索引色图像必填)、IDAT(图像数据,可能存在多个)、IEND(结束块),其余非必要块均可移除。
具体实现步骤
读取并验证PNG文件
将目标PNG读取为Data对象,先校验开头的PNG签名(8字节:89 50 4E 47 0D 0A 1A 0A),确保是合法PNG文件。遍历并过滤数据块
从签名后开始逐个解析数据块,每个块的结构为:4字节长度(大端)+4字节块类型+数据内容+4字节CRC校验。- 保留块类型:
IHDR、PLTE、IDAT、IEND - 移除所有其他块:包括
bKGD、cHRM、EXIF、tEXt(注释)、iTXt、zTXt、orNT等冗余块
- 保留块类型:
重新组装PNG文件
将保留的块按原顺序拼接,加上PNG签名,生成新的Data后写入文件即可。
示例代码片段
import Foundation func stripPNGRedundantData(from inputData: Data) -> Data? { // 校验PNG签名 let pngSignature: [UInt8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A] guard inputData.starts(with: pngSignature) else { return nil } var outputData = Data(pngSignature) var offset = pngSignature.count // 定义必须保留的块类型集合 let requiredChunkTypes: Set<String> = ["IHDR", "PLTE", "IDAT", "IEND"] while offset < inputData.count { // 读取块长度(大端字节序) let lengthBytes = inputData.subdata(in: offset..<offset+4) let length = lengthBytes.withUnsafeBytes { $0.load(as: UInt32.self).bigEndian } offset += 4 // 读取块类型 let typeBytes = inputData.subdata(in: offset..<offset+4) guard let type = String(bytes: typeBytes, encoding: .ascii) else { return nil } offset += 4 // 定位数据和CRC的位置 let dataEndOffset = offset + Int(length) let crcOffset = dataEndOffset let crcBytes = inputData.subdata(in: crcOffset..<crcOffset+4) offset = crcOffset + 4 // 保留必要块,写入输出数据 if requiredChunkTypes.contains(type) { outputData.append(lengthBytes) outputData.append(typeBytes) outputData.append(inputData.subdata(in: offset-Int(length)-4..<dataEndOffset)) outputData.append(crcBytes) } } return outputData } // 使用示例 if let inputURL = Bundle.main.url(forResource: "input", withExtension: "png"), let inputData = try? Data(contentsOf: inputURL), let strippedData = stripPNGRedundantData(from: inputData), let outputURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("output.png") { try? strippedData.write(to: outputURL) print("处理完成,输出文件路径:\(outputURL)") }
额外说明:移除orNT块
上述代码已包含移除orNT块的逻辑(因它不在必要块列表中),若需更明确处理,可在遍历块时增加单独判断:if type == "orNT" { continue }。
简化替代方案:调用ImageMagick命令行
如果不需要纯Swift实现,可通过Process直接调用ImageMagick命令行工具,实现和magick convert input.png -strip -orient Undefined output.png相同的效果:
func stripPNGWithImageMagick(inputPath: String, outputPath: String) { let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/local/bin/magick") // 根据本地ImageMagick路径调整 process.arguments = ["convert", inputPath, "-strip", "-orient", "Undefined", outputPath] do { try process.run() process.waitUntilExit() } catch { print("调用ImageMagick失败:\(error.localizedDescription)") } }
内容的提问来源于stack exchange,提问作者lcj
相关产品推荐
相关产品推荐

