We can classify color spaces into at least three categories. When you initialize a CIImage from a bitmap or data, there is an initializing color space. When you do filter operations, there is a working color space. When you render a CIImage to a destination, there is an output color space.
Initializing Color Spaces
When you initialize a CIImage from a bitmap, the initializer init(bitmapData data: Data, bytesPerRow: Int, size: CGSize, format: CIFormat, colorSpace: CGColorSpace?)
has a color space parameter, which is relatively easy to understand.
- CVPixelBuffer or other formats
When you initialize a CIImage from a CVPixelBuffer, you can optionally provide a color space through the CIImageOption.colorSpace
option key. But in most cases, a CVPixelBuffer has already color space information in it, so this is not necessary. And if you provide a different color space, you can get the wrong colors.
However it is acceptable to provide an NSNull() like CIImage(cvPixelBuffer: buffer, options: [CIImageOption.colorSpace : NSNull()])
. This means discarding the color space information. If you do this, you typically should set the workingColorSpace
to NSNull() too, otherwise the color space of the image will be considered as the workingColorSpace
when you apply filters, see below. You typically want the color space back when you render it to a destination, you don't want to lose it permanently, see the "Output Color Spaces" section below.
Working Color Spaces
CIContext has a property workingColorSpace
. The document says
The working color space determines the color space used when executing filter kernels; Core Image automatically converts to and from the source and destination color spaces of input images and output contexts...
This color space is important and can have a visible impact when you use filters such as CIFilter.colorMatrix to transform colors.
You can consider that the context first converts the input image into the working color space, performs filter calculations in the working color space, and then converts it into the output color space.
Note, after applying filters, the colorSpace
property of the CIImage may become nil. The system is informing you this way that the color space is not determined yet.
The workingColorSpace
is a read-only property, you set it in the initializer like CIContext(options: [CIContextOption.workingColorSpace : working])
If you don't provide a working color space in the initializer, the context will still have a default color space which is linearSRGB according to my test. You can also provide an NSNull() which means discarding the color space information in the input image and no color conversion. This is useful for example with statistical filters such as CIFilter.areaHistogram or when you want to avoid color conversion overhead. If it is the later case, remember to set the output color space back.
Output Color Spaces
A CIImage is just an image "recipe" that contains information on how to produce an image. Ultimately you will need to render it into a destination such as a bitmap or a CGImage. That is when you specify the output color space. This kind of color space may not always have a visible impact when the output is a CGImage and the color space is not nil (I do not recommend creating a CGImage with a nil output color space, the meaning is not clear), since it is just the internal encoding of the data, unless there are extreme colors that cannot be accurately encoded by the color space.
You can specify the output color space in the render
method of CIContext.
You can specify the output color space in the createCGImage(_: CIImage, from: CGRect, format: CIFormat, colorSpace: CGColorSpace?)
method of CIContext.
The CIContextOption.outputColorSpace
is intended to be used when you call the simpler createCGImage(_: CIImage, from: CGRect)
method overload. If you do not provide this key, the system will choose DeviceRGB according to my test, in this case the color may not be consistent across devices. If you call the above complex overload createCGImage(_: CIImage, from: CGRect, format: CIFormat, colorSpace: CGColorSpace?)
, this option will be ignored. I suggest using the complex overload since it is more explicit.
CIContext
is costly in terms of performance, so if you can stick with one - required if you are using aGLKView
- then do that (stick with one that is). ACIImage
is not an image, instead it's a "recipe" to draw or manipulate an image. None of this explains which is best for your needs though, so I guess the question for you is why are you setting a color space? It's that answer that will probably dictate which place to set things. – Technetium