Swift:使用 ICC 配置文件将 RGB (UIColor) 转换为 CMYK

Swift: Convert RGB (UIColor) to CMYK with ICC Profile

我已经使用 Xcode 7.2 中的 Swift 2 代码成功实现了以下将 UIColor 转换为 CMYK 值的功能。但是,返回值类似于基于公式的转换:

func RGBtoCMYK(rgbColor: UIColor) -> (c: CGFloat, m: CGFloat, y: CGFloat, k: CGFloat) {
    /*
    let iccFileName = "CoatedGRACoL2006"
    let iccProfile: CFDataRef = CGDataProviderCreateWithFilename(iccFileName) as! CFDataRef
    let colorSpace: CGColorSpaceRef = CGColorSpaceCreateWithICCProfile(iccProfile)!
    */
    let colorSpace: CGColorSpaceRef = CGColorSpaceCreateDeviceCMYK()!
    let intent = CGColorRenderingIntent.RenderingIntentPerceptual
    let cmykColor = CGColorCreateCopyByMatchingToColorSpace(colorSpace, intent, rgbColor.CGColor, nil)
    let c: CGFloat = round(CGColorGetComponents(cmykColor)[0] * 100)
    let m: CGFloat = round(CGColorGetComponents(cmykColor)[1] * 100)
    let y: CGFloat = round(CGColorGetComponents(cmykColor)[2] * 100)
    let k: CGFloat = round(CGColorGetComponents(cmykColor)[3] * 100)
    return (c, m, y, k)
}

相反,我想使用 ICC 配置文件。在标出的区域中,我尝试将 ColorSpace 从 CGCreateDeviceCMYK() 更改为正在寻找 NSData 输入的 CGCreateWithICCProfile()。我从 Adob​​e 下载了标准的 CMYK ICC 配置文件并将 "CoatedGRACoL2006.icc" 拖到我的 Asset.xcassets 项目文件夹中。代码编译但生成了一长串错误,而 运行 应用程序。关于如何将 ColorSpace 更改为基于 ICC 配置文件的颜色 space,我们将不胜感激任何意见、评论或建议。

我发现 CFDataRef 有点令人困惑,但我查看了 CGDataProvider works the first time, returns an empty image the second time 并且其中有一个额外的步骤。我试过这段代码:

guard let iccPath = NSBundle.mainBundle().pathForResource("CoatedGRACoL2006", ofType: "icc") else { print("bad file"); return (0,0,0,0) }
guard let iccProfile = CGDataProviderCreateWithFilename(iccPath) else { print("bad profile"); return (0,0,0,0); }
let iccCFDataRef = CGDataProviderCopyData(iccProfile) // extra step
guard let colorSpace = CGColorSpaceCreateWithICCProfile(iccCFDataRef) else { print ("bad colorSpace"); return (0,0,0,0); }
let intent = CGColorRenderingIntent.RenderingIntentPerceptual
guard let cmykColor = CGColorCreateCopyByMatchingToColorSpace(colorSpace, intent, rgbColor.CGColor, nil) else { print("Bad color"); return (0,0,0,0) }

并且 确实 创建了一个没有任何错误的 cmyk 颜色。但是,在将 CMYK 组件生成为 return 时,我发现它们与您在没有 icc 配置文件的情况下生成的组件的编号完全相同。 不过不过,我试过了

print("Recovered profile: \(CGColorSpaceCopyICCProfile(colorSpace))")

那肯定提供了配置文件,所以我不知道为什么数字没有改变。那可能是一个不同的问题。

如果你能在这方面取得进展,我认为你应该注意这些非托管 CFthings 的内存管理。我上面创建的副本必须发布;我没有写那个代码。祝你好运!

您可以使用 NSData 从您的包中加载配置文件的数据。 NSDataCFData 是免费桥接的,因此只要 API 需要 CFData 就可以使用 NSData 实例。

我把你的颜色转换方法移到了一个扩展中:

extension UIColor {
    func colorComponentsByMatchingToColorSpace(colorSpace: CGColorSpace) -> (c: CGFloat, m: CGFloat, y: CGFloat, k: CGFloat) {
        let intent = CGColorRenderingIntent.RenderingIntentPerceptual
        let cmykColor = CGColorCreateCopyByMatchingToColorSpace(colorSpace, intent, self.CGColor, nil)
        let c: CGFloat = round(CGColorGetComponents(cmykColor)[0] * 100)
        let m: CGFloat = round(CGColorGetComponents(cmykColor)[1] * 100)
        let y: CGFloat = round(CGColorGetComponents(cmykColor)[2] * 100)
        let k: CGFloat = round(CGColorGetComponents(cmykColor)[3] * 100)
        return (c, m, y, k)
    }
}

要从您的包中加载 .icc 文件并调用扩展方法,您可以使用以下方法:

guard let iccProfileURL = NSBundle.mainBundle().resourceURL?.URLByAppendingPathComponent("CoatedGRACoL2006.icc") else {
    return;
}
guard let iccProfileData = NSData(contentsOfURL: iccProfileURL) else {
    return;
}
guard let colorSpace = CGColorSpaceCreateWithICCProfile(iccProfileData) else {
    return;
}
let components = UIColor.redColor().colorComponentsByMatchingToColorSpace(colorSpace);
print("\(components)")

请注意,上面的配置文件是从您的捆绑资源中加载的,而不是从资产目录中加载的。 (因此您必须确保在 "Copy Bundle Resources" 构建阶段将 .icc 文件复制到您的包中)。 如果你想使用资产目录并且你的目标是 iOS 9.0 或更高版本,你可以查看 NSDataAsset.

@thomash-zoechling 的答案更新为 Swift 5:

extension UIColor {
    func colorComponentsByMatchingToColorSpace(colorSpace: CGColorSpace) -> (c: CGFloat, m: CGFloat, y: CGFloat, k: CGFloat) {
        let intent = CGColorRenderingIntent.perceptual
        guard let cmykColor = self.cgColor.converted(to: colorSpace, intent: intent, options: nil) else { fatalError("Couldn't convert color to CMYK.") }
        let c: CGFloat = (cmykColor.components?[0] ?? 0) * 100
        let m: CGFloat = (cmykColor.components?[1] ?? 0) * 100
        let y: CGFloat = (cmykColor.components?[2] ?? 0) * 100
        let k: CGFloat = (cmykColor.components?[3] ?? 0) * 100
        return (c,m,y,k)
    }
}

并加载 icc 配置文件:

guard let profileURL = Bundle.main.url(forResource: "filname", withExtension: "icc") else { return }
guard let data = try? Data(contentsOf: profileURL) else { return }
guard let colorspace = CGColorSpace(iccProfileData: data as CFData) else { return }