在不同颜色空间之间转换CGColors的正确方法

28
有一些问题涉及在苹果平台上的不同颜色空间之间进行颜色转换。不幸的是,答案往往包括非便携式的Objective-C类“NSColor”或“UIColor”,这些类无法在OS X和iOS上可互换使用。
因此,我想问一个非常具体的问题,我相信必须有一个好的答案。我简直不能相信苹果不会预见到这个需求。
如何以通用方式将一个颜色空间(例如单色)中的CGColor转换为另一个颜色空间(例如RGB),同时支持所有CGColorSpace类型,并仅使用便携式Core Graphics函数?
一些背景。我需要将在线服务提供的值与存储在UIColor中的值相乘。在iOS5之前正确提取RGB组件的方法(最终引入了方法“- [UIColor getRed:green:blue:alpha:]”)是使用“CGColorGetComponents()”。然后,我将此颜色乘以从在线服务获取的颜色。如果使用“+ [UIColor grayColor]”生成UIColor,则会失败。这意味着我需要将灰度颜色空间转换为RGB。在这种情况下,这很容易。提供其他颜色空间怎么办?或者在理论上的未来情况下,如果我只想处理单个像素的颜色怎么办?
某处有建议,可以将像素绘制到位图上下文中,然后读取此像素。那太疯狂了,我希望这不是唯一的方法。显然,绘图方法可以解决如何执行转换的问题;我们如何利用它而不创建位图上下文来仅在其中绘制像素呢?
附加研究:
  • 这篇文章 关于颜色转换,除了 UIColor 的未公开/私有方法 -styleString,更有趣的是提到了未公开的 CGColorTransform
  • WebKit中的 ColorCG.cpp 表示有一个名为 CoreGraphics/CGColorTransform.h 的头文件。不幸的是,在至少Mountain Lion上,这个头文件并不存在。为什么苹果要隐藏这些API呢?
  • 我唯一找到提到 CGColorTransform 的资源是 FreeQuartz,一个Core Graphics的自由/开源重新实现。
  • 我向苹果提交了 #12141580 号 radar,以开放和记录 CGColorTransform。然而我没有抱太大指望,如果有其他明智的建议,我会听取。


    1
    我已经使用vImage(Accelerate)或位图上下文进行多年的操作。像您建议的这样的实用程序需求很低 - 为什么苹果要投入时间和精力去做它们呢?给定RGB格式的像素,您可以使用在线易于查找的C代码将其转换为其他空间,处理数据,然后再转换回RGB。 - David H
    7
    @DavidH:你的观点有些可笑。为什么苹果会提供-[NSString pathExtension]呢?人们在其他语言中也乐意使用子字符串。如果需求量很低,那为什么苹果要添加-[UIColor getRed:green:blue:alpha:]呢?你谈论能源 - 如果苹果可以解决问题,为什么成千上万的开发者要花费精力来重复造轮子呢?我理解你的观点,但我认为可以在不创建上下文或使用NSImage/CGImage的情况下完成某些事情。使用vImage听起来很有趣,能否提供一个示例作为答案呢? - Ivan Vučica
    2
    正确发泄你的不满应该是在bug reporter.apple.com上输入一个增强型bug。在过去的8年中,我已经输入了大约300个这样的bug。或者,你可以选择制作一个很好的开源项目并将其放在github上(我有几个这样的项目),通过它获得名声和财富! - David H
    @DavidH:嘿 - 在过去的三年中,我只提交了大约10个问题,只有两个被关闭。是的,我对苹果缺乏易于查看的颜色空间转换方式感到沮丧,但我很感谢您对这个问题的关注(尽管您没有提供确切的答案,并且最初建议我询问“低需求”(即不必要)功能的存在)。创建一个开源项目也不如发现苹果是如何做到这一点并期望我们这样做有趣。此外,我对颜色配置文件一无所知,因此我几乎不是正确的人选。 :-) - Ivan Vučica
    3个回答

    8

    苹果在iOS 9和macOS 10.11中添加了此API:

    • ObjC: CGColorCreateCopyByMatchingToColorSpace
    • Swift: CGColor.converted(to:intent:options:)

    (注意:本文中的HTML标签已保留)

    7
    苹果公司对我提出的请求,要求公开CGColorTransform,在rdar #12141580中做出了官方回应:
    感谢您的反馈。根据以下信息,工程师已确定不会更改行为:
    请使用ColorSync。
    如果您对问题的解决有疑问,请在bug报告中更新相关信息。
    现在我们将关闭此bug报告。
    请务必定期检查新的苹果发布版本,以获取可能影响此问题的任何更新。
    显然,撰写这篇回应的人认为有必要详细解释这个决定。特别是建议使用如ColorSync等高度复杂的API的决策。

    4
    这个问题明确表示他们想避免在仅为转换颜色创建的位图上下文中绘制,但我没有意识到并提出了这个解决方案。 我会将这个作为参考留给任何可能遇到这个问题并想知道如何通过在临时位图上绘制来进行转换的人。
    CGColorRef source;
    CGColorSpaceRef targetColorSpace;
    int components = CGColorSpaceGetNumberOfComponents(targetColorSpace);
    CGFloat temp-buffer[components];
    CGContextRef temp-bmp = CGBitmapContextCreate(
        temp-buffer,
        1,1,8 * sizeof(CGFloat),components,
        targetColorSpace,
        kCGBitmapFloatComponents );
    CGContextSetFillColorWithColor(temp-bmp,source);
    CGContextFillRect(temp-bmp,CGRectMake(0,0,1,1));
    CGColor target = CGColorCreate(targetColorSpace,temp-buffer);
    
    /* then you cleanup and destroy created objects */
    

    我没有试过这段代码,可能需要进行一些修复。


    3
    对于其他人看到帖子主题并需要答案的人来说,这种方法是可行的。但是,在OP中已经明确指出,这已经是a)已知的解决方案,并且b)特别是OP(以及我作为悬赏投资者)希望避免的过程。来自OP的问题是:“如何在不创建位图上下文的情况下利用它来绘制一个像素?” 这个示例对于其他人很有用--所以请随意保留它,尽管这是Ivan和我希望避免的解决方案。 - justin
    抱歉,我没有读到那部分。我只看到需要一个可移植且不需要了解颜色空间的解决方案。 - pqnet

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接