You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Swift中以编程方式剥离PNG的冗余信息?

在Swift中剥离PNG冗余信息的实现方案

核心思路:手动筛选PNG数据块

Swift现有库(如swift-png)没有直接等价于ImageMagick -strip 的封装方法,需要手动解析PNG文件结构,保留必要数据块、移除冗余块。PNG文件由签名+多组数据块构成,必须保留的块包括:IHDR(文件头)、PLTE(调色板,索引色图像必填)、IDAT(图像数据,可能存在多个)、IEND(结束块),其余非必要块均可移除。

具体实现步骤

  1. 读取并验证PNG文件
    将目标PNG读取为Data对象,先校验开头的PNG签名(8字节:89 50 4E 47 0D 0A 1A 0A),确保是合法PNG文件。

  2. 遍历并过滤数据块
    从签名后开始逐个解析数据块,每个块的结构为:4字节长度(大端)+4字节块类型+数据内容+4字节CRC校验。

    • 保留块类型:IHDR、PLTE、IDAT、IEND
    • 移除所有其他块:包括bKGD、cHRM、EXIF、tEXt(注释)、iTXt、zTXt、orNT等冗余块
  3. 重新组装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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 15:52:30