Swift: конвертировать RGB (UIColor) в CMYK с помощью профиля ICC

Я успешно сделал следующую функцию для преобразования UIColor в значения CMYK, используя код Swift 2 в Xcode 7.2. Однако возвращаемые значения похожи на преобразование на основе формул:

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() на CGCreateWithICCProfile(), который ищет ввод NSData. Я загрузил стандартные профили CMYK ICC из Adobe и перетащил "CoatedGRACoL2006.icc" в папку проекта Asset.xcassets. Код скомпилирован, но сгенерировал длинный список ошибок при запуске приложения. Что касается того, как изменить ColorSpace на цветовое пространство, основанное на профиле ICC, любые комментарии, комментарии или предложения будут приветствоваться.

2 ответа

Решение

Ты можешь использовать NSData загрузить данные профиля из вашего пакета.NSData а также CFData являются бесплатными мостами, поэтому можно использовать NSData экземпляр всякий раз, когда API требуетCFData один.

Я переместил ваш метод преобразования цветов в расширение:

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)")

Обратите внимание, что приведенный выше загружает профиль из ресурсов вашего комплекта, а не из каталога ресурсов. (Таким образом, вы должны убедиться, что файл.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 }

Я нахожу CFDataRef несколько сбивает с толку, но я посмотрел на CGDataProvider работает в первый раз, возвращает пустое изображение во второй раз, и это имеет дополнительный шаг. Я попробовал этот код:

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 я обнаружил, что они точно совпадают с номерами компонентов, которые вы создали без профиля icc. Однако, однако, я пытался

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

и что определенно поставил профиль, так почему цифры не изменились, я не знаю. Это может быть другая проблема.

Если вам удастся добиться прогресса в этом, я думаю, вы должны позаботиться об управлении памятью этих неуправляемых CFthings. Копия, которую я создал выше, должна быть выпущена; Я не писал этот код. Удачи!

Другие вопросы по тегам